summaryrefslogtreecommitdiff
path: root/tests/util.py
blob: f2fbadd93d5c221ad54052a97b0b5e349b78c9cb (plain)
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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# -*- coding: utf-8 -*-
"""
    Sphinx test suite utilities
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~

    :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
    :license: BSD, see LICENSE for details.
"""

import os
import re
import sys
import tempfile
from functools import wraps

from six import StringIO

from nose import tools, SkipTest

from sphinx import application
from sphinx.theming import Theme
from sphinx.ext.autodoc import AutoDirective
from sphinx.pycode import ModuleAnalyzer

from path import path

try:
    # Python >=3.3
    from unittest import mock
except ImportError:
    import mock


__all__ = [
    'rootdir', 'tempdir', 'raises', 'raises_msg',
    'skip_if', 'skip_unless', 'skip_unless_importable', 'Struct',
    'ListOutput', 'TestApp', 'with_app', 'gen_with_app',
    'path', 'with_tempdir',
    'sprint', 'remove_unicode_literals',
    'mock',
]


rootdir = path(os.path.dirname(__file__) or '.').abspath()
tempdir = path(os.environ['SPHINX_TEST_TEMPDIR']).abspath()


def _excstr(exc):
    if type(exc) is tuple:
        return str(tuple(map(_excstr, exc)))
    return exc.__name__


def raises(exc, func, *args, **kwds):
    """Raise AssertionError if ``func(*args, **kwds)`` does not raise *exc*."""
    try:
        func(*args, **kwds)
    except exc:
        pass
    else:
        raise AssertionError('%s did not raise %s' %
                             (func.__name__, _excstr(exc)))


def raises_msg(exc, msg, func, *args, **kwds):
    """Raise AssertionError if ``func(*args, **kwds)`` does not raise *exc*,
    and check if the message contains *msg*.
    """
    try:
        func(*args, **kwds)
    except exc as err:
        assert msg in str(err), "\"%s\" not in \"%s\"" % (msg, err)
    else:
        raise AssertionError('%s did not raise %s' %
                             (func.__name__, _excstr(exc)))


def assert_re_search(regex, text, flags=0):
    if not re.search(regex, text, flags):
        assert False, '%r did not match %r' % (regex, text)


def assert_not_re_search(regex, text, flags=0):
    if re.search(regex, text, flags):
        assert False, '%r did match %r' % (regex, text)


def assert_startswith(thing, prefix):
    if not thing.startswith(prefix):
        assert False, '%r does not start with %r' % (thing, prefix)


def assert_in(x, thing):
    if x not in thing:
        assert False, '%r is not in %r' % (x, thing)


def assert_not_in(x, thing):
    if x in thing:
        assert False, '%r is in %r' % (x, thing)


def skip_if(condition, msg=None):
    """Decorator to skip test if condition is true."""
    def deco(test):
        @tools.make_decorator(test)
        def skipper(*args, **kwds):
            if condition:
                raise SkipTest(msg or 'conditional skip')
            return test(*args, **kwds)
        return skipper
    return deco


def skip_unless(condition, msg=None):
    """Decorator to skip test if condition is false."""
    return skip_if(not condition, msg)


def skip_unless_importable(module, msg=None):
    """Decorator to skip test if module is not importable."""
    try:
        __import__(module)
    except ImportError:
        return skip_if(True, msg)
    else:
        return skip_if(False, msg)


class Struct(object):
    def __init__(self, **kwds):
        self.__dict__.update(kwds)


class ListOutput(object):
    """
    File-like object that collects written text in a list.
    """
    def __init__(self, name):
        self.name = name
        self.content = []

    def reset(self):
        del self.content[:]

    def write(self, text):
        self.content.append(text)


class TestApp(application.Sphinx):
    """
    A subclass of :class:`Sphinx` that runs on the test root, with some
    better default values for the initialization parameters.
    """

    def __init__(self, buildername='html', testroot=None, srcdir=None,
                 freshenv=False, confoverrides=None, status=None, warning=None,
                 tags=None, docutilsconf=None):
        if testroot is None:
            defaultsrcdir = 'root'
            testroot = rootdir / 'root'
        else:
            defaultsrcdir = 'test-' + testroot
            testroot = rootdir / 'roots' / ('test-' + testroot)
        if srcdir is None:
            srcdir = tempdir / defaultsrcdir
        else:
            srcdir = tempdir / srcdir

        if not srcdir.exists():
            testroot.copytree(srcdir)

        if docutilsconf is not None:
            (srcdir / 'docutils.conf').write_text(docutilsconf)

        builddir = srcdir / '_build'
#        if confdir is None:
        confdir = srcdir
#        if outdir is None:
        outdir = builddir.joinpath(buildername)
        if not outdir.isdir():
            outdir.makedirs()
#        if doctreedir is None:
        doctreedir = builddir.joinpath('doctrees')
        if not doctreedir.isdir():
            doctreedir.makedirs()
        if confoverrides is None:
            confoverrides = {}
        if status is None:
            status = StringIO()
        if warning is None:
            warning = ListOutput('stderr')
#        if warningiserror is None:
        warningiserror = False

        self._saved_path = sys.path[:]

        application.Sphinx.__init__(self, srcdir, confdir, outdir, doctreedir,
                                    buildername, confoverrides, status, warning,
                                    freshenv, warningiserror, tags)

    def cleanup(self, doctrees=False):
        Theme.themes.clear()
        AutoDirective._registry.clear()
        ModuleAnalyzer.cache.clear()
        sys.path[:] = self._saved_path
        sys.modules.pop('autodoc_fodder', None)

    def __repr__(self):
        return '<%s buildername=%r>' % (self.__class__.__name__, self.builder.name)


def with_app(*args, **kwargs):
    """
    Make a TestApp with args and kwargs, pass it to the test and clean up
    properly.
    """
    def generator(func):
        @wraps(func)
        def deco(*args2, **kwargs2):
            status, warning = StringIO(), StringIO()
            kwargs['status'] = status
            kwargs['warning'] = warning
            app = TestApp(*args, **kwargs)
            try:
                func(app, status, warning, *args2, **kwargs2)
            finally:
                app.cleanup()
        return deco
    return generator


def gen_with_app(*args, **kwargs):
    """
    Decorate a test generator to pass a TestApp as the first argument to the
    test generator when it's executed.
    """
    def generator(func):
        @wraps(func)
        def deco(*args2, **kwargs2):
            status, warning = StringIO(), StringIO()
            kwargs['status'] = status
            kwargs['warning'] = warning
            app = TestApp(*args, **kwargs)
            try:
                for item in func(app, status, warning, *args2, **kwargs2):
                    yield item
            finally:
                app.cleanup()
        return deco
    return generator


def with_tempdir(func):
    def new_func(*args, **kwds):
        new_tempdir = path(tempfile.mkdtemp(dir=tempdir))
        func(new_tempdir, *args, **kwds)
    new_func.__name__ = func.__name__
    return new_func


def sprint(*args):
    sys.stderr.write(' '.join(map(str, args)) + '\n')


_unicode_literals_re = re.compile(r'u(".*?")|u(\'.*?\')')


def remove_unicode_literals(s):
    return _unicode_literals_re.sub(lambda x: x.group(1) or x.group(2), s)


def find_files(root, suffix=None):
    for dirpath, dirs, files in os.walk(root, followlinks=True):
        dirpath = path(dirpath)
        for f in [f for f in files if not suffix or f.endswith(suffix)]:
            fpath = dirpath / f
            yield os.path.relpath(fpath, root)