From d711d35913b797817799fe3ec66d9bd26c068c2b Mon Sep 17 00:00:00 2001 From: James Graham Date: Wed, 7 Apr 2021 12:22:29 +0100 Subject: [PATCH] Add a lint that checks paths are unique irrespective of case Paths that are the same excluding case don't work reliably across operating systems and can be forbidden by vendor CI. So we should error on these paths in the lint. --- tools/lint/lint.py | 17 ++++++++++++++++- tools/lint/rules.py | 8 ++++++++ tools/lint/tests/test_path_lints.py | 14 +++++++++++++- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/tools/lint/lint.py b/tools/lint/lint.py index d8fb9fc35ef38d..6645e99e4f9f8a 100644 --- a/tools/lint/lint.py +++ b/tools/lint/lint.py @@ -396,6 +396,20 @@ def check_unique_testharness_basenames(repo_root, paths): return errors +def check_unique_case_insensitive_paths(repo_root, paths): + # type: (Text, List[Text]) -> List[rules.Error] + seen = {} # type: Dict[Text, Text] + errors = [] + for path in paths: + lower_path = path.lower() + if lower_path in seen: + context = (seen[lower_path],) + errors.append(rules.DuplicatePathCaseInsensitive.error(path, context)) + else: + seen[lower_path] = path + return errors + + def parse_ignorelist(f): # type: (IO[Text]) -> Tuple[Ignorelist, Set[Text]] """ @@ -1107,7 +1121,8 @@ def process_errors(errors): path_lints = [check_file_type, check_path_length, check_worker_collision, check_ahem_copy, check_mojom_js, check_tentative_directories, check_gitignore_file] -all_paths_lints = [check_css_globally_unique, check_unique_testharness_basenames] +all_paths_lints = [check_css_globally_unique, check_unique_testharness_basenames, + check_unique_case_insensitive_paths] file_lints = [check_regexp_line, check_parsed, check_python_ast, check_script_metadata, check_ahem_system_font] diff --git a/tools/lint/rules.py b/tools/lint/rules.py index e9bb30b59cff07..125adb07d7f884 100644 --- a/tools/lint/rules.py +++ b/tools/lint/rules.py @@ -359,6 +359,14 @@ class DuplicateBasenamePath(Rule): to_fix = "rename files so they have unique basename paths" +class DuplicatePathCaseInsensitive(Rule): + name = "DUPLICATE-CASE-INSENSITIVE-PATH" + description = collapse(""" + Path differs from path %s only in case + """) + to_fix = "rename files so they are unique irrespective of case" + + class TentativeDirectoryName(Rule): name = "TENTATIVE-DIRECTORY-NAME" description = "Directories for tentative tests must be named exactly 'tentative'" diff --git a/tools/lint/tests/test_path_lints.py b/tools/lint/tests/test_path_lints.py index 9fefb7a1d7e061..3706a952c61f6a 100644 --- a/tools/lint/tests/test_path_lints.py +++ b/tools/lint/tests/test_path_lints.py @@ -3,7 +3,7 @@ import mock import os -from ..lint import check_path +from ..lint import check_path, check_unique_case_insensitive_paths from .base import check_errors import pytest @@ -153,3 +153,15 @@ def test_gitignore_negative(path): errors = check_path("/foo/", path) assert errors == [] + + +@pytest.mark.parametrize("paths,errors", + [(["a/b.html", "a/B.html"], ["a/B.html"]), + (["A/b.html", "a/b.html"], ["a/b.html"]), + (["a/b.html", "a/c.html"], [])]) +def test_unique_case_insensitive_paths(paths, errors): + got_errors = check_unique_case_insensitive_paths(None, paths) + assert len(got_errors) == len(errors) + for (name, _, path, _), expected_path in zip(got_errors, errors): + assert name == "DUPLICATE-CASE-INSENSITIVE-PATH" + assert path == expected_path