Skip to content
This repository has been archived by the owner on May 3, 2024. It is now read-only.

Commit

Permalink
Merge pull request #26 from sernst/25-pytest-support
Browse files Browse the repository at this point in the history
PyTest Support
  • Loading branch information
sernst committed May 28, 2018
2 parents 5fee925 + 91587ac commit 6e6d291
Show file tree
Hide file tree
Showing 11 changed files with 660 additions and 174 deletions.
13 changes: 7 additions & 6 deletions cauldron/runner/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,17 @@ def close():
return True


def reload_libraries():
def reload_libraries(library_directories: list = None):
"""
Reload the libraries stored in the project's local and shared library
directories
:return:
"""

directories = library_directories or []
project = cauldron.project.internal_project
if not project:
if project:
directories += project.library_directories

if not directories:
return

def reload_module(path: str, library_directory: str):
Expand Down Expand Up @@ -120,7 +121,7 @@ def reload_library(directory: str) -> list:

return [
reloaded_module
for directory in project.library_directories
for directory in directories
for reloaded_module in reload_library(directory)
if reload_module is not None
]
Expand Down
2 changes: 1 addition & 1 deletion cauldron/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"version": "0.3.3",
"version": "0.3.4",
"notebookVersion": "v1"
}
189 changes: 38 additions & 151 deletions cauldron/steptest/__init__.py
Original file line number Diff line number Diff line change
@@ -1,91 +1,16 @@
import inspect
import os
import tempfile
import unittest
import typing
import inspect
import time

import cauldron as cd
from cauldron import environ
from cauldron import runner
from cauldron.session import projects
from cauldron.session import exposed
from cauldron.cli import commander
from cauldron.session.caching import SharedCache


def find_project_directory(subdirectory: str) -> typing.Union[str, None]:
"""
Finds the root project directory from a subdirectory within the project
folder.
:param subdirectory:
The subdirectory to use to search for the project directory. The
subdirectory may also be the project directory.
:return:
The project directory that contains the specified subdirectory or None
if no project directory was found.
"""

if os.path.exists(os.path.join(subdirectory, 'cauldron.json')):
return subdirectory

parent = os.path.dirname(subdirectory)
if parent == subdirectory:
return None

if os.path.exists(os.path.join(parent, 'cauldron.json')):
return parent
return find_project_directory(parent)


class StepTestRunResult:
"""
This class contains information returned from running a step during testing.
"""

def __init__(
self,
step: 'projects.ProjectStep',
response: 'environ.Response'
):
self._step = step # type: projects.ProjectStep
self._response = response # type: environ.Response
self._locals = SharedCache().put(**step.test_locals)

@property
def local(self) -> SharedCache:
"""
Container object that holds all of the local variables that were
defined within the run step
"""

return self._locals

@property
def success(self) -> bool:
"""
Whether or not the step was successfully run. This value will be
False if there as an uncaught exception during the execution of the
step.
"""

return not self._response.failed

def echo_error(self) -> str:
"""
Creates a string representation of the exception that cause the step
to fail if an exception occurred. Otherwise, an empty string is returned.
:return:
The string representation of the exception that caused the running
step to fail or a blank string if no exception occurred
"""

if not self._response.errors:
return ''

return '{}'.format(self._response.errors[0].serialize())
from cauldron.session import exposed # noqa
from cauldron.steptest import support
from cauldron.steptest.functional import CauldronTest
from cauldron.steptest.results import StepTestRunResult
from cauldron.steptest.support import find_project_directory


class StepTestCase(unittest.TestCase):
Expand All @@ -99,6 +24,10 @@ class StepTestCase(unittest.TestCase):
"""

def __init__(self, *args, **kwargs):
"""
Initializes a StepTestCase with default attribute values that will be
populated during the setup and teardown phases of each test.
"""
super(StepTestCase, self).__init__(*args, **kwargs)
self.results_directory = None
self.temp_directories = dict()
Expand All @@ -109,7 +38,6 @@ def setUp(self):
If you override the setUp function in your tests, be sure to call the
super function so that this initialization happens properly
"""

environ.modes.add(environ.modes.TESTING)
super(StepTestCase, self).setUp()
results_directory = tempfile.mkdtemp(
Expand All @@ -136,49 +64,21 @@ def make_project_path(self, *args) -> str:
components are specified, the location returned will be the
project directory itself.
"""

filename = inspect.getfile(self.__class__)
project_directory = find_project_directory(filename)

if project_directory is None:
raise FileNotFoundError(' '.join([
'StepTest does not appear to be located within',
'a Cauldron project directory'
]))

project_directory = support.find_project_directory(filename)
return os.path.join(project_directory, *args)

def open_project(self) -> 'exposed.ExposedProject':
"""
Returns the Response object populated by the open project command
Opens the project associated with this test and returns the public
(exposed) project after it has been loaded. If the project cannot be
opened the test will fail.
"""

project_path = self.make_project_path()
res = environ.Response()
commander.execute(
'open', '{} --forget'.format(project_path),
res
)
res.thread.join()

# Prevent threading race conditions
check_count = 0
while res.success and not cd.project.internal_project:
if check_count > 100:
break

check_count += 1
time.sleep(0.25)

if res.failed or not cd.project.internal_project:
self.fail(
'Unable to open project at path "{}"'
.format(project_path)
)

os.chdir(cd.project.internal_project.source_directory)

return cd.project
try:
project_path = self.make_project_path()
return support.open_project(project_path)
except RuntimeError as error:
self.fail('{}'.format(error))

def run_step(
self,
Expand All @@ -200,30 +100,18 @@ def run_step(
A StepTestRunResult instance containing information about the
execution of the step.
"""

project = cd.project.internal_project
if not project:
self.fail(
'No project was open. Unable to run step "{}"'
.format(step_name)
)

step = project.get_step(step_name)
if not step:
self.fail('No step named "{}" was found'.format(step_name))

print('RUNNING', step_name)
response = commander.execute('run', '"{}" --force'.format(step_name))
print('WAITING', step_name)
response.thread.join()
print('COMPLETE', step_name)

if not allow_failure and response.failed:
self.fail('Failed to run step "{}"'.format(step_name))

return StepTestRunResult(step, response)
try:
return support.run_step(step_name, allow_failure)
except AssertionError as error:
self.fail('{}'.format(error))

def tearDown(self):
"""
After a test is run this function is used to undo any side effects that
were created by setting up and running the test. This includes removing
the temporary directories that were created to store test information
during test execution.
"""
super(StepTestCase, self).tearDown()

# Close any open project so that it doesn't persist to the next test
Expand All @@ -242,17 +130,16 @@ def tearDown(self):

def make_temp_path(self, identifier, *args) -> str:
"""
Creates a temporary path within a named directory created
for the test being executed. If any directories in the path don't exist
already, they will be created before returning the path.
:param identifier:
The identifier for the test the is used to name the folder in which
the temp path will reside within the root test folder for the given
test.
:param args:
:return:
Any additional path elements to identify the path that will
appear beneath the identifier folder.
"""

if identifier not in self.temp_directories:
self.temp_directories[identifier] = tempfile.mkdtemp(
prefix='cd-step-test-{}'.format(identifier)
)

return os.path.realpath(
os.path.join(self.temp_directories[identifier], *args)
)
return support.make_temp_path(self.temp_directories, identifier, *args)

0 comments on commit 6e6d291

Please sign in to comment.