diff options
Diffstat (limited to 'morphlib')
-rw-r--r-- | morphlib/__init__.py | 1 | ||||
-rw-r--r-- | morphlib/morphset.py | 158 | ||||
-rw-r--r-- | morphlib/morphset_tests.py | 180 |
3 files changed, 339 insertions, 0 deletions
diff --git a/morphlib/__init__.py b/morphlib/__init__.py index b9b9924b..bcdd733b 100644 --- a/morphlib/__init__.py +++ b/morphlib/__init__.py @@ -67,6 +67,7 @@ import morph2 import morphologyfactory import morph3 import morphloader +import morphset import remoteartifactcache import remoterepocache import repoaliasresolver diff --git a/morphlib/morphset.py b/morphlib/morphset.py new file mode 100644 index 00000000..98a4b8f9 --- /dev/null +++ b/morphlib/morphset.py @@ -0,0 +1,158 @@ +# Copyright (C) 2013 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. +# +# =*= License: GPL-2 =*= + + +import morphlib + + +class StratumNotInSystemError(morphlib.Error): + + def __init__(self, system_name, stratum_name): + self.msg = ( + 'System %s does not contain %s' % (system_name, stratum_name)) + + +class StratumNotInSetError(morphlib.Error): + + def __init__(self, stratum_name): + self.msg = 'Stratum %s is not in MorphologySet' % stratum_name + + +class ChunkNotInStratumError(morphlib.Error): + + def __init__(self, stratum_name, chunk_name): + self.msg = ( + 'Stratum %s does not contain %s' % (stratum_name, chunk_name)) + + +class MorphologySet(object): + + '''Store and manipulate a set of Morphology objects.''' + + def __init__(self): + self.morphologies = [] + + def add_morphology(self, morphology): + '''Add a morphology object to the set, unless it's there already.''' + + triplet = ( + morphology.repo_url, + morphology.ref, + morphology.filename + ) + for existing in self.morphologies: + existing_triplet = ( + existing.repo_url, + existing.ref, + existing.filename + ) + if existing_triplet == triplet: + return + + self.morphologies.append(morphology) + + def has(self, repo_url, ref, filename): + '''Does the set have a morphology for the given triplet?''' + return self._get_morphology(repo_url, ref, filename) is not None + + def _get_morphology(self, repo_url, ref, filename): + for m in self.morphologies: + if (m.repo_url == repo_url and + m.ref == ref and + m.filename == filename): + return m + return None + + def _find_spec(self, specs, wanted_name): + for spec in specs: + name = spec.get('morph', spec.get('name')) + if name == wanted_name: + return spec['repo'], spec['ref'], name + return None, None, None + + def get_stratum_in_system(self, system_morph, stratum_name): + '''Return morphology for a stratum that is in a system. + + If the stratum is not in the system, raise StratumNotInSystemError. + If the stratum morphology has not been added to the set, + raise StratumNotInSetError. + + ''' + + repo_url, ref, morph = self._find_spec( + system_morph['strata'], stratum_name) + if repo_url is None: + raise StratumNotInSystemError(system_morph['name'], stratum_name) + m = self._get_morphology(repo_url, ref, '%s.morph' % morph) + if m is None: + raise StratumNotInSetError(stratum_name) + return m + + def get_chunk_triplet(self, stratum_morph, chunk_name): + '''Return the repo url, ref, morph name triplet for a chunk. + + Given a stratum morphology, find the triplet used to refer to + a given chunk. Note that because of how the chunk may be + referred to using either name or morph fields in the morphology, + the morph field (or its computed value) is always returned. + Note also that the morph field, not the filename, is returned. + + Raise ChunkNotInStratumError if the chunk is not found in the + stratum. + + ''' + + repo_url, ref, morph = self._find_spec( + stratum_morph['chunks'], chunk_name) + if repo_url is None: + raise ChunkNotInStratumError(stratum_morph['name'], chunk_name) + return repo_url, ref, morph + + def change_ref(self, repo_url, orig_ref, morph_filename, new_ref): + '''Change a triplet's ref to a new one in all morphologies in a ref. + + Change orig_ref to new_ref in any morphology that references the + original triplet. This includes stratum build-dependencies. + + ''' + + def wanted_spec(spec): + return (spec['repo'] == repo_url and + spec['ref'] == orig_ref and + spec['morph'] + '.morph' == morph_filename) + + def change_specs(specs): + for spec in specs: + if wanted_spec(spec): + spec['ref'] = new_ref + m.dirty = True + + def change(m): + if m['kind'] == 'system': + change_specs(m['strata']) + elif m['kind'] == 'stratum': + change_specs(m['chunks']) + change_specs(m['build-depends']) + + for m in self.morphologies: + change(m) + + m = self._get_morphology(repo_url, orig_ref, morph_filename) + if m and m.ref != new_ref: + m.ref = new_ref + m.dirty = True + diff --git a/morphlib/morphset_tests.py b/morphlib/morphset_tests.py new file mode 100644 index 00000000..7dbc861a --- /dev/null +++ b/morphlib/morphset_tests.py @@ -0,0 +1,180 @@ +# Copyright (C) 2013 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. +# +# =*= License: GPL-2 =*= + + +import unittest + +import morphlib + + +class MorphologySetTests(unittest.TestCase): + + def setUp(self): + self.morphs = morphlib.morphset.MorphologySet() + + self.system = morphlib.morph3.Morphology({ + 'kind': 'system', + 'name': 'foo-system', + 'strata': [ + { + 'repo': 'test:morphs', + 'ref': 'master', + 'morph': 'foo-stratum', + }, + ], + }) + self.system.repo_url = 'test:morphs' + self.system.ref = 'master' + self.system.filename = 'foo-system.morph' + + self.stratum = morphlib.morph3.Morphology({ + 'kind': 'stratum', + 'name': 'foo-stratum', + 'chunks': [ + { + 'repo': 'test:foo-chunk', + 'ref': 'master', + 'morph': 'foo-chunk', + }, + ], + 'build-depends': [], + }) + self.stratum.repo_url = 'test:morphs' + self.stratum.ref = 'master' + self.stratum.filename = 'foo-stratum.morph' + + def test_is_empty_initially(self): + self.assertEqual(self.morphs.morphologies, []) + self.assertFalse( + self.morphs.has( + self.system.repo_url, self.system.ref, self.system.filename)) + + def test_adds_morphology(self): + self.morphs.add_morphology(self.system) + self.assertEqual(self.morphs.morphologies, [self.system]) + self.assertTrue( + self.morphs.has( + self.system.repo_url, self.system.ref, self.system.filename)) + + self.morphs.add_morphology(self.stratum) + self.assertEqual( + self.morphs.morphologies, + [self.system, self.stratum]) + + def test_does_not_add_morphology_twice(self): + self.morphs.add_morphology(self.system) + self.morphs.add_morphology(self.system) + self.assertEqual(self.morphs.morphologies, [self.system]) + + def test_get_stratum_in_system(self): + self.morphs.add_morphology(self.system) + self.morphs.add_morphology(self.stratum) + self.assertEqual( + self.morphs.get_stratum_in_system( + self.system, self.stratum['name']), + self.stratum) + + def test_raises_stratum_not_in_system_error(self): + self.morphs.add_morphology(self.system) + self.morphs.add_morphology(self.stratum) + self.assertRaises( + morphlib.morphset.StratumNotInSystemError, + self.morphs.get_stratum_in_system, self.system, 'unknown-stratum') + + def test_raises_stratum_not_in_set_error(self): + self.morphs.add_morphology(self.system) + self.assertRaises( + morphlib.morphset.StratumNotInSetError, + self.morphs.get_stratum_in_system, self.system, 'foo-stratum') + + def test_get_chunk_triplet(self): + self.morphs.add_morphology(self.system) + self.morphs.add_morphology(self.stratum) + self.assertEqual( + self.morphs.get_chunk_triplet(self.stratum, 'foo-chunk'), + ('test:foo-chunk', 'master', 'foo-chunk')) + + def test_raises_chunk_not_in_stratum_error(self): + self.assertRaises( + morphlib.morphset.ChunkNotInStratumError, + self.morphs.get_chunk_triplet, self.stratum, 'wrong') + + def test_changes_stratum_ref(self): + self.morphs.add_morphology(self.system) + self.morphs.add_morphology(self.stratum) + self.morphs.change_ref( + self.stratum.repo_url, + self.stratum.ref, + self.stratum.filename, + 'new-ref') + self.assertEqual(self.stratum.ref, 'new-ref') + self.assertEqual( + self.system['strata'][0], + { + 'repo': 'test:morphs', + 'ref': 'new-ref', + 'morph': 'foo-stratum' + }) + + def test_changes_stratum_ref_in_build_depends(self): + other_stratum = morphlib.morph3.Morphology({ + 'name': 'other-stratum', + 'kind': 'stratum', + 'chunks': [], + 'build-depends': [ + { + 'repo': self.stratum.repo_url, + 'ref': self.stratum.ref, + 'morph': self.stratum['name'], + }, + ] + }) + + self.morphs.add_morphology(self.system) + self.morphs.add_morphology(self.stratum) + self.morphs.add_morphology(other_stratum) + self.morphs.change_ref( + self.stratum.repo_url, + self.stratum.ref, + self.stratum.filename, + 'new-ref') + self.assertEqual( + other_stratum['build-depends'][0], + { + 'repo': 'test:morphs', + 'ref': 'new-ref', + 'morph': 'foo-stratum' + }) + + def test_changes_chunk_ref(self): + self.morphs.add_morphology(self.system) + self.morphs.add_morphology(self.stratum) + self.morphs.change_ref( + 'test:foo-chunk', + 'master', + 'foo-chunk.morph', + 'new-ref') + self.assertEqual( + self.stratum['chunks'], + [ + { + 'repo': 'test:foo-chunk', + 'ref': 'new-ref', + 'morph': 'foo-chunk', + } + ]) + |