# Copyright (C) 2003 by Intevation GmbH
# Authors:
# Bernhard Herzog <bh@intevation.de>
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with the software for details.

"""
Test cases for Thuban.UI.selection
"""

__version__ = "$Revision: 723 $"
# $Source$
# $Id: test_selection.py 723 2003-04-24 15:31:53Z bh $

import os
import unittest

import support
support.initthuban()

from Thuban.Model.session import Session
from Thuban.Model.map import Map
from Thuban.Model.layer import Layer
from Thuban.UI.selection import Selection
from Thuban.UI.messages import LAYER_SELECTED, SHAPES_SELECTED


class TestSelection(unittest.TestCase, support.SubscriberMixin):

    def setUp(self):
        """Instantiate a selection.

        Test cases implemented in this class can access the selection as
        self.selection.

        Also, subscribe self.subscribe_with_params to some of the
        selection's messages.

        Finally, create a list self.to_destroy with objects to be
        destroyes by calling their destroy method in tearDown() for
        objects created in test cases that need to be destroyed.
        """
        self.clear_messages()
        self.selection = Selection()
        for channel in (LAYER_SELECTED, SHAPES_SELECTED):
            self.selection.Subscribe(channel, self.subscribe_with_params,
                                     channel)
        self.to_destroy = [self.selection]

    def tearDown(self):
        """Destroy all objects in self.to_destroy and clear the message list"""
        for obj in self.to_destroy:
            obj.Destroy()
        self.to_destroy = None
        self.session = None
        self.selection = None
        self.clear_messages()

    def get_layer(self):
        """Return a layer to have something to test with.

        Also, instantiate self.session if not done already. The layer
        (and the session when it is created) are added to
        self.to_destroy so that they are properly destroyed at the end
        of the test.

        The layer should not be added to a map in the session to avoid a
        situation where its destroy method is called twice. This
        situation should not arise in the selection tests.
        """
        if not hasattr(self, "session"):
            self.session = Session("Test Session for %s" % self.__class__)
            self.to_destroy.append(self.session)
        filename = os.path.join("..", "Data", "iceland", "roads-line.shp")
        layer = Layer("Selection Test Layer",
                      self.session.OpenShapefile(filename))
        self.to_destroy.append(layer)
        return layer

    def test_instatiation(self):
        """Test initial state of the selection"""
        self.assertEquals(self.selection.SelectedLayer(), None)
        self.assertEquals(self.selection.SelectedShapes(), [])
        self.failIf(self.selection.HasSelectedLayer())

    #
    #   SelectLayer Tests
    #

    def test_select_layer_without_current_selection(self):
        """Test Selection.SelectLayer() without current selection"""
        layer = self.get_layer()

        self.selection.SelectLayer(layer)
        # After selecting a layer, no shapes are selected
        self.assertEquals(self.selection.SelectedLayer(), layer)
        self.failUnless(self.selection.HasSelectedLayer())
        self.assertEquals(self.selection.SelectedShapes(), [])
        # Since no shape was selected, only a LAYER_SELECTED message
        # should have been issued.
        self.check_messages([(layer, LAYER_SELECTED)])

    def test_select_currently_selected_layer(self):
        """Test Selection.SelectLayer(<currently selected layer>)"""
        layer = self.get_layer()

        self.selection.SelectLayer(layer)
        self.clear_messages()
        self.selection.SelectLayer(layer)

        # After selecting a layer, no shapes are selected
        self.assertEquals(self.selection.SelectedLayer(), layer)
        self.failUnless(self.selection.HasSelectedLayer())
        self.assertEquals(self.selection.SelectedShapes(), [])
        # Since nothing has changed, really, no messages should have
        # been issued
        self.check_messages([])

    def test_select_layer_with_previous_selection(self):
        """Test Selection.SelectLayer() with previous selection"""
        self.selection.SelectShapes(self.get_layer(), [0])
        self.clear_messages()

        layer = self.get_layer()
        self.selection.SelectLayer(layer)

        # After selecting a layer, no shapes are selected
        self.assertEquals(self.selection.SelectedLayer(), layer)
        self.failUnless(self.selection.HasSelectedLayer())
        self.assertEquals(self.selection.SelectedShapes(), [])
        # Since a shape and a layer was selected, a LAYER_SELECTED and a
        # SHAPES_SELECTED message should have been issued.
        self.check_messages([(layer, LAYER_SELECTED),
                             (layer, [], SHAPES_SELECTED)])

    def test_select_layer_with_None(self):
        """Test Selection.SelectLayer(None)

        Calling SelectLayer with None should deselect it.
        """
        self.selection.SelectShapes(self.get_layer(), [0])
        self.clear_messages()
        self.selection.SelectLayer(None)

        # After selecting a layer, no shapes are selected
        self.assertEquals(self.selection.SelectedLayer(), None)
        self.failIf(self.selection.HasSelectedLayer())
        self.assertEquals(self.selection.SelectedShapes(), [])
        # Since no shape was selected, only a LAYER_SELECTED message
        # should have been issued.
        self.check_messages([(None, LAYER_SELECTED),
                             (None, [], SHAPES_SELECTED)])

    #
    #   SelectShapes Tests
    #

    def test_select_new_layer_and_new_shape(self):
        """Test Selection.SelectShapes(<new layer>, <new shapes>)"""
        layer = self.get_layer()

        self.selection.SelectShapes(layer, [0, 3, 1])

        self.assertEquals(self.selection.SelectedLayer(), layer)
        self.failUnless(self.selection.HasSelectedLayer())
        self.assertEquals(self.selection.SelectedShapes(), [0, 1, 3])
        self.check_messages([(layer, LAYER_SELECTED),
                             (layer, [0, 1, 3], SHAPES_SELECTED)])

    def test_select_old_layer_and_old_shape(self):
        """Test Selection.SelectShape(<old layer>, <old shapes>)"""
        layer = self.get_layer()

        self.selection.SelectShapes(layer, [0, 10, 2])
        self.clear_messages()
        # Deliberate use a different order of the shape ids to check
        # whether they're still considered equal
        self.selection.SelectShapes(layer, [2, 0, 10])

        # Selecting an already selected layer and shapes should not
        # result in any messages but still have the right layer and
        # shapes selected
        self.assertEquals(self.selection.SelectedLayer(), layer)
        self.failUnless(self.selection.HasSelectedLayer())
        self.assertEquals(self.selection.SelectedShapes(), [0, 2, 10])
        self.check_messages([])

    def test_select_old_layer_and_new_shape(self):
        """Test Selection.SelectShapes(<old layer>, <new shape>)"""
        layer = self.get_layer()

        self.selection.SelectShapes(layer, [0])
        self.clear_messages()
        self.selection.SelectShapes(layer, [1])

        # Selecting a different shape in the already selected layer
        # should only produce a SHAPES_SELECTED message
        # After selecting a shape, both a shape and a layer are selected
        self.assertEquals(self.selection.SelectedLayer(), layer)
        self.failUnless(self.selection.HasSelectedLayer())
        self.assertEquals(self.selection.SelectedShapes(), [1])
        self.check_messages([(layer, [1], SHAPES_SELECTED)])

    #
    #   Adding Shapes Tests
    #

    def test_add_shapes_new_layer_new_shapes(self):
        """Test Selection.SelectShapes(<same layer>, <new shapes>, add = 1)"""
        layer = self.get_layer()

        self.selection.SelectShapes(layer, [10, 7], add = 1)

        self.assertEquals(self.selection.SelectedLayer(), layer)
        self.failUnless(self.selection.HasSelectedLayer())
        # The list of selected shapes will be sorted in ascending order
        self.assertEquals(self.selection.SelectedShapes(), [7, 10])
        self.check_messages([(layer, LAYER_SELECTED),
                             (layer, [7, 10], SHAPES_SELECTED)])

    def test_add_shapes_same_layer_new_shapes(self):
        """Test Selection.SelectShapes(<same layer>, <new shapes>, add = 1)"""
        layer = self.get_layer()

        self.selection.SelectShapes(layer, [0, 6, 5])
        self.clear_messages()

        self.selection.SelectShapes(layer, [10, 7], add = 1)

        self.assertEquals(self.selection.SelectedLayer(), layer)
        self.failUnless(self.selection.HasSelectedLayer())
        # The list of selected shapes will be sorted in ascending order
        self.assertEquals(self.selection.SelectedShapes(), [0, 5, 6, 7, 10])
        self.check_messages([(layer, [0, 5, 6, 7, 10], SHAPES_SELECTED)])

    def test_add_shapes_same_layer_already_selected_shapes(self):
        """Test Selection.SelectShapes(<same layer>, <some old shapes>, add=1)
        """
        layer = self.get_layer()

        self.selection.SelectShapes(layer, [0, 6, 5])
        self.clear_messages()

        self.selection.SelectShapes(layer, [6, 0], add = 1)

        self.assertEquals(self.selection.SelectedLayer(), layer)
        self.failUnless(self.selection.HasSelectedLayer())
        # The list of selected shapes will be sorted in ascending order
        self.assertEquals(self.selection.SelectedShapes(), [0, 5, 6])
        self.check_messages([])

    #
    #   ClearSelection Tests
    #

    def test_clear_selection(self):
        """Test Selection.ClearSelection() when nothing is selected"""
        self.selection.ClearSelection()

        # After clearing the selection nothing is selected
        self.assertEquals(self.selection.SelectedLayer(), None)
        self.failIf(self.selection.HasSelectedLayer())
        self.assertEquals(self.selection.SelectedShapes(), [])
        # No messages should have been sent because the selection
        # doesn't have changed due to the ClearSelection()
        self.check_messages([])

    def test_clear_selection_with_selected_layer(self):
        """Test Selection.ClearSelection() when a layer is selected"""
        self.selection.ClearSelection()

        self.selection.SelectLayer(self.get_layer())
        self.clear_messages()
        self.selection.ClearSelection()

        # After clearing the selection nothing is selected
        self.assertEquals(self.selection.SelectedLayer(), None)
        self.failIf(self.selection.HasSelectedLayer())
        self.assertEquals(self.selection.SelectedShapes(), [])
        # No messages should have been sent because the selection
        # doesn't have changed due to the ClearSelection()
        self.check_messages([(None, LAYER_SELECTED)])

    def test_clear_selection_with_selected_shape(self):
        """Test Selection.ClearSelection() when a layer is selected"""
        self.selection.ClearSelection()

        self.selection.SelectShapes(self.get_layer(), [0])
        self.clear_messages()
        self.selection.ClearSelection()

        # After clearing the selection nothing is selected
        self.assertEquals(self.selection.SelectedLayer(), None)
        self.failIf(self.selection.HasSelectedLayer())
        self.assertEquals(self.selection.SelectedShapes(), [])
        # No messages should have been sent because the selection
        # doesn't have changed due to the ClearSelection()
        self.check_messages([(None, LAYER_SELECTED),
                             (None, [], SHAPES_SELECTED)])


if __name__ == "__main__":
    support.run_tests()
