Beispiel #1
0
 def test_dependency_cycle_self(self):
     # This tests dependency loops
     # A -> A
     A = make_job(id='A', depends='A')
     job_list = [A]
     with self.assertRaises(DependencyCycleError) as call:
         DependencySolver.resolve_dependencies(job_list)
     self.assertEqual(call.exception.job_list, [A, A])
Beispiel #2
0
 def test_duplicate_error(self):
     A = make_job('A')
     another_A = make_job('A')
     job_list = [A, another_A]
     with self.assertRaises(DependencyDuplicateError) as call:
         DependencySolver.resolve_dependencies(job_list)
     self.assertIs(call.exception.job, A)
     self.assertIs(call.exception.duplicate_job, another_A)
 def test_dependency_cycle_self(self):
     # This tests dependency loops
     # A -> A
     A = make_job(name='A', depends='A')
     job_list = [A]
     with self.assertRaises(DependencyCycleError) as call:
         DependencySolver.resolve_dependencies(job_list)
     self.assertEqual(call.exception.job_list, [A, A])
 def test_duplicate_error(self):
     A = make_job('A')
     another_A = make_job('A')
     job_list = [A, another_A]
     with self.assertRaises(DependencyDuplicateError) as call:
         DependencySolver.resolve_dependencies(job_list)
     self.assertIs(call.exception.job, A)
     self.assertIs(call.exception.duplicate_job, another_A)
Beispiel #5
0
 def test_dependency_cycle_via_resource(self):
     # This tests dependency loops
     # A -> R -> A
     A = make_job(id='A', requires='R.key == "value"')
     R = make_job(id='R', depends='A', plugin="resource")
     job_list = [A, R]
     with self.assertRaises(DependencyCycleError) as call:
         DependencySolver.resolve_dependencies(job_list)
     self.assertEqual(call.exception.job_list, [A, R, A])
 def test_dependency_cycle_via_resource(self):
     # This tests dependency loops
     # A -> R -> A
     A = make_job(name='A', requires='R.key == "value"')
     R = make_job(name='R', depends='A', plugin="resource")
     job_list = [A, R]
     with self.assertRaises(DependencyCycleError) as call:
         DependencySolver.resolve_dependencies(job_list)
     self.assertEqual(call.exception.job_list, [A, R, A])
Beispiel #7
0
 def test_missing_direct_dependency(self):
     # This tests missing dependencies
     # A -> (inexisting B)
     A = make_job(id='A', depends='B')
     job_list = [A]
     with self.assertRaises(DependencyMissingError) as call:
         DependencySolver.resolve_dependencies(job_list)
     self.assertIs(call.exception.job, A)
     self.assertEqual(call.exception.missing_job_id, 'B')
     self.assertEqual(call.exception.dep_type,
                      call.exception.DEP_TYPE_DIRECT)
Beispiel #8
0
 def test_missing_resource_dependency(self):
     # This tests missing resource dependencies
     # A ~> (inexisting R)
     A = make_job(id='A', requires='R.attr == "value"')
     job_list = [A]
     with self.assertRaises(DependencyMissingError) as call:
         DependencySolver.resolve_dependencies(job_list)
     self.assertIs(call.exception.job, A)
     self.assertEqual(call.exception.missing_job_id, 'R')
     self.assertEqual(call.exception.dep_type,
                      call.exception.DEP_TYPE_RESOURCE)
 def test_missing_direct_dependency(self):
     # This tests missing dependencies
     # A -> (inexisting B)
     A = make_job(name='A', depends='B')
     job_list = [A]
     with self.assertRaises(DependencyMissingError) as call:
         DependencySolver.resolve_dependencies(job_list)
     self.assertIs(call.exception.job, A)
     self.assertEqual(call.exception.missing_job_name, 'B')
     self.assertEqual(call.exception.dep_type,
                      call.exception.DEP_TYPE_DIRECT)
Beispiel #10
0
 def test_dependency_cycle_longer(self):
     # This tests dependency loops
     # A -> B -> C -> D -> B
     A = make_job(name='A', depends='B')
     B = make_job(name='B', depends='C')
     C = make_job(name='C', depends='D')
     D = make_job(name='D', depends='B')
     job_list = [A, B, C, D]
     with self.assertRaises(DependencyCycleError) as call:
         DependencySolver.resolve_dependencies(job_list)
     self.assertEqual(call.exception.job_list, [B, C, D, B])
Beispiel #11
0
 def test_dependency_cycle_longer(self):
     # This tests dependency loops
     # A -> B -> C -> D -> B
     A = make_job(id='A', depends='B')
     B = make_job(id='B', depends='C')
     C = make_job(id='C', depends='D')
     D = make_job(id='D', depends='B')
     job_list = [A, B, C, D]
     with self.assertRaises(DependencyCycleError) as call:
         DependencySolver.resolve_dependencies(job_list)
     self.assertEqual(call.exception.job_list, [B, C, D, B])
Beispiel #12
0
 def test_missing_resource_dependency(self):
     # This tests missing resource dependencies
     # A ~> (inexisting R)
     A = make_job(name='A', requires='R.attr == "value"')
     job_list = [A]
     with self.assertRaises(DependencyMissingError) as call:
         DependencySolver.resolve_dependencies(job_list)
     self.assertIs(call.exception.job, A)
     self.assertEqual(call.exception.missing_job_name, 'R')
     self.assertEqual(call.exception.dep_type,
                      call.exception.DEP_TYPE_RESOURCE)
Beispiel #13
0
 def test_resource_deps(self):
     # This tests resource deps
     # A ~> R
     A = make_job(name='A', requires='R.foo == "bar"')
     R = make_job(name='R', plugin='resource')
     job_list = [A, R]
     expected = [R, A]
     observed = DependencySolver.resolve_dependencies(job_list)
     self.assertEqual(expected, observed)
Beispiel #14
0
 def test_resource_deps(self):
     # This tests resource deps
     # A ~> R
     A = make_job(id='A', requires='R.foo == "bar"')
     R = make_job(id='R', plugin='resource')
     job_list = [A, R]
     expected = [R, A]
     observed = DependencySolver.resolve_dependencies(job_list)
     self.assertEqual(expected, observed)
Beispiel #15
0
 def test_direct_deps(self):
     # This tests the following simple job chain
     # A -> B -> C
     A = make_job(id='A', depends='B')
     B = make_job(id='B', depends='C')
     C = make_job(id='C')
     job_list = [A, B, C]
     expected = [C, B, A]
     observed = DependencySolver.resolve_dependencies(job_list)
     self.assertEqual(expected, observed)
Beispiel #16
0
 def test_direct_deps(self):
     # This tests the following simple job chain
     # A -> B -> C
     A = make_job(name='A', depends='B')
     B = make_job(name='B', depends='C')
     C = make_job(name='C')
     job_list = [A, B, C]
     expected = [C, B, A]
     observed = DependencySolver.resolve_dependencies(job_list)
     self.assertEqual(expected, observed)
Beispiel #17
0
 def test_visiting_blackend_node(self):
     # This tests a visit to already visited job
     # A
     # B -> A
     # A will be visited twice
     A = make_job(name='A')
     B = make_job(name='B', depends='A')
     job_list = [A, B]
     expected = [A, B]
     observed = DependencySolver.resolve_dependencies(job_list)
     self.assertEqual(expected, observed)
Beispiel #18
0
 def test_visiting_blackend_node(self):
     # This tests a visit to already visited job
     # A
     # B -> A
     # A will be visited twice
     A = make_job(id='A')
     B = make_job(id='B', depends='A')
     job_list = [A, B]
     expected = [A, B]
     observed = DependencySolver.resolve_dependencies(job_list)
     self.assertEqual(expected, observed)
Beispiel #19
0
 def test_independent_groups_deps(self):
     # This tests two independent job chains
     # A1 -> B1
     # A2 -> B2
     A1 = make_job(name='A1', depends='B1')
     B1 = make_job(name='B1',)
     A2 = make_job(name='A2', depends='B2')
     B2 = make_job(name='B2')
     job_list = [A1, B1, A2, B2]
     expected = [B1, A1, B2, A2]
     observed = DependencySolver.resolve_dependencies(job_list)
     self.assertEqual(expected, observed)
Beispiel #20
0
 def test_independent_groups_deps(self):
     # This tests two independent job chains
     # A1 -> B1
     # A2 -> B2
     A1 = make_job(id='A1', depends='B1')
     B1 = make_job(id='B1',)
     A2 = make_job(id='A2', depends='B2')
     B2 = make_job(id='B2')
     job_list = [A1, B1, A2, B2]
     expected = [B1, A1, B2, A2]
     observed = DependencySolver.resolve_dependencies(job_list)
     self.assertEqual(expected, observed)
Beispiel #21
0
    def update_desired_job_list(self, desired_job_list):
        """
        Update the set of desired jobs (that ought to run)

        This method can be used by the UI to recompute the dependency graph.
        The argument 'desired_job_list' is a list of jobs that should run.
        Those jobs must be a sub-collection of the job_list argument that was
        passed to the constructor.

        It never fails although it may reduce the actual permitted
        desired_job_list to an empty list. It returns a list of problems (all
        instances of DependencyError class), one for each job that had to be
        removed.
        """
        # Remember a copy of original desired job list. We may modify this list
        # so let's not mess up data passed by the caller.
        self._desired_job_list = list(desired_job_list)
        # Reset run list just in case desired_job_list is empty
        self._run_list = []
        # Try to solve the dependency graph. This is done in a loop as may need
        # to remove a problematic job and re-try. The loop provides a stop
        # condition as we will eventually run out of jobs.
        problems = []
        while self._desired_job_list:
            # XXX: it might be more efficient to incorporate this 'recovery
            # mode' right into the solver, this way we'd probably save some
            # resources or runtime complexity.
            try:
                self._run_list = DependencySolver.resolve_dependencies(
                    self._job_list, self._desired_job_list)
            except DependencyError as exc:
                # When a dependency error is detected remove the affected job
                # form _desired_job_list and try again.
                self._desired_job_list.remove(exc.affected_job)
                # Remember each problem, this can be presented by the UI
                problems.append(exc)
                continue
            else:
                # Don't iterate the loop if there was no exception
                break
        # Update all job readiness state
        self._recompute_job_readiness()
        # Return all dependency problems to the caller
        return problems
Beispiel #22
0
    def update_desired_job_list(self, desired_job_list):
        """
        Update the set of desired jobs (that ought to run)

        This method can be used by the UI to recompute the dependency graph.
        The argument 'desired_job_list' is a list of jobs that should run.
        Those jobs must be a sub-collection of the job_list argument that was
        passed to the constructor.

        It never fails although it may reduce the actual permitted
        desired_job_list to an empty list. It returns a list of problems (all
        instances of DependencyError class), one for each job that had to be
        removed.
        """
        # Remember a copy of original desired job list. We may modify this list
        # so let's not mess up data passed by the caller.
        self._desired_job_list = list(desired_job_list)
        # Reset run list just in case desired_job_list is empty
        self._run_list = []
        # Try to solve the dependency graph. This is done in a loop as may need
        # to remove a problematic job and re-try. The loop provides a stop
        # condition as we will eventually run out of jobs.
        problems = []
        while self._desired_job_list:
            # XXX: it might be more efficient to incorporate this 'recovery
            # mode' right into the solver, this way we'd probably save some
            # resources or runtime complexity.
            try:
                self._run_list = DependencySolver.resolve_dependencies(
                    self._job_list, self._desired_job_list)
            except DependencyError as exc:
                # When a dependency error is detected remove the affected job
                # form _desired_job_list and try again.
                self._desired_job_list.remove(exc.affected_job)
                # Remember each problem, this can be presented by the UI
                problems.append(exc)
                continue
            else:
                # Don't iterate the loop if there was no exception
                break
        # Update all job readiness state
        self._recompute_job_readiness()
        # Return all dependency problems to the caller
        return problems
Beispiel #23
0
 def test_empty(self):
     observed = DependencySolver.resolve_dependencies([])
     expected = []
     self.assertEqual(expected, observed)
Beispiel #24
0
 def test_empty(self):
     observed = DependencySolver.resolve_dependencies([])
     expected = []
     self.assertEqual(expected, observed)