class TestSpineDBEditorWithDBMapping(unittest.TestCase): @classmethod def setUpClass(cls): """Overridden method. Runs once before all tests in this class.""" try: cls.app = QApplication().processEvents() except RuntimeError: pass logging.basicConfig( stream=sys.stderr, level=logging.DEBUG, format='%(asctime)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S', ) def setUp(self): """Overridden method. Runs before each test. Makes instances of SpineDBEditor classes.""" self._temp_dir = TemporaryDirectory() url = "sqlite:///" + os.path.join(self._temp_dir.name, "test.sqlite") with mock.patch( "spinetoolbox.spine_db_editor.widgets.spine_db_editor.SpineDBEditor.restore_ui" ), mock.patch( "spinetoolbox.spine_db_editor.widgets.spine_db_editor.SpineDBEditor.show" ): mock_settings = mock.Mock() mock_settings.value.side_effect = lambda *args, **kwards: 0 self.db_mngr = SpineDBManager(mock_settings, None) logger = mock.MagicMock() self.db_map = self.db_mngr.get_db_map(url, logger, codename="db", create=True) self.spine_db_editor = SpineDBEditor(self.db_mngr, {url: "db"}) self.spine_db_editor.pivot_table_model = mock.MagicMock() def tearDown(self): """Overridden method. Runs after each test. Use this to free resources after a test if needed. """ with mock.patch( "spinetoolbox.spine_db_editor.widgets.spine_db_editor.SpineDBEditor.save_window_state" ) as mock_save_w_s, mock.patch( "spinetoolbox.spine_db_manager.QMessageBox"): self.spine_db_editor.close() mock_save_w_s.assert_called_once() QApplication.removePostedEvents( None) # Clean up unfinished fetcher signals self.db_mngr.close_all_sessions() self.db_mngr.clean_up() self.db_mngr = None # Ensure the database file is closed to allow the temporary directory to be removed. self.spine_db_editor.deleteLater() self.spine_db_editor = None self._temp_dir.cleanup() def fetch_object_tree_model(self): for item in self.spine_db_editor.object_tree_model.visit_all(): if item.can_fetch_more(): item.fetch_more() def test_duplicate_object_in_object_tree_model(self): data = dict() data["object_classes"] = ["fish", "dog"] data["relationship_classes"] = [("fish__dog", ("fish", "dog"))] data["objects"] = [("fish", "nemo"), ("dog", "pluto")] data["relationships"] = [("fish__dog", ("nemo", "pluto"))] data["object_parameters"] = [("fish", "color")] data["object_parameter_values"] = [("fish", "nemo", "color", "orange")] with mock.patch( "spinetoolbox.spine_db_manager.SpineDBManager.entity_class_icon" ) as mock_icon: mock_icon.return_value = None loop = QEventLoop() self.db_mngr.data_imported.connect(loop.quit) self.db_mngr.import_data({self.db_map: data}) loop.exec_() loop.deleteLater() mock_icon.assert_called() self.fetch_object_tree_model() root_item = self.spine_db_editor.object_tree_model.root_item fish_item = next( iter(item for item in root_item.children if item.display_data == "fish")) nemo_item = fish_item.child(0) with mock.patch( "spinetoolbox.spine_db_editor.widgets.tree_view_mixin.QInputDialog" ) as mock_input_dialog: mock_input_dialog.getText.side_effect = lambda *args, **kwargs: ( "nemo_copy", True) loop = QEventLoop() self.db_mngr.data_imported.connect(loop.quit) self.spine_db_editor.duplicate_object(nemo_item.index()) loop.exec_() loop.deleteLater() nemo_dupe = fish_item.child(1) self.assertEqual(nemo_dupe.display_data, "nemo_copy")
class TestAddItemsDialog(unittest.TestCase): @classmethod def setUpClass(cls): """Overridden method. Runs once before all tests in this class.""" try: cls.app = QApplication().processEvents() except RuntimeError: pass logging.basicConfig( stream=sys.stderr, level=logging.DEBUG, format='%(asctime)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S', ) def setUp(self): """Overridden method. Runs before each test. Makes instance of SpineDBEditor class.""" with mock.patch( "spinetoolbox.spine_db_manager.QMessageBox" ), mock.patch( "spinetoolbox.spine_db_editor.widgets.spine_db_editor.SpineDBEditor.restore_ui" ): self.mock_db_mngr = mock.MagicMock() self.mock_db_mngr.undo_action.__getitem__.side_effect = lambda key: QAction( ) self.mock_db_mngr.redo_action.__getitem__.side_effect = lambda key: QAction( ) self.mock_db_map = mock.MagicMock() self.mock_db_map.codename = "mock_db" self.ds_view_form = SpineDBEditor(self.mock_db_mngr, self.mock_db_map) def tearDown(self): """Overridden method. Runs after each test. Use this to free resources after a test if needed. """ with mock.patch( "spinetoolbox.spine_db_editor.widgets.spine_db_editor.SpineDBEditor.save_window_state" ) as mock_save_w_s: self.ds_view_form.close() mock_save_w_s.assert_called_once() self.ds_view_form.deleteLater() self.ds_view_form = None try: os.remove('mock_db.sqlite') except OSError: pass def test_add_object_classes(self): """Test object classes are added through the manager when accepting the dialog.""" dialog = AddObjectClassesDialog(self.ds_view_form, self.mock_db_mngr, self.mock_db_map) model = dialog.model header = model.header model.fetchMore() self.assertEqual( header, ['object_class name', 'description', 'display icon', 'databases']) indexes = [ model.index(0, header.index(field)) for field in ('object_class name', 'databases') ] values = ['fish', 'mock_db'] model.batch_set_data(indexes, values) def _add_object_classes(db_map_data): self.assertTrue(self.mock_db_map in db_map_data) data = db_map_data[self.mock_db_map] self.assertEqual(len(data), 1) item = data[0] self.assertTrue("name" in item) self.assertEqual(item["name"], "fish") self.mock_db_mngr.add_object_classes.side_effect = _add_object_classes dialog.accept() self.mock_db_mngr.add_object_classes.assert_called_once() def test_do_not_add_object_classes_with_invalid_db(self): """Test object classes aren't added when the database is not correct.""" dialog = AddObjectClassesDialog(self.ds_view_form, self.mock_db_mngr, self.mock_db_map) self.ds_view_form.msg_error = mock.NonCallableMagicMock() self.ds_view_form.msg_error.attach_mock(mock.MagicMock(), "emit") model = dialog.model header = model.header model.fetchMore() self.assertEqual( header, ['object_class name', 'description', 'display icon', 'databases']) indexes = [ model.index(0, header.index(field)) for field in ('object_class name', 'databases') ] values = ['fish', 'gibberish'] model.batch_set_data(indexes, values) dialog.accept() self.mock_db_mngr.add_object_classes.assert_not_called() self.ds_view_form.msg_error.emit.assert_called_with( "Invalid database 'gibberish' at row 1")
class TestSpineDBEditor( TestSpineDBEditorAddMixin, TestSpineDBEditorUpdateMixin, TestSpineDBEditorRemoveMixin, TestSpineDBEditorFilterMixin, unittest.TestCase, ): @staticmethod def _object_class(*args): return dict(zip(["id", "name", "description", "display_order", "display_icon"], args)) @staticmethod def _object(*args): return dict(zip(["id", "class_id", "name", "description"], args)) @staticmethod def _relationship_class(*args): return dict(zip(["id", "name", "object_class_id_list", "object_class_name_list"], args)) @staticmethod def _relationship(*args): return dict( zip( ["id", "class_id", "name", "class_name", "object_class_id_list", "object_id_list", "object_name_list"], args, ) ) @staticmethod def _object_parameter_definition(*args): d = dict(zip(["id", "object_class_id", "object_class_name", "parameter_name"], args)) return d @staticmethod def _relationship_parameter_definition(*args): d = dict( zip( [ "id", "relationship_class_id", "relationship_class_name", "object_class_id_list", "object_class_name_list", "parameter_name", ], args, ) ) return d @staticmethod def _object_parameter_value(*args): d = dict( zip( [ "id", "object_class_id", "object_class_name", "object_id", "object_name", "parameter_id", "parameter_name", "value", ], args, ) ) d["entity_id"] = d["object_id"] return d @staticmethod def _relationship_parameter_value(*args): d = dict( zip( [ "id", "relationship_class_id", "relationship_class_name", "object_class_id_list", "object_class_name_list", "relationship_id", "object_id_list", "object_name_list", "parameter_id", "parameter_name", "value", ], args, ) ) d["entity_id"] = d["relationship_id"] return d @classmethod def setUpClass(cls): """Overridden method. Runs once before all tests in this class.""" try: cls.app = QApplication().processEvents() except RuntimeError: pass logging.basicConfig( stream=sys.stderr, level=logging.DEBUG, format='%(asctime)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S', ) cls.create_mock_dataset() @classmethod def create_mock_dataset(cls): cls.fish_class = cls._object_class(1, "fish", "A fish.", 1, None) cls.dog_class = cls._object_class(2, "dog", "A dog.", 3, None) cls.fish_dog_class = cls._relationship_class( 3, "fish__dog", str(cls.fish_class["id"]) + "," + str(cls.dog_class["id"]), cls.fish_class["name"] + "," + cls.dog_class["name"], ) cls.dog_fish_class = cls._relationship_class( 4, "dog__fish", str(cls.dog_class["id"]) + "," + str(cls.fish_class["id"]), cls.dog_class["name"] + "," + cls.fish_class["name"], ) cls.nemo_object = cls._object(1, cls.fish_class["id"], 'nemo', 'The lost one.') cls.pluto_object = cls._object(2, cls.dog_class["id"], 'pluto', "Mickey's.") cls.scooby_object = cls._object(3, cls.dog_class["id"], 'scooby', 'Scooby-Dooby-Doo.') cls.pluto_nemo_rel = cls._relationship( 4, cls.dog_fish_class["id"], "dog__fish_pluto__nemo", cls.dog_fish_class["name"], str(cls.dog_class["id"]) + "," + str(cls.fish_class["id"]), str(cls.pluto_object["id"]) + "," + str(cls.nemo_object["id"]), cls.pluto_object["name"] + "," + cls.nemo_object["name"], ) cls.nemo_pluto_rel = cls._relationship( 5, cls.fish_dog_class["id"], "fish__dog_nemo__pluto", cls.fish_dog_class["name"], str(cls.fish_class["id"]) + "," + str(cls.dog_class["id"]), str(cls.nemo_object["id"]) + "," + str(cls.pluto_object["id"]), cls.nemo_object["name"] + "," + cls.pluto_object["name"], ) cls.nemo_scooby_rel = cls._relationship( 6, cls.fish_dog_class["id"], "fish__dog_nemo__scooby", cls.fish_dog_class["name"], str(cls.fish_class["id"]) + "," + str(cls.dog_class["id"]), str(cls.nemo_object["id"]) + "," + str(cls.scooby_object["id"]), cls.nemo_object["name"] + "," + cls.scooby_object["name"], ) cls.water_parameter = cls._object_parameter_definition(1, cls.fish_class["id"], cls.fish_class["name"], "water") cls.breed_parameter = cls._object_parameter_definition(2, cls.dog_class["id"], cls.dog_class["name"], "breed") cls.relative_speed_parameter = cls._relationship_parameter_definition( 3, cls.fish_dog_class["id"], cls.fish_dog_class["name"], cls.fish_dog_class["object_class_id_list"], cls.fish_dog_class["object_class_name_list"], "relative_speed", ) cls.combined_mojo_parameter = cls._relationship_parameter_definition( 4, cls.dog_fish_class["id"], cls.dog_fish_class["name"], cls.dog_fish_class["object_class_id_list"], cls.dog_fish_class["object_class_name_list"], "combined_mojo", ) cls.nemo_water = cls._object_parameter_value( 1, cls.water_parameter["object_class_id"], cls.water_parameter["object_class_name"], cls.nemo_object["id"], cls.nemo_object["name"], cls.water_parameter["id"], cls.water_parameter["parameter_name"], '"salt"', ) cls.pluto_breed = cls._object_parameter_value( 2, cls.breed_parameter["object_class_id"], cls.breed_parameter["object_class_name"], cls.pluto_object["id"], cls.pluto_object["name"], cls.breed_parameter["id"], cls.breed_parameter["parameter_name"], '"bloodhound"', ) cls.scooby_breed = cls._object_parameter_value( 3, cls.breed_parameter["object_class_id"], cls.breed_parameter["object_class_name"], cls.scooby_object["id"], cls.scooby_object["name"], cls.breed_parameter["id"], cls.breed_parameter["parameter_name"], '"great dane"', ) cls.nemo_pluto_relative_speed = cls._relationship_parameter_value( 4, cls.relative_speed_parameter["relationship_class_id"], cls.relative_speed_parameter["relationship_class_name"], cls.relative_speed_parameter["object_class_id_list"], cls.relative_speed_parameter["object_class_name_list"], cls.nemo_pluto_rel["id"], cls.nemo_pluto_rel["object_id_list"], cls.nemo_pluto_rel["object_name_list"], cls.relative_speed_parameter["id"], cls.relative_speed_parameter["parameter_name"], "-1", ) cls.nemo_scooby_relative_speed = cls._relationship_parameter_value( 5, cls.relative_speed_parameter["relationship_class_id"], cls.relative_speed_parameter["relationship_class_name"], cls.relative_speed_parameter["object_class_id_list"], cls.relative_speed_parameter["object_class_name_list"], cls.nemo_scooby_rel["id"], cls.nemo_scooby_rel["object_id_list"], cls.nemo_scooby_rel["object_name_list"], cls.relative_speed_parameter["id"], cls.relative_speed_parameter["parameter_name"], "5", ) cls.pluto_nemo_combined_mojo = cls._relationship_parameter_value( 6, cls.combined_mojo_parameter["relationship_class_id"], cls.combined_mojo_parameter["relationship_class_name"], cls.combined_mojo_parameter["object_class_id_list"], cls.combined_mojo_parameter["object_class_name_list"], cls.pluto_nemo_rel["id"], cls.pluto_nemo_rel["object_id_list"], cls.pluto_nemo_rel["object_name_list"], cls.combined_mojo_parameter["id"], cls.combined_mojo_parameter["parameter_name"], "100", ) def setUp(self): """Overridden method. Runs before each test. Makes instances of SpineDBEditor classes.""" with mock.patch("spinetoolbox.spine_db_manager.DiffDatabaseMapping") as mock_DiffDBMapping, mock.patch( "spinetoolbox.spine_db_editor.widgets.spine_db_editor.SpineDBEditor.restore_ui" ), mock.patch("spinetoolbox.spine_db_editor.widgets.spine_db_editor.SpineDBEditor.show"): mock_settings = mock.Mock() mock_settings.value.side_effect = lambda *args, **kwards: 0 self.db_mngr = SpineDBManager(mock_settings, None) self.db_mngr.fetch_db_maps_for_listener = lambda *args: None def DiffDBMapping_side_effect(url, codename=None, upgrade=False, create=False): mock_db_map = mock.MagicMock() mock_db_map.db_url = url mock_db_map.codename = codename return mock_db_map mock_DiffDBMapping.side_effect = DiffDBMapping_side_effect self.spine_db_editor = SpineDBEditor(self.db_mngr, {"mock_url": "mock_db"}) self.mock_db_map = self.spine_db_editor.first_db_map self.spine_db_editor.pivot_table_model = mock.MagicMock() def tearDown(self): """Overridden method. Runs after each test. Use this to free resources after a test if needed. """ with mock.patch( "spinetoolbox.spine_db_editor.widgets.spine_db_editor.SpineDBEditor.save_window_state" ) as mock_save_w_s, mock.patch("spinetoolbox.spine_db_manager.QMessageBox"): self.spine_db_editor.close() mock_save_w_s.assert_called_once() self.spine_db_editor.db_mngr.stop_fetchers() QApplication.removePostedEvents(None) # Clean up unfinished fetcher signals self.spine_db_editor.deleteLater() self.spine_db_editor = None def put_mock_object_classes_in_db_mngr(self): """Put fish and dog object classes in the db mngr.""" object_classes = [self.fish_class, self.dog_class] self.db_mngr.object_classes_added.emit({self.mock_db_map: object_classes}) def put_mock_objects_in_db_mngr(self): """Put nemo, pluto and scooby objects in the db mngr.""" objects = [self.nemo_object, self.pluto_object, self.scooby_object] self.db_mngr.objects_added.emit({self.mock_db_map: objects}) def put_mock_relationship_classes_in_db_mngr(self): """Put dog__fish and fish__dog relationship classes in the db mngr.""" relationship_classes = [self.fish_dog_class, self.dog_fish_class] self.db_mngr.relationship_classes_added.emit({self.mock_db_map: relationship_classes}) def put_mock_relationships_in_db_mngr(self): """Put pluto_nemo, nemo_pluto and nemo_scooby relationships in the db mngr.""" relationships = [self.pluto_nemo_rel, self.nemo_pluto_rel, self.nemo_scooby_rel] self.db_mngr.relationships_added.emit({self.mock_db_map: relationships}) def put_mock_object_parameter_definitions_in_db_mngr(self): """Put water and breed object parameter definitions in the db mngr.""" parameter_definitions = [self.water_parameter, self.breed_parameter] with mock.patch.object(CompoundParameterModel, "_modify_data_in_filter_menus"): self.db_mngr.parameter_definitions_added.emit({self.mock_db_map: parameter_definitions}) def put_mock_relationship_parameter_definitions_in_db_mngr(self): """Put relative speed and combined mojo relationship parameter definitions in the db mngr.""" parameter_definitions = [self.relative_speed_parameter, self.combined_mojo_parameter] with mock.patch.object(CompoundParameterModel, "_modify_data_in_filter_menus"): self.db_mngr.parameter_definitions_added.emit({self.mock_db_map: parameter_definitions}) def put_mock_object_parameter_values_in_db_mngr(self): """Put some object parameter values in the db mngr.""" parameter_values = [self.nemo_water, self.pluto_breed, self.scooby_breed] with mock.patch.object(CompoundParameterModel, "_modify_data_in_filter_menus"): self.db_mngr.parameter_values_added.emit({self.mock_db_map: parameter_values}) def put_mock_relationship_parameter_values_in_db_mngr(self): """Put some relationship parameter values in the db mngr.""" parameter_values = [ self.nemo_pluto_relative_speed, self.nemo_scooby_relative_speed, self.pluto_nemo_combined_mojo, ] with mock.patch.object(CompoundParameterModel, "_modify_data_in_filter_menus"): self.db_mngr.parameter_values_added.emit({self.mock_db_map: parameter_values}) def put_mock_dataset_in_db_mngr(self): """Put mock dataset in the db mngr.""" self.put_mock_object_classes_in_db_mngr() self.put_mock_objects_in_db_mngr() self.put_mock_relationship_classes_in_db_mngr() self.put_mock_relationships_in_db_mngr() self.put_mock_object_parameter_definitions_in_db_mngr() self.put_mock_relationship_parameter_definitions_in_db_mngr() self.put_mock_object_parameter_values_in_db_mngr() self.put_mock_relationship_parameter_values_in_db_mngr() self.fetch_object_tree_model() def fetch_object_tree_model(self): for item in self.spine_db_editor.object_tree_model.visit_all(): if item.can_fetch_more(): item.fetch_more() def test_set_object_parameter_definition_defaults(self): """Test that defaults are set in object parameter_definition models according the object tree selection.""" self.spine_db_editor.init_models() self.put_mock_object_classes_in_db_mngr() self.fetch_object_tree_model() # Select fish item in object tree root_item = self.spine_db_editor.object_tree_model.root_item fish_item = root_item.child(0) fish_index = self.spine_db_editor.object_tree_model.index_from_item(fish_item) self.spine_db_editor.ui.treeView_object.setCurrentIndex(fish_index) self.spine_db_editor.ui.treeView_object.selectionModel().select(fish_index, QItemSelectionModel.Select) # Check default in object parameter_definition model = self.spine_db_editor.object_parameter_definition_model model.empty_model.fetchMore() h = model.header.index row_data = [] for row in range(model.rowCount()): row_data.append(tuple(model.index(row, h(field)).data() for field in ("object_class_name", "database"))) self.assertTrue(("fish", "mock_db") in row_data) @unittest.skip("TODO") def test_set_object_parameter_value_defaults(self): """Test that defaults are set in relationship parameter_definition models according the object tree selection. """ self.fail() @unittest.skip("TODO") def test_set_relationship_parameter_definition_defaults(self): """Test that defaults are set in relationship parameter_definition models according the object tree selection. """ self.fail() @unittest.skip("TODO") def test_set_relationship_parameter_value_defaults(self): """Test that defaults are set in relationship parameter_definition models according the object tree selection. """ self.fail()