123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- #!/usr/bin/env vpython3
- # Copyright (c) 2024 The Chromium Authors. All rights reserved.
- # Use of this source code is governed by a BSD-style license that can be
- # found in the LICENSE file.
- """Unit tests for presubmit_diff.py."""
- import os
- import sys
- import tempfile
- import unittest
- from typing import Dict, List
- from unittest import mock
- sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
- import gclient_utils
- import presubmit_diff
- class PresubmitDiffTest(unittest.TestCase):
- def setUp(self):
- # State of the local directory.
- self.root = tempfile.mkdtemp()
- os.makedirs(os.path.join(self.root, "nested"))
- # On Windows, writing "\n" in text mode becomes "\r\n". Write in binary
- # so that doesn't happen, otherwise tests will fail.
- with open(os.path.join(self.root, "unchanged.txt"), "wb") as f:
- f.write("unchanged\n".encode("utf-8"))
- with open(os.path.join(self.root, "added.txt"), "wb") as f:
- f.write("added\n".encode("utf-8"))
- with open(os.path.join(self.root, "modified.txt"), "wb") as f:
- f.write("modified... foo\n".encode("utf-8"))
- with open(os.path.join(self.root, "nested/modified.txt"), "wb") as f:
- f.write("goodbye\n".encode("utf-8"))
- # State of the remote repository.
- fetch_data = {
- "unchanged.txt": "unchanged\n".encode("utf-8"),
- "deleted.txt": "deleted\n".encode("utf-8"),
- "modified.txt": "modified... bar\n".encode("utf-8"),
- "nested/modified.txt": "hello\n".encode("utf-8"),
- # Intenionally invalid start byte for utf-8.
- "deleted_binary": b"\xff\x00",
- }
- def fetch_side_effect(host, repo, ref, file):
- return fetch_data.get(file, b"")
- fetch_content_mock = mock.patch("presubmit_diff.fetch_content",
- side_effect=fetch_side_effect)
- fetch_content_mock.start()
- self.addCleanup(mock.patch.stopall)
- def tearDown(self):
- gclient_utils.rmtree(self.root)
- def _test_create_diffs(self, files: List[str], expected: Dict[str, str]):
- actual = presubmit_diff.create_diffs("host", "repo", "ref", self.root,
- files)
- self.assertEqual(actual.keys(), expected.keys())
- # Manually check each line in the diffs except the "index" line because
- # hashes can differ in length.
- for file, diff in actual.items():
- expected_lines = expected[file].splitlines()
- for idx, line in enumerate(diff.splitlines()):
- if line.startswith("index "):
- continue
- self.assertEqual(line, expected_lines[idx])
- def test_create_diffs_with_nonexistent_file_raises_error(self):
- self.assertRaises(
- RuntimeError,
- presubmit_diff.create_diffs,
- "host",
- "repo",
- "ref",
- self.root,
- ["doesnotexist.txt"],
- )
- def test_create_diffs_with_unchanged_file(self):
- self._test_create_diffs(
- ["unchanged.txt"],
- {"unchanged.txt": ""},
- )
- @mock.patch('subprocess2.capture', return_value="".encode("utf-8"))
- def test_create_diffs_executes_git_diff_with_unified(self, capture):
- create_diffs = presubmit_diff.create_diffs
- # None => no -U
- create_diffs("host", "repo", "ref", self.root, ["unchanged.txt"], None)
- capture.assert_called_with(
- ["git", "diff", "--no-index", "--", mock.ANY, mock.ANY])
- # 0 => -U0
- create_diffs("host", "repo", "ref", self.root, ["unchanged.txt"], 0)
- capture.assert_called_with(
- ["git", "diff", "--no-index", "-U0", "--", mock.ANY, mock.ANY])
- # 3 => -U3
- create_diffs("host", "repo", "ref", self.root, ["unchanged.txt"], 3)
- capture.assert_called_with(
- ["git", "diff", "--no-index", "-U3", "--", mock.ANY, mock.ANY])
- def test_create_diffs_with_added_file(self):
- expected_diff = """diff --git a/added.txt b/added.txt
- new file mode 100644
- index 00000000..d5f7fc3f
- --- /dev/null
- +++ b/added.txt
- @@ -0,0 +1 @@
- +added
- """
- self._test_create_diffs(
- ["added.txt"],
- {"added.txt": expected_diff},
- )
- def test_create_diffs_with_deleted_file(self):
- expected_diff = """diff --git a/deleted.txt b/deleted.txt
- deleted file mode 100644
- index 71779d2c..00000000
- --- a/deleted.txt
- +++ /dev/null
- @@ -1 +0,0 @@
- -deleted
- """
- self._test_create_diffs(
- ["deleted.txt"],
- {"deleted.txt": expected_diff},
- )
- def test_create_diffs_with_binary_file(self):
- expected_diff = """diff --git a/deleted_binary b/deleted_binary
- deleted file mode 100644
- index ce542efaa..00000000
- Binary files a/deleted_binary and /dev/null differ
- """
- self._test_create_diffs(
- ["deleted_binary"],
- {"deleted_binary": expected_diff},
- )
- # pylint: disable=line-too-long
- def test_create_diffs_with_modified_files(self):
- expected_diff = """diff --git a/modified.txt b/modified.txt
- index a7dd0b00..12d68703 100644
- --- a/modified.txt
- +++ b/modified.txt
- @@ -1 +1 @@
- -modified... bar
- +modified... foo
- """
- expected_nested_diff = """diff --git a/nested/modified.txt b/nested/modified.txt
- index ce013625..dd7e1c6f 100644
- --- a/nested/modified.txt
- +++ b/nested/modified.txt
- @@ -1 +1 @@
- -hello
- +goodbye
- """
- self._test_create_diffs(
- ["modified.txt", "nested/modified.txt"],
- {
- "modified.txt": expected_diff,
- "nested/modified.txt": expected_nested_diff,
- },
- )
- # Test cases for _process_diff.
- def test_process_diff_with_no_changes(self):
- self.assertEqual(
- presubmit_diff._process_diff(
- "",
- "/path/to/src",
- "/path/to/dst",
- ),
- "",
- )
- @mock.patch("platform.system", return_value="Linux")
- @mock.patch("os.sep", new="/")
- def test_process_diff_handles_unix_paths(self, sys_mock):
- diff = """diff --git a/path/to/src/file.txt b/path/to/dst/file.txt
- index ce013625..dd7e1c6f 100644
- --- a/path/to/file.txt
- +++ b/path/to/file.txt
- @@ -1 +1 @@
- -random
- +content
- """
- expected = """diff --git a/file.txt b/file.txt
- index ce013625..dd7e1c6f 100644
- --- a/path/to/file.txt
- +++ b/path/to/file.txt
- @@ -1 +1 @@
- -random
- +content
- """
- self.assertEqual(
- presubmit_diff._process_diff(
- diff,
- "/path/to/src",
- "/path/to/dst",
- ),
- expected,
- )
- # Trailing slashes are handled.
- self.assertEqual(
- presubmit_diff._process_diff(
- diff,
- "/path/to/src/",
- "/path/to/dst/",
- ),
- expected,
- )
- @mock.patch("platform.system", return_value="Windows")
- @mock.patch("os.sep", new="\\")
- def test_process_diff_handles_windows_paths(self, sys_mock):
- diff = """diff --git "a/C:\\\\path\\\\to\\\\src\\\\file.txt" "b/C:\\\\path\\\\to\\\\dst\\\\file.txt"
- index ce013625..dd7e1c6f 100644
- --- "a/C:\\\\path\\\\to\\\\src\\\\file.txt
- +++ "b/C:\\\\path\\\\to\\\\dst\\\\file.txt"
- @@ -1 +1 @@
- -random
- +content
- """
- expected = """diff --git a/file.txt b/file.txt
- index ce013625..dd7e1c6f 100644
- --- a/file.txt
- +++ b/file.txt
- @@ -1 +1 @@
- -random
- +content
- """
- self.assertEqual(
- expected,
- presubmit_diff._process_diff(diff, "C:\\path\\to\\src",
- "C:\\path\\to\\dst"),
- )
- # Trailing slashes are handled.
- self.assertEqual(
- expected,
- presubmit_diff._process_diff(diff, "C:\\path\\to\\src\\",
- "C:\\path\\to\\dst\\"),
- )
- @mock.patch("platform.system", return_value="Linux")
- def test_process_diff_without_chunk_header(self, sys_mock):
- diff = """diff --git a/path/to/src/file.txt b/path/to/dst/file.txt
- old mode 100644
- new mode 100755
- """
- expected = """diff --git a/file.txt b/file.txt
- old mode 100644
- new mode 100755
- """
- self.assertEqual(
- presubmit_diff._process_diff(
- diff,
- "/path/to/src",
- "/path/to/dst",
- ),
- expected,
- )
- if __name__ == "__main__":
- unittest.main()
|