summaryrefslogtreecommitdiff
path: root/tests/test_execfile.py
blob: db78d0f6a8f1f0c2cb0d53b86c1bd9946017bf7e (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
# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt

"""Tests for coverage.execfile"""

import compileall
import fnmatch
import json
import os
import os.path
import re
import sys

import pytest

from coverage import env
from coverage.backward import binary_bytes
from coverage.execfile import run_python_file, run_python_module
from coverage.files import python_reported_file
from coverage.misc import NoCode, NoSource

from tests.coveragetest import CoverageTest, TESTS_DIR, UsingModulesMixin

TRY_EXECFILE = os.path.join(TESTS_DIR, "modules/process_test/try_execfile.py")


class RunFileTest(CoverageTest):
    """Test cases for `run_python_file`."""

    def test_run_python_file(self):
        run_python_file([TRY_EXECFILE, "arg1", "arg2"])
        mod_globs = json.loads(self.stdout())

        # The file should think it is __main__
        assert mod_globs['__name__'] == "__main__"

        # It should seem to come from a file named try_execfile.py
        dunder_file = os.path.basename(mod_globs['__file__'])
        assert dunder_file == "try_execfile.py"

        # It should have its correct module data.
        assert mod_globs['__doc__'].splitlines()[0] == "Test file for run_python_file."
        assert mod_globs['DATA'] == "xyzzy"
        assert mod_globs['FN_VAL'] == "my_fn('fooey')"

        # It must be self-importable as __main__.
        assert mod_globs['__main__.DATA'] == "xyzzy"

        # Argv should have the proper values.
        assert mod_globs['argv0'] == TRY_EXECFILE
        assert mod_globs['argv1-n'] == ["arg1", "arg2"]

        # __builtins__ should have the right values, like open().
        assert mod_globs['__builtins__.has_open'] is True

    def test_no_extra_file(self):
        # Make sure that running a file doesn't create an extra compiled file.
        self.make_file("xxx", """\
            desc = "a non-.py file!"
            """)

        assert os.listdir(".") == ["xxx"]
        run_python_file(["xxx"])
        assert os.listdir(".") == ["xxx"]

    def test_universal_newlines(self):
        # Make sure we can read any sort of line ending.
        pylines = """# try newlines|print('Hello, world!')|""".split('|')
        for nl in ('\n', '\r\n', '\r'):
            with open('nl.py', 'wb') as fpy:
                fpy.write(nl.join(pylines).encode('utf-8'))
            run_python_file(['nl.py'])
        assert self.stdout() == "Hello, world!\n"*3

    def test_missing_final_newline(self):
        # Make sure we can deal with a Python file with no final newline.
        self.make_file("abrupt.py", """\
            if 1:
                a = 1
                print("a is %r" % a)
                #""")
        with open("abrupt.py") as f:
            abrupt = f.read()
        assert abrupt[-1] == '#'
        run_python_file(["abrupt.py"])
        assert self.stdout() == "a is 1\n"

    def test_no_such_file(self):
        path = python_reported_file('xyzzy.py')
        msg = re.escape("No file to run: '{}'".format(path))
        with pytest.raises(NoSource, match=msg):
            run_python_file(["xyzzy.py"])

    def test_directory_with_main(self):
        self.make_file("with_main/__main__.py", """\
            print("I am __main__")
            """)
        run_python_file(["with_main"])
        assert self.stdout() == "I am __main__\n"

    def test_directory_without_main(self):
        self.make_file("without_main/__init__.py", "")
        with pytest.raises(NoSource, match="Can't find '__main__' module in 'without_main'"):
            run_python_file(["without_main"])


class RunPycFileTest(CoverageTest):
    """Test cases for `run_python_file`."""

    def make_pyc(self):                     # pylint: disable=inconsistent-return-statements
        """Create a .pyc file, and return the path to it."""
        if env.JYTHON:
            self.skipTest("Can't make .pyc files on Jython")

        self.make_file("compiled.py", """\
            def doit():
                print("I am here!")

            doit()
            """)
        compileall.compile_dir(".", quiet=True)
        os.remove("compiled.py")

        # Find the .pyc file!
        roots = ["."]
        prefix = getattr(sys, "pycache_prefix", None)
        if prefix:
            roots.append(prefix)
        for root in roots:                              # pragma: part covered
            for there, _, files in os.walk(root):       # pragma: part covered
                for fname in files:
                    if fnmatch.fnmatch(fname, "compiled*.pyc"):
                        return os.path.join(there, fname)

    def test_running_pyc(self):
        pycfile = self.make_pyc()
        run_python_file([pycfile])
        assert self.stdout() == "I am here!\n"

    def test_running_pyo(self):
        pycfile = self.make_pyc()
        pyofile = re.sub(r"[.]pyc$", ".pyo", pycfile)
        assert pycfile != pyofile
        os.rename(pycfile, pyofile)
        run_python_file([pyofile])
        assert self.stdout() == "I am here!\n"

    def test_running_pyc_from_wrong_python(self):
        pycfile = self.make_pyc()

        # Jam Python 2.1 magic number into the .pyc file.
        with open(pycfile, "r+b") as fpyc:
            fpyc.seek(0)
            fpyc.write(binary_bytes([0x2a, 0xeb, 0x0d, 0x0a]))

        with pytest.raises(NoCode, match="Bad magic number in .pyc file"):
            run_python_file([pycfile])

        # In some environments, the pycfile persists and pollutes another test.
        os.remove(pycfile)

    def test_no_such_pyc_file(self):
        path = python_reported_file('xyzzy.pyc')
        msg = re.escape("No file to run: '{}'".format(path))
        with pytest.raises(NoCode, match=msg):
            run_python_file(["xyzzy.pyc"])

    def test_running_py_from_binary(self):
        # Use make_file to get the bookkeeping. Ideally, it would
        # be able to write binary files.
        bf = self.make_file("binary")
        with open(bf, "wb") as f:
            f.write(b'\x7fELF\x02\x01\x01\x00\x00\x00')

        path = python_reported_file('binary')
        msg = (
            re.escape("Couldn't run '{}' as Python code: ".format(path)) +
            r"(TypeError|ValueError): "
            r"("
            r"compile\(\) expected string without null bytes"    # for py2
            r"|"
            r"source code string cannot contain null bytes"     # for py3
            r")"
        )
        with pytest.raises(Exception, match=msg):
            run_python_file([bf])


class RunModuleTest(UsingModulesMixin, CoverageTest):
    """Test run_python_module."""

    run_in_temp_dir = False

    def test_runmod1(self):
        run_python_module(["runmod1", "hello"])
        assert self.stderr() == ""
        assert self.stdout() == "runmod1: passed hello\n"

    def test_runmod2(self):
        run_python_module(["pkg1.runmod2", "hello"])
        assert self.stderr() == ""
        assert self.stdout() == "pkg1.__init__: pkg1\nrunmod2: passed hello\n"

    def test_runmod3(self):
        run_python_module(["pkg1.sub.runmod3", "hello"])
        assert self.stderr() == ""
        assert self.stdout() == "pkg1.__init__: pkg1\nrunmod3: passed hello\n"

    def test_pkg1_main(self):
        run_python_module(["pkg1", "hello"])
        assert self.stderr() == ""
        assert self.stdout() == "pkg1.__init__: pkg1\npkg1.__main__: passed hello\n"

    def test_pkg1_sub_main(self):
        run_python_module(["pkg1.sub", "hello"])
        assert self.stderr() == ""
        assert self.stdout() == "pkg1.__init__: pkg1\npkg1.sub.__main__: passed hello\n"

    def test_pkg1_init(self):
        run_python_module(["pkg1.__init__", "wut?"])
        assert self.stderr() == ""
        assert self.stdout() == "pkg1.__init__: pkg1\npkg1.__init__: __main__\n"

    def test_no_such_module(self):
        with pytest.raises(NoSource, match="No module named '?i_dont_exist'?"):
            run_python_module(["i_dont_exist"])
        with pytest.raises(NoSource, match="No module named '?i'?"):
            run_python_module(["i.dont_exist"])
        with pytest.raises(NoSource, match="No module named '?i'?"):
            run_python_module(["i.dont.exist"])

    def test_no_main(self):
        with pytest.raises(NoSource):
            run_python_module(["pkg2", "hi"])