Skip to content

Commit

Permalink
crafts invaluable UI feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrielfalcao committed Sep 22, 2023
1 parent 3540c64 commit 23fad41
Show file tree
Hide file tree
Showing 12 changed files with 504 additions and 123 deletions.
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ OPEN_COMMAND := gnome-open
else
OPEN_COMMAND := open
endif
export SURE_NO_COLORS := true
export SURE_NO_COLORS := true
export SURE_LOG_FILE := $(GIT_ROOT)/sure-$(date +"%Y-%m-%d-%H:%M:%S").log

AUTO_STYLE_TARGETS := sure/runtime.py sure/runner.py sure/meta.py sure/meta.py sure/reporter.py sure/reporters sure/actors.py sure/agents.py
######################################################################
Expand Down Expand Up @@ -54,6 +55,8 @@ test tests: clean | $(VENV)/bin/pytest # $(VENV)/bin/nosetests # @$(VENV)/bin/no
run: | $(MAIN_CLI_PATH)
$(MAIN_CLI_PATH) --immediate tests/runner/test_eins.py
$(MAIN_CLI_PATH) --immediate tests/runner/
$(MAIN_CLI_PATH) --immediate tests/
$(MAIN_CLI_PATH) --immediate

# Pushes release of this package to pypi
push-release: dist # pushes distribution tarballs of the current version
Expand Down Expand Up @@ -84,7 +87,7 @@ isort: | $(VENV)/bin/isort
@$(VENV)/bin/isort --overwrite-in-place --profile=black --ls --srx --cs --ca -n --ot --tc --color --star-first --virtual-env $(VENV) --py auto $(AUTO_STYLE_TARGETS)


autostyle: isort black flake8
autostyle: run isort black flake8


##############################################################
Expand Down
212 changes: 207 additions & 5 deletions sure/actors.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,220 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os

from sure.meta import get_actor, MetaActor, gather_actor_names
from sure.meta import MetaActor, get_actor, gather_actor_names

__path__ = os.path.abspath(os.path.dirname(__file__))


class Actor(object, metaclass=MetaActor):
"""Base class for actors.
"""
"""Base class for actors."""

__metaclass__ = MetaActor

def __init__(self, runner, ):
def __init__(
self,
runner,
):
self.runner = runner

def __repr__(self):
return '<{}>'.format(self.__class__.__name__)
return "<{}>".format(self.__class__.__name__)

def on_start(self):
"""Called as soon as `sure' starts running.
.. code:: python
from sure.reporter import Reporter
class HelloReporter(Reporter):
def on_start(self):
sys.stderr.write('Reporter.on_start works')
HelloReporter('a <sure.runner.Runner()>').on_start()
"""

def on_feature(self, feature):
"""Called when a scenario feature is about to run
.. code:: python
from sure.reporter import Reporter
class FeatureReporter(Reporter):
def on_feature(self, feature):
sys.stderr.write('Reporter.on_feature reported {}'.format(feature.name))
class feature:
name = 'a simple scenario feature'
FeatureReporter('a <sure.runner.Runner()>').on_feature(feature)
"""

def on_feature_done(self, feature, result):
"""Called when a scenario feature_done is about to run
.. code:: python
from sure.reporter import Reporter
class FeatureReporter(Reporter):
def on_feature_done(self, feature):
sys.stderr.write('Reporter.on_feature_done reported {}'.format(feature.name))
class feature_done:
name = 'a simple scenario'
Feature_doneReporter('a <sure.runner.Runner()>').on_feature_done(feature_done)
"""

def on_scenario(self, scenario, result):
"""Called when a scenario test_done is about to run
.. code:: python
from sure.reporter import Reporter
class TestReporter(Reporter):
def on_scenario_done(self, scenario):
sys.stderr.write('Reporter.on_scenario_done reported {}'.format(scenario.name))
class test_done:
name = 'a simple scenario'
TestReporter('a <sure.runner.Runner()>').on_scenario_done(test_done)
"""

def on_scenario_done(self, scenario):
"""Called when a scenario test_done is about to run
.. code:: python
from sure.reporter import Reporter
class TestReporter(Reporter):
def on_scenario_done(self, scenario):
sys.stderr.write('Reporter.on_scenario_done reported {}'.format(scenario.name))
class test_done:
name = 'a simple scenario'
TestReporter('a <sure.runner.Runner()>').on_scenario_done(test_done)
"""

def on_failure(self, scenario, error):
"""Called when a scenario fails without crashing
.. code:: python
from sure.reporter import Reporter
class FailureReporter(Reporter):
def on_failure(self, scenario):
sys.stderr.write('Reporter.on_failure reported {}'.format(scenario.name))
class failure:
name = 'a simple failure'
FailureReporter('a <sure.runner.Runner()>').on_failure(failure)
"""

def on_success(self, scenario):
"""Called when a scenario passes
.. code:: python
from sure.reporter import Reporter
class SuccessReporter(Reporter):
def on_success(self, scenario):
sys.stderr.write('Reporter.on_success reported {}'.format(scenario.name))
class success:
name = 'a simple success'
SuccessReporter('a <sure.runner.Runner()>').on_success(success)
"""

def on_error(self, scenario, error):
"""Called when a scenario fails with exception
.. code:: python
from sure.reporter import Reporter
class ErrorReporter(Reporter):
def on_error(self, scenario):
sys.stderr.write('Reporter.on_error reported {}'.format(scenario.name))
class error:
name = 'a simple error'
ErrorReporter('a <sure.runner.Runner()>').on_error(error)
"""

def on_finish(self):
"""Called as soon as `sure' finishes running.
.. code:: python
from sure.reporter import Reporter
class HelloReporter(Reporter):
def on_finish(self):
sys.stderr.write('Reporter.on_finish works')
HelloReporter('a <sure.runner.Runner()>').on_finish()
"""

@classmethod
def from_name(cls, name):
"""`def from_name(name)`
Finds a suitable Reporter class for the given name, after any
`Reporter` subclasses with a `name` class attribute are registered
and returned by this method.
# Usage
from sure.reporter import Reporter
Reporter.from_name('feature')
```
"""
if not isinstance(name, str):
raise TypeError(f'name should be a {str.__name__} but got the {type(name).__name__} {name} instead')
found = get_reporter(name)
if not found:
raise RuntimeError(
"no reporter found with name {}, options are: {}".format(
name, ", ".join(gather_reporter_names())
)
)

return found

@classmethod
def from_name_and_runner(cls, name, runner):
"""
.. code:: python
from sure.runner import Runner
from sure.reporter import Reporter
runner = Runner('/some/path')
ReporterClass = Reporter.from_name('feature')
reporter = ReporterClass(runner)
Example usage:
.. code:: python
reporter = Reporter.from_name_and_runner('feature', runner)
"""
cls.importer.load_recursive(
__path__.joinpath("reporters"),
ignore_errors=False,
)
return cls.from_name(name)(runner)
19 changes: 12 additions & 7 deletions sure/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os

from sure.meta import get_agent, MetaAgent, gather_agent_names
from sure.meta import MetaAgent, get_agent, gather_agent_names
from sure.actors import Actor

__path__ = os.path.abspath(os.path.dirname(__file__))


Expand All @@ -33,17 +34,21 @@ class Agent(Actor):
* :py:meth:`~sure.agent.Agent.on_produce`
* :py:meth:`~sure.agent.Agent.on_communication_error`
"""

__metaclass__ = MetaAgent
name = None

def __init__(self, runner, ):
def __init__(
self,
runner,
):
self.runner = runner

def __repr__(self):
return '<{}>'.format(self.__class__.__name__)
return "<{}>".format(self.__class__.__name__)

def on_present(self):
"""Called as soon as `sure' starts running.
"""Called as soon as `sure' presents running.
.. code:: python
Expand All @@ -52,10 +57,10 @@ def on_present(self):
output = open('/dev/random', 'ba')
class HelloAgent(Agent):
def on_start(self):
output.write(b"sure's test runner has started")
def on_present(self):
output.write(b"sure's test runner has presented")
HelloAgent(':py:class:`sure.runner.Runner`').on_start()
HelloAgent(':py:class:`sure.runner.Runner`').on_present()
"""

def on_finish(self):
Expand Down
29 changes: 19 additions & 10 deletions sure/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,32 +29,41 @@

from sure.importer import resolve_path
from sure.runner import Runner
from sure.meta import gather_reporter_names
from sure.reporters import gather_reporter_names, CoReporter
from sure.errors import ExitError, ExitFailure


@click.command(no_args_is_help=True)
@click.argument("paths", nargs=-1)
@click.option("-r", "--reporter", default="feature", help='default=feature') # , type=click.Choice(gather_reporter_names()))
@click.option("-R", "--reporters", multiple=True)
@click.option("-r", "--reporter", default="feature", help='default=feature', type=click.Choice(gather_reporter_names()))
@click.option("-R", "--reporters", multiple=True, help=f"options=[{','.join(gather_reporter_names())}]")
@click.option("-i", "--immediate", is_flag=True)
@click.option("-l", "--log-level", type=click.Choice(['none', 'debug', 'info', 'warning', 'error']), help="default='none'")
@click.option("-F", "--log-file", help='path to a log file. Default to SURE_LOG_FILE')
def entrypoint(paths, reporter, reporters, immediate, log_level, log_file):
@click.option("-v", "--verbose", is_flag=True, multiple=True)
@click.option("-q", "--quiet", is_flag=True, multiple=True)
def entrypoint(paths, reporter, reporters, immediate, log_level, log_file, verbose, quiet):
if not paths:
paths = glob('test*/**')
else:
paths = flatten(*list(map(glob, paths)))

reporters = reporters and list(reporters) or None
verbosity_level = sum(verbose)
quietness_level = sum(quiet)
verbosity = verbosity_level - quietness_level
quietness = quietness_level - verbosity_level

configure_logging(log_level, log_file)
runner = Runner(resolve_path(os.getcwd()), reporter)
runner = Runner(resolve_path(os.getcwd()), reporter, reporters)
result = runner.run(paths, immediate=immediate)

if result.is_failure:
raise ExitFailure(result)
if result:
if result.is_failure:
raise ExitFailure(runner.context, result)

if result.is_error:
raise ExitError(result)
if result.is_error:
raise ExitError(runner.context, result)


def configure_logging(log_level: str, log_file: str):
Expand All @@ -71,7 +80,7 @@ def configure_logging(log_level: str, log_file: str):

handler = logging.FileHandler(log_file)
else:
handler = logging.StreamHandler(sys.stderr)
handler = logging.NullHandler()

handler.setLevel(level)

Expand Down

0 comments on commit 23fad41

Please sign in to comment.