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