Constrained tests tool implementation + skill (#5873)

* constrained tests tool implementation + skill

* pr suggestions
This commit is contained in:
Mauro
2026-07-15 12:17:51 +02:00
committed by GitHub
parent 1062e2c6dd
commit 0be5693779
4 changed files with 94 additions and 2 deletions
@@ -0,0 +1,40 @@
import Foundation
/// Saturates the CPU with busy processes for the duration of a test run, approximating
/// a loaded CI runner where timing-sensitive tests are starved of processor time.
final class CPUConstraint {
private var processes = [Process]()
/// 2× the machine's cores is enough to keep the scheduler permanently oversubscribed.
private let taskCount = ProcessInfo.processInfo.processorCount * 2
func start() {
logger.info("\n🐌 Constraining the CPU with \(taskCount) tasks…\n")
for _ in 0..<taskCount {
let process = Process()
process.executableURL = URL(filePath: "/usr/bin/yes")
process.standardOutput = FileHandle.nullDevice
process.standardError = FileHandle.nullDevice
do {
try process.run()
processes.append(process)
} catch {
logger.error("Failed to start a CPU constraining task: \(error)")
}
}
}
func stop() {
guard !processes.isEmpty else { return }
logger.info("\n🐌 Releasing the CPU…\n")
processes.forEach { $0.terminate() }
processes.removeAll()
}
deinit {
stop()
}
}
+9
View File
@@ -39,6 +39,9 @@ struct RunTests: AsyncParsableCommand {
@Option(help: "Only run a specific test (format: 'ClassName/testName').")
var testName: String?
@Flag(help: "Run the tests on a saturated CPU to reproduce the flakiness of a busy CI runner.")
var constrained = false
private var isCI: Bool {
ProcessInfo.processInfo.environment["CI"] != nil
}
@@ -73,6 +76,12 @@ struct RunTests: AsyncParsableCommand {
let outputDirectory = resultBundleURL.deletingLastPathComponent()
try FileManager.default.createDirectory(at: outputDirectory, withIntermediateDirectories: true)
let cpuConstraint = CPUConstraint()
if constrained {
cpuConstraint.start()
}
defer { cpuConstraint.stop() }
try await executeXcodeBuild()
try await shutdownSimulator()
+11 -2
View File
@@ -8,6 +8,9 @@ struct UnitTests: AsyncParsableCommand {
@Flag(help: "Skip preview tests")
var skipPreviews = false
@Flag(help: "Run the unit tests on a saturated CPU to reproduce the flakiness of a busy CI runner.")
var constrained = false
private static let osVersion = CI.defaultOSVersion
private static let device = "iPhone 17"
@@ -19,12 +22,18 @@ struct UnitTests: AsyncParsableCommand {
// Run unit tests
do {
logger.info("\n🧪 Running unit tests…\n")
try await RunTests.parse([
var arguments = [
"--scheme", "UnitTests",
"--device", Self.device,
"--os-version", Self.osVersion,
"--retries", "3"
]).run()
]
if constrained {
arguments.append("--constrained")
}
try await RunTests.parse(arguments).run()
} catch {
failures.append("UnitTests")
logger.error("\n❌ Unit tests failed. \(error)\n")