From f7c40d48c0727a96843c85990cc36ae5a9ac6888 Mon Sep 17 00:00:00 2001
From: Joel Grunbaum <joelgrun@gmail.com>
Date: Sat, 07 Feb 2026 23:42:43 +0000
Subject: [PATCH] Add integration test for binary

---
 scripts/build_binary.py   |   11 +++++++++--
 src/archbuild/builder.py  |   10 +++++++---
 tests/integration_test.py |   31 +++++++++++++++++++++++++------
 3 files changed, 41 insertions(+), 11 deletions(-)

diff --git a/scripts/build_binary.py b/scripts/build_binary.py
index f56d6c9..c5eccf7 100644
--- a/scripts/build_binary.py
+++ b/scripts/build_binary.py
@@ -6,6 +6,7 @@
 import subprocess
 import sys
 import os
+import shutil
 from pathlib import Path
 
 def build():
@@ -27,13 +28,19 @@
     entry_script.write_text("from archbuild.cli import main\nif __name__ == '__main__':\n    main()\n")
 
     # PyInstaller command
-    cmd = [
-        "pyinstaller",
+    pyinstaller_exe = shutil.which("pyinstaller")
+    if pyinstaller_exe:
+        cmd = [pyinstaller_exe]
+    else:
+        cmd = [sys.executable, "-m", "PyInstaller"]
+
+    cmd += [
         "--onefile",
         "--name", "archbuild-bin",
         "--paths", str(src),
         "--clean",
         "--collect-all", "archbuild",
+        "--collect-all", "rich",
         str(entry_script)
     ]
     
diff --git a/src/archbuild/builder.py b/src/archbuild/builder.py
index d55f0b6..597f495 100644
--- a/src/archbuild/builder.py
+++ b/src/archbuild/builder.py
@@ -158,9 +158,13 @@
 
     async def __aenter__(self) -> "Builder":
         """Async context manager entry."""
-        max_workers = self.config.building.max_workers if self.config.building.parallel else 1
-        self._executor = ProcessPoolExecutor(max_workers=max_workers)
-        logger.info(f"Builder initialized with {max_workers} workers")
+        if self.config.building.parallel:
+            max_workers = self.config.building.max_workers
+            self._executor = ProcessPoolExecutor(max_workers=max_workers)
+            logger.info(f"Builder initialized with {max_workers} workers (parallel)")
+        else:
+            self._executor = None
+            logger.info("Builder initialized (sequential)")
         return self
 
     async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
diff --git a/tests/integration_test.py b/tests/integration_test.py
index cad5ccc..31e2ca0 100644
--- a/tests/integration_test.py
+++ b/tests/integration_test.py
@@ -47,9 +47,10 @@
 class IntegrationTest:
     """Integration test runner."""
 
-    def __init__(self, keep_temp: bool = False, use_cli: bool = False):
+    def __init__(self, keep_temp: bool = False, use_cli: bool = False, use_binary: bool = False):
         self.keep_temp = keep_temp
         self.use_cli = use_cli
+        self.use_binary = use_binary
         self.temp_dir: Path | None = None
         self.config: Config | None = None
         self.config_path: Path | None = None
@@ -62,10 +63,19 @@
             raise RuntimeError("Config path not set")
 
         env = os.environ.copy()
-        # Add src to PYTHONPATH so the CLI can find the package
-        env["PYTHONPATH"] = str(Path(__file__).parent.parent / "src")
         
-        cmd = [sys.executable, "-m", "archbuild.cli", "-c", str(self.config_path), command] + list(args)
+        if self.use_binary:
+            binary_path = Path(__file__).parent.parent / "dist" / "archbuild-bin"
+            if not binary_path.exists():
+                raise RuntimeError(f"Binary not found at {binary_path}. Run scripts/build_binary.py first.")
+            
+            cmd = [str(binary_path), "-c", str(self.config_path), command] + list(args)
+            # No PYTHONPATH needed for the standalone binary
+        else:
+            # Add src to PYTHONPATH so the CLI can find the package
+            env["PYTHONPATH"] = str(Path(__file__).parent.parent / "src")
+            cmd = [sys.executable, "-m", "archbuild.cli", "-c", str(self.config_path), command] + list(args)
+            
         return subprocess.run(cmd, capture_output=True, text=True, env=env)
 
     def setup(self) -> None:
@@ -345,7 +355,12 @@
 
     async def run(self) -> bool:
         """Run all integration tests."""
-        mode_str = " (CLI Mode)" if self.use_cli else " (Python Mode)"
+        mode_str = " (Python Mode)"
+        if self.use_binary:
+            mode_str = " (Binary Mode)"
+        elif self.use_cli:
+            mode_str = " (CLI Mode)"
+            
         console.print("[bold magenta]╔════════════════════════════════════════════╗[/]")
         console.print(f"[bold magenta]║     Archbuild Integration Test Suite{mode_str:<10}║[/]")
         console.print("[bold magenta]╚════════════════════════════════════════════╝[/]")
@@ -401,6 +416,10 @@
     """Main entry point."""
     keep_temp = "--keep-temp" in sys.argv
     use_cli = "--use-cli" in sys.argv
+    use_binary = "--use-binary" in sys.argv
+
+    if use_binary:
+        use_cli = True  # Binary mode is a sub-mode of CLI mode
 
     # Check if running on Arch Linux
     if not Path("/etc/arch-release").exists():
@@ -414,7 +433,7 @@
             console.print(f"[red]Required tool not found: {tool}[/]")
             return 1
 
-    test = IntegrationTest(keep_temp=keep_temp, use_cli=use_cli)
+    test = IntegrationTest(keep_temp=keep_temp, use_cli=use_cli, use_binary=use_binary)
     success = await test.run()
 
     return 0 if success else 1

--
Gitblit v1.10.0