blob: 5116d0ed8f179034b068280918ad8bc6e7d0f19f [file] [log] [blame]
# Copyright 2023 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 fwbuddy.py."""
# This is to prevent pylint from complaining about us including, but not
# using the `setup` fixture.
# pylint: disable=unused-argument
import builtins
import os
from pathlib import Path
from typing import Any
from unittest import mock
import pytest
from chromite.lib import cros_test_lib
from chromite.lib import gs
from chromite.lib.fwbuddy import fwbuddy
GENERIC_VALID_URI = "fwbuddy://dedede/galtic/R99-123.456.0/signed/serial"
FAKE_FIRMWARE_QUALS_DATA = """{
"firmware_quals": [
{
"board_name": "dedede",
"model_name": "galnat360",
"branch_name": "firmware-dedede-13606.B"
}
]
}
"""
def mock_read_text(*args: Any, **kwargs: Any) -> str:
return FAKE_FIRMWARE_QUALS_DATA
@pytest.fixture(name="setup")
def fixture_setup(monkeypatch: "pytest.MonkeyPatch") -> None:
monkeypatch.setattr(gs.GSContext, "LS", lambda *_,: ["some/path"])
monkeypatch.setattr(gs.GSContext, "Copy", lambda *_,: None)
monkeypatch.setattr(gs.GSContext, "CheckPathAccess", lambda *_,: None)
monkeypatch.setattr(fwbuddy.FwBuddy, "setup_temp_dirs", lambda *_,: None)
monkeypatch.setattr(Path, "read_text", mock_read_text)
def test_context_manager(setup: Path) -> None:
"""Tests that we can manage an FwBuddy object within a context manager"""
temp_dir_path = Path("")
with fwbuddy.FwBuddy(GENERIC_VALID_URI) as f:
temp_dir_path = f.temp_dir_path
assert os.path.exists(temp_dir_path)
# Temp directory should be cleaned up after we exit the with block.
assert not os.path.exists(temp_dir_path)
def test_usage_string(setup: Path) -> None:
"""Test that all of the URI fields are include in the usage doc."""
for field in fwbuddy.FIELD_DOCS:
assert field in fwbuddy.USAGE
def test_parse_uri(setup: Path) -> None:
"""Tests that we can properly convert a uri string into a URI object"""
assert fwbuddy.parse_uri(GENERIC_VALID_URI) == fwbuddy.URI(
board="dedede",
firmware_name="galtic",
version="R99-123.456.0",
image_type="signed",
firmware_type="serial",
)
assert fwbuddy.parse_uri(
"fwbuddy://dedede/galtic/R99-123.456.0/signed"
) == fwbuddy.URI(
board="dedede",
firmware_name="galtic",
version="R99-123.456.0",
image_type="signed",
firmware_type=None,
)
# Missing image_type
with pytest.raises(fwbuddy.FwBuddyException):
fwbuddy.parse_uri("fwbuddy://dedede/galtic/R99-123.456.0")
# Wrong header
with pytest.raises(fwbuddy.FwBuddyException):
fwbuddy.parse_uri("fwbozo://dedede/galtic/R99-123.456.0/unsigned")
def test_parse_release_string(setup: Path) -> None:
"""Tests that versions can be parsed into Release Objects"""
assert fwbuddy.Release(
"99", "123", "456", "0"
) == fwbuddy.parse_release_string("R99-123.456.0")
assert fwbuddy.Release(
"99", "123", "456", "0"
) == fwbuddy.parse_release_string("r99-123.456.0")
assert fwbuddy.Release(
"*", "123", "456", "0"
) == fwbuddy.parse_release_string("R*-123.456.0")
with pytest.raises(fwbuddy.FwBuddyException):
fwbuddy.parse_release_string("99-123.456.0")
with pytest.raises(fwbuddy.FwBuddyException):
fwbuddy.parse_release_string("R99-123.456")
def test_generate_unsigned_gspaths(setup: Path) -> None:
"""Tests that we can generate unsigned gspaths using our schemas."""
fw_image = fwbuddy.FwImage(
board="dedede",
firmware_name="galtic",
release=fwbuddy.parse_release_string("R89-13606.459.0"),
branches=set(["some-branch-name"]),
image_type="unsigned",
firmware_type="",
)
expected_gspaths = set(
[
(
"gs://chromeos-image-archive/firmware-dedede-13606.B-branch-"
"firmware/R89-13606.459.0/firmware_from_source.tar.bz2"
),
(
"gs://chromeos-image-archive/firmware-dedede-13606.B-branch-"
"firmware/R89-13606.459.0/dedede/firmware_from_source.tar.bz2"
),
(
"gs://chromeos-image-archive/dedede-firmware/R89-13606.459.0/"
"firmware_from_source.tar.bz2"
),
(
"gs://chromeos-image-archive/some-branch-name-branch-"
"firmware/R89-13606.459.0/firmware_from_source.tar.bz2"
),
(
"gs://chromeos-image-archive/some-branch-name-branch-"
"firmware/R89-13606.459.0/dedede/firmware_from_source.tar.bz2"
),
]
)
assert fwbuddy.generate_gspaths(fw_image) == expected_gspaths
def test_generate_gspaths_no_branches(setup: Path) -> None:
"""Tests that we can generate unsigned gspaths using our schemas."""
fw_image = fwbuddy.FwImage(
board="dedede",
firmware_name="galtic",
release=fwbuddy.parse_release_string("R89-13606.459.0"),
branches=set(),
image_type="unsigned",
firmware_type="",
)
expected_gspaths = set(
[
(
"gs://chromeos-image-archive/firmware-dedede-13606.B-branch-"
"firmware/R89-13606.459.0/firmware_from_source.tar.bz2"
),
(
"gs://chromeos-image-archive/firmware-dedede-13606.B-branch-"
"firmware/R89-13606.459.0/dedede/firmware_from_source.tar.bz2"
),
(
"gs://chromeos-image-archive/dedede-firmware/R89-13606.459.0/"
"firmware_from_source.tar.bz2"
),
]
)
assert fwbuddy.generate_gspaths(fw_image) == expected_gspaths
def test_lookup_branches(
setup: Path,
) -> None:
"""Tests that we correctly parse the SQL output from the branch lookup"""
f = fwbuddy.FwBuddy(GENERIC_VALID_URI)
assert f.lookup_branches() == set(["firmware-dedede-13606.B"])
def test_lookup_branches_fails(setup: Path) -> None:
"""Tests that we return an empty set when we can't access the branch map"""
f = fwbuddy.FwBuddy(GENERIC_VALID_URI)
with mock.patch(
"chromite.lib.gs.GSContext.CheckPathAccess",
side_effect=Exception("No Access"),
):
assert f.lookup_branches() == set()
def test_generate_signed_gspaths(setup: Path) -> None:
"""Tests that we can generate signed gspaths using our schemas."""
fw_image = fwbuddy.FwImage(
board="dedede",
firmware_name="galtic",
release=fwbuddy.parse_release_string("R89-13606.459.0"),
branches=set("firmware-dedede-13606.B"),
image_type="signed",
firmware_type="",
)
expected_gspaths = set(
[
"gs://chromeos-releases/canary-channel/dedede/13606.459.0/ChromeOS-"
"firmware-R89-13606.459.0-dedede.tar.bz2"
]
)
assert fwbuddy.generate_gspaths(fw_image) == expected_gspaths
def test_determine_gspath(
setup: Path, monkeypatch: "pytest.MonkeyPatch"
) -> None:
f = fwbuddy.FwBuddy(GENERIC_VALID_URI)
assert f.determine_gspath() == "some/path"
monkeypatch.setattr(fwbuddy, "generate_gspaths", lambda *_,: [])
with pytest.raises(fwbuddy.FwBuddyException):
f.determine_gspath()
def test_download(setup: Path) -> None:
f = fwbuddy.FwBuddy(GENERIC_VALID_URI)
f.download()
assert f.archive_path == Path(f.temp_dir_path) / "path"
def test_extract(setup: Path, run_mock: cros_test_lib.RunCommandMock) -> None:
run_mock.SetDefaultCmdResult(0)
# Ap image path extraction with firmware_type
f = fwbuddy.FwBuddy(GENERIC_VALID_URI)
f.archive_path = Path("/unused")
f.extract("tmp")
assert f.ap_path == Path("tmp/image-galtic.serial.bin")
# AP and EC image path extraction
f = fwbuddy.FwBuddy("fwbuddy://dedede/galtic/R99-123.456.0/signed")
f.archive_path = Path("/unused")
f.extract("tmp")
assert f.ap_path == Path("tmp/image-galtic.bin")
assert f.ec_path == Path("tmp/galtic/ec.bin")
# Some error while extracting archive contents.
run_mock.SetDefaultCmdResult(1, stderr="some error")
with pytest.raises(fwbuddy.FwBuddyException):
f.extract()
def test_export_firmware_image(
setup: Path, run_mock: cros_test_lib.RunCommandMock
) -> None:
run_mock.SetDefaultCmdResult(0)
f = fwbuddy.FwBuddy(GENERIC_VALID_URI)
f.archive_path = Path("/unused")
# Unsupported chip
f.extract("tmp")
with pytest.raises(fwbuddy.FwBuddyException):
f.export_firmware_image("tmp", "JUNK_CHIP")
# Export without extraction
f.ec_path = None
with pytest.raises(fwbuddy.FwBuddyException):
f.export_firmware_image("tmp", "EC")
# Some failure while exporting
f.extract("tmp")
run_mock.SetDefaultCmdResult(1)
with pytest.raises(fwbuddy.FwBuddyException):
f.export_firmware_image("tmp", "EC")
def test_parse_chip(setup: Path) -> None:
assert "ec" == fwbuddy.parse_chip("EC")
assert "ap" == fwbuddy.parse_chip("ap")
assert None is fwbuddy.parse_chip(None)
with pytest.raises(fwbuddy.FwBuddyException):
fwbuddy.parse_chip("junk")
def test_parse_firmware_type(setup: Path) -> None:
assert "serial" == fwbuddy.parse_firmware_type("SERIAL")
assert None is fwbuddy.parse_firmware_type(None)
with pytest.raises(fwbuddy.FwBuddyException):
fwbuddy.parse_firmware_type("junk")
def test_get_uri_interactive(
setup: Path, monkeypatch: "pytest.MonkeyPatch"
) -> None:
"""Test that we can build an fwbuddy URI from an interactive prompt."""
num = 0
def increment_num() -> int:
nonlocal num
num += 1
return num
monkeypatch.setattr(
builtins, "input", lambda *args, **kwargs: f"{increment_num()}"
)
assert fwbuddy.get_uri_interactive() == "fwbuddy://1/2/3/4/5/"
def test_interactive_mode(
setup: Path, monkeypatch: "pytest.MonkeyPatch"
) -> None:
"""Test that we can trigger interactive mode"""
mock_input = GENERIC_VALID_URI
monkeypatch.setattr(
fwbuddy,
"get_uri_interactive",
lambda *args, **kwargs: f"{mock_input}",
)
f = fwbuddy.FwBuddy("fwbuddy://")
assert f.fw_image.board == "dedede"