Files
bot/apps/macos/Tests/BotIPCTests/TestIsolation.swift
zeekay 08582cc303 Rebrand OpenClaw → Hanzo Bot across entire codebase
Complete rename of all OpenClaw/OPENCLAW/openclaw references to
Hanzo Bot/BOT/bot naming convention:

- Env vars: OPENCLAW_* → BOT_* (18 patterns)
- Config: ~/.openclaw/ → ~/.bot/, openclaw.json → bot.json
- Packages: @openclaw/* → @hanzo/bot-*
- Domains: openclaw.ai → hanzo.bot
- Mobile: OpenClawKit → BotKit, bundle IDs ai.openclaw → ai.hanzo.bot
- mDNS: _openclaw-gw._tcp → _bot-gw._tcp
- Docker: CMD hanzo-bot.mjs, BOT_PREFER_PNPM, BOT_DOCKER_APT_PACKAGES
- CI: docker-release.yml adds :latest tag on main branch
2026-02-11 11:13:08 -08:00

117 lines
3.4 KiB
Swift

import Foundation
actor TestIsolationLock {
static let shared = TestIsolationLock()
private var locked = false
private var waiters: [CheckedContinuation<Void, Never>] = []
func acquire() async {
if !self.locked {
self.locked = true
return
}
await withCheckedContinuation { cont in
self.waiters.append(cont)
}
// `unlock()` resumed us; lock is now held for this caller.
}
func release() {
if self.waiters.isEmpty {
self.locked = false
return
}
let next = self.waiters.removeFirst()
next.resume()
}
}
@MainActor
enum TestIsolation {
static func withIsolatedState<T>(
env: [String: String?] = [:],
defaults: [String: Any?] = [:],
_ body: () async throws -> T) async rethrows -> T
{
await TestIsolationLock.shared.acquire()
var previousEnv: [String: String?] = [:]
for (key, value) in env {
previousEnv[key] = getenv(key).map { String(cString: $0) }
if let value {
setenv(key, value, 1)
} else {
unsetenv(key)
}
}
let userDefaults = UserDefaults.standard
var previousDefaults: [String: Any?] = [:]
for (key, value) in defaults {
previousDefaults[key] = userDefaults.object(forKey: key)
if let value {
userDefaults.set(value, forKey: key)
} else {
userDefaults.removeObject(forKey: key)
}
}
do {
let result = try await body()
for (key, value) in previousDefaults {
if let value {
userDefaults.set(value, forKey: key)
} else {
userDefaults.removeObject(forKey: key)
}
}
for (key, value) in previousEnv {
if let value {
setenv(key, value, 1)
} else {
unsetenv(key)
}
}
await TestIsolationLock.shared.release()
return result
} catch {
for (key, value) in previousDefaults {
if let value {
userDefaults.set(value, forKey: key)
} else {
userDefaults.removeObject(forKey: key)
}
}
for (key, value) in previousEnv {
if let value {
setenv(key, value, 1)
} else {
unsetenv(key)
}
}
await TestIsolationLock.shared.release()
throw error
}
}
static func withEnvValues<T>(
_ values: [String: String?],
_ body: () async throws -> T) async rethrows -> T
{
try await self.withIsolatedState(env: values, defaults: [:], body)
}
static func withUserDefaultsValues<T>(
_ values: [String: Any?],
_ body: () async throws -> T) async rethrows -> T
{
try await self.withIsolatedState(env: [:], defaults: values, body)
}
nonisolated static func tempConfigPath() -> String {
FileManager().temporaryDirectory
.appendingPathComponent("hanzo-bot-test-config-\(UUID().uuidString).json")
.path
}
}