| #!/usr/bin/env python3 |
| # 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 unittest |
| import os |
| import tempfile |
| import shutil |
| |
| import bedrock_metrics |
| |
| |
| class TestBedrockMetrics(unittest.TestCase): |
| |
| def setUp(self): |
| # Create a temporary directory for test files. |
| self.test_dir = tempfile.mkdtemp(prefix="bedrock_test_") |
| self.sub_dir = os.path.join(self.test_dir, "sub") |
| os.makedirs(self.sub_dir) |
| |
| # --- Files for count_matching_files --- |
| # Structure: |
| # self.test_dir/ |
| # - file_alpha.h (Content: "apple banana\ncount_me_once") |
| # - file_beta.cc (Content: "banana cherry\ncount_me_once\ncount_me_once") |
| # - file_gamma.mm (Content: "apple cherry\nno_count_string") |
| # - sub/ |
| # - file_delta.h (Content: "apple\ncount_me_once") |
| # - file_exclude_me.cc (Content: "apple banana\ncontent for exclude") |
| # - other_extension.txt (Content: "apple banana\ntext file content") |
| # - empty_file.h (Content: "") |
| files_to_create_for_matching = [ |
| ("file_alpha.h", "apple banana\ncount_me_once"), |
| ("file_beta.cc", "banana cherry\ncount_me_once\ncount_me_once"), |
| ("file_gamma.mm", "apple cherry\nno_count_string"), |
| (os.path.join("sub", "file_delta.h"), "apple\ncount_me_once"), |
| ("file_exclude_me.cc", "apple banana\ncontent for exclude"), |
| ("other_extension.txt", |
| "apple banana\ntext file content"), # Invalid extension |
| ("empty_file.h", "") |
| ] |
| |
| for rel_path, content in files_to_create_for_matching: |
| full_path = os.path.join(self.test_dir, rel_path) |
| os.makedirs(os.path.dirname(full_path), exist_ok=True) |
| with open(full_path, 'w', encoding='utf-8') as f: |
| f.write(content) |
| |
| # --- Files for count_lines --- |
| self.lines_file1_path = os.path.join(self.test_dir, "lines_file1.data") |
| with open(self.lines_file1_path, 'w', encoding='utf-8') as f: |
| f.write("line1\nline2\nline3") # 3 lines |
| |
| self.lines_file2_path = os.path.join(self.test_dir, "lines_file2.data") |
| with open(self.lines_file2_path, 'w', encoding='utf-8') as f: |
| f.write("single line") # 1 line |
| |
| self.lines_empty_file_path = os.path.join(self.test_dir, "lines_empty.data") |
| with open(self.lines_empty_file_path, 'w', encoding='utf-8') as f: |
| f.write("") # 0 lines |
| |
| def tearDown(self): |
| # Remove the temporary directory and its contents after tests. |
| shutil.rmtree(self.test_dir) |
| |
| # --- Tests for count_matching_files --- |
| |
| def test_cmf_no_filters_counts_valid_extensions(self): |
| # Expected: file_alpha.h, file_beta.cc, file_gamma.mm, sub/file_delta.h, |
| # file_exclude_me.cc, empty_file.h (6 files with .h, .cc, .mm) |
| # other_extension.txt is ignored due to extension. |
| # No content_match_strings, so string_matches_count is 0. |
| count, str_count = bedrock_metrics.count_matching_files(self.test_dir) |
| self.assertEqual(count, 6, "File count mismatch with no filters") |
| self.assertEqual(str_count, 0, |
| "String count should be 0 with no content search strings") |
| |
| def test_cmf_include_filename_single(self): |
| # include_filename_strings=["alpha"] -> matches file_alpha.h |
| count, str_count = bedrock_metrics.count_matching_files( |
| self.test_dir, include_filename_strings=["alpha"]) |
| self.assertEqual(count, 1) |
| self.assertEqual(str_count, 0) |
| |
| def test_cmf_include_filename_multiple_all_match(self): |
| # include_filename_strings=["file", "alpha"] -> matches file_alpha.h |
| count, str_count = bedrock_metrics.count_matching_files( |
| self.test_dir, include_filename_strings=["file", "alpha"]) |
| self.assertEqual(count, 1) |
| self.assertEqual(str_count, 0) |
| |
| def test_cmf_include_filename_multiple_one_no_match(self): |
| # include_filename_strings=["alpha", "beta"] -> No file has BOTH "alpha" |
| # AND"beta". |
| count, str_count = bedrock_metrics.count_matching_files( |
| self.test_dir, include_filename_strings=["alpha", "nomatch"]) |
| self.assertEqual(count, 0) |
| self.assertEqual(str_count, 0) |
| |
| def test_cmf_exclude_filename_single(self): |
| # exclude_filename_strings=["exclude"] -> Excludes file_exclude_me.cc |
| # Original 6 valid files - 1 = 5 files. |
| count, str_count = bedrock_metrics.count_matching_files( |
| self.test_dir, exclude_filename_strings=["exclude"]) |
| self.assertEqual(count, 5) |
| self.assertEqual(str_count, 0) |
| |
| # These tests verify the current code behavior (ANY). |
| def test_cmf_include_file_content_strings_any_behavior_one_present(self): |
| # include_file_content_strings=["apple"] |
| # Matches: file_alpha.h, file_gamma.mm, sub/file_delta.h, file_exclude_me.cc |
| # (4 files) |
| # file_beta.cc (banana cherry) does not contain "apple". empty_file.h is |
| # empty. |
| count, str_count = bedrock_metrics.count_matching_files( |
| self.test_dir, include_file_content_strings=["apple"]) |
| self.assertEqual(count, 4) |
| self.assertEqual(str_count, 0) |
| |
| def test_cmf_include_file_content_strings_any_behavior_multiple_options(self): |
| # include_file_content_strings=["apple", "cherry"] (any of these in content) |
| # file_alpha.h ("apple") - Yes |
| # file_beta.cc ("cherry") - Yes |
| # file_gamma.mm ("apple", "cherry") - Yes |
| # sub/file_delta.h ("apple") - Yes |
| # file_exclude_me.cc ("apple") - Yes |
| # empty_file.h - No |
| # Expected: 5 files |
| count, str_count = bedrock_metrics.count_matching_files( |
| self.test_dir, include_file_content_strings=["apple", "cherry"]) |
| self.assertEqual(count, 5) |
| self.assertEqual(str_count, 0) |
| |
| def test_cmf_include_file_content_strings_none_present_in_content(self): |
| # include_file_content_strings=["nonexistent_string_pattern"] |
| count, str_count = bedrock_metrics.count_matching_files( |
| self.test_dir, |
| include_file_content_strings=["nonexistent_string_pattern"]) |
| self.assertEqual(count, 0) |
| self.assertEqual(str_count, 0) |
| |
| def test_cmf_content_match_strings_single_term_count(self): |
| # content_match_strings=["count_me_once"] |
| # All 6 valid files are checked for content counts. |
| # file_alpha.h: 1 | file_beta.cc: 2 | file_gamma.mm: 0 |
| # sub/file_delta.h: 1 | file_exclude_me.cc: 0 | empty_file.h: 0 |
| # Total string matches: 1 + 2 + 0 + 1 + 0 + 0 = 4 |
| count, str_count = bedrock_metrics.count_matching_files( |
| self.test_dir, content_match_strings=["count_me_once"]) |
| self.assertEqual( |
| count, 6, |
| "File count should include all valid files for content counting") |
| self.assertEqual(str_count, 4, "String match count error") |
| |
| def test_cmf_content_match_strings_multiple_terms_count(self): |
| # content_match_strings=["apple", "banana"] |
| # All 6 valid files are checked. |
| # file_alpha.h ("apple banana"): apple(1)+banana(1)=2 |
| # file_beta.cc ("banana cherry"): apple(0)+banana(1)=1 |
| # file_gamma.mm ("apple cherry"): apple(1)+banana(0)=1 |
| # sub/file_delta.h ("apple"): apple(1)+banana(0)=1 |
| # file_exclude_me.cc ("apple banana"): apple(1)+banana(1)=2 |
| # empty_file.h: apple(0)+banana(0)=0 |
| # Total string matches: 2+1+1+1+2+0 = 7 |
| count, str_count = bedrock_metrics.count_matching_files( |
| self.test_dir, content_match_strings=["apple", "banana"]) |
| self.assertEqual(count, 6) |
| self.assertEqual(str_count, 7) |
| |
| def test_cmf_all_filters_combined(self): |
| # include_filename_strings=["file"], exclude_filename_strings=["exclude"] |
| # include_file_content_strings=["apple"] (content filter, using 'any' logic |
| # of code) |
| # content_match_strings=["count_me_once"] |
| |
| # 1. Include "file": file_alpha.h, file_beta.cc, file_gamma.mm, |
| # file_exclude_me.cc, empty_file.h |
| # 2. Exclude "exclude": Removes file_exclude_me.cc. |
| # Remaining: file_alpha.h, file_beta.cc, file_gamma.mm, empty_file.h |
| # 3. include_file_content_strings=["apple"]: (file must contain "apple") |
| # file_alpha.h (has "apple") - Keep |
| # file_beta.cc (no "apple") - Remove |
| # file_gamma.mm (has "apple") - Keep |
| # sub/file_delta.h (has "apple"): - Keep |
| # empty_file.h (no "apple") - Remove |
| # Matched files: file_alpha.h, file_gamma.mm. So, count = 2. |
| # 4. content_match_strings=["count_me_once"] on these 2 files: |
| # file_alpha.h: 1 occurrence |
| # file_gamma.mm: 0 occurrences |
| # sub/file_delta.h (has "apple"): 1 occurrences |
| # Total str_count = 1. |
| count, str_count = bedrock_metrics.count_matching_files( |
| self.test_dir, |
| include_file_content_strings=["apple"], |
| content_match_strings=["count_me_once"], |
| include_filename_strings=["file"], |
| exclude_filename_strings=["exclude"]) |
| self.assertEqual(count, 3) |
| self.assertEqual(str_count, 2) |
| |
| def test_cmf_empty_directory(self): |
| empty_subdir_path = os.path.join(self.test_dir, "empty_subdir_for_test") |
| os.makedirs(empty_subdir_path) |
| count, str_count = bedrock_metrics.count_matching_files(empty_subdir_path) |
| self.assertEqual(count, 0) |
| self.assertEqual(str_count, 0) |
| |
| def test_cmf_no_matching_extension_files_present(self): |
| # Directory with only a .txt file, which is not in [.h, .cc, .mm] |
| other_ext_dir = os.path.join(self.test_dir, "other_ext_dir") |
| os.makedirs(other_ext_dir) |
| with open(os.path.join(other_ext_dir, "somefile.txt"), 'w') as f: |
| f.write("content") |
| count, str_count = bedrock_metrics.count_matching_files(other_ext_dir) |
| self.assertEqual(count, 0) |
| self.assertEqual(str_count, 0) |
| |
| def test_cmf_include_file_content_strings_is_empty_list(self): |
| # An empty list for include_file_content_strings means the content filter |
| # (Step 4) is skipped. |
| # Same as include_file_content_strings=None. |
| count, str_count = bedrock_metrics.count_matching_files( |
| self.test_dir, include_file_content_strings=[]) |
| self.assertEqual(count, 6) # All 6 valid extension files |
| self.assertEqual(str_count, 0) |
| |
| def test_cmf_content_match_strings_is_empty_list(self): |
| # No strings to count, so str_count should be 0. |
| # File count reflects all files matching other criteria. |
| count, str_count = bedrock_metrics.count_matching_files( |
| self.test_dir, content_match_strings=[]) |
| self.assertEqual(count, 6) # All 6 valid extension files |
| self.assertEqual(str_count, 0) |
| |
| def test_cmf_content_match_strings_includes_empty_string(self): |
| # The code `if s_str:` skips empty strings in content_match_strings. |
| # So, ["count_me_once", ""] should behave like ["count_me_once"]. |
| count, str_count = bedrock_metrics.count_matching_files( |
| self.test_dir, content_match_strings=["count_me_once", ""]) |
| self.assertEqual(count, 6) |
| self.assertEqual(str_count, 4) # Same as just ["count_me_once"] |
| |
| # --- Tests for count_lines --- |
| |
| def test_cl_single_file_multiple_lines(self): |
| self.assertEqual(bedrock_metrics.count_lines([self.lines_file1_path]), 3) |
| |
| def test_cl_single_file_one_line_no_trailing_newline(self): |
| self.assertEqual(bedrock_metrics.count_lines([self.lines_file2_path]), 1) |
| |
| def test_cl_multiple_files(self): |
| total_lines = bedrock_metrics.count_lines( |
| [self.lines_file1_path, self.lines_file2_path]) |
| self.assertEqual(total_lines, 3 + 1) # 4 |
| |
| def test_cl_empty_file(self): |
| # An empty file has 0 lines according to file.readlines(). |
| self.assertEqual(bedrock_metrics.count_lines([self.lines_empty_file_path]), |
| 0) |
| |
| def test_cl_mixed_files_including_empty(self): |
| total_lines = bedrock_metrics.count_lines([ |
| self.lines_file1_path, self.lines_empty_file_path, self.lines_file2_path |
| ]) |
| self.assertEqual(total_lines, 3 + 0 + 1) # 4 |
| |
| def test_cl_empty_file_list(self): |
| self.assertEqual(bedrock_metrics.count_lines([]), 0) |
| |
| |
| if __name__ == '__main__': |
| unittest.main(verbosity=2) |