diff options
32 files changed, 279 insertions, 226 deletions
diff --git a/.testr.conf b/.testr.conf new file mode 100644 index 0000000..8aa2cd0 --- /dev/null +++ b/.testr.conf @@ -0,0 +1,9 @@ +[DEFAULT] +test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \ + OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \ + OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-160} \ + ${PYTHON:-python} -m subunit.run discover -t ./ ./taskflow/tests $LISTOPT $IDOPTION + +test_id_option=--load-list $IDFILE +test_list_option=--list + diff --git a/requirements.txt b/requirements.txt index 49239f1..7b63dba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,7 +15,7 @@ stevedore>=0.10 # Backport for concurrent.futures which exists in 3.2+ futures>=2.1.3 # Only needed if the eventlet executor is used. -eventlet>=0.13.0 +# eventlet>=0.13.0 # NOTE(harlowja): if you want to be able to use the graph_utils # export_graph_to_dot function you will need to uncomment the following. # pydot>=1.0 @@ -17,6 +17,8 @@ classifier = Programming Language :: Python :: 2 Programming Language :: Python :: 2.6 Programming Language :: Python :: 2.7 + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.3 [global] setup-hooks = diff --git a/taskflow/examples/resume_vm_boot.py b/taskflow/examples/resume_vm_boot.py index ca99f3b..0a16f37 100644 --- a/taskflow/examples/resume_vm_boot.py +++ b/taskflow/examples/resume_vm_boot.py @@ -81,7 +81,7 @@ def get_backend(): class PrintText(task.Task): """Just inserts some text print outs in a workflow.""" def __init__(self, print_what, no_slow=False): - content_hash = hashlib.md5(print_what).hexdigest()[0:8] + content_hash = hashlib.md5(print_what.encode('utf-8')).hexdigest()[0:8] super(PrintText, self).__init__(name="Print: %s" % (content_hash)) self._text = print_what self._no_slow = no_slow @@ -257,8 +257,9 @@ except (IndexError, ValueError): # Set up how we want our engine to run, serial, parallel... engine_conf = { 'engine': 'parallel', - 'executor': e_utils.GreenExecutor(5), } +if e_utils.EVENTLET_AVAILABLE: + engine_conf['executor'] = e_utils.GreenExecutor(5) # Create/fetch a logbook that will track the workflows work. book = None diff --git a/taskflow/examples/resume_volume_create.py b/taskflow/examples/resume_volume_create.py index d189b5f..1c35c73 100644 --- a/taskflow/examples/resume_volume_create.py +++ b/taskflow/examples/resume_volume_create.py @@ -38,7 +38,6 @@ from taskflow import engines from taskflow import task from taskflow.persistence import backends -from taskflow.utils import eventlet_utils as e_utils from taskflow.utils import persistence_utils as p_utils @@ -83,7 +82,7 @@ def get_backend(): class PrintText(task.Task): def __init__(self, print_what, no_slow=False): - content_hash = hashlib.md5(print_what).hexdigest()[0:8] + content_hash = hashlib.md5(print_what.encode('utf-8')).hexdigest()[0:8] super(PrintText, self).__init__(name="Print: %s" % (content_hash)) self._text = print_what self._no_slow = no_slow @@ -156,13 +155,13 @@ else: flow_detail = find_flow_detail(backend, book_id, flow_id) # Annnnd load and run. +engine_conf = { + 'engine': 'serial', +} engine = engines.load(flow, flow_detail=flow_detail, backend=backend, - engine_conf={ - 'engine': 'parallel', - 'executor': e_utils.GreenExecutor(10), - }) + engine_conf=engine_conf) engine.run() # How to use. diff --git a/taskflow/examples/reverting_linear.out.txt b/taskflow/examples/reverting_linear.out.txt index 37c4bd9..9928652 100644 --- a/taskflow/examples/reverting_linear.out.txt +++ b/taskflow/examples/reverting_linear.out.txt @@ -2,4 +2,4 @@ Calling jim 555. Calling joe 444. Calling 444 and apologizing. Calling 555 and apologizing. -Flow failed: IOError('Suzzie not home right now.',) +Flow failed: Suzzie not home right now. diff --git a/taskflow/examples/reverting_linear.py b/taskflow/examples/reverting_linear.py index 1a39561..05ec083 100644 --- a/taskflow/examples/reverting_linear.py +++ b/taskflow/examples/reverting_linear.py @@ -98,4 +98,4 @@ except Exception as e: # You will also note that this is not a problem in this case since no # parallelism is involved; this is ensured by the usage of a linear flow, # which runs serially as well as the default engine type which is 'serial'. - print("Flow failed: %r" % e) + print("Flow failed: %s" % e) diff --git a/taskflow/examples/simple_linear_listening.out.txt b/taskflow/examples/simple_linear_listening.out.txt index ee51ef4..bf3304a 100644 --- a/taskflow/examples/simple_linear_listening.out.txt +++ b/taskflow/examples/simple_linear_listening.out.txt @@ -1,10 +1,10 @@ Flow => RUNNING Task __main__.call_jim => RUNNING Calling jim. -Context = {'joe_number': 444, 'jim_number': 555} +Context = [('jim_number', 555), ('joe_number', 444)] Task __main__.call_jim => SUCCESS Task __main__.call_joe => RUNNING Calling joe. -Context = {'joe_number': 444, 'jim_number': 555} +Context = [('jim_number', 555), ('joe_number', 444)] Task __main__.call_joe => SUCCESS Flow => SUCCESS diff --git a/taskflow/examples/simple_linear_listening.py b/taskflow/examples/simple_linear_listening.py index dcb35cc..ca38fce 100644 --- a/taskflow/examples/simple_linear_listening.py +++ b/taskflow/examples/simple_linear_listening.py @@ -58,12 +58,12 @@ from taskflow import task def call_jim(context): print("Calling jim.") - print("Context = %s" % (context)) + print("Context = %s" % (sorted(context.items(), key=lambda x: x[0]))) def call_joe(context): print("Calling joe.") - print("Context = %s" % (context)) + print("Context = %s" % (sorted(context.items(), key=lambda x: x[0]))) def flow_watch(state, details): diff --git a/taskflow/persistence/backends/impl_sqlalchemy.py b/taskflow/persistence/backends/impl_sqlalchemy.py index 59edb86..4de6471 100644 --- a/taskflow/persistence/backends/impl_sqlalchemy.py +++ b/taskflow/persistence/backends/impl_sqlalchemy.py @@ -36,6 +36,7 @@ from taskflow.persistence.backends import base from taskflow.persistence.backends.sqlalchemy import migration from taskflow.persistence.backends.sqlalchemy import models from taskflow.persistence import logbook +from taskflow.utils import eventlet_utils from taskflow.utils import misc from taskflow.utils import persistence_utils @@ -224,7 +225,9 @@ class SQLAlchemyBackend(base.Backend): # or engine arg overrides make sure we merge them in. engine_args.update(conf.pop('engine_args', {})) engine = sa.create_engine(sql_connection, **engine_args) - if misc.as_bool(conf.pop('checkin_yield', True)): + checkin_yield = conf.pop('checkin_yield', + eventlet_utils.EVENTLET_AVAILABLE) + if misc.as_bool(checkin_yield): sa.event.listen(engine, 'checkin', _thread_yield) if 'mysql' in e_url.drivername: if misc.as_bool(conf.pop('checkout_ping', True)): diff --git a/taskflow/test.py b/taskflow/test.py index b1ea801..8a55afe 100644 --- a/taskflow/test.py +++ b/taskflow/test.py @@ -16,17 +16,41 @@ # License for the specific language governing permissions and limitations # under the License. -import unittest2 +from testtools import compat +from testtools import matchers +from testtools import testcase -class TestCase(unittest2.TestCase): - """Test case base class for all unit tests.""" +class TestCase(testcase.TestCase): + """Test case base class for all taskflow unit tests.""" - def setUp(self): - super(TestCase, self).setUp() + def assertRaisesRegexp(self, exc_class, pattern, callable_obj, + *args, **kwargs): + # TODO(harlowja): submit a pull/review request to testtools to add + # this method to there codebase instead of having it exist in ours + # since it really doesn't belong here. - def tearDown(self): - super(TestCase, self).tearDown() + class ReRaiseOtherTypes(object): + def match(self, matchee): + if not issubclass(matchee[0], exc_class): + compat.reraise(*matchee) + + class CaptureMatchee(object): + def match(self, matchee): + self.matchee = matchee[1] + + capture = CaptureMatchee() + matcher = matchers.Raises(matchers.MatchesAll(ReRaiseOtherTypes(), + matchers.MatchesException(exc_class, + pattern), + capture)) + our_callable = testcase.Nullary(callable_obj, *args, **kwargs) + self.assertThat(our_callable, matcher) + return capture.matchee + + def assertRegexpMatches(self, text, pattern): + matcher = matchers.MatchesRegex(pattern) + self.assertThat(text, matcher) def assertIsSubset(self, super_set, sub_set, msg=None): missing_set = set() diff --git a/taskflow/tests/test_examples.py b/taskflow/tests/test_examples.py index 906701a..31cc21a 100644 --- a/taskflow/tests/test_examples.py +++ b/taskflow/tests/test_examples.py @@ -98,7 +98,7 @@ class ExamplesTestCase(taskflow.test.TestCase): # replace them with some constant string output = self.uuid_re.sub('<SOME UUID>', output) expected_output = self.uuid_re.sub('<SOME UUID>', expected_output) - self.assertMultiLineEqual(output, expected_output) + self.assertEqual(output, expected_output) ExamplesTestCase.update() diff --git a/taskflow/tests/unit/persistence/test_memory_persistence.py b/taskflow/tests/unit/persistence/test_memory_persistence.py index cdc9bbc..cccf9fb 100644 --- a/taskflow/tests/unit/persistence/test_memory_persistence.py +++ b/taskflow/tests/unit/persistence/test_memory_persistence.py @@ -23,6 +23,7 @@ from taskflow.tests.unit.persistence import base class MemoryPersistenceTest(test.TestCase, base.PersistenceTestMixin): def setUp(self): + super(MemoryPersistenceTest, self).setUp() self._backend = impl_memory.MemoryBackend({}) def _get_connection(self): diff --git a/taskflow/tests/unit/test_action_engine.py b/taskflow/tests/unit/test_action_engine.py index f803785..2178554 100644 --- a/taskflow/tests/unit/test_action_engine.py +++ b/taskflow/tests/unit/test_action_engine.py @@ -85,30 +85,34 @@ class EngineTaskTest(utils.EngineTestBase): 'fail reverted(Failure: RuntimeError: Woot!)', 'fail REVERTED', 'flow REVERTED'] - with self.assertRaisesRegexp(RuntimeError, '^Woot'): - engine.run() + self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run) self.assertEqual(self.values, expected) self.assertEqual(engine.storage.get_flow_state(), states.REVERTED) - with self.assertRaisesRegexp(RuntimeError, '^Woot'): - engine.run() + self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run) now_expected = expected + ['fail PENDING', 'flow PENDING'] + expected self.assertEqual(self.values, now_expected) self.assertEqual(engine.storage.get_flow_state(), states.REVERTED) def test_invalid_flow_raises(self): - value = 'i am string, not task/flow, sorry' - with self.assertRaises(TypeError) as err: + + def compile_bad(value): engine = self._make_engine(value) engine.compile() - self.assertIn(value, str(err.exception)) - def test_invalid_flow_raises_from_run(self): value = 'i am string, not task/flow, sorry' - with self.assertRaises(TypeError) as err: + err = self.assertRaises(TypeError, compile_bad, value) + self.assertIn(value, str(err)) + + def test_invalid_flow_raises_from_run(self): + + def run_bad(value): engine = self._make_engine(value) engine.run() - self.assertIn(value, str(err.exception)) + + value = 'i am string, not task/flow, sorry' + err = self.assertRaises(TypeError, run_bad, value) + self.assertIn(value, str(err)) class EngineLinearFlowTest(utils.EngineTestBase): @@ -136,8 +140,7 @@ class EngineLinearFlowTest(utils.EngineTestBase): utils.FailingTask(name='fail') ) engine = self._make_engine(flow) - with self.assertRaisesRegexp(RuntimeError, '^Woot'): - engine.run() + self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run) self.assertEqual(engine.storage.fetch_all(), {}) def test_sequential_flow_nested_blocks(self): @@ -156,8 +159,7 @@ class EngineLinearFlowTest(utils.EngineTestBase): utils.FailingTask(name='fail') ) engine = self._make_engine(flow) - with self.assertRaisesRegexp(RuntimeError, '^Gotcha'): - engine.run() + self.assertRaisesRegexp(RuntimeError, '^Gotcha', engine.run) def test_revert_not_run_task_is_not_reverted(self): flow = lf.Flow('revert-not-run').add( @@ -165,8 +167,7 @@ class EngineLinearFlowTest(utils.EngineTestBase): utils.NeverRunningTask(), ) engine = self._make_engine(flow) - with self.assertRaisesRegexp(RuntimeError, '^Woot'): - engine.run() + self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run) self.assertEqual( self.values, ['fail reverted(Failure: RuntimeError: Woot!)']) @@ -180,8 +181,7 @@ class EngineLinearFlowTest(utils.EngineTestBase): ) ) engine = self._make_engine(flow) - with self.assertRaisesRegexp(RuntimeError, '^Woot'): - engine.run() + self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run) self.assertEqual( self.values, ['task1', 'task2', @@ -195,7 +195,7 @@ class EngineLinearFlowTest(utils.EngineTestBase): def revert(m_self, result, flow_failures): self.assertEqual(result, 'RESULT') - self.assertEqual(flow_failures.keys(), ['fail1']) + self.assertEqual(list(flow_failures.keys()), ['fail1']) fail = flow_failures['fail1'] self.assertIsInstance(fail, misc.Failure) self.assertEqual(str(fail), 'Failure: RuntimeError: Woot!') @@ -205,8 +205,7 @@ class EngineLinearFlowTest(utils.EngineTestBase): utils.FailingTask(self.values, 'fail1') ) engine = self._make_engine(flow) - with self.assertRaisesRegexp(RuntimeError, '^Woot'): - engine.run() + self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run) class EngineParallelFlowTest(utils.EngineTestBase): @@ -236,8 +235,7 @@ class EngineParallelFlowTest(utils.EngineTestBase): utils.TaskNoRequiresNoReturns(name='task2') ) engine = self._make_engine(flow) - with self.assertRaisesRegexp(RuntimeError, '^Woot'): - engine.run() + self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run) def test_parallel_revert_exception_is_reraised(self): # NOTE(imelnikov): if we put NastyTask and FailingTask @@ -252,8 +250,7 @@ class EngineParallelFlowTest(utils.EngineTestBase): utils.FailingTask(self.values, sleep=0.1) ) engine = self._make_engine(flow) - with self.assertRaisesRegexp(RuntimeError, '^Gotcha'): - engine.run() + self.assertRaisesRegexp(RuntimeError, '^Gotcha', engine.run) def test_sequential_flow_two_tasks_with_resumption(self): flow = lf.Flow('lf-2-r').add( @@ -285,8 +282,7 @@ class EngineParallelFlowTest(utils.EngineTestBase): utils.SaveOrderTask(self.values, name='task2', sleep=0.01) ) engine = self._make_engine(flow) - with self.assertRaisesRegexp(RuntimeError, '^Woot'): - engine.run() + self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run) result = set(self.values) # NOTE(harlowja): task 1/2 may or may not have executed, even with the # sleeps due to the fact that the above is an unordered flow. @@ -303,8 +299,7 @@ class EngineParallelFlowTest(utils.EngineTestBase): name='task2') # this should not get reverted ) engine = self._make_engine(flow) - with self.assertRaisesRegexp(RuntimeError, '^Gotcha'): - engine.run() + self.assertRaisesRegexp(RuntimeError, '^Gotcha', engine.run) result = set(self.values) self.assertEqual(result, set(['task1'])) @@ -319,8 +314,7 @@ class EngineParallelFlowTest(utils.EngineTestBase): ) ) engine = self._make_engine(flow) - with self.assertRaisesRegexp(RuntimeError, '^Gotcha'): - engine.run() + self.assertRaisesRegexp(RuntimeError, '^Gotcha', engine.run) result = set(self.values) # Task1, task2 may *not* have executed and also may have *not* reverted # since the above is an unordered flow so take that into account by @@ -381,8 +375,7 @@ class EngineParallelFlowTest(utils.EngineTestBase): ) ) engine = self._make_engine(flow) - with self.assertRaisesRegexp(RuntimeError, '^Woot'): - engine.run() + self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run) result = set(self.values) # Task3 may or may not have executed, depending on scheduling and # task ordering selection, so it may or may not exist in the result set @@ -407,8 +400,7 @@ class EngineParallelFlowTest(utils.EngineTestBase): ) ) engine = self._make_engine(flow) - with self.assertRaisesRegexp(RuntimeError, '^Woot'): - engine.run() + self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run) result = set(self.values) # Since this is an unordered flow we can not guarantee that task1 or # task2 will exist and be reverted, although they may exist depending @@ -432,8 +424,7 @@ class EngineParallelFlowTest(utils.EngineTestBase): ) ) engine = self._make_engine(flow) - with self.assertRaisesRegexp(RuntimeError, '^Gotcha'): - engine.run() + self.assertRaisesRegexp(RuntimeError, '^Gotcha', engine.run) result = set(self.values) possible_result = set(['task1', 'task1 reverted(5)', 'task2', 'task2 reverted(5)', @@ -492,8 +483,7 @@ class EngineGraphFlowTest(utils.EngineTestBase): utils.SaveOrderTask(self.values, name='task1', provides='a')) engine = self._make_engine(flow) - with self.assertRaisesRegexp(RuntimeError, '^Woot'): - engine.run() + self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run) self.assertEqual( self.values, ['task1', 'task2', @@ -508,8 +498,7 @@ class EngineGraphFlowTest(utils.EngineTestBase): utils.SaveOrderTask(self.values, name='task1', provides='a')) engine = self._make_engine(flow) - with self.assertRaisesRegexp(RuntimeError, '^Gotcha'): - engine.run() + self.assertRaisesRegexp(RuntimeError, '^Gotcha', engine.run) self.assertEqual(engine.storage.get_flow_state(), states.FAILURE) def test_graph_flow_with_multireturn_and_multiargs_tasks(self): diff --git a/taskflow/tests/unit/test_arguments_passing.py b/taskflow/tests/unit/test_arguments_passing.py index c8fab0e..fdaea62 100644 --- a/taskflow/tests/unit/test_arguments_passing.py +++ b/taskflow/tests/unit/test_arguments_passing.py @@ -61,8 +61,9 @@ class ArgumentsPassingTest(utils.EngineTestBase): }) def test_bad_save_as_value(self): - with self.assertRaises(TypeError): - utils.TaskOneReturn(name='task1', provides=object()) + self.assertRaises(TypeError, + utils.TaskOneReturn, + name='task1', provides=object()) def test_arguments_passing(self): flow = utils.TaskMultiArgOneReturn(provides='result') @@ -78,8 +79,7 @@ class ArgumentsPassingTest(utils.EngineTestBase): flow = utils.TaskMultiArg() engine = self._make_engine(flow) engine.storage.inject({'a': 1, 'b': 4, 'x': 17}) - with self.assertRaises(exc.MissingDependencies): - engine.run() + self.assertRaises(exc.MissingDependencies, engine.run) def test_partial_arguments_mapping(self): flow = utils.TaskMultiArgOneReturn(provides='result', @@ -109,19 +109,18 @@ class ArgumentsPassingTest(utils.EngineTestBase): flow = utils.TaskMultiArg(rebind={'z': 'b'}) engine = self._make_engine(flow) engine.storage.inject({'a': 1, 'y': 4, 'c': 9, 'x': 17}) - with self.assertRaises(exc.MissingDependencies): - engine.run() + self.assertRaises(exc.MissingDependencies, engine.run) def test_invalid_argument_name_list(self): flow = utils.TaskMultiArg(rebind=['a', 'z', 'b']) engine = self._make_engine(flow) engine.storage.inject({'a': 1, 'b': 4, 'c': 9, 'x': 17}) - with self.assertRaises(exc.MissingDependencies): - engine.run() + self.assertRaises(exc.MissingDependencies, engine.run) def test_bad_rebind_args_value(self): - with self.assertRaises(TypeError): - utils.TaskOneArg(rebind=object()) + self.assertRaises(TypeError, + utils.TaskOneArg, + rebind=object()) class SingleThreadedEngineTest(ArgumentsPassingTest, diff --git a/taskflow/tests/unit/test_check_transition.py b/taskflow/tests/unit/test_check_transition.py index d587fa8..cb0388a 100644 --- a/taskflow/tests/unit/test_check_transition.py +++ b/taskflow/tests/unit/test_check_transition.py @@ -40,6 +40,7 @@ class CheckFlowTransitionTest(test.TestCase): states.check_flow_transition(states.RUNNING, states.RESUMING)) def test_bad_transition_raises(self): - with self.assertRaisesRegexp(exc.InvalidStateException, - '^Flow transition.*not allowed'): - states.check_flow_transition(states.FAILURE, states.SUCCESS) + self.assertRaisesRegexp(exc.InvalidStateException, + '^Flow transition.*not allowed', + states.check_flow_transition, + states.FAILURE, states.SUCCESS) diff --git a/taskflow/tests/unit/test_engine_helpers.py b/taskflow/tests/unit/test_engine_helpers.py index 5d2907a..ead98dd 100644 --- a/taskflow/tests/unit/test_engine_helpers.py +++ b/taskflow/tests/unit/test_engine_helpers.py @@ -29,25 +29,28 @@ class FlowFromDetailTestCase(test.TestCase): def test_no_meta(self): _lb, flow_detail = p_utils.temporary_flow_detail() self.assertIs(flow_detail.meta, None) - expected_msg = '^Cannot .* no factory information saved.$' - with self.assertRaisesRegexp(ValueError, expected_msg): - taskflow.engines.flow_from_detail(flow_detail) + self.assertRaisesRegexp(ValueError, + '^Cannot .* no factory information saved.$', + taskflow.engines.flow_from_detail, + flow_detail) def test_no_factory_in_meta(self): _lb, flow_detail = p_utils.temporary_flow_detail() flow_detail.meta = {} - expected_msg = '^Cannot .* no factory information saved.$' - with self.assertRaisesRegexp(ValueError, expected_msg): - taskflow.engines.flow_from_detail(flow_detail) + self.assertRaisesRegexp(ValueError, + '^Cannot .* no factory information saved.$', + taskflow.engines.flow_from_detail, + flow_detail) def test_no_importable_function(self): _lb, flow_detail = p_utils.temporary_flow_detail() flow_detail.meta = dict(factory=dict( name='you can not import me, i contain spaces' )) - expected_msg = '^Could not import factory' - with self.assertRaisesRegexp(ImportError, expected_msg): - taskflow.engines.flow_from_detail(flow_detail) + self.assertRaisesRegexp(ImportError, + '^Could not import factory', + taskflow.engines.flow_from_detail, + flow_detail) def test_no_arg_factory(self): name = 'some.test.factory' @@ -79,11 +82,14 @@ def my_flow_factory(task_name): class LoadFromFactoryTestCase(test.TestCase): def test_non_reimportable(self): + def factory(): pass - with self.assertRaisesRegexp(ValueError, - 'Flow factory .* is not reimportable'): - taskflow.engines.load_from_factory(factory) + + self.assertRaisesRegexp(ValueError, + 'Flow factory .* is not reimportable', + taskflow.engines.load_from_factory, + factory) def test_it_works(self): engine = taskflow.engines.load_from_factory( diff --git a/taskflow/tests/unit/test_flattening.py b/taskflow/tests/unit/test_flattening.py index 7851081..9f8eade 100644 --- a/taskflow/tests/unit/test_flattening.py +++ b/taskflow/tests/unit/test_flattening.py @@ -174,14 +174,14 @@ class FlattenTest(test.TestCase): t_utils.DummyTask(name="a"), t_utils.DummyTask(name="a") ) - with self.assertRaisesRegexp(exc.InvariantViolationException, - '^Tasks with duplicate names'): - f_utils.flatten(flo) + self.assertRaisesRegexp(exc.InvariantViolationException, + '^Tasks with duplicate names', + f_utils.flatten, flo) def test_flatten_checks_for_dups_globally(self): flo = gf.Flow("test").add( gf.Flow("int1").add(t_utils.DummyTask(name="a")), gf.Flow("int2").add(t_utils.DummyTask(name="a"))) - with self.assertRaisesRegexp(exc.InvariantViolationException, - '^Tasks with duplicate names'): - f_utils.flatten(flo) + self.assertRaisesRegexp(exc.InvariantViolationException, + '^Tasks with duplicate names', + f_utils.flatten, flo) diff --git a/taskflow/tests/unit/test_flow_dependencies.py b/taskflow/tests/unit/test_flow_dependencies.py index 7f5e198..0bdb7ab 100644 --- a/taskflow/tests/unit/test_flow_dependencies.py +++ b/taskflow/tests/unit/test_flow_dependencies.py @@ -86,10 +86,11 @@ class FlowDependenciesTest(test.TestCase): self.assertEqual(flow.provides, set(['x', 'a', 'b', 'c'])) def test_linear_flow_provides_out_of_order(self): - with self.assertRaises(exceptions.InvariantViolationException): - lf.Flow('lf').add( - utils.TaskOneArg('task2'), - utils.TaskOneReturn('task1', provides='x')) + flow = lf.Flow('lf') + self.assertRaises(exceptions.InvariantViolationException, + flow.add, + utils.TaskOneArg('task2'), + utils.TaskOneReturn('task1', provides='x')) def test_linear_flow_provides_required_values(self): flow = lf.Flow('lf').add( @@ -110,8 +111,10 @@ class FlowDependenciesTest(test.TestCase): def test_linear_flow_self_requires(self): flow = lf.Flow('uf') - with self.assertRaises(exceptions.InvariantViolationException): - flow.add(utils.TaskNoRequiresNoReturns(rebind=['x'], provides='x')) + self.assertRaises(exceptions.InvariantViolationException, + flow.add, + utils.TaskNoRequiresNoReturns(rebind=['x'], + provides='x')) def test_unordered_flow_without_dependencies(self): flow = uf.Flow('uf').add( @@ -122,8 +125,10 @@ class FlowDependenciesTest(test.TestCase): def test_unordered_flow_self_requires(self): flow = uf.Flow('uf') - with self.assertRaises(exceptions.InvariantViolationException): - flow.add(utils.TaskNoRequiresNoReturns(rebind=['x'], provides='x')) + self.assertRaises(exceptions.InvariantViolationException, + flow.add, + utils.TaskNoRequiresNoReturns(rebind=['x'], + provides='x')) def test_unordered_flow_reuires_values(self): flow = uf.Flow('uf').add( @@ -147,22 +152,25 @@ class FlowDependenciesTest(test.TestCase): self.assertEqual(flow.provides, set(['x', 'a', 'b', 'c'])) def test_unordered_flow_provides_required_values(self): - with self.assertRaises(exceptions.InvariantViolationException): - uf.Flow('uf').add( - utils.TaskOneReturn('task1', provides='x'), - utils.TaskOneArg('task2')) + flow = uf.Flow('uf') + self.assertRaises(exceptions.InvariantViolationException, + flow.add, + utils.TaskOneReturn('task1', provides='x'), + utils.TaskOneArg('task2')) def test_unordered_flow_requires_provided_value_other_call(self): flow = uf.Flow('uf') flow.add(utils.TaskOneReturn('task1', provides='x')) - with self.assertRaises(exceptions.InvariantViolationException): - flow.add(utils.TaskOneArg('task2')) + self.assertRaises(exceptions.InvariantViolationException, + flow.add, + utils.TaskOneArg('task2')) def test_unordered_flow_provides_required_value_other_call(self): flow = uf.Flow('uf') flow.add(utils.TaskOneArg('task2')) - with self.assertRaises(exceptions.InvariantViolationException): - flow.add(utils.TaskOneReturn('task1', provides='x')) + self.assertRaises(exceptions.InvariantViolationException, + flow.add, + utils.TaskOneReturn('task1', provides='x')) def test_unordered_flow_multi_provides_and_requires_values(self): flow = uf.Flow('uf').add( @@ -196,9 +204,11 @@ class FlowDependenciesTest(test.TestCase): self.assertEqual(flow.provides, set()) def test_graph_flow_self_requires(self): - with self.assertRaisesRegexp(exceptions.DependencyFailure, '^No path'): - gf.Flow('g-1-req-error').add( - utils.TaskOneArgOneReturn(requires=['a'], provides='a')) + flow = gf.Flow('g-1-req-error') + self.assertRaisesRegexp(exceptions.DependencyFailure, '^No path', + flow.add, + utils.TaskOneArgOneReturn(requires=['a'], + provides='a')) def test_graph_flow_reuires_values(self): flow = gf.Flow('gf').add( @@ -231,8 +241,9 @@ class FlowDependenciesTest(test.TestCase): def test_graph_flow_provides_provided_value_other_call(self): flow = gf.Flow('gf') flow.add(utils.TaskOneReturn('task1', provides='x')) - with self.assertRaises(exceptions.DependencyFailure): - flow.add(utils.TaskOneReturn('task2', provides='x')) + self.assertRaises(exceptions.DependencyFailure, + flow.add, + utils.TaskOneReturn('task2', provides='x')) def test_graph_flow_multi_provides_and_requires_values(self): flow = gf.Flow('gf').add( @@ -245,8 +256,12 @@ class FlowDependenciesTest(test.TestCase): self.assertEqual(flow.provides, set(['d', 'e', 'f', 'i', 'j', 'k'])) def test_graph_cyclic_dependency(self): - with self.assertRaisesRegexp(exceptions.DependencyFailure, '^No path'): - gf.Flow('g-3-cyclic').add( - utils.TaskOneArgOneReturn(provides='a', requires=['b']), - utils.TaskOneArgOneReturn(provides='b', requires=['c']), - utils.TaskOneArgOneReturn(provides='c', requires=['a'])) + flow = gf.Flow('g-3-cyclic') + self.assertRaisesRegexp(exceptions.DependencyFailure, '^No path', + flow.add, + utils.TaskOneArgOneReturn(provides='a', + requires=['b']), + utils.TaskOneArgOneReturn(provides='b', + requires=['c']), + utils.TaskOneArgOneReturn(provides='c', + requires=['a'])) diff --git a/taskflow/tests/unit/test_functor_task.py b/taskflow/tests/unit/test_functor_task.py index 6d89243..4f2ae39 100644 --- a/taskflow/tests/unit/test_functor_task.py +++ b/taskflow/tests/unit/test_functor_task.py @@ -63,6 +63,6 @@ class FunctorTaskTest(test.TestCase): t(bof.run_one, revert=bof.revert_one), t(bof.run_fail) ) - with self.assertRaisesRegexp(RuntimeError, '^Woot'): - taskflow.engines.run(flow) + self.assertRaisesRegexp(RuntimeError, '^Woot', + taskflow.engines.run, flow) self.assertEqual(values, ['one', 'fail', 'revert one']) diff --git a/taskflow/tests/unit/test_green_executor.py b/taskflow/tests/unit/test_green_executor.py index 225b828..ab6fb25 100644 --- a/taskflow/tests/unit/test_green_executor.py +++ b/taskflow/tests/unit/test_green_executor.py @@ -19,11 +19,13 @@ import collections import functools -from taskflow import test +import testtools +from taskflow import test from taskflow.utils import eventlet_utils as eu +@testtools.skipIf(not eu.EVENTLET_AVAILABLE, 'eventlet is not available') class GreenExecutorTest(test.TestCase): def make_funcs(self, called, amount): diff --git a/taskflow/tests/unit/test_storage.py b/taskflow/tests/unit/test_storage.py index 4cd33b2..6656ca7 100644 --- a/taskflow/tests/unit/test_storage.py +++ b/taskflow/tests/unit/test_storage.py @@ -124,8 +124,7 @@ class StorageTest(test.TestCase): def test_get_non_existing_var(self): s = self._get_storage() s.add_task('42', 'my task') - with self.assertRaises(exceptions.NotFound): - s.get('42') + self.assertRaises(exceptions.NotFound, s.get, '42') def test_reset(self): s = self._get_storage() @@ -133,8 +132,7 @@ class StorageTest(test.TestCase): s.save('42', 5) s.reset('42') self.assertEqual(s.get_task_state('42'), states.PENDING) - with self.assertRaises(exceptions.NotFound): - s.get('42') + self.assertRaises(exceptions.NotFound, s.get, '42') def test_reset_unknown_task(self): s = self._get_storage() @@ -151,11 +149,9 @@ class StorageTest(test.TestCase): s.reset_tasks() self.assertEqual(s.get_task_state('42'), states.PENDING) - with self.assertRaises(exceptions.NotFound): - s.get('42') + self.assertRaises(exceptions.NotFound, s.get, '42') self.assertEqual(s.get_task_state('43'), states.PENDING) - with self.assertRaises(exceptions.NotFound): - s.get('43') + self.assertRaises(exceptions.NotFound, s.get, '43') def test_reset_tasks_does_not_breaks_inject(self): s = self._get_storage() @@ -182,9 +178,9 @@ class StorageTest(test.TestCase): def test_fetch_unknown_name(self): s = self._get_storage() - with self.assertRaisesRegexp(exceptions.NotFound, - "^Name 'xxx' is not mapped"): - s.fetch('xxx') + self.assertRaisesRegexp(exceptions.NotFound, + "^Name 'xxx' is not mapped", + s.fetch, 'xxx') def test_default_task_progress(self): s = self._get_storage() @@ -230,8 +226,7 @@ class StorageTest(test.TestCase): s.add_task('42', 'my task') name = 'my result' s.set_result_mapping('42', {name: None}) - with self.assertRaises(exceptions.NotFound): - s.get(name) + self.assertRaises(exceptions.NotFound, s.get, name) self.assertEqual(s.fetch_all(), {}) def test_save_multiple_results(self): @@ -297,8 +292,8 @@ class StorageTest(test.TestCase): def test_fetch_not_found_args(self): s = self._get_storage() s.inject({'foo': 'bar', 'spam': 'eggs'}) - with self.assertRaises(exceptions.NotFound): - s.fetch_mapped_args({'viking': 'helmet'}) + self.assertRaises(exceptions.NotFound, + s.fetch_mapped_args, {'viking': 'helmet'}) def test_set_and_get_task_state(self): s = self._get_storage() @@ -309,8 +304,8 @@ class StorageTest(test.TestCase): def test_get_state_of_unknown_task(self): s = self._get_storage() - with self.assertRaisesRegexp(exceptions.NotFound, '^Unknown'): - s.get_task_state('42') + self.assertRaisesRegexp(exceptions.NotFound, '^Unknown', + s.get_task_state, '42') def test_task_by_name(self): s = self._get_storage() @@ -319,9 +314,9 @@ class StorageTest(test.TestCase): def test_unknown_task_by_name(self): s = self._get_storage() - with self.assertRaisesRegexp(exceptions.NotFound, - '^Unknown task name:'): - s.get_uuid_by_name('42') + self.assertRaisesRegexp(exceptions.NotFound, + '^Unknown task name:', + s.get_uuid_by_name, '42') def test_initial_flow_state(self): s = self._get_storage() @@ -348,9 +343,8 @@ class StorageTest(test.TestCase): s.save('42', {}) mocked_warning.assert_called_once_with( mock.ANY, 'my task', 'key', 'result') - with self.assertRaisesRegexp(exceptions.NotFound, - '^Unable to find result'): - s.fetch('result') + self.assertRaisesRegexp(exceptions.NotFound, + '^Unable to find result', s.fetch, 'result') @mock.patch.object(storage.LOG, 'warning') def test_empty_result_is_checked(self, mocked_warning): @@ -360,9 +354,8 @@ class StorageTest(test.TestCase): s.save('42', ()) mocked_warning.assert_called_once_with( mock.ANY, 'my task', 0, 'a') - with self.assertRaisesRegexp(exceptions.NotFound, - '^Unable to find result'): - s.fetch('a') + self.assertRaisesRegexp(exceptions.NotFound, + '^Unable to find result', s.fetch, 'a') @mock.patch.object(storage.LOG, 'warning') def test_short_result_is_checked(self, mocked_warning): @@ -373,9 +366,8 @@ class StorageTest(test.TestCase): mocked_warning.assert_called_once_with( mock.ANY, 'my task', 1, 'b') self.assertEqual(s.fetch('a'), 'result') - with self.assertRaisesRegexp(exceptions.NotFound, - '^Unable to find result'): - s.fetch('b') + self.assertRaisesRegexp(exceptions.NotFound, + '^Unable to find result', s.fetch, 'b') @mock.patch.object(storage.LOG, 'warning') def test_multiple_providers_are_checked(self, mocked_warning): diff --git a/taskflow/tests/unit/test_suspend_flow.py b/taskflow/tests/unit/test_suspend_flow.py index 1a53ba8..2a88fc0 100644 --- a/taskflow/tests/unit/test_suspend_flow.py +++ b/taskflow/tests/unit/test_suspend_flow.py @@ -132,8 +132,7 @@ class SuspendFlowTest(utils.EngineTestBase): ['a', 'b', 'c reverted(Failure: RuntimeError: Woot!)', 'b reverted(5)']) - with self.assertRaisesRegexp(RuntimeError, '^Woot'): - engine.run() + self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run) self.assertEqual(engine.storage.get_flow_state(), states.REVERTED) self.assertEqual( self.values, @@ -155,8 +154,7 @@ class SuspendFlowTest(utils.EngineTestBase): # pretend we are resuming engine2 = self._make_engine(flow, engine.storage._flowdetail) - with self.assertRaisesRegexp(RuntimeError, '^Woot'): - engine2.run() + self.assertRaisesRegexp(RuntimeError, '^Woot', engine2.run) self.assertEqual(engine2.storage.get_flow_state(), states.REVERTED) self.assertEqual( self.values, @@ -182,8 +180,7 @@ class SuspendFlowTest(utils.EngineTestBase): AutoSuspendingTaskOnRevert(self.values, 'b') ) engine2 = self._make_engine(flow2, engine.storage._flowdetail) - with self.assertRaisesRegexp(RuntimeError, '^Woot'): - engine2.run() + self.assertRaisesRegexp(RuntimeError, '^Woot', engine2.run) self.assertEqual(engine2.storage.get_flow_state(), states.REVERTED) self.assertEqual( self.values, @@ -207,8 +204,7 @@ class SuspendFlowTest(utils.EngineTestBase): engine.storage.get_uuid_by_name(engine.storage.injector_name), None, states.FAILURE) - with self.assertRaises(exc.MissingDependencies): - engine.run() + self.assertRaises(exc.MissingDependencies, engine.run) class SingleThreadedEngineTest(SuspendFlowTest, diff --git a/taskflow/tests/unit/test_task.py b/taskflow/tests/unit/test_task.py index 2dc431e..727ae3f 100644 --- a/taskflow/tests/unit/test_task.py +++ b/taskflow/tests/unit/test_task.py @@ -71,8 +71,8 @@ class TaskTestCase(test.TestCase): self.assertEqual(my_task.save_as, {'food': 0}) def test_bad_provides(self): - with self.assertRaisesRegexp(TypeError, '^Task provides'): - MyTask(provides=object()) + self.assertRaisesRegexp(TypeError, '^Task provides', + MyTask, provides=object()) def test_requires_by_default(self): my_task = MyTask() @@ -100,8 +100,9 @@ class TaskTestCase(test.TestCase): }) def test_requires_explicit_not_enough(self): - with self.assertRaisesRegexp(ValueError, '^Missing arguments'): - MyTask(auto_extract=False, requires=('spam', 'eggs')) + self.assertRaisesRegexp(ValueError, '^Missing arguments', + MyTask, + auto_extract=False, requires=('spam', 'eggs')) def test_requires_ignores_optional(self): my_task = DefaultArgTask() @@ -128,8 +129,8 @@ class TaskTestCase(test.TestCase): }) def test_rebind_unknown(self): - with self.assertRaisesRegexp(ValueError, '^Extra arguments'): - MyTask(rebind={'foo': 'bar'}) + self.assertRaisesRegexp(ValueError, '^Extra arguments', + MyTask, rebind={'foo': 'bar'}) def test_rebind_unknown_kwargs(self): task = KwargsTask(rebind={'foo': 'bar'}) @@ -155,8 +156,8 @@ class TaskTestCase(test.TestCase): }) def test_rebind_list_more(self): - with self.assertRaisesRegexp(ValueError, '^Extra arguments'): - MyTask(rebind=('a', 'b', 'c', 'd')) + self.assertRaisesRegexp(ValueError, '^Extra arguments', + MyTask, rebind=('a', 'b', 'c', 'd')) def test_rebind_list_more_kwargs(self): task = KwargsTask(rebind=('a', 'b', 'c')) @@ -167,8 +168,8 @@ class TaskTestCase(test.TestCase): }) def test_rebind_list_bad_value(self): - with self.assertRaisesRegexp(TypeError, '^Invalid rebind value:'): - MyTask(rebind=object()) + self.assertRaisesRegexp(TypeError, '^Invalid rebind value:', + MyTask, rebind=object()) def test_default_provides(self): task = DefaultProvidesTask() diff --git a/taskflow/tests/unit/test_utils.py b/taskflow/tests/unit/test_utils.py index 2580640..82708e3 100644 --- a/taskflow/tests/unit/test_utils.py +++ b/taskflow/tests/unit/test_utils.py @@ -227,13 +227,16 @@ class AttrDictTest(test.TestCase): self.assertEqual(attrs, dict(obj)) def test_runtime_invalid_set(self): + + def bad_assign(obj): + obj._123 = 'b' + attrs = { 'a': 1, } obj = misc.AttrDict(**attrs) self.assertEqual(obj.a, 1) - with self.assertRaises(AttributeError): - obj._123 = 'b' + self.assertRaises(AttributeError, bad_assign, obj) def test_bypass_get(self): attrs = { @@ -243,14 +246,17 @@ class AttrDictTest(test.TestCase): self.assertEqual(1, obj['a']) def test_bypass_set_no_get(self): + + def bad_assign(obj): + obj._b = 'e' + attrs = { 'a': 1, } obj = misc.AttrDict(**attrs) self.assertEqual(1, obj['a']) obj['_b'] = 'c' - with self.assertRaises(AttributeError): - obj._b = 'e' + self.assertRaises(AttributeError, bad_assign, obj) self.assertEqual('c', obj['_b']) diff --git a/taskflow/tests/unit/test_utils_failure.py b/taskflow/tests/unit/test_utils_failure.py index e06466d..841b980 100644 --- a/taskflow/tests/unit/test_utils_failure.py +++ b/taskflow/tests/unit/test_utils_failure.py @@ -75,8 +75,7 @@ class CaptureFailureTestCase(test.TestCase, GeneralFailureObjTestsMixin): self.assertIs(exc_info[1], self.fail_obj.exception) def test_reraises(self): - with self.assertRaisesRegexp(RuntimeError, '^Woot!$'): - self.fail_obj.reraise() + self.assertRaisesRegexp(RuntimeError, '^Woot!$', self.fail_obj.reraise) class ReCreatedFailureTestCase(test.TestCase, GeneralFailureObjTestsMixin): @@ -95,9 +94,8 @@ class ReCreatedFailureTestCase(test.TestCase, GeneralFailureObjTestsMixin): self.assertIs(self.fail_obj.exc_info, None) def test_reraises(self): - with self.assertRaises(exceptions.WrappedFailure) as ctx: - self.fail_obj.reraise() - exc = ctx.exception + exc = self.assertRaises(exceptions.WrappedFailure, + self.fail_obj.reraise) self.assertIs(exc.check(RuntimeError), RuntimeError) @@ -110,31 +108,30 @@ class FailureObjectTestCase(test.TestCase): self.assertRaises(TypeError, misc.Failure) def test_unknown_argument(self): - with self.assertRaises(TypeError) as ctx: - misc.Failure( - exception_str='Woot!', - traceback_str=None, - exc_type_names=['Exception'], - hi='hi there') + exc = self.assertRaises(TypeError, misc.Failure, + exception_str='Woot!', + traceback_str=None, + exc_type_names=['Exception'], + hi='hi there') expected = "Failure.__init__ got unexpected keyword argument(s): hi" - self.assertEqual(str(ctx.exception), expected) + self.assertEqual(str(exc), expected) def test_empty_does_not_reraise(self): self.assertIs(misc.Failure.reraise_if_any([]), None) def test_reraises_one(self): fls = [_captured_failure('Woot!')] - with self.assertRaisesRegexp(RuntimeError, '^Woot!$'): - misc.Failure.reraise_if_any(fls) + self.assertRaisesRegexp(RuntimeError, '^Woot!$', + misc.Failure.reraise_if_any, fls) def test_reraises_several(self): fls = [ _captured_failure('Woot!'), _captured_failure('Oh, not again!') ] - with self.assertRaises(exceptions.WrappedFailure) as ctx: - misc.Failure.reraise_if_any(fls) - self.assertEqual(list(ctx.exception), fls) + exc = self.assertRaises(exceptions.WrappedFailure, + misc.Failure.reraise_if_any, fls) + self.assertEqual(list(exc), fls) def test_failure_copy(self): fail_obj = _captured_failure('Woot!') diff --git a/taskflow/utils/eventlet_utils.py b/taskflow/utils/eventlet_utils.py index 3a91fd6..3d81d10 100644 --- a/taskflow/utils/eventlet_utils.py +++ b/taskflow/utils/eventlet_utils.py @@ -19,11 +19,14 @@ import logging import threading -from eventlet.green import threading as gthreading - -from eventlet import greenpool -from eventlet import patcher -from eventlet import queue +try: + from eventlet.green import threading as gthreading + from eventlet import greenpool + from eventlet import patcher + from eventlet import queue + EVENTLET_AVAILABLE = True +except ImportError: + EVENTLET_AVAILABLE = False from concurrent import futures @@ -93,6 +96,7 @@ class GreenExecutor(futures.Executor): """A greenthread backed executor.""" def __init__(self, max_workers=1000): + assert EVENTLET_AVAILABLE, 'eventlet is needed to use GreenExecutor' assert int(max_workers) > 0, 'Max workers must be greater than zero' self._max_workers = int(max_workers) self._pool = greenpool.GreenPool(self._max_workers) diff --git a/taskflow/utils/misc.py b/taskflow/utils/misc.py index 10f2918..ea046da 100644 --- a/taskflow/utils/misc.py +++ b/taskflow/utils/misc.py @@ -32,6 +32,7 @@ from taskflow.utils import reflection LOG = logging.getLogger(__name__) +NUMERIC_TYPES = tuple(list(six.integer_types) + [float]) def wraps(fn): @@ -81,19 +82,29 @@ def is_valid_attribute_name(name, allow_self=False, allow_hidden=False): return False # Make the name just be a simple string in latin-1 encoding in python3 name = six.b(name) - if not allow_self and name.lower().startswith('self'): + if not allow_self and name.lower().startswith(six.b('self')): return False - if not allow_hidden and name.startswith("_"): + if not allow_hidden and name.startswith(six.b("_")): return False # See: http://docs.python.org/release/2.5.2/ref/grammar.txt (or newer) # # Python identifiers should start with a letter. - if not name[0].isalpha(): - return False + if isinstance(name[0], six.integer_types): + if not chr(name[0]).isalpha(): + return False + else: + if not name[0].isalpha(): + return False for i in range(1, len(name)): + symbol = name[i] # The rest of an attribute name follows: (letter | digit | "_")* - if not (name[i].isalpha() or name[i].isdigit() or name[i] == "_"): - return False + if isinstance(symbol, six.integer_types): + symbol = chr(symbol) + if not (symbol.isalpha() or symbol.isdigit() or symbol == "_"): + return False + else: + if not (symbol.isalpha() or symbol.isdigit() or symbol == "_"): + return False return True @@ -108,7 +119,6 @@ class AttrDict(dict): if not is_valid_attribute_name(name): return False # Make the name just be a simple string in latin-1 encoding in python3 - name = six.b(name) if name in cls.NO_ATTRS: return False return True diff --git a/taskflow/utils/persistence_utils.py b/taskflow/utils/persistence_utils.py index e5dbd34..aff635e 100644 --- a/taskflow/utils/persistence_utils.py +++ b/taskflow/utils/persistence_utils.py @@ -207,7 +207,7 @@ def _format_meta(metadata, indent): for (k, v) in metadata.items(): # Progress for now is a special snowflake and will be formatted # in percent format. - if k == 'progress' and isinstance(v, (float, int, long)): + if k == 'progress' and isinstance(v, misc.NUMERIC_TYPES): v = "%0.2f%%" % (v * 100.0) lines.append("%s+ %s = %s" % (" " * (indent + 2), k, v)) return lines diff --git a/test-2.x-requirements.txt b/test-2.x-requirements.txt new file mode 100644 index 0000000..a6a628e --- /dev/null +++ b/test-2.x-requirements.txt @@ -0,0 +1 @@ +eventlet>=0.13.0 diff --git a/test-requirements.txt b/test-requirements.txt index 092a759..eb05586 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,15 +1,6 @@ -# Install bounded pep8/pyflakes first, then let flake8 install -pep8==1.4.5 -pyflakes>=0.7.2,<0.7.4 -flake8==2.0 hacking>=0.5.6,<0.8 - +discover coverage>=3.6 mock>=1.0 -nose -nose-exclude -openstack.nose_plugin>=0.7 -pylint==0.25.2 - -# Needed for features in 2.7 not in 2.6 -unittest2 +testrepository>=0.0.17 +testtools>=0.9.32 @@ -10,15 +10,19 @@ setenv = VIRTUAL_ENV={envdir} LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=C - NOSE_WITH_OPENSTACK=1 - NOSE_OPENSTACK_COLOR=1 - NOSE_OPENSTACK_RED=0.05 - NOSE_OPENSTACK_YELLOW=0.025 - NOSE_OPENSTACK_SHOW_ELAPSED=1 - NOSE_OPENSTACK_STDOUT=1 deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt -commands = nosetests {posargs} +commands = python setup.py testr --slowest --testr-args='{posargs}' + +[testenv:py26] +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + -r{toxinidir}/test-2.x-requirements.txt + +[testenv:py27] +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + -r{toxinidir}/test-2.x-requirements.txt [tox:jenkins] downloadcache = ~/cache/pip @@ -34,7 +38,7 @@ deps = -r{toxinidir}/requirements.txt commands = pylint [testenv:cover] -setenv = NOSE_WITH_COVERAGE=1 +commands = python setup.py testr --coverage --testr-args='{posargs}' [testenv:venv] commands = {posargs} |