def test_string_callable_to_runnable_tgt(self):
        """TODO."""
        import sys

        task = Task("callable_to_runnable", module=__name__)
        materialized_task_result(task, sys.maxsize)

        task = Task("callable_to_runnable_with_arg",
                    module=__name__,
                    args=(2, ))
        materialized_task_result(task, 2)

        task = Task(
            "callable_to_runnable",
            module="tests.unit.testplan.runners.pools.tasks.data.sample_tasks",
            args=(2, ),
        )
        materialized_task_result(task, 4)

        task = Task(
            "sample_tasks.callable_to_adapted_runnable",
            module="tests.unit.testplan.runners.pools.tasks.data.relative",
            args=(2, ),
        )
        materialized_task_result(task, 4)
Exemple #2
0
def test_multi_parts_invalid_parameter_1():
    """
    Execute MultiTest parts with invalid parameters that a part of
    MultiTest has been scheduled twice.
    """
    plan = Testplan(name='plan',
                    parse_cmdline=False,
                    merge_scheduled_parts=True)
    pool = ThreadPool(name='MyPool', size=2)
    plan.add_resource(pool)

    for idx in range(3):
        task = Task(target=get_mtest(part_tuple=(idx, 3)))
        plan.schedule(task, resource='MyPool')
    plan.schedule(Task(target=get_mtest(part_tuple=(1, 3))), resource='MyPool')

    with log_propagation_disabled(TESTPLAN_LOGGER):
        assert plan.run().run is False

    assert len(plan.report.entries) == 1
    assert len(plan.report.entries[0].entries) == 2
    assert plan.report.status == Status.ERROR  # Testplan result
    assert plan.report.entries[0].status == Status.ERROR  # Multitest
    assert plan.report.entries[0].entries[0].status == Status.FAILED  # Suite1
    assert plan.report.entries[0].entries[1].status == Status.FAILED  # Suite2
    assert 'invalid parameter of part provided' in \
           plan.report.entries[0].logs[0]['message']
    def test_string_runnable_tgt_other_module(self):
        """TODO."""
        task = Task(
            "Multiplier",
            module="tests.unit.testplan.runners.pools.tasks.data.sample_tasks",
            args=(4, ),
        )
        materialized_task_result(task, 8)

        task = Task(
            "Wrapper.InnerMultiplier",
            module="tests.unit.testplan.runners.pools.tasks.data.sample_tasks",
            args=(5, ),
        )
        materialized_task_result(task, 10)

        task = Task(
            "sample_tasks.Multiplier",
            module="tests.unit.testplan.runners.pools.tasks.data.relative",
            args=(4, ),
            kwargs={"multiplier": 3},
        )
        materialized_task_result(task, 12)

        task = Task(
            "sample_tasks.Wrapper.InnerMultiplier",
            module="tests.unit.testplan.runners.pools.tasks.data.relative",
            args=(5, ),
            kwargs={"multiplier": 3},
        )
        materialized_task_result(task, 15)
def test_multi_parts_merged():
    """
    Schedule MultiTest parts in different ways, execute them and merge reports.
    """
    plan = TestplanMock(name="plan", merge_scheduled_parts=True)
    pool = ThreadPool(name="MyThreadPool", size=2)
    plan.add_resource(pool)

    def _get_mtest():
        return MultiTest(name="MTest",
                         suites=[Suite1(), Suite2()],
                         part=(2, 3))

    plan.add(target=_get_mtest())  # local_runner
    plan.add(Task(target=get_mtest(part_tuple=(1, 3))))  # local_runner
    plan.schedule(Task(target=get_mtest(part_tuple=(0, 3))),
                  resource="MyThreadPool")

    assert plan.run().run is True

    assert len(plan.report.entries) == 1
    assert plan.report.entries[0].name == "MTest"
    assert len(plan.report.entries[0].entries) == 2  # 2 suites
    assert plan.report.entries[0].entries[0].name == "Suite1"
    assert plan.report.entries[0].entries[1].name == "Suite2"
    assert len(plan.report.entries[0].entries[0].entries) == 1  # param group
    assert plan.report.entries[0].entries[0].entries[0].name == "test_true"
    assert len(plan.report.entries[0].entries[1].entries) == 1  # param group
    assert plan.report.entries[0].entries[1].entries[0].name == "test_false"
    assert len(plan.report.entries[0].entries[0].entries[0].entries) == 10
    assert len(plan.report.entries[0].entries[1].entries[0].entries) == 3
    def test_path_usage(self):  # pylint: disable=R0201
        """TODO."""
        dirname = os.path.dirname(os.path.abspath(__file__))
        path = os.path.join(dirname, "data")

        task = Task("Multiplier",
                    module="relative.sample_tasks",
                    args=(4, ),
                    path=path)
        materialized_task_result(task, 8)

        task = Task(
            "callable_to_runnable",
            module="relative.sample_tasks",
            args=(2, ),
            path=path,
        )
        materialized_task_result(task, 4)

        path = os.path.join(path, "relative")

        task = Task("Multiplier", module="sample_tasks", args=(4, ), path=path)
        materialized_task_result(task, 8)

        task = Task("callable_to_runnable",
                    module="sample_tasks",
                    args=(2, ),
                    path=path)
        materialized_task_result(task, 4)
def test_multi_parts_duplicate_part():
    """
    Execute MultiTest parts with a part of MultiTest has been
    scheduled twice and automatically be filtered out.
    """
    plan = TestplanMock(name="plan", merge_scheduled_parts=True)
    pool = ThreadPool(name="MyThreadPool", size=2)
    plan.add_resource(pool)

    for idx in range(3):
        task = Task(target=get_mtest_with_custom_uid(part_tuple=(idx, 3)))
        plan.schedule(task, resource="MyThreadPool")

    task = Task(target=get_mtest_with_custom_uid(part_tuple=(1, 3)))
    plan.schedule(task, resource="MyThreadPool")

    assert len(plan._tests) == 4

    assert plan.run().run is False

    assert len(plan.report.entries) == 5  # one placeholder report & 4 siblings
    assert len(plan.report.entries[0].entries) == 0  # already cleared
    assert plan.report.status == Status.ERROR  # Testplan result
    assert ("duplicate MultiTest parts had been scheduled"
            in plan.report.entries[0].logs[0]["message"])
def test_multi_parts_incorrect_schedule():
    """
    Execute MultiTest parts with invalid parameters that a MultiTest
    has been scheduled twice and each time split into different parts.
    """
    plan = TestplanMock(name="plan", merge_scheduled_parts=True)
    pool = ThreadPool(name="MyThreadPool", size=2)
    plan.add_resource(pool)

    for idx in range(3):
        task = Task(target=get_mtest(part_tuple=(idx, 3)))
        plan.schedule(task, resource="MyThreadPool")

    for idx in range(2):
        task = Task(target=get_mtest(part_tuple=(idx, 2)))
        plan.schedule(task, resource="MyThreadPool")

    assert len(plan._tests) == 5

    assert plan.run().run is False

    assert len(plan.report.entries) == 6  # one placeholder report & 5 siblings
    assert len(plan.report.entries[0].entries) == 0  # already cleared
    assert plan.report.status == Status.ERROR  # Testplan result
    assert ("invalid parameter of part provided"
            in plan.report.entries[0].logs[0]["message"])
Exemple #8
0
    def test_basic_init(self):
        """TODO."""
        task = Task("Runnable", module=__name__)
        result = "result"
        for task_result in (
                TaskResult(task, result, True, None, None),
                TaskResult(task, result, True, "msg", None),
                TaskResult(task, result, True, None,
                           [Task("Runnable", module=__name__)]),
        ):
            serialized = task_result.dumps()
            new_task_result = TaskResult().loads(serialized)

            for attr in ("_result", "status", "_reason", "_uid"):
                task_result_attr = getattr(task_result, attr)
                new_task_result_attr = getattr(new_task_result, attr)
                assert task_result_attr == new_task_result_attr

            assert task_result._task._uid == new_task_result._task._uid
            if task_result.follow is None:
                assert task_result.follow == new_task_result.follow
            else:
                assert len(task_result.follow) == len(new_task_result.follow)
                for idx, task in enumerate(task_result.follow):
                    assert task._uid == new_task_result.follow[idx]._uid
Exemple #9
0
def test_multi_parts_invalid_parameter_2():
    """
    Execute MultiTest parts with invalid parameters that a MultiTest
    has been scheduled twice.
    """
    plan = TestplanMock(name="plan", merge_scheduled_parts=True)
    pool = ThreadPool(name="MyThreadPool", size=2)
    plan.add_resource(pool)

    for idx in range(3):
        task = Task(target=get_mtest(part_tuple=(idx, 3)))
        plan.schedule(task, resource="MyThreadPool")
    for idx in range(2):
        task = Task(target=get_mtest(part_tuple=(idx, 2)))
        plan.schedule(task, resource="MyThreadPool")

    with log_propagation_disabled(TESTPLAN_LOGGER):
        assert plan.run().run is False

    assert len(plan.report.entries) == 1
    assert len(plan.report.entries[0].entries) == 2
    assert plan.report.status == Status.ERROR  # Testplan result
    assert plan.report.entries[0].status == Status.ERROR  # Multitest
    assert plan.report.entries[0].entries[0].status == Status.FAILED  # Suite1
    assert plan.report.entries[0].entries[1].status == Status.FAILED  # Suite2
    assert ("invalid parameter of part provided"
            in plan.report.entries[0].logs[0]["message"])
Exemple #10
0
    def test_callable_to_non_runnable_tgt(self):
        """TODO."""
        from .data.relative import sample_tasks

        for task in (
                Task(callable_to_non_runnable),
                Task(sample_tasks.callable_to_non_runnable, args=(2, )),
                Task(
                    "multiply",
                    module=
                    ("tests.unit.testplan.runners.pools.tasks.data.sample_tasks"
                     ),
                    args=(2, ),
                ),
                Task(
                    "sample_tasks.multiply",
                    module=
                    "tests.unit.testplan.runners.pools.tasks.data.relative",
                    args=(2, ),
                ),
        ):
            try:
                task.materialize()
                raise Exception("Should raise.")
            except RuntimeError as exc:
                assert "must have both `run` and `uid` methods" in str(exc)
Exemple #11
0
def test_runner_timeout():
    """
    Execute MultiTests in LocalRunner, ThreadPool and ProcessPool respectively.
    Some of them will timeout and we'll get a report showing execution details.
    """
    plan = Testplan(name='plan',
                    parse_cmdline=False,
                    runnable=MyTestRunner,
                    timeout=60,
                    abort_wait_timeout=5)
    mod_path = os.path.dirname(os.path.abspath(__file__))

    THREAD_POOL = 'MyThreadPool'
    PROCESS_POOL = 'MyProcessPool'
    thread_pool = ThreadPool(name=THREAD_POOL, size=2, worker_heartbeat=None)
    proc_pool = ProcessPool(name=PROCESS_POOL, size=2, worker_heartbeat=None)
    plan.add_resource(thread_pool)
    plan.add_resource(proc_pool)
    plan.add(func_basic_tasks.get_mtest1())
    plan.add(func_basic_tasks.get_mtest2())

    task3 = Task(target='get_mtest3', module='func_basic_tasks', path=mod_path)
    task4 = Task(target='get_mtest4', module='func_basic_tasks', path=mod_path)
    task5 = Task(target='get_mtest5', module='func_basic_tasks', path=mod_path)
    task6 = Task(target='get_mtest6', module='func_basic_tasks', path=mod_path)
    plan.schedule(task3, resource=THREAD_POOL)
    plan.schedule(task4, resource=THREAD_POOL)
    plan.schedule(task5, resource=PROCESS_POOL)
    plan.schedule(task6, resource=PROCESS_POOL)

    with log_propagation_disabled(TESTPLAN_LOGGER):
        assert plan.run().run is False

    assert len(plan.report.entries) == 6
    assert plan.report.status == Status.ERROR

    entries = plan.report.entries
    assert entries[0].name == 'MTest1'
    assert entries[0].status == Status.FAILED  # testcase 'test_true' failed
    assert entries[2].name == 'MTest3'
    assert entries[2].status == Status.PASSED  # testcase 'test_equal' passed
    assert entries[4].name == 'MTest5'
    assert entries[4].status == Status.ERROR  # testcase 'test_contain' raised
    assert entries[1].name == 'MTest2'
    assert entries[1].status == Status.ERROR  # timeout
    assert ' discarding due to ' in entries[1].logs[0]['message']
    assert entries[3].name == 'Task[get_mtest4]'
    assert entries[3].status == Status.ERROR  # timeout
    assert entries[3].logs[0]['message'].startswith('_target: get_mtest4')
    assert ' discarding due to ' in entries[3].logs[1]['message']
    assert entries[5].name == 'Task[get_mtest6]'
    assert entries[5].status == Status.ERROR  # timeout
    assert entries[5].logs[0]['message'].startswith('_target: get_mtest6')
    assert ' discarding due to ' in entries[5].logs[1]['message']
Exemple #12
0
    def test_string_runnable_tgt_same_module(self):
        """TODO."""
        import sys
        task = Task('Runnable', module=__name__)
        materialized_task_result(task, sys.maxsize)

        task = Task('RunnableWithArg', module=__name__, args=(2, ))
        materialized_task_result(task, 2)

        task = Task('RunnableWithArg', module=__name__, kwargs={'number': 3})
        materialized_task_result(task, 3)
Exemple #13
0
 def test_callable_to_non_runnable_tgt(self):  # pylint: disable=R0201
     """TODO."""
     from .data.relative import sample_tasks
     for task in (Task(callable_to_non_runnable),
                  Task(sample_tasks.callable_to_non_runnable, args=(2, )),
                  Task('tasks.data.relative.sample_tasks.multiply',
                       args=(2, ))):
         try:
             task.materialize()
             raise Exception('Should raise.')
         except RuntimeError as exc:
             assert 'must have a .run() method' in str(exc)
Exemple #14
0
    def test_string_runnable_tgt_other_module(self):
        """TODO."""
        task = Task('tasks.data.sample_tasks.Multiplier', args=(4, ))
        materialized_task_result(task, 8)

        task = Task('Multiplier', module='tasks.data.sample_tasks', args=(5, ))
        materialized_task_result(task, 10)

        task = Task('tasks.data.sample_tasks.Multiplier',
                    args=(4, ),
                    kwargs={'multiplier': 3})
        materialized_task_result(task, 12)
Exemple #15
0
    def test_callable_to_none(self):
        """TODO."""
        from .data.relative import sample_tasks

        for task in (
                Task(callable_to_none),
                Task(sample_tasks.callable_to_none),
                Task("tests.unit.testplan.runners.pools.tasks.data"
                     ".relative.sample_tasks.callable_to_none"),
        ):
            try:
                task.materialize()
                raise Exception("Should raise.")
            except TaskMaterializationError as exc:
                assert "Cannot get a valid test object from target" in str(exc)
Exemple #16
0
    def test_runnable_tgt(self):  # pylint: disable=R0201
        """TODO."""
        import sys
        from .data.sample_tasks import Multiplier
        try:
            materialized_task_result(Task(RunnableThatRaises()), 2)
            raise Exception('Should raise')
        except RuntimeError as exc:
            assert 'While running.' == str(exc)

        materialized_task_result(Task(Multiplier(2, 3)), 6)
        materialized_task_result(Task(Runnable()), sys.maxsize)
        materialized_task_result(Task(RunnableWithArg(2)), 2)
        task = Task(RunnableTaskAdaptor(lambda x: x * 2, 3))
        materialized_task_result(task, 6)
Exemple #17
0
    def test_callable_to_runnable_tgt(self):  # pylint: disable=R0201
        """TODO."""
        import sys
        task = Task(callable_to_runnable)
        materialized_task_result(task, sys.maxsize)

        task = Task(callable_to_runnable_with_arg, args=(2, ))
        materialized_task_result(task, 2)

        from .data import sample_tasks
        task = Task(sample_tasks.callable_to_runnable, args=(2, ))
        materialized_task_result(task, 4)

        task = Task(sample_tasks.callable_to_adapted_runnable, args=(2, ))
        materialized_task_result(task, 4)
Exemple #18
0
def test_multi_parts_missing_parts():
    """
    Execute MultiTest parts with invalid parameters that not all
    parts of a MultiTest has been scheduled.
    """
    plan = Testplan(name="plan",
                    parse_cmdline=False,
                    merge_scheduled_parts=True)
    pool = ThreadPool(name="MyPool", size=2)
    plan.add_resource(pool)

    for idx in range(1, 3):
        task = Task(target=get_mtest(part_tuple=(idx, 3)))
        plan.schedule(task, resource="MyPool")

    with log_propagation_disabled(TESTPLAN_LOGGER):
        assert plan.run().run is False

    assert len(plan.report.entries) == 1
    assert len(plan.report.entries[0].entries) == 2
    assert plan.report.status == Status.ERROR  # Testplan result
    assert plan.report.entries[0].status == Status.ERROR  # Multitest
    assert plan.report.entries[0].entries[0].status == Status.PASSED  # Suite1
    assert plan.report.entries[0].entries[1].status == Status.FAILED  # Suite2
    assert ("not all MultiTest parts had been scheduled"
            in plan.report.entries[0].logs[0]["message"])
Exemple #19
0
    def test_serialize(self):
        """TODO."""
        import sys
        task = Task('Runnable', module=__name__)
        materialized_task_result(task, sys.maxsize, serialize=True)

        task = Task('RunnableWithArg', module=__name__, args=(2, ))
        materialized_task_result(task, 2, serialize=True)

        task = Task('RunnableWithArg', module=__name__, kwargs={'number': 3})
        materialized_task_result(task, 3, serialize=True)

        dirname = os.path.dirname(os.path.abspath(__file__))
        path = os.path.join(dirname, 'data', 'relative')
        task = Task('Multiplier', module='sample_tasks', args=(4, ), path=path)
        materialized_task_result(task, 8, serialize=True)
Exemple #20
0
def test_multi_parts_merged():
    """Execute MultiTest parts and merge report."""
    plan = Testplan(name='plan',
                    parse_cmdline=False,
                    merge_scheduled_parts=True)
    pool = ThreadPool(name='MyPool', size=2)
    plan.add_resource(pool)

    for idx in range(3):
        task = Task(target=get_mtest(part_tuple=(idx, 3)))
        plan.schedule(task, resource='MyPool')

    with log_propagation_disabled(TESTPLAN_LOGGER):
        assert plan.run().run is True

    assert len(plan.report.entries) == 1
    assert plan.report.entries[0].name == 'MTest'
    assert len(plan.report.entries[0].entries) == 2  # 2 suites
    assert plan.report.entries[0].entries[0].name == 'Suite1'
    assert plan.report.entries[0].entries[1].name == 'Suite2'
    assert len(plan.report.entries[0].entries[0].entries) == 1  # param group
    assert plan.report.entries[0].entries[0].entries[0].name == 'test_true'
    assert len(plan.report.entries[0].entries[1].entries) == 1  # param group
    assert plan.report.entries[0].entries[1].entries[0].name == 'test_false'
    assert len(plan.report.entries[0].entries[0].entries[0].entries) == 10
    assert len(plan.report.entries[0].entries[1].entries[0].entries) == 3
Exemple #21
0
def test_multi_parts_not_merged():
    """Execute MultiTest parts but do not merge report."""
    plan = TestplanMock(name="plan", merge_scheduled_parts=False)
    pool = ThreadPool(name="MyThreadPool", size=2)
    plan.add_resource(pool)

    for idx in range(3):
        task = Task(target=get_mtest(part_tuple=(idx, 3)))
        plan.schedule(task, resource="MyThreadPool")

    with log_propagation_disabled(TESTPLAN_LOGGER):
        assert plan.run().run is True

    assert len(plan.report.entries) == 3
    assert plan.report.entries[0].name == "MTest - part(1/3)"
    assert plan.report.entries[1].name == "MTest - part(2/3)"
    assert plan.report.entries[2].name == "MTest - part(3/3)"
    assert len(plan.report.entries[0].entries) == 2  # 2 suites
    assert plan.report.entries[0].entries[0].name == "Suite1"
    assert plan.report.entries[0].entries[1].name == "Suite2"
    assert len(plan.report.entries[0].entries[0].entries) == 1  # param group
    assert plan.report.entries[0].entries[0].entries[0].name == "test_true"
    assert len(plan.report.entries[0].entries[1].entries) == 1  # param group
    assert plan.report.entries[0].entries[1].entries[0].name == "test_false"
    assert len(plan.report.entries[0].entries[0].entries[0].entries) == 4
    assert len(plan.report.entries[0].entries[1].entries[0].entries) == 1
Exemple #22
0
def materialized_task_result(task, expected, serialize=False):
    """TODO."""
    assert isinstance(task, Task)
    materialized = task.materialize()
    if serialize is True:
        serialized = task.dumps()
        task = Task().loads(serialized)
    assert materialized.run() == expected
Exemple #23
0
    def test_string_callable_to_runnable_tgt(self):  # pylint: disable=R0201
        """TODO."""
        import sys
        task = Task('callable_to_runnable', module=__name__)
        materialized_task_result(task, sys.maxsize)

        task = Task('callable_to_runnable_with_arg',
                    module=__name__,
                    args=(2, ))
        materialized_task_result(task, 2)

        task = Task('tasks.data.sample_tasks.callable_to_runnable', args=(2, ))
        materialized_task_result(task, 4)

        task = Task('tasks.data.sample_tasks.callable_to_adapted_runnable',
                    args=(2, ))
        materialized_task_result(task, 4)
Exemple #24
0
 def test_non_runnable_tgt(self):  # pylint: disable=R0201
     """TODO."""
     for task in (NonRunnableObject, NonRunnableObject(),
                  NonRunnableObjectRunAttr, NonRunnableObjectRunAttr()):
         try:
             Task(task).materialize()
         except RuntimeError as exc:
             assert 'must have a .run() method' in str(exc)
Exemple #25
0
 def test_raise_on_serialization(self):
     """TODO."""
     try:
         task = Task(RunnableTaskAdaptor(lambda x: x * 2, 3))
         materialized_task_result(task, 6, serialize=True)
         raise Exception("Should raise.")
     except TaskSerializationError:
         pass
Exemple #26
0
    def test_serialize(self):
        """TODO."""
        import sys

        task = Task("Runnable", module=__name__)
        materialized_task_result(task, sys.maxsize, serialize=True)

        task = Task("RunnableWithArg", module=__name__, args=(2, ))
        materialized_task_result(task, 2, serialize=True)

        task = Task("RunnableWithArg", module=__name__, kwargs={"number": 3})
        materialized_task_result(task, 3, serialize=True)

        dirname = os.path.dirname(os.path.abspath(__file__))
        path = os.path.join(dirname, "data", "relative")
        task = Task("Multiplier", module="sample_tasks", args=(4, ), path=path)
        materialized_task_result(task, 8, serialize=True)
Exemple #27
0
    def test_raise_on_deserialization(self):
        """TODO."""
        # To add a case of a serializable but not
        # deserializable task.

        try:
            Task().loads(None)
            raise Exception("Should raise.")
        except TaskDeserializationError:
            pass
Exemple #28
0
    def test_path_usage(self):  # pylint: disable=R0201
        """TODO."""
        dirname = os.path.dirname(os.path.abspath(__file__))
        path = os.path.join(dirname, 'data', 'relative')

        task = Task('sample_tasks.Multiplier', args=(4, ), path=path)
        materialized_task_result(task, 8)

        task = Task('Multiplier', module='sample_tasks', args=(4, ), path=path)
        materialized_task_result(task, 8)

        task = Task('sample_tasks.callable_to_runnable', args=(2, ), path=path)
        materialized_task_result(task, 4)

        task = Task('callable_to_runnable',
                    args=(2, ),
                    module='sample_tasks',
                    path=path)
        materialized_task_result(task, 4)
Exemple #29
0
 def test_non_runnable_tgt(self):
     """TODO."""
     for task in (
             NonRunnableObject,
             NonRunnableObject(),
             NonRunnableObjectRunAttr,
             NonRunnableObjectRunAttr(),
     ):
         try:
             Task(task).materialize()
         except RuntimeError as exc:
             assert "must have both `run` and `uid` methods" in str(exc)
    def test_basic_init(self):
        """TODO."""
        task = Task('Runnable', module=__name__)
        result = 'result'
        for task_result in (TaskResult(task, result, True, None, None),
                            TaskResult(task, result, True, 'msg', None),
                            TaskResult(task, result, True, None,
                                       [Task('Runnable', module=__name__)])):
            serialized = task_result.dumps()
            new_task_result = TaskResult().loads(serialized)

            for attr in ('_result', 'status', '_reason', '_uid'):
                task_result_attr = getattr(task_result, attr)
                new_task_result_attr = getattr(new_task_result, attr)
                assert task_result_attr == new_task_result_attr

            assert task_result._task._uid == new_task_result._task._uid
            if task_result.follow is None:
                assert task_result.follow == new_task_result.follow
            else:
                assert len(task_result.follow) == len(new_task_result.follow)
                for idx, task in enumerate(task_result.follow):
                    assert task._uid == new_task_result.follow[idx]._uid