Files
codex/third_party/voice/test_sdk.py
Benjamin Carlsson 47ca4619be Include GIO in voice SDKs and native runtimes (#43102)
## Why

The Rust GStreamer bindings link GLib's GIO library even when none of the selected plugins imports it. Following only plugin dependencies can omit a library the bindings need.

## What changed

- Export `gio-2.0` pkg-config metadata in the voice SDK.
- Require GIO as a runtime dependency root on Linux, macOS, and Windows, including its transitive dependencies under the existing import checks.
- Reject runtime packages that omit the required GIO library.

## Testing

Extend fixtures to cover GIO inclusion and missing-library rejection across platforms, loading GIO and its transitive dependency after moving a Linux runtime, and resolving `gio-2.0` through pkg-config after moving an SDK.

GitOrigin-RevId: d12e45d387d56c54d94d788cf07d82bb22c2b4a3
2026-09-05 21:18:44 +00:00

155 lines
6.3 KiB
Python

"""Exercise exported bytes, development aliases and moved pkg-config inputs."""
import json
import os
from pathlib import Path
import shlex
import shutil
import subprocess
import tempfile
import unittest
from unittest.mock import patch
from runtime import digest
from sdk import MODULES, export_sdk
class SdkTests(unittest.TestCase):
def setUp(self):
temporary = tempfile.TemporaryDirectory(prefix="voice SDK ")
self.addCleanup(temporary.cleanup)
self.root = Path(temporary.name)
self.prefix = self.root / "prefix"
self.receipts = self.root / "receipts"
self.output = self.root / "sdk"
self.target = "aarch64-apple-darwin"
for name in ("include/glib-2.0", "lib/glib-2.0/include", "lib/pkgconfig"):
(self.prefix / name).mkdir(parents=True)
(self.prefix / "include/glib-2.0/glib.h").write_text("/* SDK fixture */")
(self.prefix / "lib/glib-2.0/include/glibconfig.h").write_text("/* target */")
self.library = self.prefix / "lib/libfixture.0.dylib"
self.library.write_bytes(b"receipt-verified fixture bytes")
for module in (*MODULES, "zlib"):
(self.prefix / f"lib/pkgconfig/{module}.pc").write_text(
"prefix=${pcfiledir}/../..\n"
f"Name: {module}\nDescription: SDK fixture\nVersion: 1.0\n"
"Cflags: -I${prefix}/include\nLibs: -L${prefix}/lib -lfixture\n"
)
audio = self.prefix / "lib/pkgconfig/gstreamer-audio-1.0.pc"
audio.write_text(
audio.read_text() + "Requires.private: gstreamer-tag-1.0, zlib\n"
)
(self.receipts / "inspection").mkdir(parents=True)
self.ci = {
"target": self.target,
"build_complete": True,
"inspection_complete": True,
"manifest_sha256": digest(Path(__file__).with_name("sources.json")),
"commit": "a" * 40,
}
(self.receipts / "ci.json").write_text(json.dumps(self.ci))
(self.receipts / "inspection/binaries.json").write_text(
json.dumps(
[
{
"path": "lib/libfixture.0.dylib",
"target": self.target,
"sha256": digest(self.library),
}
]
)
)
def test_exported_bytes_and_receipt_survive_move_without_original(self):
export_sdk(self.prefix, self.receipts, self.target, self.output)
moved = self.root / "moved SDK"
self.output.rename(moved)
shutil.rmtree(self.prefix)
manifest = json.loads((moved / "sdk.json").read_text())
actual = {
path.relative_to(moved).as_posix(): digest(path)
for path in moved.rglob("*")
if path.is_file() and path.name != "sdk.json"
}
self.assertEqual(
manifest,
{
"schemaVersion": 1,
"target": self.target,
"sourceCommit": "a" * 40,
"sourceManifestSha256": self.ci["manifest_sha256"],
"files": [
{"path": name, "sha256": actual[name]} for name in sorted(actual)
],
},
)
pkg_config = os.environ.get("VOICE_PKG_CONFIG") or shutil.which("pkg-config")
if not pkg_config:
self.skipTest("pkg-config required for the moved development-input probe")
result = subprocess.check_output(
[
pkg_config,
"--define-prefix",
"--cflags",
"--libs",
"gstreamer-audio-1.0",
"gio-2.0",
],
env={
**os.environ,
"PKG_CONFIG_PATH": "",
"PKG_CONFIG_LIBDIR": str(moved / "lib/pkgconfig"),
},
text=True,
).strip()
self.assertEqual(
shlex.split(result),
[f"-I{moved.as_posix()}/include", f"-L{moved.as_posix()}/lib", "-lfixture"],
)
def test_native_mutation_and_wrong_target_are_rejected(self):
with self.assertRaisesRegex(ValueError, "sources and target"):
export_sdk(self.prefix, self.receipts, "x86_64-apple-darwin", self.output)
self.library.write_bytes(b"changed")
with self.assertRaisesRegex(ValueError, "does not match"):
export_sdk(self.prefix, self.receipts, self.target, self.output)
self.assertFalse(self.output.exists())
def test_development_alias_is_materialized_and_escape_is_rejected(self):
alias = self.prefix / "lib/libfixture.dylib"
try:
alias.symlink_to(self.library.name)
except OSError:
self.skipTest("host cannot create the SDK's development symlink")
export_sdk(self.prefix, self.receipts, self.target, self.output)
self.assertFalse((self.output / "lib/libfixture.dylib").is_symlink())
self.assertEqual(
(self.output / "lib/libfixture.dylib").read_bytes(),
self.library.read_bytes(),
)
shutil.rmtree(self.output)
alias.unlink()
alias.symlink_to(self.receipts / "ci.json")
with self.assertRaisesRegex(ValueError, "inside the prefix"):
export_sdk(self.prefix, self.receipts, self.target, self.output)
def test_nonrelocatable_metadata_and_failed_copy_leave_no_output(self):
metadata = self.prefix / "lib/pkgconfig/gstreamer-1.0.pc"
original = metadata.read_text()
metadata.write_text(original.replace("${pcfiledir}/../..", "/old/build"))
with self.assertRaisesRegex(ValueError, "rebuild the SDK"):
export_sdk(self.prefix, self.receipts, self.target, self.output)
metadata.write_text(original)
with patch("sdk.shutil.copy2", side_effect=OSError("copy failed")):
with self.assertRaises(OSError):
export_sdk(self.prefix, self.receipts, self.target, self.output)
self.assertFalse(self.output.exists())
def test_output_cannot_overwrite_or_nest_inside_inputs(self):
for output in (self.prefix, self.prefix / "sdk", self.receipts / "sdk"):
with (
self.subTest(output=output),
self.assertRaisesRegex(ValueError, "fresh"),
):
export_sdk(self.prefix, self.receipts, self.target, output)