示例#1
0
def test_complex_substitutions():
    """Make sure accounting works for realistic objects."""
    root = MaterialRun("root",
                       process=ProcessRun("root", spec=ProcessSpec("root")),
                       spec=MaterialSpec("root"))
    root.spec.process = root.process.spec
    input = MaterialRun("input",
                        process=ProcessRun("input", spec=ProcessSpec("input")),
                        spec=MaterialSpec("input"))
    input.spec.process = input.process.spec
    IngredientRun(process=root.process,
                  material=input,
                  spec=IngredientSpec("ingredient",
                                      process=root.process.spec,
                                      material=input.spec))
    param = ParameterTemplate("Param", bounds=RealBounds(-1, 1, "m"))
    root.process.spec.template = ProcessTemplate("Proc", parameters=[param])
    root.process.parameters.append(
        Parameter("Param", value=NormalReal(0, 1, 'm'), template=param))

    links = flatten(root, scope="test-scope")
    index = make_index(links)
    rebuild = substitute_objects(links, index, inplace=True)
    rebuilt_root = next(x for x in rebuild
                        if x.name == root.name and x.typ == root.typ)
    all_objs = recursive_flatmap(rebuilt_root,
                                 func=lambda x: [x],
                                 unidirectional=False)
    unique = [x for i, x in enumerate(all_objs) if i == all_objs.index(x)]
    assert not any(isinstance(x, LinkByUID) for x in unique), "All are objects"
    assert len(links) == len(unique), "Objects are missing"
示例#2
0
def test_template_access():
    """A process run's template should be equal to its spec's template."""
    template = ProcessTemplate("process template", uids={'id': str(uuid4())})
    spec = ProcessSpec("A spec", uids={'id': str(uuid4())}, template=template)
    proc = ProcessRun("A run", uids={'id': str(uuid4())}, spec=spec)
    assert proc.template == template

    proc.spec = LinkByUID.from_entity(spec)
    assert proc.template is None
示例#3
0
def test_equality():
    """Test that the __eq__ method performs as expected."""
    link = LinkByUID(scope="foo", id="bar")
    assert link == ProcessRun("Good", uids={"foo": "bar"})
    assert link != ProcessRun("Good", uids={"foo": "rab"})
    assert link != ProcessRun("Good", uids={"oof": "bar"})
    assert link != LinkByUID(scope="foo", id="rab")
    assert link == ("foo", "bar")
    assert link != ("foo", "bar", "baz")
    assert link != ("foo", "rab")
示例#4
0
def test_inplace_v_not():
    """Test that client can copy a dictionary in which keys are BaseEntity objects."""
    spec = ProcessSpec("A process spec", uids={'id': str(uuid4()), 'auto': str(uuid4())})
    run1 = ProcessRun("A process run", spec=spec, uids={'id': str(uuid4()), 'auto': str(uuid4())})
    run2 = ProcessRun("Another process run", spec=spec, uids={'id': str(uuid4())})
    process_dict = {spec: [run1, run2]}

    subbed = substitute_links(process_dict)
    assert subbed != process_dict  # This is true because the hashes change, even if objects equal
    substitute_links(process_dict, inplace=True)
    assert subbed == process_dict
def test_process_reassignment():
    """Test that a material can be assigned to a new process."""
    drying = ProcessRun("drying")
    welding = ProcessRun("welding")
    powder = MaterialRun("Powder", process=welding)

    assert powder.process == welding
    assert welding.output_material == powder

    powder.process = drying
    assert powder.process == drying
    assert drying.output_material == powder
    assert welding.output_material is None
def test_order_objects():
    """Test that sorting works when given objects."""
    unsorted = [MaterialRun("bar"), ProcessRun("foo")]
    sorted_list = sorted(unsorted, key=lambda x: writable_sort_order(x))

    assert isinstance(sorted_list[0], ProcessRun)
    assert isinstance(sorted_list[1], MaterialRun)
示例#7
0
def test_serialize():
    """Serializing a nested object should be identical to individually serializing each piece."""
    condition = Condition(name="A condition", value=NominalReal(7, ''))
    parameter = Parameter(name="A parameter",
                          value=NormalReal(mean=17, std=1, units=''))
    input_material = MaterialRun(tags="input")
    process = ProcessRun(tags="A tag on a process run")
    ingredient = IngredientRun(material=input_material, process=process)
    material = MaterialRun(tags=["A tag on a material"], process=process)
    measurement = MeasurementRun(tags="A tag on a measurement",
                                 conditions=condition,
                                 parameters=parameter,
                                 material=material)

    # serialize the root of the tree
    native_object = json.loads(dumps(measurement))
    # ingredients don't get serialized on the process
    assert (len(native_object["context"]) == 5)
    assert (native_object["object"]["type"] == LinkByUID.typ)

    # serialize all of the nodes
    native_batch = json.loads(
        dumps([material, process, measurement, ingredient]))
    assert (len(native_batch["context"]) == 5)
    assert (len(native_batch["object"]) == 4)
    assert (all(x["type"] == LinkByUID.typ for x in native_batch["object"]))
示例#8
0
def test_dict_serialization():
    """Test that a dictionary can be serialized and then deserialized as a gemd object."""
    process = ProcessRun("A process")
    mat = MaterialRun("A material", process=process)
    meas = MeasurementRun("A measurement", material=mat)
    copy = loads(dumps(meas.as_dict()))
    assert copy == meas
示例#9
0
def test_flatmap_unidirectional_ordering():
    """Test that the unidirecitonal setting is obeyed."""
    # The writeable link is ingredient -> process, not process -> ingredients
    proc = ProcessRun(name="foo")
    IngredientRun(notes="bar", process=proc)

    assert len(recursive_flatmap(proc, lambda x: [x], unidirectional=False)) == 2
    assert len(recursive_flatmap(proc, lambda x: [x], unidirectional=True)) == 0
def test_tuple_sub():
    """substitute_objects() should correctly substitute tuple values."""
    proc = ProcessRun('foo', uids={'id': '123'})
    proc_link = LinkByUID.from_entity(proc)
    index = {(proc_link.scope, proc_link.id): proc}
    tup = (proc_link,)
    subbed = substitute_objects(tup, index)
    assert subbed[0] == proc
示例#11
0
def test_default_scope():
    """Test flatten exceptions around providing a scope."""
    ps_one = ProcessSpec(name="one")

    with pytest.raises(ValueError):
        flatten(ps_one)

    pr_one = ProcessRun(name="one", uids={'my': 'outer'}, spec=ps_one)

    with pytest.raises(ValueError):
        flatten(pr_one)

    ps_one.uids['my'] = 'id'
    assert len(flatten(pr_one)) == 2

    two = ProcessRun(name="two", spec=ProcessSpec(name="two"))
    assert len(flatten(two, scope="my")) == 2
示例#12
0
def test_unexpected_serialization():
    """Trying to serialize an unexpected class should throw a TypeError."""
    class DummyClass:
        def __init__(self, foo):
            self.foo = foo

    with pytest.raises(TypeError):
        dumps(ProcessRun("A process", notes=DummyClass("something")))
def test_invalid_assignment():
    """Invalid assignments to `process` or `spec` throw a TypeError."""
    with pytest.raises(TypeError):
        MaterialRun(name=12)
    with pytest.raises(TypeError):
        MaterialRun("name", spec=ProcessRun("a process"))
    with pytest.raises(TypeError):
        MaterialRun("name", process=MaterialSpec("a spec"))
示例#14
0
def test_object_key_substitution():
    """Test that client can copy a dictionary in which keys are BaseEntity objects."""
    spec = ProcessSpec("A process spec", uids={'id': str(uuid4()), 'auto': str(uuid4())})
    run1 = ProcessRun("A process run", spec=spec, uids={'id': str(uuid4()), 'auto': str(uuid4())})
    run2 = ProcessRun("Another process run", spec=spec, uids={'id': str(uuid4())})
    process_dict = {spec: [run1, run2]}

    subbed = substitute_links(process_dict, scope='auto')
    for key, value in subbed.items():
        assert key == LinkByUID.from_entity(spec, scope='auto')
        assert LinkByUID.from_entity(run1, scope='auto') in value
        assert LinkByUID.from_entity(run2) in value

    reverse_process_dict = {run2: spec}
    subbed = substitute_links(reverse_process_dict, scope='auto')
    for key, value in subbed.items():
        assert key == LinkByUID.from_entity(run2)
        assert value == LinkByUID.from_entity(spec, scope='auto')
def test_dependencies():
    """Test that dependency lists make sense."""
    ps = ProcessSpec(name="ps")
    pr = ProcessRun(name="pr", spec=ps)
    ms = MaterialSpec(name="ms", process=ps)
    mr = MaterialRun(name="mr", spec=ms, process=pr)

    assert ps not in mr.all_dependencies()
    assert pr in mr.all_dependencies()
    assert ms in mr.all_dependencies()
def test_make_index():
    """Test functionality of make_index method."""
    ps1 = ProcessSpec(name="hello", uids={"test_scope": "test_value"})
    pr1 = ProcessRun(
        name="world",
        spec=LinkByUID(scope="test_scope", id="test_value"),
        uids={"test_scope": "another_test_value",
              "other_test": "also_valid"
              },
    )
    ms1 = MaterialSpec(
        name="material",
        process=LinkByUID(scope="test_scope", id="test_value"),
        uids={"second_scope": "this_is_an_id"},
    )
    mr1 = MaterialRun(
        name="material_run",
        spec=LinkByUID(scope="second_scope", id="this_is_an_id"),
        process=LinkByUID(scope="test_scope", id="another_test_value"),
    )

    pr2 = ProcessRun(
        name="goodbye",
        spec=LinkByUID.from_entity(ps1),
        uids={"test_scope": "the_other_value"},
    )
    mr2 = MaterialRun(
        name="cruel",
        spec=LinkByUID.from_entity(ms1),
        process=LinkByUID.from_entity(pr2),
    )

    gems = [ps1, pr1, ms1, mr1, pr2, mr2]
    gem_index = make_index(gems)
    for gem in gems:
        for scope in gem.uids:
            assert (scope, gem.uids[scope]) in gem_index
            assert gem_index[(scope, gem.uids[scope])] == gem

    # Make sure substitute_objects can consume the index
    subbed = substitute_objects(mr1, gem_index)
    assert subbed.spec.uids == ms1.uids
示例#17
0
def test_substitute_equivalence():
    """PLA-6423: verify that substitutions match up."""
    spec = ProcessSpec(name="old spec", uids={'scope': 'spec'})
    run = ProcessRun(name="old run",
                     uids={'scope': 'run'},
                     spec=LinkByUID(id='spec', scope="scope"))

    # make a dictionary from ids to objects, to be used in substitute_objects
    gem_index = make_index([run, spec])
    substitute_objects(obj=run, index=gem_index, inplace=True)
    assert spec == run.spec
示例#18
0
def make_node(name: str,
              *,
              process_name: str = None,
              process_template: ProcessTemplate = None,
              material_template: MaterialTemplate = None) -> MaterialRun:
    """
    Generate a material-process spec-run quadruple.

    Parameters
    ----------
    name: str
        Name of the MaterialRun and MaterialSpec.
    process_name: str
        Name of the ProcessRun and ProcessSpec.  Defaults to
        `process_template.name` if `process_template` is defined, else `name`.
    process_template: ProcessTemplate
        ProcessTemplate for the quadruple.
    material_template: MaterialTemplate
        MaterialTemplate for the quadruple.

    Returns
    --------
    MaterialRun
        A MaterialRun with linked processes, specs and templates

    """
    if process_name is None:
        if process_template is None:
            process_name = name
        else:
            process_name = process_template.name

    my_process_spec = ProcessSpec(
        name=process_name,
        template=process_template
    )

    my_process_run = ProcessRun(
        name=process_name,
        spec=my_process_spec
    )

    my_mat_spec = MaterialSpec(
        name=name,
        process=my_process_spec,
        template=material_template
    )

    my_mat_run = MaterialRun(
        name=name,
        process=my_process_run,
        spec=my_mat_spec
    )
    return my_mat_run
示例#19
0
def test_unexpected_deserialization():
    """Trying to deserialize an unexpected class should throw a TypeError."""
    class DummyClass(DictSerializable):
        typ = 'dummy_class'

        def __init__(self, foo):
            self.foo = foo

    # DummyClass cannot be serialized since dumps will round-robin serialize
    # in the substitute_links method
    with pytest.raises(TypeError):
        dumps(ProcessRun("A process", notes=DummyClass("something")))
示例#20
0
def test_flatten_empty_history():
    """Test that flatten works when the objects are empty and go through a whole history."""
    procured = ProcessSpec(name="procured")
    input = MaterialSpec(name="foo", process=procured)
    transform = ProcessSpec(name="transformed")
    ingredient = IngredientSpec(name="input", material=input, process=transform)

    procured_run = ProcessRun(name="procured", spec=procured)
    input_run = MaterialRun(name="foo", process=procured_run, spec=input)
    transform_run = ProcessRun(name="transformed", spec=transform)
    ingredient_run = IngredientRun(material=input_run, process=transform_run, spec=ingredient)

    assert len(flatten(procured)) == 1
    assert len(flatten(input)) == 1
    assert len(flatten(ingredient)) == 3
    assert len(flatten(transform)) == 3

    assert len(flatten(procured_run)) == 3
    assert len(flatten(input_run)) == 3
    assert len(flatten(ingredient_run)) == 7
    assert len(flatten(transform_run)) == 7
示例#21
0
def test_link_by_uid():
    """Test that linking works."""
    root = MaterialRun(name='root', process=ProcessRun(name='root proc'))
    leaf = MaterialRun(name='leaf', process=ProcessRun(name='leaf proc'))
    IngredientRun(process=root.process, material=leaf)
    IngredientRun(process=root.process,
                  material=LinkByUID.from_entity(leaf, scope='id'))

    # Paranoid assertions about equality's symmetry since it's implemented in 2 places
    assert root.process.ingredients[0].material == root.process.ingredients[
        1].material
    assert root.process.ingredients[0].material.__eq__(
        root.process.ingredients[1].material)
    assert root.process.ingredients[1].material.__eq__(
        root.process.ingredients[0].material)

    # Verify hash collision on equal LinkByUIDs
    assert LinkByUID.from_entity(leaf) in {LinkByUID.from_entity(leaf)}

    copy = loads(dumps(root))
    assert copy.process.ingredients[0].material == copy.process.ingredients[
        1].material
示例#22
0
def test_signature():
    """Exercise various permutations of the substitute_links sig."""
    spec = ProcessSpec("A process spec", uids={'my': 'spec'})

    with pytest.warns(DeprecationWarning):
        run1 = ProcessRun("First process run", uids={'my': 'run1'}, spec=spec)
        assert isinstance(substitute_links(run1, native_uid='my').spec, LinkByUID)

    run2 = ProcessRun("Second process run", uids={'my': 'run2'}, spec=spec)
    assert isinstance(substitute_links(run2, scope='my').spec, LinkByUID)

    run3 = ProcessRun("Third process run", uids={'my': 'run3'}, spec=spec)
    assert isinstance(substitute_links(run3, 'my').spec, LinkByUID)

    with pytest.raises(ValueError):  # Test deprecated auto-population
        run4 = ProcessRun("Fourth process run", uids={'my': 'run4'}, spec=spec)
        assert isinstance(substitute_links(run4, 'other', allow_fallback=False).spec, LinkByUID)

    with pytest.warns(DeprecationWarning):
        with pytest.raises(ValueError):  # Test deprecated auto-population
            run5 = ProcessRun("Fifth process run", uids={'my': 'run4'}, spec=spec)
            assert isinstance(substitute_links(run5, scope="my", native_uid="my").spec, LinkByUID)
示例#23
0
def test_many_ingredients():
    """Test that ingredients remain connected to processes when round-robined through json."""
    proc = ProcessRun("foo", spec=ProcessSpec("sfoo"))
    expected = []
    for i in range(10):
        mat = MaterialRun(name=str(i), spec=MaterialSpec("s{}".format(i)))
        i_spec = IngredientSpec(name="i{}".format(i), material=mat.spec, process=proc.spec)
        IngredientRun(process=proc, material=mat, spec=i_spec)
        expected.append("i{}".format(i))

    reloaded = loads(dumps(proc))
    assert len(list(reloaded.ingredients)) == 10
    names = [x.name for x in reloaded.ingredients]
    assert sorted(names) == sorted(expected)
示例#24
0
def test_process_spec():
    """Tests that the Process Spec/Run connection persists when serializing."""
    # Create the ProcessSpec
    condition1 = Condition(name="a condition on the process in general")
    spec = ProcessSpec("Spec", conditions=condition1)

    # Create the ProcessRun with a link to the ProcessSpec from above
    condition2 = Condition(
        name="a condition on this process run in particular")
    process = ProcessRun("Run", conditions=condition2, spec=spec)

    copy_process = loads(dumps(process))
    assert dumps(copy_process.spec) == dumps(spec), \
        "Process spec should be preserved through serialization"
def test_process_run():
    """Test that a process run can house a material, and that it survives serde."""
    process_run = ProcessRun("Bake a cake", uids={'My_ID': str(17)})
    material_run = MaterialRun("A cake", process=process_run)

    # Check that a bi-directional link is established
    assert material_run.process == process_run
    assert process_run.output_material == material_run

    copy_material = loads(dumps(material_run))
    assert dumps(copy_material) == dumps(material_run)

    assert 'output_material' in repr(process_run)
    assert 'process' in repr(material_run)
示例#26
0
def test_ingredient_run():
    """Tests that a process can house an ingredient, and that pairing survives serialization."""
    # Create a ProcessSpec
    proc_run = ProcessRun(name="a process spec", tags=["tag1", "tag2"])
    ingred_run = IngredientRun(material=MaterialRun(name='Raw'),
                               process=proc_run)

    # Make copies of both specs
    proc_run_copy = loads(dumps(proc_run))

    assert proc_run_copy == proc_run, "Full structure wasn't preserved across serialization"

    assert 'process' in repr(ingred_run)
    assert 'ingredients' in repr(proc_run)
def test_dictionary_substitution():
    """substitute_objects() should substitute LinkByUIDs that occur in dict keys and values."""
    proc = ProcessRun("A process", uids={'id': '123'})
    mat = MaterialRun("A material", uids={'generic id': '38f8jf'})

    proc_link = LinkByUID.from_entity(proc)
    mat_link = LinkByUID.from_entity(mat)
    index = {(mat_link.scope.lower(), mat_link.id): mat,
             (proc_link.scope.lower(), proc_link.id): proc}

    test_dict = {LinkByUID.from_entity(proc): LinkByUID.from_entity(mat)}
    subbed = substitute_objects(test_dict, index)
    k, v = next((k, v) for k, v in subbed.items())
    assert k == proc
    assert v == mat
示例#28
0
def test_equality():
    """Test that equality check works as expected."""
    spec = ProcessSpec("A spec", tags=["a tag"])
    run1 = ProcessRun("A process", spec=spec)

    run2 = deepcopy(run1)
    assert run1 == run2, "Copy somehow failed"
    IngredientRun(process=run2)
    assert run1 != run2

    run3 = deepcopy(run2)
    assert run3 == run2, "Copy somehow failed"
    run3.ingredients[0].tags.append('A tag')
    assert run3 != run2

    run4 = next(x for x in flatten(run3, 'test-scope')
                if isinstance(x, ProcessRun))
    assert run4 == run3, "Flattening removes measurement references, but that's okay"
示例#29
0
def test_from_entity():
    """Test permutations of LinkByUID.from_entity arguments."""
    run = MaterialRun(name='leaf', process=ProcessRun(name='leaf proc'))
    assert LinkByUID.from_entity(run).scope == 'auto'
    assert LinkByUID.from_entity(run, scope='missing').scope == 'auto'
    assert len(run.uids) == 1

    run.uids['foo'] = 'bar'
    link1 = LinkByUID.from_entity(run, scope='foo')
    assert (link1.scope, link1.id) == ('foo', 'bar')

    with pytest.deprecated_call():
        assert LinkByUID.from_entity(run, 'foo').scope == 'foo'

    with pytest.deprecated_call():
        assert LinkByUID.from_entity(run, name='foo').scope == 'foo'

    with pytest.raises(ValueError):
        LinkByUID.from_entity(run, name='scope1', scope='scope2')
def test_recursive_foreach():
    """Test that recursive foreach will actually walk through a material history."""
    mat_run = MaterialRun("foo")
    process_run = ProcessRun("bar")
    IngredientRun(process=process_run, material=mat_run)
    output = MaterialRun("material", process=process_run)

    # property templates are trickier than templates because they are referenced in attributes
    template = PropertyTemplate("prop", bounds=RealBounds(0, 1, ""))
    prop = Property("prop", value=NominalReal(1.0, ""), template=template)
    MeasurementRun("check", material=output, properties=prop)

    types = []
    recursive_foreach(output, lambda x: types.append(x.typ))

    expected = [
        "ingredient_run", "material_run", "material_run", "process_run",
        "measurement_run", "property_template"
    ]
    assert sorted(types) == sorted(expected)