ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 1 | # Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | """Generator script for Web Bluetooth LayoutTests. |
| 5 | |
| 6 | For each script-tests/X.js creates the following test files depending on the |
| 7 | contents of X.js |
| 8 | - getPrimaryService/X.html |
| 9 | - getPrimaryServices/X.html |
| 10 | - getPrimaryServices/X-with-uuid.html |
| 11 | |
| 12 | script-tests/X.js files should contain "CALLS([variation1 | variation2 | ...])" |
| 13 | tokens that indicate what files to generate. Each variation in CALLS([...]) |
| 14 | should corresponds to a js function call and its arguments. Additionally a |
| 15 | variation can end in [UUID] to indicate that the generated file's name should |
| 16 | have the -with-uuid suffix. |
| 17 | |
| 18 | The PREVIOUS_CALL token will be replaced with the function that replaced CALLS. |
| 19 | |
| 20 | The FUNCTION_NAME token will be replaced with the name of the function that |
| 21 | replaced CALLS. |
| 22 | |
| 23 | For example, for the following template file: |
| 24 | |
| 25 | // script-tests/example.js |
| 26 | promise_test(() => { |
dougt | 36c5c1c | 2016-12-03 21:09:01 | [diff] [blame] | 27 | return navigator.bluetooth.requestDevice(...) |
| 28 | .then(device => device.gatt.CALLS([ |
| 29 | getPrimaryService('heart_rate')| |
| 30 | getPrimaryServices('heart_rate')[UUID]])) |
| 31 | .then(device => device.gatt.PREVIOUS_CALL); |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 32 | }, 'example test for FUNCTION_NAME'); |
| 33 | |
| 34 | this script will generate: |
| 35 | |
| 36 | // getPrimaryService/example.html |
| 37 | promise_test(() => { |
dougt | 36c5c1c | 2016-12-03 21:09:01 | [diff] [blame] | 38 | return navigator.bluetooth.requestDevice(...) |
| 39 | .then(device => device.gatt.getPrimaryService('heart_rate')) |
| 40 | .then(device => device.gatt.getPrimaryService('heart_rate')); |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 41 | }, 'example test for getPrimaryService'); |
| 42 | |
| 43 | // getPrimaryServices/example-with-uuid.html |
| 44 | promise_test(() => { |
dougt | 36c5c1c | 2016-12-03 21:09:01 | [diff] [blame] | 45 | return navigator.bluetooth.requestDevice(...) |
| 46 | .then(device => device.gatt.getPrimaryServices('heart_rate')) |
| 47 | .then(device => device.gatt.getPrimaryServices('heart_rate')); |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 48 | }, 'example test for getPrimaryServices'); |
| 49 | |
| 50 | Run |
| 51 | $ python //third_party/WebKit/LayoutTests/bluetooth/generate.py |
| 52 | and commit the generated files. |
| 53 | """ |
Giovanni Ortuño Urquidi | 72c95d6 | 2017-05-25 18:41:01 | [diff] [blame] | 54 | |
| 55 | import fnmatch |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 56 | import os |
| 57 | import re |
| 58 | import sys |
| 59 | |
| 60 | TEMPLATES_DIR = 'script-tests' |
| 61 | |
| 62 | |
| 63 | class GeneratedTest: |
| 64 | |
dougt | 36c5c1c | 2016-12-03 21:09:01 | [diff] [blame] | 65 | def __init__(self, data, path, template): |
| 66 | self.data = data |
| 67 | self.path = path |
| 68 | self.template = template |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 69 | |
| 70 | |
| 71 | def GetGeneratedTests(): |
dougt | 36c5c1c | 2016-12-03 21:09:01 | [diff] [blame] | 72 | """Yields a GeneratedTest for each call in templates in script-tests.""" |
| 73 | bluetooth_tests_dir = os.path.dirname(os.path.realpath(__file__)) |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 74 | |
dougt | 36c5c1c | 2016-12-03 21:09:01 | [diff] [blame] | 75 | # Read Base Test Template. |
| 76 | base_template_file_handle = open( |
| 77 | os.path.join(bluetooth_tests_dir, TEMPLATES_DIR, 'base_test_template.html')) |
| 78 | base_template_file_data = base_template_file_handle.read().decode('utf-8') |
| 79 | base_template_file_handle.close() |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 80 | |
dougt | 36c5c1c | 2016-12-03 21:09:01 | [diff] [blame] | 81 | # Get Templates. |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 82 | |
dougt | 36c5c1c | 2016-12-03 21:09:01 | [diff] [blame] | 83 | template_path = os.path.join(bluetooth_tests_dir, TEMPLATES_DIR) |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 84 | |
dougt | 36c5c1c | 2016-12-03 21:09:01 | [diff] [blame] | 85 | available_templates = [] |
| 86 | for root, _, files in os.walk(template_path): |
| 87 | for template in files: |
| 88 | if template.endswith('.js'): |
| 89 | available_templates.append(os.path.join(root, template)) |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 90 | |
dougt | 36c5c1c | 2016-12-03 21:09:01 | [diff] [blame] | 91 | # Generate Test Files |
| 92 | for template in available_templates: |
| 93 | # Read template |
| 94 | template_file_handle = open(template) |
| 95 | template_file_data = template_file_handle.read().decode('utf-8') |
| 96 | template_file_handle.close() |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 97 | |
dougt | 36c5c1c | 2016-12-03 21:09:01 | [diff] [blame] | 98 | template_name = os.path.splitext(os.path.basename(template))[0] |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 99 | |
scheib | 1e43caf | 2017-01-28 03:13:57 | [diff] [blame] | 100 | # Find function names in multiline pattern: CALLS( [ function_name,function_name2[UUID] ]) |
| 101 | result = re.search( |
| 102 | r'CALLS\(' + # CALLS( |
| 103 | r'[^\[]*' + # Any characters not [, allowing for new lines. |
| 104 | r'\[' + # [ |
| 105 | r'(.*?)' + # group matching: function_name(), function_name2[UUID] |
| 106 | r'\]\)', # adjacent closing characters: ]) |
| 107 | template_file_data, re.MULTILINE | re.DOTALL) |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 108 | |
dougt | 36c5c1c | 2016-12-03 21:09:01 | [diff] [blame] | 109 | if result is None: |
| 110 | raise Exception('Template must contain \'CALLS\' tokens') |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 111 | |
dougt | 36c5c1c | 2016-12-03 21:09:01 | [diff] [blame] | 112 | new_test_file_data = base_template_file_data.replace('TEST', |
| 113 | template_file_data) |
| 114 | # Replace CALLS([...]) with CALLS so that we don't have to replace the |
| 115 | # CALLS([...]) for every new test file. |
| 116 | new_test_file_data = new_test_file_data.replace(result.group(), 'CALLS') |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 117 | |
dougt | 36c5c1c | 2016-12-03 21:09:01 | [diff] [blame] | 118 | # Replace 'PREVIOUS_CALL' with 'CALLS' so that we can replace it while |
| 119 | # replacing CALLS. |
| 120 | new_test_file_data = new_test_file_data.replace('PREVIOUS_CALL', 'CALLS') |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 121 | |
Conley Owens | ab253b0 | 2017-08-01 20:26:17 | [diff] [blame] | 122 | for call in result.group(1).split('|'): |
dougt | 36c5c1c | 2016-12-03 21:09:01 | [diff] [blame] | 123 | # Parse call |
Conley Owens | ab253b0 | 2017-08-01 20:26:17 | [diff] [blame] | 124 | call = call.strip() |
| 125 | function_name, args, uuid_suffix = re.search(r'(.*?)\((.*)\)(\[UUID\])?', call).groups() |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 126 | |
dougt | 36c5c1c | 2016-12-03 21:09:01 | [diff] [blame] | 127 | # Replace template tokens |
| 128 | call_test_file_data = new_test_file_data |
| 129 | call_test_file_data = call_test_file_data.replace('CALLS', '{}({})'.format(function_name, args)) |
| 130 | call_test_file_data = call_test_file_data.replace('FUNCTION_NAME', function_name) |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 131 | |
dougt | 36c5c1c | 2016-12-03 21:09:01 | [diff] [blame] | 132 | # Get test file name |
| 133 | group_dir = os.path.basename(os.path.abspath(os.path.join(template, os.pardir))) |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 134 | |
dougt | 36c5c1c | 2016-12-03 21:09:01 | [diff] [blame] | 135 | call_test_file_name = 'gen-{}{}.html'.format(template_name, '-with-uuid' if uuid_suffix else '') |
| 136 | call_test_file_path = os.path.join(bluetooth_tests_dir, group_dir, function_name, call_test_file_name) |
| 137 | |
| 138 | yield GeneratedTest(call_test_file_data, call_test_file_path, template) |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 139 | |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 140 | def main(): |
Giovanni Ortuño Urquidi | 72c95d6 | 2017-05-25 18:41:01 | [diff] [blame] | 141 | previous_generated_files = set() |
| 142 | current_path = os.path.dirname(os.path.realpath(__file__)) |
| 143 | for root, _, filenames in os.walk(current_path): |
| 144 | for filename in fnmatch.filter(filenames, 'gen-*.html'): |
| 145 | previous_generated_files.add(os.path.join(root, filename)) |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 146 | |
Giovanni Ortuño Urquidi | 72c95d6 | 2017-05-25 18:41:01 | [diff] [blame] | 147 | generated_files = set() |
dougt | 36c5c1c | 2016-12-03 21:09:01 | [diff] [blame] | 148 | for generated_test in GetGeneratedTests(): |
Giovanni Ortuño Urquidi | 72c95d6 | 2017-05-25 18:41:01 | [diff] [blame] | 149 | prev_len = len(generated_files) |
| 150 | generated_files.add(generated_test.path) |
| 151 | if prev_len == len(generated_files): |
| 152 | print 'Generated the same test twice for template:\n{}'.format( |
| 153 | generated_test.template) |
| 154 | |
dougt | 36c5c1c | 2016-12-03 21:09:01 | [diff] [blame] | 155 | # Create or open test file |
| 156 | test_file_handle = open(generated_test.path, 'wb') |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 157 | |
dougt | 36c5c1c | 2016-12-03 21:09:01 | [diff] [blame] | 158 | # Write contents |
| 159 | test_file_handle.write(generated_test.data.encode('utf-8')) |
| 160 | test_file_handle.close() |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 161 | |
Giovanni Ortuño Urquidi | 72c95d6 | 2017-05-25 18:41:01 | [diff] [blame] | 162 | new_generated_files = generated_files - previous_generated_files |
| 163 | if len(new_generated_files) != 0: |
| 164 | print 'Newly generated tests:' |
| 165 | for generated_file in new_generated_files: |
| 166 | print generated_file |
| 167 | |
| 168 | obsolete_files = previous_generated_files - generated_files |
| 169 | if len(obsolete_files) != 0: |
| 170 | print 'The following files might be obsolete:' |
| 171 | for generated_file in obsolete_files: |
| 172 | print generated_file |
| 173 | |
| 174 | |
ortuno | e1fe7177 | 2016-10-20 01:54:36 | [diff] [blame] | 175 | |
| 176 | if __name__ == '__main__': |
dougt | 36c5c1c | 2016-12-03 21:09:01 | [diff] [blame] | 177 | sys.exit(main()) |