blob: 0c2c14c4b6877362bdcf5ba3da206267cd0bcbd7 [file] [log] [blame]
#!/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 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"))
with open(os.path.join(self.root, "unchanged.txt"), "w") as f:
f.write("unchanged\n")
with open(os.path.join(self.root, "added.txt"), "w") as f:
f.write("added\n")
with open(os.path.join(self.root, "modified.txt"), "w") as f:
f.write("modified... foo\n")
with open(os.path.join(self.root, "nested/modified.txt"), "w") as f:
f.write("goodbye\n")
# State of the remote repository.
fetch_data = {
"unchanged.txt": "unchanged\n",
"deleted.txt": "deleted\n",
"modified.txt": "modified... bar\n",
"nested/modified.txt": "hello\n",
}
def fetch_side_effect(host, repo, ref, file):
return fetch_data.get(file, "")
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": ""},
)
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},
)
# 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()