Skip to content

Commit

Permalink
Merge pull request #211 from jitseniesen/single-test
Browse files Browse the repository at this point in the history
PR: Add option to run a single test
  • Loading branch information
jitseniesen committed Jun 23, 2023
2 parents c30c21d + 0496dd8 commit dd684fe
Show file tree
Hide file tree
Showing 19 changed files with 504 additions and 281 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,11 @@ jobs:
shell: bash
command: |
. ~/.profile
pytest spyder_unittest -x -vv
pytest spyder_unittest -vv
- name: Run tests (Windows)
if: matrix.OS == 'windows'
uses: nick-fields/retry@v2
with:
timeout_minutes: 10
max_attempts: 3
command: pytest spyder_unittest -x -vv
command: pytest spyder_unittest -vv
41 changes: 29 additions & 12 deletions spyder_unittest/backend/abbreviator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@
# (see LICENSE.txt for details)
"""Class for abbreviating test names."""

from __future__ import annotations

# Standard imports
from dataclasses import dataclass

@dataclass
class Abbreviation:
"""
Abbreviation for one component of a test name.
Abbreviations are defined recursively, so `.head` is the abbreviation
for the first component and `.tail` specifies the abbreviations for the
second and later components.
"""
head: str
tail: Abbreviator


class Abbreviator:
"""
Expand All @@ -26,7 +43,7 @@ class Abbreviator:
the higher-level components as its second element.
"""

def __init__(self, names=[]):
def __init__(self, names: list[str]=[]) -> None:
"""
Constructor.
Expand All @@ -35,11 +52,11 @@ def __init__(self, names=[]):
names : list of str
list of words which needs to be abbreviated.
"""
self.dic = {}
self.dic: dict[str, Abbreviation] = {}
for name in names:
self.add(name)

def add(self, name):
def add(self, name: str) -> None:
"""
Add name to list of names to be abbreviated.
Expand All @@ -61,18 +78,18 @@ def add(self, name):
and len_abbrev < len(other)):
len_abbrev += 1
if len_abbrev == len(start):
self.dic[other][0] = other[:len_abbrev + 1]
self.dic[other].head = other[:len_abbrev + 1]
elif len_abbrev == len(other):
self.dic[other][0] = other
self.dic[other].head = other
len_abbrev += 1
else:
if len(self.dic[other][0]) < len_abbrev:
self.dic[other][0] = other[:len_abbrev]
if len(self.dic[other].head) < len_abbrev:
self.dic[other].head = other[:len_abbrev]
else:
self.dic[start] = [start[:len_abbrev], Abbreviator()]
self.dic[start][1].add(rest)
self.dic[start] = Abbreviation(start[:len_abbrev], Abbreviator())
self.dic[start].tail.add(rest)

def abbreviate(self, name):
def abbreviate(self, name: str) -> str:
"""Return abbreviation of name."""
if '[' in name:
name, parameters = name.split('[', 1)
Expand All @@ -81,8 +98,8 @@ def abbreviate(self, name):
parameters = ''
if '.' in name:
start, rest = name.split('.', 1)
res = (self.dic[start][0]
+ '.' + self.dic[start][1].abbreviate(rest))
res = (self.dic[start].head
+ '.' + self.dic[start].tail.abbreviate(rest))
else:
res = name
return res + parameters
27 changes: 19 additions & 8 deletions spyder_unittest/backend/frameworkregistry.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@
# (see LICENSE.txt for details)
"""Keep track of testing frameworks and create test runners when requested."""

from __future__ import annotations

# Standard imports
from typing import Optional, TYPE_CHECKING

# Local imports
if TYPE_CHECKING:
from spyder_unittest.backend.runnerbase import RunnerBase
from spyder_unittest.widgets.unittestgui import UnitTestWidget


class FrameworkRegistry():
"""
Expand All @@ -24,33 +34,34 @@ class FrameworkRegistry():
associated runners.
"""

def __init__(self):
def __init__(self) -> None:
"""Initialize self."""
self.frameworks = {}
self.frameworks: dict[str, type[RunnerBase]] = {}

def register(self, runner_class):
def register(self, runner_class: type[RunnerBase]) -> None:
"""Register runner class for a testing framework.
Parameters
----------
runner_class : type
runner_class
Class used for creating tests runners for the framework.
"""
self.frameworks[runner_class.name] = runner_class

def create_runner(self, framework, widget, tempfilename):
def create_runner(self, framework: str, widget: UnitTestWidget,
tempfilename: Optional[str]) -> RunnerBase:
"""Create test runner associated to some testing framework.
This creates an instance of the runner class whose `name` attribute
equals `framework`.
Parameters
----------
framework : str
framework
Name of testing framework.
widget : UnitTestWidget
widget
Unit test widget which constructs the test runner.
resultfilename : str or None
resultfilename
Name of file in which to store test results. If None, use default.
Returns
Expand Down
19 changes: 15 additions & 4 deletions spyder_unittest/backend/nose2runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@
# (see LICENSE.txt for details)
"""Support for Nose framework."""

from __future__ import annotations

# Standard library imports
from typing import Optional, TYPE_CHECKING

# Third party imports
from lxml import etree
from spyder.config.base import get_translation

# Local imports
from spyder_unittest.backend.runnerbase import Category, RunnerBase, TestResult
if TYPE_CHECKING:
from spyder_unittest.widgets.configdialog import Config

try:
_ = get_translation('spyder_unittest')
Expand All @@ -25,22 +32,26 @@ class Nose2Runner(RunnerBase):
module = 'nose2'
name = 'nose2'

def create_argument_list(self, config, cov_path):
def create_argument_list(self, config: Config,
cov_path: Optional[str],
single_test: Optional[str]) -> list[str]:
"""Create argument list for testing process."""
arguments = [
'-m', self.module, '--plugin=nose2.plugins.junitxml',
'--junit-xml', '--junit-xml-path={}'.format(self.resultfilename)
]
if single_test:
arguments.append(single_test)
arguments += config.args
return arguments

def finished(self):
def finished(self, exitcode: int) -> None:
"""Called when the unit test process has finished."""
output = self.read_all_process_output()
testresults = self.load_data()
self.sig_finished.emit(testresults, output, True)

def load_data(self):
def load_data(self) -> list[TestResult]:
"""
Read and parse unit test results.
Expand All @@ -56,7 +67,7 @@ def load_data(self):
try:
data = etree.parse(self.resultfilename).getroot()
except OSError:
data = []
return []

testresults = []
for testcase in data:
Expand Down
Loading

0 comments on commit dd684fe

Please sign in to comment.