def test_initialization(self): slotmap = InputSlotInfo() self.assertEqual(slotmap.source, "Environment") self.assertEqual(slotmap.name, "") with self.assertRaises(TraitError): slotmap.name = "000"
def setUp(self): self.registry = ProbeFactoryRegistry() self.plugin = self.registry.plugin self.data_values = [ DataValue(name="foo"), DataValue(name="bar"), DataValue(name="baz"), ] self.slot_map = (InputSlotInfo(name="baz"), InputSlotInfo(name="bar")) self.slots = (Slot(), Slot())
def test_data_source_run_error(self): data_values = [DataValue(name="foo")] self.layer.data_sources[0].input_slot_info = [ InputSlotInfo(name="foo") ] factory = self.registry.data_source_factories[0] factory.raises_on_data_source_run = True with testfixtures.LogCapture() as capture: with self.assertRaises(Exception): self.layer.execute_layer(data_values) capture.check( ( "force_bdss.core.execution_layer", "INFO", "Evaluating for Data Source test_data_source", ), ("force_bdss.core.execution_layer", "INFO", "Passed values:"), ( "force_bdss.core.execution_layer", "INFO", "0: foo = None (AVERAGE)", ), ( "force_bdss.core.execution_layer", "ERROR", "Evaluation could not be performed. " "Run method raised exception.", ), )
def test_error_for_non_data_source(self): data_values = [DataValue(name="foo")] self.layer.data_sources[0].input_slot_info = [ InputSlotInfo(name="foo") ] self.layer.data_sources[0].output_slot_info = [ OutputSlotInfo(name="one") ] def probe_run(self, *args, **kwargs): return ["hello"] factory = self.registry.data_source_factories[0] factory.run_function = probe_run with testfixtures.LogCapture(): with self.assertRaisesRegex( RuntimeError, "The result list returned by DataSource " "test_data_source" " contains an entry that is not a DataValue." " An entry of type <.* 'str'> was instead found" " in position 0." r" Fix the DataSource.run\(\) method to" " return the appropriate entity.", ): self.layer.execute_layer(data_values)
def from_json(cls, factory, json_data): """ Instantiate an BaseMCOModel object from a `json_data` dictionary and the generating `factory` object. Parameters ---------- factory: IDataSourceFactory Generating factory object json_data: dict Dictionary with a DataSourceModel serialized data Returns ---------- layer: BaseDataSourceModel BaseDataSourceModel instance with attributes values from the `json_data` dict """ data = deepcopy(json_data) input_slots = [InputSlotInfo(**d) for d in data["input_slot_info"]] data["input_slot_info"] = input_slots output_slots = [OutputSlotInfo(**d) for d in data["output_slot_info"]] data["output_slot_info"] = output_slots data_source = factory.create_model(data) return data_source
def test_getstate(self): model = DummyDataSourceModel(self.mock_factory) self.assertDictEqual( model.__getstate__(), { "id": "id", "model_data": { "input_slot_info": [], "output_slot_info": [] }, }, ) model.input_slot_info = [ InputSlotInfo(name="foo"), InputSlotInfo(name="bar"), ] model.output_slot_info = [ OutputSlotInfo(name="baz"), OutputSlotInfo(name="quux"), ] self.assertDictEqual( model.__getstate__(), { "id": "id", "model_data": { "input_slot_info": [ { "source": "Environment", "name": "foo" }, { "source": "Environment", "name": "bar" }, ], "output_slot_info": [{ "name": "baz" }, { "name": "quux" }], }, }, )
def test_traits_to_dict(self): wf = self.sample_workflow() exec_layer = wf.execution_layers[0] exec_layer.data_sources[0].input_slot_info = [InputSlotInfo()] datastore_list = exec_layer.__getstate__() new_slotdata = datastore_list["data_sources"][0]["model_data"][ "input_slot_info"] self.assertNotIn("__traits_version__", new_slotdata)
def test_execute_layer_results(self): data_values = [ DataValue(name="foo"), DataValue(name="bar"), DataValue(name="baz"), DataValue(name="quux"), ] def run(self, *args, **kwargs): return [DataValue(value=1), DataValue(value=2), DataValue(value=3)] ds_factory = self.registry.data_source_factories[0] ds_factory.input_slots_size = 2 ds_factory.output_slots_size = 3 ds_factory.run_function = run evaluator_model = ds_factory.create_model() evaluator_model.input_slot_info = [ InputSlotInfo(name="foo"), InputSlotInfo(name="quux"), ] evaluator_model.output_slot_info = [ OutputSlotInfo(name="one"), OutputSlotInfo(name=""), OutputSlotInfo(name="three"), ] self.layer.data_sources = [evaluator_model] res = self.layer.execute_layer(data_values) self.assertEqual(len(res), 2) self.assertEqual(res[0].name, "one") self.assertEqual(res[0].value, 1) self.assertEqual(res[1].name, "three") self.assertEqual(res[1].value, 3) with mock.patch( "force_bdss.data_sources.base_data_source.BaseDataSource._run", return_value=run(None)) as mock_run: # mock_run.side_effect = [1, 2] self.layer.execute_layer(data_values) mock_run.assert_called_once()
def test_data_sources(self): wf = self.workflow mco_factory = self.plugin.mco_factories[0] wf.mco_model = mco_factory.create_model() parameter_factory = mco_factory.parameter_factories[0] wf.mco_model.parameters.append(parameter_factory.create_model()) wf.mco_model.parameters[0].name = "name" wf.mco_model.parameters[0].type = "type" wf.mco_model.kpis.append(KPISpecification(name='name')) layer = ExecutionLayer() wf.execution_layers.append(layer) ds_factory = self.plugin.data_source_factories[0] ds_model = ds_factory.create_model() layer.data_sources.append(ds_model) errors = verify_workflow(wf) self.assertEqual(errors[0].subject, ds_model) self.assertIn( "The number of input slots (1 values) returned by " "'Dummy data source' does not match the number " "of user-defined names specified (0 values). This " "is either a plugin error or a file error.", errors[0].local_error) ds_model.input_slot_info.append(InputSlotInfo(name="name")) errors = verify_workflow(wf) self.assertEqual(errors[0].subject, ds_model) self.assertIn( "The number of output slots (1 values) returned by " "'Dummy data source' does not match the number " "of user-defined names specified (0 values). This " "is either a plugin error or a file error.", errors[0].local_error) ds_model.output_slot_info.append(OutputSlotInfo(name="name")) errors = verify_workflow(wf) self.assertEqual(len(errors), 0) ds_model.input_slot_info[0].name = '' errors = verify_workflow(wf) self.assertEqual(len(errors), 1) self.assertIn("Input slot is not named", errors[0].local_error) ds_model.output_slot_info[0].name = '' errors = verify_workflow(wf) self.assertEqual(len(errors), 3) self.assertIn("All output variables have undefined names", errors[1].local_error) self.assertIn("An output variable has an undefined name", errors[2].local_error)
def test_error_for_output_slots(self): data_values = [DataValue(name="foo")] self.layer.data_sources[0].input_slot_info = [ InputSlotInfo(name="foo") ] def probe_run(self, *args, **kwargs): return ["too", "many", "data", "values"] factory = self.registry.data_source_factories[0] factory.run_function = probe_run with testfixtures.LogCapture(): with self.assertRaisesRegex( RuntimeError, r"The number of data values \(4 values\) returned" " by 'test_data_source' does not match the number" r" of output slots it specifies \(1 values\)." " This is likely a plugin error.", ): self.layer.execute_layer(data_values)
def test_error_for_incorrect_return_type(self): data_values = [DataValue(name="foo")] self.layer.data_sources[0].input_slot_info = [ InputSlotInfo(name="foo") ] def probe_run(self, *args, **kwargs): return "hello" factory = self.registry.data_source_factories[0] factory.run_function = probe_run with testfixtures.LogCapture(): with self.assertRaisesRegex( RuntimeError, "The run method of data source " "test_data_source must return a list. It " r"returned instead <.* 'str'>. Fix the run\(\)" " method to return the appropriate entity.", ): self.layer.execute_layer(data_values)
def test_multilayer_execution(self): # The multilayer peforms the following execution # layer 0: in1 + in2 | in3 + in4 # res1 res2 # layer 1: res1 + res2 # res3 # layer 2: res3 * res1 # res4 # layer 3: res4 * res2 # out1 # Final result should be # out1 = ((in1 + in2 + in3 + in4) * (in1 + in2) * (in3 + in4) data_values = [ DataValue(value=10, name="in1"), DataValue(value=15, name="in2"), DataValue(value=3, name="in3"), DataValue(value=7, name="in4"), ] def adder(model, parameters): first = parameters[0].value second = parameters[1].value return [DataValue(value=(first + second))] adder_factory = ProbeDataSourceFactory( self.plugin, input_slots_size=2, output_slots_size=1, run_function=adder, ) def multiplier(model, parameters): first = parameters[0].value second = parameters[1].value return [DataValue(value=(first * second))] multiplier_factory = ProbeDataSourceFactory( self.plugin, input_slots_size=2, output_slots_size=1, run_function=multiplier, ) mco_factory = ProbeMCOFactory(self.plugin) mco_model = mco_factory.create_model() parameter_factory = mco_factory.parameter_factories[0] mco_model.parameters = [ parameter_factory.create_model({"name": "in1"}), parameter_factory.create_model({"name": "in2"}), parameter_factory.create_model({"name": "in3"}), parameter_factory.create_model({"name": "in4"}), ] mco_model.kpis = [KPISpecification(name="out1")] wf = Workflow( mco_model=mco_model, execution_layers=[ ExecutionLayer(), ExecutionLayer(), ExecutionLayer(), ExecutionLayer(), ], ) # Layer 0 model = adder_factory.create_model() model.input_slot_info = [ InputSlotInfo(name="in1"), InputSlotInfo(name="in2"), ] model.output_slot_info = [OutputSlotInfo(name="res1")] wf.execution_layers[0].data_sources.append(model) model = adder_factory.create_model() model.input_slot_info = [ InputSlotInfo(name="in3"), InputSlotInfo(name="in4"), ] model.output_slot_info = [OutputSlotInfo(name="res2")] wf.execution_layers[0].data_sources.append(model) # layer 1 model = adder_factory.create_model() model.input_slot_info = [ InputSlotInfo(name="res1"), InputSlotInfo(name="res2"), ] model.output_slot_info = [OutputSlotInfo(name="res3")] wf.execution_layers[1].data_sources.append(model) # layer 2 model = multiplier_factory.create_model() model.input_slot_info = [ InputSlotInfo(name="res3"), InputSlotInfo(name="res1"), ] model.output_slot_info = [OutputSlotInfo(name="res4")] wf.execution_layers[2].data_sources.append(model) # layer 3 model = multiplier_factory.create_model() model.input_slot_info = [ InputSlotInfo(name="res4"), InputSlotInfo(name="res2"), ] model.output_slot_info = [OutputSlotInfo(name="out1")] wf.execution_layers[3].data_sources.append(model) kpi_results = wf.execute(data_values) self.assertEqual(1, len(kpi_results)) self.assertEqual(8750, kpi_results[0].value)
def test_kpi_specification_adherence(self): # Often the user may only wish to treat a subset of DataSource # output slots as KPIs. This test makes sure they get what they # ask for! # keep input DataValues constant data_values = [ DataValue(value=99, name="in1"), DataValue(value=1, name="in2"), ] # dummy addition DataSource(a, b) that also returns its inputs # [a, b, a+b] def adder(model, parameters): first = parameters[0].value second = parameters[1].value return [ DataValue(value=first), DataValue(value=second), DataValue(value=(first + second)), ] adder_factory = ProbeDataSourceFactory( self.plugin, input_slots_size=2, output_slots_size=3, run_function=adder, ) mco_factory = ProbeMCOFactory(self.plugin) parameter_factory = mco_factory.parameter_factories[0] mco_model = mco_factory.create_model() # DataSourceModel stats constant throughout model = adder_factory.create_model() model.input_slot_info = [ InputSlotInfo(name="in1"), InputSlotInfo(name="in2"), ] model.output_slot_info = [ OutputSlotInfo(name="out1"), OutputSlotInfo(name="out2"), OutputSlotInfo(name="out3"), ] # test Parameter and KPI spec that follows DataSource slots # exactly mco_model.parameters = [ parameter_factory.create_model({"name": "in1"}), parameter_factory.create_model({"name": "in2"}), ] mco_model.kpis = [ KPISpecification(name="out1"), KPISpecification(name="out2"), KPISpecification(name="out3"), ] # need to make a new workflow for each KPISpecification wf = Workflow(mco_model=mco_model, execution_layers=[ExecutionLayer()]) wf.execution_layers[0].data_sources.append(model) kpi_results = wf.execute(data_values) self.assertEqual(len(kpi_results), 3) self.assertEqual(kpi_results[0].value, 99) self.assertEqual(kpi_results[1].value, 1) self.assertEqual(kpi_results[2].value, 100) self.assertEqual(kpi_results[0].name, "out1") self.assertEqual(kpi_results[1].name, "out2") self.assertEqual(kpi_results[2].name, "out3") # now test all possible combinations of KPISpecification, including # those with KPIs repeated, and empty KPI specification import itertools out_options = [("out1", 99), ("out2", 1), ("out3", 100)] for num_outputs in range(len(out_options) + 2, 0, -1): for spec in itertools.permutations(out_options, r=num_outputs): mco_model.kpis = [ KPISpecification(name=opt[0]) for opt in spec ] wf = Workflow( mco_model=mco_model, execution_layers=[ExecutionLayer()] ) wf.execution_layers[0].data_sources.append(model) kpi_results = wf.execute(data_values) self.assertEqual(len(kpi_results), num_outputs) for i in range(num_outputs): self.assertEqual(kpi_results[i].name, spec[i][0]) self.assertEqual(kpi_results[i].value, spec[i][1])