-
Notifications
You must be signed in to change notification settings - Fork 39
/
SwiftPlotTestCase.swift
154 lines (134 loc) · 4.95 KB
/
SwiftPlotTestCase.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import XCTest
@testable import SwiftPlot
class SwiftPlotTestCase: XCTestCase {
override func setUp() {
super.setUp()
do {
for outputDir in allOutputDirectories {
try FileManager.default.createDirectory(
atPath: outputDir, withIntermediateDirectories: true, attributes: nil
)
}
} catch {
fatalError("Failed to create output directory. \(error)")
}
}
}
enum KnownRenderer {
case agg
case coreGraphics
case svg
var subdirectory: String {
switch self {
case .agg: return "agg"
case .coreGraphics: return "quartz"
case .svg: return "svg"
}
}
var fileExtension: String {
switch self {
case .svg: return "svg"
default: return "png"
}
}
}
// TODO: Possibly allow setting this via a command-line flag?
fileprivate let outputDirectoryRoot: String = { () -> String? in
ProcessInfo.processInfo.environment["SWIFTPLOT_TEST_OUTPUT"]
}() ?? "./output/"
fileprivate let referenceDirectoryRoot: URL = {
// #file = SwiftPlotTests/swiftPlotTestCase.swift
var referenceDirectory = URL(fileURLWithPath: #file)
referenceDirectory.deleteLastPathComponent() // swiftPlotTestCase.swift
referenceDirectory.appendPathComponent("Reference")
precondition(FileManager.default.fileExists(atPath: referenceDirectory.path),
"Could not find reference images at \(referenceDirectory.path)")
return referenceDirectory
}()
func outputDirectory(for renderer: KnownRenderer) -> URL {
return URL(fileURLWithPath: outputDirectoryRoot)
.appendingPathComponent(renderer.subdirectory, isDirectory: true)
}
func referenceDirectory(for renderer: KnownRenderer) -> URL {
return referenceDirectoryRoot
.appendingPathComponent(renderer.subdirectory, isDirectory: true)
}
fileprivate let allOutputDirectories: [String] = {
var allOutputDirectories: [String] = []
allOutputDirectories.append(outputDirectory(for: .svg).path)
#if canImport(AGGRenderer)
allOutputDirectories.append(outputDirectory(for: .agg).path)
#endif
#if canImport(QuartzRenderer)
allOutputDirectories.append(outputDirectory(for: .coreGraphics).path)
#endif
return allOutputDirectories
}()
// Helpers for test cases.
var svgOutputDirectory: String {
outputDirectory(for: .svg).path + "/"
}
#if canImport(AGGRenderer)
var aggOutputDirectory: String {
outputDirectory(for: .agg).path + "/"
}
#endif
#if canImport(QuartzRenderer)
var coreGraphicsOutputDirectory: String {
outputDirectory(for: .coreGraphics).path + "/"
}
#endif
/// Verifies that the image rendered by a test is equal to the reference image,
/// otherwise, generates a test failure.
///
func verifyImage(name: String, renderer: KnownRenderer) {
let outputFile = outputDirectory(for: renderer)
.appendingPathComponent(name).appendingPathExtension(renderer.fileExtension)
let outputExists = FileManager.default.fileExists(atPath: outputFile.path)
XCTAssertTrue(outputExists, "🤷♂️ Could not find output file: \(outputFile.path)")
let referenceFile = referenceDirectory(for: renderer)
.appendingPathComponent(name).appendingPathExtension(renderer.fileExtension)
let referenceExists = FileManager.default.fileExists(atPath: referenceFile.path)
XCTAssertTrue(referenceExists, "🤷♂️ Could not find reference file: \(referenceFile.path)")
guard outputExists && referenceExists else { return }
XCTAssertTrue(
FileManager.default.contentsEqual(atPath: outputFile.path, andPath: referenceFile.path),
"🔥 Image mismatch: \(name) (\(renderer.subdirectory))"
)
// TODO: If the test fails, take a visual diff.
// In the mean time, https://www.diffchecker.com/image-diff seems pretty good.
}
// Render-and-verify helper for non-example testcases.
import SVGRenderer
#if canImport(AGGRenderer)
import AGGRenderer
#endif
#if canImport(QuartzRenderer)
import QuartzRenderer
#endif
extension SwiftPlotTestCase {
func renderAndVerify(_ plot: Plot, size: Size = Size(width: 1000, height: 660),
fileName: String = #function) throws {
var fileName = fileName
if fileName.hasSuffix("()") { fileName.removeLast(2) }
let svg_renderer = SVGRenderer()
try plot.drawGraphAndOutput(size: size,
fileName: svgOutputDirectory+fileName,
renderer: svg_renderer)
verifyImage(name: fileName, renderer: .svg)
#if canImport(AGGRenderer)
let agg_renderer = AGGRenderer()
try plot.drawGraphAndOutput(size: size,
fileName: aggOutputDirectory+fileName,
renderer: agg_renderer)
verifyImage(name: fileName, renderer: .agg)
#endif
#if canImport(QuartzRenderer)
let quartz_renderer = QuartzRenderer()
try plot.drawGraphAndOutput(size: size,
fileName: coreGraphicsOutputDirectory+fileName,
renderer: quartz_renderer)
verifyImage(name: fileName, renderer: .coreGraphics)
#endif
}
}