-
-
Notifications
You must be signed in to change notification settings - Fork 63
Expand file tree
/
Copy pathtestSupport.py
More file actions
240 lines (197 loc) · 7.98 KB
/
testSupport.py
File metadata and controls
240 lines (197 loc) · 7.98 KB
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
import io
import os
import random
import shutil
import sys
import tempfile
import traceback
import unittest
import AppKit # type: ignore
from PIL import Image, ImageChops
from drawBot.misc import warnings
testRootDir = os.path.dirname(os.path.abspath(__file__))
testDataDir = os.path.join(testRootDir, "data")
tempTestDataDir = os.path.join(testRootDir, "tempTestData")
if not os.path.exists(tempTestDataDir):
os.mkdir(tempTestDataDir)
warnings.shouldShowWarnings = True
class DrawBotBaseTest(unittest.TestCase):
def assertPDFFilesEqual(self, path1, path2):
# read as pdf document
pdf1 = AppKit.PDFDocument.alloc().initWithURL_(AppKit.NSURL.fileURLWithPath_(path1))
pdf2 = AppKit.PDFDocument.alloc().initWithURL_(AppKit.NSURL.fileURLWithPath_(path2))
self.assertIsNotNone(pdf1, "PDF could not be read from %r" % path1)
self.assertIsNotNone(pdf2, "PDF could not be read from %r" % path2)
self.assertTrue(pdf1.pageCount() == pdf2.pageCount(), "PDFs has not the same amount of pages")
# loop over all pages
for pageIndex in range(pdf1.pageCount()):
# get the pages
page1 = pdf1.pageAtIndex_(pageIndex)
page2 = pdf2.pageAtIndex_(pageIndex)
# create images from the page data
image1 = AppKit.NSImage.alloc().initWithData_(page1.dataRepresentation())
image2 = AppKit.NSImage.alloc().initWithData_(page2.dataRepresentation())
# compare the image tiff data
# no use to show the complete diff of the binary data
data1 = image1.TIFFRepresentation()
data2 = image2.TIFFRepresentation()
if data1 == data2:
return # all fine
# Fall back to fuzzy image compare
f1 = io.BytesIO(data1)
f2 = io.BytesIO(data2)
similarity = compareImages(f1, f2)
self.assertLessEqual(
similarity,
0.0011,
"PDF files %r and %r are not similar enough: %s (page %s)" % (path1, path2, similarity, pageIndex + 1),
)
def assertSVGFilesEqual(self, path1, path2):
# compare the content by line
self.assertEqual(readData(path1), readData(path2), "SVG files %r and %r are not identical" % (path1, path2))
def assertImageFilesEqual(self, path1, path2):
data1 = readData(path1)
data2 = readData(path2)
if data1 == data2:
return # all fine
# Fall back to fuzzy image compare
f1 = io.BytesIO(data1)
f2 = io.BytesIO(data2)
similarity = compareImages(f1, f2)
self.assertLessEqual(
similarity, 0.0011, "Images %r and %r are not similar enough: %s" % (path1, path2, similarity)
)
def assertGenericFilesEqual(self, path1, path2):
self.assertEqual(readData(path1), readData(path2))
def assertForFileExtension(self, ext, path1, path2):
# based on the ext choose an assertion test
if ext == "pdf":
self.assertPDFFilesEqual(path1, path2)
elif ext == "svg":
self.assertSVGFilesEqual(path1, path2)
elif ext in ("png", "tiff", "jpeg", "jpg"):
self.assertImageFilesEqual(path1, path2)
else:
self.assertGenericFilesEqual(path1, path2)
def executeScriptPath(self, path):
# read content of py file and exec it
from drawBot.misc import warnings
with open(path) as f:
source = f.read()
code = compile(source, path, "exec")
namespace = {"__name__": "__main__", "__file__": path}
warnings.resetWarnings() # so we can test DB warnings
with StdOutCollector(captureStdErr=True) as output:
cwd = os.getcwd()
os.chdir(os.path.dirname(path))
try:
exec(code, namespace)
except Exception:
traceback.print_exc()
os.chdir(cwd)
return output.lines()
class StdOutCollector:
def __init__(self, **kwargs):
# force captureStdErr to be a keyword argument
if kwargs:
captureStdErr = kwargs["captureStdErr"]
assert len(kwargs) == 1
else:
captureStdErr = False
self.captureStdErr = captureStdErr
self._stream = io.StringIO()
def __enter__(self):
self.out = sys.stdout
self.err = sys.stderr
sys.stdout = self
if self.captureStdErr:
sys.stderr = self
return self
def __exit__(self, type, value, traceback):
sys.stdout = self.out
sys.stderr = self.err
def write(self, txt):
self._stream.write(txt)
def flush(self):
pass
def lines(self):
return self._stream.getvalue().splitlines()
class TempFile:
"""This context manager will deliver a pathname for a temporary file, and will
remove it upon exit, if it indeed exists at that time. Note: it does _not_
_create_ the temporary file.
>>> with TempFile() as tmp:
... assert not os.path.exists(tmp.path)
... f = open(tmp.path, "wb")
... b = f.write(b"hello.")
... f.close()
... assert os.path.exists(tmp.path)
...
>>> assert not os.path.exists(tmp.path)
>>> with TempFile(suffix=".png") as tmp:
... assert tmp.path.endswith(".png")
...
"""
_create = staticmethod(tempfile.mktemp)
_destroy = staticmethod(os.remove)
def __init__(self, suffix="", prefix="tmp", dir=None):
self.suffix = suffix
self.prefix = prefix
self.dir = dir
self.path = None
def __enter__(self):
self.path = self._create(suffix=self.suffix, prefix=self.prefix, dir=self.dir)
return self
def __exit__(self, type, value, traceback):
if os.path.exists(self.path):
self._destroy(self.path)
class TempFolder(TempFile):
"""This context manager will create a temporary folder, and will remove it upon exit.
>>> with TempFolder() as tmp:
... assert os.path.exists(tmp.path)
... assert os.listdir(tmp.path) == []
...
>>> assert not os.path.exists(tmp.path)
>>> with TempFolder(suffix=".mystuff") as tmp:
... assert tmp.path.endswith(".mystuff")
...
"""
_create = staticmethod(tempfile.mkdtemp)
_destroy = staticmethod(shutil.rmtree)
def randomSeed(a):
return random.seed(a, version=1) # compatible with Python 2
def readData(path):
"""Return the raw data from a path."""
with open(path, "rb") as f:
return f.read()
def compareImages(path1, path2):
"""Compare two image files and return a number representing how similar they are.
A value of 0 means that the images are identical, a value of 1 means they are maximally
different or not comparable (for example, when their dimensions differ).
"""
im1 = Image.open(path1)
im2 = Image.open(path2)
if im1.size != im2.size:
# Dimensions differ, can't compare further
return 1
if im1 == im2:
# Image data is identical (I checked PIL's Image.__eq__ method: it's solid)
return 0
# Get the difference between the images
diff = ImageChops.difference(im1, im2)
# We'll calculate the average difference based on the histogram provided by PIL
hist = diff.histogram()
assert len(hist) == 4 * 256 # Assuming 4x8-bit RGBA for now. TODO: make this work for L and RGB modes
# Sum the histograms of each channel
summedHist = [sum(hist[pixelValue + ch * 256] for ch in range(4)) for pixelValue in range(256)]
assert len(summedHist) == 256
assert sum(hist) == sum(summedHist)
# Calculate the average of the difference
# First add all pixel values together
totalSum = sum(summedHist[pixelValue] * pixelValue for pixelValue in range(256))
# Then divide by the total number of channel values
average = totalSum / sum(summedHist)
# Scale pixel value range from 0-255 to 0-1
average = average / 255
assert 0.0 <= average <= 1.0
return average