blob: 767134d38bb13235ad85280154ffb95e8ea312c6 [file]
# Copyright 2026 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import json
import pathlib
import unittest
from unittest import mock
import build_command_query_utils
import build_utils
import ninja_artifacts
class TestBuildCommandQueryUtils(unittest.TestCase):
def test_query_ninja_commands(self) -> None:
mock_ninja = ninja_artifacts.MockNinjaRunner(
pathlib.Path("/fuchsia/out/default"),
"rustc --crate-name bar obj/foo/bar.o\n"
+ "rustc --crate-name baz obj/foo/baz.o\n",
)
with mock.patch(
"pathlib.Path.open",
mock.mock_open(
read_data=json.dumps(
{
"//foo:foo": ["obj/foo/foo.o"],
"//foo:bar": ["obj/foo/bar.o"],
"//foo:baz": ["obj/foo/baz.o"],
}
)
),
):
self.assertDictEqual(
build_command_query_utils.query_ninja_commands(
mock_ninja,
pathlib.Path("ninja_outputs.json"),
["//foo:bar", "//foo:baz"],
),
{
"//foo:bar": "rustc --crate-name bar obj/foo/bar.o",
"//foo:baz": "rustc --crate-name baz obj/foo/baz.o",
},
)
self.assertEqual(
mock_ninja.last_ninja_args(),
["-t", "commands", "-s", "obj/foo/bar.o", "obj/foo/baz.o"],
)
def test_query_ninja_commands_empty_labels(self) -> None:
mock_ninja = mock.Mock()
self.assertEqual(
build_command_query_utils.query_ninja_commands(
mock_ninja, pathlib.Path("ninja_outputs.json"), []
),
{},
)
mock_ninja.run_and_extract_output.assert_not_called()
def test_query_ninja_commands_ninja_error(self) -> None:
mock_ninja = mock.Mock()
mock_ninja.run_and_extract_output.side_effect = Exception(
"Ninja failed"
)
with mock.patch(
"pathlib.Path.open",
mock.mock_open(
read_data=json.dumps({"//foo:bar": ["obj/foo/bar.o"]})
),
):
with self.assertRaisesRegex(Exception, "Ninja failed"):
build_command_query_utils.query_ninja_commands(
mock_ninja,
pathlib.Path("ninja_outputs.json"),
["//foo:bar"],
)
def test_query_ninja_commands_mismatch(self) -> None:
mock_ninja = ninja_artifacts.MockNinjaRunner(
pathlib.Path("/fuchsia/out/default"),
"rustc --crate-name bar obj/foo/BLOOP.o\n"
+ "rustc --crate-name baz obj/foo/baz.o\n",
)
with mock.patch(
"pathlib.Path.open",
mock.mock_open(
read_data=json.dumps(
{
"//foo:bar": ["obj/foo/bar.o"],
"//foo:baz": ["obj/foo/baz.o"],
}
)
),
):
with self.assertRaisesRegex(ValueError, "Could not find command"):
build_command_query_utils.query_ninja_commands(
mock_ninja,
pathlib.Path("ninja_outputs.json"),
["//foo:bar", "//foo:baz"],
)
def test_query_ninja_commands_missing_command(self) -> None:
mock_ninja = ninja_artifacts.MockNinjaRunner(
pathlib.Path("/fuchsia/out/default"),
"rustc --crate-name bar obj/foo/bar.o\n",
)
with mock.patch(
"pathlib.Path.open",
mock.mock_open(
read_data=json.dumps(
{
"//foo:bar": ["obj/foo/bar.o"],
"//foo:baz": ["obj/foo/baz.o"],
}
)
),
):
with self.assertRaisesRegex(ValueError, "Could not find command"):
build_command_query_utils.query_ninja_commands(
mock_ninja,
pathlib.Path("ninja_outputs.json"),
["//foo:bar", "//foo:baz"],
)
def test_query_ninja_commands_missing_label(self) -> None:
mock_ninja = mock.Mock()
with mock.patch(
"pathlib.Path.open",
mock.mock_open(
read_data=json.dumps({"//foo:bar": ["obj/foo/bar.o"]})
),
):
with self.assertRaisesRegex(
ValueError, "Could not find outputs for label"
):
build_command_query_utils.query_ninja_commands(
mock_ninja,
pathlib.Path("ninja_outputs.json"),
["//foo:baz"],
)
def test_query_bazel_commands(self) -> None:
mock_bazel_launcher = build_utils.MockBazelLauncher()
mock_bazel_launcher.push_expected_outputs(
[
json.dumps(
{
"targets": [
{"id": "1", "label": "//foo:bar"},
{"id": "2", "label": "//foo:baz"},
],
"actions": [
{
"targetId": "1",
"arguments": ["rustc", "--crate-name", "bar"],
"mnemonic": "Rustc",
},
{
"targetId": "2",
"arguments": ["rustc", "--crate-name", "baz"],
"mnemonic": "Rustc",
},
],
}
)
]
)
self.assertDictEqual(
build_command_query_utils.query_bazel_commands(
mock_bazel_launcher, "execroot", ["//foo:bar", "//foo:baz"]
),
{
"//foo:bar": "rustc --crate-name bar",
"//foo:baz": "rustc --crate-name baz",
},
)
last_args = mock_bazel_launcher.command_runner.results[0].args
self.assertEqual(
last_args,
[
"bazel",
"aquery",
"--config=host",
"--config=quiet",
"--consistent_labels",
"--output=jsonproto",
'mnemonic("Rustc", //foo:bar + //foo:baz)',
],
)
def test_query_bazel_commands_with_env_vars(self) -> None:
mock_bazel_launcher = build_utils.MockBazelLauncher()
mock_bazel_launcher.push_expected_outputs(
[
json.dumps(
{
"targets": [
{"id": "1", "label": "//foo:bar"},
],
"actions": [
{
"targetId": "1",
"arguments": ["rustc", "--crate-name", "bar"],
"environmentVariables": [
{"key": "CARGO_PKG_NAME", "value": "bar"}
],
"mnemonic": "Rustc",
},
],
}
)
]
)
self.assertDictEqual(
build_command_query_utils.query_bazel_commands(
mock_bazel_launcher, "execroot", ["//foo:bar"]
),
{
"//foo:bar": "CARGO_PKG_NAME=bar rustc --crate-name bar",
},
)
def test_query_bazel_commands_normalized_label(self) -> None:
mock_bazel_launcher = build_utils.MockBazelLauncher()
mock_bazel_launcher.push_expected_outputs(
[
json.dumps(
{
"targets": [
{"id": "1", "label": "@@//foo:bar"},
],
"actions": [
{
"targetId": "1",
"arguments": ["rustc", "--crate-name", "bar"],
"mnemonic": "Rustc",
},
],
}
)
]
)
self.assertDictEqual(
build_command_query_utils.query_bazel_commands(
mock_bazel_launcher, "execroot", ["//foo:bar"]
),
{
"//foo:bar": "rustc --crate-name bar",
},
)
def test_query_bazel_commands_error(self) -> None:
mock_bazel_launcher = build_utils.MockBazelLauncher()
mock_bazel_launcher.command_runner.push_result(returncode=1)
with self.assertRaisesRegex(
ValueError, "Failed to run bazel action expansion"
):
build_command_query_utils.query_bazel_commands(
mock_bazel_launcher, "execroot", ["//foo:bar"]
)
def test_query_bazel_commands_empty_labels(self) -> None:
mock_launcher = build_utils.MockBazelLauncher()
self.assertDictEqual(
build_command_query_utils.query_bazel_commands(
mock_launcher, "execroot", []
),
{},
)
self.assertEqual(len(mock_launcher.command_runner.results), 0)
def test_query_bazel_commands_invalid_json(self) -> None:
mock_launcher = build_utils.MockBazelLauncher()
mock_launcher.push_expected_outputs(["invalid json"])
with self.assertRaisesRegex(ValueError, "Could not find command"):
build_command_query_utils.query_bazel_commands(
mock_launcher, "execroot", ["//foo:bar"]
)
def test_query_bazel_commands_missing_actions(self) -> None:
mock_launcher = build_utils.MockBazelLauncher()
mock_launcher.push_expected_outputs(
[
json.dumps(
{
"targets": [{"id": "1", "label": "//foo:bar"}],
}
)
]
)
with self.assertRaisesRegex(ValueError, "Could not find command"):
build_command_query_utils.query_bazel_commands(
mock_launcher, "execroot", ["//foo:bar"]
)
def test_query_bazel_commands_target_not_in_results(self) -> None:
mock_launcher = build_utils.MockBazelLauncher()
mock_launcher.push_expected_outputs(
[
json.dumps(
{
"targets": [{"id": "1", "label": "//foo:baz"}],
"actions": [
{
"targetId": "1",
"arguments": ["rustc", "baz"],
"mnemonic": "Rustc",
}
],
}
)
]
)
with self.assertRaisesRegex(ValueError, "Could not find command"):
build_command_query_utils.query_bazel_commands(
mock_launcher, "execroot", ["//foo:bar"]
)
def test_query_bazel_commands_empty_arguments(self) -> None:
mock_launcher = build_utils.MockBazelLauncher()
mock_launcher.push_expected_outputs(
[
json.dumps(
{
"targets": [{"id": "1", "label": "//foo:bar"}],
"actions": [
{
"targetId": "1",
"arguments": [],
"mnemonic": "Rustc",
}
],
}
)
]
)
with self.assertRaisesRegex(ValueError, "Could not find command"):
build_command_query_utils.query_bazel_commands(
mock_launcher, "execroot", ["//foo:bar"]
)
if __name__ == "__main__":
unittest.main()