tests for variant concretization

This commit is contained in:
Todd Gamblin 2015-04-27 00:45:59 -07:00
parent 535c1fac87
commit 793b842f99
2 changed files with 44 additions and 12 deletions

View file

@ -294,8 +294,13 @@ def __str__(self):
class VariantMap(HashableMap): class VariantMap(HashableMap):
def satisfies(self, other, self_is_concrete): def __init__(self, spec):
if self_is_concrete: super(VariantMap, self).__init__()
self.spec = spec
def satisfies(self, other):
if self.spec._concrete:
return all(k in self and self[k].enabled == other[k].enabled return all(k in self and self[k].enabled == other[k].enabled
for k in other) for k in other)
else: else:
@ -303,8 +308,8 @@ def satisfies(self, other, self_is_concrete):
for k in other if k in self) for k in other if k in self)
def constrain(self, other, other_is_concrete): def constrain(self, other):
if other_is_concrete: if other.spec._concrete:
for k in self: for k in self:
if k not in other: if k not in other:
raise UnsatisfiableVariantSpecError(self[k], '<absent>') raise UnsatisfiableVariantSpecError(self[k], '<absent>')
@ -316,6 +321,18 @@ def constrain(self, other, other_is_concrete):
else: else:
self[k] = other[k].copy() self[k] = other[k].copy()
@property
def concrete(self):
return self.spec._concrete or all(
v in self for v in self.spec.package.variants)
def copy(self):
clone = VariantMap(None)
for name, variant in self.items():
clone[name] = variant.copy()
return clone
def __str__(self): def __str__(self):
sorted_keys = sorted(self.keys()) sorted_keys = sorted(self.keys())
@ -361,10 +378,11 @@ def __init__(self, spec_like, *dep_like, **kwargs):
self.name = other.name self.name = other.name
self.dependents = other.dependents self.dependents = other.dependents
self.versions = other.versions self.versions = other.versions
self.variants = other.variants
self.architecture = other.architecture self.architecture = other.architecture
self.compiler = other.compiler self.compiler = other.compiler
self.dependencies = other.dependencies self.dependencies = other.dependencies
self.variants = other.variants
self.variants.spec = self
# Specs are by default not assumed to be normal, but in some # Specs are by default not assumed to be normal, but in some
# cases we've read them from a file want to assume normal. # cases we've read them from a file want to assume normal.
@ -457,14 +475,15 @@ def virtual(self):
@property @property
def concrete(self): def concrete(self):
"""A spec is concrete if it can describe only ONE build of a package. """A spec is concrete if it can describe only ONE build of a package.
If any of the name, version, architecture, compiler, or depdenencies If any of the name, version, architecture, compiler,
are ambiguous,then it is not concrete. variants, or depdenencies are ambiguous,then it is not concrete.
""" """
if self._concrete: if self._concrete:
return True return True
self._concrete = bool(not self.virtual self._concrete = bool(not self.virtual
and self.versions.concrete and self.versions.concrete
and self.variants.concrete
and self.architecture and self.architecture
and self.compiler and self.compiler.concrete and self.compiler and self.compiler.concrete
and self.dependencies.concrete) and self.dependencies.concrete)
@ -947,7 +966,7 @@ def constrain(self, other, **kwargs):
self.compiler = other.compiler self.compiler = other.compiler
self.versions.intersect(other.versions) self.versions.intersect(other.versions)
self.variants.constrain(other.variants, other._concrete) self.variants.constrain(other.variants)
self.architecture = self.architecture or other.architecture self.architecture = self.architecture or other.architecture
if constrain_deps: if constrain_deps:
@ -1020,7 +1039,7 @@ def satisfies(self, other, **kwargs):
if s and o and not s.satisfies(o): if s and o and not s.satisfies(o):
return False return False
if not self.variants.satisfies(other.variants, self._concrete): if not self.variants.satisfies(other.variants):
return False return False
# Architecture satisfaction is currently just string equality. # Architecture satisfaction is currently just string equality.
@ -1089,11 +1108,12 @@ def _dup(self, other, **kwargs):
# Local node attributes get copied first. # Local node attributes get copied first.
self.name = other.name self.name = other.name
self.versions = other.versions.copy() self.versions = other.versions.copy()
self.variants = other.variants.copy()
self.architecture = other.architecture self.architecture = other.architecture
self.compiler = other.compiler.copy() if other.compiler else None self.compiler = other.compiler.copy() if other.compiler else None
self.dependents = DependencyMap() self.dependents = DependencyMap()
self.dependencies = DependencyMap() self.dependencies = DependencyMap()
self.variants = other.variants.copy()
self.variants.spec = self
# If we copy dependencies, preserve DAG structure in the new spec # If we copy dependencies, preserve DAG structure in the new spec
if kwargs.get('deps', True): if kwargs.get('deps', True):
@ -1429,7 +1449,7 @@ def spec(self):
spec = Spec.__new__(Spec) spec = Spec.__new__(Spec)
spec.name = self.token.value spec.name = self.token.value
spec.versions = VersionList() spec.versions = VersionList()
spec.variants = VariantMap() spec.variants = VariantMap(spec)
spec.architecture = None spec.architecture = None
spec.compiler = None spec.compiler = None
spec.dependents = DependencyMap() spec.dependents = DependencyMap()

View file

@ -35,7 +35,13 @@ def check_spec(self, abstract, concrete):
self.assertEqual(abstract.versions, concrete.versions) self.assertEqual(abstract.versions, concrete.versions)
if abstract.variants: if abstract.variants:
self.assertEqual(abstract.versions, concrete.versions) for name in abstract.variants:
avariant = abstract.variants[name]
cvariant = concrete.variants[name]
self.assertEqual(avariant.enabled, cvariant.enabled)
for name in abstract.package.variants:
self.assertTrue(name in concrete.variants)
if abstract.compiler and abstract.compiler.concrete: if abstract.compiler and abstract.compiler.concrete:
self.assertEqual(abstract.compiler, concrete.compiler) self.assertEqual(abstract.compiler, concrete.compiler)
@ -66,6 +72,12 @@ def test_concretize_dag(self):
self.check_concretize('libelf') self.check_concretize('libelf')
def test_concretize_variant(self):
self.check_concretize('mpich+debug')
self.check_concretize('mpich~debug')
self.check_concretize('mpich')
def test_concretize_with_virtual(self): def test_concretize_with_virtual(self):
self.check_concretize('mpileaks ^mpi') self.check_concretize('mpileaks ^mpi')
self.check_concretize('mpileaks ^mpi@:1.1') self.check_concretize('mpileaks ^mpi@:1.1')