1 0
import os
2 0
import subprocess as sp
3 0
import sys
4

5 0
import pytest
6

7 0
try:
8 0
    from coverage import CoverageData
9 0
except ImportError:
10 0
    HAS_COVERAGE = False
11
else:
12 0
    HAS_COVERAGE = True
13 0
    from ..conftest import SUBPROCESS_COVERAGE
14

15 0
PACKAGE_DIR = os.path.dirname(__file__)
16

17

18 0
def run_cmd(cmd, args, path=None, raise_error=True):
19
    """
20
    Runs a shell command with the given argument list.  Changes directory to
21
    ``path`` if given, otherwise runs the command in the current directory.
22

23
    Returns a 3-tuple of (stdout, stderr, exit code)
24

25
    If ``raise_error=True`` raise an exception on non-zero exit codes.
26
    """
27

28 0
    if path is not None:
29
        # Transparently support py.path objects
30 0
        path = str(path)
31

32 0
    p = sp.Popen([cmd] + list(args), stdout=sp.PIPE, stderr=sp.PIPE,
33
                 cwd=path)
34 0
    streams = tuple(s.decode('latin1').strip() for s in p.communicate())
35 0
    return_code = p.returncode
36

37 0
    if raise_error and return_code != 0:
38 0
        raise RuntimeError(
39
            "The command `{0}` with args {1!r} exited with code {2}.\n"
40
            "Stdout:\n\n{3}\n\nStderr:\n\n{4}".format(
41
                cmd, list(args), return_code, streams[0], streams[1]))
42

43 0
    return streams + (return_code,)
44

45

46 0
def run_setup(setup_script, args):
47

48
    # This used to call setuptools.sandbox's run_setup, but due to issues with
49
    # this and Cython (which caused segmentation faults), we now use subprocess.
50

51 0
    setup_script = os.path.abspath(setup_script)
52

53 0
    path = os.path.dirname(setup_script)
54 0
    setup_script = os.path.basename(setup_script)
55

56 0
    if HAS_COVERAGE:
57

58
        # In this case, we run the command using the coverage command and we
59
        # then collect the coverage data into a SUBPROCESS_COVERAGE list which
60
        # is set up at the start of the testing process and is then combined
61
        # into a single .coverage file at the end of the testing process.
62

63 0
        p = sp.Popen(['coverage', 'run', setup_script] + list(args), cwd=path,
64
                     stdout=sp.PIPE, stderr=sp.PIPE)
65 0
        stdout, stderr = p.communicate()
66

67 0
        cdata = CoverageData()
68 0
        cdata.read_file(os.path.join(path, '.coverage'))
69 0
        SUBPROCESS_COVERAGE.append(cdata)
70

71
    else:
72

73
        # Otherwise we just run the tests with Python
74

75 0
        p = sp.Popen([sys.executable, setup_script] + list(args), cwd=path,
76
                     stdout=sp.PIPE, stderr=sp.PIPE)
77 0
        stdout, stderr = p.communicate()
78

79 0
    sys.stdout.write(stdout.decode('utf-8'))
80 0
    sys.stderr.write(stderr.decode('utf-8'))
81

82 0
    if p.returncode != 0:
83 0
        raise SystemExit(p.returncode)
84

85

86 0
@pytest.fixture(scope='function', autouse=True)
87 0
def reset_setup_helpers(request):
88
    """
89
    Saves and restores the global state of the astropy_helpers.setup_helpers
90
    module between tests.
91
    """
92

93 0
    mod = __import__('astropy_helpers.setup_helpers', fromlist=[''])
94

95 0
    old_state = mod._module_state.copy()
96

97 0
    def finalizer(old_state=old_state):
98 0
        mod = sys.modules.get('astropy_helpers.setup_helpers')
99 0
        if mod is not None:
100 0
            mod._module_state.update(old_state)
101

102 0
    request.addfinalizer(finalizer)
103

104

105 0
@pytest.fixture(scope='function', autouse=True)
106 0
def reset_distutils_log():
107
    """
108
    This is a setup/teardown fixture that ensures the log-level of the
109
    distutils log is always set to a default of WARN, since different
110
    settings could affect tests that check the contents of stdout.
111
    """
112

113 0
    from distutils import log
114 0
    log.set_threshold(log.WARN)
115

116

117 0
TEST_PACKAGE_SETUP_PY = """\
118
#!/usr/bin/env python
119

120
from setuptools import setup
121

122
NAME = 'astropy-helpers-test'
123
VERSION = {version!r}
124

125
setup(name=NAME, version=VERSION,
126
      packages=['_astropy_helpers_test_'],
127
      zip_safe=False)
128
"""
129

130

131 0
def create_testpackage(tmpdir, version='0.1'):
132

133 0
    source = tmpdir.mkdir('testpkg')
134

135 0
    with source.as_cwd():
136 0
        source.mkdir('_astropy_helpers_test_')
137 0
        init = source.join('_astropy_helpers_test_', '__init__.py')
138 0
        init.write('__version__ = {0!r}'.format(version))
139 0
        setup_py = TEST_PACKAGE_SETUP_PY.format(version=version)
140 0
        source.join('setup.py').write(setup_py)
141

142
        # Make the new test package into a git repo
143 0
        run_cmd('git', ['init'])
144 0
        run_cmd('git', ['add', '--all'])
145 0
        run_cmd('git', ['commit', '-m', 'test package'])
146

147 0
    return source
148

149

150 0
@pytest.fixture
151 0
def testpackage(tmpdir, version='0.1'):
152
    """
153
    This fixture creates a simplified package called _astropy_helpers_test_
154
    used primarily for testing ah_boostrap, but without using the
155
    astropy_helpers package directly and getting it confused with the
156
    astropy_helpers package already under test.
157
    """
158

159 0
    return create_testpackage(tmpdir, version=version)
160

161

162 0
def cleanup_import(package_name):
163
    """Remove all references to package_name from sys.modules"""
164

165 0
    for k in list(sys.modules):
166 0
        if not isinstance(k, str):
167
            # Some things will actually do this =_=
168 0
            continue
169 0
        elif k.startswith('astropy_helpers.tests'):
170
            # Don't delete imported test modules or else the tests will break,
171
            # badly
172 0
            continue
173 0
        if k == package_name or k.startswith(package_name + '.'):
174 0
            del sys.modules[k]

Read our documentation on viewing source code .

Loading