Coverage for tests / test_files.py: 100.000%
291 statements
« prev ^ index » next coverage.py v7.12.1a0.dev1, created at 2025-11-30 17:57 +0000
« prev ^ index » next coverage.py v7.12.1a0.dev1, created at 2025-11-30 17:57 +0000
1# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
2# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt
4"""Tests for files.py"""
6from __future__ import annotations 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
8import itertools 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
9import os 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
10import os.path 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
11import re 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
13from typing import Any, Protocol 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
14from collections.abc import Iterable 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
15from unittest import mock 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
17import pytest 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
19from coverage import env, files 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
20from coverage.exceptions import ConfigError 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
21from coverage.files import ( 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
22 GlobMatcher,
23 ModuleMatcher,
24 PathAliases,
25 TreeMatcher,
26 abs_file,
27 actual_path,
28 find_python_files,
29 flat_rootname,
30 globs_to_regex,
31)
33from tests.coveragetest import CoverageTest 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
34from tests.helpers import os_sep 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
37class FilesTest(CoverageTest): 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
38 """Tests of coverage.files."""
40 def abs_path(self, p: str) -> str: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
41 """Return the absolute path for `p`."""
42 return os.path.join(abs_file(os.getcwd()), os.path.normpath(p)) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
44 def test_simple(self) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
45 self.make_file("hello.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
46 files.set_relative_directory() 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
47 assert files.relative_filename("hello.py") == "hello.py" 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
48 a = self.abs_path("hello.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
49 assert a != "hello.py" 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
50 assert files.relative_filename(a) == "hello.py" 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
52 def test_peer_directories(self) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
53 self.make_file("sub/proj1/file1.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
54 self.make_file("sub/proj2/file2.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
55 a1 = self.abs_path("sub/proj1/file1.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
56 a2 = self.abs_path("sub/proj2/file2.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
57 d = os.path.normpath("sub/proj1") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
58 os.chdir(d) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
59 files.set_relative_directory() 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
60 assert files.relative_filename(a1) == "file1.py" 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
61 assert files.relative_filename(a2) == a2 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
63 def test_filepath_contains_absolute_prefix_twice(self) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
64 # https://github.com/coveragepy/coveragepy/issues/194
65 # Build a path that has two pieces matching the absolute path prefix.
66 # Technically, this test doesn't do that on Windows, but drive
67 # letters make that impractical to achieve.
68 files.set_relative_directory() 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
69 d = abs_file(os.curdir) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
70 trick = os.path.splitdrive(d)[1].lstrip(os.path.sep) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
71 rel = os.path.join("sub", trick, "file1.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
72 assert files.relative_filename(abs_file(rel)) == rel 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
74 def test_canonical_filename_ensure_cache_hit(self) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
75 self.make_file("sub/proj1/file1.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
76 d = actual_path(self.abs_path("sub/proj1")) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
77 os.chdir(d) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
78 files.set_relative_directory() 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
79 canonical_path = files.canonical_filename("sub/proj1/file1.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
80 assert canonical_path == self.abs_path("file1.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
81 # After the filename has been converted, it should be in the cache.
82 assert "sub/proj1/file1.py" in files.CANONICAL_FILENAME_CACHE 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
83 assert files.canonical_filename("sub/proj1/file1.py") == self.abs_path("file1.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
85 @pytest.mark.parametrize( 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
86 "curdir, sep",
87 [
88 ("/", "/"),
89 ("X:\\", "\\"),
90 ],
91 )
92 def test_relative_dir_for_root(self, curdir: str, sep: str) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
93 with mock.patch.object(files.os, "curdir", new=curdir): # type: ignore[attr-defined] 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
94 with mock.patch.object(files.os, "sep", new=sep): # type: ignore[attr-defined] 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
95 with mock.patch("coverage.files.os.path.normcase", return_value=curdir): 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
96 files.set_relative_directory() 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
97 assert files.relative_directory() == curdir 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
99 @pytest.mark.parametrize( 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
100 "to_make, to_check, answer",
101 [
102 ("a/b/c/foo.py", "a/b/c/foo.py", True),
103 ("a/b/c/foo.py", "a/b/c/bar.py", False),
104 ("src/files.zip", "src/files.zip/foo.py", True),
105 ("src/files.whl", "src/files.whl/foo.py", True),
106 ("src/files.egg", "src/files.egg/foo.py", True),
107 ("src/files.pex", "src/files.pex/foo.py", True),
108 ("src/files.zip", "src/morefiles.zip/foo.py", False),
109 ("src/files.pex", "src/files.pex/zipfiles/files.zip/foo.py", True),
110 ],
111 )
112 def test_source_exists(self, to_make: str, to_check: str, answer: bool) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
113 # source_exists won't look inside the zipfile, so it's fine to make
114 # an empty file with the zipfile name.
115 self.make_file(to_make, "") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
116 assert files.source_exists(to_check) == answer 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
119@pytest.mark.parametrize( 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
120 "original, flat",
121 [
122 ("abc.py", "abc_py"),
123 ("hellothere", "hellothere"),
124 ("a/b/c.py", "z_86bbcbe134d28fd2_c_py"),
125 ("a/b/defghi.py", "z_86bbcbe134d28fd2_defghi_py"),
126 ("/a/b/c.py", "z_bb25e0ada04227c6_c_py"),
127 ("/a/b/defghi.py", "z_bb25e0ada04227c6_defghi_py"),
128 (r"c:\foo\bar.html", "z_e7c107482373f299_bar_html"),
129 (r"d:\foo\bar.html", "z_584a05dcebc67b46_bar_html"),
130 ("Montréal/☺/conf.py", "z_c840497a2c647ce0_conf_py"),
131 ( # original:
132 r"c:\lorem\ipsum\quia\dolor\sit\amet\consectetur\adipisci\velit\sed"
133 + r"\quia\non\numquam\eius\modi\tempora\incidunt\ut\labore\et\dolore"
134 + r"\magnam\aliquam\quaerat\voluptatem\ut\enim\ad\minima\veniam\quis"
135 + r"\nostrum\exercitationem\ullam\corporis\suscipit\laboriosam"
136 + r"\Montréal\☺\my_program.py",
137 # flat:
138 "z_e597dfacb73a23d5_my_program_py",
139 ),
140 ],
141)
142def test_flat_rootname(original: str, flat: str) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
143 assert flat_rootname(original) == flat 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
146def globs_to_regex_params( 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv
147 patterns: Iterable[str],
148 case_insensitive: bool = False,
149 partial: bool = False,
150 matches: Iterable[str] = (),
151 nomatches: Iterable[str] = (),
152) -> Iterable[Any]:
153 """Generate parameters for `test_globs_to_regex`.
155 `patterns`, `case_insensitive`, and `partial` are arguments for
156 `globs_to_regex`. `matches` is a list of strings that should match, and
157 `nomatches` is a list of strings that should not match.
159 Everything is yielded so that `test_globs_to_regex` can call
160 `globs_to_regex` once and check one result.
162 """
163 pat_id = "|".join(patterns) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
164 for text in matches: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
165 yield pytest.param( 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
166 patterns,
167 case_insensitive,
168 partial,
169 text,
170 True,
171 id=f"{pat_id}:ci{case_insensitive}:par{partial}:{text}:match",
172 )
173 for text in nomatches: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
174 yield pytest.param( 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
175 patterns,
176 case_insensitive,
177 partial,
178 text,
179 False,
180 id=f"{pat_id}:ci{case_insensitive}:par{partial}:{text}:nomatch",
181 )
184@pytest.mark.parametrize( 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
185 "patterns, case_insensitive, partial, text, result",
186 list(
187 itertools.chain.from_iterable(
188 [
189 globs_to_regex_params(
190 ["abc", "xyz"],
191 matches=["abc", "xyz", "sub/mod/abc"],
192 nomatches=[
193 "ABC",
194 "xYz",
195 "abcx",
196 "xabc",
197 "axyz",
198 "xyza",
199 "sub/mod/abcd",
200 "sub/abc/more",
201 ],
202 ),
203 globs_to_regex_params(
204 ["abc", "xyz"],
205 case_insensitive=True,
206 matches=["abc", "xyz", "Abc", "XYZ", "AbC"],
207 nomatches=["abcx", "xabc", "axyz", "xyza"],
208 ),
209 globs_to_regex_params(
210 ["a*c", "x*z"],
211 matches=["abc", "xyz", "xYz", "azc", "xaz", "axyzc"],
212 nomatches=["ABC", "abcx", "xabc", "axyz", "xyza", "a/c"],
213 ),
214 globs_to_regex_params(
215 ["a?c", "x?z"],
216 matches=["abc", "xyz", "xYz", "azc", "xaz"],
217 nomatches=["ABC", "abcx", "xabc", "axyz", "xyza", "a/c"],
218 ),
219 globs_to_regex_params(
220 ["a??d"],
221 matches=["abcd", "azcd", "a12d"],
222 nomatches=["ABCD", "abcx", "axyz", "abcde"],
223 ),
224 globs_to_regex_params(
225 ["abc/hi.py"],
226 case_insensitive=True,
227 matches=["abc/hi.py", "ABC/hi.py", r"ABC\hi.py"],
228 nomatches=["abc_hi.py", "abc/hi.pyc"],
229 ),
230 globs_to_regex_params(
231 [r"abc\hi.py"],
232 case_insensitive=True,
233 matches=[r"abc\hi.py", r"ABC\hi.py", "abc/hi.py", "ABC/hi.py"],
234 nomatches=["abc_hi.py", "abc/hi.pyc"],
235 ),
236 globs_to_regex_params(
237 ["abc/*/hi.py"],
238 case_insensitive=True,
239 matches=["abc/foo/hi.py", r"ABC\foo/hi.py"],
240 nomatches=[
241 "abc/hi.py",
242 "abc/hi.pyc",
243 "ABC/foo/bar/hi.py",
244 r"ABC\foo/bar/hi.py",
245 ],
246 ),
247 globs_to_regex_params(
248 ["abc/**/hi.py"],
249 case_insensitive=True,
250 matches=[
251 "abc/foo/hi.py",
252 r"ABC\foo/hi.py",
253 "abc/hi.py",
254 "ABC/foo/bar/hi.py",
255 r"ABC\foo/bar/hi.py",
256 ],
257 nomatches=["abc/hi.pyc"],
258 ),
259 globs_to_regex_params(
260 ["abc/[a-f]*/hi.py"],
261 case_insensitive=True,
262 matches=["abc/foo/hi.py", r"ABC\boo/hi.py"],
263 nomatches=[
264 "abc/zoo/hi.py",
265 "abc/hi.py",
266 "abc/hi.pyc",
267 "abc/foo/bar/hi.py",
268 r"abc\foo/bar/hi.py",
269 ],
270 ),
271 globs_to_regex_params(
272 ["abc/[a-f]/hi.py"],
273 case_insensitive=True,
274 matches=["abc/f/hi.py", r"ABC\b/hi.py"],
275 nomatches=[
276 "abc/foo/hi.py",
277 "abc/zoo/hi.py",
278 "abc/hi.py",
279 "abc/hi.pyc",
280 "abc/foo/bar/hi.py",
281 r"abc\foo/bar/hi.py",
282 ],
283 ),
284 globs_to_regex_params(
285 ["abc/"],
286 case_insensitive=True,
287 partial=True,
288 matches=["abc/foo/hi.py", "ABC/foo/bar/hi.py", r"ABC\foo/bar/hi.py"],
289 nomatches=["abcd/foo.py", "xabc/hi.py"],
290 ),
291 globs_to_regex_params(
292 ["*/foo"],
293 case_insensitive=False,
294 partial=True,
295 matches=["abc/foo/hi.py", "foo/hi.py", "abc/def/foo/hi.py"],
296 nomatches=["abc/xfoo/hi.py"],
297 ),
298 globs_to_regex_params(
299 ["*c/foo"],
300 case_insensitive=False,
301 partial=True,
302 matches=["abc/foo/hi.py"],
303 nomatches=["abc/xfoo/hi.py", "foo/hi.py", "def/abc/foo/hi.py"],
304 ),
305 globs_to_regex_params(
306 ["foo/x*"],
307 case_insensitive=False,
308 partial=True,
309 matches=["foo/x", "foo/xhi.py", "foo/x/hi.py"],
310 nomatches=[],
311 ),
312 globs_to_regex_params(
313 ["foo/x*"],
314 case_insensitive=False,
315 partial=False,
316 matches=["foo/x", "foo/xhi.py"],
317 nomatches=["foo/x/hi.py"],
318 ),
319 globs_to_regex_params(
320 ["**/foo"],
321 matches=["foo", "hello/foo", "hi/there/foo"],
322 nomatches=["foob", "hello/foob", "hello/Foo"],
323 ),
324 globs_to_regex_params(
325 ["a+b/foo*", "x{y}z/foo*"],
326 matches=["a+b/foo", "a+b/foobar", "x{y}z/foobar"],
327 nomatches=["aab/foo", "ab/foo", "xyz/foo"],
328 ),
329 ]
330 )
331 ),
332)
333def test_globs_to_regex( 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
334 patterns: Iterable[str],
335 case_insensitive: bool,
336 partial: bool,
337 text: str,
338 result: bool,
339) -> None:
340 regex = globs_to_regex(patterns, case_insensitive=case_insensitive, partial=partial) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
341 assert bool(regex.match(text)) == result 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
344@pytest.mark.parametrize( 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
345 "pattern, bad_word",
346 [
347 ("***/foo.py", "***"),
348 ("bar/***/foo.py", "***"),
349 ("*****/foo.py", "*****"),
350 ("Hello]there", "]"),
351 ("Hello[there", "["),
352 ("x/a**/b.py", "a**"),
353 ("x/abcd**/b.py", "abcd**"),
354 ("x/**a/b.py", "**a"),
355 ("x/**/**/b.py", "**/**"),
356 ],
357)
358def test_invalid_globs(pattern: str, bad_word: str) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
359 msg = f"File pattern can't include {bad_word!r}" 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
360 with pytest.raises(ConfigError, match=re.escape(msg)): 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
361 globs_to_regex([pattern]) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
364class TMatcher(Protocol): 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
365 """The shape all Matchers have.""" 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
367 def match(self, s: str) -> bool: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
368 """Does this string match?"""
371class MatcherTest(CoverageTest): 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
372 """Tests of file matchers."""
374 def setUp(self) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
375 super().setUp() 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
376 files.set_relative_directory() 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
378 def assertMatches(self, matcher: TMatcher, filepath: str, matches: bool) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
379 """The `matcher` should agree with `matches` about `filepath`."""
380 canonical = files.canonical_filename(filepath) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
381 msg = f"File {filepath} should have matched as {matches}" 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
382 assert matches == matcher.match(canonical), msg 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
384 def test_tree_matcher(self) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
385 case_folding = env.WINDOWS 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
386 matches_to_try = [ 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
387 (self.make_file("sub/file1.py"), True),
388 (self.make_file("sub/file2.c"), True),
389 (self.make_file("sub2/file3.h"), False),
390 (self.make_file("sub3/file4.py"), True),
391 (self.make_file("sub3/file5.c"), False),
392 (self.make_file("sub4/File5.py"), case_folding),
393 (self.make_file("sub5/file6.py"), case_folding),
394 ]
395 trees = [ 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
396 files.canonical_filename("sub"),
397 files.canonical_filename("sub3/file4.py"),
398 files.canonical_filename("sub4/file5.py"),
399 files.canonical_filename("SUB5/file6.py"),
400 ]
401 tm = TreeMatcher(trees) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
402 assert tm.info() == sorted(trees) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
403 for filepath, matches in matches_to_try: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
404 self.assertMatches(tm, filepath, matches) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
406 def test_module_matcher(self) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
407 matches_to_try = [ 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
408 ("test", True),
409 ("trash", False),
410 ("testing", False),
411 ("test.x", True),
412 ("test.x.y.z", True),
413 ("py", False),
414 ("py.t", False),
415 ("py.test", True),
416 ("py.testing", False),
417 ("py.test.buz", True),
418 ("py.test.buz.baz", True),
419 ("__main__", False),
420 ("mymain", True),
421 ("yourmain", False),
422 ]
423 modules = ["test", "py.test", "mymain"] 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
424 mm = ModuleMatcher(modules) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
425 assert mm.info() == modules 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
426 for modulename, matches in matches_to_try: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
427 assert mm.match(modulename) == matches, modulename 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
429 def test_glob_matcher(self) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
430 matches_to_try = [ 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
431 (self.make_file("sub/file1.py"), True),
432 (self.make_file("sub/file2.c"), False),
433 (self.make_file("sub2/file3.h"), True),
434 (self.make_file("sub2/sub/file3.h"), True),
435 (self.make_file("sub3/file4.py"), True),
436 (self.make_file("sub3/file5.c"), False),
437 ]
438 fnm = GlobMatcher(["*.py", "*/sub2/*"]) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
439 assert fnm.info() == ["*.py", "*/sub2/*"] 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
440 for filepath, matches in matches_to_try: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
441 self.assertMatches(fnm, filepath, matches) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
443 def test_glob_matcher_overload(self) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
444 fnm = GlobMatcher(["*x%03d*.txt" % i for i in range(500)]) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
445 self.assertMatches(fnm, "x007foo.txt", True) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
446 self.assertMatches(fnm, "x123foo.txt", True) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
447 self.assertMatches(fnm, "x798bar.txt", False) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
448 self.assertMatches(fnm, "x499.txt", True) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
449 self.assertMatches(fnm, "x500.txt", False) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
451 def test_glob_windows_paths(self) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
452 # We should be able to match Windows paths even if we are running on
453 # a non-Windows OS.
454 fnm = GlobMatcher(["*/foo.py"]) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
455 self.assertMatches(fnm, r"dir\foo.py", True) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
456 fnm = GlobMatcher([r"*\foo.py"]) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
457 self.assertMatches(fnm, r"dir\foo.py", True) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
460@pytest.fixture(params=[False, True], name="rel_yn") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
461def relative_setting(request: pytest.FixtureRequest) -> bool: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
462 """Parameterized fixture to choose whether PathAliases is relative or not."""
463 return request.param # type: ignore[no-any-return] 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
466class PathAliasesTest(CoverageTest): 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
467 """Tests for coverage/files.py:PathAliases"""
469 run_in_temp_dir = False 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
471 def assert_mapped(self, aliases: PathAliases, inp: str, out: str) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
472 """Assert that `inp` mapped through `aliases` produces `out`.
474 If the aliases are not relative, then `out` is canonicalized first,
475 since aliases produce canonicalized paths by default.
477 """
478 mapped = aliases.map(inp, exists=lambda p: True) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
479 if aliases.relative: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
480 expected = out 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
481 else:
482 expected = files.canonical_filename(out) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
483 assert mapped == expected 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
485 def assert_unchanged(self, aliases: PathAliases, inp: str, exists: bool = True) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
486 """Assert that `inp` mapped through `aliases` is unchanged."""
487 assert aliases.map(inp, exists=lambda p: exists) == inp 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
489 def test_noop(self, rel_yn: bool) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
490 aliases = PathAliases(relative=rel_yn) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
491 self.assert_unchanged(aliases, "/ned/home/a.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
493 def test_nomatch(self, rel_yn: bool) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
494 aliases = PathAliases(relative=rel_yn) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
495 aliases.add("/home/*/src", "./mysrc") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
496 self.assert_unchanged(aliases, "/home/foo/a.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
498 def test_wildcard(self, rel_yn: bool) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
499 aliases = PathAliases(relative=rel_yn) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
500 aliases.add("/ned/home/*/src", "./mysrc") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
501 self.assert_mapped(aliases, "/ned/home/foo/src/a.py", "./mysrc/a.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
503 aliases = PathAliases(relative=rel_yn) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
504 aliases.add("/ned/home/*/src/", "./mysrc") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
505 self.assert_mapped(aliases, "/ned/home/foo/src/a.py", "./mysrc/a.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
507 def test_no_accidental_match(self, rel_yn: bool) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
508 aliases = PathAliases(relative=rel_yn) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
509 aliases.add("/home/*/src", "./mysrc") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
510 self.assert_unchanged(aliases, "/home/foo/srcetc") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
512 def test_no_map_if_not_exist(self, rel_yn: bool) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
513 aliases = PathAliases(relative=rel_yn) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
514 aliases.add("/ned/home/*/src", "./mysrc") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
515 self.assert_unchanged(aliases, "/ned/home/foo/src/a.py", exists=False) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
516 self.assert_unchanged(aliases, "foo/src/a.py", exists=False) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
518 def test_no_dotslash(self, rel_yn: bool) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
519 # The result shouldn't start with "./" if the map result didn't.
520 aliases = PathAliases(relative=rel_yn) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
521 aliases.add("*/project", ".") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
522 self.assert_mapped(aliases, "/ned/home/project/src/a.py", os_sep("src/a.py")) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
524 def test_relative_pattern(self) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
525 aliases = PathAliases(relative=True) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
526 aliases.add(".tox/*/site-packages", "src") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
527 self.assert_mapped( 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
528 aliases,
529 ".tox/py314/site-packages/proj/a.py",
530 os_sep("src/proj/a.py"),
531 )
533 def test_multiple_patterns(self, rel_yn: bool) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
534 # also test the debugfn...
535 msgs: list[str] = [] 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
536 aliases = PathAliases(debugfn=msgs.append, relative=rel_yn) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
537 aliases.add("/home/*/src", "./mysrc") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
538 aliases.add("/lib/*/libsrc", "./mylib") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
539 self.assert_mapped(aliases, "/home/foo/src/a.py", "./mysrc/a.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
540 self.assert_mapped(aliases, "/lib/foo/libsrc/a.py", "./mylib/a.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
541 if rel_yn: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
542 assert msgs == [ 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
543 "Aliases (relative=True):",
544 " Rule: '/home/*/src' -> './mysrc/' using regex "
545 + "'[/\\\\\\\\]home[/\\\\\\\\][^/\\\\\\\\]*[/\\\\\\\\]src[/\\\\\\\\]'",
546 " Rule: '/lib/*/libsrc' -> './mylib/' using regex "
547 + "'[/\\\\\\\\]lib[/\\\\\\\\][^/\\\\\\\\]*[/\\\\\\\\]libsrc[/\\\\\\\\]'",
548 "Matched path '/home/foo/src/a.py' to rule '/home/*/src' -> './mysrc/', "
549 + "producing './mysrc/a.py'",
550 "Matched path '/lib/foo/libsrc/a.py' to rule '/lib/*/libsrc' -> './mylib/', "
551 + "producing './mylib/a.py'",
552 ]
553 else:
554 assert msgs == [ 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
555 "Aliases (relative=False):",
556 " Rule: '/home/*/src' -> './mysrc/' using regex "
557 + "'[/\\\\\\\\]home[/\\\\\\\\][^/\\\\\\\\]*[/\\\\\\\\]src[/\\\\\\\\]'",
558 " Rule: '/lib/*/libsrc' -> './mylib/' using regex "
559 + "'[/\\\\\\\\]lib[/\\\\\\\\][^/\\\\\\\\]*[/\\\\\\\\]libsrc[/\\\\\\\\]'",
560 "Matched path '/home/foo/src/a.py' to rule '/home/*/src' -> './mysrc/', "
561 + f"producing {files.canonical_filename('./mysrc/a.py')!r}",
562 "Matched path '/lib/foo/libsrc/a.py' to rule '/lib/*/libsrc' -> './mylib/', "
563 + f"producing {files.canonical_filename('./mylib/a.py')!r}",
564 ]
566 @pytest.mark.parametrize( 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
567 "badpat",
568 [
569 "/ned/home/*",
570 "/ned/home/*/",
571 "/ned/home/*/*/",
572 ],
573 )
574 def test_cant_have_wildcard_at_end(self, badpat: str) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
575 aliases = PathAliases() 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
576 msg = "Pattern must not end with wildcards." 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
577 with pytest.raises(ConfigError, match=msg): 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
578 aliases.add(badpat, "fooey") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
580 def test_no_accidental_munging(self) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
581 aliases = PathAliases() 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
582 aliases.add(r"c:\Zoo\boo", "src/") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
583 aliases.add("/home/ned$", "src/") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
584 self.assert_mapped(aliases, r"c:\Zoo\boo\foo.py", "src/foo.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
585 self.assert_mapped(aliases, r"/home/ned$/foo.py", "src/foo.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
587 def test_paths_are_os_corrected(self, rel_yn: bool) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
588 aliases = PathAliases(relative=rel_yn) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
589 aliases.add("/home/ned/*/src", "./mysrc") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
590 aliases.add(r"c:\ned\src", "./mysrc") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
591 self.assert_mapped(aliases, r"C:\Ned\src\sub\a.py", "./mysrc/sub/a.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
593 aliases = PathAliases(relative=rel_yn) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
594 aliases.add("/home/ned/*/src", r".\mysrc") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
595 aliases.add(r"c:\ned\src", r".\mysrc") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
596 self.assert_mapped( 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
597 aliases,
598 r"/home/ned/foo/src/sub/a.py",
599 r".\mysrc\sub\a.py",
600 )
602 # Try the paths in both orders.
603 lin = "*/project/module/" 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
604 win = "*\\project\\module\\" 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
605 lin_win_paths = [[lin, win], [win, lin]] 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
607 @pytest.mark.parametrize("paths", lin_win_paths) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
608 def test_windows_on_linux(self, paths: Iterable[str], rel_yn: bool) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
609 # https://github.com/coveragepy/coveragepy/issues/618
610 aliases = PathAliases(relative=rel_yn) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
611 for path in paths: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
612 aliases.add(path, "project/module") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
613 self.assert_mapped( 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
614 aliases,
615 "C:\\a\\path\\somewhere\\coveragepy_test\\project\\module\\tests\\file.py",
616 "project/module/tests/file.py",
617 )
619 @pytest.mark.parametrize("paths", lin_win_paths) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
620 def test_linux_on_windows(self, paths: Iterable[str], rel_yn: bool) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
621 # https://github.com/coveragepy/coveragepy/issues/618
622 aliases = PathAliases(relative=rel_yn) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
623 for path in paths: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
624 aliases.add(path, "project\\module") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
625 self.assert_mapped( 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
626 aliases,
627 "C:/a/path/somewhere/coveragepy_test/project/module/tests/file.py",
628 "project\\module\\tests\\file.py",
629 )
631 @pytest.mark.parametrize("paths", lin_win_paths) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
632 def test_relative_windows_on_linux(self, paths: Iterable[str]) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
633 # https://github.com/coveragepy/coveragepy/issues/991
634 aliases = PathAliases(relative=True) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
635 for path in paths: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
636 aliases.add(path, "project/module") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
637 self.assert_mapped( 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
638 aliases,
639 r"project\module\tests\file.py",
640 r"project/module/tests/file.py",
641 )
643 @pytest.mark.parametrize("paths", lin_win_paths) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
644 def test_relative_linux_on_windows(self, paths: Iterable[str]) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
645 # https://github.com/coveragepy/coveragepy/issues/991
646 aliases = PathAliases(relative=True) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
647 for path in paths: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
648 aliases.add(path, r"project\module") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
649 self.assert_mapped( 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
650 aliases,
651 r"project/module/tests/file.py",
652 r"project\module\tests\file.py",
653 )
655 @pytest.mark.skipif(env.WINDOWS, reason="This test assumes Unix file system") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
656 def test_implicit_relative_windows_on_linux(self) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
657 # https://github.com/coveragepy/coveragepy/issues/991
658 aliases = PathAliases(relative=True) 1xyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
659 self.assert_mapped( 1xyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
660 aliases,
661 r"project\module\tests\file.py",
662 r"project/module/tests/file.py",
663 )
665 @pytest.mark.skipif(not env.WINDOWS, reason="This test assumes Windows file system") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
666 def test_implicit_relative_linux_on_windows(self) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
667 # https://github.com/coveragepy/coveragepy/issues/991
668 aliases = PathAliases(relative=True) 1abcdefghijklmnopqrstuvw
669 self.assert_mapped( 1abcdefghijklmnopqrstuvw
670 aliases,
671 r"project/module/tests/file.py",
672 r"project\module\tests\file.py",
673 )
675 def test_multiple_wildcard(self, rel_yn: bool) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
676 aliases = PathAliases(relative=rel_yn) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
677 aliases.add("/home/jenkins/*/a/*/b/*/django", "./django") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
678 self.assert_mapped( 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
679 aliases,
680 "/home/jenkins/xx/a/yy/b/zz/django/foo/bar.py",
681 "./django/foo/bar.py",
682 )
684 def test_windows_root_paths(self, rel_yn: bool) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
685 aliases = PathAliases(relative=rel_yn) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
686 aliases.add("X:\\", "/tmp/src") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
687 self.assert_mapped( 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
688 aliases,
689 "X:\\a\\file.py",
690 "/tmp/src/a/file.py",
691 )
692 self.assert_mapped( 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
693 aliases,
694 "X:\\file.py",
695 "/tmp/src/file.py",
696 )
698 def test_leading_wildcard(self, rel_yn: bool) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
699 aliases = PathAliases(relative=rel_yn) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
700 aliases.add("*/d1", "./mysrc1") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
701 aliases.add("*/d2", "./mysrc2") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
702 self.assert_mapped(aliases, "/foo/bar/d1/x.py", "./mysrc1/x.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
703 self.assert_mapped(aliases, "/foo/bar/d2/y.py", "./mysrc2/y.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
705 @pytest.mark.parametrize("dirname", [".", "..", "../other", "/"]) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
706 def test_dot(self, dirname: str) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
707 if env.WINDOWS and dirname == "/": 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
708 # The root test case was added for the manylinux Docker images,
709 # and I'm not sure how it should work on Windows, so skip it.
710 pytest.skip("Don't know how to handle root on Windows") 1abcdefghijklmnopqrstuvw
711 aliases = PathAliases() 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
712 aliases.add(dirname, "/the/source") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
713 the_file = os.path.join(dirname, "a.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
714 the_file = os.path.expanduser(the_file) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
715 the_file = os.path.abspath(os.path.realpath(the_file)) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
717 assert "~" not in the_file # to be sure the test is pure. 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
718 self.assert_mapped(aliases, the_file, "/the/source/a.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
721class PathAliasesRealFilesTest(CoverageTest): 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
722 """Tests for coverage/files.py:PathAliases using real files."""
724 def test_aliasing_zip_files(self) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
725 self.make_file("src/zipfiles/code.zip", "fake zip, doesn't matter") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
726 aliases = PathAliases() 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
727 aliases.add("*/d1", "./src") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
728 aliases.add("*/d2", "./src") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
730 expected = files.canonical_filename("src/zipfiles/code.zip/p1.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
731 assert aliases.map("tox/d1/zipfiles/code.zip/p1.py") == expected 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
734class FindPythonFilesTest(CoverageTest): 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
735 """Tests of `find_python_files`."""
737 def test_find_python_files(self) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
738 self.make_file("sub/a.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
739 self.make_file("sub/b.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
740 self.make_file("sub/x.c") # nope: not .py 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
741 self.make_file("sub/ssub/__init__.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
742 self.make_file("sub/ssub/s.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
743 self.make_file("sub/ssub/~s.py") # nope: editor effluvia 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
744 self.make_file("sub/lab/exp.py") # nope: no __init__.py 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
745 self.make_file("sub/windows.pyw") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
746 py_files = set(find_python_files("sub", include_namespace_packages=False)) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
747 self.assert_same_files( 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
748 py_files,
749 [
750 "sub/a.py",
751 "sub/b.py",
752 "sub/ssub/__init__.py",
753 "sub/ssub/s.py",
754 "sub/windows.pyw",
755 ],
756 )
758 def test_find_python_files_include_namespace_packages(self) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
759 self.make_file("sub/a.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
760 self.make_file("sub/b.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
761 self.make_file("sub/x.c") # nope: not .py 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
762 self.make_file("sub/ssub/__init__.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
763 self.make_file("sub/ssub/s.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
764 self.make_file("sub/ssub/~s.py") # nope: editor effluvia 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
765 self.make_file("sub/lab/exp.py") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
766 self.make_file("sub/windows.pyw") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
767 py_files = set(find_python_files("sub", include_namespace_packages=True)) 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
768 self.assert_same_files( 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
769 py_files,
770 [
771 "sub/a.py",
772 "sub/b.py",
773 "sub/ssub/__init__.py",
774 "sub/ssub/s.py",
775 "sub/lab/exp.py",
776 "sub/windows.pyw",
777 ],
778 )
781@pytest.mark.skipif(not env.WINDOWS, reason="Only need to run Windows tests on Windows.") 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
782class WindowsFileTest(CoverageTest): 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
783 """Windows-specific tests of file name handling."""
785 run_in_temp_dir = False 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
787 def test_actual_path(self) -> None: 1xyzAabBCDEcdFGHIefJKLMghNOPQijRSTUVWklmXYZ012nop345678qrs9!#$%'tuv()w
788 assert actual_path(r"c:\Windows") == actual_path(r"C:\wINDOWS") 1abcdefghijklmnopqrstuvw