summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--morphlib/__init__.py1
-rw-r--r--morphlib/dependencyresolver.py154
-rw-r--r--morphlib/dependencyresolver_tests.py807
3 files changed, 962 insertions, 0 deletions
diff --git a/morphlib/__init__.py b/morphlib/__init__.py
index e6040af1..ab21e4c0 100644
--- a/morphlib/__init__.py
+++ b/morphlib/__init__.py
@@ -28,6 +28,7 @@ import buildworker
import builder
import cachedir
import cachedrepo
+import dependencyresolver
import execute
import fsutils
import git
diff --git a/morphlib/dependencyresolver.py b/morphlib/dependencyresolver.py
new file mode 100644
index 00000000..eea77b5e
--- /dev/null
+++ b/morphlib/dependencyresolver.py
@@ -0,0 +1,154 @@
+# Copyright (C) 2012 Codethink Limited
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+
+import cliapp
+import collections
+
+
+class MutualDependencyError(cliapp.AppException):
+
+ def __init__(self, a, b):
+ cliapp.AppException.__init__(
+ self, 'Cyclic dependency between %s and %s detected' % (a, b))
+
+
+class CyclicDependencyChainError(cliapp.AppException):
+
+ def __init__(self):
+ cliapp.AppException.__init__(
+ self, 'Cyclic dependency chain detected')
+
+
+class DependencyOrderError(cliapp.AppException):
+
+ def __init__(self, stratum, chunk, dependency_name):
+ cliapp.AppException.__init__(
+ self, 'In stratum %s, chunk %s references its dependency %s '
+ 'before it is defined' % (stratum, chunk, dependency_name))
+
+
+class DependencyFormatError(cliapp.AppException):
+
+ def __init__(self, stratum, chunk):
+ cliapp.AppException.__init__(
+ self, 'In stratum %s, chunk %s uses an invalid '
+ 'build-depends format' % (stratum, chunk))
+
+
+class DependencyResolver(object):
+
+ def resolve_dependencies(self, source_pool):
+ queue = collections.deque(source_pool)
+ while queue:
+ source = queue.popleft()
+
+ if source.morphology['kind'] == 'system':
+ self._resolve_system_dependencies(source, queue, source_pool)
+ elif source.morphology['kind'] == 'stratum':
+ self._resolve_stratum_dependencies(source, queue, source_pool)
+
+ self._detect_cyclic_dependencies(source_pool)
+
+ def _resolve_system_dependencies(self, system, queue, source_pool):
+ for stratum_name in system.morphology['strata']:
+ stratum = source_pool.lookup(
+ system.repo,
+ system.original_ref,
+ '%s.morph' % stratum_name)
+
+ system.add_dependency(stratum)
+ queue.append(stratum)
+
+ def _resolve_stratum_dependencies(self, stratum, queue, source_pool):
+ strata = []
+
+ if stratum.morphology['build-depends']:
+ for stratum_name in stratum.morphology['build-depends']:
+ other_stratum = source_pool.lookup(
+ stratum.repo,
+ stratum.original_ref,
+ '%s.morph' % stratum_name)
+ strata.append(other_stratum)
+
+ if other_stratum.depends_on(stratum):
+ raise MutualDependencyError(stratum, other_stratum)
+
+ stratum.add_dependency(other_stratum)
+ queue.append(other_stratum)
+
+ chunks = []
+ processed_chunks = []
+ name_to_processed_chunk = {}
+
+ for info in stratum.morphology['sources']:
+ chunk = source_pool.lookup(
+ info['repo'],
+ info['ref'],
+ '%s.morph' % info['morph'])
+ chunks.append(chunk)
+
+ stratum.add_dependency(chunk)
+
+ for other_stratum in strata:
+ chunk.add_dependency(other_stratum)
+
+ build_depends = info.get('build-depends', None)
+
+ if build_depends is None:
+ for earlier_chunk in processed_chunks:
+ if earlier_chunk.depends_on(chunk):
+ raise MutualDependencyError(chunk, earlier_chunk)
+ chunk.add_dependency(earlier_chunk)
+ elif isinstance(build_depends, list):
+ for name in build_depends:
+ other_chunk = name_to_processed_chunk.get(name, None)
+ if other_chunk:
+ chunk.add_dependency(other_chunk)
+ else:
+ raise DependencyOrderError(stratum, info['name'], name)
+ else:
+ raise DependencyFormatError(stratum, info['name'])
+ processed_chunks.append(chunk)
+ name_to_processed_chunk[info['name']] = chunk
+
+ def _detect_cyclic_dependencies(self, source_pool):
+ # FIXME This is not well tested and might be incorrect. Better
+ # something based on
+ # http://stackoverflow.com/questions/546655/finding-all-cycles-in-graph
+
+ visited = set()
+ explored = set()
+ parent = {}
+
+ roots = []
+ for source in source_pool:
+ if len(source.dependents) == 0:
+ roots.append(source)
+ parent[source] = None
+
+ stack = collections.deque(roots)
+ while stack:
+ source = stack.popleft()
+ visited.add(source)
+
+ for dependency in source.dependencies:
+ if not (source, dependency) in explored:
+ explored.add((source, dependency))
+ parent[dependency] = source
+ if not dependency in visited:
+ stack.appendleft(dependency)
+ else:
+ raise CyclicDependencyChainError()
diff --git a/morphlib/dependencyresolver_tests.py b/morphlib/dependencyresolver_tests.py
new file mode 100644
index 00000000..324d032e
--- /dev/null
+++ b/morphlib/dependencyresolver_tests.py
@@ -0,0 +1,807 @@
+# Copyright (C) 2012 Codethink Limited
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+
+import unittest
+
+import morphlib
+
+
+class DependencyResolverTests(unittest.TestCase):
+
+ def setUp(self):
+ self.resolver = morphlib.dependencyresolver.DependencyResolver()
+
+ def test_create_empty_build_order_for_empty_pool(self):
+ pool = morphlib.sourcepool.SourcePool()
+ self.resolver.resolve_dependencies(pool)
+
+ def test_with_a_single_chunk(self):
+ pool = morphlib.sourcepool.SourcePool()
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "foo",
+ "kind": "chunk",
+ "artifacts": {
+ "foo-runtime": [ "usr/bin" ],
+ "foo-devel": [ "usr/lib" ]
+ }
+ }
+ ''')
+ chunk = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'foo.morph')
+ pool.add(chunk)
+
+ self.resolver.resolve_dependencies(pool)
+
+ self.assertEqual(chunk.dependencies, [])
+ self.assertEqual(chunk.dependents, [])
+
+ def test_with_a_single_empty_stratum(self):
+ pool = morphlib.sourcepool.SourcePool()
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "foo",
+ "kind": "stratum"
+ }
+ ''')
+ stratum = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'foo.morph')
+ pool.add(stratum)
+
+ self.resolver.resolve_dependencies(pool)
+
+ self.assertEqual(stratum.dependencies, [])
+ self.assertEqual(stratum.dependents, [])
+
+ def test_with_a_single_empty_system(self):
+ pool = morphlib.sourcepool.SourcePool()
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "foo",
+ "kind": "system"
+ }
+ ''')
+ system = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'foo.morph')
+ pool.add(system)
+
+ self.resolver.resolve_dependencies(pool)
+
+ self.assertEqual(system.dependencies, [])
+ self.assertEqual(system.dependents, [])
+
+ def test_with_a_one_chunk_stratum(self):
+ pool = morphlib.sourcepool.SourcePool()
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "stratum",
+ "kind": "stratum",
+ "sources": [
+ {
+ "name": "chunk",
+ "repo": "repo",
+ "ref": "original/ref"
+ }
+ ]
+ }
+ ''')
+ stratum = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'stratum.morph')
+ pool.add(stratum)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "chunk",
+ "kind": "chunk",
+ "artifacts": {
+ "foo-runtime": [ "usr/bin" ],
+ "foo-devel": [ "usr/lib" ]
+ }
+ }
+ ''')
+ chunk = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'chunk.morph')
+ pool.add(chunk)
+
+ self.resolver.resolve_dependencies(pool)
+
+ self.assertEqual(chunk.dependencies, [])
+ self.assertEqual(chunk.dependents, [stratum])
+ self.assertEqual(stratum.dependencies, [chunk])
+ self.assertEqual(stratum.dependents, [])
+
+ def test_with_a_one_chunk_artifact_stratum(self):
+ pool = morphlib.sourcepool.SourcePool()
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "stratum",
+ "kind": "stratum",
+ "sources": [
+ {
+ "name": "chunk-runtime",
+ "morph": "chunk",
+ "repo": "repo",
+ "ref": "original/ref"
+ }
+ ]
+ }
+ ''')
+ stratum = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'stratum.morph')
+ pool.add(stratum)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "chunk",
+ "kind": "chunk",
+ "artifacts": {
+ "foo-runtime": [ "usr/bin" ],
+ "foo-devel": [ "usr/lib" ]
+ }
+ }
+ ''')
+ chunk = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'chunk.morph')
+ pool.add(chunk)
+
+ self.resolver.resolve_dependencies(pool)
+
+ self.assertEqual(chunk.dependencies, [])
+ self.assertEqual(chunk.dependents, [stratum])
+ self.assertEqual(stratum.dependencies, [chunk])
+ self.assertEqual(stratum.dependents, [])
+
+ def test_with_stratum_and_implicit_dependencies(self):
+ pool = morphlib.sourcepool.SourcePool()
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "stratum",
+ "kind": "stratum",
+ "sources": [
+ {
+ "name": "chunk1",
+ "repo": "repo",
+ "ref": "original/ref"
+ },
+ {
+ "name": "chunk2",
+ "repo": "repo",
+ "ref": "original/ref"
+ },
+ {
+ "name": "chunk3",
+ "repo": "repo",
+ "ref": "original/ref"
+ }
+ ]
+ }
+ ''')
+ stratum = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'stratum.morph')
+ pool.add(stratum)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "chunk1",
+ "kind": "chunk"
+ }
+ ''')
+ chunk1 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'chunk1.morph')
+ pool.add(chunk1)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "chunk2",
+ "kind": "chunk"
+ }
+ ''')
+ chunk2 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'chunk2.morph')
+ pool.add(chunk2)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "chunk3",
+ "kind": "chunk"
+ }
+ ''')
+ chunk3 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'chunk3.morph')
+ pool.add(chunk3)
+
+ self.resolver.resolve_dependencies(pool)
+
+ self.assertEqual(chunk1.dependencies, [])
+ self.assertEqual(chunk1.dependents, [stratum, chunk2, chunk3])
+ self.assertEqual(chunk2.dependencies, [chunk1])
+ self.assertEqual(chunk2.dependents, [stratum, chunk3])
+ self.assertEqual(chunk3.dependencies, [chunk1, chunk2])
+ self.assertEqual(chunk3.dependents, [stratum])
+ self.assertEqual(stratum.dependencies, [chunk1, chunk2, chunk3])
+ self.assertEqual(stratum.dependents, [])
+
+ def test_with_explicit_dependencies(self):
+ pool = morphlib.sourcepool.SourcePool()
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "stratum",
+ "kind": "stratum",
+ "sources": [
+ {
+ "name": "chunk1",
+ "repo": "repo",
+ "ref": "original/ref",
+ "build-depends": []
+ },
+ {
+ "name": "chunk2",
+ "repo": "repo",
+ "ref": "original/ref",
+ "build-depends": []
+ },
+ {
+ "name": "chunk3",
+ "repo": "repo",
+ "ref": "original/ref",
+ "build-depends": [
+ "chunk1",
+ "chunk2"
+ ]
+ }
+ ]
+ }
+ ''')
+ stratum = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'stratum.morph')
+ pool.add(stratum)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "chunk1",
+ "kind": "chunk"
+ }
+ ''')
+ chunk1 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'chunk1.morph')
+ pool.add(chunk1)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "chunk2",
+ "kind": "chunk"
+ }
+ ''')
+ chunk2 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'chunk2.morph')
+ pool.add(chunk2)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "chunk3",
+ "kind": "chunk"
+ }
+ ''')
+ chunk3 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'chunk3.morph')
+ pool.add(chunk3)
+
+ self.resolver.resolve_dependencies(pool)
+
+ self.assertEqual(chunk1.dependencies, [])
+ self.assertEqual(chunk1.dependents, [stratum, chunk3])
+ self.assertEqual(chunk2.dependencies, [])
+ self.assertEqual(chunk2.dependents, [stratum, chunk3])
+ self.assertEqual(chunk3.dependencies, [chunk1, chunk2])
+ self.assertEqual(chunk3.dependents, [stratum])
+ self.assertEqual(stratum.dependencies, [chunk1, chunk2, chunk3])
+ self.assertEqual(stratum.dependents, [])
+
+ def test_with_stratum_dependencies(self):
+ pool = morphlib.sourcepool.SourcePool()
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "stratum1",
+ "kind": "stratum"
+ }
+ ''')
+ stratum1 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'stratum1.morph')
+ pool.add(stratum1)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "stratum2",
+ "kind": "stratum",
+ "build-depends": [
+ "stratum1"
+ ]
+ }
+ ''')
+ stratum2 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'stratum2.morph')
+ pool.add(stratum2)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "stratum3",
+ "kind": "stratum",
+ "build-depends": [
+ "stratum2"
+ ]
+ }
+ ''')
+ stratum3 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'stratum3.morph')
+ pool.add(stratum3)
+
+ self.resolver.resolve_dependencies(pool)
+
+ self.assertEqual(stratum1.dependencies, [])
+ self.assertEqual(stratum1.dependents, [stratum2])
+ self.assertEqual(stratum2.dependencies, [stratum1])
+ self.assertEqual(stratum2.dependents, [stratum3])
+ self.assertEqual(stratum3.dependencies, [stratum2])
+ self.assertEqual(stratum3.dependents, [])
+
+ def test_with_stratum_and_chunk_dependencies(self):
+ pool = morphlib.sourcepool.SourcePool()
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "stratum1",
+ "kind": "stratum"
+ }
+ ''')
+ stratum1 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'stratum1.morph')
+ pool.add(stratum1)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "stratum2",
+ "kind": "stratum",
+ "build-depends": [
+ "stratum1"
+ ],
+ "sources": [
+ {
+ "name": "chunk1",
+ "repo": "repo",
+ "ref": "original/ref"
+ },
+ {
+ "name": "chunk2",
+ "repo": "repo",
+ "ref": "original/ref"
+ }
+ ]
+ }
+ ''')
+ stratum2 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'stratum2.morph')
+ pool.add(stratum2)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "chunk1",
+ "kind": "chunk"
+ }
+ ''')
+ chunk1 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'chunk1.morph')
+ pool.add(chunk1)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "chunk2",
+ "kind": "chunk"
+ }
+ ''')
+ chunk2 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'chunk2.morph')
+ pool.add(chunk2)
+
+ self.resolver.resolve_dependencies(pool)
+
+ self.assertEqual(stratum1.dependencies, [])
+ self.assertEqual(stratum1.dependents, [stratum2, chunk1, chunk2])
+ self.assertEqual(chunk1.dependencies, [stratum1])
+ self.assertEqual(chunk1.dependents, [stratum2, chunk2])
+ self.assertEqual(chunk2.dependencies, [stratum1, chunk1])
+ self.assertEqual(chunk2.dependents, [stratum2])
+ self.assertEqual(stratum2.dependencies, [stratum1, chunk1, chunk2])
+ self.assertEqual(stratum2.dependents, [])
+
+ def test_with_a_system_and_two_strata(self):
+ pool = morphlib.sourcepool.SourcePool()
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "stratum1",
+ "kind": "stratum"
+ }
+ ''')
+ stratum1 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'stratum1.morph')
+ pool.add(stratum1)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "stratum2",
+ "kind": "stratum"
+ }
+ ''')
+ stratum2 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'stratum2.morph')
+ pool.add(stratum2)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "system",
+ "kind": "system",
+ "strata": [
+ "stratum1",
+ "stratum2"
+ ]
+ }
+ ''')
+ system = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'system.morph')
+ pool.add(system)
+
+ self.resolver.resolve_dependencies(pool)
+
+ self.assertEqual(stratum1.dependencies, [])
+ self.assertEqual(stratum1.dependents, [system])
+ self.assertEqual(stratum2.dependencies, [])
+ self.assertEqual(stratum2.dependents, [system])
+ self.assertEqual(system.dependencies, [stratum1, stratum2])
+ self.assertEqual(system.dependents, [])
+
+ def test_detection_of_mutual_dependency_between_two_strata(self):
+ pool = morphlib.sourcepool.SourcePool()
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "stratum1",
+ "kind": "stratum",
+ "build-depends": [
+ "stratum2"
+ ]
+ }
+ ''')
+ stratum1 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'stratum1.morph')
+ pool.add(stratum1)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "stratum2",
+ "kind": "stratum",
+ "build-depends": [
+ "stratum1"
+ ]
+ }
+ ''')
+ stratum2 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'stratum2.morph')
+ pool.add(stratum2)
+
+ self.assertRaises(morphlib.dependencyresolver.MutualDependencyError,
+ self.resolver.resolve_dependencies, pool)
+
+ def test_detection_of_mutual_dependency_between_consecutive_chunks(self):
+ pool = morphlib.sourcepool.SourcePool()
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "stratum1",
+ "kind": "stratum",
+ "sources": [
+ {
+ "name": "chunk1",
+ "repo": "repo",
+ "ref": "original/ref"
+ },
+ {
+ "name": "chunk2",
+ "repo": "repo",
+ "ref": "original/ref"
+ }
+ ]
+ }
+ ''')
+ stratum1 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'stratum1.morph')
+ pool.add(stratum1)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "stratum2",
+ "kind": "stratum",
+ "build-depends": [
+ "stratum1"
+ ],
+ "sources": [
+ {
+ "name": "chunk2",
+ "repo": "repo",
+ "ref": "original/ref"
+ },
+ {
+ "name": "chunk1",
+ "repo": "repo",
+ "ref": "original/ref"
+ }
+ ]
+ }
+ ''')
+ stratum2 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'stratum2.morph')
+ pool.add(stratum2)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "chunk1",
+ "kind": "chunk"
+ }
+ ''')
+ chunk1 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'chunk1.morph')
+ pool.add(chunk1)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "chunk2",
+ "kind": "chunk"
+ }
+ ''')
+ chunk2 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'chunk2.morph')
+ pool.add(chunk2)
+
+ self.assertRaises(morphlib.dependencyresolver.MutualDependencyError,
+ self.resolver.resolve_dependencies, pool)
+
+ def test_detection_of_cyclic_chunk_dependency_chain(self):
+ pool = morphlib.sourcepool.SourcePool()
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "stratum1",
+ "kind": "stratum",
+ "sources": [
+ {
+ "name": "chunk1",
+ "repo": "repo",
+ "ref": "original/ref"
+ },
+ {
+ "name": "chunk2",
+ "repo": "repo",
+ "ref": "original/ref",
+ "build-depends": [
+ "chunk1"
+ ]
+ },
+ {
+ "name": "chunk3",
+ "repo": "repo",
+ "ref": "original/ref",
+ "build-depends": [
+ "chunk2"
+ ]
+ }
+ ]
+ }
+ ''')
+ stratum1 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'stratum1.morph')
+ pool.add(stratum1)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "stratum2",
+ "kind": "stratum",
+ "build-depends": [
+ "stratum1"
+ ],
+ "sources": [
+ {
+ "name": "chunk3",
+ "repo": "repo",
+ "ref": "original/ref"
+ },
+ {
+ "name": "chunk1",
+ "repo": "repo",
+ "ref": "original/ref"
+ }
+ ]
+ }
+ ''')
+ stratum2 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'stratum2.morph')
+ pool.add(stratum2)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "chunk1",
+ "kind": "chunk"
+ }
+ ''')
+ chunk1 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'chunk1.morph')
+ pool.add(chunk1)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "chunk2",
+ "kind": "chunk"
+ }
+ ''')
+ chunk2 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'chunk2.morph')
+ pool.add(chunk2)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "chunk3",
+ "kind": "chunk"
+ }
+ ''')
+ chunk2 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'chunk3.morph')
+ pool.add(chunk2)
+
+ self.assertRaises(
+ morphlib.dependencyresolver.CyclicDependencyChainError,
+ self.resolver.resolve_dependencies, pool)
+
+ def test_detection_of_chunk_dependencies_in_invalid_order(self):
+ pool = morphlib.sourcepool.SourcePool()
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "stratum",
+ "kind": "stratum",
+ "sources": [
+ {
+ "name": "chunk1",
+ "repo": "repo",
+ "ref": "original/ref",
+ "build-depends": [
+ "chunk2"
+ ]
+ },
+ {
+ "name": "chunk2",
+ "repo": "repo",
+ "ref": "original/ref"
+ }
+ ]
+ }
+ ''')
+ stratum = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'stratum.morph')
+ pool.add(stratum)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "chunk1",
+ "kind": "chunk"
+ }
+ ''')
+ chunk1 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'chunk1.morph')
+ pool.add(chunk1)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "chunk2",
+ "kind": "chunk"
+ }
+ ''')
+ chunk2 = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'chunk2.morph')
+ pool.add(chunk2)
+
+ self.assertRaises(morphlib.dependencyresolver.DependencyOrderError,
+ self.resolver.resolve_dependencies, pool)
+
+ def test_detection_of_invalid_build_depends_format(self):
+ pool = morphlib.sourcepool.SourcePool()
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "stratum",
+ "kind": "stratum",
+ "sources": [
+ {
+ "name": "chunk",
+ "repo": "repo",
+ "ref": "original/ref",
+ "build-depends": "whatever"
+ }
+ ]
+ }
+ ''')
+ stratum = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'stratum.morph')
+ pool.add(stratum)
+
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "name": "chunk",
+ "kind": "chunk"
+ }
+ ''')
+ chunk = morphlib.source.Source(
+ 'repo', 'original/ref', 'sha1', morph, 'chunk.morph')
+ pool.add(chunk)
+
+ self.assertRaises(morphlib.dependencyresolver.DependencyFormatError,
+ self.resolver.resolve_dependencies, pool)