Files
neotest-python/neotest_python/unittest.py
Rónán Carrigan b162ba1b42 feat: init commit
2022-01-02 23:15:23 +00:00

106 lines
4.0 KiB
Python

import inspect
import traceback
import unittest
from pathlib import Path
from types import TracebackType
from typing import Any, Dict, Iterator, List, Tuple
from unittest import TestCase, TestResult, TestSuite
from unittest.runner import TextTestResult, TextTestRunner
from .base import NeotestAdapter, NeotestResultStatus
class UnittestNeotestAdapter(NeotestAdapter):
def iter_suite(
self, suite: "TestSuite | TestCase"
) -> Iterator["TestCase | TestSuite"]:
if isinstance(suite, TestSuite):
for sub in suite:
for case in self.iter_suite(sub):
yield case
else:
yield suite
def case_file(self, case) -> str:
return str(Path(inspect.getmodule(case).__file__).absolute()) # type: ignore
def case_id_elems(self, case) -> List[str]:
file = self.case_file(case)
elems = [file, case.__class__.__name__]
if isinstance(case, TestCase):
elems.append(case._testMethodName)
return elems
def case_id(self, case: "TestCase | TestSuite") -> str:
return "::".join(self.case_id_elems(case))
def run(self, args: List[str]) -> Dict:
results = {}
errs: Dict[str, Tuple[Exception, Any, TracebackType]] = {}
class NeotestTextTestResult(TextTestResult):
def addFailure(_, test: TestCase, err) -> None:
errs[self.case_id(test)] = err
return super().addFailure(test, err)
class NeotestUnittestRunner(TextTestRunner):
def run(_, test: "TestSuite | TestCase") -> "TestResult": # type: ignore
for case in self.iter_suite(test):
results[self.case_id(case)] = {
"status": NeotestResultStatus.PASSED,
"short": None,
}
results[self.case_file(case)] = {
"status": NeotestResultStatus.PASSED,
"short": None,
}
result = super().run(test)
for case, message in result.failures:
case_id = self.case_id(case)
error_line = None
case_file = self.case_file(case)
if case_id in errs:
trace = errs[case_id][2]
summary = traceback.extract_tb(trace)
error_line = next(
frame.lineno - 1
for frame in reversed(summary)
if frame.filename == case_file
)
results[case_id] = self.update_result(
results.get(case_id),
{
"status": NeotestResultStatus.FAILED,
"errors": [{"message": message, "line": error_line}],
"short": None,
},
)
results[case_file] = self.update_result(
results.get(case_file),
{
"status": NeotestResultStatus.FAILED,
"errors": [{"message": message, "line": error_line}],
"short": None,
},
)
for case, message in result.skipped:
results[self.case_id(case)] = self.update_result(
results[self.case_id(case)],
{
"short": None,
"status": NeotestResultStatus.SKIPPED,
"errors": None,
},
)
return result
unittest.main(
module=None,
argv=args,
testRunner=NeotestUnittestRunner(resultclass=NeotestTextTestResult),
exit=False,
)
return results