1
+ import importlib .util
1
2
import inspect
3
+ import os
2
4
import traceback
5
+ from pathlib import Path
3
6
from types import GeneratorType
4
7
from typing import Callable , Iterable , Iterator , Optional , Tuple
5
8
6
- import py
7
9
import pytest
8
- from _pytest .config import Config
9
- from _pytest .main import Session
10
10
11
11
from basilisp import main as basilisp
12
12
from basilisp .lang import keyword as kw
@@ -26,11 +26,11 @@ def pytest_configure(config):
26
26
basilisp .bootstrap ("basilisp.test" )
27
27
28
28
29
- def pytest_collect_file (parent , path ):
29
+ def pytest_collect_file (file_path : Path , path , parent ):
30
30
"""Primary PyTest hook to identify Basilisp test files."""
31
- if path . ext == ".lpy" :
32
- if path . basename .startswith ("test_" ) or path . purebasename .endswith ("_test" ):
33
- return BasilispFile .from_parent (parent , fspath = path )
31
+ if file_path . suffix == ".lpy" :
32
+ if file_path . name .startswith ("test_" ) or file_path . name .endswith ("_test" ):
33
+ return BasilispFile .from_parent (parent , fspath = path , path = file_path )
34
34
return None
35
35
36
36
@@ -135,18 +135,43 @@ def teardown(self) -> None:
135
135
self ._teardowns = ()
136
136
137
137
138
+ def _is_package (path : Path ) -> bool :
139
+ """Return `True` if the given path refers to a Python or Basilisp package."""
140
+ _ , _ , files = next (os .walk (path ))
141
+ for file in files :
142
+ if file in {"__init__.lpy" , "__init__.py" } or file .endswith (".lpy" ):
143
+ return True
144
+ return False
145
+
146
+
147
+ def _get_fully_qualified_module_name (file : Path ) -> str :
148
+ """Return the fully qualified module name (from the import root) for a module given
149
+ its location.
150
+
151
+ This works by traversing up the filesystem looking for the top-most package. From
152
+ there, we derive a Python module name referring to the given module path."""
153
+ top = None
154
+ for p in file .parents :
155
+ if _is_package (p ):
156
+ top = p
157
+ else :
158
+ break
159
+
160
+ if top is None or top == file .parent :
161
+ return file .stem
162
+
163
+ root = top .parent
164
+ elems = list (file .with_suffix ("" ).relative_to (root ).parts )
165
+ if elems [- 1 ] == "__init__" :
166
+ elems .pop ()
167
+ return "." .join (elems )
168
+
169
+
138
170
class BasilispFile (pytest .File ):
139
171
"""Files represent a test module in Python or a test namespace in Basilisp."""
140
172
141
- def __init__ ( # pylint: disable=too-many-arguments
142
- self ,
143
- fspath : py .path .local ,
144
- parent = None ,
145
- config : Optional [Config ] = None ,
146
- session : Optional ["Session" ] = None ,
147
- nodeid : Optional [str ] = None ,
148
- ) -> None :
149
- super ().__init__ (fspath , parent , config , session , nodeid )
173
+ def __init__ (self , ** kwargs ) -> None :
174
+ super ().__init__ (** kwargs )
150
175
self ._fixture_manager : Optional [FixtureManager ] = None
151
176
152
177
@staticmethod
@@ -192,16 +217,21 @@ def teardown(self) -> None:
192
217
assert self ._fixture_manager is not None
193
218
self ._fixture_manager .teardown ()
194
219
220
+ def _import_module (self ) -> runtime .BasilispModule :
221
+ modname = _get_fully_qualified_module_name (self .path )
222
+ module = importlib .import_module (modname )
223
+ assert isinstance (module , runtime .BasilispModule )
224
+ return module
225
+
195
226
def collect (self ):
196
- """Collect all of the tests in the namespace (module) given.
227
+ """Collect all tests from the namespace (module) given.
197
228
198
229
Basilisp's test runner imports the namespace which will (as a side effect)
199
- collect all of the test functions in a namespace (represented by `deftest`
200
- forms in Basilisp). BasilispFile.collect fetches those test functions and
201
- generates BasilispTestItems for PyTest to run the tests."""
202
- filename = self .fspath .basename
203
- module = self .fspath .pyimport ()
204
- assert isinstance (module , runtime .BasilispModule )
230
+ collect the test functions in a namespace (represented by `deftest` forms in
231
+ Basilisp). BasilispFile.collect fetches those test functions and generates
232
+ BasilispTestItems for PyTest to run the tests."""
233
+ filename = self .path .name
234
+ module = self ._import_module ()
205
235
ns = module .__basilisp_namespace__
206
236
once_fixtures , each_fixtures = self ._collected_fixtures (ns )
207
237
self ._fixture_manager = FixtureManager (once_fixtures )
0 commit comments