import importlib.util import json import os import pathlib import shutil import subprocess import sys import tempfile import types import unittest import unittest.mock class BuildBootstrapTests(unittest.TestCase): def test_arm_profile_selects_versioned_compilers_available_in_ci_and_docker(self): root = pathlib.Path(__file__).resolve().parents[1] profile = (root / 'conan/profiles/arm_64_gcc12_release').read_text() self.assertIn('tools.build:compiler_executables={"c": "aarch64-linux-gnu-gcc-12", "cpp": "aarch64-linux-gnu-g++-12"}', profile) for path in ('.gitea/workflows/ci.yml', 'package/Dockerfile.build'): source = (root / path).read_text() for compiler in ('gcc', 'g++'): self.assertIn(f'{compiler}-12-aarch64-linux-gnu', source) self.assertNotIn(f'{compiler}-aarch64-linux-gnu', source) @unittest.skipUnless(shutil.which('conan'), 'Conan is required for package identity checks') def test_arm_package_ids_exclude_unverified_internal_binaries_only(self): root = pathlib.Path(__file__).resolve().parents[1] if not (root / 'build/build_tools/conan/profiles/arm_64_gcc12_release').is_file(): self.skipTest('Configure first to export the shared Conan profiles') profile = root / 'conan/profiles/arm_64_gcc12_release' with tempfile.TemporaryDirectory() as temporary: directory = pathlib.Path(temporary) environment = dict(os.environ, CONAN_HOME=str(directory / 'conan'), CONAN_NON_INTERACTIVE='1') command = ['conan', 'graph', 'info', str(directory), f'--profile:host={profile}', f'--profile:build={profile}', '--no-remote', '--format=json'] def package_id(*extra): result = subprocess.run([*command, *extra], env=environment, text=True, capture_output=True, timeout=30) self.assertEqual(result.returncode, 0, result.stderr) return json.loads(result.stdout)['graph']['nodes']['0']['package_id'] for name in ('modulus', 'ligo', 'noto', 'luma', 'sonus', 'raylib'): with self.subTest(package=name): (directory / 'conanfile.py').write_text(f'from conan import ConanFile\nclass Fixture(ConanFile):\n name = "{name}"\n version = "latest"\n settings = "os", "arch", "compiler", "build_type"\n') current = package_id() legacy = package_id('-c:h', f'{name}/*:tools.info.package_id:confs=[]') different_compiler = package_id('-c:h', 'tools.build:compiler_executables={"c": "aarch64-linux-gnu-gcc-13", "cpp": "aarch64-linux-gnu-g++-13"}') if name == 'raylib': self.assertEqual(current, different_compiler) else: self.assertNotEqual(current, legacy) self.assertNotEqual(current, different_compiler) def test_build_recipe_is_declared_and_editor_commands_do_not_bootstrap(self): root = pathlib.Path(__file__).resolve().parents[1] spec = importlib.util.spec_from_file_location('vamp_bootstrap', root / 'modulus.py') bootstrap = importlib.util.module_from_spec(spec) spec.loader.exec_module(bootstrap) docker = json.loads((root / 'modulus.json').read_text())['tooling']['docker'] self.assertEqual(docker['socket_commands'], ['build', 'package']) self.assertEqual(json.loads((root / 'modulus.json').read_text())['tooling']['python']['groups'], ['assets']) self.assertFalse((root / 'generator/requirements.txt').exists()) self.assertNotIn('generator/requirements.txt', (root / docker['dockerfile']).read_text()) package = types.ModuleType('modulus') cli = types.ModuleType('modulus.cli') package.cli = cli docker_run = unittest.mock.Mock() cli.docker_run = docker_run cli.main = unittest.mock.Mock(return_value=0) for command in ('clangd', 'format'): with unittest.mock.patch.dict(sys.modules, {'modulus': package, 'modulus.cli': cli}), \ unittest.mock.patch.object(bootstrap, 'ensure_runtime') as prepare, \ unittest.mock.patch.object(bootstrap, 'runtime_ready', return_value=True), \ unittest.mock.patch.object(sys, 'path', list(sys.path)): self.assertEqual(bootstrap.delegate(command, []), 0) prepare.assert_not_called() docker_run.assert_not_called() cli.main.assert_called_once_with(command) self.assertIs(cli.docker_run, docker_run) cli.main.reset_mock()