/[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 2620 - (show annotations)
Fri May 6 14:19:23 2005 UTC (19 years, 10 months ago) by jonathan
Original Path: trunk/thuban/test/test_load.py
File MIME type: text/x-python
File size: 43066 byte(s)
(TestRasterLayer): Change file_contents to include opacity and masktype
variables.
(TestRasterLayer.test): Include tests for opacity and masktype changes.

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