/[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 1677 - (show annotations)
Thu Aug 28 13:34:28 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: 23805 byte(s)
(SaveSessionTest.tearDown): New. Provide a
reliable way to destroy the sessions created in the test cases
(SaveSessionTest.test_dbf_table): Bind the session to self.session
so that it gets destroyed properly

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