Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ad660d7

Browse files
author
Alexis Bienvenüe
committed
Share determinism test code for PS and PDF output.
1 parent 22f71a2 commit ad660d7

File tree

3 files changed

+153
-103
lines changed

3 files changed

+153
-103
lines changed

lib/matplotlib/testing/determinism.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
"""
2+
Provides utilities to test output reproducibility.
3+
"""
4+
5+
import io
6+
import os
7+
8+
from matplotlib import pyplot as plt
9+
10+
11+
def _test_determinism_save(filename, objects='mhi', format="pdf"):
12+
# save current value of SOURCE_DATE_EPOCH and set it
13+
# to a constant value, so that time difference is not
14+
# taken into account
15+
sde = os.environ.pop('SOURCE_DATE_EPOCH', None)
16+
os.environ['SOURCE_DATE_EPOCH'] = "946684800"
17+
18+
fig = plt.figure()
19+
20+
if 'm' in objects:
21+
# use different markers...
22+
ax1 = fig.add_subplot(1, 6, 1)
23+
x = range(10)
24+
ax1.plot(x, [1] * 10, marker=u'D')
25+
ax1.plot(x, [2] * 10, marker=u'x')
26+
ax1.plot(x, [3] * 10, marker=u'^')
27+
ax1.plot(x, [4] * 10, marker=u'H')
28+
ax1.plot(x, [5] * 10, marker=u'v')
29+
30+
if 'h' in objects:
31+
# also use different hatch patterns
32+
ax2 = fig.add_subplot(1, 6, 2)
33+
bars = ax2.bar(range(1, 5), range(1, 5)) + \
34+
ax2.bar(range(1, 5), [6] * 4, bottom=range(1, 5))
35+
ax2.set_xticks([1.5, 2.5, 3.5, 4.5])
36+
37+
patterns = ('-', '+', 'x', '\\', '*', 'o', 'O', '.')
38+
for bar, pattern in zip(bars, patterns):
39+
bar.set_hatch(pattern)
40+
41+
if 'i' in objects:
42+
# also use different images
43+
A = [[1, 2, 3], [2, 3, 1], [3, 1, 2]]
44+
fig.add_subplot(1, 6, 3).imshow(A, interpolation='nearest')
45+
A = [[1, 3, 2], [1, 2, 3], [3, 1, 2]]
46+
fig.add_subplot(1, 6, 4).imshow(A, interpolation='bilinear')
47+
A = [[2, 3, 1], [1, 2, 3], [2, 1, 3]]
48+
fig.add_subplot(1, 6, 5).imshow(A, interpolation='bicubic')
49+
50+
x = range(5)
51+
fig.add_subplot(1, 6, 6).plot(x, x)
52+
53+
fig.savefig(filename, format=format)
54+
55+
# Restores SOURCE_DATE_EPOCH
56+
if sde is None:
57+
os.environ.pop('SOURCE_DATE_EPOCH', None)
58+
else:
59+
os.environ['SOURCE_DATE_EPOCH'] = sde
60+
61+
62+
def _test_determinism(objects='mhi', format="pdf"):
63+
"""
64+
Output three times the same graphs and checks that the outputs are exactly
65+
the same.
66+
67+
Parameters
68+
----------
69+
objects : str
70+
contains characters corresponding to objects to be included in the test
71+
document: 'm' for markers, 'h' for hatch patterns, 'i' for images. The
72+
default value is "mhi", so that the test includes all these objects.
73+
format : str
74+
format string. The default value is "pdf".
75+
"""
76+
import sys
77+
from subprocess import check_call
78+
from nose.tools import assert_equal
79+
filename = 'determinism_O%s.%s' % (objects, format)
80+
plots = []
81+
for i in range(3):
82+
check_call([sys.executable, '-R', '-c',
83+
'import matplotlib; '
84+
'matplotlib.use(%r); '
85+
'from matplotlib.testing.determinism '
86+
'import _test_determinism_save;'
87+
'_test_determinism_save(%r,%r,%r)'
88+
% (format, filename, objects, format)])
89+
with open(filename, 'rb') as fd:
90+
plots.append(fd.read())
91+
os.unlink(filename)
92+
for p in plots[1:]:
93+
assert_equal(p, plots[0])
94+
95+
def _test_source_date_epoch(format, string):
96+
"""
97+
Test SOURCE_DATE_EPOCH support. Output a document with the envionment
98+
variable SOURCE_DATE_EPOCH set to 2000-01-01 00:00 UTC and check that the
99+
document contains the timestamp that corresponds to this date (given as an
100+
argument).
101+
102+
Parameters
103+
----------
104+
format : str
105+
format string, such as "pdf".
106+
string : str
107+
timestamp string for 2000-01-01 00:00 UTC.
108+
"""
109+
try:
110+
# save current value of SOURCE_DATE_EPOCH
111+
sde = os.environ.pop('SOURCE_DATE_EPOCH', None)
112+
fig = plt.figure()
113+
ax = fig.add_subplot(1, 1, 1)
114+
x = [1, 2, 3, 4, 5]
115+
ax.plot(x, x)
116+
os.environ['SOURCE_DATE_EPOCH'] = "946684800"
117+
with io.BytesIO() as output:
118+
fig.savefig(output, format=format)
119+
output.seek(0)
120+
buff = output.read()
121+
assert string in buff
122+
os.environ.pop('SOURCE_DATE_EPOCH', None)
123+
with io.BytesIO() as output:
124+
fig.savefig(output, format=format)
125+
output.seek(0)
126+
buff = output.read()
127+
assert string not in buff
128+
finally:
129+
# Restores SOURCE_DATE_EPOCH
130+
if sde is None:
131+
os.environ.pop('SOURCE_DATE_EPOCH', None)
132+
else:
133+
os.environ['SOURCE_DATE_EPOCH'] = sde

lib/matplotlib/tests/test_backend_pdf.py

Lines changed: 8 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from matplotlib import cm, rcParams
1313
from matplotlib.backends.backend_pdf import PdfPages
1414
from matplotlib import pyplot as plt
15+
from matplotlib.testing.determinism import _test_source_date_epoch, _test_determinism
1516
from matplotlib.testing.decorators import (image_comparison, knownfailureif,
1617
cleanup)
1718

@@ -111,133 +112,37 @@ def test_composite_image():
111112

112113
@cleanup
113114
def test_source_date_epoch():
114-
# Test SOURCE_DATE_EPOCH support
115-
try:
116-
# save current value of SOURCE_DATE_EPOCH
117-
sde = os.environ.pop('SOURCE_DATE_EPOCH',None)
118-
fig = plt.figure()
119-
ax = fig.add_subplot(1, 1, 1)
120-
x = [1, 2, 3, 4, 5]
121-
ax.plot(x, x)
122-
os.environ['SOURCE_DATE_EPOCH'] = "946684800"
123-
with io.BytesIO() as pdf:
124-
fig.savefig(pdf, format="pdf")
125-
pdf.seek(0)
126-
buff = pdf.read()
127-
assert b"/CreationDate (D:20000101000000Z)" in buff
128-
os.environ.pop('SOURCE_DATE_EPOCH',None)
129-
with io.BytesIO() as pdf:
130-
fig.savefig(pdf, format="pdf")
131-
pdf.seek(0)
132-
buff = pdf.read()
133-
assert not b"/CreationDate (D:20000101000000Z)" in buff
134-
finally:
135-
# Restores SOURCE_DATE_EPOCH
136-
if sde == None:
137-
os.environ.pop('SOURCE_DATE_EPOCH',None)
138-
else:
139-
os.environ['SOURCE_DATE_EPOCH'] = sde
140-
141-
142-
def _test_determinism_save(filename, objects=''):
143-
# save current value of SOURCE_DATE_EPOCH and set it
144-
# to a constant value, so that time difference is not
145-
# taken into account
146-
sde = os.environ.pop('SOURCE_DATE_EPOCH',None)
147-
os.environ['SOURCE_DATE_EPOCH'] = "946684800"
148-
149-
fig = plt.figure()
150-
151-
if 'm' in objects:
152-
# use different markers, to be recorded in the PdfFile object
153-
ax1 = fig.add_subplot(1, 6, 1)
154-
x = range(10)
155-
ax1.plot(x, [1] * 10, marker=u'D')
156-
ax1.plot(x, [2] * 10, marker=u'x')
157-
ax1.plot(x, [3] * 10, marker=u'^')
158-
ax1.plot(x, [4] * 10, marker=u'H')
159-
ax1.plot(x, [5] * 10, marker=u'v')
160-
161-
if 'h' in objects:
162-
# also use different hatch patterns
163-
ax2 = fig.add_subplot(1, 6, 2)
164-
bars = ax2.bar(range(1, 5), range(1, 5)) + \
165-
ax2.bar(range(1, 5), [6] * 4, bottom=range(1, 5))
166-
ax2.set_xticks([1.5, 2.5, 3.5, 4.5])
167-
168-
patterns = ('-', '+', 'x', '\\', '*', 'o', 'O', '.')
169-
for bar, pattern in zip(bars, patterns):
170-
bar.set_hatch(pattern)
171-
172-
if 'i' in objects:
173-
# also use different images
174-
A = [[1, 2, 3], [2, 3, 1], [3, 1, 2]]
175-
fig.add_subplot(1, 6, 3).imshow(A, interpolation='nearest')
176-
A = [[1, 3, 2], [1, 2, 3], [3, 1, 2]]
177-
fig.add_subplot(1, 6, 4).imshow(A, interpolation='bilinear')
178-
A = [[2, 3, 1], [1, 2, 3], [2, 1, 3]]
179-
fig.add_subplot(1, 6, 5).imshow(A, interpolation='bicubic')
180-
181-
x=range(5)
182-
fig.add_subplot(1, 6, 6).plot(x,x)
183-
184-
fig.savefig(filename, format="pdf")
185-
186-
# Restores SOURCE_DATE_EPOCH
187-
if sde == None:
188-
os.environ.pop('SOURCE_DATE_EPOCH',None)
189-
else:
190-
os.environ['SOURCE_DATE_EPOCH'] = sde
191-
192-
193-
def _test_determinism(objects=''):
194-
import sys
195-
from subprocess import check_call
196-
from nose.tools import assert_equal
197-
filename = 'determinism_O%s.pdf' % objects
198-
plots = []
199-
for i in range(3):
200-
check_call([sys.executable, '-R', '-c',
201-
'import matplotlib; '
202-
'matplotlib.use("pdf"); '
203-
'from matplotlib.tests.test_backend_pdf '
204-
'import _test_determinism_save;'
205-
'_test_determinism_save(%r,%r)' % (filename,objects)])
206-
with open(filename, 'rb') as fd:
207-
plots.append(fd.read())
208-
os.unlink(filename)
209-
for p in plots[1:]:
210-
assert_equal(p,plots[0])
211-
115+
"""Test SOURCE_DATE_EPOCH support for PDF output"""
116+
_test_source_date_epoch("pdf", b"/CreationDate (D:20000101000000Z)")
212117

213118
@cleanup
214119
def test_determinism_plain():
215120
"""Test for reproducible PDF output: simple figure"""
216-
_test_determinism()
121+
_test_determinism('', format="pdf")
217122

218123

219124
@cleanup
220125
def test_determinism_images():
221126
"""Test for reproducible PDF output: figure with different images"""
222-
_test_determinism('i')
127+
_test_determinism('i', format="pdf")
223128

224129

225130
@cleanup
226131
def test_determinism_hatches():
227132
"""Test for reproducible PDF output: figure with different hatches"""
228-
_test_determinism('h')
133+
_test_determinism('h', format="pdf")
229134

230135

231136
@cleanup
232137
def test_determinism_markers():
233138
"""Test for reproducible PDF output: figure with different markers"""
234-
_test_determinism('m')
139+
_test_determinism('m', format="pdf")
235140

236141

237142
@cleanup
238143
def test_determinism_all():
239144
"""Test for reproducible PDF output"""
240-
_test_determinism('mhi')
145+
_test_determinism(format="pdf")
241146

242147

243148
@image_comparison(baseline_images=['hatching_legend'],

lib/matplotlib/tests/test_backend_ps.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import matplotlib
1212
import matplotlib.pyplot as plt
1313
from matplotlib import patheffects
14+
from matplotlib.testing.determinism import _test_source_date_epoch, _test_determinism
1415
from matplotlib.testing.decorators import cleanup, knownfailureif
1516

1617

@@ -173,6 +174,17 @@ def test_tilde_in_tempfilename():
173174
# do not break if this is not removeable...
174175
print(e)
175176

177+
@cleanup
178+
def test_source_date_epoch():
179+
"""Test SOURCE_DATE_EPOCH support for PS output"""
180+
_test_source_date_epoch("ps", b"%%CreationDate: Sat Jan 1 00:00:00 2000")
181+
182+
@cleanup
183+
def test_determinism_all():
184+
"""Test for reproducible PS output"""
185+
_test_determinism(format="ps")
186+
187+
176188

177189
if __name__ == '__main__':
178190
import nose

0 commit comments

Comments
 (0)