/[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 1989 - (show annotations)
Fri Nov 28 12:00:54 2003 UTC (21 years, 3 months ago) by bh
Original Path: trunk/thuban/test/test_save.py
File MIME type: text/x-python
File size: 23700 byte(s)
Unify the filenames stored in .thuban files so that the .thuban
files are more platform independend

* Thuban/Model/save.py (unify_filename): New. Unify filenames so
that they can be used on both windows and unix
(SessionSaver.prepare_filename): New. Handle all filename
transformations for filenames stored in the thuban file
(SessionSaver.write_data_containers, SessionSaver.write_layer):
Use prepare_filename

* test/test_save.py (SaveSessionTest.testSingleLayer)
(SaveSessionTest.testLayerProjection)
(SaveSessionTest.testRasterLayer)
(SaveSessionTest.testClassifiedLayer)
(SaveSessionTest.test_dbf_table)
(SaveSessionTest.test_joined_table): Filenames are always stored
with slashes on all currently supported platforms so adapt all
tests to this

* test/test_load.py (LoadSessionTest.filenames): With the new
filename scheme the filenames in the tests should be
understandable on all currently supported platforms so we turn
this into an empty list because we don't have to normalize them
anymore

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-1.0-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 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-1.0.dtd">\n'
115 '<session title="empty session" '
116 'xmlns="http://thuban.intevation.org/dtds/thuban-1.0-dev.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(["proj=utm", "zone=27", "ellps=WGS84",
126 "datum=WGS84", "units=m"],
127 name = "WGS 84 / UTM zone 27N",
128 epsg = "32627")
129 map = Map("Test Map", projection = proj)
130 session.AddMap(map)
131 # use shapefile from the example data
132 shpfile = os.path.join(os.path.dirname(__file__),
133 os.pardir, "Data", "iceland", "political.shp")
134 layer = Layer("My Layer", session.OpenShapefile(shpfile))
135 map.AddLayer(layer)
136
137 filename = self.temp_file_name("save_singlemap.thuban")
138 save_session(session, filename)
139
140 file = open(filename)
141 written_contents = file.read()
142 file.close()
143 expected_template = '''<?xml version="1.0" encoding="UTF-8"?>
144 <!DOCTYPE session SYSTEM "thuban-1.0.dtd">
145 <session title="single map&amp;layer"
146 xmlns="http://thuban.intevation.org/dtds/thuban-1.0-dev.dtd">
147 <fileshapesource id="D1"
148 filename="../../Data/iceland/political.shp"
149 filetype="shapefile"/>
150 <map title="Test Map">
151 <projection epsg="32627" name="WGS 84 / UTM zone 27N">
152 <parameter value="proj=utm"/>
153 <parameter value="zone=27"/>
154 <parameter value="ellps=WGS84"/>
155 <parameter value="datum=WGS84"/>
156 <parameter value="units=m"/>
157 </projection>
158 <layer title="My Layer" shapestore="D1"
159 fill="None" stroke="#000000" stroke_width="1" visible="%s"/>
160 </map>
161 </session>'''
162
163 expected_contents = expected_template % "true"
164
165 self.compare_xml(written_contents, expected_contents)
166
167 self.validate_data(written_contents)
168
169 # Repeat with an invisible layer
170 layer.SetVisible(False)
171 save_session(session, filename)
172
173 file = open(filename)
174 written_contents = file.read()
175 file.close()
176 expected_contents = expected_template % "false"
177 self.compare_xml(written_contents, expected_contents)
178 self.validate_data(written_contents)
179
180 session.Destroy()
181
182 def testLayerProjection(self):
183 """Test saving layers with projections"""
184 # deliberately put an apersand in the title :)
185 session = self.session = Session("single map&layer")
186 proj = Projection(["zone=26", "proj=utm", "ellps=clrk66"])
187 map = Map("Test Map", projection = proj)
188 session.AddMap(map)
189 # use shapefile from the example data
190 shpfile = os.path.join(os.path.dirname(__file__),
191 os.pardir, "Data", "iceland", "political.shp")
192 layer = Layer("My Layer", session.OpenShapefile(shpfile))
193 proj = Projection(["proj=lcc", "ellps=clrk66",
194 "lat_1=0", "lat_2=20"],
195 "Layer Projection")
196 layer.SetProjection(proj)
197 map.AddLayer(layer)
198
199 filename = self.temp_file_name("save_layerproj.thuban")
200 save_session(session, filename)
201
202 file = open(filename)
203 written_contents = file.read()
204 file.close()
205 expected_contents = '''<?xml version="1.0" encoding="UTF-8"?>
206 <!DOCTYPE session SYSTEM "thuban-1.0.dtd">
207 <session title="single map&amp;layer"
208 xmlns="http://thuban.intevation.org/dtds/thuban-1.0-dev.dtd">
209 <fileshapesource id="D1"
210 filename="../../Data/iceland/political.shp"
211 filetype="shapefile"/>
212 <map title="Test Map">
213 <projection name="Unknown">
214 <parameter value="zone=26"/>
215 <parameter value="proj=utm"/>
216 <parameter value="ellps=clrk66"/>
217 </projection>
218 <layer title="My Layer" shapestore="D1"
219 fill="None" stroke="#000000" stroke_width="1" visible="true">
220 <projection name="Layer Projection">
221 <parameter value="proj=lcc"/>
222 <parameter value="ellps=clrk66"/>
223 <parameter value="lat_1=0"/>
224 <parameter value="lat_2=20"/>
225 </projection>
226 </layer>
227 </map>
228 </session>'''
229 #print written_contents
230 #print "********************************************"
231 #print expected_contents
232 self.compare_xml(written_contents, expected_contents)
233
234 self.validate_data(written_contents)
235
236 def testRasterLayer(self):
237 # deliberately put an apersand in the title :)
238 session = Session("single map&layer")
239 map = Map("Test Map")
240 session.AddMap(map)
241 # use shapefile from the example data
242 imgfile = os.path.join(os.path.dirname(__file__),
243 os.pardir, "Data", "iceland", "island.tif")
244 layer = RasterLayer("My RasterLayer", imgfile)
245 map.AddLayer(layer)
246
247 filename = self.temp_file_name("%s.thuban" % self.id())
248 save_session(session, filename)
249 session.Destroy()
250
251 file = open(filename)
252 written_contents = file.read()
253 file.close()
254 expected_contents = '''<?xml version="1.0" encoding="UTF-8"?>
255 <!DOCTYPE session SYSTEM "thuban-1.0.dtd">
256 <session title="single map&amp;layer"
257 xmlns="http://thuban.intevation.org/dtds/thuban-1.0-dev.dtd">
258 <map title="Test Map">
259 <rasterlayer title="My RasterLayer"
260 filename="../../Data/iceland/island.tif"
261 visible="true">
262 </rasterlayer>
263 </map>
264 </session>'''
265 #print written_contents
266 #print "********************************************"
267 #print expected_contents
268 self.compare_xml(written_contents, expected_contents)
269
270 self.validate_data(written_contents)
271
272 def testClassifiedLayer(self):
273 """Save a session with a single map with classifications"""
274 # deliberately put an apersand in the title :)
275 session = Session("Map with Classifications")
276 proj = Projection(["zone=26", "proj=utm", "ellps=clrk66"])
277 map = Map("Test Map", projection = proj)
278 session.AddMap(map)
279 # use shapefile from the example data
280 shpfile = os.path.join(os.path.dirname(__file__),
281 os.pardir, "Data", "iceland", "political.shp")
282 layer = Layer("My Layer", session.OpenShapefile(shpfile))
283 map.AddLayer(layer)
284 layer2 = Layer("My Layer", layer.ShapeStore())
285 map.AddLayer(layer2)
286
287 clazz = layer.GetClassification()
288
289 layer.SetClassificationColumn("AREA")
290
291 clazz.AppendGroup(ClassGroupSingleton(42, ClassGroupProperties(),
292 "single"))
293 clazz.AppendGroup(ClassGroupSingleton("text", ClassGroupProperties(),
294 "single-text"))
295
296 clazz.AppendGroup(ClassGroupRange((0, 42),
297 ClassGroupProperties(),
298 "range"))
299
300 range = ClassGroupRange(Range("[0;42]"))
301 range.SetProperties(ClassGroupProperties())
302 range.SetLabel("new-range")
303 clazz.AppendGroup(range)
304
305
306 clazz = layer2.GetClassification()
307 layer2.SetClassificationColumn("POPYCOUN")
308
309 # Classification with Latin 1 text
310 clazz.AppendGroup(ClassGroupSingleton('\xe4\xf6\xfc', # ae, oe, ue
311 ClassGroupProperties(),
312 '\xdcml\xe4uts')) # Uemlaeuts
313
314
315 filename = self.temp_file_name("%s.thuban" % self.id())
316 save_session(session, filename)
317
318 file = open(filename)
319 written_contents = file.read()
320 file.close()
321 expected_contents = '''<?xml version="1.0" encoding="UTF-8"?>
322 <!DOCTYPE session SYSTEM "thuban-1.0.dtd">
323 <session title="Map with Classifications"
324 xmlns="http://thuban.intevation.org/dtds/thuban-1.0-dev.dtd">
325 <fileshapesource id="D1"
326 filename="../../Data/iceland/political.shp"
327 filetype="shapefile"/>
328 <map title="Test Map">
329 <projection name="Unknown">
330 <parameter value="zone=26"/>
331 <parameter value="proj=utm"/>
332 <parameter value="ellps=clrk66"/>
333 </projection>
334 <layer title="My Layer" shapestore="D1"
335 fill="None" stroke="#000000" stroke_width="1" visible="true">
336 <classification field="AREA" field_type="double">
337 <clnull label="">
338 <cldata fill="None" stroke="#000000" stroke_width="1"/>
339 </clnull>
340 <clpoint value="42" label="single">
341 <cldata fill="None" stroke="#000000" stroke_width="1"/>
342 </clpoint>
343 <clpoint value="text" label="single-text">
344 <cldata fill="None" stroke="#000000" stroke_width="1"/>
345 </clpoint>
346 <clrange range="[0;42[" label="range">
347 <cldata fill="None" stroke="#000000" stroke_width="1"/>
348 </clrange>
349 <clrange range="[0;42]" label="new-range">
350 <cldata fill="None" stroke="#000000" stroke_width="1"/>
351 </clrange>
352 </classification>
353 </layer>
354 <layer title="My Layer" shapestore="D1"
355 fill="None" stroke="#000000" stroke_width="1" visible="true">
356 <classification field="POPYCOUN" field_type="string">
357 <clnull label="">
358 <cldata fill="None" stroke="#000000" stroke_width="1"/>
359 </clnull>
360 <clpoint value="\xc3\xa4\xc3\xb6\xc3\xbc"
361 label="\xc3\x9cml\xc3\xa4uts">
362 <cldata fill="None" stroke="#000000" stroke_width="1"/>
363 </clpoint>
364 </classification>
365 </layer>
366 </map>
367 </session>'''
368
369 #print written_contents
370 #print "********************************************"
371 #print expected_contents
372 self.compare_xml(written_contents, expected_contents)
373
374 self.validate_data(written_contents)
375
376 session.Destroy()
377
378 def test_dbf_table(self):
379 """Test saving a session with a dbf table link"""
380 session = self.session = Session("a DBF Table session")
381 # use shapefile from the example data
382 dbffile = os.path.join(os.path.dirname(__file__),
383 os.pardir, "Data", "iceland", "political.dbf")
384 table = session.AddTable(DBFTable(dbffile))
385
386 filename = self.temp_file_name("save_singletable.thuban")
387 save_session(session, filename)
388
389 file = open(filename)
390 written_contents = file.read()
391 file.close()
392 expected_contents = '''<?xml version="1.0" encoding="UTF-8"?>
393 <!DOCTYPE session SYSTEM "thuban-1.0.dtd">
394 <session title="a DBF Table session"
395 xmlns="http://thuban.intevation.org/dtds/thuban-1.0-dev.dtd">
396 <filetable id="D1" filename="../../Data/iceland/political.dbf"
397 filetype="DBF" title="political"/>
398 </session>'''
399
400 self.compare_xml(written_contents, expected_contents)
401 self.validate_data(written_contents)
402
403 def test_joined_table(self):
404 """Test saving a session with joined table"""
405 # Create a simple table to use in the join
406 dbffile = self.temp_file_name("save_joinedtable.dbf")
407 dbf = dbflib.create(dbffile)
408 dbf.add_field("RDTYPE", dbflib.FTInteger, 10, 0)
409 dbf.add_field("TEXT", dbflib.FTString, 10, 0)
410 dbf.write_record(0, {'RDTYPE': 8, "TEXT": "foo"})
411 dbf.write_record(1, {'RDTYPE': 2, "TEXT": "bar"})
412 dbf.write_record(2, {'RDTYPE': 3, "TEXT": "baz"})
413 dbf.close()
414
415 # Create the session and a map
416 session = Session("A Joined Table session")
417 try:
418 map = Map("Test Map")
419 session.AddMap(map)
420
421 # Add the dbf file to the session
422 dbftable = session.AddTable(DBFTable(dbffile))
423
424 # Create a layer with the shapefile to use in the join
425 shpfile = os.path.join(os.path.abspath(os.path.dirname(__file__)),
426 os.pardir, "Data", "iceland",
427 "roads-line.shp")
428 layer = Layer("My Layer", session.OpenShapefile(shpfile))
429 map.AddLayer(layer)
430
431 # Do the join
432 store = layer.ShapeStore()
433 #for col in store.Table().Columns():
434 # print col.name
435 joined = TransientJoinedTable(session.TransientDB(),
436 store.Table(), "RDLNTYPE",
437 dbftable, "RDTYPE",
438 outer_join = True)
439 store = session.AddShapeStore(DerivedShapeStore(store, joined))
440 layer.SetShapeStore(store)
441
442 # Save the session
443 filename = self.temp_file_name("save_joinedtable.thuban")
444 save_session(session, filename)
445
446 # Read it back and compare
447 file = open(filename)
448 written_contents = file.read()
449 file.close()
450 expected_contents = '''<?xml version="1.0" encoding="UTF-8"?>
451 <!DOCTYPE session SYSTEM "thuban-1.0.dtd">
452 <session title="A Joined Table session"
453 xmlns="http://thuban.intevation.org/dtds/thuban-1.0-dev.dtd">
454 <fileshapesource filename="../../Data/iceland/roads-line.shp"
455 filetype="shapefile" id="D142197204"/>
456 <filetable filename="save_joinedtable.dbf"
457 title="save_joinedtable"
458 filetype="DBF" id="D141881756"/>
459 <jointable id="D142180284"
460 title="Join of roads-line and save_joinedtable"
461 leftcolumn="RDLNTYPE" left="D142197204"
462 rightcolumn="RDTYPE" right="D141881756"
463 jointype="LEFT OUTER" />
464 <derivedshapesource id="D141915644"
465 table="D142180284"
466 shapesource="D142197204"/>
467 <map title="Test Map">
468 <layer title="My Layer"
469 shapestore="D141915644" visible="true"
470 stroke="#000000" stroke_width="1" fill="None"/>
471 </map>
472 </session>'''
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-1.0.dtd">
516 <session title="A PostGIS Session"
517 xmlns="http://thuban.intevation.org/dtds/thuban-1.0-dev.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