#!/usr/bin/env python3 """Install non-destructive OrionToolbar regression tests into the current project.""" from pathlib import Path from datetime import datetime import shutil import sys root = Path.cwd() if not (root/'OrionToolbar/OrionVMController.swift').is_file() or not (root/'OrionToolbar/OrionMenuView.swift').is_file(): sys.exit('Run from the OrionToolbar repository root. No changes made.') files = { 'tests/test_oriontoolbar.py': r'''#!/usr/bin/env python3 """Source-contract and macOS runtime smoke tests. No VM state is changed.""" import os import re import shutil import subprocess import sys import unittest from pathlib import Path ROOT = Path(__file__).resolve().parents[1] CONTROLLER = (ROOT/'OrionToolbar/OrionVMController.swift').read_text() VIEW = (ROOT/'OrionToolbar/OrionMenuView.swift').read_text() APP = (ROOT/'OrionToolbar/OrionToolbarApp.swift').read_text() class SourceContracts(unittest.TestCase): def test_menu_bar_app(self): self.assertIn('MenuBarExtra', APP) self.assertIn('.menuBarExtraStyle(.window)', APP) def test_vm_discovery(self): self.assertIn('func discoverVMs()', CONTROLLER) self.assertIn('runPrlctl(["list", "-a", "-j"])', CONTROLLER) def test_refresh_probes_on_startup_and_timer(self): self.assertGreaterEqual(CONTROLLER.count('await refreshServiceAvailability()'), 2) self.assertIn('func refreshServiceAvailability()', CONTROLLER) def test_ssh_only_if_reachable(self): self.assertEqual(VIEW.count('vm.reachableSSH.contains(server.id)'), 2) self.assertIn('server.sshUser.isEmpty', VIEW) def test_mailpit_only_if_reachable(self): self.assertEqual(VIEW.count('vm.reachableMailpit.contains(server.id)'), 2) self.assertIn('server.mailpitPort != nil', VIEW) def test_running_filter_for_probes(self): block = CONTROLLER.split('func refreshServiceAvailability()', 1)[1].split('private nonisolated static func tcpPortOpen',1)[0] self.assertIn('"running"', block) self.assertIn('reachableSSH = Set(', block) self.assertIn('reachableMailpit = Set(', block) def test_probe_timeouts(self): self.assertIn('"-G", "2", "-z"', CONTROLLER) self.assertRegex(CONTROLLER, r'timeoutInterval\s*=\s*3') def test_actions(self): for name in ('openSSH','openMailpit','operate','hideVM','restoreVM','reorderVM','reorderServer','integrate','removeDefinition'): with self.subTest(name=name): self.assertRegex(CONTROLLER, r'func\s+' + name + r'\s*\(') def test_vm_operations(self): for operation in ('"start"','"stop"','"restart"','"force-stop"'): with self.subTest(operation=operation): self.assertIn(operation, VIEW + CONTROLLER) def test_persisted_order_and_visibility(self): self.assertIn('orionVMOrder.v1', CONTROLLER) self.assertIn('orionHiddenVMIDs.v1', CONTROLLER) self.assertIn('UserDefaults.standard', CONTROLLER) def test_drag_and_drop(self): self.assertGreaterEqual(VIEW.count('.onDrag'), 2) self.assertGreaterEqual(VIEW.count('.onDrop'), 2) def test_icons(self): self.assertIn('VM.app', CONTROLLER) self.assertIn('func icon(for machine:', CONTROLLER) self.assertIn('func icon(for server:', CONTROLLER) def test_settings(self): self.assertIn('OrionSettingsWindowPresenter', VIEW) self.assertIn('SettingsView()', VIEW) def test_confirmation_before_force_stop(self): self.assertIn('pendingForceStop', VIEW) self.assertIn('.confirmationDialog("Force stop virtual machine?', VIEW) def test_button_spacing(self): self.assertIn('.padding(.leading, 12)', VIEW) def test_foreground_activation_contract(self): # Source-level contract only; actual window z-order requires manual UI test. self.assertIn('NSWorkspace.shared', CONTROLLER) self.assertIn('func openSSH', CONTROLLER) self.assertIn('func openMailpit', CONTROLLER) @unittest.skipUnless(sys.platform == 'darwin' and os.getenv('ORION_LIVE') == '1', 'Set ORION_LIVE=1 on macOS') class LiveReadOnly(unittest.TestCase): def test_parallels_list_json(self): import json executable = shutil.which('prlctl') or '/Applications/Parallels Desktop.app/Contents/MacOS/prlctl' if not Path(executable).exists(): self.skipTest('Parallels CLI not installed') p = subprocess.run([executable,'list','-a','-j'], capture_output=True, text=True, timeout=20) self.assertEqual(p.returncode,0,p.stderr) data = json.loads(p.stdout) self.assertIsInstance(data,list) for vm in data: self.assertIn('uuid',vm) self.assertIn('name',vm) self.assertIn('status',vm) def test_xcode_project(self): self.assertTrue((ROOT/'OrionToolbar.xcodeproj/project.pbxproj').is_file()) if __name__ == '__main__': unittest.main() ''', 'scripts/test.sh': r'''#!/bin/zsh set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" cd "$ROOT" mkdir -p .build/test-logs LOG=".build/test-logs/test-$(date +%Y%m%d-%H%M%S).log" { echo '=== ORIONTOOLBAR REGRESSION TESTS ===' echo 'Source contracts (non-destructive)' /usr/bin/env python3 -m unittest discover -s tests -p 'test_*.py' -v if [[ "${1:-}" == '--live' || "${1:-}" == '--all' ]]; then echo 'Read-only Parallels integration checks' ORION_LIVE=1 /usr/bin/env python3 -m unittest discover -s tests -p 'test_*.py' -v fi if [[ "${1:-}" == '--build' || "${1:-}" == '--all' ]]; then echo 'Xcode build (does not launch app)' /bin/zsh scripts/build.sh fi } 2>&1 | tee "$LOG" echo "Test log: $LOG" ''', 'tests/MANUAL_TEST_PLAN.md': '''# OrionToolbar manual regression checklist Run `./scripts/test.sh --all` first. These checks require a person using macOS; automated source checks **cannot** prove UI focus or VM lifecycle behaviour. ## Dashboard - [ ] Menu bar opens; Running, All, Integrated sections expand/collapse. - [ ] VM status and IP match Parallels; refresh updates them. - [ ] VM icons show the expected OS; custom integrated icon persists. - [ ] Reorder All VMs, wait 20 seconds, reopen; order persists. - [ ] Reorder Integrated Servers, wait 20 seconds, reopen; order persists. - [ ] Remove a VM from toolbar, confirm it is still in Parallels; re-add it. - [ ] Settings opens in front; edits survive app relaunch. - [ ] Button spacing remains readable; no unwanted chevrons. ## SSH and Mailpit - [ ] VM stopped: neither service button visible. - [ ] VM running with SSH port closed: SSH button hidden after refresh. - [ ] VM running with SSH port open: SSH button visible after refresh. - [ ] SSH click: Terminal appears **above** OrionToolbar and SSH starts. - [ ] VM running without Mailpit: Mailpit button hidden after refresh. - [ ] VM running with Mailpit HTTP responding: button visible after refresh. - [ ] Mailpit click: browser appears **above** OrionToolbar and correct page loads. - [ ] Stop service without stopping VM: corresponding button disappears after refresh. - [ ] Restart service: corresponding button reappears after refresh. - [ ] Open and close Settings after launching Terminal/browser; foreground behaviour still correct. ## VM operations — changes VM state; use disposable VM only - [ ] Start a stopped test VM; status becomes Running. - [ ] Restart test VM; busy indicator appears then clears. - [ ] Stop test VM; status becomes Stopped; service buttons disappear. - [ ] Resume a paused/suspended test VM, if supported. - [ ] Force Stop shows confirmation; Cancel does not stop VM. - [ ] Force Stop confirmed only on a disposable VM; state updates. - [ ] Failed Parallels command displays an error and clears busy indicator. ## Final - [ ] Quit and relaunch; preferences, hidden VMs and ordering persist. - [ ] Review `.build/test-logs/` and `.build/build-logs/`. ''' } for rel in files: target=root/rel if target.exists(): backup=root/'backups'/('tests-'+datetime.now().strftime('%Y%m%d-%H%M%S'))/rel backup.parent.mkdir(parents=True,exist_ok=True) shutil.copy2(target,backup) for rel, content in files.items(): target=root/rel target.parent.mkdir(parents=True,exist_ok=True) target.write_text(content) (root/'scripts/test.sh').chmod(0o755) print('Installed:', ', '.join(files)) print('Run: ./scripts/test.sh') print('Run build + live checks: ./scripts/test.sh --all')