Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion smart_tests/test_runners/pytest.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,12 @@ def _add_testpaths(lines: Iterable[str]):


def _parse_pytest_nodeid(nodeid: str) -> TestPath:
data = nodeid.split("::")
data = nodeid.split("::", 1)
if len(data) > 1:
# Parameter IDs may themselves contain the node separator.
test_name, bracket, parameter_id = data.pop().partition("[")
data.extend(test_name.split("::"))
data[-1] += bracket + parameter_id
file = data[0]
class_name = _path_to_class_name(file)
normalized_file = os.path.normpath(file)
Expand Down
47 changes: 47 additions & 0 deletions tests/test_runners/test_pytest.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,53 @@ def test_parse_pytest_nodeid(self):
{"type": "testcase", "name": "test_func6"},
])

@responses.activate
@mock.patch.dict(os.environ, {"SMART_TESTS_TOKEN": CliTestCase.smart_tests_token})
def test_subset_preserves_parameter_id_containing_separator(self):
nodeid = 'tests/test_mod.py::TestClass::test_value[left::right]'
result = self.cli('subset', 'pytest', '--target', '10%', '--session', self.session,
input=nodeid + '\n\n')
self.assert_success(result)
payload = json.loads(gzip.decompress(self.find_request('/subset').request.body).decode())
self.assertEqual(payload['testPaths'], [[
{'type': 'file', 'name': os.path.normpath('tests/test_mod.py')},
{'type': 'class', 'name': 'tests.test_mod.TestClass'},
{'type': 'testcase', 'name': 'test_value[left::right]'},
]])


class PytestParameterIdTest(TestCase):
def test_function_parameter_id_preserves_separator(self):
self.assertEqual(_parse_pytest_nodeid('tests/test_mod.py::test_value[left::right]'), [
{'type': 'file', 'name': os.path.normpath('tests/test_mod.py')},
{'type': 'class', 'name': 'tests.test_mod'},
{'type': 'testcase', 'name': 'test_value[left::right]'},
])

def test_class_parameter_id_preserves_separators_and_brackets(self):
self.assertEqual(_parse_pytest_nodeid('tests/test_mod.py::TestClass::test_value[a[::b]::c]'), [
{'type': 'file', 'name': os.path.normpath('tests/test_mod.py')},
{'type': 'class', 'name': 'tests.test_mod.TestClass'},
{'type': 'testcase', 'name': 'test_value[a[::b]::c]'},
])

def test_brackets_in_file_path_do_not_start_parameter_id(self):
self.assertEqual(_parse_pytest_nodeid('tests/[group]/test_mod.py::TestClass::test_value'), [
{'type': 'file', 'name': os.path.normpath('tests/[group]/test_mod.py')},
{'type': 'class', 'name': 'tests.[group].test_mod.TestClass'},
{'type': 'testcase', 'name': 'test_value'},
])

def test_json_report_preserves_parameter_id(self):
parser = PytestJSONReportParser(mock.Mock())
with tempfile.TemporaryDirectory() as directory:
path = os.path.join(directory, 'report.json')
with open(path, 'w') as stream:
stream.write(json.dumps({'nodeid': 'tests/test_mod.py::test_value[left::right]',
'when': 'call', 'outcome': 'passed'}) + '\n')
events = list(parser.parse_func(path))
self.assertEqual(events[0]['testPath'][-1], {'type': 'testcase', 'name': 'test_value[left::right]'})


class PytestJSONReportParserLongreprTest(TestCase):
class DummyClient:
Expand Down