# Copyright (C) 2003, 2005 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 Thuban.Model.baserenderer"""

__version__ = "$Revision: 1.15 $"
# $Source: /thubanrepository/thuban/test/test_baserenderer.py,v $
# $Id: test_baserenderer.py,v 1.15 2005/05/06 14:19:03 jonathan Exp $

import os
import binascii
import unittest

from mockgeo import SimpleShapeStore
import support
support.initthuban()

from Thuban.Model.color import Transparent, Color
from Thuban.Model.data import SHAPETYPE_ARC, SHAPETYPE_POLYGON, SHAPETYPE_POINT
from Thuban.Model.map import Map
from Thuban.Model.layer import BaseLayer, Layer, RasterLayer
from Thuban.Model.table import MemoryTable, \
     FIELDTYPE_DOUBLE, FIELDTYPE_INT, FIELDTYPE_STRING
from Thuban.Model.classification import ClassGroupSingleton
import Thuban.Model.resource


from Thuban.UI.baserenderer import BaseRenderer, \
     add_renderer_extension, init_renderer_extensions

if Thuban.Model.resource.has_gdal_support():
    from gdalwarp import ProjectRasterFile

class MockDC:

    def __init__(self, size = None):
        self.calls = []
        self.size = size

    def GetSizeTuple(self):
        return self.size

    def __getattr__(self, attr):
        def method(*args):
            self.calls.append((attr,) + args)
        return method

class P:

    """A simple point"""

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

    def __ne__(self, other):
        return self.x != other.x and self.y != other.y

    def __repr__(self):
        return "P(%r, %r)" % (self.x, self.y)


class SimpleRenderer(BaseRenderer):

    TRANSPARENT_PEN = ("pen", Transparent)
    TRANSPARENT_BRUSH = ("brush", Transparent)

    def make_point(self, x, y):
        return P(x, y)

    def tools_for_property(self, prop):
        fill = prop.GetFill()
        brush = ("brush", fill)

        stroke = prop.GetLineColor()
        stroke_width = prop.GetLineWidth()
        if stroke is Transparent:
            pen = ("pen", Transparent)
        else:
            pen = ("pen", stroke, stroke_width)

        return pen, brush

    def label_font(self):
        return "label font"

    def draw_raster_data(self, x, y, data, format='BMP', opacity=1.0):
        self.raster_data = data
        self.raster_format = format

    def projected_raster_layer(self, layer, srcProj, dstProj, extents,
                               resolution, dimensions, options):

        if not Thuban.Model.resource.has_gdal_support():
            raise support.SkipTest("No gdal support")

        #print srcProj, dstProj,extents, resolution, dimensions, options

        return ProjectRasterFile(layer.GetImageFilename(), 
                                 srcProj, dstProj,
                                 extents, resolution, dimensions, 
                                 options)
 
class MockProjection:

    """Objects that look like projections but simply apply non-uniform scalings
    """

    def __init__(self, xscale, yscale):
        self.xscale = float(xscale)
        self.yscale = float(yscale)

    def Forward(self, x, y):
        return (x * self.xscale, y * self.yscale)

    def Inverse(self, x, y):
        return (x / self.xscale, y / self.yscale)


class TestBaseRenderer(unittest.TestCase):

    def setUp(self):
        """Set self.to_destroy to an empty list

        Tests should put all objects whose Destroy should be called at
        the end into this list so that they're destroyed in tearDown.
        """
        self.to_destroy = []

    def tearDown(self):
        for obj in self.to_destroy:
            obj.Destroy()

    def test_arc_no_projection(self):
        """Test BaseRenderer with arc layer and no projections"""
        table = MemoryTable([("type", FIELDTYPE_STRING),
                             ("value", FIELDTYPE_DOUBLE),
                             ("code", FIELDTYPE_INT)],
                            [("UNKNOWN", 0.0, 0)])
        shapes = [[[(0, 0), (10, 10)]]]
        store = SimpleShapeStore(SHAPETYPE_ARC, shapes, table)

        map = Map("TestBaseRenderer")
        self.to_destroy.append(map)
        layer = Layer("arc layer", store)
        map.AddLayer(layer)

        dc = MockDC()
        renderer = SimpleRenderer(dc, map, 2, (10, 10))

        renderer.render_map()

        self.assertEquals(dc.calls,
                          [('BeginDrawing',),
                           ('SetBrush', ('brush', Transparent)),
                           ('SetPen', ('pen', Color(0, 0, 0), 1)),
                           ('DrawLines', [P(10, 10), P(30, -10)]),
                           ('SetFont', "label font"),
                           ('EndDrawing',)])

    def test_polygon_no_projection(self):
        """Test BaseRenderer with polygon layer and no projections"""
        table = MemoryTable([("type", FIELDTYPE_STRING),
                             ("value", FIELDTYPE_DOUBLE),
                             ("code", FIELDTYPE_INT)],
                            [("UNKNOWN", 0.0, 0)])
        shapes = [[[(0, 0), (10, 10), (10, 0), (0, 0)]]]
        store = SimpleShapeStore(SHAPETYPE_POLYGON, shapes, table)

        map = Map("TestBaseRenderer")
        layer = Layer("polygon layer", store)
        prop = layer.GetClassification().GetDefaultGroup().GetProperties()
        prop.SetFill(Color(1, 1, 1))

        map.AddLayer(layer)
        self.to_destroy.append(map)

        dc = MockDC()
        renderer = SimpleRenderer(dc, map, 2, (10, 10))

        renderer.render_map()

        self.assertEquals(dc.calls,
                          [('BeginDrawing',),
                           ('SetBrush', ('brush', Color(1, 1, 1))),
                           ('SetPen', ('pen', Transparent)),
                           ('DrawPolygon', [P(10, 10), P(30, -10), P(30, 10),
                                            P(10, 10)]),
                           ('SetBrush', ('brush', Transparent)),
                           ('SetPen', ('pen', Color(0, 0, 0), 1)),
                           ('DrawLines', [P(10, 10), P(30, -10), P(30, 10),
                                          P(10, 10)]),
                           ('SetFont', "label font"),
                           ('EndDrawing',)])

    def test_complex_polygon(self):
        """Test BaseRenderer with complex polygon and no projections"""
        # A square inside a sqare. This has to be drawn with at least a
        # draw polygon call and two draw lines calls.
        shapes = [[[(0, 0), (0, 10), (10, 10), (10, 0), (0, 0)],
                   [(2, 2), (8, 2), (8, 8), (2, 8), (2, 2)]]]

        table = MemoryTable([("type", FIELDTYPE_STRING),
                             ("value", FIELDTYPE_DOUBLE),
                             ("code", FIELDTYPE_INT)],
                            [("UNKNOWN", 0.0, 0)])
        store = SimpleShapeStore(SHAPETYPE_POLYGON, shapes, table)

        map = Map("TestBaseRenderer")
        layer = Layer("polygon layer", store)
        prop = layer.GetClassification().GetDefaultGroup().GetProperties()
        prop.SetFill(Color(1, 1, 1))

        map.AddLayer(layer)
        self.to_destroy.append(map)

        dc = MockDC()
        renderer = SimpleRenderer(dc, map, 2, (10, 10))

        renderer.render_map()

        self.assertEquals(dc.calls,
                          [('BeginDrawing',),
                           ('SetBrush', ('brush', Color(1, 1, 1))),
                           ('SetPen', ('pen', Transparent)),
                           ('DrawPolygon',
                            [P(10, 10), P(10, -10), P(30, -10), P(30, 10),
                             P(10, 10),
                             P(14, 6), P(26, 6), P(26, -6), P(14, -6),
                             P(14, 6),
                             P(10, 10)]),
                           ('SetBrush', ('brush', Transparent)),
                           ('SetPen', ('pen', Color(0, 0, 0), 1)),
                           ('DrawLines', [P(10, 10), P(10, -10), P(30, -10),
                                          P(30, 10), P(10, 10)]),
                           ('DrawLines', [P(14, 6), P(26, 6), P(26, -6),
                                          P(14, -6), P(14, 6)]),
                           ('SetFont', "label font"),
                           ('EndDrawing',)])

    def test_point_no_projection(self):
        """Test BaseRenderer with point layer and no projections"""
        table = MemoryTable([("type", FIELDTYPE_STRING),
                             ("value", FIELDTYPE_DOUBLE),
                             ("code", FIELDTYPE_INT)],
                            [("UNKNOWN", 0.0, 0),
                             ("UNKNOWN", 0.0, 1)])
        shapes = [[[(0, 0)]], [[(10, 10)]]]
        store = SimpleShapeStore(SHAPETYPE_POINT, shapes, table)

        map = Map("TestBaseRenderer")
        layer = Layer("point layer", store)
        map.AddLayer(layer)
        self.to_destroy.append(map)

        dc = MockDC()
        renderer = SimpleRenderer(dc, map, 2, (10, 10))

        renderer.render_map()

        self.assertEquals(dc.calls,
                          [('BeginDrawing',),
                           ('SetBrush', ('brush', Transparent)),
                           ('SetPen', ('pen', Color(0, 0, 0), 1)),
                           ('DrawEllipse', 5, 5, 10, 10),
                           ('SetBrush', ('brush', Transparent)),
                           ('SetPen', ('pen', Color(0, 0, 0), 1)),
                           ('DrawEllipse', 25, -15, 10, 10),
                           ('SetFont', "label font"),
                           ('EndDrawing',)])

    def test_projected_raster_layer(self):
        if not Thuban.Model.resource.has_gdal_support():
            raise support.SkipTest("No gdal support")

        layer = RasterLayer("raster layer",
                            os.path.join("..", "Data", "iceland",
                                         "island.tif"))

        dc = MockDC(size = (20, 20))
        renderer = SimpleRenderer(dc, map, 34, (800, 2250))

        # The reference data as a base64 coded RAW image
        raw_data = binascii.a2b_base64(
            'UmbmUmbmUmbmUmbmUmbmAtYCJooCAtICAq4CJooCArICAuICArICAuYCAs4COn4CO'
            'n4CAq4CAuICFpICUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmAuYCAqICAqoCAqoCFp'
            'ICJooCIo4CCpoCQnoGOn4CDpYCOn4CUmbmUmbmNo6aEpYCLoYCAqICGpICFpICUmb'
            'mAt4CUmbmNo6aAtICArYCAqoCKoYCMoICTnYKOn4CFpICUmbmUmbmUmbmUmbmAp4C'
            'NoICArYCAr4CCpoCAqYCCpoCEpYCHo4CFpICHo4CGpICFpICKoYCTnYKMoICAp4CU'
            'mbmUmbmUmbmUmbmUmbmUmbmAtYCAroCArYCCpoCAtYCAroCAtICAsYCUmbmAt4CAq'
            'YCAroCMoICAs4CAs4CAtYCAt4CAqYCUmbmUmbmUmbmUmbmAtoCAtYCAq4CAtoCBp4'
            'CAroCAqoCAq4CAr4CDpYCGpICAt4CAsICDpYCArICCpoCHo4CAs4CAuICUmbmUmbm'
            'UmbmUmbmUmbmUmbmAuICAqICFpYCAq4CDpoCAqYCFpICAqYCUmbmNo6aAsYCCpoCD'
            'pYCAqICAtoCUmbmAt4CAqoCCpoCAroCHo4CAsYCAq4CAsICAs4CAp4CUmbmAtYCAq'
            'YCIooCHo4CAsICAr4CAqICEpYCAs4CAqICArICDpYCEpYCEpYCAr4CUmbmEpYCAs4'
            'CAtICAs4CAqYCUmbmAtoCAp4CCpoCDpYCAq4CArICAqoCAqYCAqYCAtYCAtoCDpYC'
            'At4CUmbmUmbmUmbmUmbmAt4CAsoCAsoCAp4CAp4CCpoCAsoCAt4CNo6aUmbmUmbmU'
            'mbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmAt4CAtYCCpoCAqICAroCAr4CUmbmUm'
            'bmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmb'
            'mUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbm'
            'UmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmU'
            'mbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUm'
            'bmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmb'
            'mUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbm'
            'UmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmU'
            'mbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUm'
            'bmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmb'
            'mUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbm'
            'UmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbm\n')

        raw_mask = binascii.a2b_base64(
            '//8P//8P//8P//8P//8P//8P//8P//8P//8P//8P//8P//8P//8P//8P//8P/'
            '/8P//8P//8P//8P//8P\n')

        raw_mask_inverted = binascii.a2b_base64(
            'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
            'AAAAAAAAAAAAAAAAAAA\n')

        raw_alpha = binascii.a2b_base64(
            '/////////////////////////////////////////////////////////////'
            '/////////////////////////////////////////////////////////////'
            '/////////////////////////////////////////////////////////////'
            '/////////////////////////////////////////////////////////////'
            '/////////////////////////////////////////////////////////////'
            '/////////////////////////////////////////////////////////////'
            '/////////////////////////////////////////////////////////////'
            '/////////////////////////////////////////////////////////////'
            '/////////////////////////////////////////////w==\n')

        for opts, data in [[1,   (raw_data, raw_mask,          None)],
                           [1|4, (raw_data, raw_mask_inverted, None)],
                           [2,   (raw_data, None,              raw_alpha)]]:

            img_data = renderer.projected_raster_layer(layer, "", "", 
                            (-24, 65, -23, 66), [0, 0], (20, 20), opts)
            self.assertEquals(img_data, data)

    def test_raster_no_projection(self):
        """Test BaseRenderer with raster layer and no projections

        This test is very simple minded and perhaps can easily fail due
        to round-off errors. It simply compares the complete BMP file
        returned by gdalwarp.ProjectRasterFile to a BMP file data.
        """
        if not Thuban.Model.resource.has_gdal_support():
            raise support.SkipTest("No gdal support")

        map = Map("TestBaseRenderer")

        layer = RasterLayer("raster layer",
                            os.path.join("..", "Data", "iceland",
                                         "island.tif"))
        layer.SetMaskType(layer.MASK_NONE)

        map.AddLayer(layer)
        self.to_destroy.append(map)

        dc = MockDC(size = (20, 20))
        renderer = SimpleRenderer(dc, map, 34, (800, 2250))

        renderer.render_map()

        # The following commented out code block can be used to generate
        # the base64 coded reference image data
        #hexed = binascii.b2a_base64(renderer.raster_data[2][0])
        #while hexed:
            #print repr(hexed[:65])
            #hexed = hexed[65:]

        # The reference data as a base64 coded RAW image
        raw_data = binascii.a2b_base64(
            'UmbmUmbmUmbmUmbmUmbmAtYCJooCAtICAq4CJooCArICAuICArICAuYCAs4COn4CO'
            'n4CAq4CAuICFpICUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmAuYCAqICAqoCAqoCFp'
            'ICJooCIo4CCpoCQnoGOn4CDpYCOn4CUmbmUmbmNo6aEpYCLoYCAqICGpICFpICUmb'
            'mAt4CUmbmNo6aAtICArYCAqoCKoYCMoICTnYKOn4CFpICUmbmUmbmUmbmUmbmAp4C'
            'NoICArYCAr4CCpoCAqYCCpoCEpYCHo4CFpICHo4CGpICFpICKoYCTnYKMoICAp4CU'
            'mbmUmbmUmbmUmbmUmbmUmbmAtYCAroCArYCCpoCAtYCAroCAtICAsYCUmbmAt4CAq'
            'YCAroCMoICAs4CAs4CAtYCAt4CAqYCUmbmUmbmUmbmUmbmAtoCAtYCAq4CAtoCBp4'
            'CAroCAqoCAq4CAr4CDpYCGpICAt4CAsICDpYCArICCpoCHo4CAs4CAuICUmbmUmbm'
            'UmbmUmbmUmbmUmbmAuICAqICFpYCAq4CDpoCAqYCFpICAqYCUmbmNo6aAsYCCpoCD'
            'pYCAqICAtoCUmbmAt4CAqoCCpoCAroCHo4CAsYCAq4CAsICAs4CAp4CUmbmAtYCAq'
            'YCIooCHo4CAsICAr4CAqICEpYCAs4CAqICArICDpYCEpYCEpYCAr4CUmbmEpYCAs4'
            'CAtICAs4CAqYCUmbmAtoCAp4CCpoCDpYCAq4CArICAqoCAqYCAqYCAtYCAtoCDpYC'
            'At4CUmbmUmbmUmbmUmbmAt4CAsoCAsoCAp4CAp4CCpoCAsoCAt4CNo6aUmbmUmbmU'
            'mbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmAt4CAtYCCpoCAqICAroCAr4CUmbmUm'
            'bmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmb'
            'mUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbm'
            'UmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmU'
            'mbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUm'
            'bmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmb'
            'mUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbm'
            'UmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmU'
            'mbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUm'
            'bmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmb'
            'mUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbm'
            'UmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbmUmbm\n')

        self.assertEquals(renderer.raster_data, 
                          (20,20,(raw_data, None, None)))

        self.assertEquals(renderer.raster_format, "RAW")

        self.assertEquals(dc.calls,
                          [('BeginDrawing',),
                           ('SetFont', "label font"),
                           ('EndDrawing',)])

    def test_point_map_projection(self):
        """Test BaseRenderer with point layer and map projection"""
        table = MemoryTable([("type", FIELDTYPE_STRING),
                             ("value", FIELDTYPE_DOUBLE),
                             ("code", FIELDTYPE_INT)],
                            [("UNKNOWN", 0.0, 0)])
        shapes = [[[(10, 10)]]]
        store = SimpleShapeStore(SHAPETYPE_POINT, shapes, table)

        map = Map("TestBaseRenderer")
        map.SetProjection(MockProjection(-3, 3))
        layer = Layer("point layer", store)
        map.AddLayer(layer)
        self.to_destroy.append(map)

        dc = MockDC()
        renderer = SimpleRenderer(dc, map, 2, (10, 10))

        renderer.render_map()

        self.assertEquals(dc.calls,
                          [('BeginDrawing',),
                           ('SetBrush', ('brush', Transparent)),
                           ('SetPen', ('pen', Color(0, 0, 0), 1)),
                           ('DrawEllipse', -55, -55, 10, 10),
                           ('SetFont', "label font"),
                           ('EndDrawing',)])

    def test_point_layer_projection(self):
        """Test BaseRenderer with point layer and layer projection"""
        table = MemoryTable([("type", FIELDTYPE_STRING),
                             ("value", FIELDTYPE_DOUBLE),
                             ("code", FIELDTYPE_INT)],
                            [("UNKNOWN", 0.0, 0)])
        shapes = [[[(9, 9)]]]
        store = SimpleShapeStore(SHAPETYPE_POINT, shapes, table)

        map = Map("TestBaseRenderer")
        layer = Layer("point layer", store)
        layer.SetProjection(MockProjection(3, -3))
        map.AddLayer(layer)
        self.to_destroy.append(map)

        dc = MockDC()
        renderer = SimpleRenderer(dc, map, 2, (10, 10))

        renderer.render_map()

        self.assertEquals(dc.calls,
                          [('BeginDrawing',),
                           ('SetBrush', ('brush', Transparent)),
                           ('SetPen', ('pen', Color(0, 0, 0), 1)),
                           ('DrawEllipse', 11, 11, 10, 10),
                           ('SetFont', "label font"),
                           ('EndDrawing',)])

    def test_point_layer_and_map_projection(self):
        """Test BaseRenderer with point layer and layer and map projection"""
        table = MemoryTable([("type", FIELDTYPE_STRING),
                             ("value", FIELDTYPE_DOUBLE),
                             ("code", FIELDTYPE_INT)],
                            [("UNKNOWN", 0.0, 0)])
        shapes = [[[(9, 9)]]]
        store = SimpleShapeStore(SHAPETYPE_POINT, shapes, table)

        map = Map("TestBaseRenderer")
        map.SetProjection(MockProjection(-3, 3))
        layer = Layer("point layer", store)
        layer.SetProjection(MockProjection(3, -3))
        map.AddLayer(layer)
        self.to_destroy.append(map)

        dc = MockDC()
        renderer = SimpleRenderer(dc, map, 2, (10, 10))

        renderer.render_map()

        self.assertEquals(dc.calls,
                          [('BeginDrawing',),
                           ('SetBrush', ('brush', Transparent)),
                           ('SetPen', ('pen', Color(0, 0, 0), 1)),
                           ('DrawEllipse', -13, 23, 10, 10),
                           ('SetFont', "label font"),
                           ('EndDrawing',)])


    def test_point_with_classification(self):
        """Test BaseRenderer with point layer and classification"""
        table = MemoryTable([("type", FIELDTYPE_STRING),
                             ("value", FIELDTYPE_DOUBLE),
                             ("code", FIELDTYPE_INT)],
                            [("UNKNOWN", 0.0, 0),
                             ("UNKNOWN", 0.0, 1)])
        shapes = [[[(0, 0)]], [[(10, 10)]]]
        store = SimpleShapeStore(SHAPETYPE_POINT, shapes, table)

        map = Map("TestBaseRenderer")
        layer = Layer("point layer", store)
        group = ClassGroupSingleton(1)
        group.GetProperties().SetFill(Color(0, 0, 1))
        layer.GetClassification().AppendGroup(group)
        layer.SetClassificationColumn("code")

        map.AddLayer(layer)
        self.to_destroy.append(map)

        dc = MockDC()
        renderer = SimpleRenderer(dc, map, 2, (10, 10))

        renderer.render_map()

        self.assertEquals(dc.calls,
                          [('BeginDrawing',),
                           ('SetBrush', ('brush', Transparent)),
                           ('SetPen', ('pen', Color(0, 0, 0), 1)),
                           ('DrawEllipse', 5, 5, 10, 10),
                           ('SetBrush', ('brush', Color(0, 0, 1))),
                           ('SetPen', ('pen', Color(0, 0, 0), 1)),
                           ('DrawEllipse', 25, -15, 10, 10),
                           ('SetFont', "label font"),
                           ('EndDrawing',)])


    def test_renderer_extension(self):
        """Test renderer with a renderer extension"""
        class MyLayer(BaseLayer):
            pass

        calls = []
        def my_renderer(renderer, layer):
            calls.append((renderer, layer))
            return ()

        add_renderer_extension(MyLayer, my_renderer)

        try:
            map = Map("test_renderer_extension")
            layer = MyLayer("my layer")
            map.AddLayer(layer)
            self.to_destroy.append(map)

            dc = MockDC()
            renderer = SimpleRenderer(dc, map, 2, (10, 10))
            renderer.render_map()
        finally:
            init_renderer_extensions()

        self.assertEquals(calls, [(renderer, layer)])


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