# -*- coding: utf-8 -*-
"""QGIS Unit tests for QgsProject.

.. note:: This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
"""
from builtins import chr
from builtins import range
__author__ = 'Sebastian Dietrich'
__date__ = '19/11/2015'
__copyright__ = 'Copyright 2015, The QGIS Project'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '49858ca4978819f604fa70b9ad09541dbf493ec9'

import os

import qgis  # NOQA

from qgis.core import (QgsProject,
                       QgsApplication,
                       QgsUnitTypes,
                       QgsCoordinateReferenceSystem,
                       QgsVectorLayer,
                       QgsRasterLayer,
                       QgsMapLayer)
from qgis.gui import (QgsLayerTreeMapCanvasBridge,
                      QgsMapCanvas)

from qgis.PyQt.QtTest import QSignalSpy
from qgis.PyQt.QtCore import QT_VERSION_STR, QTemporaryFile, QTemporaryDir
import sip

from qgis.testing import start_app, unittest
from utilities import (unitTestDataPath)
from shutil import copyfile

app = start_app()
TEST_DATA_DIR = unitTestDataPath()


def createLayer(name):
    return QgsVectorLayer("Point?field=x:string", name, "memory")


class TestQgsProject(unittest.TestCase):

    def __init__(self, methodName):
        """Run once on class initialization."""
        unittest.TestCase.__init__(self, methodName)
        self.messageCaught = False

    def test_makeKeyTokens_(self):
        # see http://www.w3.org/TR/REC-xml/#d0e804 for a list of valid characters

        invalidTokens = []
        validTokens = []

        # all test tokens will be generated by prepending or inserting characters to this token
        validBase = "valid"

        # some invalid characters, not allowed anywhere in a token
        # note that '/' must not be added here because it is taken as a separator by makeKeyTokens_()
        invalidChars = "+*,;<>|!$%()=?#\x01"

        # generate the characters that are allowed at the start of a token (and at every other position)
        validStartChars = ":_"
        charRanges = [
            (ord('a'), ord('z')),
            (ord('A'), ord('Z')),
            (0x00F8, 0x02FF),
            (0x0370, 0x037D),
            (0x037F, 0x1FFF),
            (0x200C, 0x200D),
            (0x2070, 0x218F),
            (0x2C00, 0x2FEF),
            (0x3001, 0xD7FF),
            (0xF900, 0xFDCF),
            (0xFDF0, 0xFFFD),
            # (0x10000, 0xEFFFF),   while actually valid, these are not yet accepted by makeKeyTokens_()
        ]
        for r in charRanges:
            for c in range(r[0], r[1]):
                validStartChars += chr(c)

        # generate the characters that are only allowed inside a token, not at the start
        validInlineChars = "-.\xB7"
        charRanges = [
            (ord('0'), ord('9')),
            (0x0300, 0x036F),
            (0x203F, 0x2040),
        ]
        for r in charRanges:
            for c in range(r[0], r[1]):
                validInlineChars += chr(c)

        # test forbidden start characters
        for c in invalidChars + validInlineChars:
            invalidTokens.append(c + validBase)

        # test forbidden inline characters
        for c in invalidChars:
            invalidTokens.append(validBase[:4] + c + validBase[4:])

        # test each allowed start character
        for c in validStartChars:
            validTokens.append(c + validBase)

        # test each allowed inline character
        for c in validInlineChars:
            validTokens.append(validBase[:4] + c + validBase[4:])

        logger = QgsApplication.messageLog()
        logger.messageReceived.connect(self.catchMessage)
        prj = QgsProject.instance()

        for token in validTokens:
            self.messageCaught = False
            prj.readEntry("test", token)
            myMessage = "valid token '%s' not accepted" % (token)
            assert not self.messageCaught, myMessage

        for token in invalidTokens:
            self.messageCaught = False
            prj.readEntry("test", token)
            myMessage = "invalid token '%s' accepted" % (token)
            assert self.messageCaught, myMessage

        logger.messageReceived.disconnect(self.catchMessage)

    def catchMessage(self):
        self.messageCaught = True

    def testCrs(self):
        prj = QgsProject.instance()
        prj.clear()

        self.assertFalse(prj.crs().isValid())
        prj.setCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:3111'))
        self.assertEqual(prj.crs().authid(), 'EPSG:3111')

    def testEllipsoid(self):
        prj = QgsProject.instance()
        prj.clear()

        prj.setCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:3111'))
        prj.setEllipsoid('WGS84')
        self.assertEqual(prj.ellipsoid(), 'WGS84')

        # if project has NO crs, then ellipsoid should always be none
        prj.setCrs(QgsCoordinateReferenceSystem())
        self.assertEqual(prj.ellipsoid(), 'NONE')

    def testDistanceUnits(self):
        prj = QgsProject.instance()
        prj.clear()

        prj.setDistanceUnits(QgsUnitTypes.DistanceFeet)
        self.assertEqual(prj.distanceUnits(), QgsUnitTypes.DistanceFeet)

    def testAreaUnits(self):
        prj = QgsProject.instance()
        prj.clear()

        prj.setAreaUnits(QgsUnitTypes.AreaSquareFeet)
        self.assertEqual(prj.areaUnits(), QgsUnitTypes.AreaSquareFeet)

    def testReadEntry(self):
        prj = QgsProject.instance()
        prj.read(os.path.join(TEST_DATA_DIR, 'labeling/test-labeling.qgs'))

        # valid key, valid int value
        self.assertEqual(prj.readNumEntry("SpatialRefSys", "/ProjectionsEnabled", -1)[0], 0)
        # invalid key
        self.assertEqual(prj.readNumEntry("SpatialRefSys", "/InvalidKey", -1)[0], -1)

    def testEmbeddedGroup(self):
        testdata_path = unitTestDataPath('embedded_groups') + '/'

        prj_path = os.path.join(testdata_path, "project2.qgs")
        prj = QgsProject()
        prj.read(prj_path)

        layer_tree_group = prj.layerTreeRoot()
        layers_ids = layer_tree_group.findLayerIds()

        layers_names = []
        for layer_id in layers_ids:
            name = prj.mapLayer(layer_id).name()
            layers_names.append(name)

        expected = ['polys', 'lines']
        self.assertEqual(sorted(layers_names), sorted(expected))

    def testInstance(self):
        """ test retrieving global instance """
        self.assertTrue(QgsProject.instance())

        # register a layer to the singleton
        QgsProject.instance().addMapLayer(createLayer('test'))

        # check that the same instance is returned
        self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)

        QgsProject.instance().removeAllMapLayers()

    def test_addMapLayer(self):
        """ test adding individual map layers to registry """
        QgsProject.instance().removeAllMapLayers()

        l1 = createLayer('test')
        self.assertEqual(QgsProject.instance().addMapLayer(l1), l1)
        self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
        self.assertEqual(QgsProject.instance().count(), 1)

        # adding a second layer should leave existing layers intact
        l2 = createLayer('test2')
        self.assertEqual(QgsProject.instance().addMapLayer(l2), l2)
        self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
        self.assertEqual(len(QgsProject.instance().mapLayersByName('test2')), 1)
        self.assertEqual(QgsProject.instance().count(), 2)

        QgsProject.instance().removeAllMapLayers()

    def test_addMapLayerAlreadyAdded(self):
        """ test that already added layers can't be readded to registry """
        QgsProject.instance().removeAllMapLayers()

        l1 = createLayer('test')
        QgsProject.instance().addMapLayer(l1)
        self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
        self.assertEqual(QgsProject.instance().count(), 1)
        self.assertEqual(QgsProject.instance().addMapLayer(l1), None)
        self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
        self.assertEqual(QgsProject.instance().count(), 1)

        QgsProject.instance().removeAllMapLayers()

    def test_addMapLayerInvalid(self):
        """ test that invalid map layersd can't be added to registry """
        QgsProject.instance().removeAllMapLayers()

        self.assertEqual(QgsProject.instance().addMapLayer(QgsVectorLayer("Point?field=x:string", 'test', "xxx")), None)
        self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 0)
        self.assertEqual(QgsProject.instance().count(), 0)

        QgsProject.instance().removeAllMapLayers()

    def test_addMapLayerSignals(self):
        """ test that signals are correctly emitted when adding map layer"""

        QgsProject.instance().removeAllMapLayers()

        layer_was_added_spy = QSignalSpy(QgsProject.instance().layerWasAdded)
        layers_added_spy = QSignalSpy(QgsProject.instance().layersAdded)
        legend_layers_added_spy = QSignalSpy(QgsProject.instance().legendLayersAdded)

        l1 = createLayer('test')
        QgsProject.instance().addMapLayer(l1)

        # can't seem to actually test the data which was emitted, so best we can do is test
        # the signal count
        self.assertEqual(len(layer_was_added_spy), 1)
        self.assertEqual(len(layers_added_spy), 1)
        self.assertEqual(len(legend_layers_added_spy), 1)

        # layer not added to legend
        QgsProject.instance().addMapLayer(createLayer('test2'), False)
        self.assertEqual(len(layer_was_added_spy), 2)
        self.assertEqual(len(layers_added_spy), 2)
        self.assertEqual(len(legend_layers_added_spy), 1)

        # try readding a layer already in the registry
        QgsProject.instance().addMapLayer(l1)
        # should be no extra signals emitted
        self.assertEqual(len(layer_was_added_spy), 2)
        self.assertEqual(len(layers_added_spy), 2)
        self.assertEqual(len(legend_layers_added_spy), 1)

    def test_addMapLayers(self):
        """ test adding multiple map layers to registry """
        QgsProject.instance().removeAllMapLayers()

        l1 = createLayer('test')
        l2 = createLayer('test2')
        self.assertEqual(set(QgsProject.instance().addMapLayers([l1, l2])), set([l1, l2]))
        self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
        self.assertEqual(len(QgsProject.instance().mapLayersByName('test2')), 1)
        self.assertEqual(QgsProject.instance().count(), 2)

        # adding more layers should leave existing layers intact
        l3 = createLayer('test3')
        l4 = createLayer('test4')
        self.assertEqual(set(QgsProject.instance().addMapLayers([l3, l4])), set([l3, l4]))
        self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
        self.assertEqual(len(QgsProject.instance().mapLayersByName('test2')), 1)
        self.assertEqual(len(QgsProject.instance().mapLayersByName('test3')), 1)
        self.assertEqual(len(QgsProject.instance().mapLayersByName('test4')), 1)
        self.assertEqual(QgsProject.instance().count(), 4)

        QgsProject.instance().removeAllMapLayers()

    def test_addMapLayersInvalid(self):
        """ test that invalid map layersd can't be added to registry """
        QgsProject.instance().removeAllMapLayers()

        self.assertEqual(QgsProject.instance().addMapLayers([QgsVectorLayer("Point?field=x:string", 'test', "xxx")]), [])
        self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 0)
        self.assertEqual(QgsProject.instance().count(), 0)

        QgsProject.instance().removeAllMapLayers()

    def test_addMapLayersAlreadyAdded(self):
        """ test that already added layers can't be readded to registry """
        QgsProject.instance().removeAllMapLayers()

        l1 = createLayer('test')
        self.assertEqual(QgsProject.instance().addMapLayers([l1]), [l1])
        self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
        self.assertEqual(QgsProject.instance().count(), 1)
        self.assertEqual(QgsProject.instance().addMapLayers([l1]), [])
        self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
        self.assertEqual(QgsProject.instance().count(), 1)

        QgsProject.instance().removeAllMapLayers()

    def test_addMapLayersSignals(self):
        """ test that signals are correctly emitted when adding map layers"""
        QgsProject.instance().removeAllMapLayers()

        layer_was_added_spy = QSignalSpy(QgsProject.instance().layerWasAdded)
        layers_added_spy = QSignalSpy(QgsProject.instance().layersAdded)
        legend_layers_added_spy = QSignalSpy(QgsProject.instance().legendLayersAdded)

        l1 = createLayer('test')
        l2 = createLayer('test2')
        QgsProject.instance().addMapLayers([l1, l2])

        # can't seem to actually test the data which was emitted, so best we can do is test
        # the signal count
        self.assertEqual(len(layer_was_added_spy), 2)
        self.assertEqual(len(layers_added_spy), 1)
        self.assertEqual(len(legend_layers_added_spy), 1)

        # layer not added to legend
        QgsProject.instance().addMapLayers([createLayer('test3'), createLayer('test4')], False)
        self.assertEqual(len(layer_was_added_spy), 4)
        self.assertEqual(len(layers_added_spy), 2)
        self.assertEqual(len(legend_layers_added_spy), 1)

        # try readding a layer already in the registry
        QgsProject.instance().addMapLayers([l1, l2])
        # should be no extra signals emitted
        self.assertEqual(len(layer_was_added_spy), 4)
        self.assertEqual(len(layers_added_spy), 2)
        self.assertEqual(len(legend_layers_added_spy), 1)

    def test_mapLayerById(self):
        """ test retrieving map layer by ID """
        QgsProject.instance().removeAllMapLayers()

        # test no crash with empty registry
        self.assertEqual(QgsProject.instance().mapLayer('bad'), None)
        self.assertEqual(QgsProject.instance().mapLayer(None), None)

        l1 = createLayer('test')
        l2 = createLayer('test2')

        QgsProject.instance().addMapLayers([l1, l2])

        self.assertEqual(QgsProject.instance().mapLayer('bad'), None)
        self.assertEqual(QgsProject.instance().mapLayer(None), None)
        self.assertEqual(QgsProject.instance().mapLayer(l1.id()), l1)
        self.assertEqual(QgsProject.instance().mapLayer(l2.id()), l2)

    def test_mapLayersByName(self):
        """ test retrieving map layer by name """
        p = QgsProject()

        # test no crash with empty registry
        self.assertEqual(p.mapLayersByName('bad'), [])
        self.assertEqual(p.mapLayersByName(None), [])

        l1 = createLayer('test')
        l2 = createLayer('test2')

        p.addMapLayers([l1, l2])

        self.assertEqual(p.mapLayersByName('bad'), [])
        self.assertEqual(p.mapLayersByName(None), [])
        self.assertEqual(p.mapLayersByName('test'), [l1])
        self.assertEqual(p.mapLayersByName('test2'), [l2])

        #duplicate name
        l3 = createLayer('test')
        p.addMapLayer(l3)
        self.assertEqual(set(p.mapLayersByName('test')), set([l1, l3]))

    def test_mapLayers(self):
        """ test retrieving map layers list """
        QgsProject.instance().removeAllMapLayers()

        # test no crash with empty registry
        self.assertEqual(QgsProject.instance().mapLayers(), {})

        l1 = createLayer('test')
        l2 = createLayer('test2')

        QgsProject.instance().addMapLayers([l1, l2])

        self.assertEqual(QgsProject.instance().mapLayers(), {l1.id(): l1, l2.id(): l2})

    def test_removeMapLayersById(self):
        """ test removing map layers by ID """
        QgsProject.instance().removeAllMapLayers()

        # test no crash with empty registry
        QgsProject.instance().removeMapLayers(['bad'])
        QgsProject.instance().removeMapLayers([None])

        l1 = createLayer('test')
        l2 = createLayer('test2')
        l3 = createLayer('test3')

        QgsProject.instance().addMapLayers([l1, l2, l3])
        self.assertEqual(QgsProject.instance().count(), 3)

        #remove bad layers
        QgsProject.instance().removeMapLayers(['bad'])
        self.assertEqual(QgsProject.instance().count(), 3)
        QgsProject.instance().removeMapLayers([None])
        self.assertEqual(QgsProject.instance().count(), 3)

        # remove valid layers
        l1_id = l1.id()
        QgsProject.instance().removeMapLayers([l1_id])
        self.assertEqual(QgsProject.instance().count(), 2)
        # double remove
        QgsProject.instance().removeMapLayers([l1_id])
        self.assertEqual(QgsProject.instance().count(), 2)

        # test that layer has been deleted
        self.assertTrue(sip.isdeleted(l1))

        # remove multiple
        QgsProject.instance().removeMapLayers([l2.id(), l3.id()])
        self.assertEqual(QgsProject.instance().count(), 0)
        self.assertTrue(sip.isdeleted(l2))

        # try removing a layer not in the registry
        l4 = createLayer('test4')
        QgsProject.instance().removeMapLayers([l4.id()])
        self.assertFalse(sip.isdeleted(l4))

    # fails on qt5 due to removeMapLayers list type conversion - needs a PyName alias
    # added to removeMapLayers for QGIS 3.0
    @unittest.expectedFailure(QT_VERSION_STR[0] == '5')
    def test_removeMapLayersByLayer(self):
        """ test removing map layers by layer"""
        QgsProject.instance().removeAllMapLayers()

        # test no crash with empty registry
        QgsProject.instance().removeMapLayers([None])

        l1 = createLayer('test')
        l2 = createLayer('test2')
        l3 = createLayer('test3')

        QgsProject.instance().addMapLayers([l1, l2, l3])
        self.assertEqual(QgsProject.instance().count(), 3)

        #remove bad layers
        QgsProject.instance().removeMapLayers([None])
        self.assertEqual(QgsProject.instance().count(), 3)

        # remove valid layers
        QgsProject.instance().removeMapLayers([l1])
        self.assertEqual(QgsProject.instance().count(), 2)

        # test that layer has been deleted
        self.assertTrue(sip.isdeleted(l1))

        # remove multiple
        QgsProject.instance().removeMapLayers([l2, l3])
        self.assertEqual(QgsProject.instance().count(), 0)
        self.assertTrue(sip.isdeleted(l2))
        self.assertTrue(sip.isdeleted(l3))

    def test_removeMapLayerById(self):
        """ test removing a map layer by ID """
        QgsProject.instance().removeAllMapLayers()

        # test no crash with empty registry
        QgsProject.instance().removeMapLayer('bad')
        QgsProject.instance().removeMapLayer(None)

        l1 = createLayer('test')
        l2 = createLayer('test2')

        QgsProject.instance().addMapLayers([l1, l2])
        self.assertEqual(QgsProject.instance().count(), 2)

        #remove bad layers
        QgsProject.instance().removeMapLayer('bad')
        self.assertEqual(QgsProject.instance().count(), 2)
        QgsProject.instance().removeMapLayer(None)
        self.assertEqual(QgsProject.instance().count(), 2)

        # remove valid layers
        l1_id = l1.id()
        QgsProject.instance().removeMapLayer(l1_id)
        self.assertEqual(QgsProject.instance().count(), 1)
        # double remove
        QgsProject.instance().removeMapLayer(l1_id)
        self.assertEqual(QgsProject.instance().count(), 1)

        # test that layer has been deleted
        self.assertTrue(sip.isdeleted(l1))

        # remove second layer
        QgsProject.instance().removeMapLayer(l2.id())
        self.assertEqual(QgsProject.instance().count(), 0)
        self.assertTrue(sip.isdeleted(l2))

        # try removing a layer not in the registry
        l3 = createLayer('test3')
        QgsProject.instance().removeMapLayer(l3.id())
        self.assertFalse(sip.isdeleted(l3))

    def test_removeMapLayerByLayer(self):
        """ test removing a map layer by layer """
        QgsProject.instance().removeAllMapLayers()

        # test no crash with empty registry
        QgsProject.instance().removeMapLayer('bad')
        QgsProject.instance().removeMapLayer(None)

        l1 = createLayer('test')
        l2 = createLayer('test2')

        QgsProject.instance().addMapLayers([l1, l2])
        self.assertEqual(QgsProject.instance().count(), 2)

        #remove bad layers
        QgsProject.instance().removeMapLayer(None)
        self.assertEqual(QgsProject.instance().count(), 2)
        l3 = createLayer('test3')
        QgsProject.instance().removeMapLayer(l3)
        self.assertEqual(QgsProject.instance().count(), 2)

        # remove valid layers
        QgsProject.instance().removeMapLayer(l1)
        self.assertEqual(QgsProject.instance().count(), 1)

        # test that layer has been deleted
        self.assertTrue(sip.isdeleted(l1))

        # remove second layer
        QgsProject.instance().removeMapLayer(l2)
        self.assertEqual(QgsProject.instance().count(), 0)
        self.assertTrue(sip.isdeleted(l2))

        # try removing a layer not in the registry
        l3 = createLayer('test3')
        QgsProject.instance().removeMapLayer(l3)
        self.assertFalse(sip.isdeleted(l3))

    def test_removeAllMapLayers(self):
        """ test removing all map layers from registry """
        QgsProject.instance().removeAllMapLayers()
        l1 = createLayer('test')
        l2 = createLayer('test2')

        QgsProject.instance().addMapLayers([l1, l2])
        self.assertEqual(QgsProject.instance().count(), 2)
        QgsProject.instance().removeAllMapLayers()
        self.assertEqual(QgsProject.instance().count(), 0)
        self.assertEqual(QgsProject.instance().mapLayersByName('test'), [])
        self.assertEqual(QgsProject.instance().mapLayersByName('test2'), [])

    def test_addRemoveLayersSignals(self):
        """ test that signals are correctly emitted when removing map layers"""
        QgsProject.instance().removeAllMapLayers()

        layers_will_be_removed_spy = QSignalSpy(QgsProject.instance().layersWillBeRemoved)
        layer_will_be_removed_spy_str = QSignalSpy(QgsProject.instance().layerWillBeRemoved[str])
        layer_will_be_removed_spy_layer = QSignalSpy(QgsProject.instance().layerWillBeRemoved[QgsMapLayer])
        layers_removed_spy = QSignalSpy(QgsProject.instance().layersRemoved)
        layer_removed_spy = QSignalSpy(QgsProject.instance().layerRemoved)
        remove_all_spy = QSignalSpy(QgsProject.instance().removeAll)

        l1 = createLayer('l1')
        l2 = createLayer('l2')
        l3 = createLayer('l3')
        l4 = createLayer('l4')
        QgsProject.instance().addMapLayers([l1, l2, l3, l4])

        # remove 1 layer
        QgsProject.instance().removeMapLayer(l1)
        # can't seem to actually test the data which was emitted, so best we can do is test
        # the signal count
        self.assertEqual(len(layers_will_be_removed_spy), 1)
        self.assertEqual(len(layer_will_be_removed_spy_str), 1)
        self.assertEqual(len(layer_will_be_removed_spy_layer), 1)
        self.assertEqual(len(layers_removed_spy), 1)
        self.assertEqual(len(layer_removed_spy), 1)
        self.assertEqual(len(remove_all_spy), 0)
        self.assertEqual(QgsProject.instance().count(), 3)

        # remove 2 layers at once
        QgsProject.instance().removeMapLayers([l2.id(), l3.id()])
        self.assertEqual(len(layers_will_be_removed_spy), 2)
        self.assertEqual(len(layer_will_be_removed_spy_str), 3)
        self.assertEqual(len(layer_will_be_removed_spy_layer), 3)
        self.assertEqual(len(layers_removed_spy), 2)
        self.assertEqual(len(layer_removed_spy), 3)
        self.assertEqual(len(remove_all_spy), 0)
        self.assertEqual(QgsProject.instance().count(), 1)

        # remove all
        QgsProject.instance().removeAllMapLayers()
        self.assertEqual(len(layers_will_be_removed_spy), 3)
        self.assertEqual(len(layer_will_be_removed_spy_str), 4)
        self.assertEqual(len(layer_will_be_removed_spy_layer), 4)
        self.assertEqual(len(layers_removed_spy), 3)
        self.assertEqual(len(layer_removed_spy), 4)
        self.assertEqual(len(remove_all_spy), 1)

        #remove some layers which aren't in the registry
        QgsProject.instance().removeMapLayers(['asdasd'])
        self.assertEqual(len(layers_will_be_removed_spy), 3)
        self.assertEqual(len(layer_will_be_removed_spy_str), 4)
        self.assertEqual(len(layer_will_be_removed_spy_layer), 4)
        self.assertEqual(len(layers_removed_spy), 3)
        self.assertEqual(len(layer_removed_spy), 4)
        self.assertEqual(len(remove_all_spy), 1)

        l5 = createLayer('test5')
        QgsProject.instance().removeMapLayer(l5)
        self.assertEqual(len(layers_will_be_removed_spy), 3)
        self.assertEqual(len(layer_will_be_removed_spy_str), 4)
        self.assertEqual(len(layer_will_be_removed_spy_layer), 4)
        self.assertEqual(len(layers_removed_spy), 3)
        self.assertEqual(len(layer_removed_spy), 4)
        self.assertEqual(len(remove_all_spy), 1)

    def test_RemoveLayerShouldNotSegFault(self):
        QgsProject.instance().removeAllMapLayers()

        reg = QgsProject.instance()
        # Should not segfault
        reg.removeMapLayers(['not_exists'])
        reg.removeMapLayer('not_exists2')

        # check also that the removal of an unexistent layer does not insert a null layer
        for k, layer in list(reg.mapLayers().items()):
            assert(layer is not None)

    def testTakeLayer(self):
        # test taking ownership of a layer from the project
        l1 = createLayer('l1')
        l2 = createLayer('l2')
        p = QgsProject()

        # add one layer to project
        p.addMapLayer(l1)
        self.assertEqual(p.mapLayers(), {l1.id(): l1})
        self.assertEqual(l1.parent().parent(), p)

        # try taking some layers which don't exist in project
        self.assertFalse(p.takeMapLayer(None))
        self.assertFalse(p.takeMapLayer(l2))
        # but l2 should still exist..
        self.assertTrue(l2.isValid())

        # take layer from project
        self.assertEqual(p.takeMapLayer(l1), l1)
        self.assertFalse(p.mapLayers()) # no layers left
        # but l1 should still exist
        self.assertTrue(l1.isValid())
        # layer should have no parent now
        self.assertFalse(l1.parent())

        # destroy project
        p = None
        self.assertTrue(l1.isValid())

    def test_zip_new_project(self):
        tmpDir = QTemporaryDir()
        tmpFile = "{}/project.qgz".format(tmpDir.path())

        # zip with existing file
        open(tmpFile, 'a').close()

        project = QgsProject()
        self.assertTrue(project.write(tmpFile))

        # zip with non existing file
        os.remove(tmpFile)

        project = QgsProject()
        self.assertTrue(project.write(tmpFile))
        self.assertTrue(os.path.isfile(tmpFile))

    def test_zip_invalid_path(self):
        project = QgsProject()
        self.assertFalse(project.write())
        self.assertFalse(project.write(""))
        self.assertFalse(project.write("/fake/test.zip"))

    def test_zip_filename(self):
        tmpDir = QTemporaryDir()
        tmpFile = "{}/project.qgz".format(tmpDir.path())

        project = QgsProject()
        self.assertFalse(project.write())

        project.setFileName(tmpFile)
        self.assertTrue(project.write())
        self.assertTrue(os.path.isfile(tmpFile))

    def test_unzip_invalid_path(self):
        project = QgsProject()
        self.assertFalse(project.read())
        self.assertFalse(project.read(""))
        self.assertFalse(project.read("/fake/test.zip"))

    def test_zip_unzip(self):
        tmpDir = QTemporaryDir()
        tmpFile = "{}/project.qgz".format(tmpDir.path())

        project = QgsProject()

        l0 = QgsVectorLayer(os.path.join(TEST_DATA_DIR, "points.shp"), "points", "ogr")
        l1 = QgsVectorLayer(os.path.join(TEST_DATA_DIR, "lines.shp"), "lines", "ogr")
        project.addMapLayers([l0, l1])

        self.assertTrue(project.write(tmpFile))

        project2 = QgsProject()
        self.assertFalse(project2.isZipped())
        self.assertTrue(project2.fileName() == "")
        self.assertTrue(project2.read(tmpFile))
        self.assertTrue(project2.isZipped())
        self.assertTrue(project2.fileName() == tmpFile)
        layers = project2.mapLayers()

        self.assertEqual(len(layers.keys()), 2)
        self.assertTrue(layers[l0.id()].isValid(), True)
        self.assertTrue(layers[l1.id()].isValid(), True)

        project2.clear()
        self.assertFalse(project2.isZipped())

    def testUpgradeOtfFrom2x(self):
        """
        Test that upgrading a 2.x project correctly brings across project CRS and OTF transformation settings
        """
        prj = QgsProject.instance()
        prj.read(os.path.join(TEST_DATA_DIR, 'projects', 'test_memory_layer_proj.qgs'))
        self.assertTrue(prj.crs().isValid())
        self.assertEqual(prj.crs().authid(), 'EPSG:2056')

    def testRelativePaths(self):
        """
        Test whether paths to layer sources are stored as relative to the project path
        """
        tmpDir = QTemporaryDir()
        tmpFile = "{}/project.qgs".format(tmpDir.path())
        copyfile(os.path.join(TEST_DATA_DIR, "points.shp"), os.path.join(tmpDir.path(), "points.shp"))
        copyfile(os.path.join(TEST_DATA_DIR, "points.dbf"), os.path.join(tmpDir.path(), "points.dbf"))
        copyfile(os.path.join(TEST_DATA_DIR, "points.shx"), os.path.join(tmpDir.path(), "points.shx"))
        copyfile(os.path.join(TEST_DATA_DIR, "lines.shp"), os.path.join(tmpDir.path(), "lines.shp"))
        copyfile(os.path.join(TEST_DATA_DIR, "lines.dbf"), os.path.join(tmpDir.path(), "lines.dbf"))
        copyfile(os.path.join(TEST_DATA_DIR, "lines.shx"), os.path.join(tmpDir.path(), "lines.shx"))
        copyfile(os.path.join(TEST_DATA_DIR, "landsat_4326.tif"), os.path.join(tmpDir.path(), "landsat_4326.tif"))

        project = QgsProject()

        l0 = QgsVectorLayer(os.path.join(tmpDir.path(), "points.shp"), "points", "ogr")
        l1 = QgsVectorLayer(os.path.join(tmpDir.path(), "lines.shp"), "lines", "ogr")
        l2 = QgsRasterLayer(os.path.join(tmpDir.path(), "landsat_4326.tif"), "landsat", "gdal")
        self.assertTrue(l0.isValid())
        self.assertTrue(l1.isValid())
        self.assertTrue(l2.isValid())
        self.assertTrue(project.addMapLayers([l0, l1, l2]))
        self.assertTrue(project.write(tmpFile))
        del project

        with open(tmpFile, 'r') as f:
            content = ''.join(f.readlines())
            self.assertTrue('source="./lines.shp"' in content)
            self.assertTrue('source="./points.shp"' in content)
            self.assertTrue('source="./landsat_4326.tif"' in content)

        # Re-read the project and store absolute
        project = QgsProject()
        self.assertTrue(project.read(tmpFile))
        store = project.layerStore()
        self.assertEquals(set([l.name() for l in store.mapLayers().values()]), set(['lines', 'landsat', 'points']))
        project.writeEntryBool('Paths', '/Absolute', True)
        tmpFile2 = "{}/project2.qgs".format(tmpDir.path())
        self.assertTrue(project.write(tmpFile2))

        with open(tmpFile2, 'r') as f:
            content = ''.join(f.readlines())
            self.assertTrue('source="{}/lines.shp"'.format(tmpDir.path()) in content)
            self.assertTrue('source="{}/points.shp"'.format(tmpDir.path()) in content)
            self.assertTrue('source="{}/landsat_4326.tif"'.format(tmpDir.path()) in content)

        del project


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