import unittest

import numpy as np

from AnyQt.QtCore import QModelIndex, QItemSelectionModel

from Orange.data import Table
from Orange.widgets.tests.base import WidgetTest
from Orange.widgets.tests.utils import simulate

from orangecontrib.bioinformatics.utils import serverfiles
from orangecontrib.bioinformatics.widgets.OWMarkerGenes import (
    SERVER_FILES_DOMAIN,
    TreeItem,
    TreeView,
    OWMarkerGenes,
)


class TestTreeItem(unittest.TestCase):
    def setUp(self) -> None:
        self.item1 = TreeItem("item 1", False, None, None)
        self.item2 = TreeItem("item 2", False, None, None)
        self.item3 = TreeItem("item 3", False, None, None)

    def test_change_parent(self):
        self.item1.change_parent(self.item2)
        self.assertEqual(self.item1.parentItem, self.item2)
        self.assertIn(self.item1, self.item2.childItems)

    def test_change_parent_none(self):
        self.item1.change_parent(None)
        self.assertEqual(self.item1.parentItem, None)
        self.assertListEqual([], self.item2.childItems)

    def change_parents(self):
        TreeItem.change_parents([self.item1, self.item2], self.item3)
        self.assertEqual(self.item1.parentItem, self.item3)
        self.assertIn(self.item1, self.item3.childItems)
        self.assertEqual(self.item2.parentItem, self.item3)
        self.assertIn(self.item2, self.item3.childItems)

    def test_append_child(self):
        self.item1.append_child(self.item3)
        self.assertIn(self.item3, self.item1.childItems)

    def test_remove_children(self):
        TreeItem.change_parents([self.item1, self.item2], self.item3)
        self.assertListEqual([self.item1, self.item2], self.item3.childItems)

        s = self.item3.remove_children(
            -1, 1
        )  # should return False (it is not possible)
        self.assertFalse(s)
        self.assertListEqual([self.item1, self.item2], self.item3.childItems)

        s = self.item3.remove_children(2, 1)  # should return False (it is not possible)
        self.assertFalse(s)
        self.assertListEqual([self.item1, self.item2], self.item3.childItems)

        s = self.item3.remove_children(1, 2)  # should return False (it is not possible)
        self.assertFalse(s)
        self.assertListEqual([self.item1, self.item2], self.item3.childItems)

        s = self.item3.remove_children(0, 1)
        self.assertTrue(s)
        self.assertListEqual([self.item2], self.item3.childItems)

        s = self.item3.remove_children(0, 1)
        self.assertTrue(s)
        self.assertListEqual([], self.item3.childItems)

        s = self.item3.remove_children(0, 1)
        self.assertFalse(s)
        self.assertListEqual([], self.item3.childItems)

        # test remove multiple
        TreeItem.change_parents([self.item1, self.item2], self.item3)
        s = self.item3.remove_children(0, 2)
        self.assertTrue(s)
        self.assertListEqual([], self.item3.childItems)

    def test_children_count(self):
        self.assertEqual(0, self.item3.children_count())

        TreeItem.change_parents([self.item1, self.item2], self.item3)
        self.assertEqual(2, self.item3.children_count())

        self.item3.remove_children(0, 1)
        self.assertEqual(1, self.item3.children_count())

        self.item3.remove_children(0, 1)
        self.assertEqual(0, self.item3.children_count())

    def test_row(self):
        # when no parent it is assumed that the item is a root item - its row is 0
        self.assertEqual(0, self.item1.row())
        self.assertEqual(0, self.item2.row())
        self.assertEqual(0, self.item3.row())

        # add parent
        TreeItem.change_parents([self.item1, self.item2], self.item3)
        self.assertEqual(0, self.item1.row())
        self.assertEqual(1, self.item2.row())
        self.assertEqual(0, self.item3.row())

        # different order
        self.item3.remove_children(0, 2)
        TreeItem.change_parents([self.item2, self.item1], self.item3)
        self.assertEqual(1, self.item1.row())
        self.assertEqual(0, self.item2.row())
        self.assertEqual(0, self.item3.row())

    def test_child_from_name_index(self):
        self.assertTupleEqual((None, None), self.item3.child_from_name_index("item 1"))
        TreeItem.change_parents([self.item1, self.item2], self.item3)

        self.assertTupleEqual(
            (0, self.item1), self.item3.child_from_name_index("item 1")
        )
        self.assertTupleEqual(
            (1, self.item2), self.item3.child_from_name_index("item 2")
        )

        self.item3.remove_children(0, 2)
        TreeItem.change_parents([self.item2, self.item1], self.item3)

        self.assertTupleEqual(
            (1, self.item1), self.item3.child_from_name_index("item 1")
        )
        self.assertTupleEqual(
            (0, self.item2), self.item3.child_from_name_index("item 2")
        )

    def test_contains_text(self):
        # simulate RowItem with dictionary
        self.item1.data_row = {
            "Name": "item1",
            "Cell Type": "type1",
            "Function": "function1",
        }
        self.item2.data_row = {
            "Name": "item2",
            "Cell Type": "type2",
            "Function": "function2",
        }
        filter_columns = ["Name", "Cell Type", "Function"]
        TreeItem.change_parents([self.item2, self.item1], self.item3)

        self.assertTrue(self.item1.contains_text("item1", filter_columns))
        self.assertFalse(self.item2.contains_text("item1", filter_columns))
        self.assertFalse(self.item3.contains_text("item1", filter_columns))

        self.assertTrue(self.item1.contains_text("type1", filter_columns))
        self.assertFalse(self.item2.contains_text("type1", filter_columns))
        self.assertFalse(self.item3.contains_text("type1", filter_columns))

        self.assertTrue(self.item1.contains_text("function1", filter_columns))
        self.assertFalse(self.item2.contains_text("function1", filter_columns))
        self.assertFalse(self.item3.contains_text("function1", filter_columns))

        self.assertFalse(self.item1.contains_text("item2", filter_columns))
        self.assertTrue(self.item2.contains_text("item2", filter_columns))
        self.assertFalse(self.item3.contains_text("item2", filter_columns))

        self.assertFalse(self.item1.contains_text("type2", filter_columns))
        self.assertTrue(self.item2.contains_text("type2", filter_columns))
        self.assertFalse(self.item3.contains_text("type2", filter_columns))

        self.assertFalse(self.item1.contains_text("function2", filter_columns))
        self.assertTrue(self.item2.contains_text("function2", filter_columns))
        self.assertFalse(self.item3.contains_text("function2", filter_columns))

        # test case sensitivity
        self.assertTrue(self.item1.contains_text("Item1", filter_columns))
        self.assertFalse(self.item2.contains_text("Item1", filter_columns))

        self.assertTrue(self.item1.contains_text("Type1", filter_columns))
        self.assertFalse(self.item2.contains_text("Type1", filter_columns))

        self.assertFalse(self.item1.contains_text("Item2", filter_columns))
        self.assertTrue(self.item2.contains_text("Item2", filter_columns))

        self.assertFalse(self.item1.contains_text("Type2", filter_columns))
        self.assertTrue(self.item2.contains_text("Type2", filter_columns))

    def test_get_data_rows(self):
        TreeItem.change_parents([self.item2, self.item1], self.item3)

        self.item1.data_row = "dr1"
        self.item2.data_row = "dr2"
        self.item3.data_row = "dr3"

        self.assertListEqual(
            sorted(["dr1", "dr2", "dr3"]), sorted(self.item3.get_data_rows())
        )
        self.assertListEqual(["dr1"], self.item1.get_data_rows())
        self.assertListEqual(["dr2"], self.item2.get_data_rows())

        self.item1.data_row = "dr1"
        self.item2.data_row = "dr2"
        self.item3.data_row = None

        self.assertListEqual(sorted(["dr1", "dr2"]), sorted(self.item3.get_data_rows()))
        self.assertListEqual(["dr1"], self.item1.get_data_rows())
        self.assertListEqual(["dr2"], self.item2.get_data_rows())

        self.item1.data_row = "dr1"
        self.item2.data_row = None
        self.item3.data_row = None

        self.assertListEqual(["dr1"], self.item3.get_data_rows())
        self.assertListEqual(["dr1"], self.item1.get_data_rows())
        self.assertListEqual([], self.item2.get_data_rows())

    def test_count_marker_genes(self):
        TreeItem.change_parents([self.item2, self.item1], self.item3)

        self.item1.data_row = "dr1"
        self.item2.data_row = "dr2"
        self.item3.data_row = "dr3"

        self.assertEqual(3, self.item3.count_marker_genes())
        self.assertEqual(1, self.item1.count_marker_genes())
        self.assertEqual(1, self.item2.count_marker_genes())

        self.item1.data_row = "dr1"
        self.item2.data_row = "dr2"
        self.item3.data_row = None

        self.assertEqual(2, self.item3.count_marker_genes())
        self.assertEqual(1, self.item1.count_marker_genes())
        self.assertEqual(1, self.item2.count_marker_genes())

        self.item1.data_row = "dr1"
        self.item2.data_row = None
        self.item3.data_row = None

        self.assertEqual(1, self.item3.count_marker_genes())
        self.assertEqual(1, self.item1.count_marker_genes())
        self.assertEqual(0, self.item2.count_marker_genes())


class TestOWMarkerGenes(WidgetTest):
    @classmethod
    def setUpClass(cls):
        """Code executed only once for all tests"""
        super().setUpClass()
        file_name = "panglao_gene_markers.tab"
        serverfiles.update(SERVER_FILES_DOMAIN, file_name)
        file_path = serverfiles.localpath_download(SERVER_FILES_DOMAIN, file_name)
        cls.panglao = Table.from_file(file_path)

        file_name = "cellMarker_gene_markers.tab"
        serverfiles.update(SERVER_FILES_DOMAIN, file_name)
        file_path = serverfiles.localpath_download(SERVER_FILES_DOMAIN, file_name)
        cls.cell_markers = Table.from_file(file_path)

    def test_minimum_size(self):
        pass

    def setUp(self):
        self.widget = self.create_widget(OWMarkerGenes)

    def move_top_element(self, source_view: TreeView) -> None:
        """
        This function moves top element form the source_view to another.

        Parameters
        ----------
        source_view
            The view from which we move top element.
        """
        parent_index = source_view.model().index(0, 0)
        source_view.selectionModel().select(
            parent_index, QItemSelectionModel.Select | QItemSelectionModel.Rows
        )
        self.widget.move_button.click()

    def move_first_child(self, source_view: TreeView) -> None:
        """
        This function moves the first child element from source_view to another.

        Parameters
        ----------
        source_view
            The view from which we move top element.
        """
        parent_index = source_view.model().index(0, 0)
        gene_index = source_view.model().index(0, 0, parent_index)
        source_view.selectionModel().select(
            gene_index, QItemSelectionModel.Select | QItemSelectionModel.Rows
        )
        self.widget.move_button.click()

    def test_data_not_empty(self):
        """
        Test that data are present
        """
        w = self.widget
        self.assertIsNotNone(w.data)

    def test_available_sources(self):
        """
        Test if all sources retrieved. Currently there are two sources available.
        When more available unittest will be changed.
        """
        self.assertListEqual(
            ["CellMarker", "Panglao"], list(self.widget.available_sources.keys())
        )

    def test_source_changed(self):
        """
        Test changing the source of the data.
        """
        # cell marker data
        self.assertEqual("Panglao", self.widget.db_source_cb.currentText())
        self.assertTrue(isinstance(self.widget.data, Table))
        self.assertEqual(len(self.panglao), len(self.widget.data))

        # Panglao data
        simulate.combobox_activate_index(self.widget.controls.source_index, 1)
        self.assertEqual("CellMarker", self.widget.db_source_cb.currentText())
        self.assertTrue(isinstance(self.widget.data, Table))
        self.assertEqual(len(self.cell_markers), len(self.widget.data))

        # cell marker data
        simulate.combobox_activate_index(self.widget.controls.source_index, 0)
        self.assertEqual("Panglao", self.widget.db_source_cb.currentText())
        self.assertTrue(isinstance(self.widget.data, Table))
        self.assertEqual(len(self.panglao), len(self.widget.data))

    def test_organism_changed(self):
        """
        Test changing organisms in combination with changing the source.
        """
        simulate.combobox_activate_index(self.widget.controls.source_index, 0)
        simulate.combobox_activate_index(self.widget.controls.organism_index, 0)
        self.assertEqual("Panglao", self.widget.db_source_cb.currentText())
        self.assertEqual("Human", self.widget.group_cb.currentText())

        human_rows = self.widget.data.get_column("Organism") == "Human"
        cell_types = self.widget.data.get_column("Cell Type")

        model = self.widget.available_markers_view.model().sourceModel()
        self.assertEqual(
            sum(self.panglao.get_column("Organism") == "Human"), len(model)
        )
        self.assertEqual(
            len(np.unique(cell_types[human_rows])), len(model.rootItem.childItems)
        )

        simulate.combobox_activate_index(self.widget.controls.source_index, 0)
        simulate.combobox_activate_index(self.widget.controls.organism_index, 1)
        self.assertEqual("Panglao", self.widget.db_source_cb.currentText())
        self.assertEqual("Mouse", self.widget.group_cb.currentText())

        model = self.widget.available_markers_view.model().sourceModel()
        self.assertEqual(
            sum(self.panglao.get_column("Organism") == "Mouse"), len(model)
        )
        self.assertEqual(
            len(np.unique(cell_types[~human_rows])), len(model.rootItem.childItems)
        )

        simulate.combobox_activate_index(self.widget.controls.source_index, 1)
        simulate.combobox_activate_index(self.widget.controls.organism_index, 0)
        self.assertEqual("CellMarker", self.widget.db_source_cb.currentText())
        self.assertEqual("Human", self.widget.group_cb.currentText())

        human_rows = self.widget.data.get_column("Organism") == "Human"
        cell_types = self.widget.data.get_column("Cell Type")

        model = self.widget.available_markers_view.model().sourceModel()
        self.assertEqual(
            sum(self.cell_markers.get_column("Organism") == "Human"), len(model)
        )
        self.assertEqual(
            len(np.unique(cell_types[human_rows])), len(model.rootItem.childItems)
        )

        simulate.combobox_activate_index(self.widget.controls.source_index, 1)
        simulate.combobox_activate_index(self.widget.controls.organism_index, 1)
        self.assertEqual("CellMarker", self.widget.db_source_cb.currentText())
        self.assertEqual("Mouse", self.widget.group_cb.currentText())

        model = self.widget.available_markers_view.model().sourceModel()
        self.assertEqual(
            sum(self.cell_markers.get_column("Organism") == "Mouse"), len(model)
        )
        self.assertEqual(
            len(np.unique(cell_types[~human_rows])), len(model.rootItem.childItems)
        )

    def test_group_by_changed(self):
        """
        Test changing the group by parameter (Cell Type, Function)
        """
        # group by cell type
        simulate.combobox_activate_index(
            self.widget.controls.selected_root_attribute, 0
        )

        human_rows = self.widget.data.get_column("Organism") == "Human"
        cell_types = np.unique(self.widget.data.get_column("Cell Type")[human_rows])
        model = self.widget.available_markers_view.model().sourceModel()
        self.assertEqual(len(cell_types), len(model.rootItem.childItems))

        # group by function
        simulate.combobox_activate_index(
            self.widget.controls.selected_root_attribute, 1
        )

        functions = np.unique(self.widget.data.get_column("Function")[human_rows])
        model = self.widget.available_markers_view.model().sourceModel()
        self.assertEqual(len(functions), len(model.rootItem.childItems))

    def test_move_elements(self):
        """
        Move elements between booth views and observe if numbers matches.
        This test just move the single element.
        """
        # select first three elements
        src_view = self.widget.available_markers_view
        dest_view = self.widget.selected_markers_view
        num_items_source_view = len(src_view.model().sourceModel())
        num_root_items = src_view.model().rowCount(QModelIndex())

        # select first element
        index = src_view.model().index(0, 0)
        src_view.selectionModel().select(
            index, QItemSelectionModel.Select | QItemSelectionModel.Rows
        )
        num_items = src_view.model().rowCount(
            index
        )  # number of items under selected item

        # move element to the TreeView with selected genes
        self.widget.move_button.click()
        self.assertEqual(len(dest_view.model().sourceModel()), num_items)
        output = self.get_output(self.widget.Outputs.genes)
        self.assertEqual(num_items, len(output))
        self.assertEqual(
            dest_view.model().rowCount(QModelIndex()), 1
        )  # one root element added
        index = dest_view.model().index(0, 0)
        self.assertEqual(dest_view.model().rowCount(index), num_items)

        self.assertEqual(
            src_view.model().rowCount(QModelIndex()), num_root_items - 1
        )  # one item is remove from view

        # move everything back
        dest_view.selectionModel().select(
            index, QItemSelectionModel.Select | QItemSelectionModel.Rows
        )
        self.widget.move_button.click()

        self.assertEqual(num_items_source_view, len(src_view.model().sourceModel()))
        self.assertEqual(src_view.model().rowCount(QModelIndex()), num_root_items)

    def test_move_more_elements(self):
        """
        Move elements between booth views and observe if numbers matches.
        This test keep moving more elements.
        """
        # select first three elements
        src_view = self.widget.available_markers_view
        dest_view = self.widget.selected_markers_view
        num_items_source_view = len(src_view.model().sourceModel())
        num_root_items = src_view.model().rowCount(QModelIndex())

        # select first element
        index = src_view.model().index(0, 0)
        index1 = src_view.model().index(1, 0)
        src_view.selectionModel().select(
            index, QItemSelectionModel.Select | QItemSelectionModel.Rows
        )
        src_view.selectionModel().select(
            index1, QItemSelectionModel.Select | QItemSelectionModel.Rows
        )
        num_items = src_view.model().rowCount(index)
        num_items1 = src_view.model().rowCount(index1)

        # move elements to the TreeView with selected genes
        self.widget.move_button.click()
        self.assertEqual(len(dest_view.model().sourceModel()), num_items + num_items1)
        output = self.get_output(self.widget.Outputs.genes)
        self.assertEqual(num_items + num_items1, len(output))
        self.assertEqual(
            dest_view.model().rowCount(QModelIndex()), 2
        )  # two root element added
        index = dest_view.model().index(0, 0)
        index1 = dest_view.model().index(1, 0)
        self.assertEqual(
            dest_view.model().rowCount(index) + dest_view.model().rowCount(index1),
            num_items + num_items1,
        )

        self.assertEqual(src_view.model().rowCount(QModelIndex()), num_root_items - 2)

        # move everything back one by one
        dest_view.selectionModel().select(
            index, QItemSelectionModel.Select | QItemSelectionModel.Rows
        )
        self.widget.move_button.click()
        index1 = dest_view.model().index(0, 0)
        self.assertEqual(dest_view.model().rowCount(index1), num_items1)
        output = self.get_output(self.widget.Outputs.genes)
        self.assertEqual(num_items1, len(output))
        self.assertEqual(len(dest_view.model().sourceModel()), num_items1)
        self.assertEqual(src_view.model().rowCount(QModelIndex()), num_root_items - 1)

        # move the other one back
        dest_view.selectionModel().select(
            index1, QItemSelectionModel.Select | QItemSelectionModel.Rows
        )
        self.widget.move_button.click()
        self.assertEqual(num_items_source_view, len(src_view.model().sourceModel()))
        self.assertEqual(0, len(dest_view.model().sourceModel()))
        self.assertEqual(src_view.model().rowCount(QModelIndex()), num_root_items)
        self.assertEqual(dest_view.model().rowCount(QModelIndex()), 0)

    def test_move_genes(self):
        """
        Move elements between booth views and observe if numbers matches.
        In previous test we were moving just root elements.
        In this test we will move children elements.
        """
        src_view = self.widget.available_markers_view
        dest_view = self.widget.selected_markers_view
        num_items_source_view = len(src_view.model().sourceModel())
        num_root_items = src_view.model().rowCount(QModelIndex())

        # select first element
        parent_index = src_view.model().index(0, 0)
        index = src_view.model().index(0, 0, parent_index)
        index1 = src_view.model().index(1, 0, parent_index)

        src_view.selectionModel().select(
            index, QItemSelectionModel.Select | QItemSelectionModel.Rows
        )
        src_view.selectionModel().select(
            index1, QItemSelectionModel.Select | QItemSelectionModel.Rows
        )

        # move elements to the TreeView with selected genes
        self.widget.move_button.click()
        self.assertEqual(len(dest_view.model().sourceModel()), 2)
        output = self.get_output(self.widget.Outputs.genes)
        self.assertEqual(2, len(output))
        self.assertEqual(
            dest_view.model().rowCount(QModelIndex()), 1
        )  # one parent element added
        parent_index = dest_view.model().index(0, 0)
        self.assertEqual(dest_view.model().rowCount(parent_index), 2)
        self.assertEqual(src_view.model().rowCount(QModelIndex()), num_root_items)

        # move everything back one by one
        index = dest_view.model().index(0, 0, parent_index)
        dest_view.selectionModel().select(
            index, QItemSelectionModel.Select | QItemSelectionModel.Rows
        )
        self.widget.move_button.click()

        self.assertEqual(dest_view.model().rowCount(parent_index), 1)
        output = self.get_output(self.widget.Outputs.genes)
        self.assertEqual(1, len(output))
        self.assertEqual(len(dest_view.model().sourceModel()), 1)
        self.assertEqual(src_view.model().rowCount(QModelIndex()), num_root_items)

        # move the other one back
        index1 = dest_view.model().index(0, 0, parent_index)
        dest_view.selectionModel().select(
            index1, QItemSelectionModel.Select | QItemSelectionModel.Rows
        )
        self.widget.move_button.click()
        self.assertEqual(num_items_source_view, len(src_view.model().sourceModel()))
        self.assertEqual(src_view.model().rowCount(QModelIndex()), num_root_items)

    def test_description(self):
        """
        Test correctness of the gene description showing in the bottom of widget.
        """
        src_view = self.widget.available_markers_view
        dest_view = self.widget.selected_markers_view

        not_gene = "Select a gene to see information."
        first_gene_info = "Gene name: AKR1C3"

        # first test in the source view
        parent_index = src_view.model().index(0, 0)
        gene_index = src_view.model().index(0, 0, parent_index)
        gene_index1 = src_view.model().index(1, 0, parent_index)

        # no description for parent
        src_view.selectionModel().select(
            parent_index, QItemSelectionModel.Select | QItemSelectionModel.Rows
        )
        self.assertEqual(not_gene, self.widget.descriptionlabel.toPlainText())

        # no description for multiple genes
        src_view.selectionModel().select(
            gene_index, QItemSelectionModel.Select | QItemSelectionModel.Rows
        )
        src_view.selectionModel().select(
            gene_index1, QItemSelectionModel.Select | QItemSelectionModel.Rows
        )
        self.assertEqual(not_gene, self.widget.descriptionlabel.toPlainText())

        # description for first gene
        src_view.selectionModel().select(
            gene_index, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows
        )
        self.assertIn(first_gene_info, self.widget.descriptionlabel.toPlainText())

        # move elements
        src_view.selectionModel().select(
            gene_index, QItemSelectionModel.Select | QItemSelectionModel.Rows
        )
        src_view.selectionModel().select(
            gene_index1, QItemSelectionModel.Select | QItemSelectionModel.Rows
        )
        self.widget.move_button.click()

        # test in the dest view
        parent_index = dest_view.model().index(0, 0)
        gene_index = dest_view.model().index(0, 0, parent_index)
        gene_index1 = dest_view.model().index(1, 0, parent_index)

        # no description for parent
        dest_view.selectionModel().select(
            parent_index, QItemSelectionModel.Select | QItemSelectionModel.Rows
        )
        self.assertEqual(not_gene, self.widget.descriptionlabel.toPlainText())

        # no description for multiple genes
        dest_view.selectionModel().select(
            gene_index, QItemSelectionModel.Select | QItemSelectionModel.Rows
        )
        dest_view.selectionModel().select(
            gene_index1, QItemSelectionModel.Select | QItemSelectionModel.Rows
        )
        self.assertEqual(not_gene, self.widget.descriptionlabel.toPlainText())

        # description for first gene
        dest_view.selectionModel().select(
            gene_index, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows
        )
        self.assertIn(first_gene_info, self.widget.descriptionlabel.toPlainText())

    def test_extend_collapse_both_views(self):
        """
        Test whether the item extends in the other column when extended in one.
        """
        src_view = self.widget.available_markers_view
        dest_view = self.widget.selected_markers_view

        # I do not know how to simulate click, so I just call the
        # function that is called on click
        index = src_view.model().index(0, 0)
        src_view.expand_in_other_view(index, is_expanded=True)
        tree_node = (
            src_view.model()
            .sourceModel()
            .node_from_index(src_view.model().mapToSource(index))
        )
        self.assertTrue(tree_node.expanded)

        # move one child left to check simultaneous expand
        self.move_first_child(src_view)
        index_left = src_view.model().index(0, 0)
        index_right = dest_view.model().index(0, 0)
        tree_node_left = (
            src_view.model()
            .sourceModel()
            .node_from_index(src_view.model().mapToSource(index_left))
        )
        tree_node_right = (
            dest_view.model()
            .sourceModel()
            .node_from_index(dest_view.model().mapToSource(index_right))
        )

        src_view.expand_in_other_view(index_left, is_expanded=True)
        self.assertTrue(tree_node_left.expanded)
        self.assertTrue(tree_node_right.expanded)
        self.assertTrue(dest_view.isExpanded(index_right))

        # change back to not expanded
        src_view.expand_in_other_view(index_left, is_expanded=False)
        self.assertFalse(tree_node_left.expanded)
        self.assertFalse(tree_node_right.expanded)
        self.assertFalse(dest_view.isExpanded(index_right))

        dest_view.expand_in_other_view(index_right, is_expanded=True)
        self.assertTrue(tree_node_left.expanded)
        self.assertTrue(tree_node_right.expanded)
        self.assertTrue(src_view.isExpanded(index_left))

        # change back to not expanded
        dest_view.expand_in_other_view(index_right, is_expanded=False)
        self.assertFalse(tree_node_left.expanded)
        self.assertFalse(tree_node_right.expanded)
        self.assertFalse(dest_view.isExpanded(index_left))


if __name__ == '__main__':
    unittest.main()
