summaryrefslogtreecommitdiffstats
path: root/python/mozversioncontrol/test
diff options
context:
space:
mode:
Diffstat (limited to 'python/mozversioncontrol/test')
-rw-r--r--python/mozversioncontrol/test/conftest.py84
-rw-r--r--python/mozversioncontrol/test/python.ini10
-rw-r--r--python/mozversioncontrol/test/test_branch.py57
-rw-r--r--python/mozversioncontrol/test/test_commit.py72
-rw-r--r--python/mozversioncontrol/test/test_context_manager.py28
-rw-r--r--python/mozversioncontrol/test/test_push_to_try.py81
-rw-r--r--python/mozversioncontrol/test/test_update.py63
-rw-r--r--python/mozversioncontrol/test/test_workdir_outgoing.py108
-rw-r--r--python/mozversioncontrol/test/test_working_directory.py46
9 files changed, 549 insertions, 0 deletions
diff --git a/python/mozversioncontrol/test/conftest.py b/python/mozversioncontrol/test/conftest.py
new file mode 100644
index 0000000000..78e5ad7ca8
--- /dev/null
+++ b/python/mozversioncontrol/test/conftest.py
@@ -0,0 +1,84 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+import shutil
+import subprocess
+from pathlib import Path
+
+import pytest
+
+SETUP = {
+ "hg": [
+ """
+ echo "foo" > foo
+ echo "bar" > bar
+ hg init
+ hg add *
+ hg commit -m "Initial commit"
+ hg phase --public .
+ """,
+ """
+ echo [paths] > .hg/hgrc
+ echo "default = ../remoterepo" >> .hg/hgrc
+ """,
+ ],
+ "git": [
+ """
+ echo "foo" > foo
+ echo "bar" > bar
+ git init
+ git config user.name "Testing McTesterson"
+ git config user.email "<test@example.org>"
+ git add *
+ git commit -am "Initial commit"
+ """,
+ """
+ git remote add upstream ../remoterepo
+ git fetch upstream
+ git branch -u upstream/master
+ """,
+ ],
+}
+
+
+class RepoTestFixture:
+ def __init__(self, repo_dir: Path, vcs: str, steps: [str]):
+ self.dir = repo_dir
+ self.vcs = vcs
+
+ # This creates a step iterator. Each time execute_next_step()
+ # is called the next set of instructions will be executed.
+ self.steps = (shell(cmd, self.dir) for cmd in steps)
+
+ def execute_next_step(self):
+ next(self.steps)
+
+
+def shell(cmd, working_dir):
+ for step in cmd.split(os.linesep):
+ subprocess.check_call(step, shell=True, cwd=working_dir)
+
+
+@pytest.fixture(params=["git", "hg"])
+def repo(tmpdir, request):
+ tmpdir = Path(tmpdir)
+ vcs = request.param
+ steps = SETUP[vcs]
+
+ if hasattr(request.module, "STEPS"):
+ steps.extend(request.module.STEPS[vcs])
+
+ repo_dir = (tmpdir / "repo").resolve()
+ (tmpdir / "repo").mkdir()
+
+ repo_test_fixture = RepoTestFixture(repo_dir, vcs, steps)
+
+ repo_test_fixture.execute_next_step()
+
+ shutil.copytree(str(repo_dir), str(tmpdir / "remoterepo"))
+
+ repo_test_fixture.execute_next_step()
+
+ yield repo_test_fixture
diff --git a/python/mozversioncontrol/test/python.ini b/python/mozversioncontrol/test/python.ini
new file mode 100644
index 0000000000..79e52bf937
--- /dev/null
+++ b/python/mozversioncontrol/test/python.ini
@@ -0,0 +1,10 @@
+[DEFAULT]
+subsuite=mozversioncontrol
+
+[test_branch.py]
+[test_commit.py]
+[test_context_manager.py]
+[test_push_to_try.py]
+[test_update.py]
+[test_workdir_outgoing.py]
+[test_working_directory.py]
diff --git a/python/mozversioncontrol/test/test_branch.py b/python/mozversioncontrol/test/test_branch.py
new file mode 100644
index 0000000000..7d211f18e8
--- /dev/null
+++ b/python/mozversioncontrol/test/test_branch.py
@@ -0,0 +1,57 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import mozunit
+import pytest
+from looseversion import LooseVersion
+
+from mozversioncontrol import get_repository_object
+
+STEPS = {
+ "hg": [
+ """
+ hg bookmark test
+ """,
+ """
+ echo "bar" > foo
+ hg commit -m "second commit"
+ """,
+ ],
+ "git": [
+ """
+ git checkout -b test
+ """,
+ """
+ echo "bar" > foo
+ git commit -a -m "second commit"
+ """,
+ ],
+}
+
+
+def test_branch(repo):
+ vcs = get_repository_object(repo.dir)
+ if vcs.name == "git" and LooseVersion(vcs.tool_version) < LooseVersion("2.22.0"):
+ pytest.xfail("`git branch --show-current` not implemented yet")
+
+ if vcs.name == "git":
+ assert vcs.branch == "master"
+ else:
+ assert vcs.branch is None
+
+ repo.execute_next_step()
+ assert vcs.branch == "test"
+
+ repo.execute_next_step()
+ assert vcs.branch == "test"
+
+ vcs.update(vcs.head_ref)
+ assert vcs.branch is None
+
+ vcs.update("test")
+ assert vcs.branch == "test"
+
+
+if __name__ == "__main__":
+ mozunit.main()
diff --git a/python/mozversioncontrol/test/test_commit.py b/python/mozversioncontrol/test/test_commit.py
new file mode 100644
index 0000000000..b795c0ea6e
--- /dev/null
+++ b/python/mozversioncontrol/test/test_commit.py
@@ -0,0 +1,72 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import mozunit
+
+from mozversioncontrol import get_repository_object
+
+STEPS = {
+ "hg": [
+ """
+ echo "bar" >> bar
+ echo "baz" > foo
+ """,
+ ],
+ "git": [
+ """
+ echo "bar" >> bar
+ echo "baz" > foo
+ """,
+ ],
+}
+
+
+def test_commit(repo):
+ vcs = get_repository_object(repo.dir)
+ assert vcs.working_directory_clean()
+
+ # Modify both foo and bar
+ repo.execute_next_step()
+ assert not vcs.working_directory_clean()
+
+ # Commit just bar
+ vcs.commit(
+ message="Modify bar\n\nbut not baz",
+ author="Testing McTesterson <test@example.org>",
+ date="2017-07-14 02:40:00 UTC",
+ paths=["bar"],
+ )
+
+ # We only committed bar, so foo is still keeping the working dir dirty
+ assert not vcs.working_directory_clean()
+
+ if repo.vcs == "git":
+ log_cmd = ["log", "-1", "--format=%an,%ae,%aD,%B"]
+ patch_cmd = ["log", "-1", "-p"]
+ else:
+ log_cmd = [
+ "log",
+ "-l",
+ "1",
+ "-T",
+ "{person(author)},{email(author)},{date|rfc822date},{desc}",
+ ]
+ patch_cmd = ["log", "-l", "1", "-p"]
+
+ # Verify commit metadata (we rstrip to normalize trivial git/hg differences)
+ log = vcs._run(*log_cmd).rstrip()
+ assert log == (
+ "Testing McTesterson,test@example.org,Fri, 14 "
+ "Jul 2017 02:40:00 +0000,Modify bar\n\nbut not baz"
+ )
+
+ # Verify only the intended file was added to the commit
+ patch = vcs._run(*patch_cmd)
+ diffs = [line for line in patch.splitlines() if "diff --git" in line]
+ assert len(diffs) == 1
+ assert diffs[0] == "diff --git a/bar b/bar"
+
+
+if __name__ == "__main__":
+ mozunit.main()
diff --git a/python/mozversioncontrol/test/test_context_manager.py b/python/mozversioncontrol/test/test_context_manager.py
new file mode 100644
index 0000000000..3186a144d9
--- /dev/null
+++ b/python/mozversioncontrol/test/test_context_manager.py
@@ -0,0 +1,28 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import mozunit
+
+from mozversioncontrol import get_repository_object
+
+
+def test_context_manager(repo):
+ is_git = repo.vcs == "git"
+ cmd = ["show", "--no-patch"] if is_git else ["tip"]
+
+ vcs = get_repository_object(repo.dir)
+ output_subprocess = vcs._run(*cmd)
+ assert is_git or vcs._client.server is None
+ assert "Initial commit" in output_subprocess
+
+ with vcs:
+ assert is_git or vcs._client.server is not None
+ output_client = vcs._run(*cmd)
+
+ assert is_git or vcs._client.server is None
+ assert output_subprocess == output_client
+
+
+if __name__ == "__main__":
+ mozunit.main()
diff --git a/python/mozversioncontrol/test/test_push_to_try.py b/python/mozversioncontrol/test/test_push_to_try.py
new file mode 100644
index 0000000000..d0a0b2d993
--- /dev/null
+++ b/python/mozversioncontrol/test/test_push_to_try.py
@@ -0,0 +1,81 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import subprocess
+
+import mozunit
+import pytest
+
+from mozversioncontrol import MissingVCSExtension, get_repository_object
+
+
+def test_push_to_try(repo, monkeypatch):
+ commit_message = "commit message"
+ vcs = get_repository_object(repo.dir)
+
+ captured_commands = []
+
+ def fake_run(*args, **kwargs):
+ captured_commands.append(args[0])
+
+ monkeypatch.setattr(subprocess, "check_output", fake_run)
+ monkeypatch.setattr(subprocess, "check_call", fake_run)
+
+ vcs.push_to_try(commit_message)
+ tool = vcs._tool
+
+ if repo.vcs == "hg":
+ expected = [
+ (str(tool), "push-to-try", "-m", commit_message),
+ (str(tool), "revert", "-a"),
+ ]
+ else:
+ expected = [
+ (str(tool), "cinnabar", "--version"),
+ (
+ str(tool),
+ "-c",
+ "commit.gpgSign=false",
+ "commit",
+ "--allow-empty",
+ "-m",
+ commit_message,
+ ),
+ (
+ str(tool),
+ "push",
+ "hg::ssh://hg.mozilla.org/try",
+ "+HEAD:refs/heads/branches/default/tip",
+ ),
+ (str(tool), "reset", "HEAD~"),
+ ]
+
+ for i, value in enumerate(captured_commands):
+ assert value == expected[i]
+
+ assert len(captured_commands) == len(expected)
+
+
+def test_push_to_try_missing_extensions(repo, monkeypatch):
+ if repo.vcs != "git":
+ return
+
+ vcs = get_repository_object(repo.dir)
+
+ orig = vcs._run
+
+ def cinnabar_raises(*args, **kwargs):
+ # Simulate not having git cinnabar
+ if args[0] == "cinnabar":
+ raise subprocess.CalledProcessError(1, args)
+ return orig(*args, **kwargs)
+
+ monkeypatch.setattr(vcs, "_run", cinnabar_raises)
+
+ with pytest.raises(MissingVCSExtension):
+ vcs.push_to_try("commit message")
+
+
+if __name__ == "__main__":
+ mozunit.main()
diff --git a/python/mozversioncontrol/test/test_update.py b/python/mozversioncontrol/test/test_update.py
new file mode 100644
index 0000000000..91c7469ee5
--- /dev/null
+++ b/python/mozversioncontrol/test/test_update.py
@@ -0,0 +1,63 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from subprocess import CalledProcessError
+
+import mozunit
+import pytest
+
+from mozversioncontrol import get_repository_object
+
+STEPS = {
+ "hg": [
+ """
+ echo "bar" >> bar
+ echo "baz" > foo
+ hg commit -m "second commit"
+ """,
+ """
+ echo "foobar" > foo
+ """,
+ ],
+ "git": [
+ """
+ echo "bar" >> bar
+ echo "baz" > foo
+ git add *
+ git commit -m "second commit"
+ """,
+ """
+ echo "foobar" > foo
+ """,
+ ],
+}
+
+
+def test_update(repo):
+ vcs = get_repository_object(repo.dir)
+ rev0 = vcs.head_ref
+
+ repo.execute_next_step()
+ rev1 = vcs.head_ref
+ assert rev0 != rev1
+
+ if repo.vcs == "hg":
+ vcs.update(".~1")
+ else:
+ vcs.update("HEAD~1")
+ assert vcs.head_ref == rev0
+
+ vcs.update(rev1)
+ assert vcs.head_ref == rev1
+
+ # Update should fail with dirty working directory.
+ repo.execute_next_step()
+ with pytest.raises(CalledProcessError):
+ vcs.update(rev0)
+
+ assert vcs.head_ref == rev1
+
+
+if __name__ == "__main__":
+ mozunit.main()
diff --git a/python/mozversioncontrol/test/test_workdir_outgoing.py b/python/mozversioncontrol/test/test_workdir_outgoing.py
new file mode 100644
index 0000000000..7bf2e6ec57
--- /dev/null
+++ b/python/mozversioncontrol/test/test_workdir_outgoing.py
@@ -0,0 +1,108 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+
+import mozunit
+
+from mozversioncontrol import get_repository_object
+
+STEPS = {
+ "hg": [
+ """
+ echo "bar" >> bar
+ echo "baz" > baz
+ hg add baz
+ hg rm foo
+ """,
+ """
+ hg commit -m "Remove foo; modify bar; add baz"
+ """,
+ """
+ echo ooka >> baz
+ echo newborn > baby
+ hg add baby
+ """,
+ """
+ hg commit -m "Modify baz; add baby"
+ """,
+ ],
+ "git": [
+ """
+ echo "bar" >> bar
+ echo "baz" > baz
+ git add baz
+ git rm foo
+ """,
+ """
+ git commit -am "Remove foo; modify bar; add baz"
+ """,
+ """
+ echo ooka >> baz
+ echo newborn > baby
+ git add baz baby
+ """,
+ """
+ git commit -m "Modify baz; add baby"
+ """,
+ ],
+}
+
+
+def assert_files(actual, expected):
+ assert set(map(os.path.basename, actual)) == set(expected)
+
+
+def test_workdir_outgoing(repo):
+ vcs = get_repository_object(repo.dir)
+ assert vcs.path == str(repo.dir)
+
+ remote_path = "../remoterepo" if repo.vcs == "hg" else "upstream/master"
+
+ # Mutate files.
+ repo.execute_next_step()
+
+ assert_files(vcs.get_changed_files("A", "all"), ["baz"])
+ assert_files(vcs.get_changed_files("AM", "all"), ["bar", "baz"])
+ assert_files(vcs.get_changed_files("D", "all"), ["foo"])
+ if repo.vcs == "git":
+ assert_files(vcs.get_changed_files("AM", mode="staged"), ["baz"])
+ elif repo.vcs == "hg":
+ # Mercurial does not use a staging area (and ignores the mode parameter.)
+ assert_files(vcs.get_changed_files("AM", "unstaged"), ["bar", "baz"])
+ assert_files(vcs.get_outgoing_files("AMD"), [])
+ assert_files(vcs.get_outgoing_files("AMD", remote_path), [])
+
+ # Create a commit.
+ repo.execute_next_step()
+
+ assert_files(vcs.get_changed_files("AMD", "all"), [])
+ assert_files(vcs.get_changed_files("AMD", "staged"), [])
+ assert_files(vcs.get_outgoing_files("AMD"), ["bar", "baz", "foo"])
+ assert_files(vcs.get_outgoing_files("AMD", remote_path), ["bar", "baz", "foo"])
+
+ # Mutate again.
+ repo.execute_next_step()
+
+ assert_files(vcs.get_changed_files("A", "all"), ["baby"])
+ assert_files(vcs.get_changed_files("AM", "all"), ["baby", "baz"])
+ assert_files(vcs.get_changed_files("D", "all"), [])
+
+ # Create a second commit.
+ repo.execute_next_step()
+
+ assert_files(vcs.get_outgoing_files("AM"), ["bar", "baz", "baby"])
+ assert_files(vcs.get_outgoing_files("AM", remote_path), ["bar", "baz", "baby"])
+ if repo.vcs == "git":
+ assert_files(vcs.get_changed_files("AM", rev="HEAD~1"), ["bar", "baz"])
+ assert_files(vcs.get_changed_files("AM", rev="HEAD"), ["baby", "baz"])
+ else:
+ assert_files(vcs.get_changed_files("AM", rev=".^"), ["bar", "baz"])
+ assert_files(vcs.get_changed_files("AM", rev="."), ["baby", "baz"])
+ assert_files(vcs.get_changed_files("AM", rev=".^::"), ["bar", "baz", "baby"])
+ assert_files(vcs.get_changed_files("AM", rev="modifies(baz)"), ["baz", "baby"])
+
+
+if __name__ == "__main__":
+ mozunit.main()
diff --git a/python/mozversioncontrol/test/test_working_directory.py b/python/mozversioncontrol/test/test_working_directory.py
new file mode 100644
index 0000000000..00094a0cc4
--- /dev/null
+++ b/python/mozversioncontrol/test/test_working_directory.py
@@ -0,0 +1,46 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import mozunit
+
+from mozversioncontrol import get_repository_object
+
+STEPS = {
+ "hg": [
+ """
+ echo "bar" >> bar
+ echo "baz" > baz
+ hg rm foo
+ """,
+ """
+ hg commit -m "Remove foo; modify bar; touch baz (but don't add it)"
+ """,
+ ],
+ "git": [
+ """
+ echo "bar" >> bar
+ echo "baz" > baz
+ git rm foo
+ """,
+ """
+ git commit -am "Remove foo; modify bar; touch baz (but don't add it)"
+ """,
+ ],
+}
+
+
+def test_working_directory_clean_untracked_files(repo):
+ vcs = get_repository_object(repo.dir)
+ assert vcs.working_directory_clean()
+
+ repo.execute_next_step()
+ assert not vcs.working_directory_clean()
+
+ repo.execute_next_step()
+ assert vcs.working_directory_clean()
+ assert not vcs.working_directory_clean(untracked=True)
+
+
+if __name__ == "__main__":
+ mozunit.main()