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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2654 - (show annotations)
Wed Jul 27 21:44:16 2005 UTC (19 years, 7 months ago) by jan
Original Path: trunk/thuban/test/test_load.py
File MIME type: text/x-python
File size: 43775 byte(s)
(TestSingleLayer, TestNonAsciiColumnName, TestLayerVisibility,
TestSymbolSize, TestClassification, TestLabels, TestLayerProjection,
TestJoinedTable, TestLabelLayer): Removed attributes from layer
element to classification clnull element.

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

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26