1""" 2Provides utilities to test output reproducibility. 3""" 4 5from __future__ import (absolute_import, division, print_function, 6 unicode_literals) 7 8import six 9 10import io 11import os 12import re 13import sys 14from subprocess import check_output 15 16import pytest 17 18import matplotlib 19from matplotlib import pyplot as plt 20 21 22def _determinism_save(objects='mhi', format="pdf", usetex=False): 23 # save current value of SOURCE_DATE_EPOCH and set it 24 # to a constant value, so that time difference is not 25 # taken into account 26 sde = os.environ.pop('SOURCE_DATE_EPOCH', None) 27 os.environ['SOURCE_DATE_EPOCH'] = "946684800" 28 29 matplotlib.rcParams['text.usetex'] = usetex 30 31 fig = plt.figure() 32 33 if 'm' in objects: 34 # use different markers... 35 ax1 = fig.add_subplot(1, 6, 1) 36 x = range(10) 37 ax1.plot(x, [1] * 10, marker=u'D') 38 ax1.plot(x, [2] * 10, marker=u'x') 39 ax1.plot(x, [3] * 10, marker=u'^') 40 ax1.plot(x, [4] * 10, marker=u'H') 41 ax1.plot(x, [5] * 10, marker=u'v') 42 43 if 'h' in objects: 44 # also use different hatch patterns 45 ax2 = fig.add_subplot(1, 6, 2) 46 bars = (ax2.bar(range(1, 5), range(1, 5)) + 47 ax2.bar(range(1, 5), [6] * 4, bottom=range(1, 5))) 48 ax2.set_xticks([1.5, 2.5, 3.5, 4.5]) 49 50 patterns = ('-', '+', 'x', '\\', '*', 'o', 'O', '.') 51 for bar, pattern in zip(bars, patterns): 52 bar.set_hatch(pattern) 53 54 if 'i' in objects: 55 # also use different images 56 A = [[1, 2, 3], [2, 3, 1], [3, 1, 2]] 57 fig.add_subplot(1, 6, 3).imshow(A, interpolation='nearest') 58 A = [[1, 3, 2], [1, 2, 3], [3, 1, 2]] 59 fig.add_subplot(1, 6, 4).imshow(A, interpolation='bilinear') 60 A = [[2, 3, 1], [1, 2, 3], [2, 1, 3]] 61 fig.add_subplot(1, 6, 5).imshow(A, interpolation='bicubic') 62 63 x = range(5) 64 fig.add_subplot(1, 6, 6).plot(x, x) 65 66 if six.PY2 and format == 'ps': 67 stdout = io.StringIO() 68 else: 69 stdout = getattr(sys.stdout, 'buffer', sys.stdout) 70 fig.savefig(stdout, format=format) 71 if six.PY2 and format == 'ps': 72 sys.stdout.write(stdout.getvalue()) 73 74 # Restores SOURCE_DATE_EPOCH 75 if sde is None: 76 os.environ.pop('SOURCE_DATE_EPOCH', None) 77 else: 78 os.environ['SOURCE_DATE_EPOCH'] = sde 79 80 81def _determinism_check(objects='mhi', format="pdf", usetex=False): 82 """ 83 Output three times the same graphs and checks that the outputs are exactly 84 the same. 85 86 Parameters 87 ---------- 88 objects : str 89 contains characters corresponding to objects to be included in the test 90 document: 'm' for markers, 'h' for hatch patterns, 'i' for images. The 91 default value is "mhi", so that the test includes all these objects. 92 format : str 93 format string. The default value is "pdf". 94 """ 95 plots = [] 96 for i in range(3): 97 result = check_output([sys.executable, '-R', '-c', 98 'import matplotlib; ' 99 'matplotlib._called_from_pytest = True; ' 100 'matplotlib.use(%r); ' 101 'from matplotlib.testing.determinism ' 102 'import _determinism_save;' 103 '_determinism_save(%r,%r,%r)' 104 % (format, objects, format, usetex)]) 105 plots.append(result) 106 for p in plots[1:]: 107 if usetex: 108 if p != plots[0]: 109 pytest.skip("failed, maybe due to ghostscript timestamps") 110 else: 111 assert p == plots[0] 112 113 114def _determinism_source_date_epoch(format, string, keyword=b"CreationDate"): 115 """ 116 Test SOURCE_DATE_EPOCH support. Output a document with the environment 117 variable SOURCE_DATE_EPOCH set to 2000-01-01 00:00 UTC and check that the 118 document contains the timestamp that corresponds to this date (given as an 119 argument). 120 121 Parameters 122 ---------- 123 format : str 124 format string, such as "pdf". 125 string : str 126 timestamp string for 2000-01-01 00:00 UTC. 127 keyword : bytes 128 a string to look at when searching for the timestamp in the document 129 (used in case the test fails). 130 """ 131 buff = check_output([sys.executable, '-R', '-c', 132 'import matplotlib; ' 133 'matplotlib._called_from_pytest = True; ' 134 'matplotlib.use(%r); ' 135 'from matplotlib.testing.determinism ' 136 'import _determinism_save;' 137 '_determinism_save(%r,%r)' 138 % (format, "", format)]) 139 find_keyword = re.compile(b".*" + keyword + b".*") 140 key = find_keyword.search(buff) 141 if key: 142 print(key.group()) 143 else: 144 print("Timestamp keyword (%s) not found!" % keyword) 145 assert string in buff 146