def test_find_spec_sibling(self): s = Spec.from_literal({ 'a': { 'b +foo': { 'c': None, 'd': None }, 'e +foo': None } }) assert 'e' == find_spec(s['b'], lambda s: '+foo' in s).name assert 'b' == find_spec(s['e'], lambda s: '+foo' in s).name s = Spec.from_literal({ 'a': { 'b +foo': { 'c': None, 'd': None }, 'e': { 'f +foo': None } } }) assert 'f' == find_spec(s['b'], lambda s: '+foo' in s).name
def test_equal(self): # Different spec structures to test for equality flat = Spec.from_literal( {'mpileaks ^callpath ^libelf ^libdwarf': None} ) flat_init = Spec.from_literal({ 'mpileaks': { 'callpath': None, 'libdwarf': None, 'libelf': None } }) flip_flat = Spec.from_literal({ 'mpileaks': { 'libelf': None, 'libdwarf': None, 'callpath': None } }) dag = Spec.from_literal({ 'mpileaks': { 'callpath': { 'libdwarf': { 'libelf': None } } } }) flip_dag = Spec.from_literal({ 'mpileaks': { 'callpath': { 'libelf': { 'libdwarf': None } } } }) # All these are equal to each other with regular == specs = (flat, flat_init, flip_flat, dag, flip_dag) for lhs, rhs in zip(specs, specs): assert lhs == rhs assert str(lhs) == str(rhs) # Same DAGs constructed different ways are equal assert flat.eq_dag(flat_init) # order at same level does not matter -- (dep on same parent) assert flat.eq_dag(flip_flat) # DAGs should be unequal if nesting is different assert not flat.eq_dag(dag) assert not flat.eq_dag(flip_dag) assert not flip_flat.eq_dag(dag) assert not flip_flat.eq_dag(flip_dag) assert not dag.eq_dag(flip_dag)
def test_invalid_literal_spec(self): # Can't give type 'build' to a top-level spec with pytest.raises(spack.spec.SpecParseError): Spec.from_literal({'foo:build': None}) # Can't use more than one ':' separator with pytest.raises(KeyError): Spec.from_literal({'foo': {'bar:build:link': None}})
def test_edge_traversals(self): """Make sure child and parent traversals of specs work.""" # Mock spec - d is used for a diamond dependency spec = Spec.from_literal({ 'a': { 'b': { 'c': { 'd': None }, 'e': None }, 'f': { 'g': { 'd': None } } } }) assert (['a', 'b', 'c', 'd', 'e', 'f', 'g'] == [s.name for s in spec.traverse(direction='children')]) assert (['g', 'f', 'a'] == [ s.name for s in spec['g'].traverse(direction='parents') ]) assert (['d', 'c', 'b', 'a', 'g', 'f'] == [ s.name for s in spec['d'].traverse(direction='parents') ])
def test_construct_spec_with_deptypes(self): """Ensure that it is possible to construct a spec with explicit dependency types.""" s = Spec.from_literal({ 'a': { 'b': { 'c:build': None }, 'd': { 'e:build,link': { 'f:run': None } } } }) assert s['b']._dependencies['c'].deptypes == ('build', ) assert s['d']._dependencies['e'].deptypes == ('build', 'link') assert s['e']._dependencies['f'].deptypes == ('run', ) assert s['b']._dependencies['c'].deptypes == ('build', ) assert s['d']._dependencies['e'].deptypes == ('build', 'link') assert s['e']._dependencies['f'].deptypes == ('run', ) assert s['c']._dependents['b'].deptypes == ('build', ) assert s['e']._dependents['d'].deptypes == ('build', 'link') assert s['f']._dependents['e'].deptypes == ('run', ) assert s['c']._dependents['b'].deptypes == ('build', ) assert s['e']._dependents['d'].deptypes == ('build', 'link') assert s['f']._dependents['e'].deptypes == ('run', )
def test_construct_spec_with_deptypes(self): """Ensure that it is possible to construct a spec with explicit dependency types.""" s = Spec.from_literal({ 'a': { 'b': {'c:build': None}, 'd': { 'e:build,link': {'f:run': None} } } }) assert s['b']._dependencies['c'].deptypes == ('build',) assert s['d']._dependencies['e'].deptypes == ('build', 'link') assert s['e']._dependencies['f'].deptypes == ('run',) assert s['b']._dependencies['c'].deptypes == ('build',) assert s['d']._dependencies['e'].deptypes == ('build', 'link') assert s['e']._dependencies['f'].deptypes == ('run',) assert s['c']._dependents['b'].deptypes == ('build',) assert s['e']._dependents['d'].deptypes == ('build', 'link') assert s['f']._dependents['e'].deptypes == ('run',) assert s['c']._dependents['b'].deptypes == ('build',) assert s['e']._dependents['d'].deptypes == ('build', 'link') assert s['f']._dependents['e'].deptypes == ('run',)
def test_edge_traversals(self): """Make sure child and parent traversals of specs work.""" # Mock spec - d is used for a diamond dependency spec = Spec.from_literal({ 'a': { 'b': { 'c': {'d': None}, 'e': None }, 'f': { 'g': {'d': None} } } }) assert ( ['a', 'b', 'c', 'd', 'e', 'f', 'g'] == [s.name for s in spec.traverse(direction='children')]) assert ( ['g', 'f', 'a'] == [s.name for s in spec['g'].traverse(direction='parents')]) assert ( ['d', 'c', 'b', 'a', 'g', 'f'] == [s.name for s in spec['d'].traverse(direction='parents')])
def test_normalize_with_virtual_spec(self, ): dag = Spec.from_literal({ 'mpileaks': { 'callpath': { 'dyninst': { 'libdwarf': { 'libelf': None }, 'libelf': None }, 'mpi': None }, 'mpi': None } }) dag.normalize() # make sure nothing with the same name occurs twice counts = {} for spec in dag.traverse(key=id): if spec.name not in counts: counts[spec.name] = 0 counts[spec.name] += 1 for name in counts: assert counts[name] == 1
def test_find_spec_none(self): s = Spec.from_literal({ 'a': { 'b': { 'c': None, 'd': None }, 'e': None } }) assert find_spec(s['b'], lambda s: '+foo' in s) is None
def test_find_spec_parents(self): """Tests the spec finding logic used by concretization. """ s = Spec.from_literal( {'a +foo': { 'b +foo': { 'c': None, 'd+foo': None }, 'e +foo': None }}) assert 'a' == find_spec(s['b'], lambda s: '+foo' in s).name
def test_find_spec_parents(self): """Tests the spec finding logic used by concretization. """ s = Spec.from_literal({ 'a +foo': { 'b +foo': { 'c': None, 'd+foo': None }, 'e +foo': None } }) assert 'a' == find_spec(s['b'], lambda s: '+foo' in s).name
def test_find_spec_children(self): s = Spec.from_literal( {'a': { 'b +foo': { 'c': None, 'd+foo': None }, 'e +foo': None }}) assert 'd' == find_spec(s['b'], lambda s: '+foo' in s).name s = Spec.from_literal( {'a': { 'b +foo': { 'c+foo': None, 'd': None }, 'e +foo': None }}) assert 'c' == find_spec(s['b'], lambda s: '+foo' in s).name
def check_diamond_normalized_dag(self, spec): dag = Spec.from_literal({ 'dt-diamond': { 'dt-diamond-left:build,link': { 'dt-diamond-bottom:build': None }, 'dt-diamond-right:build,link': { 'dt-diamond-bottom:build,link,run': None }, } }) assert spec.eq_dag(dag)
def test_find_spec_children(self): s = Spec.from_literal({ 'a': { 'b +foo': { 'c': None, 'd+foo': None }, 'e +foo': None } }) assert 'd' == find_spec(s['b'], lambda s: '+foo' in s).name s = Spec.from_literal({ 'a': { 'b +foo': { 'c+foo': None, 'd': None }, 'e +foo': None } }) assert 'c' == find_spec(s['b'], lambda s: '+foo' in s).name
def test_dependents_and_dependencies_are_correct(self): spec = Spec.from_literal({ 'mpileaks': { 'callpath': { 'dyninst': { 'libdwarf': {'libelf': None}, 'libelf': None }, 'mpi': None }, 'mpi': None } }) check_links(spec) spec.normalize() check_links(spec)
def test_normalize_with_virtual_package(self): spec = Spec('mpileaks ^mpi ^[email protected] ^libdwarf') spec.normalize() expected_normalized = Spec.from_literal({ 'mpileaks': { 'callpath': { 'dyninst': { 'libdwarf': {'[email protected]': None}, '[email protected]': None }, 'mpi': None }, 'mpi': None } }) assert str(spec) == str(expected_normalized)
def test_normalize_with_virtual_spec(self, ): dag = Spec.from_literal({ 'mpileaks': { 'callpath': { 'dyninst': { 'libdwarf': {'libelf': None}, 'libelf': None }, 'mpi': None }, 'mpi': None } }) dag.normalize() # make sure nothing with the same name occurs twice counts = {} for spec in dag.traverse(key=id): if spec.name not in counts: counts[spec.name] = 0 counts[spec.name] += 1 for name in counts: assert counts[name] == 1
def test_normalize_mpileaks(self): # Spec parsed in from a string spec = Spec.from_literal({ 'mpileaks ^mpich ^callpath ^dyninst ^[email protected] ^libdwarf': None }) # What that spec should look like after parsing expected_flat = Spec.from_literal({ 'mpileaks': { 'mpich': None, 'callpath': None, 'dyninst': None, '[email protected]': None, 'libdwarf': None } }) # What it should look like after normalization mpich = Spec('mpich') libelf = Spec('[email protected]') expected_normalized = Spec.from_literal({ 'mpileaks': { 'callpath': { 'dyninst': { 'libdwarf': {libelf: None}, libelf: None }, mpich: None }, mpich: None }, }) # Similar to normalized spec, but now with copies of the same # libelf node. Normalization should result in a single unique # node for each package, so this is the wrong DAG. non_unique_nodes = Spec.from_literal({ 'mpileaks': { 'callpath': { 'dyninst': { 'libdwarf': {'[email protected]': None}, '[email protected]': None }, mpich: None }, mpich: None } }, normal=False) # All specs here should be equal under regular equality specs = (spec, expected_flat, expected_normalized, non_unique_nodes) for lhs, rhs in zip(specs, specs): assert lhs == rhs assert str(lhs) == str(rhs) # Test that equal and equal_dag are doing the right thing assert spec == expected_flat assert spec.eq_dag(expected_flat) # Normalized has different DAG structure, so NOT equal. assert spec != expected_normalized assert not spec.eq_dag(expected_normalized) # Again, different DAG structure so not equal. assert spec != non_unique_nodes assert not spec.eq_dag(non_unique_nodes) spec.normalize() # After normalizing, spec_dag_equal should match the normalized spec. assert spec != expected_flat assert not spec.eq_dag(expected_flat) # verify DAG structure without deptypes. assert spec.eq_dag(expected_normalized, deptypes=False) assert not spec.eq_dag(non_unique_nodes, deptypes=False) assert not spec.eq_dag(expected_normalized, deptypes=True) assert not spec.eq_dag(non_unique_nodes, deptypes=True)
def spec_and_expected(request): """Parameters for the normalization test.""" spec, d = request.param return spec, Spec.from_literal(d)
def test_normalize_mpileaks(self): # Spec parsed in from a string spec = Spec.from_literal({ 'mpileaks ^mpich ^callpath ^dyninst ^[email protected] ^libdwarf': None }) # What that spec should look like after parsing expected_flat = Spec.from_literal({ 'mpileaks': { 'mpich': None, 'callpath': None, 'dyninst': None, '[email protected]': None, 'libdwarf': None } }) # What it should look like after normalization mpich = Spec('mpich') libelf = Spec('[email protected]') expected_normalized = Spec.from_literal({ 'mpileaks': { 'callpath': { 'dyninst': { 'libdwarf': { libelf: None }, libelf: None }, mpich: None }, mpich: None }, }) # Similar to normalized spec, but now with copies of the same # libelf node. Normalization should result in a single unique # node for each package, so this is the wrong DAG. non_unique_nodes = Spec.from_literal( { 'mpileaks': { 'callpath': { 'dyninst': { 'libdwarf': { '[email protected]': None }, '[email protected]': None }, mpich: None }, mpich: None } }, normal=False) # All specs here should be equal under regular equality specs = (spec, expected_flat, expected_normalized, non_unique_nodes) for lhs, rhs in zip(specs, specs): assert lhs == rhs assert str(lhs) == str(rhs) # Test that equal and equal_dag are doing the right thing assert spec == expected_flat assert spec.eq_dag(expected_flat) # Normalized has different DAG structure, so NOT equal. assert spec != expected_normalized assert not spec.eq_dag(expected_normalized) # Again, different DAG structure so not equal. assert spec != non_unique_nodes assert not spec.eq_dag(non_unique_nodes) spec.normalize() # After normalizing, spec_dag_equal should match the normalized spec. assert spec != expected_flat assert not spec.eq_dag(expected_flat) # verify DAG structure without deptypes. assert spec.eq_dag(expected_normalized, deptypes=False) assert not spec.eq_dag(non_unique_nodes, deptypes=False) assert not spec.eq_dag(expected_normalized, deptypes=True) assert not spec.eq_dag(non_unique_nodes, deptypes=True)