171 lines
4.6 KiB
Python
171 lines
4.6 KiB
Python
from __future__ import annotations
|
|
|
|
import csv
|
|
import io
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
SOURCE = REPO_ROOT / "scripts" / "run_set.cpp"
|
|
|
|
|
|
def run(command: list[str], *, cwd: Path | None = None) -> subprocess.CompletedProcess[str]:
|
|
return subprocess.run(command, cwd=cwd, text=True, capture_output=True, check=True)
|
|
|
|
|
|
def build_run_set(tmp_path: Path) -> Path:
|
|
stub = tmp_path / "set_stub.c"
|
|
stub.write_text(
|
|
r'''
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
struct set {
|
|
char **items;
|
|
size_t count;
|
|
};
|
|
|
|
struct set *set_new(void)
|
|
{
|
|
return calloc(1, sizeof(struct set));
|
|
}
|
|
|
|
void set_add(struct set *set, const char *sym)
|
|
{
|
|
set->items = realloc(set->items, sizeof(*set->items) * (set->count + 1));
|
|
set->items[set->count++] = strdup(sym);
|
|
}
|
|
|
|
const char *set_fini(struct set *set, int bpp)
|
|
{
|
|
(void) set;
|
|
(void) bpp;
|
|
return strdup("stub-payload");
|
|
}
|
|
|
|
struct set *set_free(struct set *set)
|
|
{
|
|
if (set) {
|
|
for (size_t i = 0; i < set->count; ++i)
|
|
free(set->items[i]);
|
|
free(set->items);
|
|
free(set);
|
|
}
|
|
return NULL;
|
|
}
|
|
''',
|
|
encoding="utf-8",
|
|
)
|
|
stub_object = tmp_path / "set_stub.o"
|
|
executable = tmp_path / "run_set"
|
|
run(["cc", "-std=gnu11", "-O2", "-c", str(stub), "-o", str(stub_object)])
|
|
run(
|
|
[
|
|
"g++",
|
|
"-std=c++17",
|
|
"-O2",
|
|
"-Wall",
|
|
"-Wextra",
|
|
"-Werror",
|
|
str(SOURCE),
|
|
str(stub_object),
|
|
"-o",
|
|
str(executable),
|
|
]
|
|
)
|
|
return executable
|
|
|
|
|
|
def build_elf_fixture(tmp_path: Path) -> tuple[Path, Path]:
|
|
provider_source = tmp_path / "provider.c"
|
|
provider_source.write_text("int provided_label(void) { return 42; }\n", encoding="utf-8")
|
|
provider = tmp_path / "libprovider.so.1"
|
|
run(
|
|
[
|
|
"cc",
|
|
"-shared",
|
|
"-fPIC",
|
|
"-Wl,-soname,libprovider.so.1",
|
|
str(provider_source),
|
|
"-o",
|
|
str(provider),
|
|
]
|
|
)
|
|
|
|
consumer_source = tmp_path / "consumer.c"
|
|
consumer_source.write_text(
|
|
"extern int provided_label(void); int main(void) { return provided_label() == 42 ? 0 : 1; }\n",
|
|
encoding="utf-8",
|
|
)
|
|
consumer = tmp_path / "consumer"
|
|
run(
|
|
[
|
|
"cc",
|
|
str(consumer_source),
|
|
str(provider),
|
|
f"-Wl,-rpath,{tmp_path}",
|
|
"-o",
|
|
str(consumer),
|
|
]
|
|
)
|
|
return provider, consumer
|
|
|
|
|
|
def parse_output(output: str) -> tuple[dict[str, str], list[dict[str, str]]]:
|
|
lines = output.splitlines()
|
|
metadata: dict[str, str] = {}
|
|
table_start = None
|
|
for index, line in enumerate(lines):
|
|
if line.startswith("role\t"):
|
|
table_start = index
|
|
break
|
|
key, value = line.split("\t", 1)
|
|
metadata[key] = value
|
|
assert table_start is not None
|
|
rows = list(csv.DictReader(io.StringIO("\n".join(lines[table_start:])), delimiter="\t"))
|
|
return metadata, rows
|
|
|
|
|
|
def assert_timing_fields(row: dict[str, str]) -> None:
|
|
fields = ["set_new_ns", "set_add_total_ns", "set_fini_ns", "set_free_ns", "set_api_total_ns"]
|
|
values = {field: int(row[field]) for field in fields}
|
|
assert all(value >= 0 for value in values.values())
|
|
assert values["set_api_total_ns"] == sum(values[field] for field in fields[:-1])
|
|
|
|
|
|
def test_shared_library_builds_provided_set_and_reports_set_api_timings(tmp_path: Path) -> None:
|
|
run_set = build_run_set(tmp_path)
|
|
provider, _ = build_elf_fixture(tmp_path)
|
|
|
|
completed = run([str(run_set), "--bpp", "16", str(provider)])
|
|
metadata, rows = parse_output(completed.stdout)
|
|
|
|
assert metadata["kind"] == "shared-library"
|
|
assert len(rows) == 1
|
|
row = rows[0]
|
|
assert row["role"] == "provided"
|
|
assert row["object"] == str(provider.resolve())
|
|
assert int(row["labels"]) >= 1
|
|
assert row["bpp"] == "16"
|
|
assert row["set"] == "set:stub-payload"
|
|
assert_timing_fields(row)
|
|
|
|
|
|
def test_executable_builds_required_sets_grouped_by_provider(tmp_path: Path) -> None:
|
|
run_set = build_run_set(tmp_path)
|
|
provider, consumer = build_elf_fixture(tmp_path)
|
|
|
|
completed = run([str(run_set), "--bpp", "16", str(consumer)])
|
|
metadata, rows = parse_output(completed.stdout)
|
|
|
|
assert metadata["kind"] == "executable"
|
|
provider_rows = [row for row in rows if Path(row["object"]).name == provider.name]
|
|
assert len(provider_rows) == 1
|
|
row = provider_rows[0]
|
|
assert row["role"] == "required"
|
|
assert int(row["labels"]) >= 1
|
|
assert row["bpp"] == "16"
|
|
assert row["set"] == "set:stub-payload"
|
|
assert_timing_fields(row)
|