| #!/usr/bin/env python3 |
| # Copyright 2017 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Unit tests for pack_firmware.py. |
| |
| This mocks out all tools so it can run fairly quickly. |
| """ |
| |
| import contextlib |
| import glob |
| import io |
| import os |
| import shutil |
| import struct |
| import sys |
| from unittest import mock |
| |
| import pack_firmware |
| import pack_firmware_utils |
| |
| from chromite.lib import cros_test_lib |
| from chromite.lib import osutils |
| from chromite.lib import partial_mock |
| from chromite.signing.lib import firmware_unittest |
| |
| |
| # We need to poke around in internal members of PackFirmware. |
| # pylint: disable=W0212 |
| |
| # Pre-set ID expected for test/image.bin. Note the 'R' in the first is a 'W' |
| # in the second. It is confusing but this is how the firmware images are |
| # currently created. |
| RO_FRID = "Google_Reef.9264.0.0_d2017_02_09_1240" |
| RW_FWID = "Google_Reef.9264.0.0_d2017_02_09_1250" |
| NO_TIMESTAMP_RO_FRID = "Google_Reef.9264.0.1" |
| EC_RO_FRID = "reef_v1.1.5857-77f6ed7" |
| EC_RW_FWID = "reef_v1.1.5858-77f6ed7" |
| EC_RO_FRID_NEW = "reef-9264.0.0" |
| |
| |
| # Expected output from 'futility dump_fmap -p' for main image. |
| FMAP_OUTPUT = """WP_RO 0 4194304 |
| SI_DESC 0 4096 |
| IFWI 4096 2093056 |
| RO_VPD 2097152 16384 |
| RO_SECTION 2113536 2080768 |
| FMAP 2113536 2048 |
| RO_FRID 2115584 64 |
| RO_FRID_PAD 2115648 1984 |
| COREBOOT 2117632 1552384 |
| GBB 3670016 262144 |
| RO_UNUSED 3932160 262144 |
| MISC_RW 4194304 196608 |
| UNIFIED_MRC_CACHE 4194304 135168 |
| RECOVERY_MRC_CACHE 4194304 65536 |
| RW_MRC_CACHE 4259840 65536 |
| RW_VAR_MRC_CACHE 4325376 4096 |
| RW_ELOG 4329472 12288 |
| RW_SHARED 4341760 16384 |
| SHARED_DATA 4341760 8192 |
| VBLOCK_DEV 4349952 8192 |
| RW_VPD 4358144 8192 |
| RW_NVRAM 4366336 24576 |
| RW_SECTION_A 4390912 4718592 |
| VBLOCK_A 4390912 65536 |
| FW_MAIN_A 4456448 4652992 |
| RW_FWID_A 9109440 64 |
| RW_SECTION_B 9109504 4718592 |
| VBLOCK_B 9109504 65536 |
| FW_MAIN_B 9175040 4652992 |
| RW_FWID_B 13828032 64 |
| RW_LEGACY 13828096 2097152 |
| BIOS_UNUSABLE 15925248 323584 |
| DEVICE_EXTENSION 16248832 524288 |
| UNUSED_HOLE 16773120 4096 |
| """ |
| |
| # Expected output from 'futility dump_fmap -p' for alternate main image. |
| # This FMAP contains EC_MAIN_A sections, which will alter |
| # the behavior of repack. A similar FMAP is used on some legacy Chrome OS |
| # devices. |
| FMAP_OUTPUT_LEGACY = """SI_ALL 0 2097152 |
| SI_DESC 0 4096 |
| SI_ME 4096 2093056 |
| SI_BIOS 2097152 6291456 |
| RW_SECTION_A 2097152 983040 |
| VBLOCK_A 2097152 65536 |
| FW_MAIN_A 2162688 720896 |
| EC_MAIN_A 2949120 131008 |
| RW_FWID_A 3080128 64 |
| RW_SECTION_B 3080192 983040 |
| VBLOCK_B 3080192 65536 |
| FW_MAIN_B 3145728 720896 |
| EC_MAIN_B 3932160 131008 |
| RW_FWID_B 4063168 64 |
| RW_MRC_CACHE 4063232 65536 |
| RW_ELOG 4128768 16384 |
| RW_SHARED 4145152 16384 |
| SHARED_DATA 4145152 8192 |
| VBLOCK_DEV 4153344 8192 |
| RW_VPD 4161536 8192 |
| RW_UNUSED 4169728 24576 |
| RW_LEGACY 4194304 2097152 |
| WP_RO 6291456 2097152 |
| RO_VPD 6291456 16384 |
| RO_UNUSED 6307840 49152 |
| RO_SECTION 6356992 2031616 |
| FMAP 6356992 2048 |
| RO_FRID 6359040 64 |
| RO_FRID_PAD 6359104 1984 |
| GBB 6361088 978944 |
| BOOT_STUB 7340032 1048576 |
| """ |
| |
| # Size of dummy 'ecrw' file. |
| ECRW_SIZE = 0x38000 |
| |
| # Expected output from 'futility dump_fmap -p' for EC image. |
| FMAP_OUTPUT_EC = """EC_RO 64 229376 |
| FR_MAIN 64 229376 |
| RO_FRID 388 32 |
| FMAP 135232 350 |
| WP_RO 0 262144 |
| EC_RW 262144 229376 |
| RW_FWID 262468 32 |
| """ |
| |
| # Common flags that we use in several tests. |
| _COMMON_FLAGS = [ |
| "-b", |
| "test/image.bin", |
| "-q", |
| "-o", |
| ] |
| |
| |
| # Use this to suppress stdout/stderr output: |
| # with capture_sys_output() as (stdout, stderr) |
| # ...do something... |
| @contextlib.contextmanager |
| def capture_sys_output(): |
| capture_out, capture_err = io.StringIO(), io.StringIO() |
| old_out, old_err = sys.stdout, sys.stderr |
| try: |
| sys.stdout, sys.stderr = capture_out, capture_err |
| yield capture_out, capture_err |
| finally: |
| sys.stdout, sys.stderr = old_out, old_err |
| |
| |
| class TestUnit(cros_test_lib.MockTempDirTestCase): |
| """Test cases for common program flows.""" |
| |
| def setUp(self): |
| self.output = os.path.join(self.tempdir, "out") |
| self.common_flags = _COMMON_FLAGS + [self.output] |
| |
| pack_firmware_utils.MakeTestFiles() |
| self.packer = pack_firmware.FirmwarePacker(".") |
| self._ExtractFrid = pack_firmware.FirmwarePacker._ExtractFrid |
| |
| def tearDown(self): |
| pack_firmware.FirmwarePacker._ExtractFrid = self._ExtractFrid |
| |
| def testBadStartup(self): |
| """Test various bad start-up conditions.""" |
| # Starting up in another directory (without required files) should fail. |
| with self.assertRaises(pack_firmware.PackError) as e: |
| pack_firmware.main(["/"]) |
| self.assertIn("'/pack/sfx2.sh'", str(e.exception)) |
| |
| # Should check for 'zip' tool. |
| with mock.patch.object(osutils, "Which", return_value=None): |
| with self.assertRaises(pack_firmware.PackError) as e: |
| pack_firmware.main(["."]) |
| self.assertIn("'zip'", str(e.exception)) |
| |
| # Should complain if we don't provide at least one image. |
| with self.assertRaises(pack_firmware.PackError) as e: |
| args = [".", "-L", "-o", "out"] |
| pack_firmware.main(args) |
| self.assertIn("Must assign at least one", str(e.exception)) |
| |
| def testArgParse(self): |
| """Test some basic argument parsing as a validity check.""" |
| with self.assertRaises(SystemExit): |
| with capture_sys_output(): |
| self.assertIsNone(self.packer.ParseArgs(["--invalid"])) |
| |
| self.assertIsNone(self.packer.ParseArgs([]).bios_image) |
| self.assertEqual( |
| "/bios.bin", self.packer.ParseArgs(["-b", "/bios.bin"]).bios_image |
| ) |
| |
| def testEnsureCommand(self): |
| """Check that we detect a missing command.""" |
| self.packer._EnsureCommand("ls", "sample-package") |
| with self.assertRaises(pack_firmware.PackError) as e: |
| self.packer._EnsureCommand("does-not-exist", "sample-package") |
| self.assertIn("You need 'does-not-exist'", str(e.exception)) |
| |
| def testTmpdirs(self): |
| """Check creation and removal of temporary directories.""" |
| dir1 = self.packer._CreateTmpDir() |
| dir2 = self.packer._CreateTmpDir() |
| self.assertExists(dir1) |
| self.assertExists(dir2) |
| self.packer._RemoveTmpdirs() |
| self.assertNotExists(dir1) |
| self.assertNotExists(dir2) |
| |
| def testAddVersionInfoMissingFile(self): |
| """Trying to add version info for a missing file should be detected.""" |
| with self.assertRaises(IOError) as e: |
| self.packer._AddVersionInfo("BIOS", "missing-file", "v123") |
| self.assertIn("'missing-file'", str(e.exception)) |
| |
| def testAddVersionInfoNoFile(self): |
| """Check adding version info with no filename.""" |
| self.packer._AddVersionInfo("BIOS", "", "v123") |
| self.assertEqual( |
| "BIOS version: v123\n", self.packer._versions.getvalue() |
| ) |
| |
| def testAddVersionNoVersion(self): |
| """Check adding version info with no version.""" |
| self.packer._AddVersionInfo("BIOS", "test/image.bin", "") |
| self.assertEqual( |
| "BIOS image: 8ce05b02847603aef6cfa01f1bab73d0 " |
| "*test/image.bin\n", |
| self.packer._versions.getvalue(), |
| ) |
| |
| def testAddVersionInfo(self): |
| """Check adding version info with both a filename and version.""" |
| self.packer._AddVersionInfo("BIOS", "test/image.bin", "v123") |
| self.assertEqual( |
| "BIOS image: 8ce05b02847603aef6cfa01f1bab73d0 " |
| "*test/image.bin\nBIOS version: v123\n", |
| self.packer._versions.getvalue(), |
| ) |
| |
| def testExtractFrid(self): |
| """Check extracting the firmware ID from a bios image.""" |
| self.packer._tmpdir = "test" |
| self.packer._args = self.packer.ParseArgs( |
| ["--bios_image", "image.bin", "-t"] |
| ) |
| with cros_test_lib.RunCommandMock() as rc: |
| rc.AddCmdResult( |
| partial_mock.ListRegex("futility dump_fmap"), returncode=0 |
| ) |
| self.assertEqual(RO_FRID, self.packer._ExtractFrid("image.bin")) |
| |
| def testExtractFridTrailingSpace(self): |
| """Check extracting a firmware ID with a trailing space.""" |
| |
| def _SetupImage(_, **kwargs): |
| destdir = kwargs["cwd"] |
| osutils.WriteFile( |
| os.path.join(destdir, "RO_FRID"), b"TESTING \0\0\0", mode="wb" |
| ) |
| |
| self.packer._tmpdir = self.packer._CreateTmpDir() |
| self.packer._args = self.packer.ParseArgs( |
| ["--bios_image", "image.bin", "-t"] |
| ) |
| with cros_test_lib.RunCommandMock() as rc: |
| rc.AddCmdResult( |
| partial_mock.ListRegex("futility dump_fmap"), |
| returncode=0, |
| side_effect=_SetupImage, |
| ) |
| self.assertEqual("TESTING ", self.packer._ExtractFrid("image.bin")) |
| self.packer._RemoveTmpdirs() |
| |
| def testExtractEcVersion(self): |
| """Check extracting version from firmware ID.""" |
| self.assertEqual(self.packer._ExtractEcVersion(EC_RO_FRID), "1-1-5857") |
| self.assertEqual( |
| self.packer._ExtractEcVersion(EC_RO_FRID_NEW), "9264-0-0" |
| ) |
| with self.assertRaises(pack_firmware.PackError) as e: |
| self.assertEqual(self.packer._ExtractEcVersion("a_bad_id"), "") |
| self.assertIn("Malformed EC firmware ID", str(e.exception)) |
| |
| def testExtractMainVersion(self): |
| """Check extracting version from firmware ID.""" |
| self.assertEqual(self.packer._ExtractMainVersion(RO_FRID), "9264-0-0") |
| self.assertEqual(self.packer._ExtractMainVersion(RW_FWID), "9264-0-0") |
| self.assertEqual( |
| self.packer._ExtractMainVersion(NO_TIMESTAMP_RO_FRID), "9264-0-1" |
| ) |
| with self.assertRaises(pack_firmware.PackError) as e: |
| self.assertEqual(self.packer._ExtractMainVersion("a_bad_id"), "") |
| self.assertIn("Malformed coreboot firmware ID", str(e.exception)) |
| |
| def testFirmwareImageOutput(self): |
| """Check firmware image output name.""" |
| self.packer._args = self.packer.ParseArgs([]) |
| self.packer._tmpdir = self.packer._CreateTmpDir() |
| |
| tag = "ec" |
| target = "reef" |
| fw_ids = pack_firmware.FirmwareIds(EC_RO_FRID, EC_RW_FWID) |
| bad_fw_ids = pack_firmware.FirmwareIds(EC_RO_FRID, None) |
| version_fn = ( |
| lambda fwid: "1-1-5857" if fwid == EC_RO_FRID else "1-1-5858" |
| ) |
| self.assertEqual(self.packer._FirmwareImageOutput(tag), "") |
| self.assertEqual( |
| self.packer._FirmwareImageOutput( |
| tag, fw_ids=fw_ids, extract_version=version_fn |
| ), |
| "", |
| ) |
| self.assertEqual( |
| self.packer._FirmwareImageOutput(tag, target, fw_ids), "" |
| ) |
| self.assertEqual( |
| self.packer._FirmwareImageOutput( |
| tag, target, extract_version=version_fn |
| ), |
| "", |
| ) |
| self.assertEqual( |
| self.packer._FirmwareImageOutput( |
| tag, target, bad_fw_ids, version_fn |
| ), |
| "", |
| ) |
| |
| expected_fname = "ec-reef.ro-1-1-5857.rw-1-1-5858.bin" |
| self.assertEqual( |
| self.packer._FirmwareImageOutput(tag, target, fw_ids, version_fn), |
| expected_fname, |
| ) |
| self.assertEqual( |
| self.packer._FirmwareImageOutput( |
| tag, target, fw_ids, version_fn, self.packer._tmpdir |
| ), |
| os.path.join(self.packer._tmpdir, expected_fname), |
| ) |
| |
| self.packer._RemoveTmpdirs() |
| |
| def testFirmwareImageOutputLegacy(self): |
| """Check firmware image output name when legacy is set.""" |
| self.packer._args = self.packer.ParseArgs(["-L"]) |
| self.packer._tmpdir = self.packer._CreateTmpDir() |
| self.assertEqual(self.packer._FirmwareImageOutput("bios"), "bios.bin") |
| self.assertEqual( |
| self.packer._FirmwareImageOutput("bios", "reef"), "bios.bin" |
| ) |
| self.assertEqual( |
| self.packer._FirmwareImageOutput( |
| tag="bios", target_dir=self.packer._tmpdir |
| ), |
| os.path.join(self.packer._tmpdir, "bios.bin"), |
| ) |
| self.packer._RemoveTmpdirs() |
| |
| def testUntarFile(self): |
| """Test operation of the tar file unpacker.""" |
| self.packer._tmpdir = self.packer._CreateTmpDir() |
| dirname = self.packer._CreateTmpDir() |
| fname = self.packer._UntarFile("functest/Reef.9042.50.0.tbz2", dirname) |
| self.assertEqual(os.path.basename(fname), "image.bin") |
| |
| # Unpack again with a different suffix. We should get a different |
| # filename and the file contents should be different. |
| fname2 = self.packer._UntarFile( |
| "functest/Reef.9000.0.0.tbz2", dirname, "-rw" |
| ) |
| self.assertEqual(os.path.basename(fname2), "image.bin-rw") |
| self.assertNotEqual(fname, fname2) |
| data = osutils.ReadFile(fname, mode="rb") |
| data2 = osutils.ReadFile(fname2, mode="rb") |
| self.assertNotEqual(data, data2) |
| |
| dirname = self.packer._CreateTmpDir() |
| fname = self.packer._UntarFile("functest/Reef.9042.50.0.tbz2", dirname) |
| self.assertEqual(os.path.basename(fname), "image.bin") |
| |
| # This tar file has two files in it. |
| # -rw-r----- sjg/eng 64 2017-03-03 16:12 RO_FRID |
| # -rw-r----- sjg/eng 64 2017-03-15 13:38 RW_FRID |
| with self.assertRaises(pack_firmware.PackError) as e: |
| fname = self.packer._UntarFile("test/two_files.tbz2", dirname) |
| self.assertIn("Expected 1 member", str(e.exception)) |
| |
| # This tar file has as directory name in its member's filename. |
| # -rw-r----- sjg/eng 64 2017-03-03 16:12 test/RO_FRID |
| with self.assertRaises(pack_firmware.PackError) as e: |
| fname = self.packer._UntarFile("test/path.tbz2", dirname) |
| self.assertIn("should be a simple name", str(e.exception)) |
| |
| self.packer._RemoveTmpdirs() |
| |
| @staticmethod |
| def _FilesInDir(dirname): |
| """Get a list of files in a directory. |
| |
| Args: |
| dirname: Directory name to check. |
| |
| Returns: |
| List of files in that directory (basename only). Any subdirectories |
| are ignored. |
| """ |
| return sorted( |
| [ |
| os.path.basename(fname) |
| for fname in glob.glob(os.path.join(dirname, "*")) |
| if not os.path.isdir(fname) |
| ] |
| ) |
| |
| def testBaseDirPath(self): |
| """Check that _BaseDirPath() works as expected.""" |
| self.packer._basedir = "base" |
| self.assertEqual("base/fred", self.packer._BaseDirPath("fred")) |
| |
| def _GetFakeReefFirmware(self): |
| """Get the fake reef firmware config. |
| |
| Returns: |
| Dict of the reef firmware config |
| """ |
| return { |
| "ec-ro-image": "bcs://Reef_EC.9042.50.0.tbz2", |
| "extra": [ |
| "${FILESDIR}/extra", |
| "${SYSROOT}/usr/sbin/ectool", |
| "bcs://Reef.9000.0.0.tbz2", |
| ], |
| "main-ro-image": "bcs://Reef.9042.50.0.tbz2", |
| } |
| |
| def testExtractFileBcs(self): |
| """Test handling file extraction based on a configuration property,""" |
| self.packer._tmpdir = self.packer._CreateTmpDir() |
| self.packer._args = self.packer.ParseArgs(["--imagedir", "functest"]) |
| dirname = self.packer._CreateTmpDir() |
| fake_fw = self._GetFakeReefFirmware() |
| |
| # This should look up main-ro-image, find that file in functest/, copy |
| # it to our directory and return its filename. |
| main_image = fake_fw["main-ro-image"] |
| self.assertEqual( |
| self.packer._ExtractFile(None, None, main_image, dirname), |
| os.path.join(dirname, "image.bin"), |
| ) |
| ec_image = fake_fw["ec-ro-image"] |
| self.assertEqual( |
| self.packer._ExtractFile(None, None, ec_image, dirname), |
| os.path.join(dirname, "ec.bin"), |
| ) |
| self.packer._RemoveTmpdirs() |
| |
| @staticmethod |
| def _AddMocks(rc): |
| def _CopySections(_, **kwargs): |
| destdir = kwargs["cwd"] |
| for fname in ["RO_FRID", "RW_FWID"]: |
| shutil.copy2(os.path.join("test", fname), destdir) |
| |
| rc.AddCmdResult( |
| partial_mock.ListRegex(r"(?:^|\s)file(?:$|\s)"), |
| returncode=0, |
| stdout="ELF 64-bit LSB executable, etc.\n", |
| ) |
| rc.AddCmdResult( |
| partial_mock.ListRegex( |
| r"futility dump_fmap -x .*image\.bin (?:RO_FRID|RW_FWID)$" |
| ), |
| side_effect=_CopySections, |
| returncode=0, |
| ) |
| rc.AddCmdResult( |
| partial_mock.ListRegex("futility gbb"), |
| returncode=0, |
| stdout=" - exported root_key to file: rootkey.bin", |
| ) |
| rc.AddCmdResult(partial_mock.ListRegex("--repack"), returncode=0) |
| rc.AddCmdResult( |
| partial_mock.ListRegex(r"futility dump_fmap -x .*ec\.bin"), |
| side_effect=_CopySections, |
| returncode=0, |
| ) |
| |
| @staticmethod |
| def _CreateCbfstoolFile(cmd, **_kwargs): |
| """Called as a side effect to emulate the effect of cbfstool. |
| |
| This handles the 'cbfstool...extract' command which is supposed to |
| extract a particular 'file' from inside the CBFS archive. We deal with |
| this by creating a zero-filled file with the correct name and size. |
| See _ExtractEcRwUsingCbfs() for where this command is generated. |
| |
| Args: |
| cmd: Arguments, of the form: |
| ['cbfstool.sh', ..., '-f', <filename>, ...] |
| See _SetPreambleFlags() for where this is generated. |
| """ |
| file_arg = cmd.index("-f") |
| fname = cmd[file_arg + 1] |
| with open(fname, "wb") as fd: |
| fd.truncate(ECRW_SIZE) |
| |
| @staticmethod |
| def _CreateDumpfmapFile(cmd, **_kwargs): |
| """Called as a side effect to emulate the effect of dump_fmap. |
| |
| This handles the 'dump_fmap -x' command which is supposed to |
| extract a particular region from a file with an FMAP descriptor. |
| See _ExtractEcRwUsingFMAP() for where this command is generated. |
| |
| Args: |
| cmd: Arguments, of the form: |
| ['dump_fmap', '-x', <filename>, <region>] |
| <region> specifies the region to extract from <filename>, which |
| will be extracted to cwd in a file named <region>. |
| See _ExtractEcRwUsingFMAP() for where this is generated. |
| """ |
| fname = os.path.join(_kwargs["cwd"], cmd.pop()) |
| with open(fname, "wb") as fd: |
| # Write a dummy header with image size = ECRW_SIZE, and payload of |
| # zeros |
| fd.write(struct.pack("<III", 1, 12, ECRW_SIZE)) |
| fd.truncate(ECRW_SIZE + 12) |
| |
| @classmethod |
| def _AddMergeMocks(cls, rc, mocked_dump_fmap_output): |
| rc.AddCmdResult( |
| partial_mock.ListRegex( |
| r"futility dump_fmap -x .*/images-merged/.*image_rw\.bin" |
| ), |
| returncode=0, |
| ) |
| rc.AddCmdResult( |
| partial_mock.ListRegex( |
| r"futility dump_fmap -p .*/images-merged/.*image_rw\.bin" |
| ), |
| returncode=0, |
| stdout=mocked_dump_fmap_output, |
| ) |
| rc.AddCmdResult( |
| partial_mock.ListRegex(r"futility dump_fmap -p .*image\.binrw"), |
| returncode=0, |
| stdout=mocked_dump_fmap_output, |
| ) |
| rc.AddCmdResult( |
| partial_mock.ListRegex(r"futility dump_fmap -p .*image\.bin"), |
| returncode=0, |
| stdout=mocked_dump_fmap_output, |
| ) |
| rc.AddCmdResult(partial_mock.Regex("extract_ecrw"), returncode=0) |
| rc.AddCmdResult( |
| partial_mock.ListRegex(r"futility dump_fmap -p .*ec\.bin"), |
| returncode=0, |
| stdout=FMAP_OUTPUT_EC, |
| ) |
| rc.AddCmdResult( |
| partial_mock.ListRegex("cbfstool"), |
| returncode=0, |
| side_effect=cls._CreateCbfstoolFile, |
| ) |
| rc.AddCmdResult( |
| partial_mock.ListRegex( |
| r"futility dump_fmap -x .*image\.bin .*_MAIN_A" |
| ), |
| returncode=0, |
| side_effect=cls._CreateDumpfmapFile, |
| ) |
| |
| def testMockedRun(self): |
| """Start up with a valid updater script and BIOS.""" |
| args = [".", "-L", "-e", "test/ec.bin"] + self.common_flags |
| with cros_test_lib.RunCommandMock() as rc: |
| self._AddMocks(rc) |
| firmware_unittest.MockFirmwareSigner(rc) |
| pack_firmware.main(args) |
| pack_firmware.packer._versions.getvalue().splitlines() |
| |
| def _testMockedRunWithMerge(self, mocked_dump_fmap_output): |
| args = [ |
| "--bios_rw_image", |
| "test/image_rw.bin", |
| "-L", |
| "-e", |
| "test/ec.bin", |
| ] + self.common_flags |
| with cros_test_lib.RunCommandMock() as rc: |
| self._AddMocks(rc) |
| self._AddMergeMocks(rc, mocked_dump_fmap_output) |
| self.packer.Start(args, remove_tmpdirs=False) |
| result = self.packer._versions.getvalue().splitlines() |
| self.assertEqual(8, len(result)) |
| self.assertIn(RO_FRID, self._FindLineInList(result, "EC version")) |
| self.assertIn(RW_FWID, self._FindLineInList(result, "EC (RW) version")) |
| rw_fname = self.packer._BaseDirPath("ec.bin") |
| self.assertAlmostEqual( |
| os.stat(rw_fname).st_mtime, os.stat("test/image_rw.bin").st_mtime |
| ) |
| ec_fname = self.packer._BaseDirPath("ec.bin") |
| self.assertAlmostEqual( |
| os.stat(ec_fname).st_mtime, os.stat("test/image_rw.bin").st_mtime |
| ) |
| self.packer._RemoveTmpdirs() |
| |
| def testMockedRunWithMerge(self): |
| """Start up with a valid updater script and merge the RW BIOS.""" |
| self._testMockedRunWithMerge(FMAP_OUTPUT) |
| |
| def testMockedRunWithMergeLegacyFmap(self): |
| """Repeat merge test with alternate FMAP used on legacy devices.""" |
| self._testMockedRunWithMerge(FMAP_OUTPUT_LEGACY) |
| |
| def testMockedRunUnibuildBad(self): |
| """Try unified build options with invalid arguments.""" |
| |
| args = [".", "-m", "reef", "-o", "output"] |
| with self.assertRaises(pack_firmware.PackError) as e: |
| pack_firmware.main(args) |
| self.assertIn("Missing model configuration file", str(e.exception)) |
| |
| def SetupRunWithUnibuild(self, config_fname): |
| """Set up to run with a valid updater script and BIOS. |
| |
| Args: |
| config_fname: Model configuration file to use. |
| |
| Returns: |
| List of arguments to pass to pack_firmware.main() |
| """ |
| |
| args = [ |
| ".", |
| "-m", |
| "reef", |
| "-m", |
| "pyro", |
| "-q", |
| "-o", |
| self.output, |
| "-c", |
| "test/%s" % config_fname, |
| "-i", |
| "functest", |
| "-t", |
| ] |
| |
| os.environ["SYSROOT"] = "test" |
| os.environ["FILESDIR"] = "test" |
| return args |
| |
| def testMockedRunWithUnibuild(self): |
| """Start up with a valid updater script and BIOS.""" |
| args = self.SetupRunWithUnibuild("config.yaml") |
| with cros_test_lib.RunCommandMock() as rc: |
| self._AddMocks(rc) |
| pack_firmware.main(args) |
| pack_firmware.packer._versions.getvalue().splitlines() |
| |
| def testMockedRunWithUnibuildMixedUris(self): |
| """Start up with config where a subset of devices have firmware uris.""" |
| args = self.SetupRunWithUnibuild("config_mixed_uri.yaml") |
| with cros_test_lib.RunCommandMock() as rc: |
| self._AddMocks(rc) |
| pack_firmware.main(args) |
| # Only reef is in the output, configs without firmware uris are |
| # skipped. |
| self.assertListEqual( |
| pack_firmware.packer._versions.getvalue().strip().splitlines(), |
| [ |
| "Model: reef", |
| "BIOS image: 99a6fc64e45596aa2c1a9911cddce952" |
| " */reef/image.bin", |
| "BIOS version: Google_Reef.9264.0.0_d2017_02_09_1240", |
| "EC image: 60c08e5aefa3a660687c7027d1358df0" |
| " */reef/ec.bin", |
| "EC version: Google_Reef.9264.0.0_d2017_02_09_1240", |
| "EC (RW) version: Google_Reef.9264.0.0_d2017_02_09_1250", |
| ], |
| ) |
| |
| def testMockedRunWithUnibuildMerge(self): |
| """Start up with a valid updater script and both RO and RW BIOS.""" |
| |
| # Due to a bug in the DT impl, the RW firmware image was never actually |
| # present. |
| # TODO(sjg): Change this to config_rw.json once the RW merge case if |
| # correctly supported. |
| args = self.SetupRunWithUnibuild("config.yaml") |
| with cros_test_lib.RunCommandMock() as rc: |
| self._AddMocks(rc) |
| self._AddMergeMocks(rc, FMAP_OUTPUT) |
| pack_firmware.main(args) |
| pack_firmware.packer._versions.getvalue().splitlines() |
| |
| def _FindLineInList(self, lines, start_text): |
| """Find a single line starting with the given text and return it. |
| |
| Args: |
| lines: List of lines to check. |
| start_text: Text to find. |
| |
| Returns: |
| Line found, as a string (or assertion failure if exactly one |
| matching line was not found). |
| """ |
| found = [line for line in lines if line.strip().startswith(start_text)] |
| self.assertEqual(len(found), 1) |
| return found[0] |
| |
| def testNoECFirmware(self): |
| """Simple test of creating firmware without an EC image.""" |
| args = self.common_flags + ["-L"] |
| with cros_test_lib.RunCommandMock() as rc: |
| self._AddMocks(rc) |
| self.packer.Start(args) |
| |
| # There should be no EC version in the VERSION file. |
| result = self.packer._versions.getvalue() |
| self.assertNotIn("EC version", result) |
| self.assertEqual(4, len(result.splitlines())) |
| |
| |
| if __name__ == "__main__": |
| cros_test_lib.main(module=__name__) |