Validate exact Vision boundary values and real-project regression
This commit is contained in:
@@ -19,9 +19,9 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.4.1</string>
|
<string>0.4.2</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>6</string>
|
<string>7</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>13.0</string>
|
<string>13.0</string>
|
||||||
<key>NSHighResolutionCapable</key>
|
<key>NSHighResolutionCapable</key>
|
||||||
|
|||||||
@@ -924,9 +924,10 @@ final class VideoProcessor: ObservableObject {
|
|||||||
if let pose = sample.pose {
|
if let pose = sample.pose {
|
||||||
guard [pose.x, pose.y, pose.height, pose.yaw, pose.roll].allSatisfy({ $0.isFinite }),
|
guard [pose.x, pose.y, pose.height, pose.yaw, pose.roll].allSatisfy({ $0.isFinite }),
|
||||||
(0...1).contains(pose.x), (0...1).contains(pose.y), (0...1).contains(pose.height),
|
(0...1).contains(pose.x), (0...1).contains(pose.y), (0...1).contains(pose.height),
|
||||||
// Vision angles originate as Float; Float.pi is slightly larger than Double.pi.
|
// Vision may return either Float rounding adjacent to pi. Swift Float.pi
|
||||||
abs(pose.yaw) <= Double(Float.pi), abs(pose.roll) <= Double(Float.pi),
|
// is not necessarily the rounding used by Vision; allow 1e-6 radians.
|
||||||
pose.pitch == nil || (pose.pitch!.isFinite && abs(pose.pitch!) <= Double(Float.pi)) else { return false }
|
abs(pose.yaw) <= (.pi + 1e-6), abs(pose.roll) <= (.pi + 1e-6),
|
||||||
|
pose.pitch == nil || (pose.pitch!.isFinite && abs(pose.pitch!) <= (.pi + 1e-6)) else { return false }
|
||||||
}
|
}
|
||||||
if let color = sample.color {
|
if let color = sample.color {
|
||||||
guard color.luminance.isFinite, (0...1).contains(color.luminance),
|
guard color.luminance.isFinite, (0...1).contains(color.luminance),
|
||||||
|
|||||||
@@ -29,6 +29,35 @@ struct RenderIntegration {
|
|||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
static func main() async throws {
|
static func main() async throws {
|
||||||
|
if let path = ProcessInfo.processInfo.environment["GROWTHLAPSE_VALIDATE_PROJECT"] {
|
||||||
|
let validator = VideoProcessor()
|
||||||
|
guard let project = validator.loadProject(from: URL(fileURLWithPath: path)) else {
|
||||||
|
throw TestFailure.failed("Real project validation failed: \(validator.state)")
|
||||||
|
}
|
||||||
|
print("PASS real project load: \(project.clips.count) clips")
|
||||||
|
if ProcessInfo.processInfo.environment["GROWTHLAPSE_TEST_REAL_DRAFT"] == "1" {
|
||||||
|
let original = try Data(contentsOf: URL(fileURLWithPath: path))
|
||||||
|
var subset = project
|
||||||
|
subset.clips = project.clips.filter { $0.displayName.hasPrefix("063") || $0.displayName.hasPrefix("066") }
|
||||||
|
try require(subset.clips.count == 2, "Expected two selected regression clips")
|
||||||
|
validator.project = subset
|
||||||
|
validator.startDraft(settings: SettingsStore.load())
|
||||||
|
let deadline = Date().addingTimeInterval(180)
|
||||||
|
while validator.state.isRunning && Date() < deadline {
|
||||||
|
try await Task.sleep(nanoseconds: 100_000_000)
|
||||||
|
}
|
||||||
|
guard let draft = validator.draftOutput else {
|
||||||
|
validator.cancel()
|
||||||
|
throw TestFailure.failed("Real draft failed: \(validator.state)\n\(validator.logs.suffix(25).joined(separator: "\n"))")
|
||||||
|
}
|
||||||
|
try require(FileManager.default.fileExists(atPath: draft.path), "Real draft missing")
|
||||||
|
let after = try Data(contentsOf: URL(fileURLWithPath: path))
|
||||||
|
try require(original == after, "Real draft changed source project")
|
||||||
|
print("PASS actual affected clips rendered as isolated draft: \(draft.path)")
|
||||||
|
validator.discardDraft()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
try ContinuityChecks.run()
|
try ContinuityChecks.run()
|
||||||
let defaults = RenderSettings()
|
let defaults = RenderSettings()
|
||||||
try require(defaults.inputFolder == nil && defaults.outputFile == nil
|
try require(defaults.inputFolder == nil && defaults.outputFile == nil
|
||||||
@@ -198,7 +227,7 @@ struct RenderIntegration {
|
|||||||
|
|
||||||
// Vision's single-precision ±pi must survive project validation unchanged.
|
// Vision's single-precision ±pi must survive project validation unchanged.
|
||||||
var angleProject = second
|
var angleProject = second
|
||||||
let visionPi = Double(Float.pi)
|
let visionPi = 3.1415927410125732 // Exact value observed in Vision JSON; not Swift Float.pi
|
||||||
angleProject.clips[0].visualAnalysis = ClipVisualAnalysis(sourceSignature: "angle-test", samples: [
|
angleProject.clips[0].visualAnalysis = ClipVisualAnalysis(sourceSignature: "angle-test", samples: [
|
||||||
VisualSample(time: 0, faceCount: 1,
|
VisualSample(time: 0, faceCount: 1,
|
||||||
pose: FacePose(x: 0.5, y: 0.5, height: 0.3, yaw: visionPi,
|
pose: FacePose(x: 0.5, y: 0.5, height: 0.3, yaw: visionPi,
|
||||||
@@ -210,10 +239,10 @@ struct RenderIntegration {
|
|||||||
let accepted = angleLoader.loadProject(from: angleURL)
|
let accepted = angleLoader.loadProject(from: angleURL)
|
||||||
try require(accepted?.clips[0].visualAnalysis == angleProject.clips[0].visualAnalysis,
|
try require(accepted?.clips[0].visualAnalysis == angleProject.clips[0].visualAnalysis,
|
||||||
"Valid Vision Float.pi angles rejected or altered")
|
"Valid Vision Float.pi angles rejected or altered")
|
||||||
angleProject.clips[0].visualAnalysis!.samples[0].pose!.roll = 3.15
|
angleProject.clips[0].visualAnalysis!.samples[0].pose!.roll = Double.pi + 0.00001
|
||||||
try JSONEncoder().encode(angleProject).write(to: angleURL)
|
try JSONEncoder().encode(angleProject).write(to: angleURL)
|
||||||
try require(angleLoader.loadProject(from: angleURL) == nil, "Out-of-range angle accepted")
|
try require(angleLoader.loadProject(from: angleURL) == nil, "Out-of-range angle accepted")
|
||||||
print("PASS Vision Float.pi project round-trip and invalid-angle rejection")
|
print("PASS Vision exact boundary project round-trip and invalid-angle rejection")
|
||||||
|
|
||||||
// Exercise failure after normalization, while FFmpeg is producing the final video.
|
// Exercise failure after normalization, while FFmpeg is producing the final video.
|
||||||
let wrapper = root.appendingPathComponent("ffmpeg-fail")
|
let wrapper = root.appendingPathComponent("ffmpeg-fail")
|
||||||
|
|||||||
Reference in New Issue
Block a user