|
| 1 | +#!/usr/bin/env swift |
| 2 | + |
| 3 | +import AppKit |
| 4 | +import CoreImage |
| 5 | +import Foundation |
| 6 | + |
| 7 | +let fileManager = FileManager.default |
| 8 | +let repoRoot = URL(fileURLWithPath: fileManager.currentDirectoryPath, isDirectory: true) |
| 9 | +let artifactsURL = URL( |
| 10 | + fileURLWithPath: ProcessInfo.processInfo.environment["SNAPSHOT_ARTIFACTS"] |
| 11 | + ?? repoRoot.appendingPathComponent("snapshot-artifacts", isDirectory: true).path, |
| 12 | + isDirectory: true |
| 13 | +) |
| 14 | +let referencesRootURL = repoRoot |
| 15 | + .appendingPathComponent("Tests", isDirectory: true) |
| 16 | + .appendingPathComponent("TextDiffTests", isDirectory: true) |
| 17 | + .appendingPathComponent("__Snapshots__", isDirectory: true) |
| 18 | + |
| 19 | +guard fileManager.fileExists(atPath: artifactsURL.path) else { |
| 20 | + print("No snapshot artifacts directory found at \(artifactsURL.path)") |
| 21 | + exit(0) |
| 22 | +} |
| 23 | + |
| 24 | +let ciContext = CIContext(options: nil) |
| 25 | +let pngExtension = "png" |
| 26 | + |
| 27 | +func loadCIImage(from url: URL) -> CIImage? { |
| 28 | + guard let image = NSImage(contentsOf: url), |
| 29 | + let tiff = image.tiffRepresentation, |
| 30 | + let bitmap = NSBitmapImageRep(data: tiff) else { |
| 31 | + return nil |
| 32 | + } |
| 33 | + return CIImage(bitmapImageRep: bitmap) |
| 34 | +} |
| 35 | + |
| 36 | +func writePNG(ciImage: CIImage, to url: URL) throws { |
| 37 | + let extent = ciImage.extent.integral |
| 38 | + guard let colorSpace = CGColorSpace(name: CGColorSpace.sRGB), |
| 39 | + let cgImage = ciContext.createCGImage(ciImage, from: extent, format: .RGBA8, colorSpace: colorSpace) else { |
| 40 | + throw NSError(domain: "collect_snapshot_artifacts", code: 1) |
| 41 | + } |
| 42 | + |
| 43 | + let rep = NSBitmapImageRep(cgImage: cgImage) |
| 44 | + guard let data = rep.representation(using: .png, properties: [:]) else { |
| 45 | + throw NSError(domain: "collect_snapshot_artifacts", code: 2) |
| 46 | + } |
| 47 | + try data.write(to: url) |
| 48 | +} |
| 49 | + |
| 50 | +func diffImage(referenceURL: URL, failedURL: URL) -> CIImage? { |
| 51 | + guard let reference = loadCIImage(from: referenceURL), |
| 52 | + let failed = loadCIImage(from: failedURL), |
| 53 | + let filter = CIFilter(name: "CIDifferenceBlendMode") else { |
| 54 | + return nil |
| 55 | + } |
| 56 | + filter.setValue(reference, forKey: kCIInputImageKey) |
| 57 | + filter.setValue(failed, forKey: kCIInputBackgroundImageKey) |
| 58 | + return filter.outputImage |
| 59 | +} |
| 60 | + |
| 61 | +let artifactFiles = fileManager.enumerator( |
| 62 | + at: artifactsURL, |
| 63 | + includingPropertiesForKeys: [.isRegularFileKey], |
| 64 | + options: [.skipsHiddenFiles] |
| 65 | +) ?? NSEnumerator() |
| 66 | + |
| 67 | +var enrichedCount = 0 |
| 68 | + |
| 69 | +for case let fileURL as URL in artifactFiles { |
| 70 | + guard fileURL.pathExtension == pngExtension else { continue } |
| 71 | + |
| 72 | + let relativePath = fileURL.path.replacingOccurrences(of: artifactsURL.path + "/", with: "") |
| 73 | + guard !relativePath.contains(".reference."), |
| 74 | + !relativePath.contains(".failed."), |
| 75 | + !relativePath.contains(".diff.") else { |
| 76 | + continue |
| 77 | + } |
| 78 | + |
| 79 | + let referenceURL = referencesRootURL.appendingPathComponent(relativePath) |
| 80 | + guard fileManager.fileExists(atPath: referenceURL.path) else { |
| 81 | + continue |
| 82 | + } |
| 83 | + |
| 84 | + let baseURL = fileURL.deletingPathExtension() |
| 85 | + let failedCopyURL = baseURL.appendingPathExtension("failed").appendingPathExtension(pngExtension) |
| 86 | + let referenceCopyURL = baseURL.appendingPathExtension("reference").appendingPathExtension(pngExtension) |
| 87 | + let diffURL = baseURL.appendingPathExtension("diff").appendingPathExtension(pngExtension) |
| 88 | + |
| 89 | + if fileManager.fileExists(atPath: failedCopyURL.path) { |
| 90 | + try fileManager.removeItem(at: fileURL) |
| 91 | + } else { |
| 92 | + try fileManager.moveItem(at: fileURL, to: failedCopyURL) |
| 93 | + } |
| 94 | + |
| 95 | + if !fileManager.fileExists(atPath: referenceCopyURL.path) { |
| 96 | + try fileManager.copyItem(at: referenceURL, to: referenceCopyURL) |
| 97 | + } |
| 98 | + |
| 99 | + if !fileManager.fileExists(atPath: diffURL.path), |
| 100 | + let diff = diffImage(referenceURL: referenceURL, failedURL: failedCopyURL) { |
| 101 | + try writePNG(ciImage: diff, to: diffURL) |
| 102 | + } |
| 103 | + |
| 104 | + enrichedCount += 1 |
| 105 | +} |
| 106 | + |
| 107 | +print("Enriched \(enrichedCount) snapshot artifact bundle(s) in \(artifactsURL.path)") |
0 commit comments