1# -*- coding: utf-8 -*-
2
3from __future__ import absolute_import, division, print_function
4
5import io
6import re
7import warnings
8
9import numpy as np
10import pytest
11import six
12
13import matplotlib
14import matplotlib.pyplot as plt
15from matplotlib import patheffects
16from matplotlib.testing.decorators import image_comparison
17from matplotlib.testing.determinism import (_determinism_source_date_epoch,
18                                            _determinism_check)
19
20
21with warnings.catch_warnings():
22    warnings.simplefilter('ignore')
23    needs_ghostscript = pytest.mark.skipif(
24        matplotlib.checkdep_ghostscript()[0] is None,
25        reason="This test needs a ghostscript installation")
26    needs_usetex = pytest.mark.skipif(
27        not matplotlib.checkdep_usetex(True),
28        reason="This test needs a TeX installation")
29
30
31# This tests tends to hit a TeX cache lock on AppVeyor.
32@pytest.mark.flaky(reruns=3)
33@pytest.mark.parametrize('format, use_log, rcParams', [
34    ('ps', False, {}),
35    pytest.param('ps', False, {'ps.usedistiller': 'ghostscript'},
36                 marks=needs_ghostscript),
37    pytest.param('ps', False, {'text.latex.unicode': True,
38                               'text.usetex': True},
39                 marks=[needs_ghostscript, needs_usetex]),
40    ('eps', False, {}),
41    ('eps', True, {'ps.useafm': True}),
42    pytest.param('eps', False, {'text.latex.unicode': True,
43                                'text.usetex': True},
44                 marks=[needs_ghostscript, needs_usetex]),
45], ids=[
46    'ps',
47    'ps with distiller',
48    'ps with usetex',
49    'eps',
50    'eps afm',
51    'eps with usetex'
52])
53def test_savefig_to_stringio(format, use_log, rcParams):
54    matplotlib.rcParams.update(rcParams)
55
56    fig, ax = plt.subplots()
57    buffers = [
58        six.moves.StringIO(),
59        io.StringIO(),
60        io.BytesIO()]
61
62    if use_log:
63        ax.set_yscale('log')
64
65    ax.plot([1, 2], [1, 2])
66    ax.set_title(u"Déjà vu")
67    for buffer in buffers:
68        fig.savefig(buffer, format=format)
69
70    values = [x.getvalue() for x in buffers]
71
72    if six.PY3:
73        values = [
74            values[0].encode('ascii'),
75            values[1].encode('ascii'),
76            values[2]]
77
78    # Remove comments from the output.  This includes things that
79    # could change from run to run, such as the time.
80    values = [re.sub(b'%%.*?\n', b'', x) for x in values]
81
82    assert values[0] == values[1]
83    assert values[1] == values[2].replace(b'\r\n', b'\n')
84    for buffer in buffers:
85        buffer.close()
86
87
88def test_patheffects():
89    with matplotlib.rc_context():
90        matplotlib.rcParams['path.effects'] = [
91            patheffects.withStroke(linewidth=4, foreground='w')]
92        fig, ax = plt.subplots()
93        ax.plot([1, 2, 3])
94        with io.BytesIO() as ps:
95            fig.savefig(ps, format='ps')
96
97
98@needs_usetex
99@needs_ghostscript
100def test_tilde_in_tempfilename():
101    # Tilde ~ in the tempdir path (e.g. TMPDIR, TMP or TEMP on windows
102    # when the username is very long and windows uses a short name) breaks
103    # latex before https://github.com/matplotlib/matplotlib/pull/5928
104    import tempfile
105    import shutil
106    import os
107    import os.path
108
109    tempdir = None
110    old_tempdir = tempfile.tempdir
111    try:
112        # change the path for new tempdirs, which is used
113        # internally by the ps backend to write a file
114        tempdir = tempfile.mkdtemp()
115        base_tempdir = os.path.join(tempdir, "short~1")
116        os.makedirs(base_tempdir)
117        tempfile.tempdir = base_tempdir
118
119        # usetex results in the latex call, which does not like the ~
120        plt.rc('text', usetex=True)
121        plt.plot([1, 2, 3, 4])
122        plt.xlabel(r'\textbf{time} (s)')
123        output_eps = os.path.join(base_tempdir, 'tex_demo.eps')
124        # use the PS backend to write the file...
125        plt.savefig(output_eps, format="ps")
126    finally:
127        tempfile.tempdir = old_tempdir
128        if tempdir:
129            try:
130                shutil.rmtree(tempdir)
131            except Exception as e:
132                # do not break if this is not removable...
133                print(e)
134
135
136def test_source_date_epoch():
137    """Test SOURCE_DATE_EPOCH support for PS output"""
138    # SOURCE_DATE_EPOCH support is not tested with text.usetex,
139    # because the produced timestamp comes from ghostscript:
140    # %%CreationDate: D:20000101000000Z00\'00\', and this could change
141    # with another ghostscript version.
142    _determinism_source_date_epoch(
143        "ps", b"%%CreationDate: Sat Jan 01 00:00:00 2000")
144
145
146def test_determinism_all():
147    """Test for reproducible PS output"""
148    _determinism_check(format="ps")
149
150
151@needs_usetex
152@needs_ghostscript
153def test_determinism_all_tex():
154    """Test for reproducible PS/tex output"""
155    _determinism_check(format="ps", usetex=True)
156
157
158@image_comparison(baseline_images=["empty"], extensions=["eps"])
159def test_transparency():
160    fig, ax = plt.subplots()
161    ax.set_axis_off()
162    ax.plot([0, 1], color="r", alpha=0)
163    ax.text(.5, .5, "foo", color="r", alpha=0)
164
165
166@needs_usetex
167def test_failing_latex(tmpdir):
168    """Test failing latex subprocess call"""
169    path = str(tmpdir.join("tmpoutput.ps"))
170
171    matplotlib.rcParams['text.usetex'] = True
172
173    # This fails with "Double subscript"
174    plt.xlabel("$22_2_2$")
175    with pytest.raises(RuntimeError):
176        plt.savefig(path)
177