/[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 2642 - (show annotations)
Fri Jul 1 20:49:04 2005 UTC (19 years, 8 months ago) by bh
Original Path: trunk/thuban/test/test_load.py
File MIME type: text/x-python
File size: 43311 byte(s)
First step towards unicode.  With this roughly we're at step 1
string_representation.txt

* Doc/technotes/string_representation.txt: New.  Document how
strings are represented in Thuban and how to get to a Unicode
Thuban.

* Thuban/__init__.py (set_internal_encoding)
(unicode_from_internal, internal_from_unicode): New. The first few
functions for the internal string representation

* Thuban/UI/about.py (unicodeToLocale): Removed.  Use
internal_from_unicode instead.

* Thuban/UI/__init__.py (install_wx_translation): Determine the
encoding to use for the internal string representation.  Also,
change the translation function to return strings in internal
representation even on unicode builds of wxPython

* Thuban/Model/load.py (SessionLoader.check_attrs): Decode
filenames too.
(SessionLoader.start_clrange): Use check_attrs to decode and check
the attributes.

* Thuban/Model/xmlreader.py (XMLReader.encode): Use
internal_from_unicode to convert unicode strings.

* Thuban/Model/xmlwriter.py (XMLWriter.encode): Use
unicode_from_internal when applicable

* test/runtests.py (main): New command line option:
internal-encoding to specify the internal string encoding to use
in the tests.

* test/support.py (initthuban): Set the internal encoding to
latin-1

* test/test_load.py (TestSingleLayer.test, TestClassification.test)
(TestLabelLayer.test): Use the internal string representation when
dealing with non-ascii characters

* test/test_load_1_0.py (TestSingleLayer.test)
(TestClassification.test, TestLabelLayer.test): Use the internal
string representation when dealing with non-ascii characters

* test/test_load_0_9.py (TestSingleLayer.test)
(TestClassification.test): Use the internal string representation
when dealing with non-ascii characters

* test/test_load_0_8.py (TestUnicodeStrings.test): Use the
internal string representation when dealing with non-ascii
characters

* test/test_save.py (XMLWriterTest.testEncode)
(SaveSessionTest.testClassifiedLayer): Use the internal string
representation when dealing with non-ascii characters where
applicable

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