/[thuban]/branches/WIP-pyshapelib-bramz/test/test_load.py
ViewVC logotype

Diff of /branches/WIP-pyshapelib-bramz/test/test_load.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

trunk/thuban/test/test_load.py revision 409 by jonathan, Wed Feb 19 16:50:39 2003 UTC branches/WIP-pyshapelib-bramz/test/test_load.py revision 2734 by bramz, Thu Mar 1 12:42:59 2007 UTC
# Line 1  Line 1 
1  # Copyright (c) 2002 by Intevation GmbH  # Copyright (c) 2002, 2003, 2004, 2005 by Intevation GmbH
2  # Authors:  # Authors:
3  # Bernhard Herzog <[email protected]>  # Bernhard Herzog <[email protected]>
4  #  #
# Line 7  Line 7 
7    
8  """  """
9  Test loading a thuban session from a file  Test loading a thuban session from a file
10    
11    The tests in this file (test_load.py) are always be the tests for the
12    current version of the thuban file format. Tests for older versions can
13    be found in the version specific test modules, e.g. test_load_0_2 for
14    files created by Thuban 0.2.
15    
16    Maintenance of the test cases:
17    
18    When during a development period the file format is changed with respect
19    to the last released version for the first time, the tests here should
20    be copied to the version specific test file.  The round-trip tests which
21    save the session again and compare the XML files should not be copied
22    over as they only make sense here to make sure th that the files checked
23    here are actually ones that may have been written by the current thuban
24    version.
25  """  """
26    
27  __version__ = "$Revision$"  __version__ = "$Revision$"
# Line 19  import unittest Line 34  import unittest
34  import support  import support
35  support.initthuban()  support.initthuban()
36    
37  from Thuban.Model.load import load_session  import postgissupport
38  from Thuban.Model.session import Session  from xmlsupport import sax_eventlist
39  from Thuban.Model.map import Map  
40  from Thuban.Model.layer import Layer  import dbflib
41  from Thuban.Model.proj import Projection  import shapelib
42  from Thuban.Model.color import Color  
43    from Thuban import internal_from_unicode
44    from Thuban.Model.save import save_session
45    from Thuban.Model.load import load_session, parse_color, LoadError, \
46         LoadCancelled
47    from Thuban.Model.color import Transparent
48    from Thuban.Model.classification import ClassGroupProperties, ClassGroupRange,\
49        ClassGroupSingleton, ClassGroupPattern, ClassGroupDefault
50    from Thuban.Model.postgisdb import ConnectionError
51    from Thuban.Model.table import DBFTable, MemoryTable, \
52         FIELDTYPE_DOUBLE, FIELDTYPE_INT, FIELDTYPE_STRING, \
53         table_to_dbf
54    from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \
55         ALIGN_LEFT, ALIGN_RIGHT, ALIGN_BASELINE
56    
57  def filenames_equal(name1, name2):  def filenames_equal(name1, name2):
58      """Return true if the filenames name1 and name2 are equal.      """Return true if the filenames name1 and name2 are equal.
# Line 38  def filenames_equal(name1, name2): Line 66  def filenames_equal(name1, name2):
66      return os.path.normpath(name1) == os.path.normpath(name2)      return os.path.normpath(name1) == os.path.normpath(name2)
67    
68    
 contents_single_map = '''\  
 <?xml version="1.0" encoding="UTF-8"?>  
 <!DOCTYPE session SYSTEM "thuban.dtd">  
 <session title="single map&amp;layer">  
         <map title="Test Map">  
                 <projection>  
                         <parameter value="zone=26"/>  
                         <parameter value="proj=utm"/>  
                         <parameter value="ellps=clrk66"/>  
                 </projection>  
                 <layer title="My Layer" stroke_width="1" fill="None"  
                     filename="../../Data/iceland/political.shp"  
                     stroke="#000000"/>  
         </map>  
 </session>  
 '''  
69    
70    class LoadSessionTest(support.FileLoadTestCase):
71    
72  class LoadSessionTest(unittest.TestCase, support.FileTestMixin):      """Base class for .thuban file loading tests
73    
74        Basically the same as the FileLoadTestCase, except that all tests
75        use the '.thuban' extension by default and that setUp and tearDown
76        handle sessions.
77        """
78    
79        file_extension = ".thuban"
80    
81      def setUp(self):      def setUp(self):
82          """Create the test files"""          """Create the test files"""
83          file = open(self.temp_file_name("load_singlelayer.thuban"), "w")          support.FileLoadTestCase.setUp(self)
         file.write(contents_single_map)  
         file.close()  
84          self.session = None          self.session = None
85    
86      def tearDown(self):      def tearDown(self):
87          if self.session is not None:          if self.session is not None:
88              self.session.Destroy()              self.session.Destroy()
89            self.session = None
90    
91    
92        dtd = "http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
93        thubanids = [((dtd, n), (None, "id")) for n in
94                     ["fileshapesource", "filetable", "jointable",
95                      "derivedshapesource"]]
96        thubanidrefs = [((dtd, n), (None, m)) for n, m in
97                        [("layer", "shapestore"),
98                         ("jointable", "left"),
99                         ("jointable", "right"),
100                         ("derivedshapesource", "table"),
101                         ("derivedshapesource", "shapesource")]]
102    
103        # The filenames in the tests should be understandable on all
104        # currently supported platforms so filenames is an empty list
105        filenames = []
106    
107        del n, m, dtd
108    
109        def check_format(self):
110            """Check whether the file we loaded from matches the one that
111            would be written. Call this from each test case after loading
112            the session
113            """
114            filename = self.temp_file_name(self.id() + ".roundtrip.thuban")
115            save_session(self.session, filename)
116            el1 = sax_eventlist(filename = filename, ids = self.thubanids,
117                                idrefs = self.thubanidrefs,
118                                filenames = self.filenames)
119            el2 = sax_eventlist(filename = self.filename(), ids = self.thubanids,
120                                idrefs = self.thubanidrefs,
121                                filenames = self.filenames)
122            if 0:
123                for a, b in zip(el1, el2):
124                    print a != b and "***************" or ""
125                    print a
126                    print b
127    
128            self.assertEquals(el1, el2,
129                              "loaded file not equivalent to the saved file")
130    
131    
132    class ClassificationTest(LoadSessionTest):
133    
134        """
135        Base class for tests that do some detailed checking of classifications
136        """
137    
138        def TestLayers(self, layers, expected):
139            TITLE = 0
140            NUM_GROUPS = 1
141            CLASSES = 2
142            GROUP_TYPE = 0
143            GROUP_DATA = 1
144            GROUP_LABEL = 2
145            GROUP_PROPS = 3
146    
147            eq = self.assertEquals
148    
149            eq(len(layers), len(expected))
150    
151            for layer, data in zip(layers, expected):
152                eq(layer.Title(), data[TITLE])
153    
154      def testSingleLayer(self):              clazz = layer.GetClassification()
155                eq(clazz.GetNumGroups(), data[NUM_GROUPS])
156                eq(clazz.GetNumGroups() + 1, len(data[CLASSES]))
157    
158                i = 0
159                for group in clazz:
160                    props = ClassGroupProperties()
161                    props.SetLineColor(
162                        parse_color(data[CLASSES][i][GROUP_PROPS][0]))
163                    props.SetLineWidth(data[CLASSES][i][GROUP_PROPS][1])
164                    props.SetFill(
165                        parse_color(data[CLASSES][i][GROUP_PROPS][2]))
166                    if len(data[CLASSES][i][GROUP_PROPS]) > 3:
167                        props.SetSize(data[CLASSES][i][GROUP_PROPS][3])
168    
169                    if data[CLASSES][i][GROUP_TYPE] == "default":
170                        g = ClassGroupDefault(props, data[CLASSES][i][GROUP_LABEL])
171                    elif data[CLASSES][i][GROUP_TYPE] == "range":
172                        g = ClassGroupRange((data[CLASSES][i][GROUP_DATA][0],
173                                             data[CLASSES][i][GROUP_DATA][1]),
174                                            props, data[CLASSES][i][GROUP_LABEL])
175                    elif data[CLASSES][i][GROUP_TYPE] == "single":
176                        g = ClassGroupSingleton(data[CLASSES][i][GROUP_DATA],
177                                              props, data[CLASSES][i][GROUP_LABEL])
178                    elif data[CLASSES][i][GROUP_TYPE] == "pattern":
179                        g = ClassGroupPattern(data[CLASSES][i][GROUP_DATA],
180                                              props, data[CLASSES][i][GROUP_LABEL])
181    
182                    eq(group, g)
183    
184                    i += 1
185    
186    
187    
188    class TestSingleLayer(LoadSessionTest):
189    
190        # Note: The use of &amp; and non-ascii characters is deliberate. We
191        # want to test whether the loading code handles that correctly.
192        file_contents = '''\
193    <?xml version="1.0" encoding="UTF-8"?>
194    <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
195    <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
196            title="Stra\xc3\x9fen &amp; Landmarken">
197        <fileshapesource filetype="shapefile" id="D1"
198            filename="../../Data/iceland/political.shp"/>
199        <map title="\xc3\x9cbersicht">
200            <projection epsg="32627" name="WGS 84 / UTM zone 27N">
201                <parameter value="datum=WGS84"/>
202                <parameter value="ellps=WGS84"/>
203                <parameter value="proj=utm"/>
204                <parameter value="units=m"/>
205                <parameter value="zone=27"/>
206            </projection>
207            <layer shapestore="D1" visible="true" title="K\xc3\xbcste">
208                <classification>
209                    <clnull label="">
210                        <cldata stroke="#000000" stroke_width="1" fill="None"/>
211                    </clnull>
212                </classification>
213            </layer>
214        </map>
215    </session>
216    '''
217    
218        def test(self):
219          """Load a session with a single map with a single layer"""          """Load a session with a single map with a single layer"""
220          eq = self.assertEquals          eq = self.assertEquals
221          session = load_session(self.temp_file_name("load_singlelayer.thuban"))          session = load_session(self.filename())
222          self.session = session          self.session = session
223    
224          # Check the title          # Check the title
225          eq(session.Title(), "single map&layer")          eq(session.Title(), internal_from_unicode(u"Stra\xdfen & Landmarken"))
226    
227          # the session has one map.          # the session has one map.
228          maps = session.Maps()          maps = session.Maps()
# Line 84  class LoadSessionTest(unittest.TestCase, Line 230  class LoadSessionTest(unittest.TestCase,
230    
231          # Check the map's attributes          # Check the map's attributes
232          map = maps[0]          map = maps[0]
233          eq(map.Title(), "Test Map")          eq(map.Title(), internal_from_unicode(u"\xdcbersicht"))
234            proj = map.GetProjection()
235            eq(proj.GetName(), "WGS 84 / UTM zone 27N")
236            eq(proj.EPSGCode(), "32627")
237            params = proj.GetAllParameters()
238            params.sort()
239            eq(params, ["datum=WGS84", "ellps=WGS84", "proj=utm", "units=m",
240                        "zone=27"])
241    
242          # the map has a single layer          # the map has a single layer
243          layers = map.Layers()          layers = map.Layers()
# Line 92  class LoadSessionTest(unittest.TestCase, Line 245  class LoadSessionTest(unittest.TestCase,
245    
246          # Check the layer attributes          # Check the layer attributes
247          layer = layers[0]          layer = layers[0]
248          eq(layer.Title(), "My Layer")          eq(layer.Title(), internal_from_unicode(u"K\xfcste"))
249          self.failUnless(filenames_equal(layer.filename,          self.failUnless(filenames_equal(layer.ShapeStore().FileName(),
250                                          os.path.join(self.temp_dir(),                                          os.path.join(self.temp_dir(),
251                                                       os.pardir, os.pardir,                                                       os.pardir, os.pardir,
252                                                       "Data", "iceland",                                                       "Data", "iceland",
253                                                       "political.shp")))                                                       "political.shp")))
254          eq(layer.GetClassification().GetDefaultFill(), Color.None)          eq(layer.GetClassification().GetDefaultFill(), Transparent)
255          eq(layer.GetClassification().GetDefaultStroke().hex(), "#000000")          eq(layer.GetClassification().GetDefaultLineColor().hex(), "#000000")
256            eq(layer.Visible(), True)
257    
258            self.check_format()
259    
260            self.session.Destroy()
261            self.session = None
262    
263        def test_leak(self):
264            """Test load_session for resource leaks
265    
266            The load_session function had a resource leak in that it created
267            cyclic references. The objects would have been eventually
268            collected by the garbage collector but too late. One symptom is
269            that when layers are removed so that the last normal reference
270            owned indirectly by the session to a shape store goes away, the
271            shape store is not actually removed from the session even though
272            the session only keeps weak references because there are still
273            references owned by the cyclic garbage.
274            """
275            session = load_session(self.filename())
276            self.session = session
277    
278            # sanity check
279            self.assertEquals(len(session.ShapeStores()), 1)
280    
281            # remove the map. The shapestore should go away too
282            session.RemoveMap(session.Maps()[0])
283            self.assertEquals(len(session.ShapeStores()), 0)
284    
285    
286    class TestNonAsciiColumnName(LoadSessionTest):
287    
288        file_contents = '''\
289    <?xml version="1.0" encoding="UTF-8"?>
290    <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
291    <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
292            title="Non ASCII column name test">
293        <fileshapesource filetype="shapefile" id="D1"
294            filename="TestNonAsciiColumnName.shp"/>
295        <map title="map">
296            <projection name="Some Projection">
297                <parameter value="datum=WGS84"/>
298                <parameter value="ellps=WGS84"/>
299                <parameter value="proj=utm"/>
300                <parameter value="units=m"/>
301                <parameter value="zone=27"/>
302            </projection>
303            <layer shapestore="D1" visible="true" title="layer">
304                <classification field="Fl\xc3\xa4che" field_type="double">
305                    <clnull label="">
306                        <cldata stroke="#000000" stroke_width="1" fill="None"/>
307                    </clnull>
308                </classification>
309            </layer>
310        </map>
311    </session>
312    '''
313    
314        def test(self):
315            """Load a session with a single map with a single layer"""
316    
317            # Create a shapefile and a dbffile with a non-ascii column name
318            dbffile = self.temp_file_name("TestNonAsciiColumnName.dbf")
319            shpfile = self.temp_file_name("TestNonAsciiColumnName.shp")
320            dbf = dbflib.create(dbffile)
321            dbf.add_field('Fl\xe4che', dbflib.FTDouble, 10, 5)
322            dbf.write_record(0, (0.0,))
323            dbf.close()
324            shp = shapelib.create(shpfile, shapelib.SHPT_POLYGON)
325            shp.write_object(-1, shapelib.SHPObject(shapelib.SHPT_POLYGON, 1,
326                                                    [[(0,0), (10, 10), (10, 0),
327                                                      (0, 0)]]))
328            shp.close()
329    
330            try:
331                session = load_session(self.filename())
332            except ValueError, v:
333                # Usually if the field name is not decoded properly the
334                # loading fails because the field type mentioned in the file
335                # is not None as returned from the layer for a non-existing
336                # column name so we check for that and report it as failure.
337                # Other exceptions are errors in the test case.
338                if str(v) == "xml field type differs from database!":
339                    self.fail("Cannot load file with non-ascii column names")
340                else:
341                    raise
342            self.session = session
343    
344            # In case Thuban could load the file anyway (i.e. no ValueError
345            # exception in load_session()), check explicitly whether the
346            # field name was decoded properly. The test will probably lead
347            # to a UnicodeError instead of a test failure so we check that
348            # too
349            layer = session.Maps()[0].Layers()[0]
350            try:
351                self.assertEquals(layer.GetClassificationColumn(), 'Fl\xe4che')
352            except UnicodeError:
353                # FIXME: Obviously this will have to change if Thuban ever
354                # supports unicode properly.
355                self.fail("Column name was not converted to a bytestring")
356    
357            # roundtrip check
358            self.check_format()
359    
360    
361    class TestLayerVisibility(LoadSessionTest):
362    
363        file_contents = '''\
364    <?xml version="1.0" encoding="UTF-8"?>
365    <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
366    <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
367            title="single map&amp;layer">
368        <fileshapesource filetype="shapefile" id="D1"
369            filename="../../Data/iceland/political.shp"/>
370        <map title="Test Map">
371            <projection name="Unknown">
372                <parameter value="zone=26"/>
373                <parameter value="proj=utm"/>
374                <parameter value="ellps=clrk66"/>
375            </projection>
376            <layer shapestore="D1" visible="false" title="My Layer">
377                <classification>
378                    <clnull label="">
379                        <cldata stroke="#000000" stroke_width="1" fill="None"/>
380                    </clnull>
381                </classification>
382            </layer>
383        </map>
384    </session>
385    '''
386    
387        def test(self):
388            """Test that the visible flag is correctly loaded for a layer."""
389            eq = self.assertEquals
390            session = load_session(self.filename())
391            self.session = session
392            maps = session.Maps()
393            eq(len(maps), 1)
394            map = maps[0]
395            layers = map.Layers()
396            eq(len(layers), 1)
397            layer = layers[0]
398    
399            eq(layer.Visible(), False)
400    
401            self.check_format()
402    
403    
404    class TestSymbolSize(ClassificationTest):
405    
406        file_contents = '''\
407    <?xml version="1.0" encoding="UTF-8"?>
408    <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
409    <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd" title="Thuban sample session">
410        <fileshapesource filetype="shapefile" id="D813968480" filename="../../Data/iceland/cultural_landmark-point.shp"/>
411        <map title="Iceland map">
412            <layer title="cultural_landmark-point" shapestore="D813968480" visible="true">
413                <classification field="CLPTLABEL" field_type="string">
414                    <clnull label="">
415                        <cldata stroke="#000000" stroke_width="1" size="3" fill="#000000"/>
416                    </clnull>
417                    <clpoint label="" value="RUINS">
418                        <cldata stroke="#000000" stroke_width="1" size="6" fill="#ffffff"/>
419                    </clpoint>
420                    <clpoint label="" value="FARM">
421                        <cldata stroke="#000000" stroke_width="1" size="9" fill="#ffff00"/>
422                    </clpoint>
423                </classification>
424            </layer>
425        </map>
426    </session>
427    '''
428    
429        def test(self):
430            """Test that the size attribute for point symbols is correctly
431            loaded for a layer."""
432            eq = self.assertEquals
433            session = load_session(self.filename())
434            self.session = session
435    
436            map = session.Maps()[0] # only one map in the sample
437    
438            expected = [("cultural_landmark-point", 2,
439                            [("default", (), "",
440                                ("#000000", 1, "#000000", 3)),
441                             ("single", "RUINS", "",
442                                ("#000000", 1, "#ffffff", 6)),
443                             ("single", "FARM", "",
444                                ("#000000", 1, "#ffff00", 9))])]
445    
446            self.TestLayers(map.Layers(), expected)
447    
448            self.check_format()
449    
450    
451    class TestClassification(ClassificationTest):
452    
453        file_contents = '''\
454    <?xml version="1.0" encoding="UTF-8"?>
455    <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
456    <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
457            title="single map&amp;layer">
458        <fileshapesource filetype="shapefile" id="D138389860"
459            filename="../../Data/iceland/political.shp"/>
460        <fileshapesource filetype="shapefile" id="D138504492"
461            filename="../../Data/iceland/political.shp"/>
462        <fileshapesource filetype="shapefile" id="D123456789"
463            filename="../../Data/iceland/cultural_landmark-point.shp"/>
464        <map title="Test Map">
465            <projection name="">
466                <parameter value="zone=26"/>
467                <parameter value="proj=utm"/>
468                <parameter value="ellps=clrk66"/>
469            </projection>
470            <layer shapestore="D138389860" visible="true" title="My Layer">
471                <classification field="POPYREG" field_type="string">
472                    <clnull label="">
473                        <cldata stroke="#000000" stroke_width="1" fill="None"/>
474                    </clnull>
475                    <clpoint label="" value="1">
476                        <cldata stroke="#000000" stroke_width="2" fill="None"/>
477                    </clpoint>
478                    <clpoint label="" value="1">
479                        <cldata stroke="#000000" stroke_width="10" fill="None"/>
480                    </clpoint>
481                    <clpoint label="\xc3\x9cml\xc3\xa4uts"
482                            value="\xc3\xa4\xc3\xb6\xc3\xbc">
483                        <cldata stroke="#000000" stroke_width="1" fill="None"/>
484                    </clpoint>
485                </classification>
486            </layer>
487            <layer shapestore="D138504492" visible="true" title="My Layer 2">
488                <classification field="AREA" field_type="double">
489                    <clnull label="">
490                        <cldata stroke="#000000" stroke_width="2" fill="None"/>
491                    </clnull>
492                    <clrange label="" range="[0;1[">
493                        <cldata stroke="#111111" stroke_width="1" fill="None"/>
494                    </clrange>
495                    <clpoint label="" value="0.5">
496                        <cldata stroke="#000000" stroke_width="1" fill="#111111"/>
497                    </clpoint>
498                    <clrange label="" range="[-1;0[">
499                        <cldata stroke="#000000" stroke_width="1" fill="None"/>
500                    </clrange>
501                    <clpoint label="" value="-0.5">
502                        <cldata stroke="#000000" stroke_width="1" fill="None"/>
503                    </clpoint>
504                </classification>
505            </layer>
506            <layer shapestore="D123456789" visible="true" title="My Layer 3">
507                <classification field="CLPTLABEL" field_type="string">
508                    <clnull label="">
509                        <cldata stroke="#000000" size="5" stroke_width="2" fill="None"/>
510                    </clnull>
511                    <clpoint label="" value="FARM">
512                        <cldata stroke="#111111" size="5" stroke_width="1" fill="None"/>
513                    </clpoint>
514                    <clpattern label="" pattern="BUI">
515                        <cldata stroke="#000000" size="5" stroke_width="1" fill="None"/>
516                    </clpattern>
517                </classification>
518            </layer>
519        </map>
520    </session>
521    '''
522    
523        def test(self):
524            """Load a Thuban session with a map and classified layers."""
525            session = load_session(self.filename())
526            self.session = session
527    
528            map = self.session.Maps()[0] # only one map in the sample
529    
530            expected = [("My Layer", 3,
531                            [("default", (), "",
532                                ("#000000", 1, "None")),
533                             ("single", "1", "",
534                                ("#000000", 2, "None")),
535                             ("single", "1", "",
536                                ("#000000", 10, "None")),
537                             ("single", internal_from_unicode(u"\xe4\xf6\xfc"),
538                              internal_from_unicode(u"\xdcml\xe4uts"),
539                                ("#000000", 1, "None"))]),
540                         ("My Layer 2", 4,
541                             [("default", (), "",
542                                ("#000000", 2, "None")),
543                              ("range", (0, 1), "",
544                                ("#111111", 1, "None")),
545                              ("single", .5, "",
546                                ("#000000", 1, "#111111")),
547                              ("range", (-1, 0), "",
548                                ("#000000", 1, "None")),
549                              ("single", -.5, "",
550                                ("#000000", 1, "None"))]),
551                         ("My Layer 3", 2,
552                             [("default", (), "",
553                                ("#000000", 2, "None")),
554                              ("single", "FARM", "",
555                                ("#111111", 1, "None")),
556                              ("pattern", "BUI", "",
557                                ("#000000", 1, "None"))]),
558                        ]
559    
560            self.TestLayers(map.Layers(), expected)
561    
562            self.check_format()
563    
564    
565    class TestLabels(ClassificationTest):
566    
567        file_contents = '''\
568    <?xml version="1.0" encoding="UTF-8"?>
569    <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
570    <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
571            title="single map&amp;layer">
572        <fileshapesource filetype="shapefile" id="D1"
573            filename="../../Data/iceland/political.shp"/>
574        <map title="Test Map">
575            <projection name="Unknown">
576                <parameter value="zone=26"/>
577                <parameter value="proj=utm"/>
578                <parameter value="ellps=clrk66"/>
579            </projection>
580            <layer shapestore="D1" visible="true" title="My Layer">
581                <classification field="POPYREG" field_type="string">
582                    <clnull label="hallo">
583                        <cldata stroke="#000000" stroke_width="1" fill="None"/>
584                    </clnull>
585                    <clpoint label="welt" value="1">
586                        <cldata stroke="#000000" stroke_width="2" fill="None"/>
587                    </clpoint>
588                </classification>
589            </layer>
590        </map>
591    </session>
592    '''
593    
594        def test(self):
595            """Load a session and test for reading the group labels."""
596            eq = self.assertEquals
597            session = load_session(self.filename())
598            self.session = session
599    
600            map = self.session.Maps()[0] # only one map in the sample
601    
602            expected = [("My Layer", 1,
603                            [("default", (), "hallo",
604                                ("#000000", 1, "None")),
605                             ("single", "1", "welt",
606                                ("#000000", 2, "None"))])]
607    
608            self.TestLayers(map.Layers(), expected)
609            self.check_format()
610    
611    
612    class TestLayerProjection(LoadSessionTest):
613    
614        file_contents = '''\
615    <?xml version="1.0" encoding="UTF-8"?>
616    <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
617    <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
618            title="single map&amp;layer">
619        <fileshapesource filetype="shapefile" id="D2"
620            filename="../../Data/iceland/roads-line.shp"/>
621        <fileshapesource filetype="shapefile" id="D4"
622            filename="../../Data/iceland/political.shp"/>
623        <map title="Test Map">
624            <projection name="Unknown">
625                <parameter value="zone=26"/>
626                <parameter value="proj=utm"/>
627                <parameter value="ellps=clrk66"/>
628            </projection>
629            <layer shapestore="D4" visible="true" title="My Layer">
630                <projection name="hello">
631                    <parameter value="zone=13"/>
632                    <parameter value="proj=tmerc"/>
633                    <parameter value="ellps=clrk66"/>
634                </projection>
635                <classification field="POPYREG" field_type="string">
636                    <clnull label="hallo">
637                        <cldata stroke="#000000" stroke_width="1" fill="None"/>
638                    </clnull>
639                    <clpoint label="welt" value="1">
640                        <cldata stroke="#000000" stroke_width="2" fill="None"/>
641                    </clpoint>
642                </classification>
643            </layer>
644            <layer shapestore="D2" visible="true" title="My Layer">
645                <projection name="Unknown">
646                    <parameter value="proj=lcc"/>
647                    <parameter value="lat_1=10"/>
648                    <parameter value="lat_2=20"/>
649                    <parameter value="ellps=clrk66"/>
650                </projection>
651                <classification>
652                    <clnull label="">
653                        <cldata stroke="#000000" stroke_width="1" fill="None"/>
654                    </clnull>
655                </classification>
656            </layer>
657        </map>
658    </session>
659    '''
660    
661        def test(self):
662            """Test loading layers with projections"""
663            eq = self.assertEquals
664            neq = self.assertNotEqual
665    
666            session = load_session(self.filename())
667            self.session = session
668    
669            map = self.session.Maps()[0] # only one map in the sample
670    
671            layers = map.Layers() # two layers in the sample
672    
673            # test layer with a named projection
674            proj = layers[0].GetProjection()
675            neq(proj, None)
676            eq(proj.GetName(), "hello")
677            eq(proj.GetParameter("proj"), "tmerc")
678            eq(proj.GetParameter("zone"), "13")
679            eq(proj.GetParameter("ellps"), "clrk66")
680    
681            # test layer with an unnamed projection
682            proj = layers[1].GetProjection()
683            neq(proj, None)
684            eq(proj.GetName(), "Unknown")
685            eq(proj.GetParameter("proj"), "lcc")
686            eq(proj.GetParameter("lat_1"), "10")
687            eq(proj.GetParameter("lat_2"), "20")
688            eq(proj.GetParameter("ellps"), "clrk66")
689    
690            self.check_format()
691    
692    
693    class TestRasterLayer(LoadSessionTest):
694    
695        file_contents = '''\
696    <?xml version="1.0" encoding="UTF-8"?>
697    <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
698    <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
699            title="single map&amp;layer">
700        <map title="Test Map">
701            <rasterlayer visible="false" filename="../../Data/iceland/island.tif"
702                    title="My RasterLayer" opacity="0.4" masktype="alpha"/>
703        </map>
704    </session>
705    '''
706    
707        def test(self):
708            eq = self.assertEquals
709            neq = self.assertNotEqual
710    
711            session = load_session(self.filename())
712            self.session = session
713    
714            map = self.session.Maps()[0] # only one map in the sample
715    
716            layer = map.Layers()[0] # one layer in the sample
717    
718            eq(layer.Title(), "My RasterLayer")
719            eq(layer.Opacity(), 0.4)
720            eq(layer.MaskType(), layer.MASK_ALPHA)
721    
722            self.failIf(layer.Visible())
723            self.failUnless(filenames_equal(layer.GetImageFilename(),
724                                            os.path.join(self.temp_dir(),
725                                                         os.pardir, os.pardir,
726                                                         "Data", "iceland",
727                                                         "island.tif")))
728            self.check_format()
729    
730    
731    class TestJoinedTable(LoadSessionTest):
732    
733        file_contents = '''<?xml version="1.0" encoding="UTF-8"?>
734    <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
735    <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd" title="A Joined Table session">
736        <fileshapesource filetype="shapefile" id="D137227612"
737            filename="../../Data/iceland/roads-line.shp"/>
738        <filetable filetype="DBF" filename="load_joinedtable.dbf" id="D136171140"
739            title="Some Title"/>
740        <jointable id="D136169900" title="Joined"
741            right="D136171140" left="D137227612"
742            leftcolumn="RDLNTYPE" rightcolumn="RDTYPE"
743            jointype="LEFT OUTER"/>
744        <derivedshapesource table="D136169900" shapesource="D137227612"
745            id="D136170932"/>
746        <map title="Test Map">
747            <layer shapestore="D136170932" visible="true" title="My Layer">
748                <classification>
749                    <clnull label="">
750                        <cldata stroke="#000000" stroke_width="1" fill="None"/>
751                    </clnull>
752                </classification>
753            </layer>
754        </map>
755    </session>
756    '''
757    
758        def setUp(self):
759            """Extend inherited method to create the dbffile for the join"""
760            LoadSessionTest.setUp(self)
761            dbffile = self.temp_file_name("load_joinedtable.dbf")
762            dbf = dbflib.create(dbffile)
763            dbf.add_field("RDTYPE", dbflib.FTInteger, 10, 0)
764            dbf.add_field("TEXT", dbflib.FTString, 10, 0)
765            dbf.write_record(0, {'RDTYPE': 8, "TEXT": "foo"})
766            dbf.write_record(1, {'RDTYPE': 2, "TEXT": "bar"})
767            dbf.write_record(2, {'RDTYPE': 3, "TEXT": "baz"})
768            dbf.close()
769    
770        def test(self):
771            """Test loading a session containing a joined table"""
772            session = load_session(self.filename())
773            self.session = session
774    
775            tables = session.Tables()
776            self.assertEquals(len(tables), 3)
777            # FIXME: The tests shouldn't assume a certain order of the tables
778            self.assertEquals(tables[0].Title(), "Some Title")
779            self.assertEquals(tables[1].Title(), "Joined")
780            self.assertEquals(tables[1].JoinType(), "LEFT OUTER")
781            self.check_format()
782    
783    
784    class TestLabelLayer(LoadSessionTest):
785    
786        # Note that the labels deliberately contain non-ascii characters to
787        # test whether they're supported correctly.
788    
789        file_contents = '''<?xml version="1.0" encoding="UTF-8"?>
790    <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
791    <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd" title="Thuban sample session">
792        <fileshapesource filetype="shapefile" id="D145265052"
793            filename="../../Data/iceland/political.shp"/>
794        <fileshapesource filetype="shapefile" id="D145412868"
795            filename="../../Data/iceland/cultural_landmark-point.shp"/>
796        <map title="Iceland map">
797            <projection name="Unknown">
798                <parameter value="zone=26"/>
799                <parameter value="proj=utm"/>
800                <parameter value="ellps=clrk66"/>
801            </projection>
802            <layer shapestore="D145265052" visible="true" title="political">
803                <projection name="Geographic">
804                    <parameter value="proj=latlong"/>
805                    <parameter value="to_meter=0.017453"/>
806                    <parameter value="ellps=clrk66"/>
807                </projection>
808                <classification>
809                    <clnull label="">
810                        <cldata stroke="#000000" stroke_width="1" fill="#c0c0c0"/>
811                    </clnull>
812                </classification>
813            </layer>
814            <layer shapestore="D145412868" visible="true" title="landmarks">
815                <projection name="Geographic">
816                    <parameter value="proj=latlong"/>
817                    <parameter value="to_meter=0.017453"/>
818                    <parameter value="ellps=clrk66"/>
819                </projection>
820                <classification>
821                    <clnull label="">
822                        <cldata size="5" stroke="#000000" stroke_width="1" fill="#ffff00"/>
823                    </clnull>
824                </classification>
825            </layer>
826            <labellayer>
827                <label x="-21.5" y="64.25" text="RUINS"
828                    halign="left" valign="center"/>
829                <label x="-15.125" y="64.75" text="H\xc3\xbctte"
830                    halign="right" valign="top"/>
831            </labellayer>
832        </map>
833    </session>
834    '''
835    
836        def test(self):
837            """Test loading a session with a label layer"""
838            session = load_session(self.filename())
839            self.session = session
840    
841            label_layer = self.session.Maps()[0].LabelLayer()
842            expected_labels = [(-21.5, 64.25, "RUINS", ALIGN_LEFT, ALIGN_CENTER),
843                               (-15.125, 64.75, internal_from_unicode(u"H\xfctte"),
844                                ALIGN_RIGHT, ALIGN_TOP),
845                               ]
846            for label, values in zip(label_layer.Labels(), expected_labels):
847                self.assertEquals((label.x, label.y, label.text, label.halign,
848                                   label.valign),
849                                  values)
850            self.check_format()
851    
852    
853    class TestPostGISLayer(LoadSessionTest):
854    
855        file_contents = '''<?xml version="1.0" encoding="UTF-8"?>
856    <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
857    <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
858            title="unnamed session">
859        <dbconnection port="%(port)s" host="%(host)s" user="%(user)s"
860            dbtype="postgis" id="D142684948" dbname="%(dbname)s"/>
861        <dbshapesource id="D143149420" dbconn="D142684948"
862            tablename="landmarks_point_id" id_column="point_id"
863            geometry_column="the_geom" />
864        <map title="unnamed map">
865            <layer shapestore="D143149420" visible="true" stroke="#000000"
866                    title="landmarks" stroke_width="1" fill="None"/>
867        </map>
868    </session>
869    '''
870    
871        def setUp(self):
872            """Extend the inherited method to start the postgis server
873    
874            Furthermore, patch the file contents with the real postgis db
875            information
876            """
877            postgissupport.skip_if_no_postgis()
878            self.server = postgissupport.get_test_server()
879            self.postgisdb = self.server.get_default_static_data_db()
880    
881            self.file_contents = self.__class__.file_contents % {
882                "dbname": self.postgisdb.dbname,
883                "user": self.server.user_name,
884                "port": self.server.port,
885                "host": self.server.host}
886            LoadSessionTest.setUp(self)
887    
888        def test(self):
889            """Test loading a session containing a postgis shapestore"""
890            session = load_session(self.filename())
891            self.session = session
892            connections = session.DBConnections()
893            self.assertEquals(len(connections), 1)
894            conn = connections[0]
895            for attr, value in [("host", self.server.host),
896                                ("port", str(self.server.port)),
897                                ("user", self.server.user_name),
898                                ("dbname", self.postgisdb.dbname)]:
899                self.assertEquals(getattr(conn, attr), value)
900            layer = session.Maps()[0].Layers()[0]
901            self.failUnless(layer.ShapeStore().DBConnection() is conn)
902    
903    
904    class TestPostGISLayerPassword(LoadSessionTest):
905    
906        file_contents = '''<?xml version="1.0" encoding="UTF-8"?>
907    <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
908    <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
909            title="unnamed session">
910        <dbconnection port="%(port)s" host="%(host)s" user="%(user)s"
911            dbtype="postgis" id="D142684948" dbname="%(dbname)s"/>
912        <dbshapesource tablename="landmarks" id="D143149420" dbconn="D142684948"/>
913        <map title="unnamed map">
914            <layer shapestore="D143149420" visible="true" stroke="#000000"
915                    title="landmarks" stroke_width="1" fill="None"/>
916        </map>
917    </session>
918    '''
919    
920        def setUp(self):
921            """Extend the inherited method to start the postgis server
922    
923            Furthermore, patch the file contents with the real postgis db
924            information
925            """
926            postgissupport.skip_if_no_postgis()
927            self.server = postgissupport.get_test_server()
928            self.postgisdb = self.server.get_default_static_data_db()
929    
930            self.file_contents = self.__class__.file_contents % {
931                "dbname": self.postgisdb.dbname,
932                "user": self.server.user_name,
933                "port": self.server.port,
934                "host": self.server.host}
935            LoadSessionTest.setUp(self)
936    
937            self.db_connection_callback_called = False
938            self.server.require_authentication(True)
939    
940        def tearDown(self):
941            """Extend the inherited method to switch off postgresql authentication
942            """
943            self.server.require_authentication(False)
944            LoadSessionTest.tearDown(self)
945    
946        def db_connection_callback(self, params, message):
947            """Implementation of Thuban.Model.hooks.query_db_connection_parameters
948            """
949            self.assertEquals(params,
950                              {"dbname": self.postgisdb.dbname,
951                               "user": self.server.user_name,
952                               "port": str(self.server.port),
953                               "host": self.server.host})
954            self.db_connection_callback_called = True
955            params = params.copy()
956            params["password"] = self.server.user_password
957            return params
958    
959        def test_with_callback(self):
960            """Test loading a session with postgis, authentication and a callback
961            """
962            session = load_session(self.filename(),
963                          db_connection_callback = self.db_connection_callback)
964            self.session = session
965            connections = session.DBConnections()
966            self.assertEquals(len(connections), 1)
967            conn = connections[0]
968            for attr, value in [("host", self.server.host),
969                                ("port", str(self.server.port)),
970                                ("user", self.server.user_name),
971                                ("dbname", self.postgisdb.dbname)]:
972                self.assertEquals(getattr(conn, attr), value)
973            layer = session.Maps()[0].Layers()[0]
974            self.failUnless(layer.ShapeStore().DBConnection() is conn)
975            self.failUnless(self.db_connection_callback_called)
976    
977        def test_without_callback(self):
978            """Test loading a session with postgis, authentication and no callback
979            """
980            # A password is required and there's no callback, so we should
981            # get a ConnectionError
982            self.assertRaises(ConnectionError, load_session, self.filename())
983    
984        def test_cancel(self):
985            """Test loading a session with postgis and cancelling authentication
986            """
987            def cancel(*args):
988                self.db_connection_callback_called = True
989                return None
990    
991            # If the user cancels, i.e. if the callbakc returns None, a
992            # LoadCancelled exception is raised.
993            self.assertRaises(LoadCancelled,
994                              load_session, self.filename(), cancel)
995            self.failUnless(self.db_connection_callback_called)
996    
997    
998    class TestLoadError(LoadSessionTest):
999    
1000        file_contents = '''\
1001    <?xml version="1.0" encoding="UTF-8"?>
1002    <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
1003    <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"
1004            title="single map&amp;layer">
1005        <fileshapesource id="D1" filename="../../Data/iceland/political.shp"/>
1006        <map title="Test Map">
1007            <projection name="Unknown">
1008                <parameter value="zone=26"/>
1009                <parameter value="proj=utm"/>
1010                <parameter value="ellps=clrk66"/>
1011            </projection>
1012            <layer shapestore="D1" visible="true"
1013                    stroke="#000000" title="My Layer" stroke_width="1"
1014                    fill="None"/>
1015        </map>
1016    </session>
1017    '''
1018    
1019        def test(self):
1020            """Test loading a session missing a required attribute"""
1021            # Don't use assertRaises to make sure that if a session is
1022            # actually returned it gets destroyed properly.
1023            try:
1024                self.session = load_session(self.filename())
1025            except LoadError, value:
1026                # Check the actual messge in value to make sure the
1027                # LoadError really was about the missing attribute
1028                self.assertEquals(str(value),
1029                  "Element "
1030                  "(u'http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd',"
1031                  " u'fileshapesource') requires an attribute 'filetype'")
1032            else:
1033                self.fail("Missing filetype attribute doesn't raise LoadError")
1034    
1035    class Shapefile_CallBack:
1036    
1037        def __init__(self, params):
1038            """Initialize the callback return values.
1039              
1040               params must be a dictionary of the potential CB modes (keys),
1041               with lists of tuples of return values as values.
1042               Depending on the test the callback can be called multiple,
1043               each time a return value is poped from the list
1044            """
1045    
1046            self.params = params
1047    
1048    
1049        def s_cb(self, filename, mode = None, second_try= 0):
1050            if self.params.has_key(mode):
1051                return self.params[mode].pop(0)
1052            else:
1053                raise LoadError
1054            
1055    class TestAltPath(LoadSessionTest):
1056    
1057        """Test the various cases in the alternative path feature.
1058    
1059           The test checks the reasonable cases:
1060           - First recognition of a path error, fixed with user interaction.
1061           - First recognition of a path error, load cancelled.
1062           - Path error fixed from list, confirmed by user.
1063           - Path error fixed from list, changed by user.
1064           - Path error fixed from list, cancelled by user.
1065           - Path error wrongly fixed from list, manual fix forced.
1066        """
1067    
1068        file_contents = '''\
1069    <?xml version="1.0" encoding="UTF-8"?>
1070    <!DOCTYPE session SYSTEM "thuban-1.1.dtd">
1071    <session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd" title="AltPath Test session">
1072        <fileshapesource filetype="shapefile" id="D1108450956" filename="../../Data/iceland/political.shp"/>
1073        <fileshapesource filetype="shapefile" id="D1108900076" filename="../Data/iceland/roads-line.shp"/>
1074        <fileshapesource filetype="shapefile" id="D1108947244" filename="../../Data/iceland/cultural_landmark-point.shp"/>
1075        <map title="not the iceland map">
1076            <layer title="political" stroke_width="1" shapestore="D1108450956" visible="true" stroke="#000000" fill="#c0c0c0"/>
1077            <layer title="roads-line" stroke_width="1" shapestore="D1108900076" visible="true" stroke="#000000" fill="None"/>
1078            <layer title="something else" stroke_width="1" shapestore="D1108947244" visible="true" stroke="#000000" fill="None"/>
1079        </map>
1080    </session>
1081    '''
1082    
1083        def checkSession(self, session):
1084            """Check if session has been loaded successfully."""
1085            
1086            eq = self.assertEquals
1087    
1088            map = session.Maps()[0]
1089            layers = map.Layers()
1090    
1091            eq("AltPath Test session", session.Title())
1092            eq("not the iceland map", map.Title())
1093            eq(3,len(layers))
1094            eq("political",layers[0].Title())
1095            eq("roads-line",layers[1].Title())
1096            eq("something else",layers[2].Title())
1097    
1098        def test_01_single_path_error_fix(self):
1099            """Test single file path error fix."""
1100            # The usual initial case
1101            s_cb = Shapefile_CallBack({
1102                        "search": [("../Data/iceland/roads-line.shp",0)],
1103                        "check": [(None, None)]})
1104            self.session = load_session(self.filename(),
1105                                        shapefile_callback =s_cb.s_cb)
1106            self.checkSession(self.session)
1107            
1108        def test_02_path_error_fix_from_list(self):
1109            """Test single file path error fix."""
1110            # This represents the usual case for "from_list"
1111            s_cb = Shapefile_CallBack({
1112                    "search": [("../Data/iceland/roads-line.shp",1)],
1113                    "check": [(os.path.abspath("../Data/iceland/roads-line.shp"),1)]
1114                   })
1115            self.session = load_session(self.filename(),
1116                                        shapefile_callback =s_cb.s_cb)
1117            self.checkSession(self.session)
1118    
1119        def test_03_single_path_error_cancelled(self):
1120            """Test alternative path cancelled."""
1121            s_cb = Shapefile_CallBack({
1122                        "search": [(None,0)],
1123                        "check": [(None, None)]})
1124            self.assertRaises(LoadCancelled,
1125                                load_session, self.filename(), None, s_cb.s_cb)
1126    
1127        def test_04_path_error_fix_from_list_cancelled(self):
1128            """Test alternative path from list cancelled."""
1129            s_cb = Shapefile_CallBack({
1130                    "search": [("../Data/iceland/roads-line.shp",1)],
1131                    "check": [(None,1)]
1132                   })
1133            self.assertRaises(LoadCancelled,
1134                                load_session, self.filename(), None, s_cb.s_cb)
1135    
1136        def test_05_path_error_fix_from_list_changed(self):
1137            """Test alternative path from list changed."""
1138            s_cb = Shapefile_CallBack({
1139                    "search": [("../Data/iceland/roads-line.shp",1)],
1140                    "check": [("../Data/iceland/roads-line.shp",0)]
1141                   })
1142            self.session = load_session(self.filename(),
1143                                        shapefile_callback =s_cb.s_cb)
1144            self.checkSession(self.session)
1145    
1146        def test_06_path_error_fix_from_list_fails(self):
1147            """Test alternative path recovery from list."""
1148            s_cb = Shapefile_CallBack({
1149                    "search": [("../wrong/iceland/roads-line.shp",1),
1150                                ("../Data/iceland/roads-line.shp",0)],
1151                    "check": [(None,None)]
1152                   })
1153            self.session = load_session(self.filename(),
1154                                        shapefile_callback =s_cb.s_cb)
1155            self.assertRaises(IndexError,
1156                                s_cb.s_cb, None, "search")
1157            
1158    
1159    
1160  if __name__ == "__main__":  if __name__ == "__main__":
1161      unittest.main()      support.run_tests()

Legend:
Removed from v.409  
changed lines
  Added in v.2734

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26