1""" 2 3 dexml.test: testcases for dexml module. 4 5""" 6 7import sys 8import os 9import os.path 10import unittest 11import doctest 12from xml.dom import minidom 13from io import StringIO 14 15import dexml2 16from dexml2 import fields 17 18 19def b(raw): 20 """Compatability wrapper for b"string" syntax.""" 21 return raw.encode("ascii") 22 23 24def model_fields_equal(m1,m2): 25 """Check for equality by comparing model fields.""" 26 for nm in m1.__class__._fields: 27 v1 = getattr(m1,nm.field_name) 28 v2 = getattr(m2,nm.field_name) 29 if isinstance(v1, dexml2.Model): 30 if not model_fields_equal(v1,v2): 31 return False 32 elif v1 != v2: 33 return False 34 return True 35 36 37 38class TestDexmlDocstring(unittest.TestCase): 39 40 def test_docstring(self): 41 """Test dexml docstrings 42 43 We don't do this on python3 because of the many small ways in 44 which the output has changed in that version. 45 """ 46 if sys.version_info < (3,): 47 assert doctest.testmod(dexml2)[0] == 0 48 49 def test_readme_matches_docstring(self): 50 """Ensure that the README is in sync with the docstring. 51 52 This test should always pass; if the README is out of sync it just 53 updates it with the contents of dexml.__doc__. 54 """ 55 dirname = os.path.dirname 56 readme = os.path.join(dirname(dirname(__file__)),"README.rst") 57 if not os.path.isfile(readme): 58 f = open(readme,"wb") 59 f.write(dexml2.__doc__.encode()) 60 f.close() 61 else: 62 f = open(readme,"rb") 63 if f.read() != dexml2.__doc__: 64 f.close() 65 f = open(readme,"wb") 66 f.write(dexml2.__doc__.encode()) 67 f.close() 68 69 70 71class TestDexml(unittest.TestCase): 72 73 74 def test_base(self): 75 """Test operation of a dexml.Model class with no fields.""" 76 class hello(dexml2.Model): 77 pass 78 79 h = hello.parse("<hello />") 80 self.assertTrue(h) 81 82 h = hello.parse("<hello>\n</hello>") 83 self.assertTrue(h) 84 85 h = hello.parse("<hello>world</hello>") 86 self.assertTrue(h) 87 88 d = minidom.parseString("<hello>world</hello>") 89 h = hello.parse(d) 90 self.assertTrue(h) 91 92 self.assertRaises(dexml2.ParseError, hello.parse, "<Hello />") 93 self.assertRaises(dexml2.ParseError, hello.parse, "<hllo />") 94 self.assertRaises(dexml2.ParseError, hello.parse, "<hello xmlns='T:' />") 95 96 hello.meta.ignore_unknown_elements = False 97 self.assertRaises(dexml2.ParseError, hello.parse, "<hello>world</hello>") 98 hello.meta.ignore_unknown_elements = True 99 100 h = hello() 101 self.assertEqual(h.render(),'<?xml version="1.0" ?><hello />') 102 self.assertEqual(h.render(fragment=True),"<hello />") 103 self.assertEqual(h.render(pretty=True), '<?xml version="1.0" ?>\n<hello/>\n') 104 self.assertEqual(h.render(fragment=True, pretty=True), "<hello/>\n") 105 self.assertEqual(h.render(encoding="utf8"),b('<?xml version="1.0" encoding="utf8" ?><hello />')) 106 self.assertEqual(h.render(encoding="utf8", pretty=True), b('<?xml version="1.0" encoding="utf8" ?>\n<hello/>\n')) 107 self.assertEqual(h.render(encoding="utf8",fragment=True),b("<hello />")) 108 self.assertEqual(h.render(encoding="utf8", fragment=True, pretty=True), b("<hello/>\n")) 109 110 self.assertEqual(h.render(),"".join(h.irender())) 111 self.assertEqual(h.render(fragment=True),"".join(h.irender(fragment=True))) 112 self.assertEqual(h.render(encoding="utf8"),b("").join(h.irender(encoding="utf8"))) 113 self.assertEqual(h.render(encoding="utf8",fragment=True),b("").join(h.irender(encoding="utf8",fragment=True))) 114 115 116 def test_errors_on_malformed_xml(self): 117 class hello(dexml2.Model): 118 pass 119 120 self.assertRaises(dexml2.XmlError, hello.parse, b("<hello>")) 121 self.assertRaises(dexml2.XmlError, hello.parse, b("<hello></helo>")) 122 self.assertRaises(dexml2.XmlError, hello.parse, b("")) 123 124 self.assertRaises(dexml2.XmlError, hello.parse, u"") 125 self.assertRaises(dexml2.XmlError, hello.parse, u"<hello>") 126 self.assertRaises(dexml2.XmlError, hello.parse, u"<hello></helo>") 127 128 self.assertRaises(dexml2.XmlError, hello.parse, StringIO(u"<hello>")) 129 self.assertRaises(dexml2.XmlError, hello.parse, StringIO(u"<hello></helo>")) 130 self.assertRaises(dexml2.XmlError, hello.parse, StringIO(u"")) 131 132 self.assertRaises(ValueError,hello.parse,None) 133 self.assertRaises(ValueError,hello.parse,42) 134 self.assertRaises(ValueError,hello.parse,staticmethod) 135 136 137 def test_unicode_model_tagname(self): 138 """Test a dexml.Model class with a unicode tag name.""" 139 class hello(dexml2.Model): 140 class meta: 141 tagname = u"hel\N{GREEK SMALL LETTER LAMDA}o" 142 143 h = hello.parse(u"<hel\N{GREEK SMALL LETTER LAMDA}o />") 144 self.assertTrue(h) 145 146 h = hello.parse(u"<hel\N{GREEK SMALL LETTER LAMDA}o>\n</hel\N{GREEK SMALL LETTER LAMDA}o>") 147 self.assertTrue(h) 148 self.assertRaises(dexml2.ParseError, hello.parse, u"<hello />") 149 self.assertRaises(dexml2.ParseError, hello.parse, u"<Hello />") 150 self.assertRaises(dexml2.ParseError, hello.parse, u"<hllo />") 151 self.assertRaises(dexml2.ParseError, hello.parse, u"<Hel\N{GREEK SMALL LETTER LAMDA}o />") 152 153 h = hello.parse(u"<hel\N{GREEK SMALL LETTER LAMDA}o>world</hel\N{GREEK SMALL LETTER LAMDA}o>") 154 self.assertTrue(h) 155 156 h = hello.parse(u"<?xml version='1.0' encoding='utf-8' ?><hel\N{GREEK SMALL LETTER LAMDA}o>world</hel\N{GREEK SMALL LETTER LAMDA}o>") 157 h = hello.parse(u"<?xml version='1.0' encoding='utf-16' ?><hel\N{GREEK SMALL LETTER LAMDA}o>world</hel\N{GREEK SMALL LETTER LAMDA}o>") 158 self.assertTrue(h) 159 160 h = hello() 161 self.assertEqual(h.render(),u'<?xml version="1.0" ?><hel\N{GREEK SMALL LETTER LAMDA}o />') 162 self.assertEqual(h.render(fragment=True),u"<hel\N{GREEK SMALL LETTER LAMDA}o />") 163 self.assertEqual(h.render(encoding="utf8"),u'<?xml version="1.0" encoding="utf8" ?><hel\N{GREEK SMALL LETTER LAMDA}o />'.encode("utf8")) 164 self.assertEqual(h.render(encoding="utf8",fragment=True),u"<hel\N{GREEK SMALL LETTER LAMDA}o />".encode("utf8")) 165 166 self.assertEqual(h.render(),"".join(h.irender())) 167 self.assertEqual(h.render(fragment=True),"".join(h.irender(fragment=True))) 168 self.assertEqual(h.render(encoding="utf8"),b("").join(h.irender(encoding="utf8"))) 169 self.assertEqual(h.render(encoding="utf8",fragment=True),b("").join(h.irender(encoding="utf8",fragment=True))) 170 171 def test_unicode_string_field(self): 172 """Test a dexml.Model class with a unicode string field.""" 173 class Person(dexml2.Model): 174 name = fields.String() 175 176 p = Person.parse(u"<Person name='hel\N{GREEK SMALL LETTER LAMDA}o'/>") 177 self.assertEqual(p.name, u"hel\N{GREEK SMALL LETTER LAMDA}o") 178 179 p = Person() 180 p.name = u"hel\N{GREEK SMALL LETTER LAMDA}o" 181 self.assertEqual(p.render(encoding="utf8"), u'<?xml version="1.0" encoding="utf8" ?><Person name="hel\N{GREEK SMALL LETTER LAMDA}o" />'.encode("utf8")) 182 183 def test_model_meta_attributes(self): 184 class hello(dexml2.Model): 185 pass 186 187 self.assertRaises(dexml2.ParseError, hello.parse, "<Hello />") 188 hello.meta.case_sensitive = False 189 self.assertTrue(hello.parse("<Hello />")) 190 self.assertRaises(dexml2.ParseError, hello.parse, "<Helpo />") 191 hello.meta.case_sensitive = True 192 193 self.assertTrue(hello.parse("<hello>world</hello>")) 194 hello.meta.ignore_unknown_elements = False 195 self.assertRaises(dexml2.ParseError, hello.parse, "<hello>world</hello>") 196 hello.meta.ignore_unknown_elements = True 197 198 199 def test_namespace(self): 200 """Test basic handling of namespaces.""" 201 class hello(dexml2.Model): 202 class meta: 203 namespace = "http://hello.com/" 204 ignore_unknown_elements = False 205 206 h = hello.parse("<hello xmlns='http://hello.com/' />") 207 self.assertTrue(h) 208 209 h = hello.parse("<H:hello xmlns:H='http://hello.com/' />") 210 self.assertTrue(h) 211 212 self.assertRaises(dexml2.ParseError, hello.parse, "<hello />") 213 self.assertRaises(dexml2.ParseError, hello.parse, "<H:hllo xmlns:H='http://hello.com/' />") 214 self.assertRaises(dexml2.ParseError, hello.parse, "<H:hello xmlns:H='http://hello.com/'>world</H:hello>") 215 216 hello.meta.case_sensitive = False 217 self.assertRaises(dexml2.ParseError, hello.parse, "<Hello />") 218 self.assertRaises(dexml2.ParseError, hello.parse, "<H:hllo xmlns:H='http://hello.com/' />") 219 self.assertRaises(dexml2.ParseError, hello.parse, "<H:hello xmlns:H='http://Hello.com/' />") 220 hello.parse("<H:HeLLo xmlns:H='http://hello.com/' />") 221 hello.meta.case_sensitive = True 222 223 h = hello() 224 self.assertEqual(h.render(fragment=True),'<hello xmlns="http://hello.com/" />') 225 226 hello.meta.namespace_prefix = "H" 227 self.assertEqual(h.render(fragment=True),'<H:hello xmlns:H="http://hello.com/" />') 228 229 230 231 def test_base_field(self): 232 """Test operation of the base Field class (for coverage purposes).""" 233 class tester(dexml2.Model): 234 value = fields.Field() 235 assert isinstance(tester.value,fields.Field) 236 # This is a parse error because Field doesn't consume any nodes 237 self.assertRaises(dexml2.ParseError, tester.parse, "<tester value='42' />") 238 self.assertRaises(dexml2.ParseError, tester.parse, "<tester><value>42</value></tester>") 239 # Likewise, Field doesn't output any XML so it thinks value is missing 240 self.assertRaises(dexml2.RenderError, tester(value=None).render) 241 242 243 def test_value_fields(self): 244 """Test operation of basic value fields.""" 245 class hello(dexml2.Model): 246 recipient = fields.String() 247 sentby = fields.String(attrname="sender") 248 strength = fields.Integer(default=1) 249 message = fields.String(tagname="msg") 250 251 h = hello.parse("<hello recipient='ryan' sender='lozz' strength='7'><msg>hi there</msg></hello>") 252 self.assertEqual(h.recipient,"ryan") 253 self.assertEqual(h.sentby,"lozz") 254 self.assertEqual(h.message,"hi there") 255 self.assertEqual(h.strength,7) 256 257 # These are parse errors due to namespace mismatches 258 self.assertRaises(dexml2.ParseError, hello.parse, "<hello xmlns:N='N:' N:recipient='ryan' sender='lozz' strength='7'><msg>hi there</msg></hello>") 259 self.assertRaises(dexml2.ParseError, hello.parse, "<hello xmlns:N='N:' recipient='ryan' sender='lozz' strength='7'><N:msg>hi there</N:msg></hello>") 260 261 # These are parse errors due to subtags 262 self.assertRaises(dexml2.ParseError, hello.parse, "<hello recipient='ryan' sender='lozz' strength='7'><msg>hi <b>there</b></msg></hello>") 263 264 265 def test_float_field(self): 266 class F(dexml2.Model): 267 value = fields.Float() 268 self.assertEqual(F.parse("<F value='4.2' />").value,4.2) 269 270 271 def test_boolean_field(self): 272 class F(dexml2.Model): 273 value = fields.Boolean() 274 self.assertTrue(F.parse("<F value='' />").value) 275 self.assertTrue(F.parse("<F value='on' />").value) 276 self.assertTrue(F.parse("<F value='YeS' />").value) 277 self.assertFalse(F.parse("<F value='off' />").value) 278 self.assertFalse(F.parse("<F value='no' />").value) 279 self.assertFalse(F.parse("<F value='FaLsE' />").value) 280 281 f = F.parse("<F value='' />") 282 assert model_fields_equal(F.parse(f.render()),f) 283 f.value = "someotherthing" 284 assert model_fields_equal(F.parse(f.render()),f) 285 f.value = False 286 assert model_fields_equal(F.parse(f.render()),f) 287 288 289 def test_string_with_special_chars(self): 290 class letter(dexml2.Model): 291 message = fields.String(tagname="msg") 292 293 l = letter.parse("<letter><msg>hello & goodbye</msg></letter>") 294 self.assertEqual(l.message,"hello & goodbye") 295 l = letter.parse("<letter><msg><![CDATA[hello & goodbye]]></msg></letter>") 296 self.assertEqual(l.message,"hello & goodbye") 297 298 l = letter(message="XML <tags> are fun!") 299 self.assertEqual(l.render(fragment=True),'<letter><msg>XML <tags> are fun!</msg></letter>') 300 301 class update(dexml2.Model): 302 status = fields.String(attrname="status") 303 304 u = update(status="feeling <awesome>!") 305 self.assertEqual(u.render(fragment=True),'<update status="feeling <awesome>!" />') 306 307 308 def test_cdata_fields(self): 309 try: 310 class update(dexml2.Model): 311 status = fields.CDATA() 312 assert False, "CDATA allowed itself to be created without tagname" 313 except ValueError: 314 pass 315 class update(dexml2.Model): 316 status = fields.CDATA(tagname=True) 317 u = update(status="feeling <awesome>!") 318 self.assertEqual(u.render(fragment=True),'<update><status><![CDATA[feeling <awesome>!]]></status></update>') 319 320 321 def test_model_field(self): 322 """Test operation of fields.Model.""" 323 class person(dexml2.Model): 324 name = fields.String() 325 age = fields.Integer() 326 class pet(dexml2.Model): 327 name = fields.String() 328 species = fields.String(required=False) 329 class Vet(dexml2.Model): 330 class meta: 331 tagname = "vet" 332 name = fields.String() 333 class pets(dexml2.Model): 334 person = fields.Model() 335 pet1 = fields.Model("pet") 336 pet2 = fields.Model(pet,required=False) 337 pet3 = fields.Model((None,pet),required=False) 338 vet = fields.Model((None,"Vet"),required=False) 339 340 p = pets.parse("<pets><person name='ryan' age='26'/><pet name='riley' species='dog' /></pets>") 341 self.assertEqual(p.person.name,"ryan") 342 self.assertEqual(p.pet1.species,"dog") 343 self.assertEqual(p.pet2,None) 344 345 p = pets.parse("<pets>\n<person name='ryan' age='26'/>\n<pet name='riley' species='dog' />\n<pet name='fishy' species='fish' />\n</pets>") 346 self.assertEqual(p.person.name,"ryan") 347 self.assertEqual(p.pet1.name,"riley") 348 self.assertEqual(p.pet2.species,"fish") 349 350 p = pets.parse("<pets><person name='ryan' age='26'/><pet name='riley' species='dog' /><pet name='fishy' species='fish' /><pet name='meowth' species='cat' /><vet name='Nic' /></pets>") 351 self.assertEqual(p.person.name,"ryan") 352 self.assertEqual(p.pet1.name,"riley") 353 self.assertEqual(p.pet2.species,"fish") 354 self.assertEqual(p.pet3.species,"cat") 355 self.assertEqual(p.vet.name,"Nic") 356 357 self.assertRaises(dexml2.ParseError, pets.parse, "<pets><pet name='riley' species='fish' /></pets>") 358 self.assertRaises(dexml2.ParseError, pets.parse, "<pets><person name='riley' age='2' /></pets>") 359 360 def assign(val): 361 p.pet1 = val 362 self.assertRaises(ValueError, assign, person(name = 'ryan', age = 26)) 363 self.assertEqual(p.pet1.name,"riley") 364 assign(pet(name="spike")) 365 self.assertEqual(p.pet1.name,"spike") 366 367 p = pets() 368 self.assertRaises(dexml2.RenderError, p.render) 369 p.person = person(name="lozz",age="25") 370 p.pet1 = pet(name="riley") 371 self.assertEqual(p.render(fragment=True),'<pets><person name="lozz" age="25" /><pet name="riley" /></pets>') 372 self.assertEqual("".join(p.irender(fragment=True)),'<pets><person name="lozz" age="25" /><pet name="riley" /></pets>') 373 p.pet2 = pet(name="guppy",species="fish") 374 self.assertEqual(p.render(fragment=True),'<pets><person name="lozz" age="25" /><pet name="riley" /><pet name="guppy" species="fish" /></pets>') 375 self.assertEqual("".join(p.irender(fragment=True)),'<pets><person name="lozz" age="25" /><pet name="riley" /><pet name="guppy" species="fish" /></pets>') 376 377 378 def test_model_field_namespace(self): 379 """Test operation of fields.Model with namespaces""" 380 class petbase(dexml2.Model): 381 class meta: 382 namespace = "http://www.pets.com/PetML" 383 namespace_prefix = "P" 384 class person(petbase): 385 name = fields.String() 386 age = fields.Integer() 387 status = fields.String(tagname=("S:","status"),required=False) 388 class pet(petbase): 389 name = fields.String() 390 species = fields.String(required=False) 391 class pets(petbase): 392 person = fields.Model() 393 pet1 = fields.Model("pet") 394 pet2 = fields.Model(pet,required=False) 395 396 p = pets.parse("<pets xmlns='http://www.pets.com/PetML'><person name='ryan' age='26'/><pet name='riley' species='dog' /></pets>") 397 self.assertEqual(p.person.name,"ryan") 398 self.assertEqual(p.pet1.species,"dog") 399 self.assertEqual(p.pet2,None) 400 401 p = pets.parse("<P:pets xmlns:P='http://www.pets.com/PetML'><P:person name='ryan' age='26'/><P:pet name='riley' species='dog' /><P:pet name='fishy' species='fish' /></P:pets>") 402 self.assertEqual(p.person.name,"ryan") 403 self.assertEqual(p.pet1.name,"riley") 404 self.assertEqual(p.pet2.species,"fish") 405 406 self.assertRaises(dexml2.ParseError, pets.parse, "<pets><pet name='riley' species='fish' /></pets>") 407 self.assertRaises(dexml2.ParseError, pets.parse, "<pets><person name='riley' age='2' /></pets>") 408 409 p = pets() 410 self.assertRaises(dexml2.RenderError, p.render) 411 412 p.person = person(name="lozz",age="25") 413 p.pet1 = pet(name="riley") 414 self.assertEqual(p.render(fragment=True),'<P:pets xmlns:P="http://www.pets.com/PetML"><P:person name="lozz" age="25" /><P:pet name="riley" /></P:pets>') 415 416 p.pet2 = pet(name="guppy",species="fish") 417 self.assertEqual(p.render(fragment=True),'<P:pets xmlns:P="http://www.pets.com/PetML"><P:person name="lozz" age="25" /><P:pet name="riley" /><P:pet name="guppy" species="fish" /></P:pets>') 418 419 p = person.parse('<P:person xmlns:P="http://www.pets.com/PetML" name="ryan" age="26"><status>awesome</status></P:person>') 420 self.assertEqual(p.status,None) 421 p = person.parse('<P:person xmlns:P="http://www.pets.com/PetML" name="ryan" age="26"><P:status>awesome</P:status></P:person>') 422 self.assertEqual(p.status,None) 423 p = person.parse('<P:person xmlns:P="http://www.pets.com/PetML" xmlns:S="S:" name="ryan" age="26"><S:sts>awesome</S:sts></P:person>') 424 self.assertEqual(p.status,None) 425 p = person.parse('<P:person xmlns:P="http://www.pets.com/PetML" xmlns:S="S:" name="ryan" age="26"><S:status>awesome</S:status></P:person>') 426 self.assertEqual(p.status,"awesome") 427 428 429 def test_list_field(self): 430 """Test operation of fields.List""" 431 class person(dexml2.Model): 432 name = fields.String() 433 age = fields.Integer() 434 class pet(dexml2.Model): 435 name = fields.String() 436 species = fields.String(required=False) 437 class reward(dexml2.Model): 438 date = fields.String() 439 class pets(dexml2.Model): 440 person = fields.Model() 441 pets = fields.List("pet",minlength=1) 442 notes = fields.List(fields.String(tagname="note"),maxlength=2) 443 rewards = fields.List("reward",tagname="rewards",required=False) 444 445 p = pets.parse("<pets><person name='ryan' age='26'/><pet name='riley' species='dog' /></pets>") 446 self.assertEqual(p.person.name,"ryan") 447 self.assertEqual(p.pets[0].species,"dog") 448 self.assertEqual(len(p.pets),1) 449 self.assertEqual(len(p.notes),0) 450 451 p = pets.parse("<pets>\n\t<person name='ryan' age='26'/>\n\t<pet name='riley' species='dog' />\n\t<pet name='fishy' species='fish' />\n\t<note>noted</note></pets>") 452 self.assertEqual(p.person.name,"ryan") 453 self.assertEqual(p.pets[0].name,"riley") 454 self.assertEqual(p.pets[1].species,"fish") 455 self.assertEqual(p.notes[0],"noted") 456 self.assertEqual(len(p.pets),2) 457 self.assertEqual(len(p.notes),1) 458 459 self.assertRaises(dexml2.ParseError, pets.parse, "<pets><pet name='riley' species='fish' /></pets>") 460 self.assertRaises(dexml2.ParseError, pets.parse, "<pets><person name='ryan' age='26' /></pets>") 461 self.assertRaises(dexml2.ParseError, pets.parse, "<pets><person name='ryan' age='26'/><pet name='riley' species='dog' /><note>too</note><note>many</note><note>notes</note></pets>") 462 463 p = pets() 464 p.person = person(name="lozz",age="25") 465 self.assertRaises(dexml2.RenderError, p.render) 466 467 p.pets.append(pet(name="riley")) 468 self.assertEqual(p.render(fragment=True),'<pets><person name="lozz" age="25" /><pet name="riley" /></pets>') 469 470 p.pets.append(pet(name="guppy",species="fish")) 471 p.notes.append("noted") 472 self.assertEqual(p.render(fragment=True),'<pets><person name="lozz" age="25" /><pet name="riley" /><pet name="guppy" species="fish" /><note>noted</note></pets>') 473 474 p = pets() 475 p.person = person(name="lozz",age="25") 476 yielded_items = [] 477 def gen_pets(): 478 for p in (pet(name="riley"),pet(name="guppy",species="fish")): 479 yielded_items.append(p) 480 yield p 481 p.pets = gen_pets() 482 self.assertEqual(len(yielded_items),0) 483 p.notes.append("noted") 484 self.assertEqual(p.render(fragment=True),'<pets><person name="lozz" age="25" /><pet name="riley" /><pet name="guppy" species="fish" /><note>noted</note></pets>') 485 self.assertEqual(len(yielded_items),2) 486 487 p = pets.parse("<pets><person name='ryan' age='26'/><pet name='riley' species='dog' /><rewards><reward date='February 23, 2010'/><reward date='November 10, 2009'/></rewards></pets>") 488 self.assertEqual(len(p.rewards), 2) 489 self.assertEqual(p.rewards[1].date, 'November 10, 2009') 490 self.assertEqual(p.render(fragment = True), '<pets><person name="ryan" age="26" /><pet name="riley" species="dog" /><rewards><reward date="February 23, 2010" /><reward date="November 10, 2009" /></rewards></pets>') 491 492 pets.meta.ignore_unknown_elements = False 493 self.assertRaises(dexml2.ParseError, pets.parse, "<pets><person name='ryan' age='26' /><pet name='riley' species='dog' /><reward date='February 23, 2010'/><reward date='November 10, 2009' /></pets>") 494 495 def test_list_field_tagname(self): 496 """Test List(tagname="items",required=True).""" 497 class obj(dexml2.Model): 498 items = fields.List(fields.String(tagname="item"),tagname="items") 499 o = obj(items=[]) 500 self.assertEqual(o.render(fragment=True), '<obj><items /></obj>') 501 self.assertRaises(dexml2.ParseError, obj.parse, '<obj />') 502 o = obj.parse('<obj><items /></obj>') 503 self.assertEqual(o.items,[]) 504 505 def test_list_field_sanity_checks(self): 506 class GreedyField(fields.Field): 507 def parse_child_node(self,obj,node): 508 return dexml2.PARSE_MORE 509 class SaneList(dexml2.Model): 510 item = fields.List(GreedyField(tagname="item")) 511 self.assertRaises(ValueError,SaneList.parse,"<SaneList><item /><item /></SaneList>") 512 513 514 def test_list_field_max_min(self): 515 try: 516 class MyStuff(dexml2.Model): 517 items = fields.List(fields.String(tagname="item"),required=False,minlength=2) 518 assert False, "List allowed creation with nonsensical args" 519 except ValueError: 520 pass 521 522 class MyStuff(dexml2.Model): 523 items = fields.List(fields.String(tagname="item"),required=False) 524 self.assertEqual(MyStuff.parse("<MyStuff />").items,[]) 525 526 MyStuff.items.maxlength = 1 527 self.assertEqual(MyStuff.parse("<MyStuff><item /></MyStuff>").items,[""]) 528 self.assertRaises(dexml2.ParseError, MyStuff.parse, "<MyStuff><item /><item /></MyStuff>") 529 s = MyStuff() 530 s.items = ["one","two"] 531 self.assertRaises(dexml2.RenderError, s.render) 532 533 MyStuff.items.maxlength = None 534 MyStuff.items.minlength = 2 535 MyStuff.items.required = True 536 self.assertEqual(MyStuff.parse("<MyStuff><item /><item /></MyStuff>").items,["",""]) 537 self.assertRaises(dexml2.ParseError, MyStuff.parse, "<MyStuff><item /></MyStuff>") 538 539 540 def test_dict_field(self): 541 """Test operation of fields.Dict""" 542 class item(dexml2.Model): 543 name = fields.String() 544 attr = fields.String(tagname = 'attr') 545 class obj(dexml2.Model): 546 items = fields.Dict('item', key = 'name') 547 548 xml = '<obj><item name="item1"><attr>val1</attr></item><item name="item2"><attr>val2</attr></item></obj>' 549 o = obj.parse(xml) 550 self.assertEqual(len(o.items), 2) 551 self.assertEqual(o.items['item1'].name, 'item1') 552 self.assertEqual(o.items['item2'].attr, 'val2') 553 del o.items['item2'] 554 self.assertEqual(o.render(fragment = True), '<obj><item name="item1"><attr>val1</attr></item></obj>') 555 556 o.items['item3'] = item(attr = 'val3') 557 self.assertEqual(o.items['item3'].attr, 'val3') 558 def _setitem(): 559 o.items['item3'] = item(name = 'item2', attr = 'val3') 560 self.assertRaises(ValueError, _setitem) 561 562 class obj(dexml2.Model): 563 items = fields.Dict(fields.Model(item), key = 'name', unique = True) 564 xml = '<obj><item name="item1"><attr>val1</attr></item><item name="item1"><attr>val2</attr></item></obj>' 565 self.assertRaises(dexml2.ParseError, obj.parse, xml) 566 567 class obj(dexml2.Model): 568 items = fields.Dict('item', key = 'name', tagname = 'items') 569 xml = '<obj> <ignoreme /> <items> <item name="item1"><attr>val1</attr></item> <item name="item2"><attr>val2</attr></item> </items> </obj>' 570 571 o = obj.parse(xml) 572 self.assertEqual(len(o.items), 2) 573 self.assertEqual(o.items['item1'].name, 'item1') 574 self.assertEqual(o.items['item2'].attr, 'val2') 575 del o.items['item2'] 576 self.assertEqual(o.render(fragment = True), '<obj><items><item name="item1"><attr>val1</attr></item></items></obj>') 577 578 # Test that wrapper tags are still required even for empty fields 579 o = obj(items={}) 580 self.assertEqual(o.render(fragment=True), '<obj><items /></obj>') 581 o = obj.parse('<obj><items /></obj>') 582 self.assertEqual(o.items,{}) 583 self.assertRaises(dexml2.ParseError, obj.parse, '<obj />') 584 obj.items.required = False 585 self.assertEqual(o.render(fragment=True), '<obj />') 586 obj.items.required = True 587 588 from collections import defaultdict 589 class _dict(defaultdict): 590 def __init__(self): 591 super(_dict, self).__init__(item) 592 593 class obj(dexml2.Model): 594 items = fields.Dict('item', key = 'name', dictclass = _dict) 595 o = obj() 596 self.assertEqual(o.items['item1'].name, 'item1') 597 598 599 def test_dict_field_sanity_checks(self): 600 class GreedyField(fields.Field): 601 def parse_child_node(self,obj,node): 602 return dexml2.PARSE_MORE 603 class SaneDict(dexml2.Model): 604 item = fields.Dict(GreedyField(tagname="item"),key="name") 605 self.assertRaises(ValueError,SaneDict.parse,"<SaneDict><item /></SaneDict>") 606 607 class item(dexml2.Model): 608 name = fields.String() 609 value = fields.String() 610 class MyStuff(dexml2.Model): 611 items = fields.Dict(item,key="wrongname") 612 self.assertRaises(dexml2.ParseError, MyStuff.parse, "<MyStuff><ignoreme /><item name='hi' value='world' /></MyStuff>") 613 614 615 def test_dict_field_max_min(self): 616 class item(dexml2.Model): 617 name = fields.String() 618 value = fields.String() 619 try: 620 class MyStuff(dexml2.Model): 621 items = fields.Dict(item,key="name",required=False,minlength=2) 622 assert False, "Dict allowed creation with nonsensical args" 623 except ValueError: 624 pass 625 626 class MyStuff(dexml2.Model): 627 items = fields.Dict(item,key="name",required=False) 628 self.assertEqual(MyStuff.parse("<MyStuff />").items,{}) 629 630 MyStuff.items.maxlength = 1 631 self.assertEqual(len(MyStuff.parse("<MyStuff><item name='hi' value='world' /></MyStuff>").items),1) 632 self.assertRaises(dexml2.ParseError, MyStuff.parse, "<MyStuff><item name='hi' value='world' /><item name='hello' value='earth' /></MyStuff>") 633 s = MyStuff() 634 s.items = [item(name="yo",value="dawg"),item(name="wazzup",value="yo")] 635 self.assertRaises(dexml2.RenderError, s.render) 636 637 MyStuff.items.maxlength = None 638 MyStuff.items.minlength = 2 639 MyStuff.items.required = True 640 self.assertEqual(len(MyStuff.parse("<MyStuff><item name='hi' value='world' /><item name='hello' value='earth' /></MyStuff>").items),2) 641 self.assertRaises(dexml2.ParseError, MyStuff.parse, "<MyStuff><item name='hi' value='world' /></MyStuff>") 642 643 s = MyStuff() 644 s.items = [item(name="yo",value="dawg")] 645 self.assertRaises(dexml2.RenderError, s.render) 646 647 648 def test_choice_field(self): 649 """Test operation of fields.Choice""" 650 class breakfast(dexml2.Model): 651 meal = fields.Choice("bacon","cereal") 652 class bacon(dexml2.Model): 653 num_rashers = fields.Integer() 654 class cereal(dexml2.Model): 655 with_milk = fields.Boolean() 656 657 b = breakfast.parse("<breakfast><bacon num_rashers='4' /></breakfast>") 658 self.assertEqual(b.meal.num_rashers,4) 659 660 b = breakfast.parse("<breakfast><cereal with_milk='true' /></breakfast>") 661 self.assertTrue(b.meal.with_milk) 662 663 self.assertRaises(dexml2.ParseError, b.parse, "<breakfast><eggs num='2' /></breakfast>") 664 self.assertRaises(dexml2.ParseError, b.parse, "<breakfast />") 665 666 b = breakfast() 667 self.assertRaises(dexml2.RenderError, b.render) 668 b.meal = bacon(num_rashers=1) 669 self.assertEqual(b.render(fragment=True),"<breakfast><bacon num_rashers=\"1\" /></breakfast>") 670 671 672 def test_choice_field_sanity_checks(self): 673 try: 674 class SaneChoice(dexml2.Model): 675 item = fields.Choice(fields.String(),fields.Integer()) 676 assert False, "Choice field failed its sanity checks" 677 except ValueError: 678 pass 679 class GreedyModel(fields.Model): 680 def parse_child_node(self,obj,node): 681 return dexml2.PARSE_MORE 682 class SaneChoice(dexml2.Model): 683 item = fields.Choice(GreedyModel("SaneChoice")) 684 685 self.assertRaises(ValueError,SaneChoice.parse,"<SaneChoice><SaneChoice /></SaneChoice>") 686 687 688 def test_list_of_choice(self): 689 """Test operation of fields.Choice inside fields.List""" 690 class breakfast(dexml2.Model): 691 meals = fields.List(fields.Choice("bacon","cereal")) 692 class bacon(dexml2.Model): 693 num_rashers = fields.Integer() 694 class cereal(dexml2.Model): 695 with_milk = fields.Boolean() 696 697 b = breakfast.parse("<breakfast><bacon num_rashers='4' /></breakfast>") 698 self.assertEqual(len(b.meals),1) 699 self.assertEqual(b.meals[0].num_rashers,4) 700 701 b = breakfast.parse("<breakfast><bacon num_rashers='2' /><cereal with_milk='true' /></breakfast>") 702 self.assertEqual(len(b.meals),2) 703 self.assertEqual(b.meals[0].num_rashers,2) 704 self.assertTrue(b.meals[1].with_milk) 705 706 707 def test_empty_only_boolean(self): 708 """Test operation of fields.Boolean with empty_only=True""" 709 class toggles(dexml2.Model): 710 toggle_str = fields.Boolean(required=False) 711 toggle_empty = fields.Boolean(tagname=True,empty_only=True) 712 713 t = toggles.parse("<toggles />") 714 self.assertFalse(t.toggle_str) 715 self.assertFalse(t.toggle_empty) 716 717 t = toggles.parse("<toggles toggle_str=''><toggle_empty /></toggles>") 718 self.assertTrue(t.toggle_str) 719 self.assertTrue(t.toggle_empty) 720 721 t = toggles.parse("<toggles toggle_str='no'><toggle_empty /></toggles>") 722 self.assertFalse(t.toggle_str) 723 self.assertTrue(t.toggle_empty) 724 725 self.assertRaises(ValueError,toggles.parse,"<toggles><toggle_empty>no</toggle_empty></toggles>") 726 self.assertFalse("toggle_empty" in toggles(toggle_empty=False).render()) 727 self.assertTrue("<toggle_empty />" in toggles(toggle_empty=True).render()) 728 729 def test_XmlNode(self): 730 """Test correct operation of fields.XmlNode.""" 731 class bucket(dexml2.Model): 732 class meta: 733 namespace = "bucket-uri" 734 contents = fields.XmlNode(encoding="utf8") 735 b = bucket.parse("<B:bucket xmlns:B='bucket-uri'><B:contents><hello><B:world /></hello></B:contents></B:bucket>") 736 self.assertEqual(b.contents.childNodes[0].tagName,"hello") 737 self.assertEqual(b.contents.childNodes[0].namespaceURI,None) 738 self.assertEqual(b.contents.childNodes[0].childNodes[0].localName,"world") 739 self.assertEqual(b.contents.childNodes[0].childNodes[0].namespaceURI,"bucket-uri") 740 741 b = bucket() 742 b.contents = "<hello>world</hello>" 743 b = bucket.parse(b.render()) 744 self.assertEqual(b.contents.tagName,"hello") 745 b.contents = u"<hello>world</hello>" 746 b = bucket.parse(b.render()) 747 self.assertEqual(b.contents.tagName,"hello") 748 749 b = bucket.parse("<bucket xmlns='bucket-uri'><bucket><hello /></bucket></bucket>") 750 b2 = bucket.parse("".join(fields.XmlNode.render_children(b,b.contents,{}))) 751 self.assertEqual(b2.contents.tagName,"hello") 752 753 class bucket(dexml2.Model): 754 class meta: 755 namespace = "bucket-uri" 756 contents = fields.XmlNode(tagname="contents") 757 b = bucket.parse("<B:bucket xmlns:B='bucket-uri'><ignoreme /><B:contents><hello><B:world /></hello></B:contents></B:bucket>") 758 self.assertEqual(b.contents.childNodes[0].tagName,"hello") 759 760 761 def test_namespaced_attrs(self): 762 class nsa(dexml2.Model): 763 f1 = fields.Integer(attrname=("test:","f1")) 764 n = nsa.parse("<nsa t:f1='7' xmlns:t='test:' />") 765 self.assertEqual(n.f1,7) 766 n2 = nsa.parse(n.render()) 767 self.assertEqual(n2.f1,7) 768 769 class nsa_decl(dexml2.Model): 770 class meta: 771 tagname = "nsa" 772 namespace = "test:" 773 namespace_prefix = "t" 774 f1 = fields.Integer(attrname=("test:","f1")) 775 n = nsa_decl.parse("<t:nsa t:f1='7' xmlns:t='test:' />") 776 self.assertEqual(n.f1,7) 777 self.assertEqual(n.render(fragment=True),'<t:nsa xmlns:t="test:" t:f1="7" />') 778 779 780 def test_namespaced_children(self): 781 class nsc(dexml2.Model): 782 f1 = fields.Integer(tagname=("test:","f1")) 783 n = nsc.parse("<nsc xmlns:t='test:'><t:f1>7</t:f1></nsc>") 784 self.assertEqual(n.f1,7) 785 n2 = nsc.parse(n.render()) 786 self.assertEqual(n2.f1,7) 787 788 n = nsc.parse("<nsc><f1 xmlns='test:'>7</f1></nsc>") 789 self.assertEqual(n.f1,7) 790 n2 = nsc.parse(n.render()) 791 self.assertEqual(n2.f1,7) 792 793 class nsc_decl(dexml2.Model): 794 class meta: 795 tagname = "nsc" 796 namespace = "test:" 797 namespace_prefix = "t" 798 f1 = fields.Integer(tagname=("test:","f1")) 799 n = nsc_decl.parse("<t:nsc xmlns:t='test:'><t:f1>7</t:f1></t:nsc>") 800 self.assertEqual(n.f1,7) 801 n2 = nsc_decl.parse(n.render()) 802 self.assertEqual(n2.f1,7) 803 804 n = nsc_decl.parse("<nsc xmlns='test:'><f1>7</f1></nsc>") 805 self.assertEqual(n.f1,7) 806 n2 = nsc_decl.parse(n.render()) 807 self.assertEqual(n2.f1,7) 808 809 self.assertEqual(n2.render(fragment=True),'<t:nsc xmlns:t="test:"><t:f1>7</t:f1></t:nsc>') 810 811 812 def test_order_sensitive(self): 813 """Test operation of order-sensitive and order-insensitive parsing""" 814 class junk(dexml2.Model): 815 class meta: 816 order_sensitive = True 817 name = fields.String(tagname=True) 818 notes = fields.List(fields.String(tagname="note")) 819 amount = fields.Integer(tagname=True) 820 class junk_unordered(junk): 821 class meta: 822 tagname = "junk" 823 order_sensitive = False 824 825 j = junk.parse("<junk><name>test1</name><note>note1</note><note>note2</note><amount>7</amount></junk>") 826 self.assertEqual(j.name,"test1") 827 self.assertEqual(j.notes,["note1","note2"]) 828 self.assertEqual(j.amount,7) 829 830 j = junk_unordered.parse("<junk><name>test1</name><note>note1</note><note>note2</note><amount>7</amount></junk>") 831 self.assertEqual(j.name,"test1") 832 self.assertEqual(j.notes,["note1","note2"]) 833 self.assertEqual(j.amount,7) 834 835 self.assertRaises(dexml2.ParseError, junk.parse, "<junk><note>note1</note><amount>7</amount><note>note2</note><name>test1</name></junk>") 836 837 j = junk_unordered.parse("<junk><note>note1</note><amount>7</amount><note>note2</note><name>test1</name></junk>") 838 self.assertEqual(j.name,"test1") 839 self.assertEqual(j.notes,["note1","note2"]) 840 self.assertEqual(j.amount,7) 841 842 843 def test_namespace_prefix_generation(self): 844 class A(dexml2.Model): 845 class meta: 846 namespace='http://xxx' 847 a = fields.String(tagname=('http://yyy','a')) 848 class B(dexml2.Model): 849 class meta: 850 namespace='http://yyy' 851 b = fields.Model(A) 852 853 b1 = B(b=A(a='value')) 854 855 # With no specific prefixes set we can't predict the output, 856 # but it should round-trip OK. 857 assert model_fields_equal(B.parse(b1.render()),b1) 858 859 # With specific prefixes set, output is predictable. 860 A.meta.namespace_prefix = "x" 861 B.meta.namespace_prefix = "y" 862 self.assertEqual(b1.render(),'<?xml version="1.0" ?><y:B xmlns:y="http://yyy"><x:A xmlns:x="http://xxx"><y:a>value</y:a></x:A></y:B>') 863 A.meta.namespace_prefix = None 864 B.meta.namespace_prefix = None 865 866 # This is a little hackery to trick the random-prefix generator 867 # into looping a few times before picking one. We can't predict 868 # the output but it'll exercise the code. 869 class pickydict(dict): 870 def __init__(self,*args,**kwds): 871 self.__counter = 0 872 super(pickydict,self).__init__(*args,**kwds) 873 def __contains__(self,key): 874 if self.__counter > 5: 875 return super(pickydict,self).__contains__(key) 876 self.__counter += 1 877 return True 878 assert model_fields_equal(B.parse(b1.render(nsmap=pickydict())),b1) 879 880 class A(dexml2.Model): 881 class meta: 882 namespace='T:' 883 a = fields.String(attrname=('A:','a')) 884 b = fields.String(attrname=(None,'b')) 885 c = fields.String(tagname=(None,'c')) 886 887 a1 = A(a="hello",b="world",c="owyagarn") 888 889 # With no specific prefixes set we can't predict the output, 890 # but it should round-trip OK. 891 assert model_fields_equal(A.parse(a1.render()),a1) 892 893 # With specific prefixes set, output is predictable. 894 # Note that this suppresses generation of the xmlns declarations, 895 # so the output is actually broken here. Broken, but predictable. 896 nsmap = {} 897 nsmap["T"] = ["T:"] 898 nsmap["A"] = ["A:"] 899 self.assertEqual(a1.render(fragment=True,nsmap=nsmap),'<A xmlns="T:" A:a="hello" b="world"><c xmlns="">owyagarn</c></A>') 900 901 # This is a little hackery to trick the random-prefix generator 902 # into looping a few times before picking one. We can't predict 903 # the output but it'll exercise the code. 904 class pickydict(dict): 905 def __init__(self,*args,**kwds): 906 self.__counter = 0 907 super(pickydict,self).__init__(*args,**kwds) 908 def __contains__(self,key): 909 if self.__counter > 5: 910 return super(pickydict,self).__contains__(key) 911 self.__counter += 1 912 return True 913 assert model_fields_equal(A.parse(a1.render(nsmap=pickydict())),a1) 914 915 A.c.tagname = ("C:","c") 916 assert model_fields_equal(A.parse(a1.render(nsmap=pickydict())),a1) 917 a1 = A(a="hello",b="world",c="") 918 assert model_fields_equal(A.parse(a1.render(nsmap=pickydict())),a1) 919 920 921 def test_parsing_value_from_tag_contents(self): 922 class attr(dexml2.Model): 923 name = fields.String() 924 value = fields.String(tagname=".") 925 class obj(dexml2.Model): 926 id = fields.String() 927 attrs = fields.List(attr) 928 o = obj.parse('<obj id="z108"><attr name="level">6</attr><attr name="descr">description</attr></obj>') 929 self.assertEqual(o.id,"z108") 930 self.assertEqual(len(o.attrs),2) 931 self.assertEqual(o.attrs[0].name,"level") 932 self.assertEqual(o.attrs[0].value,"6") 933 self.assertEqual(o.attrs[1].name,"descr") 934 self.assertEqual(o.attrs[1].value,"description") 935 936 o = obj(id="test") 937 o.attrs.append(attr(name="hello",value="world")) 938 o.attrs.append(attr(name="wherethe",value="bloodyhellareya")) 939 self.assertEqual(o.render(fragment=True),'<obj id="test"><attr name="hello">world</attr><attr name="wherethe">bloodyhellareya</attr></obj>') 940 941 942 def test_inheritance_of_meta_attributes(self): 943 class Base1(dexml2.Model): 944 class meta: 945 tagname = "base1" 946 order_sensitive = True 947 class Base2(dexml2.Model): 948 class meta: 949 tagname = "base2" 950 order_sensitive = False 951 952 class Sub(Base1): 953 pass 954 self.assertEqual(Sub.meta.order_sensitive,True) 955 956 class Sub(Base2): 957 pass 958 self.assertEqual(Sub.meta.order_sensitive,False) 959 960 class Sub(Base2): 961 class meta: 962 order_sensitive = True 963 self.assertEqual(Sub.meta.order_sensitive,True) 964 965 class Sub(Base1,Base2): 966 pass 967 self.assertEqual(Sub.meta.order_sensitive,True) 968 969 class Sub(Base2,Base1): 970 pass 971 self.assertEqual(Sub.meta.order_sensitive,False) 972 973 974 def test_mixing_in_other_base_classes(self): 975 class Thing(dexml2.Model): 976 testit = fields.String() 977 class Mixin(object): 978 def _get_testit(self): 979 return 42 980 def _set_testit(self,value): 981 pass 982 testit = property(_get_testit,_set_testit) 983 984 class Sub(Thing,Mixin): 985 pass 986 assert issubclass(Sub,Thing) 987 assert issubclass(Sub,Mixin) 988 s = Sub.parse('<Sub testit="hello" />') 989 self.assertEqual(s.testit,"hello") 990 991 class Sub(Mixin,Thing): 992 pass 993 assert issubclass(Sub,Thing) 994 assert issubclass(Sub,Mixin) 995 s = Sub.parse('<Sub testit="hello" />') 996 self.assertEqual(s.testit,42) 997 998 999 def test_error_using_undefined_model_class(self): 1000 class Whoopsie(dexml2.Model): 1001 value = fields.Model("UndefinedModel") 1002 self.assertRaises(ValueError,Whoopsie.parse,"<Whoopsie><UndefinedModel /></Whoopsie>") 1003 self.assertRaises(ValueError,Whoopsie,value=None) 1004 1005 class Whoopsie(dexml2.Model): 1006 value = fields.Model((None,"UndefinedModel")) 1007 self.assertRaises(ValueError,Whoopsie.parse,"<Whoopsie><UndefinedModel /></Whoopsie>") 1008 self.assertRaises(ValueError,Whoopsie,value=None) 1009 1010 class Whoopsie(dexml2.Model): 1011 value = fields.Model(("W:","UndefinedModel")) 1012 self.assertRaises(ValueError,Whoopsie.parse,"<Whoopsie><UndefinedModel /></Whoopsie>") 1013 self.assertRaises(ValueError,Whoopsie,value=None) 1014 1015 1016 def test_unordered_parse_of_list_field(self): 1017 class Notebook(dexml2.Model): 1018 class meta: 1019 order_sensitive = False 1020 notes = fields.List(fields.String(tagname="note"),tagname="notes") 1021 1022 n = Notebook.parse("<Notebook><notes><note>one</note><note>two</note></notes></Notebook>") 1023 self.assertEqual(n.notes,["one","two"]) 1024 1025 Notebook.parse("<Notebook><wtf /><notes><note>one</note><note>two</note><wtf /></notes></Notebook>") 1026 1027 Notebook.meta.ignore_unknown_elements = False 1028 self.assertRaises(dexml2.ParseError, Notebook.parse, "<Notebook><wtf /><notes><note>one</note><note>two</note><wtf /></notes></Notebook>") 1029 self.assertRaises(dexml2.ParseError, Notebook.parse, "<Notebook tag='home'><notes><note>one</note><note>two</note></notes></Notebook>") 1030 1031 1032class TestListField(unittest.TestCase): 1033 class F(dexml2.Model): 1034 class meta: 1035 tagname = "f" 1036 name = fields.String(tagname="name") 1037 1038 def test_empty(self): 1039 class obj(dexml2.Model): 1040 fs = fields.List(fields.Model(self.F)) 1041 1042 o = obj() 1043 self.assertEqual(o.render(fragment=True), "<obj />") 1044 1045 def test_simple(self): 1046 class obj(dexml2.Model): 1047 fs = fields.List(fields.Model(self.F)) 1048 1049 o = obj() 1050 o.fs.append(self.F(name="N1")) 1051 self.assertEqual(o.render(fragment=True), "<obj><f><name>N1</name></f></obj>") 1052 1053 def test_empty_with_tagname(self): 1054 class obj(dexml2.Model): 1055 fs = fields.List(fields.Model(self.F), tagname="L") 1056 1057 o = obj() 1058 self.assertEqual(o.render(fragment=True), "<obj><L /></obj>") 1059 1060 def test_tagname(self): 1061 class obj(dexml2.Model): 1062 fs = fields.List(fields.Model(self.F), tagname="L") 1063 1064 o = obj() 1065 o.fs.append(self.F(name="N1")) 1066 self.assertEqual(o.render(fragment=True), "<obj><L><f><name>N1</name></f></L></obj>") 1067 1068 def test_model_tagname(self): 1069 class FF(self.F): 1070 pass 1071 class obj(dexml2.Model): 1072 fs = fields.List(fields.Model(FF)) 1073 1074 o = obj() 1075 o.fs.append(FF(name="N1")) 1076 self.assertEqual(o.render(fragment=True), "<obj><FF><name>N1</name></FF></obj>") 1077 1078 def test_list_and_model_tagnames(self): 1079 class FF(self.F): 1080 class meta: 1081 tagname = "FF" 1082 class obj(dexml2.Model): 1083 fs = fields.List(fields.Model(FF), tagname="L") 1084 1085 o = obj() 1086 o.fs.append(FF(name="N1")) 1087 self.assertEqual(o.render(fragment=True), "<obj><L><FF><name>N1</name></FF></L></obj>") 1088 1089 def test_strings(self): 1090 class obj(dexml2.Model): 1091 fs = fields.List(fields.String(tagname="val")) 1092 1093 o = obj() 1094 o.fs.append("s1") 1095 self.assertEqual(o.render(fragment=True), "<obj><val>s1</val></obj>") 1096 1097 def test_model_field_tagname(self): 1098 class FF(self.F): 1099 pass 1100 class obj(dexml2.Model): 1101 fs = fields.Model(FF, tagname="subFF") 1102 1103 o = obj() 1104 o.fs = FF(name="N1") 1105 self.assertEqual(o.render(fragment=True), "<obj><subFF><name>N1</name></subFF></obj>") 1106 1107