def test_errors(): """Make sure invalid bounds raise value errors.""" with pytest.raises(ValueError): IntegerBounds() with pytest.raises(ValueError): IntegerBounds(0, float("inf")) with pytest.raises(ValueError): IntegerBounds(10, 1)
def test_contains(): """Test contains logic.""" int_bounds = IntegerBounds(0, 2) assert int_bounds.contains(IntegerBounds(0, 1)) assert int_bounds.contains(IntegerBounds(1, 2)) assert not int_bounds.contains(IntegerBounds(1, 3)) assert not int_bounds.contains(None) with pytest.raises(TypeError): int_bounds.contains([0, 1])
def test_filter_by_attribute_bounds(collection, session): # Given sample_run = MaterialRunDataFactory() session.set_response({'contents': [sample_run]}) link = LinkByUIDFactory() bounds = {link: IntegerBounds(1, 5)} # When runs = collection.filter_by_attribute_bounds(bounds, page=1, per_page=10) # Then assert 1 == session.num_calls expected_call = FakeCall( method='POST', path='projects/{}/material-runs/filter-by-attribute-bounds'.format( collection.project_id), params={ "page": 1, "per_page": 10, "dataset_id": str(collection.dataset_id) }, json={ 'attribute_bounds': { link.id: { 'lower_bound': 1, 'upper_bound': 5, 'type': 'integer_bounds' } } }) assert expected_call == session.last_call assert 1 == len(runs) assert sample_run['uids'] == runs[0].uids
def test_type_mismatch(): """Test that incompatible types cannot be matched against RealBounds.""" bounds = RealBounds(0, 1, default_units="meters") assert not bounds.contains(IntegerBounds(0, 1)) assert not bounds.contains(None) with pytest.raises(TypeError): bounds.contains([.33, .66])
def test_object_template_serde(): """Test serde of an object template.""" length_template = PropertyTemplate("Length", bounds=RealBounds(2.0, 3.5, 'cm')) sub_bounds = RealBounds(2.5, 3.0, 'cm') color_template = PropertyTemplate("Color", bounds=CategoricalBounds(["red", "green", "blue"])) # Properties are a mixture of property templates and [template, bounds], pairs block_template = MaterialTemplate("Block", properties=[[length_template, sub_bounds], color_template]) copy_template = MaterialTemplate.build(block_template.dump()) assert copy_template == block_template # Tests below exercise similar code, but for measurement and process templates pressure_template = ConditionTemplate("pressure", bounds=RealBounds(0.1, 0.11, 'MPa')) index_template = ParameterTemplate("index", bounds=IntegerBounds(2, 10)) meas_template = MeasurementTemplate("A measurement of length", properties=[length_template], conditions=[pressure_template], description="Description", parameters=[index_template], tags=["foo"]) assert MeasurementTemplate.build(meas_template.dump()) == meas_template proc_template = ProcessTemplate("Make an object", parameters=[index_template], conditions=[pressure_template], allowed_labels=["Label"], allowed_names=["first sample", "second sample"]) assert ProcessTemplate.build(proc_template.dump()) == proc_template # Check that serde still works if the template is a LinkByUID pressure_template.uids['id'] = '12345' # uids['id'] not populated by default proc_template.conditions[0][0] = LinkByUID('id', pressure_template.uids['id']) assert ProcessTemplate.build(proc_template.dump()) == proc_template
def test_object_template_validation(): """Test that attribute templates are validated against given bounds.""" length_template = PropertyTemplate("Length", bounds=RealBounds(2.0, 3.5, 'cm')) dial_template = ConditionTemplate("dial", bounds=IntegerBounds(0, 5)) color_template = ParameterTemplate("Color", bounds=CategoricalBounds(["red", "green", "blue"])) with pytest.raises(TypeError): MaterialTemplate() with pytest.raises(ValueError): MaterialTemplate("Block", properties=[[length_template, RealBounds(3.0, 4.0, 'cm')]]) with pytest.raises(ValueError): ProcessTemplate("a process", conditions=[[color_template, CategoricalBounds(["zz"])]]) with pytest.raises(ValueError): MeasurementTemplate("A measurement", parameters=[[dial_template, IntegerBounds(-3, -1)]])
def test_register_data_concepts(dataset): """Check that register routes to the correct collections""" expected = { MaterialTemplateCollection: MaterialTemplate("foo"), MaterialSpecCollection: MaterialSpec("foo"), MaterialRunCollection: MaterialRun("foo"), ProcessTemplateCollection: ProcessTemplate("foo"), ProcessSpecCollection: ProcessSpec("foo"), ProcessRunCollection: ProcessRun("foo"), MeasurementTemplateCollection: MeasurementTemplate("foo"), MeasurementSpecCollection: MeasurementSpec("foo"), MeasurementRunCollection: MeasurementRun("foo"), IngredientSpecCollection: IngredientSpec("foo"), IngredientRunCollection: IngredientRun(), PropertyTemplateCollection: PropertyTemplate("bar", bounds=IntegerBounds(0, 1)), ParameterTemplateCollection: ParameterTemplate("bar", bounds=IntegerBounds(0, 1)), ConditionTemplateCollection: ConditionTemplate("bar", bounds=IntegerBounds(0, 1)) } for collection, obj in expected.items(): assert len(obj.uids) == 0 registered = dataset.register(obj) assert len(obj.uids) == 1 assert len(registered.uids) == 1 assert basename(dataset.session.calls[-1].path) == basename( collection._path_template) for pair in obj.uids.items(): assert pair[1] == registered.uids[pair[0]]
def test_cursor_paginated_searches(collection, session): """ Tests that search methods using cursor-pagination are hooked up correctly. There is no real search logic tested here. """ all_runs = [ MaterialRunDataFactory(name="foo_{}".format(i)) for i in range(20) ] fake_request = make_fake_cursor_request_function(all_runs) # pretty shady, need to add these methods to the fake session to test their # interactions with the actual search methods setattr(session, 'get_resource', fake_request) setattr(session, 'post_resource', fake_request) setattr(session, 'cursor_paged_resource', Session.cursor_paged_resource) assert len(list(collection.list_by_name('unused', per_page=2))) == len(all_runs) assert len(list(collection.list_all(per_page=2))) == len(all_runs) assert len(list(collection.list_by_tag('unused', per_page=2))) == len(all_runs) assert len( list( collection.list_by_attribute_bounds( {LinkByUIDFactory(): IntegerBounds(1, 5)}, per_page=2))) == len(all_runs) # invalid inputs with pytest.raises(TypeError): collection.list_by_attribute_bounds([1, 5], per_page=2) with pytest.raises(NotImplementedError): collection.list_by_attribute_bounds( { LinkByUIDFactory(): IntegerBounds(1, 5), LinkByUIDFactory(): IntegerBounds(1, 5), }, per_page=2) with pytest.raises(RuntimeError): collection.dataset_id = None collection.list_by_name('unused', per_page=2)
def test_bounds_optional(): """Test that each object template can have passthrough bounds for any of its attributes.""" def link(): return LinkByUID(id=str(uuid4()), scope=str(uuid4())) for template_type, attribute_args in [ (MaterialTemplate, [ ('properties', PropertyTemplate), ]), (ProcessTemplate, [ ('conditions', ConditionTemplate), ('parameters', ParameterTemplate), ]), (MeasurementTemplate, [ ('properties', PropertyTemplate), ('conditions', ConditionTemplate), ('parameters', ParameterTemplate), ]), ]: kwargs = {} for name, attribute_type in attribute_args: kwargs[name] = [ [link(), IntegerBounds(0, 10)], link(), attribute_type('foo', bounds=IntegerBounds(0, 10)), (link(), None) ] template = template_type(name='foo', **kwargs) for name, _ in attribute_args: attributes = getattr(template, name) assert len(attributes) == 4 for _, bounds in attributes[1:]: assert bounds is None dumped = template.dump() for _, bounds in dumped[name][1:]: assert bounds is None assert template_type.build(dumped) == template
def test_fields_from_property(): """Test that several fields of the attribute are derived from the property.""" prop_template = PropertyTemplate(name="cookie eating template", bounds=IntegerBounds(0, 1000)) cond_template = ConditionTemplate(name="Hunger template", bounds=CategoricalBounds(["hungry", "full", "peckish"])) prop = Property(name="number of cookies eaten", template=prop_template, origin='measured', value=NominalInteger(27)) cond = Condition(name="hunger level", template=cond_template, origin='specified', value=NominalCategorical("hungry")) prop_and_conds = PropertyAndConditions(property=prop, conditions=[cond]) assert prop_and_conds.name == prop.name assert prop_and_conds.template == prop.template assert prop_and_conds.origin == prop.origin assert prop_and_conds.value == prop.value
def test_contains(): """Test contains logic.""" int_bounds = IntegerBounds(0, 2) assert int_bounds.contains(IntegerBounds(0, 1)) assert int_bounds.contains(IntegerBounds(1, 2)) assert not int_bounds.contains(IntegerBounds(1, 3)) assert not int_bounds.contains(None) with pytest.raises(TypeError): int_bounds.contains([0, 1]) from gemd.entity.value import NominalInteger assert int_bounds.contains(NominalInteger(1)) assert not int_bounds.contains(NominalInteger(5))
def test_incompatible_types(): """Make sure that incompatible types aren't contained or validated.""" int_bounds = IntegerBounds(0, 1) assert not int_bounds.contains(RealBounds(0.0, 1.0, ''))
def test_register_all_data_concepts(dataset): """Check that register_all registers everything and routes to all collections""" bounds = IntegerBounds(0, 1) property_template = PropertyTemplate("bar", bounds=bounds) parameter_template = ParameterTemplate("bar", bounds=bounds) condition_template = ConditionTemplate("bar", bounds=bounds) foo_process_template = ProcessTemplate( "foo", conditions=[[condition_template, bounds]], parameters=[[parameter_template, bounds]]) foo_process_spec = ProcessSpec("foo", template=foo_process_template) foo_process_run = ProcessRun("foo", spec=foo_process_spec) foo_material_template = MaterialTemplate( "foo", properties=[[property_template, bounds]]) foo_material_spec = MaterialSpec("foo", template=foo_material_template, process=foo_process_spec) foo_material_run = MaterialRun("foo", spec=foo_material_spec, process=foo_process_run) baz_template = MaterialTemplate("baz") foo_measurement_template = MeasurementTemplate( "foo", conditions=[[condition_template, bounds]], parameters=[[parameter_template, bounds]], properties=[[property_template, bounds]]) foo_measurement_spec = MeasurementSpec("foo", template=foo_measurement_template) foo_measurement_run = MeasurementRun("foo", spec=foo_measurement_spec, material=foo_material_run) foo_ingredient_spec = IngredientSpec("foo", material=foo_material_spec, process=foo_process_spec) foo_ingredient_run = IngredientRun(spec=foo_ingredient_spec, material=foo_material_run, process=foo_process_run) baz_run = MeasurementRun("baz") # worst order possible expected = { foo_ingredient_run: IngredientRunCollection, foo_ingredient_spec: IngredientSpecCollection, foo_measurement_run: MeasurementRunCollection, foo_measurement_spec: MeasurementSpecCollection, foo_measurement_template: MeasurementTemplateCollection, foo_material_run: MaterialRunCollection, foo_material_spec: MaterialSpecCollection, foo_material_template: MaterialTemplateCollection, foo_process_run: ProcessRunCollection, foo_process_spec: ProcessSpecCollection, foo_process_template: ProcessTemplateCollection, baz_template: MaterialTemplateCollection, baz_run: MeasurementRunCollection, property_template: PropertyTemplateCollection, parameter_template: ParameterTemplateCollection, condition_template: ConditionTemplateCollection } for obj in expected: assert len(obj.uids) == 0 # All should be without ids registered = dataset.register_all(expected.keys()) assert len(registered) == len(expected) seen_ids = set() for obj in expected: assert len(obj.uids) == 1 # All should now have exactly 1 id for pair in obj.uids.items(): assert pair not in seen_ids # All ids are different seen_ids.add(pair) for obj in registered: for pair in obj.uids.items(): assert pair in seen_ids # registered items have the same ids call_basenames = [ call.path.split('/')[-2] for call in dataset.session.calls ] collection_basenames = [ basename(collection._path_template) for collection in expected.values() ] assert set(call_basenames) == set(collection_basenames) assert len(set(call_basenames)) == len( call_basenames) # calls are batched internally # spot check order. Does not check every constraint assert call_basenames.index( basename( IngredientRunCollection._path_template)) > call_basenames.index( basename(IngredientSpecCollection._path_template)) assert call_basenames.index(basename( MaterialRunCollection._path_template)) > call_basenames.index( basename(MaterialSpecCollection._path_template)) assert call_basenames.index( basename( MeasurementRunCollection._path_template)) > call_basenames.index( basename(MeasurementSpecCollection._path_template)) assert call_basenames.index(basename( ProcessRunCollection._path_template)) > call_basenames.index( basename(ProcessSpecCollection._path_template)) assert call_basenames.index(basename( MaterialSpecCollection._path_template)) > call_basenames.index( basename(MaterialTemplateCollection._path_template)) assert call_basenames.index( basename( MeasurementSpecCollection._path_template)) > call_basenames.index( basename(MeasurementTemplateCollection._path_template)) assert call_basenames.index(basename( ProcessSpecCollection._path_template)) > call_basenames.index( basename(ProcessTemplateCollection._path_template)) assert call_basenames.index(basename( MaterialSpecCollection._path_template)) > call_basenames.index( basename(ProcessSpecCollection._path_template)) assert call_basenames.index(basename( MaterialSpecCollection._path_template)) > call_basenames.index( basename(MeasurementSpecCollection._path_template)) assert call_basenames.index( basename(MeasurementTemplateCollection._path_template) ) > call_basenames.index( basename(ConditionTemplateCollection._path_template)) assert call_basenames.index( basename(MeasurementTemplateCollection._path_template) ) > call_basenames.index( basename(ParameterTemplateCollection._path_template)) assert call_basenames.index( basename( MaterialTemplateCollection._path_template)) > call_basenames.index( basename(PropertyTemplateCollection._path_template))
def test_parameter_template(): """Test creation and serde of parameter templates.""" bounds = IntegerBounds(-3, 8) template = ParameterTemplate("Position knob", bounds=bounds, tags=["Tag1", "A::B::C"]) assert template.uids is not None # uids should be added automatically assert ParameterTemplate.build(template.dump()) == template