Skip to content

Commit

Permalink
adjust semantics
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrielfalcao committed Sep 25, 2023
1 parent fa31f75 commit a080d5c
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 10 deletions.
6 changes: 3 additions & 3 deletions sure/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
Scenario,
BaseResult,
ErrorStack,
SpecContext,
RuntimeContext,
FeatureResult,
RuntimeOptions,
ScenarioResult,
Expand Down Expand Up @@ -108,7 +108,7 @@ def runin(self, lookup_paths, immediate: bool = False):
for feature in self.load_features(lookup_paths):
self.reporter.on_feature(feature)
runtime = RuntimeOptions(immediate=immediate)
context = SpecContext(self.reporter, runtime)
context = RuntimeContext(self.reporter, runtime)

result = feature.run(self.reporter, runtime=runtime)
if runtime.immediate:
Expand Down Expand Up @@ -142,4 +142,4 @@ def runtime(self, immediate: bool = False):

@cached_property
def context(self):
return SpecContext(self.reporter, self.runtime)
return RuntimeContext(self.reporter, self.runtime)
112 changes: 105 additions & 7 deletions sure/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import logging
import unittest
import traceback
from typing import Dict, List, Optional
from typing import Dict, List, Optional, Any, Callable

from mock import Mock

Expand Down Expand Up @@ -71,7 +71,7 @@ def __repr__(self):
return f"<RuntimeOptions immediate={self.immediate}>"


class SpecContext(object):
class RuntimeContext(object):
reporter: Reporter
runtime: RuntimeOptions

Expand All @@ -80,7 +80,7 @@ def __init__(self, reporter: Reporter, runtime: RuntimeOptions):
self.runtime = runtime

def __repr__(self):
return f"<SpecContext reporter={self.reporter} runtime={self.runtime}>"
return f"<RuntimeContext reporter={self.reporter} runtime={self.runtime}>"


class BaseResult(object):
Expand All @@ -98,6 +98,104 @@ def ok(self):
return len(er.union(fe)) == 0


class PreparedTestSuiteContainer(object):
"""Thought with the goal of providing a hermetically isolated
environment where the runtime context and associated reporters are
kept in sync with potentially nested occurrences of scenarios
Contains a setup/teardown and a list of runnable tests associated
with a :py:class:`unittest.TestCase` along with a reference to the
original instance and a runtime context.
"""
source: Any
context: RuntimeContext
setup_methods: List[Callable]
teardown_methods: List[Callable]
test_methods: List[Callable]

def __init__(self,
source: Any,
context: RuntimeContext,
setup_methods: List[Callable],
teardown_methods: List[Callable],
test_methods: List[Callable]):
self.log = Logort(self)
self.source_instance = source
self.context = context
self.setup_methods = setup_methods
self.teardown_methods = teardown_methods
self.test_methods = test_methods

def run_predicates(self, context):
pass

def run_complements(self, context):
pass

def run_class_based_test(self, context):
last_failure = None
last_error = None
for name in dir(self.object):
if last_failure and context.runtime.immediate:
# XXX: raise last_failure
self.log.internal.warning(f"fail: {result}")
raise ImmediateFailure(last_failure)

if last_error and context.runtime.immediate:
# XXX: raise last_error
self.log.internal.error(f"error: {result}")
raise ImmediateError(last_error)

if not seem_to_indicate_test(name):
self.log.internal.debug(f"ignoring {self.object}.{name}")
continue

if isinstance(self.object, type) and issubclass(self.object, unittest.TestCase):
runnable = getattr(self.object(name), name, None)
else:
# XXX: support non-unittest.TestCase classes
runnable = getattr(self.object, name, None)

if isinstance(runnable, types.MethodType) and seem_to_indicate_test(
name
):
result = self.run_single_test(runnable, context)
if result.failure:
last_failure = result
if result.error:
last_error = result

yield result

def run_single_test(self, test, context):
if not hasattr(test, "__code__"):
raise RuntimeError(
f"expected {test} to be a function in this instance"
)
code = test.__code__
varnames = set(code.co_varnames).intersection({"context"})
argcount = len(varnames)
location = TestLocation(test, isinstance(self.object, type) and self.object or None)
self.log.set_location(location)
try:
if argcount == 0:
test()
elif argcount == 1:
test(context)
else:
raise NonValidTest(
f"it appears that the test function {self.object} takes more than one argument: {argcount}"
)

except AssertionError as failure:
return ScenarioResult(self, location, context, failure)

except Exception as error:
return ScenarioResult(self, location, context, error)

return ScenarioResult(self, location, context)


class Feature(object):
def __init__(self, module):
name = getattr(
Expand Down Expand Up @@ -126,7 +224,7 @@ def read_scenarios(self, suts):
def run(self, reporter, runtime: RuntimeOptions):
results = []
for scenario in self.scenarios:
context = SpecContext(reporter, runtime)
context = RuntimeContext(reporter, runtime)

reporter.on_scenario(scenario)
self.run_predicates(context)
Expand Down Expand Up @@ -214,7 +312,7 @@ def __init__(self, class_or_callable, feature):
self.feature = feature
self.fail_immediately = False

def run_class_based_test(self, context):
def run_class_based_test(self, context) -> PreparedTestSuiteContainer:
last_failure = None
last_error = None
for name in dir(self.object):
Expand Down Expand Up @@ -292,7 +390,7 @@ class ScenarioResult(BaseResult):
failure: Optional[AssertionError]
location: TestLocation

def __init__(self, scenario, location: TestLocation, context: SpecContext, error=None):
def __init__(self, scenario, location: TestLocation, context: RuntimeContext, error=None):
self.scenario = scenario
self.location = location
self.context = context
Expand Down Expand Up @@ -374,7 +472,7 @@ class ScenarioResultSet(ScenarioResult):
failure: Optional[ScenarioResult]

def __init__(
self, scenario_results: List[ScenarioResult], context: SpecContext
self, scenario_results: List[ScenarioResult], context: RuntimeContext
):
self.scenario_results = scenario_results
self.failed_scenarios = []
Expand Down

0 comments on commit a080d5c

Please sign in to comment.