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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1640 - (show annotations)
Fri Aug 22 18:24:44 2003 UTC (21 years, 6 months ago) by bh
Original Path: trunk/thuban/test/test_save.py
File MIME type: text/x-python
File size: 23534 byte(s)
(SaveSessionTest.test_dbf_table)
(SaveSessionTest.test_joined_table): Add XML validation tests.

1 # Copyright (c) 2002, 2003 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 saving a thuban session as XML
10 """
11
12 __version__ = "$Revision$"
13 # $Source$
14 # $Id$
15
16 import os
17 import unittest
18 from StringIO import StringIO
19
20 import xmlsupport
21 import postgissupport
22
23 import support
24 support.initthuban()
25
26 import dbflib
27
28 from Thuban.Lib.fileutil import relative_filename
29 from Thuban.Model.save import XMLWriter, save_session, sort_data_stores
30 from Thuban.Model.session import Session
31 from Thuban.Model.map import Map
32 from Thuban.Model.layer import Layer, RasterLayer
33 from Thuban.Model.proj import Projection
34 from Thuban.Model.table import DBFTable
35 from Thuban.Model.transientdb import TransientJoinedTable
36 from Thuban.Model.data import DerivedShapeStore
37
38 from Thuban.Model.classification import ClassGroupSingleton, ClassGroupRange, \
39 ClassGroupProperties
40
41 from Thuban.Model.range import Range
42
43 from Thuban.Model.postgisdb import PostGISConnection, PostGISShapeStore
44
45
46 class XMLWriterTest(unittest.TestCase):
47
48 def testEncode(self):
49 """Test XMLWriter.encode"""
50 writer = XMLWriter()
51 eq = self.assertEquals
52
53 eq(writer.encode("hello world"), "hello world")
54 eq(writer.encode(unicode("hello world")), unicode("hello world"))
55
56 eq(writer.encode("\x80\x90\xc2\x100"),
57 "\xc2\x80\xc2\x90\xc3\x82\x100")
58 eq(writer.encode(u"\x80\x90\xc2\x100"),
59 "\xc2\x80\xc2\x90\xc3\x82\x100")
60 eq(writer.encode(u"\xFF5E"), "\xc3\xbf5E")
61
62 eq(writer.encode('&"\'<>'), "&amp;&quot;&apos;&lt;&gt;")
63 eq(writer.encode(unicode('&"\'<>')), "&amp;&quot;&apos;&lt;&gt;")
64
65 class SaveSessionTest(unittest.TestCase, support.FileTestMixin,
66 xmlsupport.ValidationTest):
67
68 dtd = "http://thuban.intevation.org/dtds/thuban-0.9-dev.dtd"
69 thubanids = [((dtd, n), (None, "id")) for n in
70 ["fileshapesource", "filetable", "jointable",
71 "derivedshapesource", "dbshapesource", "dbconnection"]]
72 thubanidrefs = [((dtd, n), (None, m)) for n, m in
73 [("layer", "shapestore"),
74 ("jointable", "left"),
75 ("jointable", "right"),
76 ("derivedshapesource", "table"),
77 ("derivedshapesource", "shapesource"),
78 ("dbshapesource", "dbconn")]]
79 del n, m, dtd
80
81 def compare_xml(self, xml1, xml2):
82 list1 = xmlsupport.sax_eventlist(xml1, ids = self.thubanids,
83 idrefs = self.thubanidrefs)
84 list2 = xmlsupport.sax_eventlist(xml2, ids = self.thubanids,
85 idrefs = self.thubanidrefs)
86 if list1 != list2:
87 for a, b in zip(list1, list2):
88 if a != b:
89 self.fail("%r != %r" % (a, b))
90
91
92 def testEmptySession(self):
93 """Save an empty session"""
94 session = Session("empty session")
95 filename = self.temp_file_name("save_emptysession.thuban")
96 save_session(session, filename)
97 session.Destroy()
98
99 file = open(filename)
100 written_contents = file.read()
101 file.close()
102 self.compare_xml(written_contents,
103 '<?xml version="1.0" encoding="UTF-8"?>\n'
104 '<!DOCTYPE session SYSTEM "thuban-0.9.dtd">\n'
105 '<session title="empty session" '
106 'xmlns="http://thuban.intevation.org/dtds/thuban-0.9-dev.dtd">'
107 '\n</session>\n')
108
109 self.validate_data(written_contents)
110
111 def testSingleLayer(self):
112 """Save a session with a single map with a single layer"""
113 # deliberately put an apersand in the title :)
114 session = Session("single map&layer")
115 proj = Projection(["zone=26", "proj=utm", "ellps=clrk66"])
116 map = Map("Test Map", projection = proj)
117 session.AddMap(map)
118 # use shapefile from the example data
119 shpfile = os.path.join(os.path.dirname(__file__),
120 os.pardir, "Data", "iceland", "political.shp")
121 layer = Layer("My Layer", session.OpenShapefile(shpfile))
122 map.AddLayer(layer)
123
124 filename = self.temp_file_name("save_singlemap.thuban")
125 save_session(session, filename)
126
127 file = open(filename)
128 written_contents = file.read()
129 file.close()
130 expected_template = '''<?xml version="1.0" encoding="UTF-8"?>
131 <!DOCTYPE session SYSTEM "thuban-0.9.dtd">
132 <session title="single map&amp;layer"
133 xmlns="http://thuban.intevation.org/dtds/thuban-0.9-dev.dtd">
134 <fileshapesource id="D1" filename="%s" filetype="shapefile"/>
135 <map title="Test Map">
136 <projection name="Unknown">
137 <parameter value="zone=26"/>
138 <parameter value="proj=utm"/>
139 <parameter value="ellps=clrk66"/>
140 </projection>
141 <layer title="My Layer" shapestore="D1"
142 fill="None" stroke="#000000" stroke_width="1" visible="%s"/>
143 </map>
144 </session>'''
145
146 expected_contents = expected_template % \
147 (os.path.join("..", "..", "Data", "iceland", "political.shp"),
148 "true")
149
150 self.compare_xml(written_contents, expected_contents)
151
152 self.validate_data(written_contents)
153
154 layer.SetVisible(False)
155 save_session(session, filename)
156
157 file = open(filename)
158 written_contents = file.read()
159 file.close()
160 expected_contents = expected_template % \
161 (os.path.join("..", "..", "Data", "iceland", "political.shp"),
162 "false")
163 self.compare_xml(written_contents, expected_contents)
164 self.validate_data(written_contents)
165
166 session.Destroy()
167
168 def testLayerProjection(self):
169 """Test saving layers with projections"""
170 # deliberately put an apersand in the title :)
171 session = Session("single map&layer")
172 proj = Projection(["zone=26", "proj=utm", "ellps=clrk66"])
173 map = Map("Test Map", projection = proj)
174 session.AddMap(map)
175 # use shapefile from the example data
176 shpfile = os.path.join(os.path.dirname(__file__),
177 os.pardir, "Data", "iceland", "political.shp")
178 layer = Layer("My Layer", session.OpenShapefile(shpfile))
179 proj = Projection(["proj=lcc", "ellps=clrk66"], "Layer Projection")
180 layer.SetProjection(proj)
181 map.AddLayer(layer)
182
183 filename = self.temp_file_name("save_layerproj.thuban")
184 save_session(session, filename)
185 session.Destroy()
186
187 file = open(filename)
188 written_contents = file.read()
189 file.close()
190 expected_contents = '''<?xml version="1.0" encoding="UTF-8"?>
191 <!DOCTYPE session SYSTEM "thuban-0.9.dtd">
192 <session title="single map&amp;layer"
193 xmlns="http://thuban.intevation.org/dtds/thuban-0.9-dev.dtd">
194 <fileshapesource id="D1" filename="%s" filetype="shapefile"/>
195 <map title="Test Map">
196 <projection name="Unknown">
197 <parameter value="zone=26"/>
198 <parameter value="proj=utm"/>
199 <parameter value="ellps=clrk66"/>
200 </projection>
201 <layer title="My Layer" shapestore="D1"
202 fill="None" stroke="#000000" stroke_width="1" visible="true">
203 <projection name="Layer Projection">
204 <parameter value="proj=lcc"/>
205 <parameter value="ellps=clrk66"/>
206 </projection>
207 </layer>
208 </map>
209 </session>''' % os.path.join("..", "..", "Data", "iceland",
210 "political.shp")
211 #print written_contents
212 #print "********************************************"
213 #print expected_contents
214 self.compare_xml(written_contents, expected_contents)
215
216 self.validate_data(written_contents)
217
218 def testRasterLayer(self):
219 # deliberately put an apersand in the title :)
220 session = Session("single map&layer")
221 map = Map("Test Map")
222 session.AddMap(map)
223 # use shapefile from the example data
224 imgfile = os.path.join(os.path.dirname(__file__),
225 os.pardir, "Data", "iceland", "island.tif")
226 layer = RasterLayer("My RasterLayer", imgfile)
227 map.AddLayer(layer)
228
229 filename = self.temp_file_name("%s.thuban" % self.id())
230 save_session(session, filename)
231 session.Destroy()
232
233 file = open(filename)
234 written_contents = file.read()
235 file.close()
236 expected_contents = '''<?xml version="1.0" encoding="UTF-8"?>
237 <!DOCTYPE session SYSTEM "thuban-0.9.dtd">
238 <session title="single map&amp;layer"
239 xmlns="http://thuban.intevation.org/dtds/thuban-0.9-dev.dtd">
240 <map title="Test Map">
241 <rasterlayer title="My RasterLayer" filename="%s"
242 visible="true">
243 </rasterlayer>
244 </map>
245 </session>''' % os.path.join(os.pardir, os.pardir, "Data", "iceland",
246 "island.tif")
247 #print written_contents
248 #print "********************************************"
249 #print expected_contents
250 self.compare_xml(written_contents, expected_contents)
251
252 self.validate_data(written_contents)
253
254 def testClassifiedLayer(self):
255 """Save a session with a single map with classifications"""
256 # deliberately put an apersand in the title :)
257 session = Session("Map with Classifications")
258 proj = Projection(["zone=26", "proj=utm", "ellps=clrk66"])
259 map = Map("Test Map", projection = proj)
260 session.AddMap(map)
261 # use shapefile from the example data
262 shpfile = os.path.join(os.path.dirname(__file__),
263 os.pardir, "Data", "iceland", "political.shp")
264 layer = Layer("My Layer", session.OpenShapefile(shpfile))
265 map.AddLayer(layer)
266 layer2 = Layer("My Layer", layer.ShapeStore())
267 map.AddLayer(layer2)
268
269 clazz = layer.GetClassification()
270
271 layer.SetClassificationColumn("AREA")
272
273 clazz.AppendGroup(ClassGroupSingleton(42, ClassGroupProperties(),
274 "single"))
275 clazz.AppendGroup(ClassGroupSingleton("text", ClassGroupProperties(),
276 "single-text"))
277
278 clazz.AppendGroup(ClassGroupRange((0, 42),
279 ClassGroupProperties(),
280 "range"))
281
282 range = ClassGroupRange(Range("[0;42]"))
283 range.SetProperties(ClassGroupProperties())
284 range.SetLabel("new-range")
285 clazz.AppendGroup(range)
286
287
288 clazz = layer2.GetClassification()
289 layer2.SetClassificationColumn("POPYCOUN")
290
291 # Classification with Latin 1 text
292 clazz.AppendGroup(ClassGroupSingleton('\xe4\xf6\xfc', # ae, oe, ue
293 ClassGroupProperties(),
294 '\xdcml\xe4uts')) # Uemlaeuts
295
296
297 filename = self.temp_file_name("%s.thuban" % self.id())
298 save_session(session, filename)
299
300 file = open(filename)
301 written_contents = file.read()
302 file.close()
303 expected_template = '''<?xml version="1.0" encoding="UTF-8"?>
304 <!DOCTYPE session SYSTEM "thuban-0.9.dtd">
305 <session title="Map with Classifications"
306 xmlns="http://thuban.intevation.org/dtds/thuban-0.9-dev.dtd">
307 <fileshapesource id="D1" filename="%s" filetype="shapefile"/>
308 <map title="Test Map">
309 <projection name="Unknown">
310 <parameter value="zone=26"/>
311 <parameter value="proj=utm"/>
312 <parameter value="ellps=clrk66"/>
313 </projection>
314 <layer title="My Layer" shapestore="D1"
315 fill="None" stroke="#000000" stroke_width="1" visible="%s">
316 <classification field="AREA" field_type="double">
317 <clnull label="">
318 <cldata fill="None" stroke="#000000" stroke_width="1"/>
319 </clnull>
320 <clpoint value="42" label="single">
321 <cldata fill="None" stroke="#000000" stroke_width="1"/>
322 </clpoint>
323 <clpoint value="text" label="single-text">
324 <cldata fill="None" stroke="#000000" stroke_width="1"/>
325 </clpoint>
326 <clrange range="[0;42[" label="range">
327 <cldata fill="None" stroke="#000000" stroke_width="1"/>
328 </clrange>
329 <clrange range="[0;42]" label="new-range">
330 <cldata fill="None" stroke="#000000" stroke_width="1"/>
331 </clrange>
332 </classification>
333 </layer>
334 <layer title="My Layer" shapestore="D1"
335 fill="None" stroke="#000000" stroke_width="1" visible="true">
336 <classification field="POPYCOUN" field_type="string">
337 <clnull label="">
338 <cldata fill="None" stroke="#000000" stroke_width="1"/>
339 </clnull>
340 <clpoint value="\xc3\xa4\xc3\xb6\xc3\xbc"
341 label="\xc3\x9cml\xc3\xa4uts">
342 <cldata fill="None" stroke="#000000" stroke_width="1"/>
343 </clpoint>
344 </classification>
345 </layer>
346 </map>
347 </session>'''
348
349 expected_contents = expected_template % \
350 (os.path.join("..", "..", "Data", "iceland", "political.shp"),
351 "true")
352
353 #print written_contents
354 #print "********************************************"
355 #print expected_contents
356 self.compare_xml(written_contents, expected_contents)
357
358 self.validate_data(written_contents)
359
360 session.Destroy()
361
362 def test_dbf_table(self):
363 """Test saving a session with a dbf table link"""
364 session = Session("a DBF Table session")
365 # use shapefile from the example data
366 dbffile = os.path.join(os.path.dirname(__file__),
367 os.pardir, "Data", "iceland", "political.dbf")
368 table = session.AddTable(DBFTable(dbffile))
369
370 filename = self.temp_file_name("save_singletable.thuban")
371 save_session(session, filename)
372
373 file = open(filename)
374 written_contents = file.read()
375 file.close()
376 expected_template = '''<?xml version="1.0" encoding="UTF-8"?>
377 <!DOCTYPE session SYSTEM "thuban-0.9.dtd">
378 <session title="a DBF Table session"
379 xmlns="http://thuban.intevation.org/dtds/thuban-0.9-dev.dtd">
380 <filetable id="D1" filename="%s" filetype="DBF" title="political"/>
381 </session>'''
382
383 expected_contents = (expected_template
384 % os.path.join(os.pardir, os.pardir, "Data",
385 "iceland", "political.dbf"))
386 self.compare_xml(written_contents, expected_contents)
387 self.validate_data(written_contents)
388
389 def test_joined_table(self):
390 """Test saving a session with joined table"""
391 # Create a simple table to use in the join
392 dbffile = self.temp_file_name("save_joinedtable.dbf")
393 dbf = dbflib.create(dbffile)
394 dbf.add_field("RDTYPE", dbflib.FTInteger, 10, 0)
395 dbf.add_field("TEXT", dbflib.FTString, 10, 0)
396 dbf.write_record(0, {'RDTYPE': 8, "TEXT": "foo"})
397 dbf.write_record(1, {'RDTYPE': 2, "TEXT": "bar"})
398 dbf.write_record(2, {'RDTYPE': 3, "TEXT": "baz"})
399 dbf.close()
400
401 # Create the session and a map
402 session = Session("A Joined Table session")
403 try:
404 map = Map("Test Map")
405 session.AddMap(map)
406
407 # Add the dbf file to the session
408 dbftable = session.AddTable(DBFTable(dbffile))
409
410 # Create a layer with the shapefile to use in the join
411 shpfile = os.path.join(os.path.abspath(os.path.dirname(__file__)),
412 os.pardir, "Data", "iceland",
413 "roads-line.shp")
414 layer = Layer("My Layer", session.OpenShapefile(shpfile))
415 map.AddLayer(layer)
416
417 # Do the join
418 store = layer.ShapeStore()
419 #for col in store.Table().Columns():
420 # print col.name
421 joined = TransientJoinedTable(session.TransientDB(),
422 store.Table(), "RDLNTYPE",
423 dbftable, "RDTYPE",
424 outer_join = True)
425 store = session.AddShapeStore(DerivedShapeStore(store, joined))
426 layer.SetShapeStore(store)
427
428 # Save the session
429 filename = self.temp_file_name("save_joinedtable.thuban")
430 save_session(session, filename)
431
432 # Read it back and compare
433 file = open(filename)
434 written_contents = file.read()
435 file.close()
436 expected_template = '''<?xml version="1.0" encoding="UTF-8"?>
437 <!DOCTYPE session SYSTEM "thuban-0.9.dtd">
438 <session title="A Joined Table session"
439 xmlns="http://thuban.intevation.org/dtds/thuban-0.9-dev.dtd">
440 <fileshapesource filename="%(shpfile)s"
441 filetype="shapefile" id="D142197204"/>
442 <filetable filename="%(dbffile)s"
443 title="save_joinedtable"
444 filetype="DBF" id="D141881756"/>
445 <jointable id="D142180284"
446 title="Join of roads-line and save_joinedtable"
447 leftcolumn="RDLNTYPE" left="D142197204"
448 rightcolumn="RDTYPE" right="D141881756"
449 jointype="LEFT OUTER" />
450 <derivedshapesource id="D141915644"
451 table="D142180284"
452 shapesource="D142197204"/>
453 <map title="Test Map">
454 <layer title="My Layer"
455 shapestore="D141915644" visible="true"
456 stroke="#000000" stroke_width="1" fill="None"/>
457 </map>
458 </session>'''
459
460 expected_contents = expected_template % {
461 "dbffile": relative_filename(self.temp_dir(), dbffile),
462 "shpfile": relative_filename(self.temp_dir(), shpfile)
463 }
464 self.compare_xml(written_contents, expected_contents)
465 self.validate_data(written_contents)
466 finally:
467 session.Destroy()
468 session = None
469
470
471 def test_save_postgis(self):
472 """Test saving a session with a postgis connection"""
473
474 class NonConnection(PostGISConnection):
475 """connection class that doesn't actually connect """
476 def connect(self):
477 pass
478
479 class NonConnectionStore(PostGISShapeStore):
480 """Shapestore that doesn't try to access the server"""
481 def _fetch_table_information(self):
482 pass
483
484 session = Session("A PostGIS Session")
485 try:
486 dbconn = NonConnection(dbname="plugh", host="xyzzy", port="42",
487 user="grue")
488 session.AddDBConnection(dbconn)
489 map = Map("Test Map")
490 session.AddMap(map)
491 store = NonConnectionStore(dbconn, "roads")
492 session.AddShapeStore(store)
493 layer = Layer("Roads to Nowhere", store)
494 map.AddLayer(layer)
495
496 # Save the session
497 filename = self.temp_file_name(self.id() + ".thuban")
498 save_session(session, filename)
499
500 # Read it back and compare
501 file = open(filename)
502 written = file.read()
503 file.close()
504 expected = '''<?xml version="1.0" encoding="UTF-8"?>
505 <!DOCTYPE session SYSTEM "thuban-0.9.dtd">
506 <session title="A PostGIS Session"
507 xmlns="http://thuban.intevation.org/dtds/thuban-0.9-dev.dtd">
508 <dbconnection id="DB"
509 dbtype="postgis" dbname="plugh"
510 host="xyzzy" port="42"
511 user="grue"/>
512 <dbshapesource id="roads" dbconn="DB" tablename="roads"/>
513 <map title="Test Map">
514 <layer title="Roads to Nowhere"
515 shapestore="roads" visible="true"
516 stroke="#000000" stroke_width="1" fill="None"/>
517 </map>
518 </session>'''
519 self.compare_xml(written, expected)
520 self.validate_data(written)
521 finally:
522 session.Destroy()
523
524
525 class MockDataStore:
526
527 """A very simple data store that only has dependencies"""
528
529 def __init__(self, name, *dependencies):
530 self.name = name
531 self.dependencies = dependencies
532
533 def __repr__(self):
534 return self.name
535
536 def Dependencies(self):
537 return self.dependencies
538
539
540 class TestStoreSort(unittest.TestCase):
541
542 def check_sort(self, containers, sorted):
543 """Check whether the list of data containers is sorted"""
544 # check whether sorted is in the right order
545 seen = {}
546 for container in sorted:
547 self.failIf(id(container) in seen,
548 "Container %r at least twice in %r" % (container,
549 sorted))
550 for dep in container.Dependencies():
551 self.assert_(id(dep) in seen,
552 "Dependency %r of %r not yet seen" % (dep,
553 container))
554 seen[id(container)] = 1
555 # check whether all of containers is in sorted
556 for container in containers:
557 self.assert_(id(container) in seen,
558 "Container %r in containers but not in sorted")
559 self.assertEquals(len(containers), len(sorted))
560
561 def test_sort_data_stores(self):
562 """Test Thuban.Model.save.sort_data_stores"""
563 d1 = MockDataStore("d1")
564 d2 = MockDataStore("d2")
565 d3 = MockDataStore("d3", d1)
566 d4 = MockDataStore("d4", d1, d3)
567
568 containers = [d4, d1, d2, d3]
569 self.check_sort(containers, sort_data_stores(containers))
570 containers = [d1, d3, d2, d4]
571 self.check_sort(containers, sort_data_stores(containers))
572
573
574
575 if __name__ == "__main__":
576 # Fake the __file__ global because it's needed by a test
577 import sys
578 __file__ = sys.argv[0]
579 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