910e62b5创建于 1月15日历史提交
#!/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
from commit import Commit
from datetime import datetime
from test_data import FAKE_GIT_LOG
from gitutils import split_log_into_commits


class CommitTest(unittest.TestCase):

    def setUp(self):
        """Sets up a new Commit object for each test."""
        # Pass an empty string for initialization, as we are testing
        # analyse_line directly.
        self.commit = Commit('')

    def test_analyse_line_author(self):
        """Tests that an author line is correctly parsed."""
        line = 'Author: Test User <test.user@chromium.org>'
        self.commit.analyse_line(line)
        self.assertEqual(self.commit.author, 'test.user')

    def test_analyse_line_reviewer(self):
        """Tests that a reviewer line is correctly parsed."""
        line = '    Reviewed-by: Reviewer One <reviewer.one@chromium.org>'
        self.commit.analyse_line(line)
        self.assertIn('reviewer.one', self.commit.reviewers)

    def test_analyse_line_multiple_reviewers(self):
        """Tests that multiple reviewer lines are correctly parsed."""
        lines = [
            '    Reviewed-by: Reviewer One <reviewer.one@chromium.org>',
            '    Reviewed-by: Reviewer Two <reviewer.two@chromium.org>'
        ]
        for line in lines:
            self.commit.analyse_line(line)
        self.assertIn('reviewer.one', self.commit.reviewers)
        self.assertIn('reviewer.two', self.commit.reviewers)
        self.assertEqual(len(self.commit.reviewers), 2)

    def test_analyse_line_date(self):
        """Tests that a date line is correctly parsed."""
        # This is a sample line from `git log` output.
        line = 'Date:   Tue Sep 2 15:30:00 2025 -0700'
        self.commit.analyse_line(line)
        expected_date = datetime(2025, 9, 2, 15, 30, 0)
        self.assertEqual(self.commit.date, expected_date)

    def test_analyse_line_file_change(self):
        """Tests that a file change line is correctly parsed."""
        line = ' ios/chrome/browser/ui/some_file.mm | 10 +++++-----'
        self.commit.analyse_line(line)
        self.assertEqual(self.commit.total_change, 10)
        self.assertIn('ios/chrome/browser/ui', self.commit.files_stats)
        self.assertEqual(self.commit.files_stats['ios/chrome/browser/ui'], 10)

    def test_analyse_line_file_change_with_ellipsis(self):
        """Tests parsing a file change line with an ellipsis prefix."""
        line = ' .../browser/ui/another_file.h | 5 +++++'
        self.commit.analyse_line(line)
        self.assertEqual(self.commit.total_change, 5)
        self.assertIn('.../browser/ui', self.commit.files_stats)
        self.assertEqual(self.commit.files_stats['.../browser/ui'], 5)

    def test_analyse_line_file_change_unittest_file_is_not_ignored(self):
        """Tests that file changes in unittest files are not ignored."""
        line = ' ios/chrome/browser/ui/some_file_unittest.mm | 10 +++++-----'
        self.commit.analyse_line(line)
        self.assertEqual(self.commit.total_change, 10)
        self.assertIn('ios/chrome/browser/ui', self.commit.files_stats)

    def test_analyse_line_irrelevant_line(self):
        """Tests that an irrelevant line does not alter the commit data."""
        line = 'This is a commit message body.'
        self.commit.analyse_line(line)
        self.assertEqual(self.commit.author, '')
        self.assertEqual(len(self.commit.reviewers), 0)
        self.assertEqual(self.commit.date, datetime.min)
        self.assertEqual(self.commit.total_change, 0)
        self.assertEqual(len(self.commit.files_stats), 0)

    def test_analyse_line_file_change_long_extension(self):
        """Tests that a file with a long extension is parsed correctly."""
        line = ' ios/chrome/browser/ui/some_file.swift | 7 +++++--'
        self.commit.analyse_line(line)
        self.assertEqual(self.commit.total_change, 7)
        self.assertIn('ios/chrome/browser/ui', self.commit.files_stats)
        self.assertEqual(self.commit.files_stats['ios/chrome/browser/ui'], 7)

    def test_analyse_line_binary_file_change_ignored(self):
        """Tests that changes to binary files are ignored."""
        line = ' ios/chrome/browser/ui/icon.png | Bin 1024 -> 2048 bytes'
        self.commit.analyse_line(line)
        self.assertEqual(self.commit.total_change, 0)
        self.assertEqual(len(self.commit.files_stats), 0)

    def test_determine_modified_folder_finds_common_parent(self):
        """Tests that the nearest parent is chosen for distributed changes."""
        commit_description = """commit 12345
Author: Test User <test.user@chromium.org>
Date:   Tue Sep 2 15:30:00 2025 -0700

    A sample commit.

 ios/chrome/browser/feature1/file.mm | 10 +++++-----
 ios/chrome/browser/feature2/file.mm | 10 +++++-----
"""
        commit = Commit(commit_description)
        # The common parent 'ios/chrome/browser' has 20 changes (100%)
        # and should be selected.
        self.assertEqual(commit.modified_path, 'ios/chrome/browser')

    def test_analyse_line_file_change_with_realistic_spacing(self):
        """Tests a realistic git log line with varied spacing."""
        line = ' ios/chrome/browser/ui/file1.mm    |   10 +++++-----'
        self.commit.analyse_line(line)
        self.assertEqual(self.commit.total_change, 10)
        self.assertIn('ios/chrome/browser/ui', self.commit.files_stats)

    def test_extend_paths_aggregation(self):
        """Tests that extend_paths correctly aggregates changes up the tree."""
        self.commit.files_stats = {
            'ios/feature_a/ui': 10,
            'ios/feature_a/data': 5,
            'ios/feature_b': 20
        }
        extended = self.commit.extend_paths()

        self.assertEqual(extended.get('ios/feature_a/ui'), 10)
        self.assertEqual(extended.get('ios/feature_a/data'), 5)
        self.assertEqual(extended.get('ios/feature_b'), 20)
        self.assertEqual(extended.get('ios/feature_a'), 15)
        self.assertEqual(extended.get('ios'), 35)
        self.assertIsNone(extended.get(''))  # Root should not be present.

    def test_analyse_line_path_with_test_substring_is_not_ignored(self):
        """Tests that a path with 'test' as a substring is not ignored."""
        # The old logic would incorrectly ignore this path.
        line = ' ios/chrome/browser/attestation/file.mm | 4 ++--'
        self.commit.analyse_line(line)
        self.assertEqual(self.commit.total_change, 4)
        self.assertIn('ios/chrome/browser/attestation',
                      self.commit.files_stats)

    def test_analyse_line_path_in_test_dir_is_ignored(self):
        """Tests that a path in a directory named 'test' is ignored."""
        line = ' ios/chrome/browser/ui/test/file.mm | 10 +++++-----'
        self.commit.analyse_line(line)
        self.assertEqual(self.commit.total_change, 0)
        self.assertEqual(len(self.commit.files_stats), 0)

    def test_analyse_line_skipping_tests_disabled(self):
        """Tests that test paths are not ignored if skipping is disabled."""
        commit = Commit('', skip_tests=False)
        line = ' ios/chrome/browser/ui/test/file.mm | 10 +++++-----'
        commit.analyse_line(line)
        self.assertEqual(commit.total_change, 10)
        self.assertIn('ios/chrome/browser/ui/test', commit.files_stats)

    def test_analyse_line_unindented_reviewer_is_parsed(self):
        """Tests that a reviewer line with no indentation is parsed."""
        line = 'Reviewed-by: Reviewer One <reviewer.one@chromium.org>'
        self.commit.analyse_line(line)
        self.assertIn('reviewer.one', self.commit.reviewers)

    def test_commit_parsing_with_fake_git_log(self):
        """Tests the Commit class with a realistic commit message."""
        # The first commit from our test corpus.
        commits = split_log_into_commits(FAKE_GIT_LOG)
        self.assertGreater(len(commits), 0, "FAKE_GIT_LOG was not split")
        commit_text = commits[0]

        commit = Commit(commit_text)
        self.assertEqual(commit.author, 'user_a')
        self.assertIn('user_b', commit.reviewers)
        self.assertEqual(commit.total_change, 10)
        self.assertIn('ios/chrome/browser/feature', commit.files_stats)


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