Exemple #1
0
def test_sac():
    """Make S&C table and assert that it can be serialized."""
    sac = make_strehlow_objects(import_table())
    sac_tbl = make_strehlow_table(sac)

    # Check that all shapes of records serialize and deserialize
    for comp in sac:
        assert je.loads(je.dumps(comp)) == comp

    # Verify that specs are shared when compounds match
    for comp1 in sac:
        for comp2 in sac:
            # xand
            assert (comp1.name == comp2.name) == (comp1.spec.uids == comp2.spec.uids)

    # Look at each different combination of Value types in a S&C record
    smaller = minimal_subset(sac_tbl['content'])
    # Make sure that the diversity of value types isn't lost, e.g. something is being None'd
    assert len(smaller) == 162

    # Make sure there's no migration with repeated serialization
    for row in sac_tbl:
        assert je.dumps(je.loads(je.dumps(row))) == je.dumps(row)

    # Verify that the serialization trick for mocking a structured table works
    json.dumps(json.loads(je.dumps(sac_tbl))["object"], indent=2)
Exemple #2
0
def test_material_spec():
    """Test that Process/Material Spec link survives serialization."""
    # Create a ProcessSpec
    proc_spec = ProcessSpec(name="a process spec", tags=["tag1", "tag2"])

    # Create MaterialSpec without a ProcessSpec
    prop = Property(name="The material is a solid",
                    value=DiscreteCategorical(probabilities="solid"))
    mat_spec = MaterialSpec(name="a material spec",
                            properties=PropertyAndConditions(prop))
    assert mat_spec.process is None, \
        "MaterialSpec should be initialized with no ProcessSpec, by default"

    # Assign a ProcessSpec to mat_spec, first ensuring that the type is enforced
    with pytest.raises(TypeError):
        mat_spec.process = 17
    mat_spec.process = proc_spec

    # Assert circular links
    assert dumps(proc_spec.output_material.process) == dumps(proc_spec), \
        "ProcessSpec should link to MaterialSpec that links back to itself"

    assert dumps(mat_spec.process.output_material) == dumps(mat_spec), \
        "MaterialSpec should link to ProcessSpec that links back to itself"

    # Make copies of both specs
    mat_spec_copy = loads(dumps(mat_spec))
    proc_spec_copy = loads(dumps(proc_spec))

    assert proc_spec_copy.output_material == mat_spec, \
        "Serialization should preserve link from ProcessSpec to MaterialSpec"

    assert mat_spec_copy.process == proc_spec, \
        "Serialization should preserve link from MaterialSpec to ProcessSpec"
Exemple #3
0
def test_attribute_serde():
    """An attribute with a link to an attribute template should be copy-able."""
    prop_tmpl = PropertyTemplate(name='prop_tmpl',
                                 bounds=RealBounds(0, 2, 'm'))
    prop = Property(name='prop', template=prop_tmpl, value=NominalReal(1, 'm'))
    meas_spec = MeasurementSpec("a spec")
    meas = MeasurementRun("a measurement", spec=meas_spec, properties=[prop])
    assert loads(dumps(prop)) == prop
    assert loads(dumps(meas)) == meas
    assert isinstance(prop.template, PropertyTemplate)
def test_numpy():
    """Test that ndarrays, Series work as well."""
    assert len(array_like()) < 5  # In case we extend at some point

    if len(array_like()) > 2:  # Test numpy
        import numpy as np
        np_bounds = CategoricalBounds(np.array(["spam", "eggs"], dtype=object))
        np_copy = loads(dumps(np_bounds))
        assert np_copy == np_bounds

    if len(array_like()) > 3:  # Test numpy
        import pandas as pd
        pd_bounds = CategoricalBounds(pd.Series(["spam", "eggs"]))
        pd_copy = loads(dumps(pd_bounds))
        assert pd_copy == pd_bounds
def test_material_run():
    """
    Test the ability to create a MaterialRun that is linked to a MaterialSpec.

    Make sure all enumerated values are respected, and check consistency after
    serializing and deserializing.
    """
    # Define a property, and make sure that an inappropriate value for origin throws ValueError
    with pytest.raises(ValueError):
        prop = Property(name="A property", origin="bad origin", value=NominalReal(17, units=''))

    # Create a MaterialSpec with a property
    prop = Property(name="A property", origin="specified", value=NominalReal(17, units=''))
    mat_spec = MaterialSpec(
        name="a specification for a material",
        properties=PropertyAndConditions(prop),
        notes="Funny lookin'"
    )

    # Make sure that when property is serialized, origin (an enumeration) is serialized as a string
    copy_prop = json.loads(dumps(mat_spec))
    copy_origin = copy_prop["context"][0]["properties"][0]['property']['origin']
    assert isinstance(copy_origin, str)

    # Create a MaterialRun, and make sure an inappropriate value for sample_type throws ValueError
    with pytest.raises(ValueError):
        mat = MaterialRun("name", spec=mat_spec, sample_type="imaginary")
    mat = MaterialRun("name", spec=mat_spec, sample_type="virtual")

    # ensure that serialization does not change the MaterialRun
    copy = loads(dumps(mat))
    assert dumps(copy) == dumps(mat), \
        "Material run is modified by serialization or deserialization"
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
def test_process_id_link():
    """Test that a process run can house a LinkByUID object, and that it survives serde."""
    uid = str(uuid4())
    proc_link = LinkByUID(scope='id', id=uid)
    mat_run = MaterialRun("Another cake", process=proc_link)
    copy_material = loads(dumps(mat_run))
    assert dumps(copy_material) == dumps(mat_run)
def test_passthrough_bounds():
    """Test that unspecified Bounds are accepted and set to None."""
    template = ProcessTemplate('foo',
                               conditions=[
                                   (LinkByUID('1', '2'), None),
                                   [LinkByUID('3', '4'), None],
                                   LinkByUID('5', '6'),
                                   ConditionTemplate('foo',
                                                     bounds=IntegerBounds(
                                                         0, 10)),
                               ])
    assert len(template.conditions) == 4
    for _, bounds in template.conditions:
        assert bounds is None
    copied = loads(dumps(template))
    assert len(copied.conditions) == 4
    for _, bounds in copied.conditions:
        assert bounds is None
    from_dict = ProcessTemplate.build({
        'type':
        'process_template',
        'name':
        'foo',
        'conditions': [[
            {
                'scope': 'foo',
                'id': 'bar',
                'type': 'link_by_uid',
            },
            None,
        ]],
    })
    assert len(from_dict.conditions) == 1
def test_recursive_equals():
    """Verify that the recursive/crawling equals behaves well."""
    cake = make_cake()
    copy = loads(dumps(cake))
    assert cake == copy

    copy.process.ingredients[0].material.process.ingredients[0].material.tags.append('Hi')
    assert cake != copy
Exemple #10
0
def test_material_soft_link():
    """Test that a measurement run can link to a material run, and that it survives serde."""
    dye = MaterialRun("rhodamine",
                      file_links=FileLink(filename='a.csv', url='/a/path'))
    assert dye.measurements == [], "default value of .measurements should be an empty list"

    # The .measurements member should not be settable
    with pytest.raises(AttributeError):
        dye.measurements = [MeasurementRun("Dummy")]

    absorbance = MeasurementRun(name="Absorbance",
                                uids={'id': str(uuid4())},
                                properties=[
                                    Property(name='Abs at 500 nm',
                                             value=NominalReal(0.1, ''))
                                ])
    assert absorbance.material is None, "Measurements should have None as the material by default"
    absorbance.material = dye
    assert absorbance.material == dye, "Material not set correctly for measurement"
    assert dye.measurements == [
        absorbance
    ], "Soft-link from material to measurement not created"

    fluorescence = MeasurementRun(name="Fluorescence",
                                  uids={'id': str(uuid4())},
                                  properties=[
                                      Property(name='PL counts at 550 nm',
                                               value=NominalReal(30000, ''))
                                  ],
                                  material=dye)

    assert fluorescence.material == dye, "Material not set correctly for measurement"
    assert dye.measurements == [absorbance, fluorescence], \
        "Soft-link from material to measurements not created"

    assert loads(dumps(absorbance)) == absorbance, \
        "Measurement should remain unchanged when serialized"
    assert loads(dumps(fluorescence)) == fluorescence, \
        "Measurement should remain unchanged when serialized"

    assert 'measurements' in repr(dye)
    assert 'material' in repr(fluorescence)
    assert 'material' in repr(absorbance)

    subbed = substitute_links(dye)
    assert 'measurements' in repr(subbed)
def test_json_serde():
    """Test that values can be ser/de using our custom json loads/dumps."""
    # Enums are only used in the context of another class --
    # it is not possible to deserialize to enum with the current
    # serialization strategy (plain string) without this context.
    original = Property(name="foo", origin=Origin.MEASURED)
    copy = loads(dumps(original))
    assert original == copy
def test_uid_deser():
    """Test that uids continue to be a CaseInsensitiveDict after deserialization."""
    material = MaterialRun("Input material", tags="input", uids={'Sample ID': '500-B'})
    ingredient = IngredientRun(material=material)
    ingredient_copy = loads(dumps(ingredient))
    assert isinstance(ingredient_copy.uids, CaseInsensitiveDict)
    assert ingredient_copy.material == material
    assert ingredient_copy.material.uids['sample id'] == material.uids['Sample ID']
Exemple #13
0
def test_source():
    """Test that source can be set, serialized, and deserialized."""
    source = PerformedSource(performed_by="Marie Curie",
                             performed_date="1898-07-01")
    measurement = MeasurementRun(name="Polonium", source=source)
    assert loads(dumps(measurement)).source.performed_by == "Marie Curie"

    with pytest.raises(TypeError):
        MeasurementRun(name="Polonium", source="Marie Curie on 1898-07-01")
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))

    copy = loads(dumps(root))
    assert copy.process.ingredients[0].material == copy.process.ingredients[
        1].material
Exemple #15
0
def test_condition_template():
    """Test creation and serde of condition templates."""
    bounds = RealBounds(2.5, 10.0, default_units='cm')
    template = ConditionTemplate("Chamber width", bounds=bounds, description="width of chamber")
    assert template.uids is not None  # uids should be added automatically

    # Take template through a serde cycle and ensure that it is unchanged
    assert ConditionTemplate.build(template.dump()) == template
    # A more complicated cycle that goes through both gemd-python and citrine-python serde.
    assert ConditionTemplate.build(loads(dumps(template.dump())).as_dict()) == template
Exemple #16
0
def test_ingredient_spec():
    """Tests that a process can house an ingredient, and that pairing survives serialization."""
    # Create a ProcessSpec
    proc_spec = ProcessSpec(name="a process spec", tags=["tag1", "tag2"])
    IngredientSpec(name='Input',
                   material=MaterialSpec(name='Raw'),
                   process=proc_spec)

    # Make copies of both specs
    proc_spec_copy = loads(dumps(proc_spec))

    assert proc_spec_copy == proc_spec, "Full structure wasn't preserved across serialization"
Exemple #17
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)
Exemple #18
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(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(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_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)
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)
def test_process_attachment():
    """Test that a process can be attached to a material, and that the connection survives serde"""
    cake = MaterialRun('Final cake')
    cake.process = ProcessRun('Icing', uids={'id': '12345'})
    cake_data = cake.dump()

    cake_copy = loads(dumps(cake_data)).as_dict()

    assert cake_copy['name'] == cake.name
    assert cake_copy['uids'] == cake.uids
    assert cake.process.uids['id'] == cake_copy['process'].uids['id']

    reconstituted_cake = MaterialRun.build(cake_copy)
    assert isinstance(reconstituted_cake, MaterialRun)
    assert isinstance(reconstituted_cake.process, ProcessRun)
Exemple #22
0
def test_measurement_spec():
    """Test the measurement spec/run connection survives ser/de."""
    condition = Condition(name="Temp condition",
                          value=NominalReal(nominal=298, units='kelvin'))
    parameter = Parameter(name="Important parameter")
    spec = MeasurementSpec(name="Precise way to do a measurement",
                           parameters=parameter,
                           conditions=condition)

    # Create a measurement run from this measurement spec
    measurement = MeasurementRun("The Measurement",
                                 conditions=condition,
                                 spec=spec)

    copy = loads(dumps(measurement))
    assert dumps(copy.spec) == dumps(measurement.spec), \
        "Measurement spec should be preserved if measurement run is serialized"
def test_case_insensitive_rehydration():
    """

    Test that loads() can connect id scopes with different cases.

    This situation should not occur in gemd on its own, but faraday returns LinkOrElse objects
    with the default scope "ID", whereas citrine-python assigns ids with the scope "id".
    The uids dictionary is supposed to be case-insensitive, so rehydration should still work.
    """
    # A simple json string that could be loaded, representing an ingredient linked to a material.
    # The material link has "scope": "ID", whereas the material in the context list, which is
    # to be loaded, has uid with scope "id".
    json_str = '''
          {
            "context": [
              {
                "uids": {
                  "id": "9118c2d3-1c38-47fe-a650-c2b92fdb6777"
                },
                "type": "material_run",
                "name": "flour"
              }
            ],
            "object": {
              "type": "ingredient_run",
              "uids": {
                "id": "8858805f-ec02-49e4-ba3b-d784e2aea3f8"
              },
              "material": {
                "type": "link_by_uid",
                "scope": "ID",
                "id": "9118c2d3-1c38-47fe-a650-c2b92fdb6777"
              },
              "process": {
                "type": "link_by_uid",
                "scope": "ID",
                "id": "9148c2d3-2c38-47fe-b650-c2b92fdb6777"
              }
            }
          }
       '''
    loaded_ingredient = loads(json_str)
    # The ingredient's material will either be a MaterialRun (pass) or a LinkByUID (fail)
    assert isinstance(loaded_ingredient.material, MaterialRun)
Exemple #24
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
Exemple #25
0
def test_material_id_link():
    """Check that a measurement can be linked to a material that is a LinkByUID."""
    mat = LinkByUID('id', str(uuid4()))
    meas = MeasurementRun("name", material=mat)
    assert meas.material == mat
    assert loads(dumps(meas)) == meas
def test_deeply_nested_rehydration():
    """
    Tests that loads fully replaces links with objects.

    In particular, this test makes sure that loads is robust to objects being referenced by
    LinkByUid before they are "declared" in the JSON array.
    """
    json_str = '''
{
  "context": [
    {
      "type": "process_spec",
      "parameters": [
        {
          "type": "parameter",
          "name": "oven",
          "value": {
            "type": "nominal_categorical",
            "category": "oven 1"
          },
          "template": {
            "type": "link_by_uid",
            "scope": "id",
            "id": "536a3ebb-55a4-4560-a6df-fba44cdb917a"
          },
          "origin": "unknown",
          "file_links": []
        }
      ],
      "conditions": [],
      "uids": {
        "id": "f77dc327-ef44-4a39-a617-061ace5fa789"
      },
      "tags": [],
      "name": "Ideal baking",
      "file_links": []
    },
    {
      "type": "process_run",
      "spec": {
        "type": "link_by_uid",
        "scope": "id",
        "id": "f77dc327-ef44-4a39-a617-061ace5fa789"
      },
      "parameters": [
        {
          "type": "parameter",
          "name": "oven",
          "value": {
            "type": "nominal_categorical",
            "category": "oven 1"
          },
          "template": {
            "type": "link_by_uid",
            "scope": "id",
            "id": "536a3ebb-55a4-4560-a6df-fba44cdb917a"
          },
          "origin": "unknown",
          "file_links": []
        }
      ],
      "conditions": [],
      "uids": {
        "id": "7cb9471b-0c90-4fd9-bfe1-0e9d7602ab0d",
        "my_id": "jvkzrlnm"
      },
      "tags": [
        "cake::yes"
      ],
      "name": "cake baking",
      "file_links": []
    },
    {
      "type": "material_spec",
      "properties": [],
      "uids": {
        "id": "230fc837-8a19-402c-86ad-e451b7a80f9d"
      },
      "tags": [],
      "name": "Flour",
      "file_links": []
    },
    {
      "type": "material_spec",
      "process": {
        "type": "link_by_uid",
        "scope": "id",
        "id": "f77dc327-ef44-4a39-a617-061ace5fa789"
      },
      "properties": [],
      "uids": {
        "id": "b935aa7d-93a4-407f-937f-cca32d7a8413"
      },
      "tags": [],
      "name": "An ideal cake",
      "file_links": []
    },
    {
      "type": "material_spec",
      "properties": [
        {
          "property": {
            "type": "property",
            "name": "mass",
            "value": {
              "type": "normal_real",
              "mean": 0.84,
              "std": 0.04,
              "units": "gram"
            },
            "template": {
              "type": "link_by_uid",
              "scope": "id",
              "id": "3b46b191-b3d0-4b31-bdba-377cca315cbd"
            },
            "origin": "unknown",
            "file_links": []
          },
          "conditions": [
            {
              "type": "condition",
              "name": "temperature",
              "value": {
                "type": "nominal_real",
                "nominal": 20,
                "units": "degC"
              },
              "template": {
                "type": "link_by_uid",
                "scope": "id",
                "id": "09fb94ab-17fb-4428-a20e-d6b0d0ae5fb2"
              },
              "origin": "unknown",
              "file_links": []
            }
          ],
          "type": "property_and_conditions"
        }
      ],
      "uids": {
        "id": "39ec0605-0b9b-443c-ab6a-4d7bc1b73b24"
      },
      "tags": [],
      "name": "Butter",
      "file_links": []
    },
    {
      "type": "ingredient_spec",
      "material": {
        "type": "link_by_uid",
        "scope": "id",
        "id": "39ec0605-0b9b-443c-ab6a-4d7bc1b73b24"
      },
      "process": {
        "type": "link_by_uid",
        "scope": "id",
        "id": "f77dc327-ef44-4a39-a617-061ace5fa789"
      },
      "labels": [],
      "uids": {
        "id": "118eacb7-6edc-4e57-b40b-2971481d37e5"
      },
      "tags": [],
      "file_links": []
    },
    {
      "type": "ingredient_spec",
      "material": {
        "type": "link_by_uid",
        "scope": "id",
        "id": "230fc837-8a19-402c-86ad-e451b7a80f9d"
      },
      "process": {
        "type": "link_by_uid",
        "scope": "id",
        "id": "f77dc327-ef44-4a39-a617-061ace5fa789"
      },
      "labels": [],
      "absolute_quantity": {
        "type": "normal_real",
        "mean": 500,
        "std": 50,
        "units": "gram"
      },
      "uids": {
        "id": "f694d2cc-5b00-42ef-92b7-dee3cdc7239a"
      },
      "tags": [],
      "file_links": []
    },
    {
      "type": "material_run",
      "spec": {
        "type": "link_by_uid",
        "scope": "id",
        "id": "230fc837-8a19-402c-86ad-e451b7a80f9d"
      },
      "sample_type": "unknown",
      "uids": {
        "id": "76185e4f-c778-4654-a2ae-cc49851e291f"
      },
      "tags": [],
      "name": "Flour",
      "file_links": []
    },
        {
      "type": "material_run",
      "spec": {
        "type": "link_by_uid",
        "scope": "id",
        "id": "39ec0605-0b9b-443c-ab6a-4d7bc1b73b24"
      },
      "sample_type": "unknown",
      "uids": {
        "id": "605bf096-3b2d-4c3b-afaf-f77bcff9806f"
      },
      "tags": [],
      "name": "Butter",
      "file_links": []
    },{
      "type": "material_run",
      "process": {
        "type": "link_by_uid",
        "scope": "id",
        "id": "7cb9471b-0c90-4fd9-bfe1-0e9d7602ab0d"
      },
      "spec": {
        "type": "link_by_uid",
        "scope": "id",
        "id": "b935aa7d-93a4-407f-937f-cca32d7a8413"
      },
      "sample_type": "unknown",
      "uids": {
        "id": "f0f41fb9-32dc-4903-aaf4-f369de71530f"
      },
      "tags": [],
      "name": "A cake",
      "file_links": []
    },
    {
      "type": "ingredient_run",
      "material": {
        "type": "link_by_uid",
        "scope": "id",
        "id": "76185e4f-c778-4654-a2ae-cc49851e291f"
      },
      "process": {
        "type": "link_by_uid",
        "scope": "id",
        "id": "7cb9471b-0c90-4fd9-bfe1-0e9d7602ab0d"
      },
      "spec": {
        "type": "link_by_uid",
        "scope": "id",
        "id": "f694d2cc-5b00-42ef-92b7-dee3cdc7239a"
      },
      "name": "500 g flour",
      "labels": [],
      "uids": {
        "id": "36aa5bff-c89d-43fa-95c8-fa6b710061d8"
      },
      "tags": [],
      "file_links": []
    },
    {
      "type": "ingredient_run",
      "material": {
        "type": "link_by_uid",
        "scope": "id",
        "id": "605bf096-3b2d-4c3b-afaf-f77bcff9806f"
      },
      "process": {
        "type": "link_by_uid",
        "scope": "id",
        "id": "7cb9471b-0c90-4fd9-bfe1-0e9d7602ab0d"
      },
      "spec": {
        "type": "link_by_uid",
        "scope": "id",
        "id": "118eacb7-6edc-4e57-b40b-2971481d37e5"
      },
      "name": "1 stick butter",
      "labels": [],
      "absolute_quantity": {
        "type": "nominal_real",
        "nominal": 1,
        "units": "dimensionless"
      },
      "uids": {
        "id": "91ab45f2-ceec-4109-8f74-2f9964a4bc2c"
      },
      "tags": [],
      "file_links": []
    },
    {
      "type": "measurement_spec",
      "parameters": [],
      "conditions": [],
      "uids": {
        "id": "85c911eb-af5a-4c34-9b59-b88b84780239"
      },
      "tags": [],
      "name": "Tasty spec",
      "file_links": []
    },
    {
      "type": "measurement_run",
      "spec": {
        "type": "link_by_uid",
        "scope": "id",
        "id": "85c911eb-af5a-4c34-9b59-b88b84780239"
      },
      "material": {
        "type": "link_by_uid",
        "scope": "id",
        "id": "f0f41fb9-32dc-4903-aaf4-f369de71530f"
      },
      "properties": [],
      "parameters": [],
      "conditions": [],
      "uids": {
        "id": "9673f15d-76df-4dcd-a409-7152cb629a3f"
      },
      "tags": [
        "example::tag"
      ],
      "name": "Tastiness",
      "notes": "it is tasty",
      "file_links": []
    }
  ],
  "object": {
    "type": "link_by_uid",
    "scope": "id",
    "id": "f0f41fb9-32dc-4903-aaf4-f369de71530f"
  }
}
    '''
    material_history = loads(json_str)
    assert isinstance(material_history.process.ingredients[1].spec, IngredientSpec)
    assert isinstance(material_history.measurements[0], MeasurementRun)

    copied = loads(dumps(material_history))
    assert isinstance(copied.process.ingredients[1].spec, IngredientSpec)
    assert isinstance(copied.measurements[0], MeasurementRun)
def test_deserialize_extra_fields():
    """Extra JSON fields should be ignored in deserialization."""
    json_data = '{"context": [],' \
                ' "object": {"nominal": 5, "type": "nominal_integer", "extra garbage": "foo"}}'
    assert(loads(json_data) == NominalInteger(nominal=5))
Exemple #28
0
def test_json():
    """Test that serialization works (empty dictionary)."""
    bounds = MolecularStructureBounds()
    copy = loads(dumps(bounds))
    assert copy == bounds
Exemple #29
0
 def test_for_loss(obj):
     assert (obj == loads(dumps(obj)))
Exemple #30
0
def test_cake():
    """Create cake, serialize, deserialize."""
    cake = make_cake()

    def test_for_loss(obj):
        assert (obj == loads(dumps(obj)))

    recursive_foreach(cake, test_for_loss)

    # And verify equality was working in the first place
    cake2 = loads(dumps(cake))
    cake2.name = "It's a trap!"
    assert (cake2 != cake)
    cake2.name = cake.name
    assert (cake == cake2)
    cake2.uids['new'] = "It's a trap!"
    assert (cake2 != cake)

    # Check that all the objects show up
    tot_count = 0

    def increment(dummy):
        nonlocal tot_count
        tot_count += 1

    recursive_foreach(cake, increment)
    assert tot_count == 131

    # And make sure nothing was lost
    tot_count = 0
    recursive_foreach(loads(dumps(complete_material_history(cake))), increment)
    assert tot_count == 131

    # Check that no UIDs collide
    uid_seen = dict()

    def check_ids(obj):
        nonlocal uid_seen
        for scope in obj.uids:
            lbl = '{}::{}'.format(scope, obj.uids[scope])
            if lbl in uid_seen:
                assert uid_seen[lbl] == id(obj)
            uid_seen[lbl] = id(obj)

    recursive_foreach(cake, check_ids)

    queue = [cake]
    seen = set()
    while queue:
        obj = queue.pop()
        if obj in seen:
            continue

        seen.add(obj)

        if isinstance(obj, MaterialSpec):
            if obj.process is not None:
                queue.append(obj.process)
                assert obj.process.output_material == obj
        elif isinstance(obj, MaterialRun):
            if obj.process is not None:
                queue.append(obj.process)
                assert obj.process.output_material == obj
            if obj.measurements:
                queue.extend(obj.measurements)
                for msr in obj.measurements:
                    assert msr.material == obj
            if obj.spec is not None:
                queue.append(obj.spec)
                if obj.process is not None:
                    assert obj.spec.process == obj.process.spec
        elif isinstance(obj, ProcessRun):
            if obj.ingredients:
                queue.extend(obj.ingredients)
            if obj.output_material is not None:
                queue.append(obj.output_material)
                assert obj.output_material.process == obj
                if obj.spec is not None:
                    assert obj.spec.output_material == obj.output_material.spec
        elif isinstance(obj, ProcessSpec):
            if obj.ingredients:
                queue.extend(obj.ingredients)
            if obj.output_material is not None:
                queue.append(obj.output_material)
                assert obj.output_material.process == obj
        elif isinstance(obj, MeasurementSpec):
            pass  # Doesn't link
        elif isinstance(obj, MeasurementRun):
            if obj.spec:
                queue.append(obj.spec)
        elif isinstance(obj, IngredientSpec):
            if obj.material:
                queue.append(obj.material)
        elif isinstance(obj, IngredientRun):
            if obj.spec:
                queue.append(obj.spec)
                if obj.material and isinstance(obj.material, MaterialRun):
                    assert obj.spec.material == obj.material.spec
            if obj.material:
                queue.append(obj.material)