blob: a90f3d101e65443a138d10230c86ff74f3ed6823 [file] [log] [blame] [edit]
#!/usr/bin/env python3
# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Tests for rust_uprev.py"""
import os
from pathlib import Path
import shutil
import subprocess
import tempfile
import unittest
from unittest import mock
from llvm_tools import git
# rust_uprev sets SOURCE_ROOT to the output of `repo --show-toplevel`.
# The mock below makes us not actually run repo but use a fake value
# instead.
with mock.patch("subprocess.check_output", return_value="/fake/chromiumos"):
import rust_uprev
def _fail_command(cmd, *_args, **_kwargs):
err = subprocess.CalledProcessError(returncode=1, cmd=cmd)
err.stderr = b"mock failure"
raise err
def start_mock(obj, *args, **kwargs):
"""Creates a patcher, starts it, and registers a cleanup to stop it.
Args:
obj:
the object to attach the cleanup to
*args:
passed to mock.patch()
**kwargs:
passsed to mock.patch()
"""
patcher = mock.patch(*args, **kwargs)
val = patcher.start()
obj.addCleanup(patcher.stop)
return val
class FetchDistfileTest(unittest.TestCase):
"""Tests rust_uprev.fetch_distfile_from_mirror()"""
@mock.patch.object(
rust_uprev, "get_distdir", return_value=Path("/fake/distfiles")
)
@mock.patch.object(subprocess, "call", side_effect=_fail_command)
def test_fetch_difstfile_fail(self, *_args) -> None:
with self.assertRaises(subprocess.CalledProcessError):
rust_uprev.fetch_distfile_from_mirror("test_distfile.tar.gz")
@mock.patch.object(
rust_uprev,
"get_command_output_unchecked",
return_value="AccessDeniedException: Access denied.",
)
@mock.patch.object(
rust_uprev, "get_distdir", return_value=Path("/fake/distfiles")
)
@mock.patch.object(subprocess, "call", return_value=0)
def test_fetch_distfile_acl_access_denied(self, *_args) -> None:
rust_uprev.fetch_distfile_from_mirror("test_distfile.tar.gz")
@mock.patch.object(
rust_uprev,
"get_command_output_unchecked",
return_value='[ { "entity": "allUsers", "role": "READER" } ]',
)
@mock.patch.object(
rust_uprev, "get_distdir", return_value=Path("/fake/distfiles")
)
@mock.patch.object(subprocess, "call", return_value=0)
def test_fetch_distfile_acl_ok(self, *_args) -> None:
rust_uprev.fetch_distfile_from_mirror("test_distfile.tar.gz")
@mock.patch.object(
rust_uprev,
"get_command_output_unchecked",
return_value='[ { "entity": "___fake@google.com", "role": "OWNER" } ]',
)
@mock.patch.object(
rust_uprev, "get_distdir", return_value=Path("/fake/distfiles")
)
@mock.patch.object(subprocess, "call", return_value=0)
def test_fetch_distfile_acl_wrong(self, *_args) -> None:
with self.assertRaisesRegex(Exception, "allUsers.*READER"):
with self.assertLogs(level="ERROR") as log:
rust_uprev.fetch_distfile_from_mirror("test_distfile.tar.gz")
self.assertIn(
'[ { "entity": "___fake@google.com", "role": "OWNER" } ]',
"\n".join(log.output),
)
class FetchRustSrcFromUpstreamTest(unittest.TestCase):
"""Tests for rust_uprev.fetch_rust_src_from_upstream."""
def setUp(self) -> None:
self._mock_get_distdir = start_mock(
self,
"rust_uprev.get_distdir",
return_value=Path("/fake/distfiles"),
)
self._mock_gpg = start_mock(
self,
"subprocess.run",
side_effect=self.fake_gpg,
)
self._mock_urlretrieve = start_mock(
self,
"urllib.request.urlretrieve",
side_effect=self.fake_urlretrieve,
)
self._mock_rust_signing_key = start_mock(
self,
"rust_uprev.RUST_SIGNING_KEY",
"1234567",
)
@staticmethod
def fake_urlretrieve(src: str, dest: Path) -> None:
pass
@staticmethod
def fake_gpg(cmd, **_kwargs):
val = mock.Mock()
val.returncode = 0
val.stdout = ""
if "--verify" in cmd:
val.stdout = "GOODSIG 1234567"
return val
def test_success(self):
with mock.patch("rust_uprev.GPG", "gnupg"):
rust_uprev.fetch_rust_src_from_upstream(
"fakehttps://rustc-1.60.3-src.tar.gz",
Path("/fake/distfiles/rustc-1.60.3-src.tar.gz"),
)
self._mock_urlretrieve.has_calls(
[
(
"fakehttps://rustc-1.60.3-src.tar.gz",
Path("/fake/distfiles/rustc-1.60.3-src.tar.gz"),
),
(
"fakehttps://rustc-1.60.3-src.tar.gz.asc",
Path("/fake/distfiles/rustc-1.60.3-src.tar.gz.asc"),
),
]
)
self._mock_gpg.has_calls(
[
(["gnupg", "--refresh-keys", "1234567"], {"check": True}),
]
)
def test_no_signature_file(self):
def _urlretrieve(src, dest):
if src.endswith(".asc"):
raise Exception("404 not found")
return self.fake_urlretrieve(src, dest)
self._mock_urlretrieve.side_effect = _urlretrieve
with self.assertRaises(rust_uprev.SignatureVerificationError) as ctx:
rust_uprev.fetch_rust_src_from_upstream(
"fakehttps://rustc-1.60.3-src.tar.gz",
Path("/fake/distfiles/rustc-1.60.3-src.tar.gz"),
)
self.assertIn("error fetching signature file", ctx.exception.message)
def test_key_expired(self):
def _gpg_verify(cmd, *args, **kwargs):
val = self.fake_gpg(cmd, *args, **kwargs)
if "--verify" in cmd:
val.stdout = "EXPKEYSIG 1234567"
return val
self._mock_gpg.side_effect = _gpg_verify
with self.assertRaises(rust_uprev.SignatureVerificationError) as ctx:
rust_uprev.fetch_rust_src_from_upstream(
"fakehttps://rustc-1.60.3-src.tar.gz",
Path("/fake/distfiles/rustc-1.60.3-src.tar.gz"),
)
self.assertIn("key has expired", ctx.exception.message)
def test_key_revoked(self):
def _gpg_verify(cmd, *args, **kwargs):
val = self.fake_gpg(cmd, *args, **kwargs)
if "--verify" in cmd:
val.stdout = "REVKEYSIG 1234567"
return val
self._mock_gpg.side_effect = _gpg_verify
with self.assertRaises(rust_uprev.SignatureVerificationError) as ctx:
rust_uprev.fetch_rust_src_from_upstream(
"fakehttps://rustc-1.60.3-src.tar.gz",
Path("/fake/distfiles/rustc-1.60.3-src.tar.gz"),
)
self.assertIn("key has been revoked", ctx.exception.message)
def test_signature_expired(self):
def _gpg_verify(cmd, *args, **kwargs):
val = self.fake_gpg(cmd, *args, **kwargs)
if "--verify" in cmd:
val.stdout = "EXPSIG 1234567"
return val
self._mock_gpg.side_effect = _gpg_verify
with self.assertRaises(rust_uprev.SignatureVerificationError) as ctx:
rust_uprev.fetch_rust_src_from_upstream(
"fakehttps://rustc-1.60.3-src.tar.gz",
Path("/fake/distfiles/rustc-1.60.3-src.tar.gz"),
)
self.assertIn("signature has expired", ctx.exception.message)
def test_wrong_key(self):
def _gpg_verify(cmd, *args, **kwargs):
val = self.fake_gpg(cmd, *args, **kwargs)
if "--verify" in cmd:
val.stdout = "GOODSIG 0000000"
return val
self._mock_gpg.side_effect = _gpg_verify
with self.assertRaises(rust_uprev.SignatureVerificationError) as ctx:
rust_uprev.fetch_rust_src_from_upstream(
"fakehttps://rustc-1.60.3-src.tar.gz",
Path("/fake/distfiles/rustc-1.60.3-src.tar.gz"),
)
self.assertIn("1234567 not found", ctx.exception.message)
class FindEbuildPathTest(unittest.TestCase):
"""Tests for rust_uprev.find_ebuild_path()"""
def test_exact_version(self):
with tempfile.TemporaryDirectory() as t:
tmpdir = Path(t)
ebuild = tmpdir / "test-1.3.4.ebuild"
ebuild.touch()
(tmpdir / "test-1.2.3.ebuild").touch()
result = rust_uprev.find_ebuild_path(
tmpdir, "test", rust_uprev.RustVersion(1, 3, 4)
)
self.assertEqual(result, ebuild)
def test_no_version(self):
with tempfile.TemporaryDirectory() as t:
tmpdir = Path(t)
ebuild = tmpdir / "test-1.2.3.ebuild"
ebuild.touch()
result = rust_uprev.find_ebuild_path(tmpdir, "test")
self.assertEqual(result, ebuild)
def test_patch_version(self):
with tempfile.TemporaryDirectory() as t:
tmpdir = Path(t)
ebuild = tmpdir / "test-1.3.4-r3.ebuild"
ebuild.touch()
(tmpdir / "test-1.2.3.ebuild").touch()
result = rust_uprev.find_ebuild_path(
tmpdir, "test", rust_uprev.RustVersion(1, 3, 4)
)
self.assertEqual(result, ebuild)
def test_multiple_versions(self):
with tempfile.TemporaryDirectory() as t:
tmpdir = Path(t)
(tmpdir / "test-1.3.4-r3.ebuild").touch()
(tmpdir / "test-1.3.5.ebuild").touch()
with self.assertRaises(AssertionError):
rust_uprev.find_ebuild_path(tmpdir, "test")
def test_selected_version(self):
with tempfile.TemporaryDirectory() as t:
tmpdir = Path(t)
ebuild = tmpdir / "test-1.3.4-r3.ebuild"
ebuild.touch()
(tmpdir / "test-1.3.5.ebuild").touch()
result = rust_uprev.find_ebuild_path(
tmpdir, "test", rust_uprev.RustVersion(1, 3, 4)
)
self.assertEqual(result, ebuild)
def test_symlink(self):
# Symlinks to ebuilds in the same directory are allowed, and the return
# value is the regular file.
with tempfile.TemporaryDirectory() as t:
tmpdir = Path(t)
ebuild = tmpdir / "test-1.3.4.ebuild"
ebuild.touch()
(tmpdir / "test-1.3.4-r1.ebuild").symlink_to("test-1.3.4.ebuild")
result = rust_uprev.find_ebuild_path(tmpdir, "test")
self.assertEqual(result, ebuild)
class FindRustVersionsTest(unittest.TestCase):
"""Tests for rust_uprev.find_rust_versions."""
def test_with_symlinks(self):
with tempfile.TemporaryDirectory() as t:
tmpdir = Path(t)
rust_1_49_1_ebuild = tmpdir / "rust-1.49.1.ebuild"
rust_1_50_0_ebuild = tmpdir / "rust-1.50.0.ebuild"
rust_1_50_0_r1_ebuild = tmpdir / "rust-1.50.0-r1.ebuild"
rust_1_49_1_ebuild.touch()
rust_1_50_0_ebuild.touch()
rust_1_50_0_r1_ebuild.symlink_to(rust_1_50_0_ebuild)
with mock.patch("rust_uprev.RUST_PATH", tmpdir):
actual = rust_uprev.find_rust_versions()
expected = [
(rust_uprev.RustVersion(1, 49, 1), rust_1_49_1_ebuild),
(rust_uprev.RustVersion(1, 50, 0), rust_1_50_0_ebuild),
]
self.assertEqual(actual, expected)
class MirrorHasFileTest(unittest.TestCase):
"""Tests for rust_uprev.mirror_has_file."""
@mock.patch.object(subprocess, "run")
def test_no(self, mock_run):
mock_run.return_value = mock.Mock(
returncode=1,
stdout="CommandException: One or more URLs matched no objects.",
)
self.assertFalse(rust_uprev.mirror_has_file("rustc-1.69.0-src.tar.gz"))
@mock.patch.object(subprocess, "run")
def test_yes(self, mock_run):
mock_run.return_value = mock.Mock(
returncode=0,
# pylint: disable=line-too-long
stdout="gs://chromeos-localmirror/distfiles/rustc-1.69.0-src.tar.gz",
)
self.assertTrue(rust_uprev.mirror_has_file("rustc-1.69.0-src.tar.gz"))
class MirrorRustSourceTest(unittest.TestCase):
"""Tests for rust_uprev.mirror_rust_source."""
def setUp(self) -> None:
start_mock(self, "rust_uprev.GSUTIL", "gsutil")
start_mock(self, "rust_uprev.MIRROR_PATH", "fakegs://fakemirror/")
start_mock(
self, "rust_uprev.get_distdir", return_value=Path("/fake/distfiles")
)
self.mock_mirror_has_file = start_mock(
self,
"rust_uprev.mirror_has_file",
)
self.mock_fetch_rust_src_from_upstream = start_mock(
self,
"rust_uprev.fetch_rust_src_from_upstream",
)
self.mock_subprocess_run = start_mock(
self,
"subprocess.run",
)
def test_already_present(self):
self.mock_mirror_has_file.return_value = True
rust_uprev.mirror_rust_source(
rust_uprev.RustVersion.parse("1.67.3"),
)
self.mock_fetch_rust_src_from_upstream.assert_not_called()
self.mock_subprocess_run.assert_not_called()
def test_fetch_and_upload(self):
self.mock_mirror_has_file.return_value = False
rust_uprev.mirror_rust_source(
rust_uprev.RustVersion.parse("1.67.3"),
)
self.mock_fetch_rust_src_from_upstream.called_once()
self.mock_subprocess_run.has_calls(
[
(
[
"gsutil",
"cp",
"-a",
"public-read",
"/fake/distdir/rustc-1.67.3-src.tar.gz",
"fakegs://fakemirror/rustc-1.67.3-src.tar.gz",
]
),
]
)
class RemoveEbuildVersionTest(unittest.TestCase):
"""Tests for rust_uprev.remove_ebuild_version()"""
@mock.patch.object(subprocess, "check_call")
def test_single(self, check_call):
with tempfile.TemporaryDirectory() as tmpdir:
ebuild_dir = Path(tmpdir, "test-ebuilds")
ebuild_dir.mkdir()
ebuild = Path(ebuild_dir, "test-3.1.4.ebuild")
ebuild.touch()
Path(ebuild_dir, "unrelated-1.0.0.ebuild").touch()
rust_uprev.remove_ebuild_version(
ebuild_dir, "test", rust_uprev.RustVersion(3, 1, 4)
)
check_call.assert_called_once_with(
["git", "rm", "test-3.1.4.ebuild"], cwd=ebuild_dir
)
@mock.patch.object(subprocess, "check_call")
def test_symlink(self, check_call):
with tempfile.TemporaryDirectory() as tmpdir:
ebuild_dir = Path(tmpdir, "test-ebuilds")
ebuild_dir.mkdir()
ebuild = Path(ebuild_dir, "test-3.1.4.ebuild")
ebuild.touch()
symlink = Path(ebuild_dir, "test-3.1.4-r5.ebuild")
symlink.symlink_to(ebuild.name)
Path(ebuild_dir, "unrelated-1.0.0.ebuild").touch()
rust_uprev.remove_ebuild_version(
ebuild_dir, "test", rust_uprev.RustVersion(3, 1, 4)
)
check_call.assert_has_calls(
[
mock.call(
["git", "rm", "test-3.1.4.ebuild"], cwd=ebuild_dir
),
mock.call(
["git", "rm", "test-3.1.4-r5.ebuild"], cwd=ebuild_dir
),
],
any_order=True,
)
class RustVersionTest(unittest.TestCase):
"""Tests for RustVersion class"""
def test_str(self):
obj = rust_uprev.RustVersion(major=1, minor=2, patch=3)
self.assertEqual(str(obj), "1.2.3")
def test_parse_version_only(self):
expected = rust_uprev.RustVersion(major=1, minor=2, patch=3)
actual = rust_uprev.RustVersion.parse("1.2.3")
self.assertEqual(expected, actual)
def test_parse_ebuild_name(self):
expected = rust_uprev.RustVersion(major=1, minor=2, patch=3)
actual = rust_uprev.RustVersion.parse_from_ebuild("rust-1.2.3.ebuild")
self.assertEqual(expected, actual)
actual = rust_uprev.RustVersion.parse_from_ebuild(
"rust-1.2.3-r1.ebuild"
)
self.assertEqual(expected, actual)
def test_parse_fail(self):
with self.assertRaises(AssertionError) as context:
rust_uprev.RustVersion.parse("invalid-rust-1.2.3")
self.assertEqual(
"failed to parse 'invalid-rust-1.2.3'", str(context.exception)
)
class PrepareUprevTest(unittest.TestCase):
"""Tests for prepare_uprev step in rust_uprev"""
def setUp(self):
self.version_old = rust_uprev.RustVersion(1, 2, 3)
self.version_new = rust_uprev.RustVersion(1, 3, 5)
@mock.patch.object(
rust_uprev,
"find_ebuild_for_rust_version",
return_value=Path("/path/to/ebuild"),
)
@mock.patch.object(rust_uprev, "get_command_output")
def test_success_with_template(self, mock_command, _ebuild_for_version):
expected = rust_uprev.PreparedUprev(self.version_old)
actual = rust_uprev.prepare_uprev(
rust_version=self.version_new, template=self.version_old
)
self.assertEqual(expected, actual)
mock_command.assert_not_called()
@mock.patch.object(
rust_uprev,
"find_ebuild_for_rust_version",
return_value="/path/to/ebuild",
)
@mock.patch.object(rust_uprev, "get_command_output")
def test_return_none_with_template_larger_than_input(
self, mock_command, *_args
):
ret = rust_uprev.prepare_uprev(
rust_version=self.version_old, template=self.version_new
)
self.assertIsNone(ret)
mock_command.assert_not_called()
def test_prepare_uprev_from_json(self):
json_result = (list(self.version_new),)
expected = rust_uprev.PreparedUprev(
self.version_new,
)
actual = rust_uprev.prepare_uprev_from_json(json_result)
self.assertEqual(expected, actual)
class ToggleProfileData(unittest.TestCase):
"""Tests functionality to include or exclude profile data from SRC_URI."""
ebuild_with_profdata = """
# Some text here.
INCLUDE_PROFDATA_IN_SRC_URI=yes
some code here
"""
ebuild_without_profdata = """
# Some text here.
INCLUDE_PROFDATA_IN_SRC_URI=
some code here
"""
ebuild_unexpected_content = """
# Does not contain OMIT_PROFDATA_FROM_SRC_URI assignment
"""
def setUp(self):
self.mock_read_text = start_mock(self, "pathlib.Path.read_text")
def test_turn_off_profdata(self):
# Test that a file with profdata on is rewritten to a file with
# profdata off.
self.mock_read_text.return_value = self.ebuild_with_profdata
ebuild_file = "/path/to/eclass/cros-rustc.eclass"
with mock.patch("pathlib.Path.write_text") as mock_write_text:
rust_uprev.set_include_profdata_src(ebuild_file, include=False)
mock_write_text.assert_called_once_with(
self.ebuild_without_profdata, encoding="utf-8"
)
def test_turn_on_profdata(self):
# Test that a file with profdata off is rewritten to a file with
# profdata on.
self.mock_read_text.return_value = self.ebuild_without_profdata
ebuild_file = "/path/to/eclass/cros-rustc.eclass"
with mock.patch("pathlib.Path.write_text") as mock_write_text:
rust_uprev.set_include_profdata_src(ebuild_file, include=True)
mock_write_text.assert_called_once_with(
self.ebuild_with_profdata, encoding="utf-8"
)
def test_turn_on_profdata_fails_if_no_assignment(self):
# Test that if the string the code expects to find is not found,
# this causes an exception and the file is not overwritten.
self.mock_read_text.return_value = self.ebuild_unexpected_content
ebuild_file = "/path/to/eclass/cros-rustc.eclass"
with mock.patch("pathlib.Path.write_text") as mock_write_text:
with self.assertRaises(Exception):
rust_uprev.set_include_profdata_src(ebuild_file, include=False)
mock_write_text.assert_not_called()
class UpdateBootstrapVersionTest(unittest.TestCase):
"""Tests for update_bootstrap_version step in rust_uprev"""
ebuild_file_before = """
BOOTSTRAP_VERSION="1.2.0"
"""
ebuild_file_after = """
BOOTSTRAP_VERSION="1.3.6"
"""
def setUp(self):
self.mock_read_text = start_mock(self, "pathlib.Path.read_text")
def test_success(self):
self.mock_read_text.return_value = self.ebuild_file_before
# ebuild_file and new bootstrap version are deliberately different
ebuild_file = "/path/to/rust/cros-rustc.eclass"
with mock.patch("pathlib.Path.write_text") as mock_write_text:
rust_uprev.update_bootstrap_version(
ebuild_file, rust_uprev.RustVersion.parse("1.3.6")
)
mock_write_text.assert_called_once_with(
self.ebuild_file_after, encoding="utf-8"
)
def test_fail_when_ebuild_misses_a_variable(self):
self.mock_read_text.return_value = ""
ebuild_file = "/path/to/rust/rust-1.3.5.ebuild"
with self.assertRaises(RuntimeError) as context:
rust_uprev.update_bootstrap_version(
ebuild_file, rust_uprev.RustVersion.parse("1.2.0")
)
self.assertEqual(
"BOOTSTRAP_VERSION not found in /path/to/rust/rust-1.3.5.ebuild",
str(context.exception),
)
class UpdateRustPackagesTests(unittest.TestCase):
"""Tests for update_rust_packages step."""
def setUp(self):
self.old_version = rust_uprev.RustVersion(1, 1, 0)
self.current_version = rust_uprev.RustVersion(1, 2, 3)
self.new_version = rust_uprev.RustVersion(1, 3, 5)
self.ebuild_file = os.path.join(
rust_uprev.RUST_PATH, "rust-{self.new_version}.ebuild"
)
def test_add_new_rust_packages(self):
package_before = (
f"dev-lang/rust-{self.old_version}\n"
f"dev-lang/rust-{self.current_version}"
)
package_after = (
f"dev-lang/rust-{self.old_version}\n"
f"dev-lang/rust-{self.current_version}\n"
f"dev-lang/rust-{self.new_version}"
)
mock_open = mock.mock_open(read_data=package_before)
with mock.patch("builtins.open", mock_open):
rust_uprev.update_rust_packages(
"dev-lang/rust", self.new_version, add=True
)
mock_open.return_value.__enter__().write.assert_called_once_with(
package_after
)
def test_remove_old_rust_packages(self):
package_before = (
f"dev-lang/rust-{self.old_version}\n"
f"dev-lang/rust-{self.current_version}\n"
f"dev-lang/rust-{self.new_version}"
)
package_after = (
f"dev-lang/rust-{self.current_version}\n"
f"dev-lang/rust-{self.new_version}"
)
mock_open = mock.mock_open(read_data=package_before)
with mock.patch("builtins.open", mock_open):
rust_uprev.update_rust_packages(
"dev-lang/rust", self.old_version, add=False
)
mock_open.return_value.__enter__().write.assert_called_once_with(
package_after
)
class RustUprevOtherStagesTests(unittest.TestCase):
"""Tests for other steps in rust_uprev"""
def setUp(self):
self.old_version = rust_uprev.RustVersion(1, 1, 0)
self.current_version = rust_uprev.RustVersion(1, 2, 3)
self.new_version = rust_uprev.RustVersion(1, 3, 5)
self.ebuild_file = os.path.join(
rust_uprev.RUST_PATH, "rust-{self.new_version}.ebuild"
)
@mock.patch.object(shutil, "copyfile")
@mock.patch.object(subprocess, "check_call")
def test_create_rust_ebuild(self, mock_call, mock_copy):
template_ebuild = (
rust_uprev.EBUILD_PREFIX
/ f"dev-lang/rust/rust-{self.current_version}.ebuild"
)
new_ebuild = (
rust_uprev.EBUILD_PREFIX
/ f"dev-lang/rust/rust-{self.new_version}.ebuild"
)
rust_uprev.create_ebuild(
"dev-lang", "rust", self.current_version, self.new_version
)
mock_copy.assert_called_once_with(
template_ebuild,
new_ebuild,
)
mock_call.assert_called_once_with(
["git", "add", f"rust-{self.new_version}.ebuild"],
cwd=new_ebuild.parent,
)
@mock.patch.object(shutil, "copyfile")
@mock.patch.object(subprocess, "check_call")
def test_create_rust_host_ebuild(self, mock_call, mock_copy):
template_ebuild = (
rust_uprev.EBUILD_PREFIX
/ f"dev-lang/rust-host/rust-host-{self.current_version}.ebuild"
)
new_ebuild = (
rust_uprev.EBUILD_PREFIX
/ f"dev-lang/rust-host/rust-host-{self.new_version}.ebuild"
)
rust_uprev.create_ebuild(
"dev-lang", "rust-host", self.current_version, self.new_version
)
mock_copy.assert_called_once_with(
template_ebuild,
new_ebuild,
)
mock_call.assert_called_once_with(
["git", "add", f"rust-host-{self.new_version}.ebuild"],
cwd=new_ebuild.parent,
)
@mock.patch.object(subprocess, "check_call")
def test_remove_virtual_rust(self, mock_call):
with tempfile.TemporaryDirectory() as tmpdir:
ebuild_path = Path(
tmpdir, f"virtual/rust/rust-{self.old_version}.ebuild"
)
os.makedirs(ebuild_path.parent)
ebuild_path.touch()
with mock.patch("rust_uprev.EBUILD_PREFIX", Path(tmpdir)):
rust_uprev.remove_virtual_rust(self.old_version)
mock_call.assert_called_once_with(
["git", "rm", str(ebuild_path.name)], cwd=ebuild_path.parent
)
@mock.patch.object(subprocess, "check_call")
def test_remove_virtual_rust_with_symlink(self, mock_call):
with tempfile.TemporaryDirectory() as tmpdir:
ebuild_path = Path(
tmpdir, f"virtual/rust/rust-{self.old_version}.ebuild"
)
symlink_path = Path(
tmpdir, f"virtual/rust/rust-{self.old_version}-r14.ebuild"
)
os.makedirs(ebuild_path.parent)
ebuild_path.touch()
symlink_path.symlink_to(ebuild_path.name)
with mock.patch("rust_uprev.EBUILD_PREFIX", Path(tmpdir)):
rust_uprev.remove_virtual_rust(self.old_version)
mock_call.assert_has_calls(
[
mock.call(
["git", "rm", ebuild_path.name],
cwd=ebuild_path.parent,
),
mock.call(
["git", "rm", symlink_path.name],
cwd=ebuild_path.parent,
),
],
any_order=True,
)
@mock.patch.object(rust_uprev, "find_ebuild_path")
@mock.patch.object(shutil, "copyfile")
@mock.patch.object(subprocess, "check_call")
def test_update_virtual_rust(self, mock_call, mock_copy, mock_find_ebuild):
ebuild_path = Path(
f"/some/dir/virtual/rust/rust-{self.current_version}.ebuild"
)
mock_find_ebuild.return_value = Path(ebuild_path)
rust_uprev.update_virtual_rust(self.current_version, self.new_version)
mock_call.assert_called_once_with(
["git", "add", f"rust-{self.new_version}.ebuild"],
cwd=ebuild_path.parent,
)
mock_copy.assert_called_once_with(
ebuild_path.parent.joinpath(f"rust-{self.current_version}.ebuild"),
ebuild_path.parent.joinpath(f"rust-{self.new_version}.ebuild"),
)
@mock.patch("rust_uprev.find_rust_versions")
def test_find_oldest_rust_version_pass(self, rust_versions):
oldest_version_name = f"rust-{self.old_version}.ebuild"
rust_versions.return_value = [
(self.old_version, oldest_version_name),
(self.current_version, f"rust-{self.current_version}.ebuild"),
(self.new_version, f"rust-{self.new_version}.ebuild"),
]
actual = rust_uprev.find_oldest_rust_version()
expected = self.old_version
self.assertEqual(expected, actual)
@mock.patch("rust_uprev.find_rust_versions")
def test_find_oldest_rust_version_fail_with_only_one_ebuild(
self, rust_versions
):
rust_versions.return_value = [
(self.new_version, f"rust-{self.new_version}.ebuild"),
]
with self.assertRaises(RuntimeError) as context:
rust_uprev.find_oldest_rust_version()
self.assertEqual(
"Expect to find more than one Rust versions", str(context.exception)
)
@mock.patch.object(rust_uprev, "get_command_output")
@mock.patch.object(git, "CreateBranch")
def test_create_new_repo(self, mock_branch, mock_output):
mock_output.return_value = ""
rust_uprev.create_new_repo(self.new_version)
mock_branch.assert_called_once_with(
rust_uprev.EBUILD_PREFIX, f"rust-to-{self.new_version}"
)
@mock.patch.object(rust_uprev, "run_in_chroot")
def test_build_cross_compiler(self, mock_run_in_chroot):
cros_targets = [
"x86_64-cros-linux-gnu",
"armv7a-cros-linux-gnueabihf",
"aarch64-cros-linux-gnu",
]
all_triples = ["x86_64-pc-linux-gnu"] + cros_targets
rust_ebuild = "RUSTC_TARGET_TRIPLES=(" + "\n\t".join(all_triples) + ")"
with mock.patch("rust_uprev.find_ebuild_path") as mock_find_ebuild_path:
mock_path = mock.Mock()
mock_path.read_text.return_value = rust_ebuild
mock_find_ebuild_path.return_value = mock_path
rust_uprev.build_cross_compiler(rust_uprev.RustVersion(7, 3, 31))
mock_run_in_chroot.assert_called_once_with(
["sudo", "emerge", "-j", "-G"]
+ [f"cross-{x}/gcc" for x in cros_targets + ["arm-none-eabi"]]
)
if __name__ == "__main__":
unittest.main()