1 """
2 Test suite for the StyledLayerDescriptor python library.
3
4 License
5 =======
6 Copyright 2011 David Zwarg <U{dzwarg@azavea.com}>
7
8 Licensed under the Apache License, Version 2.0 (the "License");
9 you may not use this file except in compliance with the License.
10 You may obtain a copy of the License at
11
12 U{http://www.apache.org/licenses/LICENSE-2.0}
13
14 Unless required by applicable law or agreed to in writing, software
15 distributed under the License is distributed on an "AS IS" BASIS,
16 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 See the License for the specific language governing permissions and
18 limitations under the License.
19
20 @author: David Zwarg
21 @contact: dzwarg@azavea.com
22 @copyright: 2011, Azavea
23 @license: Apache 2.0
24 @version: 1.0.6
25 """
26 from sld import *
27 import unittest, copy
28 from lxml import etree
29
31 """
32 All tests for django-sld are contained in this TestCase class.
33 """
34
35 _sld0 = None
36 """Store a parsed SLD, with known styles and structure"""
37
38 _sld1 = None
39 """Store a dynamically generated SLD"""
40
48
49
51 """
52 Test an empty constructor, and make sure the SLD is valid.
53 """
54 sld = StyledLayerDescriptor()
55
56 self.assertTrue( 'sld' in sld._nsmap )
57
58 expected = """<sld:StyledLayerDescriptor xmlns:sld="http://www.opengis.net/sld" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ogc="http://www.opengis.net/ogc" version="1.0.0"/>"""
59 actual = etree.tostring(sld._node, with_tail=False)
60 self.assertEqual(actual, expected)
61
62 sld.normalize()
63
64 self.assertTrue(sld.validate())
65
66
68 """
69 Test a constructor on a bogus file.
70 """
71 try:
72 sld = StyledLayerDescriptor('junk')
73 self.fail("Error")
74 except:
75
76 pass
77
79 """
80 Test the SLD version on the root element.
81 """
82 self.assertEqual( self._sld0.version, "1.0.0" )
83
85 """
86 Test the namespace on the root element.
87 """
88 self.assertEqual( self._sld0.xmlns, 'http://www.opengis.net/sld' )
89
91 """
92 Test the object type of the NamedLayer property.
93 """
94 self.assertTrue( isinstance(self._sld0.NamedLayer, NamedLayer), "NamedLayer property is not the proper class.")
95
109
111 """
112 Test the proper parsing of the name of the NamedLayer.
113 """
114 expected = 'poptot'
115 self.assertEqual( self._sld0.NamedLayer.Name, expected, "NamedLayer was named '%s', not '%s'" % (self._sld0.NamedLayer.Name, expected,))
116
118 """
119 Test the object type of the UserStyle property.
120 """
121 self.assertTrue( isinstance(self._sld0.NamedLayer.UserStyle, UserStyle), "UserStyle property is not the proper class.")
122
139
141 """
142 Test the parsing of the UserStyle Title, and proper rendering.
143 """
144 sld = copy.deepcopy(self._sld0)
145 us = sld.NamedLayer.UserStyle
146 expected = 'Population'
147 self.assertEqual( us.Title, expected, "UserStyle Title was '%s', not '%s'" % (us.Title, expected,))
148
149 expected = 'Consternation'
150 us.Title = expected
151 self.assertEqual( us.Title, expected, "UserStyle Title was '%s', not '%s'" % (us.Title, expected,))
152
153 us._node.remove(us._node[2])
154
155 expected = """<UserStyle xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
156 <Title>%s</Title>
157 <Abstract>A grayscale style showing the population numbers in a given geounit.</Abstract>
158 </UserStyle>""" % expected
159 actual = etree.tostring(us._node, with_tail=False)
160 self.assertEqual( len(actual), len(expected))
161 self.assertEqual( actual, expected, "UserStyle was not serialized correctly.\n%s" % actual )
162
163 sld.normalize()
164 self.assertFalse(sld.validate())
165
167 """
168 Test the construction of the UserStyle Title, and proper rendering.
169 """
170 sld = copy.deepcopy(self._sld1)
171 sld.create_namedlayer('test named layer')
172 sld.NamedLayer.create_userstyle()
173
174 us = sld.NamedLayer.UserStyle
175 self.assertTrue( us.Title is None, "UserStyle Title was not None")
176
177 expected = 'Consternation'
178 us.Title = expected
179 self.assertEqual( us.Title, expected, "UserStyle Title was '%s', not '%s'" % (us.Title, expected,))
180
181 expected = """<sld:UserStyle xmlns:sld="http://www.opengis.net/sld" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ogc="http://www.opengis.net/ogc"><sld:Title>%s</sld:Title></sld:UserStyle>""" % expected
182 actual = etree.tostring(us._node, with_tail=False)
183 self.assertEqual( len(actual), len(expected))
184 self.assertEqual( actual, expected, "UserStyle was not serialized correctly.\n%s" % actual )
185
186 sld.normalize()
187 self.assertFalse(sld.validate())
188
190 """
191 Test the parsing of the UserStyle Abstract, and proper rendering.
192 """
193 sld = copy.deepcopy(self._sld0)
194 us = sld.NamedLayer.UserStyle
195 expected = 'A grayscale style showing the population numbers in a given geounit.'
196 self.assertEqual( us.Abstract, expected, "UserStyle Abstract was '%s', not '%s'" % (us.Abstract, expected,))
197
198 expected = 'Something completely different'
199 us.Abstract = expected
200 self.assertEqual( us.Abstract, expected, "UserStyle Abstract was '%s', not '%s'" % (us.Abstract, expected,))
201
202 us._node.remove(us._node[2])
203
204 expected = """<UserStyle xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
205 <Title>Population</Title>
206 <Abstract>%s</Abstract>
207 </UserStyle>""" % expected
208 actual = etree.tostring(us._node, with_tail=False)
209 self.assertEqual( len(actual), len(expected) )
210 self.assertEqual( actual, expected, "UserStyle was not serialized correctly.\n%s" % actual )
211
212 sld.normalize()
213 self.assertFalse(sld.validate())
214
216 """
217 Test the construction of the UserStyle Abstract, and proper rendering.
218 """
219 sld = copy.deepcopy(self._sld1)
220 sld.create_namedlayer('test named layer')
221 sld.NamedLayer.create_userstyle()
222
223 us = sld.NamedLayer.UserStyle
224 self.assertTrue( us.Abstract is None, "UserStyle Abstract was not None")
225
226 expected = 'Something completely different'
227 us.Abstract = expected
228 self.assertEqual( us.Abstract, expected, "UserStyle Abstract was '%s', not '%s'" % (us.Abstract, expected,))
229
230 expected = """<sld:UserStyle xmlns:sld="http://www.opengis.net/sld" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ogc="http://www.opengis.net/ogc"><sld:Abstract>%s</sld:Abstract></sld:UserStyle>""" % expected
231 actual = etree.tostring(us._node, with_tail=False)
232 self.assertEqual( len(actual), len(expected) )
233 self.assertEqual( actual, expected, "UserStyle was not serialized correctly.\n%s" % actual )
234
235 sld.normalize()
236 self.assertFalse(sld.validate())
237
243
260
262 """
263 Test the parsing of the Rules property.
264 """
265 rules = self._sld0.NamedLayer.UserStyle.FeatureTypeStyle.Rules
266 self.assertEqual( len(rules), 6 )
267 self.assertTrue( isinstance(rules[0], Rule), "Rule item in list is not the proper class." )
268
288
290 """
291 Test the parsing of the individual Rule properties.
292 """
293 sld = copy.deepcopy(self._sld0)
294 rule = sld.NamedLayer.UserStyle.FeatureTypeStyle.Rules[0]
295
296 expected = "> 880"
297 self.assertEqual( rule.Title, expected )
298
299 expected = "> 999"
300 rule.Title = expected
301 self.assertEqual( rule.Title, expected )
302
303 expected = """<Rule xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
304 <Title>> 999</Title>
305 <ogc:Filter>
306 <ogc:PropertyIsGreaterThanOrEqualTo>
307 <ogc:PropertyName>number</ogc:PropertyName>
308 <ogc:Literal>880</ogc:Literal>
309 </ogc:PropertyIsGreaterThanOrEqualTo>
310 </ogc:Filter>
311 <PolygonSymbolizer>
312 <Fill>
313 <CssParameter name="fill">#252525</CssParameter>
314 </Fill>
315 </PolygonSymbolizer>
316 </Rule>"""
317 actual = etree.tostring(rule._node, with_tail=False)
318 self.assertEqual( actual, expected, actual )
319
320 sld.normalize()
321 self.assertTrue(sld.validate())
322
345
347 """
348 Test the parsing of the Filter property.
349 """
350 sld = copy.deepcopy(self._sld0)
351 rule = sld.NamedLayer.UserStyle.FeatureTypeStyle.Rules[0]
352
353 self.assertFalse( rule.Filter.PropertyIsGreaterThanOrEqualTo is None )
354
355 self.assertEqual( rule.Filter.PropertyIsGreaterThanOrEqualTo.PropertyName, 'number' )
356 self.assertEqual( rule.Filter.PropertyIsGreaterThanOrEqualTo.Literal, '880' )
357
358 sld.normalize()
359 self.assertTrue(sld.validate())
360
375
377 """
378 Test the construction of an equality filter.
379 """
380 sld = copy.deepcopy(self._sld1)
381 namedlayer = sld.create_namedlayer('test named layer')
382 userstyle = namedlayer.create_userstyle()
383 featuretypestyle = userstyle.create_featuretypestyle()
384 rule = featuretypestyle.create_rule('test rule', PointSymbolizer)
385 rfilter = rule.create_filter('valueA', '==', '5000')
386
387 self.assertTrue( rfilter.PropertyIsNotEqualTo is None )
388 self.assertTrue( rfilter.PropertyIsLessThan is None )
389 self.assertTrue( rfilter.PropertyIsLessThanOrEqualTo is None )
390 self.assertTrue( rfilter.PropertyIsGreaterThan is None )
391 self.assertTrue( rfilter.PropertyIsGreaterThanOrEqualTo is None )
392 self.assertTrue( rfilter.PropertyIsLike is None )
393 self.assertFalse( rfilter.PropertyIsEqualTo is None )
394 self.assertEqual( rfilter.PropertyIsEqualTo.PropertyName, 'valueA' )
395 self.assertEqual( rfilter.PropertyIsEqualTo.Literal, '5000' )
396
397 sld.normalize()
398 self.assertTrue(sld.validate())
399
401 """
402 Test the construction of a less-than-or-equal Filter.
403 """
404 sld = copy.deepcopy(self._sld1)
405 namedlayer = sld.create_namedlayer('test named layer')
406 userstyle = namedlayer.create_userstyle()
407 featuretypestyle = userstyle.create_featuretypestyle()
408 rule = featuretypestyle.create_rule('test rule', PointSymbolizer)
409 rfilter = rule.create_filter('valueB', '<=', '5000')
410
411 self.assertTrue( rfilter.PropertyIsEqualTo is None )
412 self.assertTrue( rfilter.PropertyIsNotEqualTo is None )
413 self.assertTrue( rfilter.PropertyIsLessThan is None )
414 self.assertTrue( rfilter.PropertyIsGreaterThan is None )
415 self.assertTrue( rfilter.PropertyIsGreaterThanOrEqualTo is None )
416 self.assertTrue( rfilter.PropertyIsLike is None )
417 self.assertFalse( rfilter.PropertyIsLessThanOrEqualTo is None )
418 self.assertEqual( rfilter.PropertyIsLessThanOrEqualTo.PropertyName, 'valueB' )
419 self.assertEqual( rfilter.PropertyIsLessThanOrEqualTo.Literal, '5000' )
420
421 sld.normalize()
422 self.assertTrue(sld.validate())
423
425 """
426 Test the construction of a less-than Filter.
427 """
428 sld = copy.deepcopy(self._sld1)
429 namedlayer = sld.create_namedlayer('test named layer')
430 userstyle = namedlayer.create_userstyle()
431 featuretypestyle = userstyle.create_featuretypestyle()
432 rule = featuretypestyle.create_rule('test rule', PointSymbolizer)
433 rfilter = rule.create_filter('valueC', '<', '500')
434
435 self.assertTrue( rfilter.PropertyIsEqualTo is None )
436 self.assertTrue( rfilter.PropertyIsLessThanOrEqualTo is None )
437 self.assertTrue( rfilter.PropertyIsNotEqualTo is None )
438 self.assertTrue( rfilter.PropertyIsGreaterThan is None )
439 self.assertTrue( rfilter.PropertyIsGreaterThanOrEqualTo is None )
440 self.assertTrue( rfilter.PropertyIsLike is None )
441 self.assertFalse( rfilter.PropertyIsLessThan is None )
442 self.assertEqual( rfilter.PropertyIsLessThan.PropertyName, 'valueC' )
443 self.assertEqual( rfilter.PropertyIsLessThan.Literal, '500' )
444
445 sld.normalize()
446 self.assertTrue(sld.validate())
447
449 """
450 Test the construction of a greater-than-or-equal Filter.
451 """
452 sld = copy.deepcopy(self._sld1)
453 namedlayer = sld.create_namedlayer('test named layer')
454 userstyle = namedlayer.create_userstyle()
455 featuretypestyle = userstyle.create_featuretypestyle()
456 rule = featuretypestyle.create_rule('test rule', PointSymbolizer)
457 rfilter = rule.create_filter('valueD', '>=', '100')
458
459 self.assertTrue( rfilter.PropertyIsEqualTo is None )
460 self.assertTrue( rfilter.PropertyIsLessThanOrEqualTo is None )
461 self.assertTrue( rfilter.PropertyIsLessThan is None )
462 self.assertTrue( rfilter.PropertyIsNotEqualTo is None )
463 self.assertTrue( rfilter.PropertyIsGreaterThan is None )
464 self.assertTrue( rfilter.PropertyIsLike is None )
465 self.assertFalse( rfilter.PropertyIsGreaterThanOrEqualTo is None )
466 self.assertEqual( rfilter.PropertyIsGreaterThanOrEqualTo.PropertyName, 'valueD' )
467 self.assertEqual( rfilter.PropertyIsGreaterThanOrEqualTo.Literal, '100' )
468
469 sld.normalize()
470 self.assertTrue(sld.validate())
471
473 """
474 Test the construction of a greater-than Filter.
475 """
476 sld = copy.deepcopy(self._sld1)
477 namedlayer = sld.create_namedlayer('test named layer')
478 userstyle = namedlayer.create_userstyle()
479 featuretypestyle = userstyle.create_featuretypestyle()
480 rule = featuretypestyle.create_rule('test rule', PointSymbolizer)
481 rfilter = rule.create_filter('valueE', '>', '10')
482
483 self.assertTrue( rfilter.PropertyIsEqualTo is None )
484 self.assertTrue( rfilter.PropertyIsLessThanOrEqualTo is None )
485 self.assertTrue( rfilter.PropertyIsLessThan is None )
486 self.assertTrue( rfilter.PropertyIsGreaterThanOrEqualTo is None )
487 self.assertTrue( rfilter.PropertyIsNotEqualTo is None )
488 self.assertTrue( rfilter.PropertyIsLike is None )
489 self.assertFalse( rfilter.PropertyIsGreaterThan is None )
490 self.assertEqual( rfilter.PropertyIsGreaterThan.PropertyName, 'valueE' )
491 self.assertEqual( rfilter.PropertyIsGreaterThan.Literal, '10' )
492
493 sld.normalize()
494 self.assertTrue(sld.validate())
495
497 """
498 Test the construction of an inequality Filter.
499 """
500 sld = copy.deepcopy(self._sld1)
501 namedlayer = sld.create_namedlayer('test named layer')
502 userstyle = namedlayer.create_userstyle()
503 featuretypestyle = userstyle.create_featuretypestyle()
504 rule = featuretypestyle.create_rule('test rule', PointSymbolizer)
505 rfilter = rule.create_filter('valueF', '!=', '0.01')
506
507 self.assertTrue( rfilter.PropertyIsEqualTo is None )
508 self.assertTrue( rfilter.PropertyIsLessThan is None )
509 self.assertTrue( rfilter.PropertyIsLessThanOrEqualTo is None )
510 self.assertTrue( rfilter.PropertyIsGreaterThan is None )
511 self.assertTrue( rfilter.PropertyIsGreaterThanOrEqualTo is None )
512 self.assertTrue( rfilter.PropertyIsLike is None )
513 self.assertFalse( rfilter.PropertyIsNotEqualTo is None )
514 self.assertEqual( rfilter.PropertyIsNotEqualTo.PropertyName, 'valueF' )
515 self.assertEqual( rfilter.PropertyIsNotEqualTo.Literal, '0.01' )
516
517 sld.normalize()
518 self.assertTrue(sld.validate())
519
521 """
522 Test the construction of a logical-and Filter.
523 """
524 sld = copy.deepcopy(self._sld1)
525 namedlayer = sld.create_namedlayer('test named layer')
526 userstyle = namedlayer.create_userstyle()
527 featuretypestyle = userstyle.create_featuretypestyle()
528 rule = featuretypestyle.create_rule('test rule', PointSymbolizer)
529
530 filter1 = Filter(rule)
531 filter1.PropertyIsGreaterThan = PropertyCriterion(filter1, 'PropertyIsGreaterThan')
532 filter1.PropertyIsGreaterThan.PropertyName = 'number'
533 filter1.PropertyIsGreaterThan.Literal = '-10'
534
535 filter2 = Filter(rule)
536 filter2.PropertyIsLessThanOrEqualTo = PropertyCriterion(filter2, 'PropertyIsLessThanOrEqualTo')
537 filter2.PropertyIsLessThanOrEqualTo.PropertyName = 'number'
538 filter2.PropertyIsLessThanOrEqualTo.Literal = '10'
539
540 rule.Filter = filter1 + filter2
541
542 expected = """<sld:Rule xmlns:sld="http://www.opengis.net/sld" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ogc="http://www.opengis.net/ogc"><sld:Title>test rule</sld:Title><sld:PointSymbolizer><sld:Graphic><sld:Mark><sld:WellKnownName>square</sld:WellKnownName><sld:Fill><sld:CssParameter name="fill">#ff0000</sld:CssParameter></sld:Fill></sld:Mark></sld:Graphic></sld:PointSymbolizer><ogc:Filter><ogc:And><ogc:PropertyIsGreaterThan><ogc:PropertyName>number</ogc:PropertyName><ogc:Literal>-10</ogc:Literal></ogc:PropertyIsGreaterThan><ogc:PropertyIsLessThanOrEqualTo><ogc:PropertyName>number</ogc:PropertyName><ogc:Literal>10</ogc:Literal></ogc:PropertyIsLessThanOrEqualTo></ogc:And></ogc:Filter></sld:Rule>"""
543 actual = etree.tostring(rule._node, with_tail=False)
544 self.assertEqual(actual, expected)
545
546 sld.normalize()
547 self.assertTrue(sld.validate())
548
550 """
551 Test the construction of a logical-or Filter.
552 """
553 sld = copy.deepcopy(self._sld1)
554 namedlayer = sld.create_namedlayer('test named layer')
555 userstyle = namedlayer.create_userstyle()
556 featuretypestyle = userstyle.create_featuretypestyle()
557 rule = featuretypestyle.create_rule('test rule', PointSymbolizer)
558
559 filter1 = Filter(rule)
560 filter1.PropertyIsGreaterThan = PropertyCriterion(filter1, 'PropertyIsGreaterThan')
561 filter1.PropertyIsGreaterThan.PropertyName = 'number'
562 filter1.PropertyIsGreaterThan.Literal = '10'
563
564 filter2 = Filter(rule)
565 filter2.PropertyIsLessThan= PropertyCriterion(filter2, 'PropertyIsLessThan')
566 filter2.PropertyIsLessThan.PropertyName = 'number'
567 filter2.PropertyIsLessThan.Literal = '-10'
568
569 rule.Filter = filter1 | filter2
570
571 expected = """<sld:Rule xmlns:sld="http://www.opengis.net/sld" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ogc="http://www.opengis.net/ogc"><sld:Title>test rule</sld:Title><sld:PointSymbolizer><sld:Graphic><sld:Mark><sld:WellKnownName>square</sld:WellKnownName><sld:Fill><sld:CssParameter name="fill">#ff0000</sld:CssParameter></sld:Fill></sld:Mark></sld:Graphic></sld:PointSymbolizer><ogc:Filter><ogc:Or><ogc:PropertyIsGreaterThan><ogc:PropertyName>number</ogc:PropertyName><ogc:Literal>10</ogc:Literal></ogc:PropertyIsGreaterThan><ogc:PropertyIsLessThan><ogc:PropertyName>number</ogc:PropertyName><ogc:Literal>-10</ogc:Literal></ogc:PropertyIsLessThan></ogc:Or></ogc:Filter></sld:Rule>"""
572 actual = etree.tostring(rule._node, with_tail=False)
573 self.assertEqual(actual, expected)
574
575 sld.normalize()
576 self.assertTrue(sld.validate())
577
579 """
580 Test the construction of a logical-and combined with a logical-or Filter.
581 """
582 sld = copy.deepcopy(self._sld1)
583 namedlayer = sld.create_namedlayer('test named layer')
584 userstyle = namedlayer.create_userstyle()
585 featuretypestyle = userstyle.create_featuretypestyle()
586 rule = featuretypestyle.create_rule('test rule', PointSymbolizer)
587
588 filter1 = Filter(rule)
589 filter1.PropertyIsGreaterThan = PropertyCriterion(filter1, 'PropertyIsGreaterThan')
590 filter1.PropertyIsGreaterThan.PropertyName = 'number'
591 filter1.PropertyIsGreaterThan.Literal = '10'
592
593 filter2 = Filter(rule)
594 filter2.PropertyIsLessThan = PropertyCriterion(filter2, 'PropertyIsLessThan')
595 filter2.PropertyIsLessThan.PropertyName = 'number'
596 filter2.PropertyIsLessThan.Literal = '-10'
597
598 filter3 = Filter(rule)
599 filter3.PropertyIsEqualTo = PropertyCriterion(filter3, 'PropertyIsEqualTo')
600 filter3.PropertyIsEqualTo.PropertyName = 'value'
601 filter3.PropertyIsEqualTo.Literal = 'yes'
602
603 rule.Filter = filter1 + (filter2 | filter3)
604
605 expected = """<sld:Rule xmlns:sld="http://www.opengis.net/sld" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ogc="http://www.opengis.net/ogc"><sld:Title>test rule</sld:Title><sld:PointSymbolizer><sld:Graphic><sld:Mark><sld:WellKnownName>square</sld:WellKnownName><sld:Fill><sld:CssParameter name="fill">#ff0000</sld:CssParameter></sld:Fill></sld:Mark></sld:Graphic></sld:PointSymbolizer><ogc:Filter><ogc:And><ogc:PropertyIsGreaterThan><ogc:PropertyName>number</ogc:PropertyName><ogc:Literal>10</ogc:Literal></ogc:PropertyIsGreaterThan><ogc:Or><ogc:PropertyIsLessThan><ogc:PropertyName>number</ogc:PropertyName><ogc:Literal>-10</ogc:Literal></ogc:PropertyIsLessThan><ogc:PropertyIsEqualTo><ogc:PropertyName>value</ogc:PropertyName><ogc:Literal>yes</ogc:Literal></ogc:PropertyIsEqualTo></ogc:Or></ogc:And></ogc:Filter></sld:Rule>"""
606 actual = etree.tostring(rule._node, with_tail=False, pretty_print=False)
607 self.assertEqual(actual, expected)
608
609 sld.normalize()
610 self.assertTrue(sld.validate())
611
623
641
657
675
689
709
710 if __name__ == '__main__':
711 unittest.main()
712