| #!/usr/bin/env python3 | |
| # Copyright 2026 The Chromium Authors | |
| # Use of this source code is governed by a BSD-style license that can be | |
| # found in the LICENSE file. | |
| import os | |
| import sys | |
| import unittest | |
| # Add repo root to path for PRESUBMIT_test_mocks | |
| sys.path.append( | |
| os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')) | |
| import PRESUBMIT | |
| from PRESUBMIT_test_mocks import MockInputApi, MockOutputApi, MockFile | |
| class AwFeatureOverridesCommentsTest(unittest.TestCase): | |
| def testValidDisabledTemporary(self): | |
| lines = [ | |
| ' // DISABLED_TEMPORARY: crbug.com/123456', | |
| ' aw_feature_overrides.DisableFeature(blink::features::kSomeFeature);', | |
| ] | |
| input_api = MockInputApi() | |
| input_api.files = [ | |
| MockFile('android_webview/browser/aw_field_trials.cc', lines) | |
| ] | |
| errors = PRESUBMIT._CheckAwFeatureOverridesComments( | |
| input_api, MockOutputApi()) | |
| self.assertEqual(0, len(errors)) | |
| def testValidDisabledIncompatible(self): | |
| lines = [ | |
| ' // DISABLED_INCOMPATIBLE: WebView architecture constraints', | |
| ' aw_feature_overrides.DisableFeature(blink::features::kSomeFeature);', | |
| ] | |
| input_api = MockInputApi() | |
| input_api.files = [ | |
| MockFile('android_webview/browser/aw_field_trials.cc', lines) | |
| ] | |
| errors = PRESUBMIT._CheckAwFeatureOverridesComments( | |
| input_api, MockOutputApi()) | |
| self.assertEqual(0, len(errors)) | |
| def testValidDisabledNeedsApi(self): | |
| lines = [ | |
| ' // DISABLED_NEEDS_API: requires app-facing setting to turn it off', | |
| ' aw_feature_overrides.DisableFeature(blink::features::kSomeFeature);', | |
| ] | |
| input_api = MockInputApi() | |
| input_api.files = [ | |
| MockFile('android_webview/browser/aw_field_trials.cc', lines) | |
| ] | |
| errors = PRESUBMIT._CheckAwFeatureOverridesComments( | |
| input_api, MockOutputApi()) | |
| self.assertEqual(0, len(errors)) | |
| def testValidDisabledOther(self): | |
| lines = [ | |
| ' // DISABLED_OTHER: some other reason goes here', | |
| ' aw_feature_overrides.DisableFeature(blink::features::kSomeFeature);', | |
| ] | |
| input_api = MockInputApi() | |
| input_api.files = [ | |
| MockFile('android_webview/browser/aw_field_trials.cc', lines) | |
| ] | |
| errors = PRESUBMIT._CheckAwFeatureOverridesComments( | |
| input_api, MockOutputApi()) | |
| self.assertEqual(0, len(errors)) | |
| def testValidMultipleFeaturesWithOneReason(self): | |
| lines = [ | |
| ' // DISABLED_TEMPORARY: crbug.com/123456', | |
| ' aw_feature_overrides.DisableFeature(blink::features::kFeatureA);', | |
| ' aw_feature_overrides.DisableFeature(blink::features::kFeatureB);', | |
| ] | |
| input_api = MockInputApi() | |
| input_api.files = [ | |
| MockFile('android_webview/browser/aw_field_trials.cc', lines) | |
| ] | |
| errors = PRESUBMIT._CheckAwFeatureOverridesComments( | |
| input_api, MockOutputApi()) | |
| self.assertEqual(0, len(errors)) | |
| def testValidMultiLineComment(self): | |
| lines = [ | |
| ' // DISABLED_TEMPORARY: crbug.com/123', | |
| ' // Some extra explanation here.', | |
| ' aw_feature_overrides.DisableFeature(blink::features::kSomeFeature);', | |
| ] | |
| input_api = MockInputApi() | |
| input_api.files = [ | |
| MockFile('android_webview/browser/aw_field_trials.cc', lines) | |
| ] | |
| errors = PRESUBMIT._CheckAwFeatureOverridesComments( | |
| input_api, MockOutputApi()) | |
| self.assertEqual(0, len(errors)) | |
| def testValidMultiLineComment_alternateOrder(self): | |
| lines = [ | |
| ' // Some extra explanation here.', | |
| ' // DISABLED_TEMPORARY: crbug.com/123', | |
| ' aw_feature_overrides.DisableFeature(blink::features::kSomeFeature);', | |
| ] | |
| input_api = MockInputApi() | |
| input_api.files = [ | |
| MockFile('android_webview/browser/aw_field_trials.cc', lines) | |
| ] | |
| errors = PRESUBMIT._CheckAwFeatureOverridesComments( | |
| input_api, MockOutputApi()) | |
| self.assertEqual(0, len(errors)) | |
| def testInvalidCommentPrefix(self): | |
| lines = [ | |
| ' // Disable this feature without the proper comment prefix', | |
| ' aw_feature_overrides.DisableFeature(blink::features::kSomeFeature);', | |
| ] | |
| input_api = MockInputApi() | |
| input_api.files = [ | |
| MockFile('android_webview/browser/aw_field_trials.cc', lines) | |
| ] | |
| errors = PRESUBMIT._CheckAwFeatureOverridesComments( | |
| input_api, MockOutputApi()) | |
| self.assertEqual(1, len(errors)) | |
| self.assertIn( | |
| 'Preceding comment was: Line 1: ' | |
| '// Disable this feature without the proper comment prefix', | |
| errors[0].items[0]) | |
| def testInvalidMissingCommentEmptyLine(self): | |
| lines = [ | |
| '', | |
| ' aw_feature_overrides.DisableFeature(blink::features::kSomeFeature);', | |
| ] | |
| input_api = MockInputApi() | |
| input_api.files = [ | |
| MockFile('android_webview/browser/aw_field_trials.cc', lines) | |
| ] | |
| errors = PRESUBMIT._CheckAwFeatureOverridesComments( | |
| input_api, MockOutputApi()) | |
| self.assertEqual(1, len(errors)) | |
| self.assertIn('Preceding comment was: None', errors[0].items[0]) | |
| def testInvalidMissingCommentOtherCode(self): | |
| lines = [ | |
| ' some_other_code();', | |
| ' aw_feature_overrides.DisableFeature(blink::features::kSomeFeature);', | |
| ] | |
| input_api = MockInputApi() | |
| input_api.files = [ | |
| MockFile('android_webview/browser/aw_field_trials.cc', lines) | |
| ] | |
| errors = PRESUBMIT._CheckAwFeatureOverridesComments( | |
| input_api, MockOutputApi()) | |
| self.assertEqual(1, len(errors)) | |
| self.assertIn('Preceding comment was: None', errors[0].items[0]) | |
| def testInvalidMissingPrefix_multipleFeatures(self): | |
| lines = [ | |
| ' // Disable these features', | |
| ' aw_feature_overrides.DisableFeature(blink::features::kFeatureA);', | |
| ' aw_feature_overrides.DisableFeature(blink::features::kFeatureB);', | |
| ] | |
| input_api = MockInputApi() | |
| input_api.files = [ | |
| MockFile('android_webview/browser/aw_field_trials.cc', lines) | |
| ] | |
| errors = PRESUBMIT._CheckAwFeatureOverridesComments( | |
| input_api, MockOutputApi()) | |
| self.assertEqual(1, len(errors)) | |
| self.assertEqual(2, len(errors[0].items)) | |
| def testMixedValidAndInvalid_multipleFeatures(self): | |
| lines = [ | |
| ' // This feature has no prefix', | |
| ' aw_feature_overrides.DisableFeature(blink::features::kFeatureA);', | |
| ' // DISABLED_TEMPORARY: This feature was annotated correctly', | |
| ' aw_feature_overrides.DisableFeature(blink::features::kFeatureB);', | |
| ] | |
| input_api = MockInputApi() | |
| input_api.files = [ | |
| MockFile('android_webview/browser/aw_field_trials.cc', lines) | |
| ] | |
| errors = PRESUBMIT._CheckAwFeatureOverridesComments( | |
| input_api, MockOutputApi()) | |
| self.assertEqual(1, len(errors)) | |
| self.assertIn( | |
| 'Preceding comment was: Line 1: // This feature has no prefix', | |
| errors[0].items[0]) | |
| def testInvalidMissingExplanation(self): | |
| lines = [ | |
| ' // DISABLED_TEMPORARY:', | |
| ' aw_feature_overrides.DisableFeature(blink::features::kSomeFeature);', | |
| ] | |
| input_api = MockInputApi() | |
| input_api.files = [ | |
| MockFile('android_webview/browser/aw_field_trials.cc', lines) | |
| ] | |
| errors = PRESUBMIT._CheckAwFeatureOverridesComments( | |
| input_api, MockOutputApi()) | |
| self.assertEqual(1, len(errors)) | |
| self.assertIn( | |
| 'You must provide an explanation why the feature is disabled', | |
| errors[0].items[0]) | |
| def testIgnoreOtherFiles(self): | |
| lines = [ | |
| '// DISABLED_TEMPORARY: should not matter', | |
| 'aw_feature_overrides.DisableFeature(blink::features::kSomeFeature);', | |
| ] | |
| input_api = MockInputApi() | |
| input_api.files = [ | |
| MockFile('android_webview/browser/other_file.cc', lines) | |
| ] | |
| errors = PRESUBMIT._CheckAwFeatureOverridesComments( | |
| input_api, MockOutputApi()) | |
| self.assertEqual(0, len(errors)) | |
| if __name__ == '__main__': | |
| unittest.main() |