/[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

revision 690 by jonathan, Wed Apr 16 13:47:22 2003 UTC revision 1970 by bh, Mon Nov 24 18:36:00 2003 UTC
# Line 1  Line 1 
1  # Copyright (c) 2002 by Intevation GmbH  # Copyright (c) 2002, 2003 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, the tests here should be
20    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, parse_color  import postgissupport
38  from Thuban.Model.session import Session  from xmlsupport import sax_eventlist
 from Thuban.Model.map import Map  
 from Thuban.Model.layer import Layer  
 from Thuban.Model.proj import Projection  
 from Thuban.Model.color import Color  
39    
40  from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, FIELDTYPE_STRING  import dbflib
41    import shapelib
42    
43    from Thuban.Model.save import save_session
44    from Thuban.Model.load import load_session, parse_color, LoadError, \
45         LoadCancelled
46    from Thuban.Model.color import Transparent
47  from Thuban.Model.classification import ClassGroupProperties, ClassGroupRange,\  from Thuban.Model.classification import ClassGroupProperties, ClassGroupRange,\
48      ClassGroupSingleton, ClassGroupDefault      ClassGroupSingleton, ClassGroupDefault
49    from Thuban.Model.postgisdb import ConnectionError
50    from Thuban.Model.table import DBFTable, MemoryTable, \
51         FIELDTYPE_DOUBLE, FIELDTYPE_INT, FIELDTYPE_STRING, \
52         table_to_dbf
53    
54    
55  def filenames_equal(name1, name2):  def filenames_equal(name1, name2):
56      """Return true if the filenames name1 and name2 are equal.      """Return true if the filenames name1 and name2 are equal.
# Line 43  def filenames_equal(name1, name2): Line 64  def filenames_equal(name1, name2):
64      return os.path.normpath(name1) == os.path.normpath(name2)      return os.path.normpath(name1) == os.path.normpath(name2)
65    
66    
67  contents_single_map = '''\  
68    class LoadSessionTest(support.FileLoadTestCase):
69    
70        """Base class for .thuban file loading tests
71    
72        Basically the same as the FileLoadTestCase, except that all tests
73        use the '.thuban' extension by default and that setUp and tearDown
74        handle sessions.
75        """
76    
77        file_extension = ".thuban"
78    
79        def setUp(self):
80            """Create the test files"""
81            support.FileLoadTestCase.setUp(self)
82            self.session = None
83    
84        def tearDown(self):
85            if self.session is not None:
86                self.session.Destroy()
87            self.session = None
88    
89    
90        dtd = "http://thuban.intevation.org/dtds/thuban-1.0-dev.dtd"
91        thubanids = [((dtd, n), (None, "id")) for n in
92                     ["fileshapesource", "filetable", "jointable",
93                      "derivedshapesource"]]
94        thubanidrefs = [((dtd, n), (None, m)) for n, m in
95                        [("layer", "shapestore"),
96                         ("jointable", "left"),
97                         ("jointable", "right"),
98                         ("derivedshapesource", "table"),
99                         ("derivedshapesource", "shapesource")]]
100        filenames = [((dtd, n), (None, m)) for n, m in
101                     [("fileshapesource", "filename"),
102                      ("rasterlayer", "filename"),
103                      ("filetable", "filename")]]
104        del n, m, dtd
105    
106        def check_format(self):
107            """Check whether the file we loaded from matches the one that
108            would be written. Call this from each test case after loading
109            the session
110            """
111            filename = self.temp_file_name(self.id() + ".roundtrip.thuban")
112            save_session(self.session, filename)
113            el1 = sax_eventlist(filename = filename, ids = self.thubanids,
114                                idrefs = self.thubanidrefs,
115                                filenames = self.filenames)
116            el2 = sax_eventlist(filename = self.filename(), ids = self.thubanids,
117                                idrefs = self.thubanidrefs,
118                                filenames = self.filenames)
119            if 0:
120                for a, b in zip(el1, el2):
121                    print a != b and "***************" or ""
122                    print a
123                    print b
124            self.assertEquals(el1, el2,
125                              "loaded file not equivalent to the saved file")
126    
127    
128    class ClassificationTest(LoadSessionTest):
129    
130        """
131        Base class for tests that do some detailed checking of classifications
132        """
133    
134        def TestLayers(self, layers, expected):
135            TITLE = 0
136            NUM_GROUPS = 1
137            CLASSES = 2
138            GROUP_TYPE = 0
139            GROUP_DATA = 1
140            GROUP_LABEL = 2
141            GROUP_PROPS = 3
142    
143            eq = self.assertEquals
144    
145            eq(len(layers), len(expected))
146    
147            for layer, data in zip(layers, expected):
148                eq(layer.Title(), data[TITLE])
149    
150                clazz = layer.GetClassification()
151                eq(clazz.GetNumGroups(), data[NUM_GROUPS])
152                eq(clazz.GetNumGroups() + 1, len(data[CLASSES]))
153    
154                i = 0
155                for group in clazz:
156                    props = ClassGroupProperties()
157                    props.SetLineColor(
158                        parse_color(data[CLASSES][i][GROUP_PROPS][0]))
159                    props.SetLineWidth(data[CLASSES][i][GROUP_PROPS][1])
160                    props.SetFill(
161                        parse_color(data[CLASSES][i][GROUP_PROPS][2]))
162    
163                    if data[CLASSES][i][GROUP_TYPE] == "default":
164                        g = ClassGroupDefault(props, data[CLASSES][i][GROUP_LABEL])
165                    elif data[CLASSES][i][GROUP_TYPE] == "range":
166                        g = ClassGroupRange((data[CLASSES][i][GROUP_DATA][0],
167                                             data[CLASSES][i][GROUP_DATA][1]),
168                                            props, data[CLASSES][i][GROUP_LABEL])
169                    elif data[CLASSES][i][GROUP_TYPE] == "single":
170                        g = ClassGroupSingleton(data[CLASSES][i][GROUP_DATA],
171                                              props, data[CLASSES][i][GROUP_LABEL])
172    
173                    eq(group, g)
174    
175                    i += 1
176    
177    
178    
179    class TestSingleLayer(LoadSessionTest):
180    
181        # Note: The use of &amp; and non-ascii characters is deliberate. We
182        # want to test whether the loading code handles that correctly.
183        file_contents = '''\
184  <?xml version="1.0" encoding="UTF-8"?>  <?xml version="1.0" encoding="UTF-8"?>
185  <!DOCTYPE session SYSTEM "thuban.dtd">  <!DOCTYPE session SYSTEM "thuban-1.0.dtd">
186  <session title="single map&amp;layer">  <session xmlns="http://thuban.intevation.org/dtds/thuban-1.0-dev.dtd"
187          <map title="Test Map">          title="Stra\xc3\x9fen &amp; Landmarken">
188                  <projection>      <fileshapesource filetype="shapefile" id="D1"
189                          <parameter value="zone=26"/>          filename="../../Data/iceland/political.shp"/>
190                          <parameter value="proj=utm"/>      <map title="\xc3\x9cbersicht">
191                          <parameter value="ellps=clrk66"/>          <projection epsg="32627" name="WGS 84 / UTM zone 27N">
192                  </projection>              <parameter value="datum=WGS84"/>
193                  <layer title="My Layer" stroke_width="1" fill="None"              <parameter value="ellps=WGS84"/>
194                      filename="../../Data/iceland/political.shp"              <parameter value="proj=utm"/>
195                      stroke="#000000"/>              <parameter value="units=m"/>
196          </map>              <parameter value="zone=27"/>
197            </projection>
198            <layer shapestore="D1" visible="true"
199                    stroke="#000000" title="K\xc3\xbcste" stroke_width="1"
200                    fill="None"/>
201        </map>
202    </session>
203    '''
204    
205        def test(self):
206            """Load a session with a single map with a single layer"""
207            eq = self.assertEquals
208            session = load_session(self.filename())
209            self.session = session
210    
211            # Check the title
212            eq(session.Title(), "Stra\xdfen & Landmarken")
213    
214            # the session has one map.
215            maps = session.Maps()
216            eq(len(maps), 1)
217    
218            # Check the map's attributes
219            map = maps[0]
220            eq(map.Title(), "\xdcbersicht")
221            proj = map.GetProjection()
222            eq(proj.GetName(), "WGS 84 / UTM zone 27N")
223            eq(proj.EPSGCode(), "32627")
224            params = proj.GetAllParameters()
225            params.sort()
226            eq(params, ["datum=WGS84", "ellps=WGS84", "proj=utm", "units=m",
227                        "zone=27"])
228    
229            # the map has a single layer
230            layers = map.Layers()
231            eq(len(layers), 1)
232    
233            # Check the layer attributes
234            layer = layers[0]
235            eq(layer.Title(), "K\xfcste")
236            self.failUnless(filenames_equal(layer.ShapeStore().FileName(),
237                                            os.path.join(self.temp_dir(),
238                                                         os.pardir, os.pardir,
239                                                         "Data", "iceland",
240                                                         "political.shp")))
241            eq(layer.GetClassification().GetDefaultFill(), Transparent)
242            eq(layer.GetClassification().GetDefaultLineColor().hex(), "#000000")
243            eq(layer.Visible(), True)
244    
245            self.check_format()
246    
247            self.session.Destroy()
248            self.session = None
249    
250        def test_leak(self):
251            """Test load_session for resource leaks
252    
253            The load_session function had a resource leak in that it created
254            cyclic references. The objects would have been eventually
255            collected by the garbage collector but too late. One symptom is
256            that when layers are removed so that the last normal reference
257            owned indirectly by the session to a shape store goes away, the
258            shape store is not actually removed from the session even though
259            the session only keeps weak references because there are still
260            references owned by the cyclic garbage.
261            """
262            session = load_session(self.filename())
263            self.session = session
264    
265            # sanity check
266            self.assertEquals(len(session.ShapeStores()), 1)
267    
268            # remove the map. The shapestore should go away too
269            session.RemoveMap(session.Maps()[0])
270            self.assertEquals(len(session.ShapeStores()), 0)
271    
272    
273    class TestNonAsciiColumnName(LoadSessionTest):
274    
275        file_contents = '''\
276    <?xml version="1.0" encoding="UTF-8"?>
277    <!DOCTYPE session SYSTEM "thuban-1.0.dtd">
278    <session xmlns="http://thuban.intevation.org/dtds/thuban-1.0-dev.dtd"
279            title="Non ASCII column name test">
280        <fileshapesource filetype="shapefile" id="D1"
281            filename="TestNonAsciiColumnName.shp"/>
282        <map title="map">
283            <projection name="Some Projection">
284                <parameter value="datum=WGS84"/>
285                <parameter value="ellps=WGS84"/>
286                <parameter value="proj=utm"/>
287                <parameter value="units=m"/>
288                <parameter value="zone=27"/>
289            </projection>
290            <layer shapestore="D1" visible="true"
291                    stroke="#000000" title="layer" stroke_width="1"
292                    fill="None">
293                <classification field="Fl\xc3\xa4che" field_type="double">
294                    <clnull label="">
295                        <cldata stroke="#000000" stroke_width="1" fill="None"/>
296                    </clnull>
297                </classification>
298            </layer>
299        </map>
300    </session>
301    '''
302    
303        def test(self):
304            """Load a session with a single map with a single layer"""
305    
306            # Create a shapefile and a dbffile with a non-ascii column name
307            dbffile = self.temp_file_name("TestNonAsciiColumnName.dbf")
308            shpfile = self.temp_file_name("TestNonAsciiColumnName.shp")
309            dbf = dbflib.create(dbffile)
310            dbf.add_field('Fl\xe4che', dbflib.FTDouble, 10, 5)
311            dbf.write_record(0, (0.0,))
312            dbf.close()
313            shp = shapelib.create(shpfile, shapelib.SHPT_POLYGON)
314            shp.write_object(-1, shapelib.SHPObject(shapelib.SHPT_POLYGON, 1,
315                                                    [[(0,0), (10, 10), (10, 0),
316                                                      (0, 0)]]))
317            shp.close()
318    
319            try:
320                session = load_session(self.filename())
321            except ValueError, v:
322                # Usually if the field name is not decoded properly the
323                # loading fails because the field type mentioned in the file
324                # is not None as returned from the layer for a non-existing
325                # column name so we check for that and report it as failure.
326                # Other exceptions are errors in the test case.
327                if str(v) == "xml field type differs from database!":
328                    self.fail("Cannot load file with non-ascii column names")
329                else:
330                    raise
331            self.session = session
332    
333            # In case Thuban could load the file anyway (i.e. no ValueError
334            # exception in load_session()), check explicitly whether the
335            # field name was decoded properly. The test will probably lead
336            # to a UnicodeError instead of a test failure so we check that
337            # too
338            layer = session.Maps()[0].Layers()[0]
339            try:
340                self.assertEquals(layer.GetClassificationColumn(), 'Fl\xe4che')
341            except UnicodeError:
342                # FIXME: Obviously this will have to change if Thuban ever
343                # supports unicode properly.
344                self.fail("Column name was not converted to a bytestring")
345    
346            # roundtrip check
347            self.check_format()
348    
349    
350    class TestLayerVisibility(LoadSessionTest):
351    
352        file_contents = '''\
353    <?xml version="1.0" encoding="UTF-8"?>
354    <!DOCTYPE session SYSTEM "thuban-1.0.dtd">
355    <session xmlns="http://thuban.intevation.org/dtds/thuban-1.0-dev.dtd"
356            title="single map&amp;layer">
357        <fileshapesource filetype="shapefile" id="D1"
358            filename="../../Data/iceland/political.shp"/>
359        <map title="Test Map">
360            <projection name="Unknown">
361                <parameter value="zone=26"/>
362                <parameter value="proj=utm"/>
363                <parameter value="ellps=clrk66"/>
364            </projection>
365            <layer shapestore="D1" visible="false" stroke="#000000"
366                    title="My Layer" stroke_width="1" fill="None"/>
367        </map>
368  </session>  </session>
369  '''  '''
370    
371  contents_classified_map_v0_2 = '''\      def test(self):
372            """Test that the visible flag is correctly loaded for a layer."""
373            eq = self.assertEquals
374            session = load_session(self.filename())
375            self.session = session
376            maps = session.Maps()
377            eq(len(maps), 1)
378            map = maps[0]
379            layers = map.Layers()
380            eq(len(layers), 1)
381            layer = layers[0]
382    
383            eq(layer.Visible(), False)
384    
385            self.check_format()
386    
387    
388    class TestClassification(ClassificationTest):
389    
390        file_contents = '''\
391  <?xml version="1.0" encoding="UTF-8"?>  <?xml version="1.0" encoding="UTF-8"?>
392  <!DOCTYPE session SYSTEM "thuban.dtd">  <!DOCTYPE session SYSTEM "thuban.dtd">
393  <session title="single map&amp;layer">  <session title="single map&amp;layer">
# Line 83  contents_classified_map_v0_2 = '''\ Line 410  contents_classified_map_v0_2 = '''\
410                  <clpoint value="1">                  <clpoint value="1">
411                      <cldata stroke="#000000" stroke_width="10" fill="None"/>                      <cldata stroke="#000000" stroke_width="10" fill="None"/>
412                  </clpoint>                  </clpoint>
413                    <clpoint value="\xc3\xa4\xc3\xb6\xc3\xbc"
414                             label="\xc3\x9cml\xc3\xa4uts">
415                        <cldata fill="None" stroke="#000000" stroke_width="1"/>
416                    </clpoint>
417              </classification>              </classification>
418          </layer>          </layer>
419                  <layer title="My Layer 2" stroke_width="1" fill="None"                  <layer title="My Layer 2" stroke_width="1" fill="None"
# Line 90  contents_classified_map_v0_2 = '''\ Line 421  contents_classified_map_v0_2 = '''\
421                      stroke="#000000">                      stroke="#000000">
422              <classification field="AREA" field_type="double">              <classification field="AREA" field_type="double">
423                  <clnull>                  <clnull>
                     <cldata stroke="#000000" stroke_width="1" fill="None"/>  
                 </clnull>  
                 <clnull>  
424                      <cldata stroke="#000000" stroke_width="2" fill="None"/>                      <cldata stroke="#000000" stroke_width="2" fill="None"/>
425                  </clnull>                  </clnull>
426                  <clrange min="0" max="1">                  <clrange min="0" max="1">
427                      <cldata stroke="#000000" stroke_width="1" fill="None"/>                      <cldata stroke="#111111" stroke_width="1" fill="None"/>
428                  </clrange>                  </clrange>
429                  <clpoint value=".5">                  <clpoint value=".5">
430                      <cldata stroke="#000000" stroke_width="1" fill="None"/>                      <cldata stroke="#000000" stroke_width="1" fill="#111111"/>
431                  </clpoint>                  </clpoint>
432                  <clrange min="-1" max="0">                  <clrange min="-1" max="0">
433                      <cldata stroke="#000000" stroke_width="1" fill="None"/>                      <cldata stroke="#000000" stroke_width="1" fill="None"/>
# Line 113  contents_classified_map_v0_2 = '''\ Line 441  contents_classified_map_v0_2 = '''\
441  </session>  </session>
442  '''  '''
443    
444  contents_test_labels = '''\      def test(self):
445            """Load a Thuban session with a map and classified layers."""
446            session = load_session(self.filename())
447            self.session = session
448    
449            map = self.session.Maps()[0] # only one map in the sample
450    
451            expected = [("My Layer", 3,
452                            [("default", (), "",
453                                ("#000000", 1, "None")),
454                             ("single", "1", "",
455                                ("#000000", 2, "None")),
456                             ("single", "1", "",
457                                ("#000000", 10, "None")),
458                             ("single", "\xe4\xf6\xfc", "\xdcml\xe4uts",
459                                ("#000000", 1, "None"))]),
460                         ("My Layer 2", 4,
461                             [("default", (), "",
462                                ("#000000", 2, "None")),
463                              ("range", (0, 1), "",
464                                ("#111111", 1, "None")),
465                              ("single", .5, "",
466                                ("#000000", 1, "#111111")),
467                              ("range", (-1, 0), "",
468                                ("#000000", 1, "None")),
469                              ("single", -.5, "",
470                                ("#000000", 1, "None"))])]
471    
472            self.TestLayers(map.Layers(), expected)
473    
474    
475    class TestLabels(ClassificationTest):
476    
477        file_contents = '''\
478  <?xml version="1.0" encoding="UTF-8"?>  <?xml version="1.0" encoding="UTF-8"?>
479  <!DOCTYPE session SYSTEM "thuban.dtd">  <!DOCTYPE session SYSTEM "thuban-1.0.dtd">
480  <session title="single map&amp;layer">  <session xmlns="http://thuban.intevation.org/dtds/thuban-1.0-dev.dtd"
481          <map title="Test Map">          title="single map&amp;layer">
482                  <projection>      <fileshapesource filetype="shapefile" id="D1"
483                          <parameter value="zone=26"/>          filename="../../Data/iceland/political.shp"/>
484                          <parameter value="proj=utm"/>      <map title="Test Map">
485                          <parameter value="ellps=clrk66"/>          <projection name="Unknown">
486                  </projection>              <parameter value="zone=26"/>
487                  <layer title="My Layer" stroke_width="1" fill="None"              <parameter value="proj=utm"/>
488                      filename="../../Data/iceland/political.shp"              <parameter value="ellps=clrk66"/>
489                      stroke="#000000">          </projection>
490            <layer shapestore="D1" visible="true" stroke="#000000"
491                    title="My Layer" stroke_width="1" fill="None">
492              <classification field="POPYREG" field_type="string">              <classification field="POPYREG" field_type="string">
493                  <clnull label="hallo">                  <clnull label="hallo">
494                      <cldata stroke="#000000" stroke_width="1" fill="None"/>                      <cldata stroke="#000000" stroke_width="1" fill="None"/>
# Line 139  contents_test_labels = '''\ Line 502  contents_test_labels = '''\
502  </session>  </session>
503  '''  '''
504    
505  class LoadSessionTest(unittest.TestCase, support.FileTestMixin):      def test(self):
506            """Load a session and test for reading the group labels."""
507            eq = self.assertEquals
508            session = load_session(self.filename())
509            self.session = session
510    
511      def setUp(self):          map = self.session.Maps()[0] # only one map in the sample
         """Create the test files"""  
         file = open(self.temp_file_name("load_singlelayer.thuban"), "w")  
         file.write(contents_single_map)  
         file.close()  
   
         file = open(self.temp_file_name("load_classified_v0_2.thuban"), "w")  
         file.write(contents_classified_map_v0_2)  
         file.close()  
   
         file = open(self.temp_file_name("load_labels.thuban"), "w")  
         file.write(contents_test_labels)  
         file.close()  
         self.session = None  
512    
513      def tearDown(self):          expected = [("My Layer", 1,
514          if self.session is not None:                          [("default", (), "hallo",
515              self.session.Destroy()                              ("#000000", 1, "None")),
516                             ("single", "1", "welt",
517                                ("#000000", 2, "None"))])]
518    
519      def testSingleLayer(self):          self.TestLayers(map.Layers(), expected)
520          """Load a session with a single map with a single layer"""          self.check_format()
521    
522    
523    class TestLayerProjection(LoadSessionTest):
524    
525        file_contents = '''\
526    <?xml version="1.0" encoding="UTF-8"?>
527    <!DOCTYPE session SYSTEM "thuban-1.0.dtd">
528    <session xmlns="http://thuban.intevation.org/dtds/thuban-1.0-dev.dtd"
529            title="single map&amp;layer">
530        <fileshapesource filetype="shapefile" id="D2"
531            filename="../../Data/iceland/roads-line.shp"/>
532        <fileshapesource filetype="shapefile" id="D4"
533            filename="../../Data/iceland/political.shp"/>
534        <map title="Test Map">
535            <projection name="Unknown">
536                <parameter value="zone=26"/>
537                <parameter value="proj=utm"/>
538                <parameter value="ellps=clrk66"/>
539            </projection>
540            <layer shapestore="D4" visible="true" stroke="#000000"
541                    title="My Layer" stroke_width="1" fill="None">
542                <projection name="hello">
543                    <parameter value="zone=13"/>
544                    <parameter value="proj=tmerc"/>
545                    <parameter value="ellps=clrk66"/>
546                </projection>
547                <classification field="POPYREG" field_type="string">
548                    <clnull label="hallo">
549                        <cldata stroke="#000000" stroke_width="1" fill="None"/>
550                    </clnull>
551                    <clpoint label="welt" value="1">
552                        <cldata stroke="#000000" stroke_width="2" fill="None"/>
553                    </clpoint>
554                </classification>
555            </layer>
556            <layer shapestore="D2" visible="true" stroke="#000000"
557                    title="My Layer" stroke_width="1" fill="None">
558                <projection name="Unknown">
559                    <parameter value="proj=lcc"/>
560                    <parameter value="lat_1=10"/>
561                    <parameter value="lat_2=20"/>
562                    <parameter value="ellps=clrk66"/>
563                </projection>
564            </layer>
565        </map>
566    </session>
567    '''
568    
569        def test(self):
570            """Test loading layers with projections"""
571          eq = self.assertEquals          eq = self.assertEquals
572          session = load_session(self.temp_file_name("load_singlelayer.thuban"))          neq = self.assertNotEqual
573    
574            session = load_session(self.filename())
575          self.session = session          self.session = session
576    
577          # Check the title          map = self.session.Maps()[0] # only one map in the sample
         eq(session.Title(), "single map&layer")  
578    
579          # the session has one map.          layers = map.Layers() # two layers in the sample
         maps = session.Maps()  
         eq(len(maps), 1)  
580    
581          # Check the map's attributes          # test layer with a named projection
582          map = maps[0]          proj = layers[0].GetProjection()
583          eq(map.Title(), "Test Map")          neq(proj, None)
584            eq(proj.GetName(), "hello")
585            eq(proj.GetParameter("proj"), "tmerc")
586            eq(proj.GetParameter("zone"), "13")
587            eq(proj.GetParameter("ellps"), "clrk66")
588    
589            # test layer with an unnamed projection
590            proj = layers[1].GetProjection()
591            neq(proj, None)
592            eq(proj.GetName(), "Unknown")
593            eq(proj.GetParameter("proj"), "lcc")
594            eq(proj.GetParameter("lat_1"), "10")
595            eq(proj.GetParameter("lat_2"), "20")
596            eq(proj.GetParameter("ellps"), "clrk66")
597    
598          # the map has a single layer          self.check_format()
         layers = map.Layers()  
         eq(len(layers), 1)  
599    
         # Check the layer attributes  
         layer = layers[0]  
         eq(layer.Title(), "My Layer")  
         self.failUnless(filenames_equal(layer.filename,  
                                         os.path.join(self.temp_dir(),  
                                                      os.pardir, os.pardir,  
                                                      "Data", "iceland",  
                                                      "political.shp")))  
         eq(layer.GetClassification().GetDefaultFill(), Color.Transparent)  
         eq(layer.GetClassification().GetDefaultLineColor().hex(), "#000000")  
600    
601          self.session.Destroy()  class TestRasterLayer(LoadSessionTest):
602          self.session = None  
603        file_contents = '''\
604    <?xml version="1.0" encoding="UTF-8"?>
605    <!DOCTYPE session SYSTEM "thuban-1.0.dtd">
606    <session xmlns="http://thuban.intevation.org/dtds/thuban-1.0-dev.dtd"
607            title="single map&amp;layer">
608        <map title="Test Map">
609            <rasterlayer visible="false" filename="../../Data/iceland/island.tif"
610                    title="My RasterLayer"/>
611        </map>
612    </session>
613    '''
614    
615      def testClassification(self):      def test(self):
616          """Load a session with a map and classified layers."""          eq = self.assertEquals
617            neq = self.assertNotEqual
618    
619          session = load_session(self.temp_file_name("load_classified_v0_2.thuban"))          session = load_session(self.filename())
620          self.session = session          self.session = session
621    
622          map = self.session.Maps()[0] # only one map in the sample          map = self.session.Maps()[0] # only one map in the sample
623    
624          expected = [("My Layer", 2,          layer = map.Layers()[0] # one layer in the sample
                         [("default", (), "",  
                             ("#000000", 1, "None")),  
                          ("single", "1", "",  
                             ("#000000", 2, "None")),  
                          ("single", "1", "",  
                             ("#000000", 10, "None"))]),  
                      ("My Layer 2", 4,  
                          [("default", (), "",  
                             ("#000000", 2, "None")),  
                           ("range", (0, 1), "",  
                             ("#000000", 1, "None")),  
                           ("single", .5, "",  
                             ("#000000", 1, "None")),  
                           ("range", (-1, 0), "",  
                             ("#000000", 1, "None")),  
                           ("single", -.5, "",  
                             ("#000000", 1, "None"))])]  
625    
626          self.TestLayers(map.Layers(), expected)          eq(layer.Title(), "My RasterLayer")
627            self.failIf(layer.Visible())
628            self.failUnless(filenames_equal(layer.GetImageFilename(),
629                                            os.path.join(self.temp_dir(),
630                                                         os.pardir, os.pardir,
631                                                         "Data", "iceland",
632                                                         "island.tif")))
633            self.check_format()
634    
     def TestLayers(self, layers, expected):  
635    
636          TITLE = 0  class TestJoinedTable(LoadSessionTest):
         NUM_GROUPS = 1  
         CLASSES = 2  
         GROUP_TYPE = 0  
         GROUP_DATA = 1  
         GROUP_LABEL = 2  
         GROUP_PROPS = 3  
637    
638          eq = self.assertEquals      file_contents = '''<?xml version="1.0" encoding="UTF-8"?>
639    <!DOCTYPE session SYSTEM "thuban-1.0.dtd">
640    <session xmlns="http://thuban.intevation.org/dtds/thuban-1.0-dev.dtd" title="A Joined Table session">
641        <fileshapesource filetype="shapefile" id="D137227612"
642            filename="../../Data/iceland/roads-line.shp"/>
643        <filetable filetype="DBF" filename="load_joinedtable.dbf" id="D136171140"
644            title="Some Title"/>
645        <jointable id="D136169900" title="Joined"
646            right="D136171140" left="D137227612"
647            leftcolumn="RDLNTYPE" rightcolumn="RDTYPE"
648            jointype="LEFT OUTER"/>
649        <derivedshapesource table="D136169900" shapesource="D137227612"
650            id="D136170932"/>
651        <map title="Test Map">
652            <layer shapestore="D136170932" visible="true" stroke="#000000"
653                    title="My Layer" stroke_width="1" fill="None"/>
654        </map>
655    </session>
656    '''
657    
658          eq(len(layers), len(expected))      def setUp(self):
659            """Extend inherited method to create the dbffile for the join"""
660            LoadSessionTest.setUp(self)
661            dbffile = self.temp_file_name("load_joinedtable.dbf")
662            dbf = dbflib.create(dbffile)
663            dbf.add_field("RDTYPE", dbflib.FTInteger, 10, 0)
664            dbf.add_field("TEXT", dbflib.FTString, 10, 0)
665            dbf.write_record(0, {'RDTYPE': 8, "TEXT": "foo"})
666            dbf.write_record(1, {'RDTYPE': 2, "TEXT": "bar"})
667            dbf.write_record(2, {'RDTYPE': 3, "TEXT": "baz"})
668            dbf.close()
669    
670        def test(self):
671            """Test loading a session containing a joined table"""
672            session = load_session(self.filename())
673            self.session = session
674    
675          for layer, data in zip(layers, expected):          tables = session.Tables()
676              eq(layer.Title(), data[TITLE])          self.assertEquals(len(tables), 3)
677            # FIXME: The tests shouldn't assume a certain order of the tables
678            self.assertEquals(tables[0].Title(), "Some Title")
679            self.assertEquals(tables[1].Title(), "Joined")
680            self.assertEquals(tables[1].JoinType(), "LEFT OUTER")
681            self.check_format()
682    
             clazz = layer.GetClassification()  
             eq(clazz.GetNumGroups(), data[NUM_GROUPS])  
             eq(clazz.GetNumGroups() + 1, len(data[CLASSES]))  
683    
             i = 0  
             for group in clazz:  
                   
                 props = ClassGroupProperties()  
                 props.SetLineColor(  
                     parse_color(data[CLASSES][i][GROUP_PROPS][0]))  
                 props.SetLineWidth(data[CLASSES][i][GROUP_PROPS][1])  
                 props.SetFill(  
                     parse_color(data[CLASSES][i][GROUP_PROPS][2]))  
684    
685                  if data[CLASSES][i][GROUP_TYPE] == "default":  class TestPostGISLayer(LoadSessionTest):
686                      g = ClassGroupDefault(props, data[CLASSES][i][GROUP_LABEL])  
687                  elif data[CLASSES][i][GROUP_TYPE] == "range":      file_contents = '''<?xml version="1.0" encoding="UTF-8"?>
688                      g = ClassGroupRange(data[CLASSES][i][GROUP_DATA][0],  <!DOCTYPE session SYSTEM "thuban-1.0.dtd">
689                                          data[CLASSES][i][GROUP_DATA][1],  <session xmlns="http://thuban.intevation.org/dtds/thuban-1.0-dev.dtd"
690                                          props, data[CLASSES][i][GROUP_LABEL])          title="unnamed session">
691                  elif data[CLASSES][i][GROUP_TYPE] == "single":      <dbconnection port="%(port)s" host="%(host)s" user="%(user)s"
692                      g = ClassGroupSingleton(data[CLASSES][i][GROUP_DATA],          dbtype="postgis" id="D142684948" dbname="%(dbname)s"/>
693                                            props, data[CLASSES][i][GROUP_LABEL])      <dbshapesource tablename="landmarks" id="D143149420" dbconn="D142684948"/>
694        <map title="unnamed map">
695            <layer shapestore="D143149420" visible="true" stroke="#000000"
696                    title="landmarks" stroke_width="1" fill="None"/>
697        </map>
698    </session>
699    '''
700    
701                  eq(group, g)      def setUp(self):
702            """Extend the inherited method to start the postgis server
703    
704                  i += 1          Furthermore, patch the file contents with the real postgis db
705            information
706            """
707            postgissupport.skip_if_no_postgis()
708            self.server = postgissupport.get_test_server()
709            self.postgisdb = self.server.get_default_static_data_db()
710    
711            self.file_contents = self.__class__.file_contents % {
712                "dbname": self.postgisdb.dbname,
713                "user": self.server.user_name,
714                "port": self.server.port,
715                "host": self.server.host}
716            LoadSessionTest.setUp(self)
717    
718        def test(self):
719            """Test loading a session containing a postgis shapestore"""
720            session = load_session(self.filename())
721            self.session = session
722            connections = session.DBConnections()
723            self.assertEquals(len(connections), 1)
724            conn = connections[0]
725            for attr, value in [("host", self.server.host),
726                                ("port", str(self.server.port)),
727                                ("user", self.server.user_name),
728                                ("dbname", self.postgisdb.dbname)]:
729                self.assertEquals(getattr(conn, attr), value)
730            layer = session.Maps()[0].Layers()[0]
731            self.failUnless(layer.ShapeStore().DBConnection() is conn)
732    
733    
734    class TestPostGISLayerPassword(LoadSessionTest):
735    
736        file_contents = '''<?xml version="1.0" encoding="UTF-8"?>
737    <!DOCTYPE session SYSTEM "thuban-1.0.dtd">
738    <session xmlns="http://thuban.intevation.org/dtds/thuban-1.0-dev.dtd"
739            title="unnamed session">
740        <dbconnection port="%(port)s" host="%(host)s" user="%(user)s"
741            dbtype="postgis" id="D142684948" dbname="%(dbname)s"/>
742        <dbshapesource tablename="landmarks" id="D143149420" dbconn="D142684948"/>
743        <map title="unnamed map">
744            <layer shapestore="D143149420" visible="true" stroke="#000000"
745                    title="landmarks" stroke_width="1" fill="None"/>
746        </map>
747    </session>
748    '''
749    
750      def testLabels(self):      def setUp(self):
751          """Load a session and test for reading the group labels."""          """Extend the inherited method to start the postgis server
752    
753          eq = self.assertEquals          Furthermore, patch the file contents with the real postgis db
754          session = load_session(self.temp_file_name("load_labels.thuban"))          information
755          self.session = session          """
756            postgissupport.skip_if_no_postgis()
757            self.server = postgissupport.get_test_server()
758            self.postgisdb = self.server.get_default_static_data_db()
759    
760            self.file_contents = self.__class__.file_contents % {
761                "dbname": self.postgisdb.dbname,
762                "user": self.server.user_name,
763                "port": self.server.port,
764                "host": self.server.host}
765            LoadSessionTest.setUp(self)
766    
767          map = self.session.Maps()[0] # only one map in the sample          self.db_connection_callback_called = False
768            self.server.require_authentication(True)
769    
770          expected = [("My Layer", 1,      def tearDown(self):
771                          [("default", (), "hallo",          """Extend the inherited method to switch off postgresql authentication
772                              ("#000000", 1, "None")),          """
773                           ("single", "1", "welt",          self.server.require_authentication(False)
774                              ("#000000", 2, "None"))])]          LoadSessionTest.tearDown(self)
775    
776        def db_connection_callback(self, params, message):
777            """Implementation of Thuban.Model.hooks.query_db_connection_parameters
778            """
779            self.assertEquals(params,
780                              {"dbname": self.postgisdb.dbname,
781                               "user": self.server.user_name,
782                               "port": str(self.server.port),
783                               "host": self.server.host})
784            self.db_connection_callback_called = True
785            params = params.copy()
786            params["password"] = self.server.user_password
787            return params
788    
789        def test_with_callback(self):
790            """Test loading a session with postgis, authentication and a callback
791            """
792            session = load_session(self.filename(),
793                          db_connection_callback = self.db_connection_callback)
794            self.session = session
795            connections = session.DBConnections()
796            self.assertEquals(len(connections), 1)
797            conn = connections[0]
798            for attr, value in [("host", self.server.host),
799                                ("port", str(self.server.port)),
800                                ("user", self.server.user_name),
801                                ("dbname", self.postgisdb.dbname)]:
802                self.assertEquals(getattr(conn, attr), value)
803            layer = session.Maps()[0].Layers()[0]
804            self.failUnless(layer.ShapeStore().DBConnection() is conn)
805            self.failUnless(self.db_connection_callback_called)
806    
807        def test_without_callback(self):
808            """Test loading a session with postgis, authentication and no callback
809            """
810            # A password is required and there's no callback, so we should
811            # get a ConnectionError
812            self.assertRaises(ConnectionError, load_session, self.filename())
813    
814        def test_cancel(self):
815            """Test loading a session with postgis and cancelling authentication
816            """
817            def cancel(*args):
818                self.db_connection_callback_called = True
819                return None
820    
821            # If the user cancels, i.e. if the callbakc returns None, a
822            # LoadCancelled exception is raised.
823            self.assertRaises(LoadCancelled,
824                              load_session, self.filename(), cancel)
825            self.failUnless(self.db_connection_callback_called)
826    
         self.TestLayers(map.Layers(), expected)  
827    
828  if __name__ == "__main__":  class TestLoadError(LoadSessionTest):
     unittest.main()  
829    
830        file_contents = '''\
831    <?xml version="1.0" encoding="UTF-8"?>
832    <!DOCTYPE session SYSTEM "thuban-1.0.dtd">
833    <session xmlns="http://thuban.intevation.org/dtds/thuban-1.0-dev.dtd"
834            title="single map&amp;layer">
835        <fileshapesource id="D1" filename="../../Data/iceland/political.shp"/>
836        <map title="Test Map">
837            <projection name="Unknown">
838                <parameter value="zone=26"/>
839                <parameter value="proj=utm"/>
840                <parameter value="ellps=clrk66"/>
841            </projection>
842            <layer shapestore="D1" visible="true"
843                    stroke="#000000" title="My Layer" stroke_width="1"
844                    fill="None"/>
845        </map>
846    </session>
847    '''
848    
849        def test(self):
850            """Test loading a session missing a required attribute"""
851            # Don't use assertRaises to make sure that if a session is
852            # actually returned it gets destroyed properly.
853            try:
854                self.session = load_session(self.filename())
855            except LoadError, value:
856                # Check the actual messge in value to make sure the
857                # LoadError really was about the missing attribute
858                self.assertEquals(str(value),
859                  "Element "
860                  "(u'http://thuban.intevation.org/dtds/thuban-1.0-dev.dtd',"
861                  " u'fileshapesource') requires an attribute 'filetype'")
862            else:
863                self.fail("Missing filetype attribute doesn't raise LoadError")
864    
865    if __name__ == "__main__":
866        support.run_tests()

Legend:
Removed from v.690  
changed lines
  Added in v.1970

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26