feat: init commit
This commit is contained in:
105
neotest_python/unittest.py
Normal file
105
neotest_python/unittest.py
Normal file
@@ -0,0 +1,105 @@
|
||||
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
|
Reference in New Issue
Block a user