blob: bf08d301de553fb788ac8cbba0b9d2a12f7dde7a [file] [log] [blame]
# Copyright 2019 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Unittests for Artifacts operations."""
import os
import pathlib
from typing import Optional
from unittest import mock
from chromite.api import api_config
from chromite.api.controller import artifacts
from chromite.api.controller import controller_util
from chromite.api.controller import image as image_controller
from chromite.api.controller import sysroot as sysroot_controller
from chromite.api.controller import test as test_controller
from chromite.api.gen.chromite.api import artifacts_pb2
from chromite.api.gen.chromite.api import sysroot_pb2
from chromite.api.gen.chromiumos import common_pb2
from chromite.cbuildbot import commands
from chromite.lib import chroot_lib
from chromite.lib import constants
from chromite.lib import cros_build_lib
from chromite.lib import cros_test_lib
from chromite.lib import osutils
from chromite.lib import sysroot_lib
from chromite.service import artifacts as artifacts_svc
class BundleRequestMixin:
"""Mixin to provide bundle request methods."""
def EmptyRequest(self):
return artifacts_pb2.BundleRequest()
def BuildTargetRequest(
self, build_target=None, output_dir=None, chroot=None
):
"""Get a build target format request instance."""
request = self.EmptyRequest()
if build_target:
request.build_target.name = build_target
if output_dir:
request.result_path.path.path = str(output_dir)
request.result_path.path.location = common_pb2.Path.Location.OUTSIDE
if chroot:
request.chroot.path = chroot
return request
def SysrootRequest(
self,
sysroot=None,
build_target=None,
output_dir=None,
chroot=None,
chroot_out=None,
):
"""Get a sysroot format request instance."""
request = self.EmptyRequest()
if sysroot:
request.sysroot.path = sysroot
if build_target:
request.sysroot.build_target.name = build_target
if output_dir:
request.result_path.path.path = output_dir
request.result_path.path.location = common_pb2.Path.Location.OUTSIDE
if chroot:
request.chroot.path = str(chroot)
if chroot_out:
request.chroot.out_path = str(chroot_out)
return request
class BundleTestCase(
cros_test_lib.MockTempDirTestCase,
api_config.ApiConfigMixin,
BundleRequestMixin,
):
"""Basic setup for all artifacts unittests."""
def setUp(self) -> None:
self.PatchObject(cros_build_lib, "IsInsideChroot", return_value=False)
self.output_dir = os.path.join(self.tempdir, "artifacts")
osutils.SafeMakedirs(self.output_dir)
self.sysroot_path = "/build/target"
self.sysroot = sysroot_lib.Sysroot(self.sysroot_path)
self.chroot = chroot_lib.Chroot(
path=self.tempdir / "chroot",
out_path=self.tempdir / "out",
)
full_sysroot_path = self.chroot.full_path(self.sysroot_path)
osutils.SafeMakedirs(full_sysroot_path)
osutils.SafeMakedirs(self.chroot.path)
osutils.SafeMakedirs(self.chroot.out_path)
# All requests use same response type.
self.response = artifacts_pb2.BundleResponse()
# Build target request.
self.target_request = self.BuildTargetRequest(
build_target="target",
output_dir=self.output_dir,
chroot=self.chroot.path,
)
# Sysroot request.
self.sysroot_request = self.SysrootRequest(
sysroot=self.sysroot_path,
build_target="target",
output_dir=self.output_dir,
chroot=self.chroot.path,
chroot_out=self.chroot.out_path,
)
self.source_root = self.tempdir
self.PatchObject(constants, "SOURCE_ROOT", new=self.tempdir)
class BundleImageArchivesTest(BundleTestCase):
"""BundleImageArchives tests."""
def testValidateOnly(self) -> None:
"""Quick check that a validate only call does not execute any logic."""
patch = self.PatchObject(artifacts_svc, "ArchiveImages")
artifacts.BundleImageArchives(
self.sysroot_request, self.response, self.validate_only_config
)
patch.assert_not_called()
def testMockCall(self) -> None:
"""Test a mock call does not execute logic, returns mocked value."""
patch = self.PatchObject(artifacts_svc, "ArchiveImages")
artifacts.BundleImageArchives(
self.sysroot_request, self.response, self.mock_call_config
)
patch.assert_not_called()
self.assertEqual(len(self.response.artifacts), 2)
self.assertEqual(
self.response.artifacts[0].artifact_path.path,
os.path.join(self.output_dir, "path0.tar.xz"),
)
self.assertEqual(
self.response.artifacts[1].artifact_path.path,
os.path.join(self.output_dir, "path1.tar.xz"),
)
def testNoBuildTarget(self) -> None:
"""Test that no build target fails."""
request = self.BuildTargetRequest(output_dir=str(self.tempdir))
with self.assertRaises(cros_build_lib.DieSystemExit):
artifacts.BundleImageArchives(
request, self.response, self.api_config
)
def testNoOutputDir(self) -> None:
"""Test no output dir fails."""
request = self.BuildTargetRequest(build_target="board")
with self.assertRaises(cros_build_lib.DieSystemExit):
artifacts.BundleImageArchives(
request, self.response, self.api_config
)
def testInvalidOutputDir(self) -> None:
"""Test invalid output dir fails."""
request = self.BuildTargetRequest(
build_target="board", output_dir=os.path.join(self.tempdir, "DNE")
)
with self.assertRaises(cros_build_lib.DieSystemExit):
artifacts.BundleImageArchives(
request, self.response, self.api_config
)
def testOutputHandling(self) -> None:
"""Test the artifact output handling."""
expected = [os.path.join(self.output_dir, f) for f in ("a", "b", "c")]
self.PatchObject(artifacts_svc, "ArchiveImages", return_value=expected)
self.PatchObject(os.path, "exists", return_value=True)
artifacts.BundleImageArchives(
self.sysroot_request, self.response, self.api_config
)
self.assertCountEqual(
expected, [a.artifact_path.path for a in self.response.artifacts]
)
class BundleImageZipTest(BundleTestCase):
"""Unittests for BundleImageZip."""
def testValidateOnly(self) -> None:
"""Quick check that a validate only call does not execute any logic."""
patch = self.PatchObject(commands, "BuildImageZip")
artifacts.BundleImageZip(
self.target_request, self.response, self.validate_only_config
)
patch.assert_not_called()
def testMockCall(self) -> None:
"""Test a mock call does not execute logic, returns mocked value."""
patch = self.PatchObject(commands, "BuildImageZip")
artifacts.BundleImageZip(
self.target_request, self.response, self.mock_call_config
)
patch.assert_not_called()
self.assertEqual(len(self.response.artifacts), 1)
self.assertEqual(
self.response.artifacts[0].artifact_path.path,
os.path.join(self.output_dir, "image.zip"),
)
def testBundleImageZip(self) -> None:
"""BundleImageZip calls cbuildbot/commands with correct args."""
bundle_image_zip = self.PatchObject(
artifacts_svc, "BundleImageZip", return_value="image.zip"
)
self.PatchObject(os.path, "exists", return_value=True)
artifacts.BundleImageZip(
self.target_request, self.response, self.api_config
)
self.assertEqual(
[
artifact.artifact_path.path
for artifact in self.response.artifacts
],
[os.path.join(self.output_dir, "image.zip")],
)
latest = os.path.join(
self.source_root, "src/build/images/target/latest"
)
self.assertEqual(
bundle_image_zip.call_args_list,
[mock.call(self.output_dir, latest)],
)
def testBundleImageZipNoImageDir(self) -> None:
"""BundleImageZip dies when image dir does not exist."""
self.PatchObject(os.path, "exists", return_value=False)
with self.assertRaises(cros_build_lib.DieSystemExit):
artifacts.BundleImageZip(
self.target_request, self.response, self.api_config
)
class BundleAutotestFilesTest(BundleTestCase):
"""Unittests for BundleAutotestFiles."""
def testValidateOnly(self) -> None:
"""Quick check that a validate only call does not execute any logic."""
patch = self.PatchObject(artifacts_svc, "BundleAutotestFiles")
artifacts.BundleAutotestFiles(
self.sysroot_request, self.response, self.validate_only_config
)
patch.assert_not_called()
def testMockCall(self) -> None:
"""Test a mock call does not execute logic, returns mocked value."""
patch = self.PatchObject(artifacts_svc, "BundleAutotestFiles")
artifacts.BundleAutotestFiles(
self.sysroot_request, self.response, self.mock_call_config
)
patch.assert_not_called()
self.assertEqual(len(self.response.artifacts), 1)
self.assertEqual(
self.response.artifacts[0].artifact_path.path,
os.path.join(self.output_dir, "autotest-a.tar.gz"),
)
def testBundleAutotestFiles(self) -> None:
"""BundleAutotestFiles calls service correctly."""
files = {
artifacts_svc.ARCHIVE_CONTROL_FILES: (
"/tmp/artifacts/autotest-a.tar.gz"
),
artifacts_svc.ARCHIVE_PACKAGES: "/tmp/artifacts/autotest-b.tar.gz",
}
patch = self.PatchObject(
artifacts_svc, "BundleAutotestFiles", return_value=files
)
artifacts.BundleAutotestFiles(
self.sysroot_request, self.response, self.api_config
)
# Verify the arguments are being passed through.
patch.assert_called_with(mock.ANY, self.sysroot, self.output_dir)
# Verify the output proto is being populated correctly.
self.assertTrue(self.response.artifacts)
paths = [
artifact.artifact_path.path for artifact in self.response.artifacts
]
self.assertCountEqual(list(files.values()), paths)
def testInvalidOutputDir(self) -> None:
"""Test invalid output directory argument."""
request = self.SysrootRequest(
chroot=self.chroot.path, sysroot=self.sysroot_path
)
with self.assertRaises(cros_build_lib.DieSystemExit):
artifacts.BundleAutotestFiles(
request, self.response, self.api_config
)
def testInvalidSysroot(self) -> None:
"""Test no sysroot directory."""
request = self.SysrootRequest(
chroot=self.chroot.path, output_dir=self.output_dir
)
with self.assertRaises(cros_build_lib.DieSystemExit):
artifacts.BundleAutotestFiles(
request, self.response, self.api_config
)
def testSysrootDoesNotExist(self) -> None:
"""Test dies when no sysroot does not exist."""
request = self.SysrootRequest(
chroot=self.chroot.path,
sysroot="/does/not/exist",
output_dir=self.output_dir,
)
artifacts.BundleAutotestFiles(request, self.response, self.api_config)
self.assertFalse(self.response.artifacts)
class BundleTastFilesTest(BundleTestCase):
"""Unittests for BundleTastFiles."""
def testValidateOnly(self) -> None:
"""Quick check that a validate only call does not execute any logic."""
patch = self.PatchObject(artifacts_svc, "BundleTastFiles")
artifacts.BundleTastFiles(
self.sysroot_request, self.response, self.validate_only_config
)
patch.assert_not_called()
def testMockCall(self) -> None:
"""Test a mock call does not execute logic, returns mocked value."""
patch = self.PatchObject(artifacts_svc, "BundleTastFiles")
artifacts.BundleTastFiles(
self.sysroot_request, self.response, self.mock_call_config
)
patch.assert_not_called()
self.assertEqual(len(self.response.artifacts), 2)
self.assertEqual(
self.response.artifacts[0].artifact_path.path,
os.path.join(self.output_dir, "tast_bundles.tar.gz"),
)
def testBundleTastFilesNoLogs(self) -> None:
"""BundleTasteFiles succeeds when no tast files found."""
self.PatchObject(commands, "BuildTastBundleTarball", return_value=None)
artifacts.BundleTastFiles(
self.sysroot_request, self.response, self.api_config
)
self.assertFalse(self.response.artifacts)
def testBundleTastFiles(self) -> None:
"""BundleTastFiles calls service correctly."""
expected_archive = os.path.join(
self.output_dir, artifacts_svc.TAST_BUNDLE_NAME
)
# Patch the service being called.
bundle_patch = self.PatchObject(
artifacts_svc, "BundleTastFiles", return_value=expected_archive
)
artifacts.BundleTastFiles(
self.sysroot_request, self.response, self.api_config
)
# Make sure the artifact got recorded successfully.
self.assertTrue(self.response.artifacts)
self.assertEqual(
expected_archive, self.response.artifacts[0].artifact_path.path
)
# Make sure the service got called correctly.
bundle_patch.assert_called_once_with(
self.chroot, self.sysroot, self.output_dir
)
class BundleFirmwareTest(BundleTestCase):
"""Unittests for BundleFirmware."""
def testValidateOnly(self) -> None:
"""Quick check that a validate only call does not execute any logic."""
patch = self.PatchObject(artifacts_svc, "BundleTastFiles")
artifacts.BundleFirmware(
self.sysroot_request, self.response, self.validate_only_config
)
patch.assert_not_called()
def testMockCall(self) -> None:
"""Test a mock call does not execute logic, returns mocked value."""
patch = self.PatchObject(artifacts_svc, "BundleTastFiles")
artifacts.BundleFirmware(
self.sysroot_request, self.response, self.mock_call_config
)
patch.assert_not_called()
self.assertEqual(len(self.response.artifacts), 1)
self.assertEqual(
self.response.artifacts[0].artifact_path.path,
os.path.join(self.output_dir, "firmware.tar.gz"),
)
def testBundleFirmware(self) -> None:
"""BundleFirmware calls cbuildbot/commands with correct args."""
self.PatchObject(
artifacts_svc,
"BuildFirmwareArchive",
return_value=os.path.join(self.output_dir, "firmware.tar.gz"),
)
artifacts.BundleFirmware(
self.sysroot_request, self.response, self.api_config
)
self.assertEqual(
[
artifact.artifact_path.path
for artifact in self.response.artifacts
],
[os.path.join(self.output_dir, "firmware.tar.gz")],
)
def testBundleFirmwareNoLogs(self) -> None:
"""BundleFirmware dies when no firmware found."""
self.PatchObject(commands, "BuildFirmwareArchive", return_value=None)
artifacts.BundleFirmware(
self.sysroot_request, self.response, self.api_config
)
self.assertEqual(len(self.response.artifacts), 0)
class BundleFpmcuUnittestsTest(BundleTestCase):
"""Unittests for BundleFpmcuUnittests."""
def testValidateOnly(self) -> None:
"""Quick check that a validate only call does not execute any logic."""
patch = self.PatchObject(artifacts_svc, "BundleFpmcuUnittests")
artifacts.BundleFpmcuUnittests(
self.sysroot_request, self.response, self.validate_only_config
)
patch.assert_not_called()
def testMockCall(self) -> None:
"""Test a mock call does not execute logic, returns mocked value."""
patch = self.PatchObject(artifacts_svc, "BundleFpmcuUnittests")
artifacts.BundleFpmcuUnittests(
self.sysroot_request, self.response, self.mock_call_config
)
patch.assert_not_called()
self.assertEqual(len(self.response.artifacts), 1)
self.assertEqual(
self.response.artifacts[0].artifact_path.path,
os.path.join(self.output_dir, "fpmcu_unittests.tar.gz"),
)
def testBundleFpmcuUnittests(self) -> None:
"""BundleFpmcuUnittests calls cbuildbot/commands with correct args."""
self.PatchObject(
artifacts_svc,
"BundleFpmcuUnittests",
return_value=os.path.join(
self.output_dir, "fpmcu_unittests.tar.gz"
),
)
artifacts.BundleFpmcuUnittests(
self.sysroot_request, self.response, self.api_config
)
self.assertEqual(
[
artifact.artifact_path.path
for artifact in self.response.artifacts
],
[os.path.join(self.output_dir, "fpmcu_unittests.tar.gz")],
)
def testBundleFpmcuUnittestsNoLogs(self) -> None:
"""BundleFpmcuUnittests does not die when no fpmcu unittests found."""
self.PatchObject(
artifacts_svc, "BundleFpmcuUnittests", return_value=None
)
artifacts.BundleFpmcuUnittests(
self.sysroot_request, self.response, self.api_config
)
self.assertFalse(self.response.artifacts)
class BundleEbuildLogsTest(BundleTestCase):
"""Unittests for BundleEbuildLogs."""
def testValidateOnly(self) -> None:
"""Quick check that a validate only call does not execute any logic."""
patch = self.PatchObject(commands, "BuildEbuildLogsTarball")
artifacts.BundleEbuildLogs(
self.sysroot_request, self.response, self.validate_only_config
)
patch.assert_not_called()
def testMockCall(self) -> None:
"""Test a mock call does not execute logic, returns mocked value."""
patch = self.PatchObject(commands, "BuildEbuildLogsTarball")
artifacts.BundleEbuildLogs(
self.sysroot_request, self.response, self.mock_call_config
)
patch.assert_not_called()
self.assertEqual(len(self.response.artifacts), 1)
self.assertEqual(
self.response.artifacts[0].artifact_path.path,
os.path.join(self.output_dir, "ebuild-logs.tar.gz"),
)
def testBundleEbuildLogs(self) -> None:
"""BundleEbuildLogs calls cbuildbot/commands with correct args."""
bundle_ebuild_logs_tarball = self.PatchObject(
artifacts_svc,
"BundleEBuildLogsTarball",
return_value="ebuild-logs.tar.gz",
)
artifacts.BundleEbuildLogs(
self.sysroot_request, self.response, self.api_config
)
self.assertEqual(
[
artifact.artifact_path.path
for artifact in self.response.artifacts
],
[os.path.join(self.output_dir, "ebuild-logs.tar.gz")],
)
self.assertEqual(
bundle_ebuild_logs_tarball.call_args_list,
[mock.call(mock.ANY, self.sysroot, self.output_dir)],
)
def testBundleEbuildLogsNoLogs(self) -> None:
"""BundleEbuildLogs dies when no logs found."""
self.PatchObject(commands, "BuildEbuildLogsTarball", return_value=None)
artifacts.BundleEbuildLogs(
self.sysroot_request, self.response, self.api_config
)
self.assertFalse(self.response.artifacts)
class BundleChromeOSConfigTest(BundleTestCase):
"""Unittests for BundleChromeOSConfig"""
def testValidateOnly(self) -> None:
"""Quick check that a validate only call does not execute any logic."""
patch = self.PatchObject(artifacts_svc, "BundleChromeOSConfig")
artifacts.BundleChromeOSConfig(
self.sysroot_request, self.response, self.validate_only_config
)
patch.assert_not_called()
def testMockCall(self) -> None:
"""Test a mock call does not execute logic, returns mocked value."""
patch = self.PatchObject(artifacts_svc, "BundleChromeOSConfig")
artifacts.BundleChromeOSConfig(
self.sysroot_request, self.response, self.mock_call_config
)
patch.assert_not_called()
self.assertEqual(len(self.response.artifacts), 1)
self.assertEqual(
self.response.artifacts[0].artifact_path.path,
os.path.join(self.output_dir, "config.yaml"),
)
def testBundleChromeOSConfigSuccess(self) -> None:
"""Test standard success case."""
bundle_chromeos_config = self.PatchObject(
artifacts_svc, "BundleChromeOSConfig", return_value="config.yaml"
)
artifacts.BundleChromeOSConfig(
self.sysroot_request, self.response, self.api_config
)
self.assertEqual(
[
artifact.artifact_path.path
for artifact in self.response.artifacts
],
[os.path.join(self.output_dir, "config.yaml")],
)
self.assertEqual(
bundle_chromeos_config.call_args_list,
[mock.call(mock.ANY, self.sysroot, self.output_dir)],
)
def testBundleChromeOSConfigNoConfigFound(self) -> None:
"""Empty results when the config payload isn't found."""
self.PatchObject(
artifacts_svc, "BundleChromeOSConfig", return_value=None
)
artifacts.BundleChromeOSConfig(
self.sysroot_request, self.response, self.api_config
)
self.assertFalse(self.response.artifacts)
class BundleTestUpdatePayloadsTest(
cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin
):
"""Unittests for BundleTestUpdatePayloads."""
def setUp(self) -> None:
self.source_root = os.path.join(self.tempdir, "cros")
osutils.SafeMakedirs(self.source_root)
self.archive_root = os.path.join(self.tempdir, "output")
osutils.SafeMakedirs(self.archive_root)
self.target = "target"
self.image_root = os.path.join(
self.source_root, "src/build/images/target/latest"
)
self.request = artifacts_pb2.BundleRequest()
self.request.build_target.name = self.target
self.request.output_dir = self.archive_root
self.request.result_path.path.path = self.archive_root
self.request.result_path.path.location = (
common_pb2.Path.Location.OUTSIDE
)
self.response = artifacts_pb2.BundleResponse()
self.PatchObject(constants, "SOURCE_ROOT", new=self.source_root)
def MockPayloads(_, image_path, archive_dir):
osutils.WriteFile(
os.path.join(archive_dir, "payload1.bin"), image_path
)
osutils.WriteFile(
os.path.join(archive_dir, "payload2.bin"), image_path
)
return [
os.path.join(archive_dir, "payload1.bin"),
os.path.join(archive_dir, "payload2.bin"),
]
self.bundle_patch = self.PatchObject(
artifacts_svc, "BundleTestUpdatePayloads", side_effect=MockPayloads
)
def testValidateOnly(self) -> None:
"""Quick check that a validate only call does not execute any logic."""
patch = self.PatchObject(artifacts_svc, "BundleTestUpdatePayloads")
artifacts.BundleTestUpdatePayloads(
self.request, self.response, self.validate_only_config
)
patch.assert_not_called()
def testMockCall(self) -> None:
"""Test a mock call does not execute logic, returns mocked value."""
patch = self.PatchObject(artifacts_svc, "BundleTestUpdatePayloads")
artifacts.BundleTestUpdatePayloads(
self.request, self.response, self.mock_call_config
)
patch.assert_not_called()
self.assertEqual(len(self.response.artifacts), 3)
self.assertEqual(
self.response.artifacts[0].artifact_path.path,
os.path.join(self.archive_root, "payload1.bin"),
)
self.assertEqual(
self.response.artifacts[1].artifact_path.path,
os.path.join(self.archive_root, "payload1.json"),
)
self.assertEqual(
self.response.artifacts[2].artifact_path.path,
os.path.join(self.archive_root, "payload1.log"),
)
def testBundleTestUpdatePayloads(self) -> None:
"""BundleTestUpdatePayloads calls cbuildbot/commands correctly."""
image_path = os.path.join(self.image_root, constants.BASE_IMAGE_BIN)
osutils.WriteFile(image_path, "image!", makedirs=True)
artifacts.BundleTestUpdatePayloads(
self.request, self.response, self.api_config
)
actual = [
os.path.basename(artifact.artifact_path.path)
for artifact in self.response.artifacts
]
expected = ["payload1.bin", "payload2.bin"]
self.assertCountEqual(actual, expected)
actual = [
os.path.basename(path)
for path in osutils.DirectoryIterator(
os.path.dirname(self.response.artifacts[0].artifact_path.path)
)
]
self.assertCountEqual(actual, expected)
def testBundleTestUpdatePayloadsNoImageDir(self) -> None:
"""BundleTestUpdatePayloads dies if no image dir is found."""
# Intentionally do not write image directory.
artifacts.BundleTestUpdatePayloads(
self.request, self.response, self.api_config
)
self.assertFalse(self.response.artifacts)
def testBundleTestUpdatePayloadsNoImage(self) -> None:
"""BundleTestUpdatePayloads dies if no usable image found for target."""
# Intentionally do not write image, but create the directory.
osutils.SafeMakedirs(self.image_root)
with self.assertRaises(cros_build_lib.DieSystemExit):
artifacts.BundleTestUpdatePayloads(
self.request, self.response, self.api_config
)
class BundleSimpleChromeArtifactsTest(
cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin
):
"""BundleSimpleChromeArtifacts tests."""
def setUp(self) -> None:
self.PatchObject(cros_build_lib, "IsInsideChroot", return_value=False)
self.chroot = chroot_lib.Chroot(
path=self.tempdir / "chroot",
out_path=self.tempdir / "out",
)
self.sysroot_path = "/sysroot"
self.sysroot_dir = self.chroot.full_path(self.sysroot_path)
osutils.SafeMakedirs(self.sysroot_dir)
self.output_dir = os.path.join(self.tempdir, "output_dir")
osutils.SafeMakedirs(self.output_dir)
self.does_not_exist = os.path.join(self.tempdir, "does_not_exist")
self.response = artifacts_pb2.BundleResponse()
def _GetRequest(
self,
chroot: Optional[chroot_lib.Chroot] = None,
sysroot: Optional[str] = None,
build_target: Optional[str] = None,
output_dir: Optional[str] = None,
) -> artifacts_pb2.BundleRequest:
"""Helper to create a request message instance.
Args:
chroot: The chroot path.
sysroot: The sysroot path.
build_target: The build target name.
output_dir: The output directory.
"""
return artifacts_pb2.BundleRequest(
sysroot={"path": sysroot, "build_target": {"name": build_target}},
chroot={
"path": chroot.path if chroot else None,
"out_path": str(chroot.out_path) if chroot else None,
},
result_path=common_pb2.ResultPath(
path=common_pb2.Path(
path=output_dir,
location=common_pb2.Path.OUTSIDE,
)
),
)
def testValidateOnly(self) -> None:
"""Quick check that a validate only call does not execute any logic."""
patch = self.PatchObject(artifacts_svc, "BundleSimpleChromeArtifacts")
request = self._GetRequest(
chroot=self.chroot,
sysroot=self.sysroot_path,
build_target="board",
output_dir=self.output_dir,
)
artifacts.BundleSimpleChromeArtifacts(
request, self.response, self.validate_only_config
)
patch.assert_not_called()
def testMockCall(self) -> None:
"""Test a mock call does not execute logic, returns mocked value."""
patch = self.PatchObject(artifacts_svc, "BundleSimpleChromeArtifacts")
request = self._GetRequest(
chroot=self.chroot,
sysroot=self.sysroot_path,
build_target="board",
output_dir=self.output_dir,
)
artifacts.BundleSimpleChromeArtifacts(
request, self.response, self.mock_call_config
)
patch.assert_not_called()
self.assertEqual(len(self.response.artifacts), 1)
self.assertEqual(
self.response.artifacts[0].artifact_path.path,
os.path.join(self.output_dir, "simple_chrome.txt"),
)
def testNoBuildTarget(self) -> None:
"""Test no build target fails."""
request = self._GetRequest(
chroot=self.chroot,
sysroot=self.sysroot_path,
output_dir=self.output_dir,
)
response = self.response
with self.assertRaises(cros_build_lib.DieSystemExit):
artifacts.BundleSimpleChromeArtifacts(
request, response, self.api_config
)
def testNoSysroot(self) -> None:
"""Test no sysroot fails."""
request = self._GetRequest(
build_target="board", output_dir=self.output_dir
)
response = self.response
with self.assertRaises(cros_build_lib.DieSystemExit):
artifacts.BundleSimpleChromeArtifacts(
request, response, self.api_config
)
def testSysrootDoesNotExist(self) -> None:
"""Test no sysroot fails."""
request = self._GetRequest(
build_target="board",
output_dir=self.output_dir,
sysroot=self.does_not_exist,
)
response = self.response
artifacts.BundleSimpleChromeArtifacts(
request, response, self.api_config
)
self.assertFalse(self.response.artifacts)
def testNoOutputDir(self) -> None:
"""Test no output dir fails."""
request = self._GetRequest(
chroot=self.chroot,
sysroot=self.sysroot_path,
build_target="board",
)
response = self.response
with self.assertRaises(cros_build_lib.DieSystemExit):
artifacts.BundleSimpleChromeArtifacts(
request, response, self.api_config
)
def testOutputDirDoesNotExist(self) -> None:
"""Test no output dir fails."""
request = self._GetRequest(
chroot=self.chroot,
sysroot=self.sysroot_path,
build_target="board",
output_dir=self.does_not_exist,
)
response = self.response
with self.assertRaises(cros_build_lib.DieSystemExit):
artifacts.BundleSimpleChromeArtifacts(
request, response, self.api_config
)
def testOutputHandling(self) -> None:
"""Test response output."""
files = ["file1", "file2", "file3"]
expected_files = [os.path.join(self.output_dir, f) for f in files]
self.PatchObject(
artifacts_svc,
"BundleSimpleChromeArtifacts",
return_value=expected_files,
)
request = self._GetRequest(
chroot=self.chroot,
sysroot=self.sysroot_path,
build_target="board",
output_dir=self.output_dir,
)
response = self.response
artifacts.BundleSimpleChromeArtifacts(
request, response, self.api_config
)
self.assertTrue(response.artifacts)
self.assertCountEqual(
expected_files, [a.artifact_path.path for a in response.artifacts]
)
class BundleVmFilesTest(
cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin
):
"""BuildVmFiles tests."""
def setUp(self) -> None:
self.output_dir = os.path.join(self.tempdir, "output")
osutils.SafeMakedirs(self.output_dir)
self.response = artifacts_pb2.BundleResponse()
def _GetInput(
self,
chroot: Optional[str] = None,
sysroot: Optional[str] = None,
test_results_dir: Optional[str] = None,
output_dir: Optional[str] = None,
) -> artifacts_pb2.BundleVmFilesRequest:
"""Helper to build out an input message instance.
Args:
chroot: The chroot path.
sysroot: The sysroot path relative to the chroot.
test_results_dir: The test results directory relative to the
sysroot.
output_dir: The directory where the results tarball should be saved.
"""
return artifacts_pb2.BundleVmFilesRequest(
chroot={"path": chroot},
sysroot={"path": sysroot},
test_results_dir=test_results_dir,
output_dir=output_dir,
)
def testValidateOnly(self) -> None:
"""Quick check that a validate only call does not execute any logic."""
patch = self.PatchObject(artifacts_svc, "BundleVmFiles")
in_proto = self._GetInput(
chroot="/chroot/dir",
sysroot="/build/board",
test_results_dir="/test/results",
output_dir=self.output_dir,
)
artifacts.BundleVmFiles(
in_proto, self.response, self.validate_only_config
)
patch.assert_not_called()
def testMockCall(self) -> None:
"""Test a mock call does not execute logic, returns mocked value."""
patch = self.PatchObject(artifacts_svc, "BundleVmFiles")
in_proto = self._GetInput(
chroot="/chroot/dir",
sysroot="/build/board",
test_results_dir="/test/results",
output_dir=self.output_dir,
)
artifacts.BundleVmFiles(in_proto, self.response, self.mock_call_config)
patch.assert_not_called()
self.assertEqual(len(self.response.artifacts), 1)
self.assertEqual(
self.response.artifacts[0].path,
os.path.join(self.output_dir, "f1.tar"),
)
def testChrootMissing(self) -> None:
"""Test error handling for missing chroot."""
in_proto = self._GetInput(
sysroot="/build/board",
test_results_dir="/test/results",
output_dir=self.output_dir,
)
with self.assertRaises(cros_build_lib.DieSystemExit):
artifacts.BundleVmFiles(in_proto, self.response, self.api_config)
def testTestResultsDirMissing(self) -> None:
"""Test error handling for missing test results directory."""
in_proto = self._GetInput(
chroot="/chroot/dir",
sysroot="/build/board",
output_dir=self.output_dir,
)
with self.assertRaises(cros_build_lib.DieSystemExit):
artifacts.BundleVmFiles(in_proto, self.response, self.api_config)
def testOutputDirMissing(self) -> None:
"""Test error handling for missing output directory."""
in_proto = self._GetInput(
chroot="/chroot/dir",
sysroot="/build/board",
test_results_dir="/test/results",
)
with self.assertRaises(cros_build_lib.DieSystemExit):
artifacts.BundleVmFiles(in_proto, self.response, self.api_config)
def testOutputDirDoesNotExist(self) -> None:
"""Test error handling for output directory that does not exist."""
in_proto = self._GetInput(
chroot="/chroot/dir",
sysroot="/build/board",
output_dir=os.path.join(self.tempdir, "dne"),
test_results_dir="/test/results",
)
with self.assertRaises(cros_build_lib.DieSystemExit):
artifacts.BundleVmFiles(in_proto, self.response, self.api_config)
def testValidCall(self) -> None:
"""Test image dir building."""
in_proto = self._GetInput(
chroot="/chroot/dir",
sysroot="/build/board",
test_results_dir="/test/results",
output_dir=self.output_dir,
)
expected_files = ["/tmp/output/f1.tar", "/tmp/output/f2.tar"]
patch = self.PatchObject(
artifacts_svc, "BundleVmFiles", return_value=expected_files
)
artifacts.BundleVmFiles(in_proto, self.response, self.api_config)
patch.assert_called_with(mock.ANY, "/test/results", self.output_dir)
# Make sure we have artifacts, and that every artifact is an expected
# file.
self.assertTrue(self.response.artifacts)
for artifact in self.response.artifacts:
self.assertIn(artifact.path, expected_files)
expected_files.remove(artifact.path)
# Make sure we've seen all of the expected files.
self.assertFalse(expected_files)
class BundleGceTarballTest(BundleTestCase):
"""Unittests for BundleGceTarball."""
def testValidateOnly(self) -> None:
"""Check that a validate only call does not execute any logic."""
patch = self.PatchObject(artifacts_svc, "BundleGceTarball")
artifacts.BundleGceTarball(
self.target_request, self.response, self.validate_only_config
)
patch.assert_not_called()
def testMockCall(self) -> None:
"""Test a mock call does not execute logic, returns mocked value."""
patch = self.PatchObject(artifacts_svc, "BundleGceTarball")
artifacts.BundleGceTarball(
self.target_request, self.response, self.mock_call_config
)
patch.assert_not_called()
self.assertEqual(len(self.response.artifacts), 1)
self.assertEqual(
self.response.artifacts[0].artifact_path.path,
os.path.join(self.output_dir, constants.TEST_IMAGE_GCE_TAR),
)
def testBundleGceTarball(self) -> None:
"""BundleGceTarball calls cbuildbot/commands with correct args."""
bundle_gce_tarball = self.PatchObject(
artifacts_svc,
"BundleGceTarball",
return_value=os.path.join(
self.output_dir, constants.TEST_IMAGE_GCE_TAR
),
)
self.PatchObject(os.path, "exists", return_value=True)
artifacts.BundleGceTarball(
self.target_request, self.response, self.api_config
)
self.assertEqual(
[
artifact.artifact_path.path
for artifact in self.response.artifacts
],
[os.path.join(self.output_dir, constants.TEST_IMAGE_GCE_TAR)],
)
latest = os.path.join(
self.source_root, "src/build/images/target/latest"
)
self.assertEqual(
bundle_gce_tarball.call_args_list,
[mock.call(self.output_dir, latest)],
)
def testBundleGceTarballNoImageDir(self) -> None:
"""BundleGceTarball dies when image dir does not exist."""
self.PatchObject(os.path, "exists", return_value=False)
with self.assertRaises(cros_build_lib.DieSystemExit):
artifacts.BundleGceTarball(
self.target_request, self.response, self.api_config
)
class FetchCentralizedSuitesTestCase(
cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin
):
"""Unittests for FetchCentralizedSuites."""
sysroot_path = "/build/coral"
chroot_name = "chroot"
def setUp(self) -> None:
self.PatchObject(cros_build_lib, "IsInsideChroot", return_value=False)
self.chroot = chroot_lib.Chroot(
path=self.tempdir / "chroot",
out_path=self.tempdir / "out",
)
pathlib.Path(self.chroot.path).touch()
self.chroot.out_path.touch()
self.expected_suite_set_file = self.chroot.full_path(
"/build/coral/usr/share/centralized-suites/suite_sets.pb"
)
self.expected_suite_file = self.chroot.full_path(
"/build/coral/usr/share/centralized-suites/suites.pb"
)
self.expected_mock_suite_set = "/centralized-suites/suite_sets.pb"
self.expected_mock_suite = "/centralized-suites/suites.pb"
self.PatchObject(cros_build_lib, "AssertOutsideChroot")
def createFetchCentralizedSuitesRequest(
self, use_sysroot_path=True, use_chroot=True
):
"""Construct a FetchCentralizedSuitesRequest for use in test cases."""
request = artifacts_pb2.FetchCentralizedSuitesRequest()
if use_sysroot_path:
request.sysroot.path = self.sysroot_path
if use_chroot:
request.chroot.path = self.chroot.path
request.chroot.out_path = str(self.chroot.out_path)
return request
def testValidateOnly(self) -> None:
"""Check that a validate only call does not execute any logic."""
patch = self.PatchObject(controller_util, "ParseSysroot")
request = self.createFetchCentralizedSuitesRequest()
response = artifacts_pb2.FetchCentralizedSuitesResponse()
artifacts.FetchCentralizedSuites(
request, response, self.validate_only_config
)
patch.assert_not_called()
def testMockCall(self) -> None:
"""Test a mock call does not execute logic, returns mocked value."""
patch = self.PatchObject(controller_util, "ParseSysroot")
request = self.createFetchCentralizedSuitesRequest()
response = artifacts_pb2.FetchCentralizedSuitesResponse()
artifacts.FetchCentralizedSuites(
request, response, self.mock_call_config
)
patch.assert_not_called()
self.assertEqual(
response.suite_set_file.path.path, self.expected_mock_suite_set
)
self.assertEqual(
response.suite_file.path.path, self.expected_mock_suite
)
def testNoSysrootPath(self) -> None:
"""Check that a request with no sysroot.path results in failure."""
request = self.createFetchCentralizedSuitesRequest(
use_sysroot_path=False
)
response = artifacts_pb2.FetchCentralizedSuitesResponse()
with self.assertRaises(cros_build_lib.DieSystemExit):
artifacts.FetchCentralizedSuites(request, response, self.api_config)
def testNoChroot(self) -> None:
"""Check that a request with no chroot results in failure."""
request = self.createFetchCentralizedSuitesRequest(use_chroot=False)
response = artifacts_pb2.FetchCentralizedSuitesResponse()
with self.assertRaises(cros_build_lib.DieSystemExit):
artifacts.FetchCentralizedSuites(request, response, self.api_config)
def testSuccess(self) -> None:
"""Check that a well-formed request yields the expected results."""
request = self.createFetchCentralizedSuitesRequest(use_chroot=True)
response = artifacts_pb2.FetchCentralizedSuitesResponse()
artifacts.FetchCentralizedSuites(request, response, self.api_config)
self.assertEqual(
response.suite_set_file.path.path, self.expected_suite_set_file
)
self.assertEqual(
response.suite_file.path.path, self.expected_suite_file
)
self.assertEqual(
response.suite_set_file.path.location, common_pb2.Path.OUTSIDE
)
self.assertEqual(
response.suite_file.path.location, common_pb2.Path.OUTSIDE
)
class FetchMetadataTestCase(
cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin
):
"""Unittests for FetchMetadata."""
sysroot_path = "/build/coral"
chroot_name = "chroot"
def setUp(self) -> None:
self.PatchObject(cros_build_lib, "IsInsideChroot", return_value=False)
self.chroot = chroot_lib.Chroot(
path=self.tempdir / "chroot",
out_path=self.tempdir / "out",
)
pathlib.Path(self.chroot.path).touch()
self.chroot.out_path.touch()
self.expected_filepaths = [
self.chroot.full_path(fp)
for fp in (
"/build/coral/usr/local/build/autotest/autotest_metadata.pb",
"/build/coral/usr/share/tast/metadata/local/cros.pb",
"/build/coral/build/share/tast/metadata/local/crosint.pb",
"/usr/share/tast/metadata/remote/cros.pb",
"/build/coral/usr/local/build/gtest/gtest_metadata.pb",
)
]
self.PatchObject(cros_build_lib, "AssertOutsideChroot")
def createFetchMetadataRequest(
self, use_sysroot_path=True, use_chroot=True
):
"""Construct a FetchMetadataRequest for use in test cases."""
request = artifacts_pb2.FetchMetadataRequest()
if use_sysroot_path:
request.sysroot.path = self.sysroot_path
if use_chroot:
request.chroot.path = self.chroot.path
request.chroot.out_path = str(self.chroot.out_path)
return request
def testValidateOnly(self) -> None:
"""Check that a validate only call does not execute any logic."""
patch = self.PatchObject(controller_util, "ParseSysroot")
request = self.createFetchMetadataRequest()
response = artifacts_pb2.FetchMetadataResponse()
artifacts.FetchMetadata(request, response, self.validate_only_config)
patch.assert_not_called()
def testMockCall(self) -> None:
"""Test a mock call does not execute logic, returns mocked value."""
patch = self.PatchObject(controller_util, "ParseSysroot")
request = self.createFetchMetadataRequest()
response = artifacts_pb2.FetchMetadataResponse()
artifacts.FetchMetadata(request, response, self.mock_call_config)
patch.assert_not_called()
self.assertGreater(len(response.filepaths), 0)
def testNoSysrootPath(self) -> None:
"""Check that a request with no sysroot.path results in failure."""
request = self.createFetchMetadataRequest(use_sysroot_path=False)
response = artifacts_pb2.FetchMetadataResponse()
with self.assertRaises(cros_build_lib.DieSystemExit):
artifacts.FetchMetadata(request, response, self.api_config)
def testNoChroot(self) -> None:
"""Check that a request with no chroot results in failure."""
request = self.createFetchMetadataRequest(use_chroot=False)
response = artifacts_pb2.FetchMetadataResponse()
with self.assertRaises(cros_build_lib.DieSystemExit):
artifacts.FetchMetadata(request, response, self.api_config)
def testSuccess(self) -> None:
"""Check that a well-formed request yields the expected results."""
request = self.createFetchMetadataRequest(use_chroot=True)
response = artifacts_pb2.FetchMetadataResponse()
artifacts.FetchMetadata(request, response, self.api_config)
actual_filepaths = [fp.path.path for fp in response.filepaths]
self.assertEqual(
sorted(actual_filepaths), sorted(self.expected_filepaths)
)
self.assertTrue(
all(
fp.path.location == common_pb2.Path.OUTSIDE
for fp in response.filepaths
)
)
class GetTest(cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin):
"""Get function tests."""
def setUp(self) -> None:
self.sysroot_path = "/build/target"
self.sysroot = sysroot_lib.Sysroot(self.sysroot_path)
def _InputProto(self):
"""Helper to build an input proto instance."""
# pylint: disable=line-too-long
return artifacts_pb2.GetRequest(
sysroot=sysroot_pb2.Sysroot(path=self.sysroot_path),
artifact_info=common_pb2.ArtifactsByService(
sysroot=common_pb2.ArtifactsByService.Sysroot(
output_artifacts=[
common_pb2.ArtifactsByService.Sysroot.ArtifactInfo(
artifact_types=[
common_pb2.ArtifactsByService.Sysroot.ArtifactType.FUZZER_SYSROOT
]
)
],
),
image=common_pb2.ArtifactsByService.Image(
output_artifacts=[
common_pb2.ArtifactsByService.Image.ArtifactInfo(
artifact_types=[
common_pb2.ArtifactsByService.Image.ArtifactType.LICENSE_CREDITS
]
)
],
),
test=common_pb2.ArtifactsByService.Test(
output_artifacts=[
common_pb2.ArtifactsByService.Test.ArtifactInfo(
artifact_types=[
common_pb2.ArtifactsByService.Test.ArtifactType.HWQUAL
]
)
],
),
),
result_path=common_pb2.ResultPath(
path=common_pb2.Path(path=str(self.tempdir))
),
)
# pylint: enable=line-too-long
def _OutputProto(self):
"""Helper to build an output proto instance."""
return artifacts_pb2.GetResponse()
def testSuccess(self) -> None:
"""Test Get."""
# pylint: disable=line-too-long
image_mock = self.PatchObject(
image_controller,
"GetArtifacts",
return_value=[
{
"paths": ["/foo/bar/license_credits.html"],
"type": common_pb2.ArtifactsByService.Image.ArtifactType.LICENSE_CREDITS,
}
],
)
sysroot_mock = self.PatchObject(
sysroot_controller,
"GetArtifacts",
return_value=[
{
"type": common_pb2.ArtifactsByService.Sysroot.ArtifactType.FUZZER_SYSROOT,
"failed": True,
"failure_reason": "Bad data!",
}
],
)
test_mock = self.PatchObject(
test_controller,
"GetArtifacts",
return_value=[
{
"paths": ["/foo/bar/hwqual.tar.xz"],
"type": common_pb2.ArtifactsByService.Test.ArtifactType.HWQUAL,
}
],
)
# pylint: enable=line-too-long
in_proto = self._InputProto()
out_proto = self._OutputProto()
artifacts.Get(
in_proto,
out_proto,
self.api_config,
)
image_mock.assert_called_once()
sysroot_mock.assert_called_once()
test_mock.assert_called_once()
# pylint: disable=line-too-long
expected = common_pb2.UploadedArtifactsByService(
sysroot=common_pb2.UploadedArtifactsByService.Sysroot(
artifacts=[
common_pb2.UploadedArtifactsByService.Sysroot.ArtifactPaths(
artifact_type=common_pb2.ArtifactsByService.Sysroot.ArtifactType.FUZZER_SYSROOT,
failed=True,
failure_reason="Bad data!",
)
]
),
image=common_pb2.UploadedArtifactsByService.Image(
artifacts=[
common_pb2.UploadedArtifactsByService.Image.ArtifactPaths(
artifact_type=common_pb2.ArtifactsByService.Image.ArtifactType.LICENSE_CREDITS,
paths=[
common_pb2.Path(
path="/foo/bar/license_credits.html",
location=common_pb2.Path.OUTSIDE,
)
],
)
]
),
test=common_pb2.UploadedArtifactsByService.Test(
artifacts=[
common_pb2.UploadedArtifactsByService.Test.ArtifactPaths(
artifact_type=common_pb2.ArtifactsByService.Test.ArtifactType.HWQUAL,
paths=[
common_pb2.Path(
path="/foo/bar/hwqual.tar.xz",
location=common_pb2.Path.OUTSIDE,
)
],
)
]
),
)
# pylint: enable=line-too-long
self.assertEqual(out_proto.artifacts, expected)