blob: f076e137c68b1b11aea36374b2cacafc9f1a7f70 [file] [edit]
#!/usr/bin/env fuchsia-vendored-python
# Copyright 2025 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 os
import sys
import unittest
from pathlib import Path
from unittest import mock
sys.path.insert(0, os.path.dirname(__file__))
import bazel_compdb_utils
class BazelCompdbUtilsTests(unittest.TestCase):
"""Tests for bazel_compdb_utils."""
def test_extract_file_from_args(self):
self.assertEqual(
bazel_compdb_utils.extract_file_from_args(
["gcc", "-c", "foo.cc", "-o", "foo.o"]
),
"foo.cc",
)
self.assertEqual(
bazel_compdb_utils.extract_file_from_args(["clang", "bar.cpp"]),
"bar.cpp",
)
self.assertEqual(
bazel_compdb_utils.extract_file_from_args(["clang", "baz.c"]),
"baz.c",
)
with self.assertRaises(ValueError):
bazel_compdb_utils.extract_file_from_args(
["gcc", "foo.cc", "bar.cc"]
)
with self.assertRaises(ValueError):
bazel_compdb_utils.extract_file_from_args(["gcc", "foo.h"])
with self.assertRaises(ValueError):
bazel_compdb_utils.extract_file_from_args([])
def test_action_init(self):
action_dict = {
"targetId": 1,
"actionKey": "key",
"arguments": ["gcc", "foo.cc"],
"environmentVariables": {},
}
target_dict = {"label": "//foo:bar"}
action = bazel_compdb_utils.Action(action_dict, target_dict)
self.assertEqual(action.label, "//foo:bar")
self.assertEqual(action.target_id, 1)
self.assertEqual(action.action_key, "key")
self.assertEqual(action.arguments, ["gcc", "foo.cc"])
self.assertEqual(action.environment_vars, {})
self.assertEqual(action.file, "foo.cc")
def test_action_is_external(self):
action_dict = {
"targetId": 1,
"actionKey": "key",
"arguments": ["gcc", "foo.cc"],
"environmentVariables": {},
}
internal_target = {"label": "//foo:bar"}
external_target = {"label": "@ext//foo:bar"}
internal_action = bazel_compdb_utils.Action(
action_dict, internal_target
)
external_action = bazel_compdb_utils.Action(
action_dict, external_target
)
self.assertFalse(internal_action.is_external())
self.assertTrue(external_action.is_external())
def test_compdb_formatter_init(self):
formatter = bazel_compdb_utils.CompDBFormatter(
"/build_dir", "/output_base", "/output_path"
)
self.assertEqual(formatter.build_dir, "/build_dir")
self.assertEqual(formatter.output_base, "/output_base")
self.assertEqual(formatter.output_path, "/output_path")
self.assertEqual(formatter.output_base_rel, "../output_base")
self.assertEqual(formatter.output_path_rel, "../output_path")
def test_compdb_formatter_rewrite_file(self):
formatter = bazel_compdb_utils.CompDBFormatter(
"/build", "/build/out/base", "/build/out/path"
)
# External action
action_dict_ext = {
"targetId": 1,
"actionKey": "key",
"arguments": ["gcc", "external/file.cc"],
"environmentVariables": {},
}
target_dict_ext = {"label": "@ext//:lib"}
action_ext = bazel_compdb_utils.Action(action_dict_ext, target_dict_ext)
self.assertEqual(
formatter.rewrite_file(action_ext), "out/base/external/file.cc"
)
# Internal action
action_dict_int = {
"targetId": 2,
"actionKey": "key",
"arguments": ["gcc", "internal/file.cc"],
"environmentVariables": {},
}
target_dict_int = {"label": "//:lib"}
action_int = bazel_compdb_utils.Action(action_dict_int, target_dict_int)
self.assertEqual(
formatter.rewrite_file(action_int), "../../internal/file.cc"
)
def test_compdb_formatter_maybe_rewrite_path(self):
formatter = bazel_compdb_utils.CompDBFormatter(
"/build", "/build/out/base", "/build/out/path"
)
action_dict = {
"targetId": 1,
"actionKey": "key",
"arguments": ["gcc", "action_file.cc"],
"environmentVariables": {},
}
target_dict = {"label": "//:lib"}
action = bazel_compdb_utils.Action(action_dict, target_dict)
# Case 1: file_path == action.file
with mock.patch.object(
formatter, "rewrite_file", return_value="rewritten_action_file"
) as mock_rewrite_file:
self.assertEqual(
formatter.maybe_rewrite_path("action_file.cc", action),
"rewritten_action_file",
)
mock_rewrite_file.assert_called_once_with(action)
# Case 2: file_path == "."
self.assertEqual(formatter.maybe_rewrite_path(".", action), "../../")
# Case 3: file_path starts with sdk/, src/, vendor/, zircon/
self.assertEqual(
formatter.maybe_rewrite_path("sdk/some/path", action),
"../../sdk/some/path",
)
self.assertEqual(
formatter.maybe_rewrite_path("src/some/path", action),
"../../src/some/path",
)
# Case 4: file_path contains "bazel-out/"
self.assertEqual(
formatter.maybe_rewrite_path("foo/bazel-out/bar", action),
"foo/out/path/bar",
)
# Case 5: file_path contains "external/"
self.assertEqual(
formatter.maybe_rewrite_path("foo/external/bar", action),
"foo/out/base/external/bar",
)
# Case 6: Default case
self.assertEqual(
formatter.maybe_rewrite_path("some/other/path", action),
"some/other/path",
)
@mock.patch("bazel_compdb_utils.CompDBFormatter.maybe_rewrite_path")
@mock.patch("bazel_compdb_utils.CompDBFormatter.rewrite_file")
def test_compdb_formatter_action_to_compile_commands(
self, mock_rewrite_file, mock_maybe_rewrite_path
):
formatter = bazel_compdb_utils.CompDBFormatter(
"/build", "/build/out/base", "/build/out/path"
)
action_dict = {
"targetId": 1,
"actionKey": "key",
"arguments": ["gcc", "foo.cc", "arg1", "arg2"],
"environmentVariables": {},
}
target_dict = {"label": "//:lib"}
action = bazel_compdb_utils.Action(action_dict, target_dict)
mock_rewrite_file.return_value = "rewritten_file"
mock_maybe_rewrite_path.side_effect = (
lambda arg, act: f"rewritten_{arg}"
if arg in ["arg1", "arg2"]
else arg
)
expected_compdb = {
"directory": "/build",
"file": "rewritten_file",
"arguments": ["gcc", "foo.cc", "rewritten_arg1", "rewritten_arg2"],
}
self.assertEqual(
formatter.action_to_compile_commands(action), expected_compdb
)
mock_rewrite_file.assert_called_once_with(action)
mock_maybe_rewrite_path.assert_has_calls(
[
mock.call("gcc", action),
mock.call("foo.cc", action),
mock.call("arg1", action),
mock.call("arg2", action),
],
any_order=True,
)
def test_collect_actions(self):
action_graph = {
"targets": [
{"id": 1, "label": "//foo:bar"},
{"id": 2, "label": "//baz:qux"},
],
"actions": [
{
"targetId": 1,
"actionKey": "key1",
"arguments": ["gcc", "foo.cc"],
"environmentVariables": {},
},
{
"targetId": 2,
"actionKey": "key2",
"arguments": ["clang", "baz.cc"],
"environmentVariables": {},
},
],
}
actions = bazel_compdb_utils.collect_actions(action_graph)
self.assertEqual(len(actions), 2)
self.assertIsInstance(actions[0], bazel_compdb_utils.Action)
self.assertEqual(actions[0].label, "//foo:bar")
self.assertEqual(actions[0].file, "foo.cc")
self.assertIsInstance(actions[1], bazel_compdb_utils.Action)
self.assertEqual(actions[1].label, "//baz:qux")
self.assertEqual(actions[1].file, "baz.cc")
def test_collect_actions_empty(self):
self.assertEqual(bazel_compdb_utils.collect_actions({}), [])
self.assertEqual(
bazel_compdb_utils.collect_actions({"targets": [], "actions": []}),
[],
)
@mock.patch("bazel_compdb_utils.run")
@mock.patch("bazel_compdb_utils.get_action_graph_from_labels")
def test_compdb_for_labels(self, mock_get_actions, mock_run):
mock_run.return_value = (
"output_base: /build/out/base\noutput_path: /build/out/path"
)
action_dict1 = {
"targetId": 1,
"actionKey": "key1",
"arguments": ["gcc", "foo.cc"],
"environmentVariables": {},
}
target_dict1 = {"label": "//foo:bar"}
action1 = bazel_compdb_utils.Action(action_dict1, target_dict1)
action_dict2 = {
"targetId": 2,
"actionKey": "key2",
"arguments": ["clang", "baz.cc"],
"environmentVariables": {},
}
target_dict2 = {"label": "//baz:qux"}
action2 = bazel_compdb_utils.Action(action_dict2, target_dict2)
mock_get_actions.return_value = [action1, action2]
build_dir = Path("/build")
result = bazel_compdb_utils.compdb_for_labels(
build_dir, "bazel", ["--config=fuchsia"], ["//foo:bar"]
)
self.assertEqual(
result,
[
{
"directory": "/build",
"file": "../../foo.cc",
"arguments": ["gcc", "../../foo.cc"],
},
{
"directory": "/build",
"file": "../../baz.cc",
"arguments": ["clang", "../../baz.cc"],
},
],
)
mock_get_actions.assert_called_once_with(
"bazel", ["--config=fuchsia"], ["//foo:bar"]
)
mock_run.assert_called_once_with(
"bazel", "info", "output_base", "output_path"
)
def test_dedupe(self):
compdb = [
{"file": "a.cc", "arguments": ["gcc", "a.cc"]},
{"file": "b.cc", "arguments": ["gcc", "b.cc"]},
{"file": "a.cc", "arguments": ["clang", "a.cc"]},
]
deduped = bazel_compdb_utils.dedupe(compdb)
self.assertEqual(
sorted(deduped, key=lambda c: c["file"]),
[
{"file": "a.cc", "arguments": ["clang", "a.cc"]},
{"file": "b.cc", "arguments": ["gcc", "b.cc"]},
],
)
def test_dedupe_empty(self):
self.assertEqual(bazel_compdb_utils.dedupe([]), [])
if __name__ == "__main__":
unittest.main()