From fc2c834d2fcdebe79c96e46caab29a659ab264e4 Mon Sep 17 00:00:00 2001 From: antono2 Date: Wed, 4 Nov 2026 14:07:50 +0100 Subject: [PATCH 1/5] fix: align allocator setup with supported dependency pins --- .github/workflows/test.yml | 6 ++++ scripts/test_setup.py | 65 ++++++++++++++++++++++++++++++++++++++ setup.vsh | 4 +-- 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 scripts/test_setup.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ff4ba7d..ab78ed9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -47,6 +47,9 @@ jobs: - uses: prantlf/setup-v-action@92f20bea50d2c9931a28c96bb4997a3c7357baf0 # v4 with: version: ${{ steps.compat.outputs.v_release }} + - name: Test setup command routing + working-directory: source/modules/antono2/vkmemalloc + run: python scripts/test_setup.py - name: Verify the declared dependency graph shell: bash run: v run source/modules/antono2/vkmemalloc/scripts/check_installed_deps.vsh @@ -112,6 +115,9 @@ jobs: - uses: prantlf/setup-v-action@92f20bea50d2c9931a28c96bb4997a3c7357baf0 # v4 with: version: ${{ steps.compat.outputs.v_release }} + - name: Test setup command routing + working-directory: source/modules/antono2/vkmemalloc + run: python scripts/test_setup.py - name: Verify the declared dependency graph shell: bash run: v run source/modules/antono2/vkmemalloc/scripts/check_installed_deps.vsh diff --git a/scripts/test_setup.py b/scripts/test_setup.py new file mode 100644 index 0000000..a03f507 --- /dev/null +++ b/scripts/test_setup.py @@ -0,0 +1,65 @@ +"""Exercise setup's public CLI without package or native-system mutations.""" +import json +import os +from pathlib import Path +import shutil +import subprocess +import sys +import tempfile +import unittest + + +class SetupChecks(unittest.TestCase): + def setUp(self): + self.root = Path(__file__).resolve().parents[1] + self.compiler = os.environ.get('SETUP_TEST_V') or shutil.which('v') + if not self.compiler: + self.skipTest('V compiler unavailable') + self.temporary = tempfile.TemporaryDirectory(prefix='allocator setup with spaces ') + self.addCleanup(self.temporary.cleanup) + self.work = Path(self.temporary.name) + self.log = self.work / 'commands.jsonl' + self.modules = self.work / 'modules' + setup = self.modules / 'antono2/vulkan/setup.vsh' + setup.parent.mkdir(parents=True) + setup.write_text('// Native setup is intercepted by the fixture CLI.\n') + fake = self.work / 'fake_v.py' + fake.write_text('import json, os, sys\n' + 'with open(os.environ["SETUP_COMMAND_LOG"], "a") as log:\n' + ' log.write(json.dumps(sys.argv[1:])+"\\n")\n') + if os.name == 'nt': + (self.work / 'v.cmd').write_text(f'@echo off\n"{sys.executable}" "{fake}" %*\n') + else: + cli = self.work / 'v' + cli.write_text(f'#!{sys.executable}\n'+fake.read_text()) + cli.chmod(0o755) + self.env = dict(os.environ, VMODULES=str(self.modules), + SETUP_COMMAND_LOG=str(self.log), V_C_ERROR_BUG_REPORT_DISABLED='1') + self.env['PATH'] = str(self.work)+os.pathsep+os.environ['PATH'] + + def run_setup(self, *args, expected=0): + result = subprocess.run([self.compiler, 'run', str(self.root / 'setup.vsh'), *args], + env=self.env, cwd=self.work, capture_output=True, text=True) + self.assertEqual(result.returncode, expected, result.stdout+result.stderr) + return [json.loads(line) for line in self.log.read_text().splitlines()] if self.log.exists() else [] + + def test_install_uses_declared_dependency_versions(self): + commands = self.run_setup('--install') + pins = json.loads((self.root / 'compatibility.json').read_text())['modules'] + installs = [args[1] for args in commands if args[0] == 'install'] + self.assertEqual(installs, [f'antono2.vulkan@{pins["vulkan"]}', + f'antono2.memory@{pins["memory"]}', 'antono2.vkmemalloc']) + self.assertEqual(commands[1], ['run', str(self.modules / 'antono2/vulkan/setup.vsh'), '--install']) + self.assertEqual(commands[-1], ['test', str(self.root)]) + + def test_check_performs_no_installs(self): + self.assertEqual(self.run_setup('--check'), [ + ['run', str(self.modules / 'antono2/vulkan/setup.vsh'), '--check'], + ['test', str(self.root)]]) + + def test_invalid_option_performs_no_commands(self): + self.assertEqual(self.run_setup('--unknown', expected=2), []) + + +if __name__ == '__main__': + unittest.main() diff --git a/setup.vsh b/setup.vsh index c17f083..a258600 100644 --- a/setup.vsh +++ b/setup.vsh @@ -27,7 +27,7 @@ fn main() { } install := os.args.len == 1 || os.args[1] == '--install' if install { - run('v install antono2.vulkan@v3.2.0') or { panic(err) } + run('v install antono2.vulkan@v3.2.2') or { panic(err) } } vulkan_setup := os.join_path(os.vmodules_dir(), 'antono2', 'vulkan', 'setup.vsh') if !os.is_file(vulkan_setup) { @@ -44,7 +44,7 @@ fn main() { } } if install { - run('v install antono2.memory') or { panic(err) } + run('v install antono2.memory@v1.5.0') or { panic(err) } run('v install antono2.vkmemalloc') or { panic(err) } } project_dir := os.dir(os.real_path(@FILE)) From a8cf563ef685f342341897d6ef43e030d53f731c Mon Sep 17 00:00:00 2001 From: antono2 Date: Wed, 4 Nov 2026 14:13:12 +0100 Subject: [PATCH 2/5] test: exercise allocator setup with V fixtures --- .github/workflows/test.yml | 9 ++-- scripts/setup_test.v | 100 +++++++++++++++++++++++++++++++++++++ scripts/test_setup.py | 65 ------------------------ 3 files changed, 103 insertions(+), 71 deletions(-) create mode 100644 scripts/setup_test.v delete mode 100644 scripts/test_setup.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ab78ed9..4593e80 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,6 +11,9 @@ on: permissions: contents: read +env: + V_C_ERROR_BUG_REPORT_DISABLED: '1' + concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} @@ -47,9 +50,6 @@ jobs: - uses: prantlf/setup-v-action@92f20bea50d2c9931a28c96bb4997a3c7357baf0 # v4 with: version: ${{ steps.compat.outputs.v_release }} - - name: Test setup command routing - working-directory: source/modules/antono2/vkmemalloc - run: python scripts/test_setup.py - name: Verify the declared dependency graph shell: bash run: v run source/modules/antono2/vkmemalloc/scripts/check_installed_deps.vsh @@ -115,9 +115,6 @@ jobs: - uses: prantlf/setup-v-action@92f20bea50d2c9931a28c96bb4997a3c7357baf0 # v4 with: version: ${{ steps.compat.outputs.v_release }} - - name: Test setup command routing - working-directory: source/modules/antono2/vkmemalloc - run: python scripts/test_setup.py - name: Verify the declared dependency graph shell: bash run: v run source/modules/antono2/vkmemalloc/scripts/check_installed_deps.vsh diff --git a/scripts/setup_test.v b/scripts/setup_test.v new file mode 100644 index 0000000..af22eb7 --- /dev/null +++ b/scripts/setup_test.v @@ -0,0 +1,100 @@ +import json +import os +import time + +struct SetupProfile { + modules map[string]string +} + +struct SetupFixture { + work string + modules string + log string + original_env map[string]string +} + +fn new_setup_fixture() SetupFixture { + work := os.join_path(os.temp_dir(), + 'allocator setup with spaces ${os.getpid()} ${time.sys_mono_now()}') + modules := os.join_path(work, 'modules') + log := os.join_path(work, 'commands.jsonl') + setup := os.join_path(modules, 'antono2', 'vulkan', 'setup.vsh') + os.mkdir_all(os.dir(setup)) or { panic(err) } + os.write_file(setup, '// Native setup is intercepted by the fixture CLI.\n') or { panic(err) } + fake_source := os.join_path(work, 'fake_v.v') + os.write_file(fake_source, 'module main +import json +import os +fn main() { + mut log := os.open_append(os.getenv("SETUP_COMMAND_LOG")) or { panic(err) } + defer { log.close() } + log.writeln(json.encode(os.args[1..])) or { panic(err) } +} +') or { + panic(err) + } + fake_cli := os.join_path(work, $if windows { 'v.exe' } $else { 'v' }) + compile := + os.execute('${os.quoted_path(@VEXE)} -o ${os.quoted_path(fake_cli)} ${os.quoted_path(fake_source)}') + assert compile.exit_code == 0, compile.output + fixture := SetupFixture{work, modules, log, os.environ()} + os.setenv('VMODULES', modules, true) + os.setenv('SETUP_COMMAND_LOG', log, true) + os.setenv('V_C_ERROR_BUG_REPORT_DISABLED', '1', true) + os.setenv('PATH', work + os.path_delimiter + os.getenv('PATH'), true) + return fixture +} + +fn (fixture SetupFixture) close() { + for name in ['VMODULES', 'SETUP_COMMAND_LOG', 'V_C_ERROR_BUG_REPORT_DISABLED', 'PATH'] { + if name in fixture.original_env { + os.setenv(name, fixture.original_env[name], true) + } else { + os.unsetenv(name) + } + } + os.rmdir_all(fixture.work) or { panic(err) } +} + +fn (fixture SetupFixture) run_setup(arguments string, expected int) [][]string { + result := os.execute('${os.quoted_path(@VEXE)} run ${os.quoted_path(os.join_path(@VMODROOT, + 'setup.vsh'))} ${arguments}') + assert result.exit_code == expected, result.output + mut commands := [][]string{} + if os.is_file(fixture.log) { + for line in os.read_lines(fixture.log) or { panic(err) } { + commands << json.decode([]string, line) or { panic(err) } + } + } + return commands +} + +fn test_setup_install_uses_declared_dependency_versions() { + fixture := new_setup_fixture() + defer { fixture.close() } + commands := fixture.run_setup('--install', 0) + profile := json.decode(SetupProfile, os.read_file(os.join_path(@VMODROOT, 'compatibility.json')) or { + panic(err) + }) or { panic(err) } + installs := commands.filter(it[0] == 'install').map(it[1]) + assert installs == ['antono2.vulkan@${profile.modules['vulkan']}', + 'antono2.memory@${profile.modules['memory']}', 'antono2.vkmemalloc'] + assert commands[1] == ['run', os.join_path(fixture.modules, 'antono2', 'vulkan', 'setup.vsh'), + '--install'] + assert commands.last() == ['test', @VMODROOT] +} + +fn test_setup_check_performs_no_installs() { + fixture := new_setup_fixture() + defer { fixture.close() } + assert fixture.run_setup('--check', 0) == [ + ['run', os.join_path(fixture.modules, 'antono2', 'vulkan', 'setup.vsh'), '--check'], + ['test', @VMODROOT], + ] +} + +fn test_setup_invalid_option_performs_no_commands() { + fixture := new_setup_fixture() + defer { fixture.close() } + assert fixture.run_setup('--unknown', 2) == [][]string{} +} diff --git a/scripts/test_setup.py b/scripts/test_setup.py deleted file mode 100644 index a03f507..0000000 --- a/scripts/test_setup.py +++ /dev/null @@ -1,65 +0,0 @@ -"""Exercise setup's public CLI without package or native-system mutations.""" -import json -import os -from pathlib import Path -import shutil -import subprocess -import sys -import tempfile -import unittest - - -class SetupChecks(unittest.TestCase): - def setUp(self): - self.root = Path(__file__).resolve().parents[1] - self.compiler = os.environ.get('SETUP_TEST_V') or shutil.which('v') - if not self.compiler: - self.skipTest('V compiler unavailable') - self.temporary = tempfile.TemporaryDirectory(prefix='allocator setup with spaces ') - self.addCleanup(self.temporary.cleanup) - self.work = Path(self.temporary.name) - self.log = self.work / 'commands.jsonl' - self.modules = self.work / 'modules' - setup = self.modules / 'antono2/vulkan/setup.vsh' - setup.parent.mkdir(parents=True) - setup.write_text('// Native setup is intercepted by the fixture CLI.\n') - fake = self.work / 'fake_v.py' - fake.write_text('import json, os, sys\n' - 'with open(os.environ["SETUP_COMMAND_LOG"], "a") as log:\n' - ' log.write(json.dumps(sys.argv[1:])+"\\n")\n') - if os.name == 'nt': - (self.work / 'v.cmd').write_text(f'@echo off\n"{sys.executable}" "{fake}" %*\n') - else: - cli = self.work / 'v' - cli.write_text(f'#!{sys.executable}\n'+fake.read_text()) - cli.chmod(0o755) - self.env = dict(os.environ, VMODULES=str(self.modules), - SETUP_COMMAND_LOG=str(self.log), V_C_ERROR_BUG_REPORT_DISABLED='1') - self.env['PATH'] = str(self.work)+os.pathsep+os.environ['PATH'] - - def run_setup(self, *args, expected=0): - result = subprocess.run([self.compiler, 'run', str(self.root / 'setup.vsh'), *args], - env=self.env, cwd=self.work, capture_output=True, text=True) - self.assertEqual(result.returncode, expected, result.stdout+result.stderr) - return [json.loads(line) for line in self.log.read_text().splitlines()] if self.log.exists() else [] - - def test_install_uses_declared_dependency_versions(self): - commands = self.run_setup('--install') - pins = json.loads((self.root / 'compatibility.json').read_text())['modules'] - installs = [args[1] for args in commands if args[0] == 'install'] - self.assertEqual(installs, [f'antono2.vulkan@{pins["vulkan"]}', - f'antono2.memory@{pins["memory"]}', 'antono2.vkmemalloc']) - self.assertEqual(commands[1], ['run', str(self.modules / 'antono2/vulkan/setup.vsh'), '--install']) - self.assertEqual(commands[-1], ['test', str(self.root)]) - - def test_check_performs_no_installs(self): - self.assertEqual(self.run_setup('--check'), [ - ['run', str(self.modules / 'antono2/vulkan/setup.vsh'), '--check'], - ['test', str(self.root)]]) - - def test_invalid_option_performs_no_commands(self): - self.assertEqual(self.run_setup('--unknown', expected=2), []) - - -if __name__ == '__main__': - unittest.main() From d78837dc917c120d57f83343b34dd2d5808ac098 Mon Sep 17 00:00:00 2001 From: antono2 Date: Wed, 4 Nov 2026 14:16:39 +0100 Subject: [PATCH 3/5] test: isolate setup probe outputs and environment snapshots --- scripts/setup_test.v | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/scripts/setup_test.v b/scripts/setup_test.v index af22eb7..a58ad30 100644 --- a/scripts/setup_test.v +++ b/scripts/setup_test.v @@ -37,7 +37,13 @@ fn main() { compile := os.execute('${os.quoted_path(@VEXE)} -o ${os.quoted_path(fake_cli)} ${os.quoted_path(fake_source)}') assert compile.exit_code == 0, compile.output - fixture := SetupFixture{work, modules, log, os.environ()} + mut original_env := map[string]string{} + for name in ['VMODULES', 'SETUP_COMMAND_LOG', 'V_C_ERROR_BUG_REPORT_DISABLED', 'PATH'] { + if value := os.getenv_opt(name) { + original_env[name] = value + } + } + fixture := SetupFixture{work, modules, log, original_env} os.setenv('VMODULES', modules, true) os.setenv('SETUP_COMMAND_LOG', log, true) os.setenv('V_C_ERROR_BUG_REPORT_DISABLED', '1', true) @@ -57,7 +63,10 @@ fn (fixture SetupFixture) close() { } fn (fixture SetupFixture) run_setup(arguments string, expected int) [][]string { - result := os.execute('${os.quoted_path(@VEXE)} run ${os.quoted_path(os.join_path(@VMODROOT, + // setup --check can itself run this suite. Give the probe its own output + // path so Windows never tries to replace the still-running outer setup.exe. + probe := os.join_path(fixture.work, $if windows { 'setup-probe.exe' } $else { 'setup-probe' }) + result := os.execute('${os.quoted_path(@VEXE)} -o ${os.quoted_path(probe)} run ${os.quoted_path(os.join_path(@VMODROOT, 'setup.vsh'))} ${arguments}') assert result.exit_code == expected, result.output mut commands := [][]string{} From 38b403dc6a819d7bc8c98e8de16b2adde6210bc6 Mon Sep 17 00:00:00 2001 From: antono2 Date: Wed, 4 Nov 2026 14:18:45 +0100 Subject: [PATCH 4/5] test: run setup CLI regression as a separate V script --- .github/workflows/test.yml | 10 +++++++++- scripts/{setup_test.v => test_setup.vsh} | 5 +++++ 2 files changed, 14 insertions(+), 1 deletion(-) rename scripts/{setup_test.v => test_setup.vsh} (94%) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4593e80..0be2462 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -50,6 +50,9 @@ jobs: - uses: prantlf/setup-v-action@92f20bea50d2c9931a28c96bb4997a3c7357baf0 # v4 with: version: ${{ steps.compat.outputs.v_release }} + - name: Test setup command routing + working-directory: source/modules/antono2/vkmemalloc + run: v run scripts/test_setup.vsh - name: Verify the declared dependency graph shell: bash run: v run source/modules/antono2/vkmemalloc/scripts/check_installed_deps.vsh @@ -115,6 +118,9 @@ jobs: - uses: prantlf/setup-v-action@92f20bea50d2c9931a28c96bb4997a3c7357baf0 # v4 with: version: ${{ steps.compat.outputs.v_release }} + - name: Test setup command routing + working-directory: source/modules/antono2/vkmemalloc + run: v run scripts/test_setup.vsh - name: Verify the declared dependency graph shell: bash run: v run source/modules/antono2/vkmemalloc/scripts/check_installed_deps.vsh @@ -201,7 +207,9 @@ jobs: - run: sudo apt-get update && sudo apt-get install -y libvulkan-dev libvulkan-volk-dev - name: Test with V3 and GCC working-directory: source/modules/antono2/vkmemalloc - run: v -cc gcc test . + run: | + v run scripts/test_setup.vsh + v -cc gcc test . test: if: always() diff --git a/scripts/setup_test.v b/scripts/test_setup.vsh similarity index 94% rename from scripts/setup_test.v rename to scripts/test_setup.vsh index a58ad30..d71110f 100644 --- a/scripts/setup_test.v +++ b/scripts/test_setup.vsh @@ -107,3 +107,8 @@ fn test_setup_invalid_option_performs_no_commands() { defer { fixture.close() } assert fixture.run_setup('--unknown', 2) == [][]string{} } + +test_setup_install_uses_declared_dependency_versions() +test_setup_check_performs_no_installs() +test_setup_invalid_option_performs_no_commands() +println('Setup CLI checks passed: dependency requests, read-only mode, invalid option.') From 58d0f17ee7c1388a3ccdc038bf3d73a7bb6bdb98 Mon Sep 17 00:00:00 2001 From: antono2 Date: Wed, 4 Nov 2026 14:22:42 +0100 Subject: [PATCH 5/5] test: keep setup fixture compatible with stable and current V --- scripts/test_setup.vsh | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/scripts/test_setup.vsh b/scripts/test_setup.vsh index d71110f..befb943 100644 --- a/scripts/test_setup.vsh +++ b/scripts/test_setup.vsh @@ -1,10 +1,6 @@ -import json import os import time - -struct SetupProfile { - modules map[string]string -} +import v.vmod struct SetupFixture { work string @@ -17,18 +13,17 @@ fn new_setup_fixture() SetupFixture { work := os.join_path(os.temp_dir(), 'allocator setup with spaces ${os.getpid()} ${time.sys_mono_now()}') modules := os.join_path(work, 'modules') - log := os.join_path(work, 'commands.jsonl') + log := os.join_path(work, 'commands.tsv') setup := os.join_path(modules, 'antono2', 'vulkan', 'setup.vsh') os.mkdir_all(os.dir(setup)) or { panic(err) } os.write_file(setup, '// Native setup is intercepted by the fixture CLI.\n') or { panic(err) } fake_source := os.join_path(work, 'fake_v.v') os.write_file(fake_source, 'module main -import json import os fn main() { mut log := os.open_append(os.getenv("SETUP_COMMAND_LOG")) or { panic(err) } defer { log.close() } - log.writeln(json.encode(os.args[1..])) or { panic(err) } + log.writeln(os.args[1..].join("\t")) or { panic(err) } } ') or { panic(err) @@ -72,7 +67,7 @@ fn (fixture SetupFixture) run_setup(arguments string, expected int) [][]string { mut commands := [][]string{} if os.is_file(fixture.log) { for line in os.read_lines(fixture.log) or { panic(err) } { - commands << json.decode([]string, line) or { panic(err) } + commands << line.split('\t') } } return commands @@ -82,12 +77,13 @@ fn test_setup_install_uses_declared_dependency_versions() { fixture := new_setup_fixture() defer { fixture.close() } commands := fixture.run_setup('--install', 0) - profile := json.decode(SetupProfile, os.read_file(os.join_path(@VMODROOT, 'compatibility.json')) or { + profile := vmod.decode(os.read_file(os.join_path(@VMODROOT, 'v.mod')) or { panic(err) }) or { panic(err) - }) or { panic(err) } + } installs := commands.filter(it[0] == 'install').map(it[1]) - assert installs == ['antono2.vulkan@${profile.modules['vulkan']}', - 'antono2.memory@${profile.modules['memory']}', 'antono2.vkmemalloc'] + mut expected_installs := profile.dependencies.clone() + expected_installs << profile.name + assert installs == expected_installs assert commands[1] == ['run', os.join_path(fixture.modules, 'antono2', 'vulkan', 'setup.vsh'), '--install'] assert commands.last() == ['test', @VMODROOT]