| # Copyright 2018 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| """Presubmit tests for android_webview/javatests/ |
| |
| Runs various style checks before upload. |
| """ |
| |
| def CheckChangeOnUpload(input_api, output_api): |
| results = [] |
| results.extend(_CheckAwJUnitTestRunner(input_api, output_api)) |
| results.extend(_CheckNoSkipCommandLineAnnotation(input_api, output_api)) |
| results.extend(_CheckNoSandboxedRendererSwitch(input_api, output_api)) |
| results.extend(_CheckNoDomUtils(input_api, output_api)) |
| return results |
| |
| |
| def _CheckAwJUnitTestRunner(input_api, output_api): |
| """Checks that new tests use the AwJUnit4ClassRunner instead of some other |
| test runner. This is because WebView has special logic in the |
| AwJUnit4ClassRunner. |
| """ |
| |
| run_with_pattern = input_api.re.compile( |
| r'^@RunWith\((.*)\)$') |
| aw_runner = 'AwJUnit4ClassRunner.class' |
| parameterized_runner = 'Parameterized.class' |
| runners_factory_pattern = input_api.re.compile( |
| r'^@UseParametersRunnerFactory\((.*)\)$') |
| correct_factory = 'AwJUnit4ClassRunnerWithParameters.Factory.class' |
| |
| errors = [] |
| |
| def _FilterFile(affected_file): |
| return input_api.FilterSourceFile( |
| affected_file, |
| files_to_skip=input_api.DEFAULT_FILES_TO_SKIP, |
| files_to_check=[r'.*Test\.java$']) |
| |
| for f in input_api.AffectedSourceFiles(_FilterFile): |
| run_with_matches = [] |
| prev_line_standard_runner = False |
| prev_line_parameterized_runner = False |
| for line in f.NewContents(): |
| if prev_line_parameterized_runner: |
| prev_line_parameterized_runner = False |
| match = runners_factory_pattern.search(line) |
| if match: |
| if match.group(1) != correct_factory: |
| errors.append("%s - %s" % (f.LocalPath(), line)) |
| else: |
| # Note: we require specific order and adjacent lines here |
| # to simplify the check. This is not required by Java. |
| errors.append("%s - %s" % ( |
| f.LocalPath(), |
| "Missing @UseParametersRunnerFactory annotation")) |
| elif prev_line_standard_runner: |
| prev_line_standard_runner = False |
| match = runners_factory_pattern.search(line) |
| if match: |
| errors.append("%s - %s" % ( |
| f.LocalPath(), |
| "@UseParametersRunnerFactory annotation present but runner is not" |
| " Parameterized")) |
| else: |
| match = run_with_pattern.search(line) |
| if match: |
| run_with_matches.append(line) |
| if match.group(1) == parameterized_runner: |
| prev_line_parameterized_runner = True |
| elif match.group(1) == aw_runner: |
| prev_line_standard_runner = True |
| else: |
| errors.append("%s - %s" % (f.LocalPath(), line)) |
| if not run_with_matches: |
| errors.append("%s - %s" % (f.LocalPath(), "Missing @RunWith annotation")) |
| |
| results = [] |
| |
| if errors: |
| results.append(output_api.PresubmitPromptWarning(""" |
| android_webview/javatests/ should use either |
| @RunWith(AwJUnit4ClassRunner.class), |
| or @RunWith(Parameterized.class) together with |
| @UseParametersRunnerFactory(AwJUnit4ClassRunnerWithParameters.Factory.class) |
| - in that order and on adjacent lines (this is to simplify the check) - |
| not any other test runner (e.g., BaseJUnit4ClassRunner). We assume this is |
| supposed to be a test class because the filename ends with 'Test.java' (if this |
| is actually a helper/utility class, please rename). |
| """, errors)) |
| |
| return results |
| |
| |
| def _CheckNoSkipCommandLineAnnotation(input_api, output_api): |
| """Checks that tests do not add @SkipCommandLineParameterization annotation. |
| This was previously used to run the test in single-process-mode only (or, |
| multi-process-mode only if used with |
| @CommandLineFlags.Add(AwSwitches.WEBVIEW_SANDBOXED_RENDERER)). This is |
| obsolete because we have dedicated annotations (@OnlyRunInSingleProcessMode |
| and @OnlyRunInMultiProcessMode). |
| """ |
| |
| skip_command_line_annotation = input_api.re.compile( |
| r'^\s*@SkipCommandLineParameterization.*$') |
| |
| errors = [] |
| |
| def _FilterFile(affected_file): |
| return input_api.FilterSourceFile( |
| affected_file, |
| files_to_skip=input_api.DEFAULT_FILES_TO_SKIP, |
| files_to_check=[r'.*\.java$']) |
| |
| for f in input_api.AffectedSourceFiles(_FilterFile): |
| for line_num, line in f.ChangedContents(): |
| match = skip_command_line_annotation.search(line) |
| if match: |
| errors.append("%s:%d" % (f.LocalPath(), line_num)) |
| |
| results = [] |
| |
| if errors: |
| results.append(output_api.PresubmitPromptWarning(""" |
| android_webview/javatests/ should not use @SkipCommandLineParameterization to |
| run in either multi-process or single-process only. Instead, use @OnlyRunIn. |
| """, errors)) |
| |
| return results |
| |
| |
| def _CheckNoSandboxedRendererSwitch(input_api, output_api): |
| """Checks that tests do not add the AwSwitches.WEBVIEW_SANDBOXED_RENDERER |
| command line flag. Tests should instead use @OnlyRunIn(MULTI_PROCESS). |
| """ |
| |
| # This will not catch multi-line annotations (which are valid if adding |
| # multiple switches), but is better than nothing (and avoids false positives). |
| sandboxed_renderer_pattern = input_api.re.compile( |
| r'^\s*@CommandLineFlags\.Add\(.*' |
| r'\bAwSwitches\.WEBVIEW_SANDBOXED_RENDERER\b.*\)$') |
| |
| errors = [] |
| |
| def _FilterFile(affected_file): |
| return input_api.FilterSourceFile( |
| affected_file, |
| files_to_skip=input_api.DEFAULT_FILES_TO_SKIP, |
| files_to_check=[r'.*\.java$']) |
| |
| for f in input_api.AffectedSourceFiles(_FilterFile): |
| for line_num, line in f.ChangedContents(): |
| match = sandboxed_renderer_pattern.search(line) |
| if match: |
| errors.append("%s:%d" % (f.LocalPath(), line_num)) |
| |
| results = [] |
| |
| if errors: |
| results.append(output_api.PresubmitPromptWarning(""" |
| android_webview/javatests/ should not use AwSwitches.WEBVIEW_SANDBOXED_RENDERER |
| to run in multi-process only. Instead, use @OnlyRunIn(MULTI_PROCESS). |
| """, errors)) |
| |
| return results |
| |
| |
| def _CheckNoDomUtils(input_api, output_api): |
| """Checks that tests prefer JSUtils.clickNodeWithUserGesture() over |
| DOMUtils.clickNode(). |
| """ |
| |
| dom_utils_pattern = input_api.re.compile(r'DOMUtils\.clickNode\(') |
| errors = [] |
| def _FilterFile(affected_file): |
| return input_api.FilterSourceFile( |
| affected_file, |
| files_to_skip=input_api.DEFAULT_FILES_TO_SKIP, |
| files_to_check=[r'.*\.java$']) |
| |
| for f in input_api.AffectedSourceFiles(_FilterFile): |
| for line_num, line in f.ChangedContents(): |
| m = dom_utils_pattern.search(line) |
| if m: |
| errors.append("%s:%d" % (f.LocalPath(), line_num)) |
| |
| results = [] |
| if errors: |
| results.append(output_api.PresubmitPromptWarning(""" |
| DOMUtils.clickNode() has been observed to cause flakiness in WebView tests. |
| Prefer using JSUtils.clickNodeWithUserGesture() as a more reliable replacement |
| where possible. This is a "soft" warning, so you can bypass this if |
| DOMUtils.clickNode() is the only way. |
| """, errors)) |
| |
| return results |
| |
| |