You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
depot_tools/tests/presubmit_diff_test.py

277 lines
8.4 KiB
Python

#!/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()