910e62b5创建于 1月15日历史提交
#!/usr/bin/env python
# Copyright 2025 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.path
import sys
import unittest

import PRESUBMIT

file_dir_path = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.join(file_dir_path, '..', '..', '..', '..'))
from PRESUBMIT_test_mocks import MockAffectedFile
from PRESUBMIT_test_mocks import MockInputApi, MockOutputApi


class CheckCheckChromeFeatureListIsSorted(unittest.TestCase):
    """
    Tests the CheckChromeFeatureIsSorted PRESUBMIT check for export list.
    """
    FILE_PATH = 'chrome/browser/flags/android/chrome_feature_list.cc'
    NAME = 'kFeaturesExposedToJava'
    START_PATTERN = '// FEATURE_EXPORT_LIST_START'
    END_PATTERN = '// FEATURE_EXPORT_LIST_END'

    def _generate_file_content(self, features):
        """Helper to generate mock file content with a list of features."""
        content = [
            '// Some header content',
            '#include "some/header.h"',
            '',
            'namespace chrome {',
            'namespace android {',
            self.START_PATTERN,
        ]
        content.extend([f'    {feature}' for feature in features])
        content.extend([
            self.END_PATTERN,
            '}  // namespace android',
            '}  // namespace chrome',
        ])
        return content

    def testSortedListPasses(self):
        """Tests that a correctly sorted feature list passes the check."""
        features = [
            'autofill::features::kAutofillEnableSupportForHomeAndWork',
            'commerce::kCommerceMerchantViewer',
            'features::kAndroidBrowserControlsInViz',
            'kAdaptiveButtonInTopToolbarCustomizationV2',
        ]
        content = self._generate_file_content(features)

        mock_input_api = MockInputApi()
        mock_input_api.files = [MockAffectedFile(self.FILE_PATH, content)]
        results = PRESUBMIT.CheckChromeFeatureIsSorted(
            mock_input_api, MockOutputApi(), self.FILE_PATH, self.NAME,
            self.START_PATTERN, self.END_PATTERN)
        self.assertEqual([], results)

    def testUnsortedListFails(self):
        """
        Tests that an unsorted feature list fails the check with
        a specific error.
        """
        features = [
            'autofill::features::kAutofillEnableSupportForHomeAndWork',
            'features::kAndroidBrowser',  # Out of order
            'commerce::kCommerceMerchantViewer',
            'kAdaptiveButtonInTopToolbarCustomizationV2',
        ]
        content = self._generate_file_content(features)

        mock_input_api = MockInputApi()
        mock_input_api.files = [MockAffectedFile(self.FILE_PATH, content)]
        results = PRESUBMIT.CheckChromeFeatureIsSorted(
            mock_input_api, MockOutputApi(), self.FILE_PATH, self.NAME,
            self.START_PATTERN, self.END_PATTERN)

        self.assertEqual(1, len(results))
        self.assertEqual('error', results[0].type)
        self.assertIn("`kFeaturesExposedToJava`", results[0].message)
        self.assertIn("'features::kAndroidBrowser'",results[0].message)
        self.assertIn("'commerce::kCommerceMerchantViewer'", results[0].message)

    def testUnrelatedFileModifiedPasses(self):
        """Tests the check is skipped if the target file is not modified."""
        mock_input_api = MockInputApi()
        mock_input_api.files = [
            MockAffectedFile('some/other/file.cc', ['// No changes here'])
        ]
        results = PRESUBMIT.CheckChromeFeatureIsSorted(
            mock_input_api, MockOutputApi(), self.FILE_PATH, self.NAME,
            self.START_PATTERN, self.END_PATTERN)
        self.assertEqual([], results)

    def testCaseSensitiveSortFails(self):
        """Tests sorting fails correctly for case-insensitive misordering."""
        # 'feature::A_feature' should come before 'feature::a_feature'.
        features = [
            'feature::a_feature',
            'feature::A_feature',
            'feature::c_feature',
        ]
        content = self._generate_file_content(features)
        mock_input_api = MockInputApi()
        mock_input_api.files = [MockAffectedFile(self.FILE_PATH, content)]
        results = PRESUBMIT.CheckChromeFeatureIsSorted(
            mock_input_api, MockOutputApi(), self.FILE_PATH, self.NAME,
            self.START_PATTERN, self.END_PATTERN)

        self.assertEqual(1, len(results))
        self.assertIn("'feature::A_feature'",results[0].message)
        self.assertIn("'feature::a_feature'",results[0].message)


class CheckChromeFeatureDefinitionsAreSortedTest(unittest.TestCase):
    """
    Tests the CheckChromeFeatureIsSorted PRESUBMIT check for definitions.
    """
    FILE_PATH = 'chrome/browser/flags/android/chrome_feature_list.cc'
    NAME = 'BASE_FEATURE'
    START_PATTERN = '// BASE_FEATURE_START'
    END_PATTERN = '// BASE_FEATURE_END'

    def _generate_file_content(self, feature_lines):
        """
        Helper to generate mock file content with a list of BASE_FEATURE
        definitions.
        """
        content = [
            '// Some header content',
            'namespace features {',
            self.START_PATTERN,
        ]
        content.extend(feature_lines)
        content.extend([
            self.END_PATTERN,
            '}  // namespace features',
        ])
        return content

    def testSortedListPasses(self):
        """Tests that a correctly sorted list of BASE_FEATUREs passes."""
        feature_lines = [
            'BASE_FEATURE(kAdaptiveFoo, base::FEATURE_ENABLED_BY_DEFAULT);',
            'BASE_FEATURE(kAndroidBar, base::FEATURE_ENABLED_BY_DEFAULT);',
            'BASE_FEATURE(kCommerce, base::FEATURE_ENABLED_BY_DEFAULT);',
            'BASE_FEATURE(kSomething, base::FEATURE_ENABLED_BY_DEFAULT);',
        ]
        content = self._generate_file_content(feature_lines)

        mock_input_api = MockInputApi()
        mock_input_api.files = [MockAffectedFile(self.FILE_PATH, content)]
        results = PRESUBMIT.CheckChromeFeatureIsSorted(
            mock_input_api, MockOutputApi(), self.FILE_PATH, self.NAME,
            self.START_PATTERN, self.END_PATTERN)
        self.assertEqual([], results)

    def testUnsortedListFails(self):
        """
        Tests that an unsorted list of BASE_FEATUREs fails the check with
        a specific error.
        """
        # kAdaptiveFoo comes alphabetically after kCommerce
        feature_lines = [
            # Out of order
            'BASE_FEATURE(kCommerce, base::FEATURE_ENABLED_BY_DEFAULT);',
            'BASE_FEATURE(kAdaptiveFoo, base::FEATURE_ENABLED_BY_DEFAULT);',
            'BASE_FEATURE(kAndroidBar, base::FEATURE_ENABLED_BY_DEFAULT);',
            'BASE_FEATURE(kSomething, base::FEATURE_ENABLED_BY_DEFAULT);',
        ]
        content = self._generate_file_content(feature_lines)

        mock_input_api = MockInputApi()
        mock_input_api.files = [MockAffectedFile(self.FILE_PATH, content)]
        results = PRESUBMIT.CheckChromeFeatureIsSorted(
            mock_input_api, MockOutputApi(), self.FILE_PATH, self.NAME,
            self.START_PATTERN, self.END_PATTERN)

        self.assertEqual(1, len(results))
        self.assertEqual('error', results[0].type)
        self.assertIn("`BASE_FEATURE` values", results[0].message)
        self.assertIn("'BASE_FEATURE(kCommerce", results[0].message)
        self.assertIn("'BASE_FEATURE(kAdaptiveFoo", results[0].message)

    def testUnrelatedFileModifiedPasses(self):
        """Tests the check is skipped if the target file is not modified."""
        mock_input_api = MockInputApi()
        mock_input_api.files = [
            MockAffectedFile('some/other/file.cc', ['// No changes here'])
        ]
        results = PRESUBMIT.CheckChromeFeatureIsSorted(
            mock_input_api, MockOutputApi(), self.FILE_PATH, self.NAME,
            self.START_PATTERN, self.END_PATTERN)
        self.assertEqual([], results)

    def testCaseSensitiveSortFails(self):
        """Tests sorting fails correctly for case-insensitive misordering."""
        # 'A_feature' should come before 'a_feature' (case-insensitive)
        feature_lines = [
            # Out of order
            'BASE_FEATURE(a_feature, base::FEATURE_ENABLED_BY_DEFAULT);',
            'BASE_FEATURE(A_feature, base::FEATURE_ENABLED_BY_DEFAULT);',
            'BASE_FEATURE(B_feature, base::FEATURE_ENABLED_BY_DEFAULT);',
        ]
        content = self._generate_file_content(feature_lines)

        mock_input_api = MockInputApi()
        mock_input_api.files = [MockAffectedFile(self.FILE_PATH, content)]
        results = PRESUBMIT.CheckChromeFeatureIsSorted(
            mock_input_api, MockOutputApi(), self.FILE_PATH, self.NAME,
            self.START_PATTERN, self.END_PATTERN)

        self.assertEqual(1, len(results))
        self.assertIn("'BASE_FEATURE(A_feature", results[0].message)
        self.assertIn("'BASE_FEATURE(a_feature", results[0].message)


class CheckChromeFeatureDeclarationsHeaderIsSortedTest(unittest.TestCase):
    """
    Tests the CheckChromeFeatureIsSorted PRESUBMIT check for declarations.
    """
    FILE_PATH = 'chrome/browser/flags/android/chrome_feature_list.h'
    NAME = 'BASE_DECLARE_FEATURE'
    START_PATTERN = '// BASE_DECLARE_FEATURE_START'
    END_PATTERN = '// BASE_DECLARE_FEATURE_END'

    def _generate_file_content(self, declare_names):
        """
        Helper to generate mock file content with a list of
        BASE_DECLARE_FEATURE declarations.
        """
        content = [
            '// Some header content',
            '#define CHROME_BROWSER_FLAGS_ANDROID_CHROME_FEATURE_LIST_H_',
            'namespace features {',
            self.START_PATTERN,
        ]
        content.extend([f'BASE_DECLARE_FEATURE({d});' for d in declare_names])
        content.extend([
            self.END_PATTERN,
            '}  // namespace features',
        ])
        return content

    def testSortedListPasses(self):
        """
        Tests that a correctly sorted list of BASE_DECLARE_FEATUREs passes.
        """
        declare_names = [
            'kAdaptiveButtonInTopToolbarCustomizationV2',
            'kAndroidBrowserControlsInViz',
            'kCommerceMerchantViewer',
            'kSomeOtherFeature',
        ]
        content = self._generate_file_content(declare_names)

        mock_input_api = MockInputApi()
        mock_input_api.files = [MockAffectedFile(self.FILE_PATH, content)]
        results = PRESUBMIT.CheckChromeFeatureIsSorted(
            mock_input_api, MockOutputApi(), self.FILE_PATH, self.NAME,
            self.START_PATTERN, self.END_PATTERN)
        self.assertEqual([], results)

    def testUnsortedListFails(self):
        """
        Tests that an unsorted list of BASE_DECLARE_FEATUREs fails the check with
        a specific error.
        """
        # kCommerce comes alphabetically after kAndroid
        declare_names = [
            'kAdaptiveButtonInTopToolbarCustomizationV2',
            'kCommerce',  # Out of order
            'kAndroid',
            'kSomeOtherFeature',
        ]
        content = self._generate_file_content(declare_names)

        mock_input_api = MockInputApi()
        mock_input_api.files = [MockAffectedFile(self.FILE_PATH, content)]
        results = PRESUBMIT.CheckChromeFeatureIsSorted(
            mock_input_api, MockOutputApi(), self.FILE_PATH, self.NAME,
            self.START_PATTERN, self.END_PATTERN)

        self.assertEqual(1, len(results))
        self.assertEqual('error', results[0].type)
        self.assertIn("`BASE_DECLARE_FEATURE` values", results[0].message)
        self.assertIn("'BASE_DECLARE_FEATURE(kCommerce);'", results[0].message)
        self.assertIn("'BASE_DECLARE_FEATURE(kAndroid);'", results[0].message)


    def testUnrelatedFileModifiedPasses(self):
        """Tests the check is skipped if the target file is not modified."""
        mock_input_api = MockInputApi()
        mock_input_api.files = [
            MockAffectedFile('some/other/file.cc', ['// No changes here'])
        ]
        results = PRESUBMIT.CheckChromeFeatureIsSorted(
            mock_input_api, MockOutputApi(), self.FILE_PATH, self.NAME,
            self.START_PATTERN, self.END_PATTERN)
        self.assertEqual([], results)

    def testCaseSensitiveSortFails(self):
        """Tests sorting fails correctly for case-insensitive misordering."""
        # 'A_flag' should come before 'a_flag' (case-insensitive)
        declare_names = [
            'a_flag',
            'A_flag', # Out of order
            'c_flag',
        ]
        content = self._generate_file_content(declare_names)
        mock_input_api = MockInputApi()
        mock_input_api.files = [MockAffectedFile(self.FILE_PATH, content)]
        results = PRESUBMIT.CheckChromeFeatureIsSorted(
            mock_input_api, MockOutputApi(), self.FILE_PATH, self.NAME,
            self.START_PATTERN, self.END_PATTERN)

        self.assertEqual(1, len(results))
        self.assertIn("'BASE_DECLARE_FEATURE(A_flag);'", results[0].message)
        self.assertIn("'BASE_DECLARE_FEATURE(a_flag);'", results[0].message)


if __name__ == '__main__':
    unittest.main()