From 8c897bbbeb4c0de711337b05cd8316439d4f22d1 Mon Sep 17 00:00:00 2001 From: Moshe Atlow Date: Fri, 2 Sep 2022 01:39:18 +0300 Subject: [PATCH] support nested TAP --- tap2junit/tap13.py | 41 +++++++++++++++++++--------- test/fixtures/test-nested.tap | 50 +++++++++++++++++++++++++++++++++++ test/output/test-nested.xml | 31 ++++++++++++++++++++++ 3 files changed, 109 insertions(+), 13 deletions(-) create mode 100644 test/fixtures/test-nested.tap create mode 100644 test/output/test-nested.xml diff --git a/tap2junit/tap13.py b/tap2junit/tap13.py index f6a8a0e..e799ece 100644 --- a/tap2junit/tap13.py +++ b/tap2junit/tap13.py @@ -57,7 +57,8 @@ def __init__(self, result, id, description=None, directive=None, comment=None): class TAP13: def __init__(self): self.tests = [] - self.__tests_counter = 0 + self.__tests_counter = [0] + self._test_indentation = [0] self.tests_planned = None def _parse_yaml(self, line, in_yaml, in_yaml_block): @@ -77,6 +78,14 @@ def _parse_yaml(self, line, in_yaml, in_yaml_block): return in_yaml, in_yaml_block + def _handle_indentation(self, current_indentation): + if current_indentation > self._test_indentation[-1]: + self._test_indentation.append(current_indentation) + self.__tests_counter.append(0) + if current_indentation < self._test_indentation[-1]: + self._test_indentation.pop() + self.__tests_counter.pop() + def _parse(self, source): seek_version = True seek_plan = False @@ -104,7 +113,8 @@ def _parse(self, source): seek_version = False seek_plan = True seek_test = True - self.__tests_counter = 0 + self.__tests_counter = [0] + self._test_indentation = [0] for line in source: if ( @@ -120,14 +130,16 @@ def _parse(self, source): in_test = False in_yaml = False in_yaml_block = False - self.__tests_counter = 0 + self.__tests_counter = [0] + self._test_indentation = [0] # raise ValueError("Bad TAP format, multiple TAP headers") if in_yaml: in_yaml, in_yaml_block = self._parse_yaml(line, in_yaml, in_yaml_block) continue - line = line.strip() + unstriped_line = line + line = unstriped_line.strip() if in_test: if RE_EXPLANATION.match(line): @@ -163,38 +175,41 @@ def _parse(self, source): # Stop processing if tests were found before the plan # if plan is at the end, it must be last line -> stop processing - if self.__tests_counter > 0: + if len(self.__tests_counter) == 1 and self.__tests_counter[0] > 0: break if seek_test: match = RE_TEST_LINE.match(line) if match: - self.__tests_counter += 1 + self._handle_indentation(len(unstriped_line) - len(line)) + self.__tests_counter[-1] += 1 t_attrs = match.groupdict() if t_attrs["id"] is None: - t_attrs["id"] = self.__tests_counter + t_attrs["id"] = self.__tests_counter[-1] t_attrs["id"] = int(t_attrs["id"]) - if t_attrs["id"] < self.__tests_counter: + if t_attrs["id"] < self.__tests_counter[-1]: raise ValueError("Descending test id on line: %r" % line) # according to TAP13 specs, missing tests must be handled as # 'not ok' so here we add the missing tests in sequence - while t_attrs["id"] > self.__tests_counter: + while t_attrs["id"] > self.__tests_counter[-1]: self.tests.append( Test( "not ok", - self.__tests_counter, + self.__tests_counter[-1], comment="DIAG: Test %s not present" - % self.__tests_counter, + % self.__tests_counter[-1], ) ) - self.__tests_counter += 1 + self.__tests_counter[-1] += 1 t = Test(**t_attrs) if t.result == "Bail out!": t.result = "not ok" # according to TAP13 specs, everything after this is an # explanation of why testing must be stopped t.diagnostics = t.diagnostics or t.description - t.description = "Bail out for Test %s" % self.__tests_counter + t.description = ( + "Bail out for Test %s" % self.__tests_counter[-1] + ) self.tests.append(t) in_test = True continue diff --git a/test/fixtures/test-nested.tap b/test/fixtures/test-nested.tap new file mode 100644 index 0000000..d9af80f --- /dev/null +++ b/test/fixtures/test-nested.tap @@ -0,0 +1,50 @@ +TAP version 13 +# Subtest: parent + # Subtest: top level 1 + ok 1 - top level 1 + --- + duration_ms: 0.340575089 + ... + # Subtest: top level 2 + ok 2 - top level 2 + --- + duration_ms: 0.30303944 + ... + # Subtest: nested + # Subtest: nested 1 + ok 1 - nested 1 + --- + duration_ms: 2.367745206 + ... + # Subtest: nested 2 + not ok 2 - nested 2 + --- + duration_ms: 2.332323873 + failureType: 'cancelledByParent' + error: 'Promise resolution is still pending but the event loop has already resolved' + code: 'ERR_TEST_FAILURE' + stack: |- + process.emit (node:events:513:28) + ... + 1..2 + not ok 3 - nested + --- + duration_ms: 2.367870901 + failureType: 'fail' + error: 'Promise resolution is still pending but the event loop has already resolved' + code: 'ERR_TEST_FAILURE' + stack: |- + process.emit (node:events:513:28) + ... + # Subtest: top level 4 + ok 4 - top level 4 + --- + duration_ms: 0.340575089 + ... + 1..4 +ok 1 - parent + --- + duration_ms: 2.489501484 + ... +1..1 +# tests 1 \ No newline at end of file diff --git a/test/output/test-nested.xml b/test/output/test-nested.xml new file mode 100644 index 0000000..ffac3a0 --- /dev/null +++ b/test/output/test-nested.xml @@ -0,0 +1,31 @@ + + + + + + + + +--- +code: ERR_TEST_FAILURE +duration_ms: 2.332323873 +error: Promise resolution is still pending but the event loop has already resolved +failureType: cancelledByParent +... + + + + +--- +code: ERR_TEST_FAILURE +duration_ms: 2.367870901 +error: Promise resolution is still pending but the event loop has already resolved +failureType: fail +... + + ['# Subtest: top level 4'] + + + + +