xmlunit-1.6/0000755000000000000000000000000012451007672011562 5ustar rootrootxmlunit-1.6/KEYS0000644000000000000000000011416512451007364012266 0ustar rootrootThis file contains the PGP keys of various developers. Users: pgp < KEYS gpg --import KEYS Developers: pgp -kxa and append it to this file. (pgpk -ll && pgpk -xa ) >> this file. (gpg --list-sigs && gpg --armor --export ) >> this file. pub 1024D/5F6B8B72 2001-05-28 uid Stefan Bodewig sig 3 5F6B8B72 2001-05-28 Stefan Bodewig sig 51898504 2002-01-11 Conor MacNeill sig 3 F88341D9 2003-03-17 Lars Eilebrecht sig 3 2261D073 2003-03-17 Astrid Kessler (Kess) sig 21D0A71B 2003-03-17 Dirk-Willem van Gulik (Chief Internet Architect, role) sig 75A67692 2003-03-18 Erik Abele sig B3B2A12C 2003-05-20 ct magazine CERTIFICATE sig 3 8103A37E 2003-04-04 Andre Malo sig 3 5F6B8B72 2001-05-28 Stefan Bodewig sig D6298F01 2003-04-27 Paulo Henrique Gaspar Jorge sig 0CAA68B4 2004-11-11 Patrick Rentsch sig 2FE28BCF 2005-07-01 Harald Wilhelm (HAWI) sig 5793498F 2005-07-21 Tim Ellison sig E4136392 2005-07-21 Noel J. Bergman sig 8408F755 2005-07-21 Christian Geisert sig 2 FC243F3C 2005-07-20 Henk P. Penning sig 3 EC140B81 2005-07-20 Dirk-Willem van Gulik (Chief Internet Architect, role) sig 3 EE65E321 2005-07-20 Martin Kraemer sig 3 A99F75DD 2005-07-21 Rodent of Unusual Size sig 3 3642CB4B 2005-07-20 Martin Kraemer sig 3 302DA568 2005-07-21 Rodent of Unusual Size (DSA) sig 3 2C312D2F 2005-07-21 Rodent of Unusual Size (DSS) sig 3 CC78C893 2005-07-22 Rich Bowen sig 3 E2D774DF 2005-07-22 Sylvain Wallez sig 3 E04F9A89 2005-07-22 Roy T. Fielding sig 3 015AFC8A 2005-07-22 Bertrand Delacretaz sig 3 87315C31 2005-07-23 Raphal Luta sig 3 E41EDC7E 2005-07-24 Carsten Ziegeler sig 3 F39B3750 2005-07-24 Colm MacCarthaigh sig 1CD4861F 2005-07-25 Eran Chinthaka (IU Email) sig EA1BA38D 2005-07-25 Ajith Harshana Ranabahu (Made at Apachecon 2005) sig 333E4E84 2005-07-26 Chathura Kamalanath Herath (Apachecon Europe 2005) sig 152924AF 2005-07-29 Sander Temme sig 3 9C85222B 2005-07-24 Henning Schmiedehausen sig 3 9978AF86 2005-07-25 Christoph Probst sig 3 2A623F72 2005-07-25 Christoph Probst sig 3 F8EA2967 2005-07-26 Brian McCallister sig 3 C152431A 2005-07-27 Steve Loughran sig DE885DD3 2005-11-25 Sander Striker sig CE419C8F 2007-01-05 Upayavira sig E222DE4F 2007-05-02 Mathias Herberts sig 911203E4 2007-05-02 Mathias Herberts sig F12F6072 2007-05-05 Fred Vos sig 3 990ED4AA 2007-05-02 Knut Anders Hatlen sig 3 311A3DE5 2007-05-05 Ruediger Pluem sig 3 88817402 2007-05-06 Thomas Vandahl sig 5F298824 2007-05-06 Simon Pepping sig 4CEED75F 2007-05-06 Nick Burch sig 4358C584 2007-05-06 Vincent Hennebert sig 0B7E6CFA 2007-05-06 Sami Siren sig 3 01530235 2007-05-02 Luc Maisonobe (general purpose) sig 40581837 2007-05-08 Nick Kew sig 6BD872A0 2007-05-17 Michael Busch (Lucene Committer) sig 6210BFC0 2007-05-17 Jean-Frederic Clere sig 0F143BC1 2007-05-22 Matt Hogstrom sig A46C4CA1 2007-05-22 Matt Hogstrom sig D1AAEA60 2007-05-31 Reinhard Poetz sig 084C9113 2007-07-24 Brett Porter sig AF5EC452 2007-07-30 Dennis Lundberg (CODE SIGNING KEY) sig C3110611 2008-02-23 Petar Tahchiev sig 51047D66 2009-03-25 Tony Stevenson sig 6A017B17 2009-03-29 H.-Dirk Schmitt sig 3B7C75B1 2009-03-31 Gilles Scokart (at apache) uid Stefan Bodewig sig 3 5F6B8B72 2003-03-07 Stefan Bodewig sig 3 F88341D9 2003-03-17 Lars Eilebrecht sig 3 2261D073 2003-03-17 Astrid Kessler (Kess) sig 21D0A71B 2003-03-17 Dirk-Willem van Gulik (Chief Internet Architect, role) sig 75A67692 2003-03-18 Erik Abele sig B3B2A12C 2003-05-20 ct magazine CERTIFICATE sig 3 8103A37E 2003-04-04 Andre Malo sig 51898504 2005-06-21 Conor MacNeill sig 0CAA68B4 2004-11-11 Patrick Rentsch sig 2FE28BCF 2005-07-01 Harald Wilhelm (HAWI) sig 5793498F 2005-07-21 Tim Ellison sig 8408F755 2005-07-21 Christian Geisert sig 3 EC140B81 2005-07-20 Dirk-Willem van Gulik (Chief Internet Architect, role) sig 3 EE65E321 2005-07-20 Martin Kraemer sig 3 A99F75DD 2005-07-21 Rodent of Unusual Size sig 3 3642CB4B 2005-07-20 Martin Kraemer sig 3 302DA568 2005-07-21 Rodent of Unusual Size (DSA) sig 3 2C312D2F 2005-07-21 Rodent of Unusual Size (DSS) sig 3 CC78C893 2005-07-22 Rich Bowen sig 3 E2D774DF 2005-07-22 Sylvain Wallez sig 3 E04F9A89 2005-07-22 Roy T. Fielding sig 3 87315C31 2005-07-23 Raphal Luta sig 3 E41EDC7E 2005-07-24 Carsten Ziegeler sig 3 F39B3750 2005-07-24 Colm MacCarthaigh sig 1CD4861F 2005-07-25 Eran Chinthaka (IU Email) sig EA1BA38D 2005-07-25 Ajith Harshana Ranabahu (Made at Apachecon 2005) sig 333E4E84 2005-07-26 Chathura Kamalanath Herath (Apachecon Europe 2005) sig 152924AF 2005-07-29 Sander Temme sig 3 9C85222B 2005-07-24 Henning Schmiedehausen sig 3 9978AF86 2005-07-25 Christoph Probst sig 3 2A623F72 2005-07-25 Christoph Probst sig 3 F8EA2967 2005-07-26 Brian McCallister sig 3 C152431A 2005-07-27 Steve Loughran sig DE885DD3 2005-11-25 Sander Striker sig E222DE4F 2007-05-02 Mathias Herberts sig 911203E4 2007-05-02 Mathias Herberts sig F12F6072 2007-05-05 Fred Vos sig 3 990ED4AA 2007-05-02 Knut Anders Hatlen sig 3 311A3DE5 2007-05-05 Ruediger Pluem sig 3 88817402 2007-05-06 Thomas Vandahl sig 4CEED75F 2007-05-06 Nick Burch sig 4358C584 2007-05-06 Vincent Hennebert sig 0B7E6CFA 2007-05-06 Sami Siren sig 3 DE8884A0 2007-05-07 Xavier Hanin sig 3 01530235 2007-05-02 Luc Maisonobe (general purpose) sig 40581837 2007-05-08 Nick Kew sig 6BD872A0 2007-05-17 Michael Busch (Lucene Committer) sig 6210BFC0 2007-05-17 Jean-Frederic Clere sig 0F143BC1 2007-05-22 Matt Hogstrom sig A46C4CA1 2007-05-22 Matt Hogstrom sig D1AAEA60 2007-05-31 Reinhard Poetz sig 084C9113 2007-07-24 Brett Porter sig AF5EC452 2007-07-30 Dennis Lundberg (CODE SIGNING KEY) sig C3110611 2008-02-23 Petar Tahchiev sig 51047D66 2009-03-25 Tony Stevenson uid Stefan Bodewig sig 3 5F6B8B72 2005-05-31 Stefan Bodewig sig 51898504 2005-06-21 Conor MacNeill sig 2FE28BCF 2005-07-01 Harald Wilhelm (HAWI) sig 5793498F 2005-07-21 Tim Ellison sig 3 EC140B81 2005-07-20 Dirk-Willem van Gulik (Chief Internet Architect, role) sig 3 EE65E321 2005-07-20 Martin Kraemer sig 3 A99F75DD 2005-07-21 Rodent of Unusual Size sig 3 21D0A71B 2005-07-20 Dirk-Willem van Gulik (Chief Internet Architect, role) sig 3 3642CB4B 2005-07-20 Martin Kraemer sig 3 302DA568 2005-07-21 Rodent of Unusual Size (DSA) sig 3 2C312D2F 2005-07-21 Rodent of Unusual Size (DSS) sig 3 CC78C893 2005-07-22 Rich Bowen sig 3 E2D774DF 2005-07-22 Sylvain Wallez sig 3 E04F9A89 2005-07-22 Roy T. Fielding sig 3 87315C31 2005-07-23 Raphal Luta sig 3 E41EDC7E 2005-07-24 Carsten Ziegeler sig 3 F39B3750 2005-07-24 Colm MacCarthaigh sig 1CD4861F 2005-07-25 Eran Chinthaka (IU Email) sig EA1BA38D 2005-07-25 Ajith Harshana Ranabahu (Made at Apachecon 2005) sig 333E4E84 2005-07-26 Chathura Kamalanath Herath (Apachecon Europe 2005) sig 152924AF 2005-07-29 Sander Temme sig 3 9C85222B 2005-07-24 Henning Schmiedehausen sig 3 9978AF86 2005-07-25 Christoph Probst sig 3 2A623F72 2005-07-25 Christoph Probst sig 3 F8EA2967 2005-07-26 Brian McCallister sig 3 C152431A 2005-07-27 Steve Loughran sig DE885DD3 2005-11-25 Sander Striker sig E222DE4F 2007-05-02 Mathias Herberts sig 911203E4 2007-05-02 Mathias Herberts sig F12F6072 2007-05-05 Fred Vos sig 3 990ED4AA 2007-05-02 Knut Anders Hatlen sig 3 311A3DE5 2007-05-05 Ruediger Pluem sig 3 88817402 2007-05-06 Thomas Vandahl sig 4CEED75F 2007-05-06 Nick Burch sig 4358C584 2007-05-06 Vincent Hennebert sig 0B7E6CFA 2007-05-06 Sami Siren sig 3 01530235 2007-05-02 Luc Maisonobe (general purpose) sig 40581837 2007-05-08 Nick Kew sig 6BD872A0 2007-05-17 Michael Busch (Lucene Committer) sig 6210BFC0 2007-05-17 Jean-Frederic Clere sig 0F143BC1 2007-05-22 Matt Hogstrom sig A46C4CA1 2007-05-22 Matt Hogstrom sig D1AAEA60 2007-05-31 Reinhard Poetz sig 084C9113 2007-07-24 Brett Porter sig AF5EC452 2007-07-30 Dennis Lundberg (CODE SIGNING KEY) sig C3110611 2008-02-23 Petar Tahchiev sig 51047D66 2009-03-25 Tony Stevenson sub 1024g/24774157 2001-05-28 sig 5F6B8B72 2001-05-28 Stefan Bodewig -----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.2 (GNU/Linux) mQGiBDsSIk4RBADSCj6rUjV64tYCGT1DYKYR7GthyWpNdGHSYLbETBcDatAe1dzQ 5NsCgfrlybfyeY+y1lxr3T9bqf6zJWDw/718wff96qmmv1qzexSYtmIrj+h53V82 EXwWOFuYMJisuxdT940iQzosm3GOv4MJdEg3oI2SgfEyRQQ6vO4Ob5rHDwCg5taZ nrHOrXx2dIGHxpxRZ0SUl30D/jmtttFjYOQ3LBMriikz5mh2sK3ZnoSRF4o5O0zW Ve6e2SFXOEjVjImKsH6KCbdQNelrAdgiyOoXClyQKsQ27pncbdWo6bO0E3POJZVm XaeW7iudHVr63rU5PViXObIQrdQl0D59j5brKj4vdlTyUw8kaHPvbKPDEOwvZq4Y LJQ5BACA1YilTeXRJqwFsNlpcxCHwlULD4QUVP496prQWf1B7Z6g0KvLGrQsO0Vn Jcn+fEqukysTJixSXCPebosltd4RalJIupVYkp4w6MJ7biaDAlLuNhDcI/AiXTmV dXUedVXIaM8I3Ne23gucwbAyc0Hvb+3cSAKRhl/azFQhuHBvlrQjU3RlZmFuIEJv ZGV3aWcgPGJvZGV3aWdAYXBhY2hlLm9yZz6IYgQTEQIAGgULBwoDBAMVAwIDFgIB AheAAhkBBQI7EiJPABIHZUdQRwABAQkQohFa4V9ri3KPOACfdr6cV41veYBlBHiV FxfLcX7x5OEAniK4u3g2jpNQH3E0ROubdj/RO+RTiEYEEBECAAYFAjw+1Y8ACgkQ gQRkT1GJhQSdkgCeM6RDHUF/E334TtiLPgw7GpmNJSkAoNCLQCW/9VHrV+ZHsodn XUnaD4dIiJkEEwECAAYFAj513wwACgkQPo+38viDQdknZAPlHNiMnR+LUavo2yOY iJT+W9+8+qNs2grYDZ+WSYujaWT2NJrUCYXQRM6gKDyFlkcJvHI9lF2yYMkVetll ZVN1TJkeEdtbHncNHcdq+ZUQR0NkFKTF9d1K7UI2rfWxt1y6a13TcUjpJXzbtw/O XX9EZSI6QQt4rSFlvci9J3mIRgQTEQIABgUCPnXawQAKCRDu0eo5ImHQc0W4AJ9v uq4wlkc6TmmmZPF/gZVLluHcTgCeItrnvzyS11xkIETk6v4b7K4gaiiIPwMFED51 qhr9b4jGIdCnGxECRAUAoOaVZW5CdZ9oYr3PwI/i8RJN+JfJAKCmd/XIlYOCpa9Q c4C855pM8NFw6YhGBBARAgAGBQI+d6QQAAoJEBU/oM11pnaSL+sAn1DTHmbhITeE w0ZSgyBLQw2ZhcM5AJ0ZrRBbZ9lbgHXBKOJQiLpWBj4XsYhGBBARAgAGBQI+yi6W AAoJENvSRfyzsqEsF/AAoNXq7Cp/0AwEmWvhoTjmtY6eVYB5AKCMFhBUdYWNXVya lPTq8ThswNUnr4hGBBMRAgAGBQI+jc4sAAoJEMppOXSBA6N+kUoAn1Nj6YqarQg0 sL2KrFsQROM3A6fSAKCyl40SpfVJSO33fYuPci9dHp+QCIhXBBMRAgAXBQI7EiJO BQsHCgMEAxUDAgMWAgECF4AACgkQohFa4V9ri3IsngCfbIpJDWj6UgXY7rBH8To1 2BgB+RIAn3jw72WJzplAtShVTmuMlRFS+FUNiEYEEBECAAYFAj6sazwACgkQqywx 6dYpjwFkeQCeOkJrnO5r2hWDhX4ACPPLObZvXLIAnR0VHAgkEH1W/t7B4zdDYdBB Zrd5iEYEEBECAAYFAkGS8mMACgkQ5BNhMwyqaLQs9ACgio5zJcieYLppigvSYLBf ubUVrXUAnRKZJ6MACpH6fpoz2vkc2dh69tbSiEYEEBECAAYFAkLFMoEACgkQm/Ij RS/ii88aCQCfd1cIawDqpkYU86f3JEjcN85ntFcAni0m8WR6s+bkh3fd+EIrSRsr u3uQiEYEEBECAAYFAkLfRQIACgkQQeoJoFeTSY8XxQCdFd+XEWqyDkCx37gaIQAG 4dHpwiUAoOZ/K5OHyTJCNFaBUDtpCh7hL8TPiEYEEBECAAYFAkLfkncACgkQAQVm vOQTY5L3SgCgiEi5/1vYvJrKoAdl0hRWU57ieUIAn2n08BQfMZJQ439aNW/CnIK8 jPBPiEYEEBECAAYFAkLgNdAACgkQc84u+4QI91XdNQCgoBB1ebohIflinAPlvI37 pFHuu0MAoJ4yMtbKZMaq0xIBnxV9c5uu99tGiEYEEhECAAYFAkLerWMACgkQi5Yp Q/wkPzxD7ACgqKnyeb/fjVS8vov4FePxeLju4msAn1SCGaiF9gEf+qIaZUnjcT7J DJ96iJwEEwECAAYFAkLerG8ACgkQMaY9luwUC4Ea9gP/WON+0xIWOvWP7mKkg/+X 0ukW+mbjE426qKtG/B0vNrTKpElmz8ttR+oajqbg20LazoEUuA9ZXjLPfsdWA+vF kxgV6qIdtxYPMamPm7ytEBOmgMowYXUftGteqM5fxLlceHiwdUlynG2fmtMqvPnd 2OCezSFRx3W6nvAiIjoLZpCInAQTAQIABgUCQt7H0wAKCRA34/Rf7mXjIcAUA/4n DlQbnToSSDOZkFj1CoGL8TjsVgzrO3r3S3x38uQQTFAE/AGBY4mtHgNcYmiJaC2h N1Y+mlEGu/80Rjv185ZfJsFEerU6Y/9tRJJ1So9AAe5AmvGpD9ysXae5geB+k+ep IMSuf9WMeTRUCbQs9ufGZLV5a8jqstv+btcrzNaY9oicBBMBAgAGBQJC32x4AAoJ EJrNPMCpn3XdRBkD/iNi0Y6A3afDG9ZL/K4JrOPgHUFWC/DgAEBme4AY62agUsT0 uXlz+Mu1Ps2E0t26ejScuVMMvqpXg7iJ2+3yKzsnX0ySEXW6/696XEpe3TFn1iVO mMElPKxakn3t/jr6SDepo9jqD5P5CJR4GsDsG3iKIisWdDf81ZXpf86y7A5eiEYE ExECAAYFAkLeuuUACgkQMsnkzjZCy0vmSQCdHGC6jOEVo96yyospTq7bL+EEeioA oNMKIZy5qFLXXZbSNvsj7mDRg2c8iEYEExECAAYFAkLfbHoACgkQUI6uxTAtpWhY hQCaAvqVBsTX5s4c+sTOo06BNMdzHIUAoIwpThAKq936Szy/3Gfv8K3gs5NOiEYE ExECAAYFAkLfbHwACgkQ3bpkuiwxLS9z8ACfYeocOK4J204xwbXgEdUJQyvHK2UA oKz2AF1I2b8Ebu7vTUZLNFV1QMtwiEYEExECAAYFAkLgyTgACgkQXP03+sx4yJNb EgCfRcj6QKHVHQtYVXdCYKUbrj97wAoAnimqV15cvz1siDjUK9K/aTskGwajiEYE ExECAAYFAkLg7MsACgkQybWm7OLXdN8UoQCdFfqef8My1xhn6mLd9WTLLaIewTQA nRXGh/Af4hVG0KwtZcJEA464nCoJiEYEExECAAYFAkLg7TwACgkQW5aAEOBPmol+ JwCeLxZjKNisjgP4AxV5BCKR+5SU9NoAoIwPF/7B2NmGNR0t3EZze8wpNhQ0iEYE ExECAAYFAkLg7V8ACgkQN/aP9QFa/IqerACfafKJi4s8LYV2JxNfQKHgmRXzeIIA oNBHOzukDCdxIvmYJfamItnCP45giEYEExECAAYFAkLiYm8ACgkQbZiNF4cxXDH8 HwCgq8P29CwMX7PKhRmY3T32APsOaMEAnjdd/WvzVBFtTcJFWkH6iF4L8EQpiEYE ExECAAYFAkLjVb4ACgkQEy5J1OQe3H56DACcDPfWLO5cDkeKFCvIP8mc4p4KkfkA oJITROldIRxXqUiML1oTJxieuHJfiEYEExECAAYFAkLjZNoACgkQdcqio/ObN1CI tACgsJhqBxeZTaSrRVNk3aj6ciAJrgEAoIxPXYTvIpnWBr4/WMbN0jpV0TGEiEYE EBECAAYFAkLkbxIACgkQjON2uBzUhh/gZQCbBpIqkCEuIbd6tqChz3PzcIGiZbgA njluBFHl4l1/NHtP9fEYCgl8nbCviEYEEBECAAYFAkLkkr4ACgkQBJE0Quobo42f +QCgjtO6EOdDRiruCi6gKvwM1a2eRwcAn0XUELm5AZezL5E0rEfIM2FBiMi5iEYE EBECAAYFAkLlwh0ACgkQYRlqLjM+ToS9pwCfUEgO834XY/clWzkw/VLBfe7MLZQA mwdz0nleOHYWFBrnYgEz53d4MxUPiEYEEBECAAYFAkLqY/QACgkQsr68QBUpJK/o MQCfc7M9KpApCWW7eE22PlLoN1sPK+4AoJdwE8TsDM2Pmehk9K+uHIx6FoRviEYE ExECAAYFAkLj7WcACgkQMoZOQZyFIitClACfWpH0+V/N6vuucWZ7bsMm2BcmM3oA n3fF5qqovlog4/PcgvKCToNEF8uWiEYEExECAAYFAkLlELcACgkQUnkvr5l4r4YU ZwCgg7vJpDpUXnuNvgc5RHgG7UYhRQYAoIEKHsrswh6XzVn5yQRkfjdB/A0OiEYE ExECAAYFAkLlEaQACgkQa3OhBipiP3JA4QCffb8NgQssOQXaVR0dSwPCeU2nQPUA n15EAjykVZsUi2tZWqEM08SNOKI9iEYEExECAAYFAkLmmWIACgkQaOuMdvjqKWd7 AQCbBpwyitQ77kd9KIT6y95Im1vmWt8AnAnkNTBctVtMfwddYTG+xLkaOllOiEYE ExECAAYFAkLnYVAACgkQbpR1lMFSQxqIRACffQqUXTgOa4hyHYQBUwrlGEqmWt4A nRMXVGhd47loS27MmiEiWwDlkNjJiEYEEBECAAYFAkOHn54ACgkQZjW2wN6IXdOr 9gCgh2fn26W0DSL5WZATvvQkwZeJNiMAnR6+0AlUK8uFSFIVhl+RZMnY+XFwiEYE EBECAAYFAkWdnk0ACgkQIYJJVs5BnI/0SgCeKCw39INy9ISFunlAojYgSInHfokA n2vU8q4JNjg13qNeclZN9kmN9mbWiEYEEBECAAYFAkY44sMACgkQFUWz/uIi3k+q vACffppBpoY82MEvDV7c4/6cjw544CQAoJAPCdZA/LRqICJm0iFbDrwhsSb6iEYE EBECAAYFAkY4558ACgkQY9CtrpESA+QrAACglRB/VdEmovbyWdMDmsTdyw4kha4A n0uKwZeKHfBR3cC2s7MvqqmMoz9jiEUEEBECAAYFAkY8kyoACgkQmHDv8/EvYHIk CgCYgXQZTJ8VmHwSX3pXOxnMhp7mbACeIPXwcPvmfP709nfgQ8/GpT2z9ISIRgQT EQIABgUCRjkasQAKCRDh4fKwmQ7UqhZKAJ9iraDBstzeXPMtst3x+ZXdLQm7cgCf WDDgaQOa8CoM5/+7WCtkyasP6BiIRgQTEQIABgUCRjxQRwAKCRBMBCgYMRo95eP4 AKCuEQU6fjPy/cPEiqhGH23J2YEr7gCfS8vBTEU4sRbOomTEuINPxb96OZmIRgQT EQIABgUCRj2gkgAKCRAuuUaCiIF0AgOBAJ0bJmFzA9WkG5FmfaP4ieG9+SCbXACg w+2wcOA/B94LKRtjhJT6j6zSiDmIRgQQEQIABgUCRj4VvwAKCRA+Km/CXymIJIvc AJ9QSE4mCQldVnpbYwLTCk+xHDqhcQCggT9P3/rHIzIvv1tJ+A1ZJPvXOcqIRgQQ EQIABgUCRj3WeAAKCRD1wmAWTO7XXwpbAJ4mr2IxFtx0ppkefxx0l0TJ6cFkrQCd EFbc+aMxRKhK9SCAWi3mq1UqEWiIRgQQEQIABgUCRj31AAAKCRCgctTQQ1jFhByK AJ9SIielTuD3StxPQpBkAkYP6Ld88ACgg1oPX9ryJA7YuhMD7byXQsETzD+IRgQQ EQIABgUCRj4FxQAKCRACpaYFC35s+k/GAJ9/VDyw2vNzk1xjcu/QZCa3gGI2zgCf eG8klJ78bAGknzxBlK3XtmoNqASISgQQEQIACgUCRjj3hAMFAzwACgkQc92MFgFT AjVJogCeL+3FTTVR5snJx9qbGQsgv23ZaT0An2Hy1CcXVklcYBF7LbnbAgbe1Hpf iEYEEBECAAYFAkZAtkMACgkQbQvHOkBYGDePegCbBe6rmz9/kYDV7w5pvwnugVsv biEAniTfLW7NW8z1SRBWf6lMH3clGAs8iEYEEBECAAYFAkZMRFMACgkQHyEjw2vY cqB22gCg1np1JYFYPqCB3ekZts3K+pn7RkwAnRWd6HmtjRolZdrZfkqQDJKmd5zv iEYEEBECAAYFAkZMfQEACgkQD0UKJmIQv8DJYgCfW0C9rDAToLU+0BKLYCiWwtFJ 98MAn2HvQ3CDhv8WTm+av36lETLqhjnfiEYEEBECAAYFAkZSb1kACgkQMsHW7w8U O8GGZwCg0l2T1O/OpOECXs/vYE2649wNTaYAoLrUpLKYev8uHAfc53lZ6LE0h1T0 iEYEEBECAAYFAkZSb2AACgkQy66+OaRsTKHZbwCdFSloWJh3uuTLk87aSt4uYeZr KToAoIrN7epZxeu9n9e6hqVOLz85zc3TiEYEEBECAAYFAkZe1aoACgkQmobXzNGq 6mD+cwCg3k4BRrRi6pjrY/UggHjhiHWSD1YAniDQn1MVB620Ik2cVL7hR1V0ZL6b iEYEEBECAAYFAkalTCwACgkQOb5RoQhMkROqQwCdHhIdklVR341azVFBO6aGArSO P2QAn0WtSIiqaLTEQ57+ir62FxRYBQdWiEYEEBECAAYFAkatzFQACgkQM81nM69e xFIdRgCfSGft6KIZ+CTEPIGr8lp8oOpNaHMAn1NCXZTJOW+r0G5ply4hlu8UXC4A iQEcBBABAgAGBQJHwH2YAAoJEBllhVDDEQYRZ4cH/3XnLW6UAdDd4k0xl2lUAj9g B7ITUbejCwvnFqUyKAE9P38boBHNfc6cliQUOz4ITWDPhiinbjNnJHglp9vK0o4R /tFFyGImIvbmu1C8lyO2BJPgF2yMNrBgZhx0+IkAG3R4iy9JFIDGgddjLQSP4TX3 uRUFUXEAhHzGA//XP4tnC3CisvOsuoc6ZjyZGSt/HUzZoKf+wsdJlfabiK3QpD8l SOw8KEZF54JUC8uaYGuBGs7ih4FcO+Aqb52UAx4/+13eEdAognVF2HbaiI+G2jEe kyAwD0bP3DWyg+9fGBtnwtDMj0OrHklvA8qoHxAMvXHIGhxjqZBOFehh8DNEB6SI RgQQEQIABgUCScqH2QAKCRDJx5JOUQR9Zj6WAJwOtRlhq45DedrYNH54QIJSFw3X JQCfQI9fZl6zmKWSm1nJqXRC+awKmwyIRgQQEQIABgUCSc/UtQAKCRAkwaN4agF7 F75XAJ0TyTdCMGIZGCooM/xr3w+qvyZLgACg0W8O9WOf0qwSVgynmh2vQggUiyKI RgQQEQIABgUCSdI2jwAKCRCusBoVO3x1sZHiAKClsXinnJfHMQYewFPqy16zr//f 4ACgulnu+ObADHMquuGCw4BLwrvqMIK0IFN0ZWZhbiBCb2Rld2lnIDxib2Rld2ln QGJvc3QuZGU+iF8EExECABcFAjsSOYEFCwcKAwQDFQMCAxYCAQIXgAASCRCiEVrh X2uLcgdlR1BHAAEByboAoNoD/9Jgm/alxfAYELz05LMa/HLeAKDWTHqq7rMkppZo TUv2gWpVzrk5RIhGBBARAgAGBQI8PtWVAAoJEIEEZE9RiYUE0LMAn22/u01Lo3Bo 5lDxxHSkayUkYq25AKCm20yaGFGtTDJW4Rdz50pfut1AwoiZBBMBAgAGBQI+dd8P AAoJED6Pt/L4g0HZWboD4gPGJi0y93+Zp37uFGgpe8PkB10HVLCe9B0l7R7BK0UF hnFl004td2RWeALAAnOI8ZlxCahwQdUys34zF77c5fQ8Rn7co46wBSL59Oi/bG9/ wRYqBf13SWL2ITK1UDgzRznZrds9MLQqSL8oBjebyg28CZPBYH10FKigUUMwiEYE ExECAAYFAj512scACgkQ7tHqOSJh0HOu5gCcDO9Ou8NA2+gChoNAn6j/J2owDxkA nA0Q5AMezP7rKdsw+hCYqZSp8QhIiD8DBRA+daoh/W+IxiHQpxsRAiSnAJ4id/ij cLliSH/EGh1UiaunYK9zLwCgyfeZ7mnhKXauba2NXFMlm3axSvuIRgQQEQIABgUC PnekGgAKCRAVP6DNdaZ2kikaAKCJMBE/oJ/4ko7FRpUWvQv0MLmhRwCgjEXsPmY5 Ur8AVynVzE2TcEu12reIRgQQEQIABgUCPsouMgAKCRDb0kX8s7KhLABsAKCU2ntX Y/DhTnvki6igzrvttl/ynACfZTZNwePs9imtT6phGTInelrsXLKIRgQTEQIABgUC Po3ONQAKCRDKaTl0gQOjflg/AJ4khT+aic33qc/iMmMC5+URcxt6ZQCgleruhUJi 44Kpav9PdVbQMzdb52eIRgQQEQIABgUCQZLz6wAKCRDkE2EzDKpotDZHAJ4xwN/h tv44yNFQnACTYsc322HjZACfTd9WoxRkRWY6tVd9YgumNc0swMiIRgQQEQIABgUC QsUyhgAKCRCb8iNFL+KLz+ClAJ99ddEJ5l/VW/mKHvTITZleDSv+uwCglgqx3HQr lqp+gTPKIEKPkjjom+GIagQwEQIAKgUCQuE4ECMdIFRoaXMgd2FzIGEgam9iIGFk ZHJlc3MgYW5kIEkgcXVpdAAKCRCiEVrhX2uLcvEYAKCJD7CVpr2Iw657kO6G3Is8 xKa6IgCgiStyJgU5/dUEEPQctZ8ZVZSrHNGIRgQQEQIABgUCQt9FDAAKCRBB6gmg V5NJj+d2AJ9QRCXhFzmee7cbhlfejg7LBsXsMQCfce2/Wz+if56L7WaZLpn893CA zu+InAQTAQIABgUCQt6scQAKCRAxpj2W7BQLgXUkA/96klgNlfh+VTSxrwCUW1JE 5j87qDeJWrnN5ibVYPd7TE45hNeWQie2RgWGpsHNlDekVh9aZuHMJb9NzRGKAAJ2 augQQuvDKt8sge+ydRMXsLkAvpK4VBmobqqgyO0cV3ooMyizawMRndVcMbVu5b6G kdj2tZEko/Nv9KBJ61MJ64icBBMBAgAGBQJC3sfZAAoJEDfj9F/uZeMhrGYEAKJg LDFku3GdpF/BI4GQBKqadLygF3Igq9Np310sTcLOI2ARb4B18Tvq9CyR4PEvdlVC 5uEpaJozgHthTadjGTgg1WmiTWqG31s3U+zL5NLdK+k8qqrxGLzFzhk8PB1wJwIm JcvLmJHm3HeIGycdEzn4swgmD4uI6p39mcGyCCONiJwEEwECAAYFAkLfbHkACgkQ ms08wKmfdd2sxAP/e8W2cqyypPqYHs05nTxNzD5wLl72ABWvljfdf5mA97sEl3q4 8234j3sUN1Uk6c21NlK+eRBn8Lv1ihyLTJkACgdiXNFvi1eC4vLhQMGOPcGW8+wI 4olmsqftvG+2hNt4eCMead6IjAK7LNKgDWEBjGI+WIOvC5UJBO50cNXGOXWIRgQT EQIABgUCQt667QAKCRAyyeTONkLLSxJgAJ9faCKziDmN6nQeMoAECTfVvIdTRACg jnb3h8sc54gcosIh28qb7uBUuf6IRgQTEQIABgUCQt9sewAKCRBQjq7FMC2laDoH AJ9VC11NFs0+BAYWoZBJSUEnjn3F9gCgsqGPrxhTBkHlWAh4iiumq31tZHaIRgQT EQIABgUCQt9sfQAKCRDdumS6LDEtL3hJAKCEHj7lHAZHRk7LLbFQDh7oiY7plACg iORbBhF3VWn1JCglbk51Kq5hJy2IRgQTEQIABgUCQuDJOwAKCRBc/Tf6zHjIk6wA AJ4qjf2FNE1VXK+PnL2iFP1h7f8L4wCfbtoQqsaDE1vCrnSobEUT6nfqPt+IRgQT EQIABgUCQuDszAAKCRDJtabs4td03yLQAKCz5pbjUWdyEHQr85R0He3QuDiLkgCg z6XQ/LFLdcmwDAj4lsKbRpHdUDyIRgQTEQIABgUCQuDtQAAKCRBbloAQ4E+aiRuo AJwLeKfpT6aqNLBvrusHnNNjROFi5wCgjhXup7RcdMNTDBY6BGj83NHuTU6IRgQT EQIABgUCQuJibwAKCRBtmI0XhzFcMZwOAKCLkKunJnUNy7QgowvTkV+/DyU+FgCf ScvQFzMSj1Gk1ViDbK0n5i2MpQWIRgQTEQIABgUCQuNVwAAKCRATLknU5B7cfur2 AJ9XnFPKjlIPsbrZVJRuNh96py7FfACgoC5yGwyRq9hYK3SMGGAu5MmQWpSIRgQT EQIABgUCQuNk2wAKCRB1yqKj85s3UB1kAKClSCLmqecNSlVeFOwlSijhTjzmxgCg 5eYxuHJo4wf2D2d1gWbloc8xt/2IRgQQEQIABgUCQuRvFAAKCRCM43a4HNSGH1Jz AKCoUQuAh01aTLbbUS4WCMrOAQblagCfdwFlsT48wWEBnJSFAiXaEcRtUkiIRgQQ EQIABgUCQuSSxwAKCRAEkTRC6hujjcShAJ9EK1u8wehMaZLt2ZnexHICPhbtagCg kN+i7LXBnm1IwlP5cGbmgW3BJRKIRgQQEQIABgUCQuXCIAAKCRBhGWouMz5OhEAf AJsHEwc1jK9tiYBvWRMS3zJ0XrrShgCffOyuZlrBNeuO9s8T9WkL7/vCnOmIRgQQ EQIABgUCQupj+AAKCRCyvrxAFSkkrxWDAJ9oJHjkm3MWfPS/iMK6iipoUaAfzQCf YFygT+mws9MQIZEMoTi/sk0AOcKIRgQTEQIABgUCQuPtagAKCRAyhk5BnIUiKxsG AJ4mMBcsZ/PlqEN2CjOoNits7PFYbwCeLuEXDDEcUAh7jb46wvrHB5EPjp+IRgQT EQIABgUCQuUQtwAKCRBSeS+vmXivhlvNAJ4wGMXMO8EgWYrlU0i+9wrd6N0M/ACg vODXK0oKDcDQ55t8xf2evmJA7HCIRgQTEQIABgUCQuURpQAKCRBrc6EGKmI/cl6+ AJ4kaPB7Ois5KuLwhbEwmpO3e07OQQCgw1kJOjcCZwogIWG1222By45k1YCIRgQT EQIABgUCQuaZYgAKCRBo64x2+OopZ+DxAJ91h0aGRvukGqAWEafe4nnT6xj9CACf U91kJ9G1WB2T8lW/fkXt8mnlrUKIRgQTEQIABgUCQudhVwAKCRBulHWUwVJDGgmC AJ9DsO7lkpvuigmPoIX6d7vufFW5iACeMsXW1nX0DWf6E9pPgDaeZ+dba1GIRgQQ EQIABgUCQ4efngAKCRBmNbbA3ohd09++AJ9GFjNIUutctozuFNreIeS2xATWJQCf TUwt6nd4R13f5U0+iOsTwWVX6h2IRgQQEQIABgUCQ4efngAKCRBmNbbA3ohd06v2 AKCHZ+fbpbQNIvlZkBO+9CTBl4k2IwCdHr7QCVQry4VIUhWGX5Fkydj5cXCJARwE EAECAAYFAkfAfZgACgkQGWWFUMMRBhFLagf6AqFi2y+DPg+duogX5hHslLpeRVXb qEqX9bB2BzzinUhTmmRpEpiVnCkTd69scXh/ZVTECfA2zBYV67gp3eitUB7CDSeL ZwqQCIz42uF5ADq9oj+j6uf8pPmsk9qO4VZcr7mUwJ4tDy6znG7Qg5H7y4HRRQ8c wodDIa2jpLdQ+v9+fms4Nq5j/IJRmHjT7Ha6n78arpl8DlBtjjG0dpmKfBB9n68M biFLX19yIxO98X/nEoDCk6DuLX79Ratt4jEr08YCyJ4PfAqJKUy+F5jrKnp3G/qj 6H2N72vHZLzoZRfZjBzbpN3V9rPossxQauoRqmU5M9wFDnBoqyszMMU+KokBHAQQ AQIABgUCR8B9mAAKCRAZZYVQwxEGEWeHB/915y1ulAHQ3eJNMZdpVAI/YAeyE1G3 owsL5xalMigBPT9/G6ARzX3OnJYkFDs+CE1gz4Yop24zZyR4JafbytKOEf7RRchi JiL25rtQvJcjtgST4BdsjDawYGYcdPiJABt0eIsvSRSAxoHXYy0Ej+E197kVBVFx AIR8xgP/1z+LZwtworLzrLqHOmY8mRkrfx1M2aCn/sLHSZX2m4it0KQ/JUjsPChG ReeCVAvLmmBrgRrO4oeBXDvgKm+dlAMeP/td3hHQKIJ1Rdh22oiPhtoxHpMgMA9G z9w1soPvXxgbZ8LQzI9Dqx5JbwPKqB8QDL1xyBocY6mQThXoYfAzRAektCpTdGVm YW4gQm9kZXdpZyA8c3RlZmFuLmJvZGV3aWdAZnJlZW5ldC5kZT6IXAQTEQIAHAUC PmiBXAIbAwQLBwMCAxUCAwMWAgECHgECF4AACgkQohFa4V9ri3JPKACfXhFOr4gM MIQwEGvUb6J/X4asuzIAnje2H9T1r/jSVi1NbIEtQfbpmyQXiJkEEwECAAYFAj51 3w8ACgkQPo+38viDQdmGkgPnUYJfHlDrQ2jYJWuPDve9n4pV9OvyIllKyLdfULjs LWXw4Jm+fMQJgWr2GEov1ZYIyS2+rGDZpbz/vxaBVbgqM8TPdiXzxwU8AFq4k+Em 4fKXR58ByLSJjvLLza61spNHJennEUFp3tTpEsr4bNuuaoRYhtiKzTqNuoC+0x+I RgQTEQIABgUCPnXaxwAKCRDu0eo5ImHQc5lcAJ9FOoAL4uHMuqHXrrxsEVPJvEJ2 /ACggaOR62EziUqcqpdm/1Zy3P4T22iIPwMFED51qjj9b4jGIdCnGxECWssAoKnk R63bWGCFDB5YJtfZJ+nQq3TAAKD1bzG/kITQpIHsT2vCY1yBmlWbBYhGBBARAgAG BQI+d6QaAAoJEBU/oM11pnaS+9IAn0kFlXAzOhhu4r5fglMVuw5bTAd0AKCtYRX2 ESU/+tsE8vuXWtsMCGeI5YhGBBARAgAGBQI+yi67AAoJENvSRfyzsqEsSccAoKqG 39X2y6xoQa56nviF5iCj/oTFAJwLKx9GyKEUbSM9f/IQ7AsZPhHT54hGBBMRAgAG BQI+jc41AAoJEMppOXSBA6N+xYgAn1HbGamcXTDYSFjn3U5ik2vQa8nuAKC/uUM0 1gsdn+71EiAkmrjlH6H9MIhGBBARAgAGBQJCuAqrAAoJEIEEZE9RiYUE0MUAn3cW FVW03CDuMuA7TSUqTf/eWARWAKC/GjQnPZ0+g7NbDw68ZSY3k8WuuohGBBARAgAG BQJBkvP5AAoJEOQTYTMMqmi0GIQAn1mpdHz/wV++rSU2Qn7R32slfNdpAKDJPqpn mcN4MUmk90yZEeHAdJmrhIhGBBARAgAGBQJCxTKGAAoJEJvyI0Uv4ovP3YUAn1z8 L1EnLdIYttWrT1zs0E89Lz4BAJ4sFzcSq2NO3OZsEQfh9F6CrUe3UohGBBARAgAG BQJC30UMAAoJEEHqCaBXk0mPIUgAoOF/OCzco+n/3sGJ57m0+aqGLOwjAJ9bC86B 2hRvkaZlGm/ne3TzCgFGnohGBBARAgAGBQJC4DXTAAoJEHPOLvuECPdVsEoAn2hq 8w0JUtfJpu3SmMUkFZLBFm3+AJ48pxmjHXjoBJGpokXw4i9Cfb56BoibBBMBAgAG BQJC3qxxAAoJEDGmPZbsFAuBGDUD93nHzcDWR6AuDxDvaDp1xR8oJZHojeZ9sWSR QffiNJKF07vvdjGbygqiWbQmtTM9qBnAxEiBpSpJemBhvGTKivPkb9H88KQe2jqo wDO3IJorIVMT+eUFB+4pn5c+kFLD/IhG7ohZ2rBIc47L62FFuD19esxvMcVelrBF q67ZV4KInAQTAQIABgUCQt7H2QAKCRA34/Rf7mXjIY9vBADqrguzlNOJFkWvEkgd VXCCH2TP3OUuPZ9JQtbJfbkg1nn1hCnMITapNJlEE3yyjB7kEpBkmUtyKv3VSwOD spfis/ps6zJRIne/2R5xqtT38Hf/sYucoMZUceipFAoCLYOqkKJKwTpYM0401dUp uxp1y8Lq5N0SDtTW9fKMNs9jioicBBMBAgAGBQJC32x5AAoJEJrNPMCpn3Xdek0E ANLj3wh2FCukydsfSzqgjFDpkVjiBxbTtCep86flaLMrF3H0idckGCjMUf/JostB sjwCpL2VlCtvhwl5ZgIzm8luceHupq2iC5hvUN0IjKuIXMLbZP7DE+Lfcvw82nKt 8JNPpjosXiMhysQZms2XfuNP6f/Ey8J5jaB2Z2JCYWJBiEYEExECAAYFAkLeuu0A CgkQMsnkzjZCy0vBrgCglfCttIxFGqVTLTq/nhPnRFMHX9YAoM4Z05oTuPFxGWS4 RzxCzCb3vpsAiEYEExECAAYFAkLfbHsACgkQUI6uxTAtpWgipACfTBzbyb7Vc0jr 2FZDiXJFUSBCoEYAnR+ySWmedkcJWESStqRstD8T72mOiEYEExECAAYFAkLfbH0A CgkQ3bpkuiwxLS/5RACgm88lpYDxy0bYF9/ubFbvqCw6i9YAnjrQoOLRBtQ4QmIP R+T/9HReOeSciEYEExECAAYFAkLgyTwACgkQXP03+sx4yJNXxQCffoccUBWO51YI EM0mbsqpnKHtawoAn2xTiDgo3TjtbRJskADuw+QnATQLiEYEExECAAYFAkLg7MwA CgkQybWm7OLXdN8bPwCfeKtfQWKm8i+KuFJTIoMTvAN65QQAn2A9G1wtEN4Vzg6H fnTvtgus32rRiEYEExECAAYFAkLg7UAACgkQW5aAEOBPmonAkwCfeF3l1lE2WijQ F0BmPMfqvkyA2UcAnRQzuc7PWyP0nydGoJvfEUHByVs7iEYEExECAAYFAkLiYm8A CgkQbZiNF4cxXDEC6gCgqICh/djzt1i6uadJAOztZc/zxl0AnjR1OaM2JxR+wqK3 loSTEKX1uOpBiEYEExECAAYFAkLjVcAACgkQEy5J1OQe3H7rRwCeJWLmLyPzpg3n r65j7AxqGRJeDfUAnRT2Oa85dAM02wgdYlj3FejPuNFziEYEExECAAYFAkLjZNsA CgkQdcqio/ObN1DA+gCeOaeMarEJDPbZjaN3y/Pf5PB/Cv0AoMGPjPSTGt/iHJ2a Arfyd0ME/R52iEYEEBECAAYFAkLkbxQACgkQjON2uBzUhh+lnQCfVbPGF5UWlzFb xK+xKmY2DbKXb5wAnjpoAxffSRO1PZlQUHgWlS+NaeCziEYEEBECAAYFAkLkkscA CgkQBJE0Quobo40fbACgo9YCJXu5Hpoc/SVp3rHCYmTuEMYAoIt2q6rPL/Jbbkik bRqausEnFstziEYEEBECAAYFAkLlwiAACgkQYRlqLjM+ToRP8wCeOVAIpaG+q9G7 uJgVCyDZ0JbjjfIAoJgg+NrBfhNeOBuu5mS1PSGDztQ+iEYEEBECAAYFAkLqY/gA CgkQsr68QBUpJK8HfwCfWgNlYNLBWmn/nejlx0m6NstT2CsAnRdhsusv6RbcKNoV UudxGG4Xm3nGiEYEExECAAYFAkLj7WoACgkQMoZOQZyFIiv6jQCeMior8Tg4msrR c+FfXfj5Uln03d4An3RTaHKU+Sv4SgEecXlW0RYlVa6eiEYEExECAAYFAkLlELcA CgkQUnkvr5l4r4ZC9gCbB5X0rL/DtpGptiNO12DdTQqzsMMAoOWpJFg3W7zAr7al Vjqlx0t1vS4KiEYEExECAAYFAkLlEaUACgkQa3OhBipiP3LYawCgiuQqhVay7FE8 e05Za69seQwF4CYAnAxBVNyvXu/1aeAuSi3iC6tywvFoiEYEExECAAYFAkLmmWIA CgkQaOuMdvjqKWfdbQCdHfDneYM+nWXT/oYOqohamLvk8iQAn1LLJhkLiCMOa9/b iF24zKt+MjaHiEYEExECAAYFAkLnYVcACgkQbpR1lMFSQxog0ACgp/m7d/NdNP0w 605VyrJWCdogeG8An2E+FpFBQgyvJaD6klzDRNNYLc71iEYEEBECAAYFAkOHn54A CgkQZjW2wN6IXdNgDgCfUVMGJZV/D1QEdnwrTkRmMb37KR4AnRLDWMsKtq4qKXQj YbmuLNdHa1EQiEYEEBECAAYFAkY44sgACgkQFUWz/uIi3k8H/ACgmKEdToiBaw1w AMXXKjj+8PTU880AnjTWDVjV2fZnj8iXaOK7MTx01gMRiEYEEBECAAYFAkY456IA CgkQY9CtrpESA+QXcACcCFtUvEcmTT5ezAsMsWPbRJAqtwoAnjFdqKAoBDkPr85/ VI89GhfttMRdiEYEEBECAAYFAkY8kyoACgkQmHDv8/EvYHLdewCgo42arxjKFiq+ lp9b11fuTqvimDkAn1KOSLRaqubUBhfEBgZzx9kh6wh/iEYEExECAAYFAkY5GrkA CgkQ4eHysJkO1KqIOgCdHwUD/IZcQB3qA/QCiKjIwePp7QoAoIB2fFdQH95LA33w SR5XBI+mIEsbiEYEExECAAYFAkY8UEcACgkQTAQoGDEaPeX5dgCfY6TPlbpJpsrK KtQaMXfSWv0E/xUAoIGqw9ZHnQ7y9wbSUMuSnW25b7rdiEYEExECAAYFAkY9oJIA CgkQLrlGgoiBdAJMWQCgirz2diHoCbFyrBAV5iKWgP4Ua/QAnjWVrS+SKzUlQgMz cU07zHDlVs1kiEYEEBECAAYFAkY91ngACgkQ9cJgFkzu119zGQCcCwwCeGu9A1U9 Amz8/nKxBModdnsAoIavFQWLkVD3egS5YEarD/Edwx9UiEYEEBECAAYFAkY99QUA CgkQoHLU0ENYxYRKfgCeMfNpqkkRohgWFz/f2EhGbmfR2X0An2UBeqbP5hBKokpD 9xyF0pst2FIZiEYEEBECAAYFAkY+BcUACgkQAqWmBQt+bPrGDwCfUQUaIJ4jUL7s FQOojnGU+mzpYHkAn1xSgTyDWKkT9C0JVhw5SEPnfLFjiEYEExECAAYFAkY/BjEA CgkQA/aMvd6IhKCNuwCgvN28t3l3yYAMxkMUuL3LA8uTikEAoK3bfHFYDA1KPlb6 48YsvpHQgRvoiEoEEBECAAoFAkY494cDBQM8AAoJEHPdjBYBUwI1PekAnR36SmoX YYegiEUSKxHCjHQnnYnbAJ9zmdCCloaaDIyqh2qr5x2jJscjHYhGBBARAgAGBQJG QLZMAAoJEG0LxzpAWBg3YX4An2XrLaazjvwgpUwVEj9KCHAq9FYBAJ4khHqEJHjp BjQioqGQfL2Z/NqI9ohGBBARAgAGBQJGTERTAAoJEB8hI8Nr2HKg6F0Anjm/ExEy BoePXnjwORS20+/Rx1gdAJ0aVz/9sdWcnDCVQXP/U7ixfAO21ohGBBARAgAGBQJG TH0BAAoJEA9FCiZiEL/A7x8Anjgvd31Atoy8y9wjvxowEkfbLZ1KAJ973Fhgy5i9 /2oSWKGKX/tr/2nrwIhGBBARAgAGBQJGUm9ZAAoJEDLB1u8PFDvBEgMAn3Gjh6BS BWjqH83sDmcwG5Pd1nqMAKDLY5za4VhiEa8ECBCkeoRJl/iy44hGBBARAgAGBQJG Um9gAAoJEMuuvjmkbEyhglQAoL+JREGW7kkrFkmpQnoPaTNi6ji1AKDFOm+kNYqq sYgzw54D89t1QmpOQIhGBBARAgAGBQJGXtWqAAoJEJqG18zRqupgx3wAoMcKQRL6 tnmUztp7C5X7+i46ScZAAJ948yxdrkvg4qMDezOdydmRxzpaXohGBBARAgAGBQJG pUwuAAoJEDm+UaEITJETPdoAoIVf/V5humFKY/Yph+AeSGDod/VTAKC2NmslQ2e6 Gw8ZEr4Wih52zcxq0YhGBBARAgAGBQJGrcxXAAoJEDPNZzOvXsRS8j0AoLVDN8Wk RLiuBvr7qWrNhg7Ylb1LAKC1mSGqxofl69FmIiCXOMeZ+A8mN4kBHAQQAQIABgUC R8B9mAAKCRAZZYVQwxEGESOgCADhwUA+q0JhWkR/Q1zt5yKrGlQsYtpQI64N8xmL PWRKReLgWIMAptJ+twYA+YuH7wCJBsgP7U8oi2x9azlmO4HMnDa2gXs4pJbE0pRI mhP5JqkYauzepvLBlZGLzZ1zCpLE/JmaxB8tc40lF4/CBbLj5EE2+jfyPWEplyV/ zEwjjDXAkxSzfhdp70EYyguGm4n6M4KgbgkTdxR1/uG+HIkq0yHGsCJhViZWW6tK G00XcLOGrm2bzQlv2bXCVlEWC9QTqb/ZdbA+ONLSWdmegEVVnQeUx3jtvRg67yJ5 LXA2CSq2lHAGoEEfZc4ziRHmn+5Pusk33Jq/rPV8QSCZXlMbiEYEEBECAAYFAknK h9kACgkQyceSTlEEfWbGtwCePr57kk5WrDbIcRgYgvl37P2JRF8AoJHpjz2MwXPO /asQ9sCsLE54aa6utCRTdGVmYW4gQm9kZXdpZyA8c3RlZmFuQHNhbWFmbG9zdC5k ZT6IXgQTEQIAHgUCQpxp5QIbAwYLCQgHAwIDFQIDAxYCAQIeAQIXgAAKCRCiEVrh X2uLcsktAJ9XkWAsu0zJ293dGa+Yban+Zr1V9ACg3b9Petds6ebqQZoiHRopckbb OLqIRgQQEQIABgUCQrgKqwAKCRCBBGRPUYmFBP0VAKCPH0b7S+TylV1uBuYcYnWI b/RJzwCeJvRTMPnWNjVz+CVOvVzJTH4ol5mIRgQQEQIABgUCQsUyhgAKCRCb8iNF L+KLz3iqAKCXRZWdGjBVbj3IBFl3kvh3xF2gsgCcD3H79mbVDRNMxpGArFQ1hqQF zleIRgQQEQIABgUCQt9FDAAKCRBB6gmgV5NJjzHQAJ9IfkjKkiEuFxUhznsghAQ8 bsBWnACgoT0kWSB3iUepLIDoWhhGtDIS5FSInAQTAQIABgUCQt6scQAKCRAxpj2W 7BQLgebOBACAFFpEKETO3ZHbjMnPogACNr6EZCQxzGTIXrXSyWQs68VcH54wUOA4 yk3cGpfH2pgAxYjaHejTJRvDKvGrPGlKHgCZFy4+wHzo17pW9J1aKk2sUWlT67sn DVdMun/i8WxD9yz299cXR6iCxPfP2HIMEqbsxWJaXITo7drWSjO35YicBBMBAgAG BQJC3sfZAAoJEDfj9F/uZeMhRawEAM9wfn9sBIsFzQRQbAO+ll83f8ki++A4Anj6 DXQ4xRmClUxqahL1BjxxeQhE+Qomq1IebDJr0Se34XB0g3J7bzr/i9QmEwEqnDJf WVobv1Ugjy+1jzErlZBhm8hnCI+zPnrWKLk0n78vzJ5RrnVaTTV+OW5r4rdVZ86y KYHtpVSoiJwEEwECAAYFAkLfbHkACgkQms08wKmfdd0HDQP8DDD+1FQU8PPPe+Ku f2bJOO7Ycrej4JF1I/Gbs2HH3xXgOZsRv6WJ41M/ovxJLYrpVqQA2YF/Gxwguwrf 4lPk+4spFdabguiJK0d2/KZAtnLsjIzdYcoY01IKGT3xkPwIDErNFSmxX6bKCUeP cFNHYZ6dDBHFFcYVTsdo/wbAe6aIRgQTEQIABgUCQt6wsgAKCRD9b4jGIdCnG30U AKDCxsPZksKIcvj7tbHQEwm+PV5+DwCg7PorUCgIvTIWnID8zRWDBG4ACXaIRgQT EQIABgUCQt667QAKCRAyyeTONkLLS/d2AJwM7BQIQgqLA0qA75R2EjHFXQKZWACg o7iaANHxIRc/Nw19j8CxNbWJRJ6IRgQTEQIABgUCQt9sewAKCRBQjq7FMC2laIx3 AJsF0Hjrm4N21EwdrmhS9PHKQL2KdgCgjlus2GyuCzafgb9JHVhBDrhelkmIRgQT EQIABgUCQt9sfQAKCRDdumS6LDEtL7MWAKC6rQU6ZjSS6gVnwswutaqBwfwtvwCg v2mMGJf2hnYVaNNqV5WIFAuycmOIRgQTEQIABgUCQuDJOwAKCRBc/Tf6zHjIk9Tl AJ9dbM2HowI5oD6hGSnADhI2dKfBrQCg4O9WtFiRzLqC1TgCAsbigqy+JDiIRgQT EQIABgUCQuDszAAKCRDJtabs4td0311pAJ9L3yUe7GUeDqMzd3WLWatclf7ruQCe OenA9nhyKgHASeEK/ZXQXDDBW0uIRgQTEQIABgUCQuDtQAAKCRBbloAQ4E+aibNV AJ4wnAfcA/rtUs3+Hu9nNn8ar/2Q5wCfe6W+k9yHjd7hZWnYHdnCkAZkOMeIRgQT EQIABgUCQuJibwAKCRBtmI0XhzFcMezQAKCnk+So0Anm4kLDwl+srHvIB7b6jACg qROBN5MeEGXQm+Gan2VSt+nvTZ+IRgQTEQIABgUCQuNVwAAKCRATLknU5B7cflR0 AKCTAlfhPFwHPXnBo+5IROopwNQnsQCgh2vHS9VRZRt5I9isNDaNf1biCQmIRgQT EQIABgUCQuNk2wAKCRB1yqKj85s3UK9XAKCELi7ymxtLxdwYfdfV3dxd63mV2wCg jgaUlQqFXjx5mXnRsgy4S6cS9yuIRgQQEQIABgUCQuRvFAAKCRCM43a4HNSGH5/s AJ9JVHMVwBwHD8PN3DQq8hHEumn8twCfVQSXooNY2P744K+8k6lLO8nOH6GIRgQQ EQIABgUCQuSSxwAKCRAEkTRC6hujjb+qAJ0Z+AoGDYe122wRAOYAKayl9f9e0QCe Ketoll6NZ+Rm/NKbFJGP6fYywIuIRgQQEQIABgUCQuXCIAAKCRBhGWouMz5OhDd7 AJ40l37cLZcSxfPt3M7/aOPgVGpa5wCfciaEynzuHDfIQD/vtXrZb2m0+NeIRgQQ EQIABgUCQupj+AAKCRCyvrxAFSkkrwQsAJwM8IqtXQk/TBiQi6Fyq/HHm5/zvACg 5atZV8F+r7jVRhT1SJ+FaVsaQDiIRgQTEQIABgUCQuPtagAKCRAyhk5BnIUiKwuy AJwOljL2++fVQ0BSKRvFSvS+fSu3KACeJxsOhbyCd3o3rqwaVeY5FFi+Fm+IRgQT EQIABgUCQuUQtwAKCRBSeS+vmXivhv0OAJ0Sg/UEnB/IAoqjHzKoBivCMYDtrQCf VY3IDKRHbbLNfWBSDERWCTpHXtiIRgQTEQIABgUCQuURpQAKCRBrc6EGKmI/cqGB AKDEgTewzt6TjmCkI9RrYjF46a9H4wCeJPh4bmTymcfwRGn60h0a9Mz1mKaIRgQT EQIABgUCQuaZYgAKCRBo64x2+OopZ3lEAJ9w4EWAgRUMxf0Ud1zoygYDQedAgQCe JPHSbk62Ej11NljNGN1zdwzRHuSIRgQTEQIABgUCQudhVwAKCRBulHWUwVJDGkOf AKCgQM+50dTktJDaDd8gVOGBKRiSIgCgkT9gdtDac0m9s2IHAqktk0mc0U+IRgQQ EQIABgUCQ4efngAKCRBmNbbA3ohd05uvAKCjMnn4GpnZhjWFS7iN0LIXgxm5PwCf YodjKF5zSbIROx79dJ41Gg0/VxWIRgQQEQIABgUCRjjiyAAKCRAVRbP+4iLeTznP AKCaIUKdiySarhu//zEVn67y9q/szACcDUob1L2ac1R1FHB9XE4fTf/PV1KIRgQQ EQIABgUCRjjnogAKCRBj0K2ukRID5FlVAJoDhc0dijUvPmOKILkX6fG5g73DugCe PsOrjW+YIc5+T9qeVMzHyfm2opuIRgQQEQIABgUCRjyTKgAKCRCYcO/z8S9gctnJ AKCc7DZ7JzXgaB4ImiwB2dyGMFUC8QCgitOFKEw1y4+V1dNN3kZYL4P/M/uIRgQT EQIABgUCRjkauQAKCRDh4fKwmQ7UqvVYAJ9BjHLDyGmR56xKlKF3qVq1+jAmgwCf QR+0qbVWaSIaVS1DCg8yUr2txOeIRgQTEQIABgUCRjxQRwAKCRBMBCgYMRo95VO1 AKCewEwAscfj9VfTxswF6BL6zNj8rACfW/3kG7zPI2dSjWJzGYPQYPAa0smIRgQT EQIABgUCRj2gkgAKCRAuuUaCiIF0AjxRAKCu9kiQfvVmSrVZb9HK8Mazhut+hwCf Y5guSOz96KH5dJ2585cm5wPyT5mIRgQQEQIABgUCRj3WeAAKCRD1wmAWTO7XX04y AJ4/ZvOfsexCgIQRuoREg1/D9bniKgCfTcKh9dLFkPjlD3yIw/NCc1L0/ruIRgQQ EQIABgUCRj31BQAKCRCgctTQQ1jFhJmBAJ0TPZlIksq1EnAYtTTSb/tHpXxNUACf d/m3jaTHdJljRXGI7UBsVHnL0nWIRgQQEQIABgUCRj4FxQAKCRACpaYFC35s+iQn AJ0eGzB7NIQtXLEgyuphyW0nBppVrQCcDj6tm1MCKXA7f4zV1R0u30jrUeCISgQQ EQIACgUCRjj3hwMFAzwACgkQc92MFgFTAjV92QCeI+02yLkSqmdJlMBVfVE9joT/ pBAAnjJlywot38PS8FtodliCfNvqn6VIiEYEEBECAAYFAkZAtkwACgkQbQvHOkBY GDcfVwCfbS6bS20V1ElnuQBAofsmi0yjbzoAn3eztrDQIrh+/BkXIJo7IF0Ny+gV iEYEEBECAAYFAkZMRFMACgkQHyEjw2vYcqBPqACg1jy6peePfEuvYJEKfJBNG7FV wPwAn3y5/eBtZdRefj90FeIiS3dr3D3siEYEEBECAAYFAkZMfQEACgkQD0UKJmIQ v8AfLQCfeHzJB6tJdA4bjPEcJKi0sMFceCwAnAovkjdUhF2aJrpK2cr4bZhm5Rbh iEYEEBECAAYFAkZSb1kACgkQMsHW7w8UO8FdFACfSFzmzz3lZmB+qclUq7q+YVgd 3hYAnRyNi3iYLUVrk746XsvzWcv8UonRiEYEEBECAAYFAkZSb2AACgkQy66+OaRs TKE0LgCfYZfXtB9Er7iKXoDfhNuuDIdKmqQAniGNC3piLBCggMPpJ5vQp2KsptvJ iEYEEBECAAYFAkZe1aoACgkQmobXzNGq6mC8pQCfeV2ib+Ymo/KQ+jYsr1BxYVFC OmsAoO312vLgv8Q46hucGIq9aN2isEnEiEYEEBECAAYFAkalTC4ACgkQOb5RoQhM kRPl4wCfebfolpLZYdGk48JuUwd2shtkicwAoMGAdNOSoXynI/6/b9jsxQl8qmwZ iEYEEBECAAYFAkatzFcACgkQM81nM69exFIBlgCg0CUQ1h61lCLBjE9+/Kvskrh1 QAgAn0gXeq1NKEuepDB6hQo7fVZrSpF8iQEcBBABAgAGBQJHwH2YAAoJEBllhVDD EQYR8ZEIALAYFxipk7FfpDbEnUrTI237QugKjpvrX9n7CdHxJLnwOBr1g2/e/RMg oJHH8yqP8iQPGMfZXCVLM6ME/EoUQAVT0M0I1QsBVxTIXyPqQIzCv6zibLYyEXDl QDNVB4hqdhozzxyjGruqbn75zfb8mlTMoj9lElNhVIdcUOVL2xHkBy6g/YpmuZb/ pt4HXBOUyWkmFK8zBMxhXw5bOuOP2zSJk9rZt7wdKNj3iC+/+936yXZzqWFuUOq0 RX61RtW8e3SJfowGFBd728snsiD0IFLTXor62aBfBJ5yiGKFUBM8LQ27FcJasfo7 a8SiBbJOO/OsyQ1lRvLS85kM+XZDXZaIRgQQEQIABgUCScqH2QAKCRDJx5JOUQR9 Zlt8AKCAMAc8652qgKVPdH0XJbzoq6ykNwCgkTboPY7d+GFyEwNCHk+0PAmkPru0 KFN0ZWZhbiBCb2Rld2lnIDxzdGVmYW4uYm9kZXdpZ0BlcG9zdC5kZT6IdwQwEQIA NwUCQsVK6jAdIEkgbm8gbG9uZ2VyIGhhdmUgYWNjZXNzIHRvIHRoYXQgZW1haWwg YWRkcmVzcy4ACgkQohFa4V9ri3LW7wCdEc6hdCr094a8LG+chTd+OzGxfFUAnR3F vtuG8sv367Knk0ybMnpOM/4hiEYEEBECAAYFAj53pBoACgkQFT+gzXWmdpL1ewCe OSe7lOufhc3mfTXs7eSvqECt89oAn0VM+YgQHbfdVp32YE7Ht6N6GPf0iJkEEwEC AAYFAj513w8ACgkQPo+38viDQdkP7QPmPZXPi7m6wRiLofsTlHCbBrR+ehWoSSqC mHQjN1DGRtamGE6X8QbMIttD+NLp+uTx8j/E0sGUdPnWkky6fwt1f3AYeoAgCXNv PoewsC6mZn3FMdEo6vJc43FmhsUfumOtunvGNBnXdM8GSCJ+RBS/ASMjRrECF12/ 14xwgyyIVwQTEQIAFwUCPD7aNgULBwoDBAMVAwIDFgIBAheAAAoJEKIRWuFfa4ty s/4AoND5QhEdyVIypBvCUHv5SCaAKcd/AKDFthtZTrjF+eEYlktPLRtI9zjeE4hG BBMRAgAGBQI+jc41AAoJEMppOXSBA6N+jAIAoIcAeCIKt2QBPnAthnUk4DhlmM7F AKCA0Iz9ZutXGb2l+p8s7hhF3+Y9L4hGBBMRAgAGBQI+ddrHAAoJEO7R6jkiYdBz i84AnRddvByuDodl5KaCSdpe6k9aYkLqAJoC/ud28X0M478KlmacVVjb+PqzBIg/ AwUQPnWqLv1viMYh0KcbEQJ6DwCff918LRigFUyEvYj04C12so87JNUAn0RNFw+P 1/SR9Mr/JQmOzJVhlwdriEYEEBECAAYFAkLFMoYACgkQm/IjRS/ii8+wZwCfRvfW 6NyBoAp7oS9ILRHNYh2GbhsAnRYGs1hSaGK4rGxm/fmqxj+DvqI2iQEcBBABAgAG BQJHwH2YAAoJEBllhVDDEQYRqFAH/28B/f92MsQX9ZRJG1v9EDGVx1U+pcE16a7i plCP4QuUR6uA2EUe9fptzZfX2iT2nr2XgCB3x2NHf0rzNpTAM3OtqKQhXdvS3EWz WqR8UaDf6dxKN57B4QONRIhuImf3m9DWFNwIr3oOtO25Q+tG7YcZen/zbwU5O23C EakNsysxGEHn/3BPjRyA1FE7NVLrAmxFu8LXBUD9y3HNNetM4WlucnObqw5cBFsZ MtnGcVLs3suTAsxwrnBo7jq/DbZVvzUZtEkGdV7LpSWkivSrq0+h9Gzug8EcYTjr dR6LFA5xGan6R9zrSe4mxe7vja10fmGEdIOQIapgO/iOWDR83MG5AQ0EOxIiVBAE AM1SlkvEK5MrMnW0ybtv9eMCG89gqIvd2gBnpcAsF0sX+dCaWHWNy5HL3dBak/G3 BJ8+NzAksfL5Srm0LVKcfVjBiG+IsbUoSyeJQGuhSZXYcnIc/3Z8Ujcs+TfFurG8 uHU1cWnNK5aMYwDrqxmp4Ru0zLYHw4tHBBKF0cgFaCsjAAMFA/49aSZuDaatppSa BOzCt7wIYCsGBxX5ZibrJqr0gLUbhXU9eaWzCawOWwCvpQN0lTjoYVkwiLZaYUkd qsSQgHAU3jjKlIuaIRXApEkTb8Jg7R/vNAdwXoZRLBCjZPGd5qGtnIezsZ2+lxFx +bRieUL8fUInemXwWl8e23PMisgm+IhOBBgRAgAGBQI7EiJUABIJEKIRWuFfa4ty B2VHUEcAAQENMgCgnc22kj8TfjktU6u4SUUqud25ZZcAn0B2b0zPjKjGuiwdKSnk FbNcFS3g =LIse -----END PGP PUBLIC KEY BLOCK----- xmlunit-1.6/README.txt0000644000000000000000000001346412451007364013266 0ustar rootrootXMLUnit version 1.0 =================== To run this software you will need: - Junit (http://www.junit.org/) - a JAXP compliant XML SAX and DOM parser (e.g. Apache Xerces) - a JAXP/Trax compliant XSLT engine (e.g. Apache Xalan) in your classpath If you want to build the source code you will also need Ant (http://jakarta.apache.org/ant) This build of the source code was prepared using JDK1.4.1, Junit3.8.1, and Ant1.5: it is not binary compatible with JDK1.1.x or Junit3.7.x. If you want to use these older libraries then you will need to rebuild the source code first. Enjoy! http://xmlunit.sourceforge.net/ Changes in this version: - DifferenceListener interface refactored: single method now provides the NodeDetail of nodes that differ when a comparison is performed - NEW NodeDetail class added to supply details of compared nodes including XPath location! - NEW ElementQualifier interface added so that documents containing elements with repeated names can be compared using attribute or text values to determine which of the candidate elements are actually comparable (fixes various feature requests / posted bugs) - NEW ElementNameQualifier, ElementNameAndTextQualifier and ElementNameAndAttributeQualifier classes added to provide the default (backwards compatible) and extended implementions of the ElementQualifier interface - NEW ComparisonController interface now used to control the operation of a DifferenceEngine instance (extracted from DifferenceListener) - Incorporated DifferenceConstants patch submitted by ludovicc - Added support for namespaced attributes, previously missing - Build file now incorporates JUnitReport - Deprecated assertNotXpathsEqual() in favour of assertXpathsNotEqual() in XMLTestCase - Moved assertion methods from XMLTestCase to new XMLAssert class - PDF overview document added to distribution and content updated - Updated Javadocs on website tim.bacon@thoughtworks.com November/December 2002, April 2003 Changes in version 0.8: - Changes to compiled jar in distribution required for compatibility with JUnit 3.8 - Fixes for a defect in the DetailedDiff class that caused a ClassCastException, raised by Ryan MacLachlan - Small API changes for usability (e.g. allow use of Source constructor arguments for Diff and Transform) tim.bacon@thoughtworks.com September 2002 Changes in version 0.7: - DifferenceListener interface extended to allow more user control over how to evaluate differences between control and test XML - NEW IgnoreTextAndAttributeValuesDifferenceListener class added to allow difference evaluation solely on the basis of tag and attribute names (ignoring differences between text and attribute values completely) - Fixes for a defect in the DetailedDiff class that caused a NullPointerException, raised by eBernhard and bob - Additional methods assertXpathExists and assertNotXpathExists added to XMLTestCase class - Additional methods added to Transform class to complete wrapping of javax.xml.Transformer class, requested by tCantegrel tim.bacon@thoughtworks.com August 2002 Changes in version 0.6 - NEW DetailedDiff class to extract all the differences between two pieces of XML - NEW HTMLDocumentBuilder and TolerantSaxDocumentBuilder classes added to enable XML assertions to be made against badly formed HTML documents (uses the Swing html parser) - toString() method to return identity and description added to Difference class - assertXMLEqual and assertXMLNotEqual with (Document, Document) arguments added to XMLTestCase - XMLTestCase now implements the XSLTConstants (and by implication the XMLConstants interface) - Fixes for defects raised by danny zapata (handling namespaces), aakture (handling redundant whitespace in XMLUnit.getIgnoreWhitespace), and Craeg strong (handling nested XSL include/import in Transform) Thanks for the feedack guys! timBacon@primeEight.co.uk June 2002 Changes in version 0.5: - NEW assertion methods in XMLTestCase: assertXMLEqual, assertXMLNotEqual, assertXpathsEqual, assertXpathsNotEqual, assertXpathValuesEqual, assertXpathValuesNotEqual, assertXpathEvaluatesTo, assertXMLValid, assertXMLIdentical, assertNodeTestPasses - NEW DifferenceEngine and Difference classes used by the Diff class to facilitate comparison across multiple namespaces and for more node types (DocumentType, Comment, CDATASection, and ProcessingInstruction as well as Element, Attribute and Text) - Diff messages now more descriptive - NEW SimpleXpathEngine class for testing Xpath expressions - NEW SimpleSerializer class - TransformerFactory setter and getter, and utility buildDocument() methods added to XMLUnit class - 'global' but non-static methods in XMLTestCase deprecated (XMLUnit static methods should be used instead) - Xalan and Xerces specified as default JAXP implementations in the 'test' target of the ant build file (to get around limitations in the Crimson parser) timBacon@primeEight.co.uk April 2002 Features added in version 0.4: - NEW Validator class for validation against DTDs - NEW NodeTest class, NodeTester interface, AbstractNodeTester and CountingNodeTester classes for validating individual Nodes in a DOM tree (NB: requires support for DOM Traversal in your DOM implementation) - cleaner separation of source and test code through directory structure - better documentation - replacement of all deprecated JUnit assert() with assertEquals() calls timBacon@primeEight.co.uk March 2002 Features added in version 0.3: - NEW Transform class added to support for JAXP/Trax compliant XSLT engines - Diff class extended to support Transform class - more testcases added - removed external dependency on JDOM timBacon@primeEight.co.uk October 2001 Original source: - Diff class to describe differences between DOM documents - XMLUnit class to act as a Diff factory - XMLTestCase extension to JUnit TestCase jeff@customMonkey.org April 2001 xmlunit-1.6/docbook.xml0000644000000000000000000001344012451007364013724 0ustar rootroot xmlunit-1.6/userguide/0000755000000000000000000000000012451007672013556 5ustar rootrootxmlunit-1.6/userguide/XMLUnit-Java.pdf0000644000000000000000000102033012451007666016432 0ustar rootroot%PDF-1.5 % 5 0 obj << /Length 231 /Filter /FlateDecode >> stream xڝMK1{~dwɵ` CVYh$+ b/ !x'3[wG[1X\:!oGqbduڿ\4"4˷q7J9ڷ5]:s̳<#zC0Q7 Lڒu;sk tV2D;vQ@PY *5O&J7|KA` endstream endobj 14 0 obj << /Length 1460 /Filter /FlateDecode >> stream xŚKoFUξ7ɖ]~6rPl%ib'Ň-= g~3;K' &ͩgm2?-$]nf8:ϗ#GB/7V_i=V|?{Z߭F7'̥aBk49i:{#Oٺ`N~=O3]#?>?'^^糶&H,~Zv6횆RXVOj <!,0QKK4f__\'a:R|x=_I0p J4#h0 -B1Z\ h4譬ьCcJ"+,-,<̓4xF3B UYZ !FΙ@ s9sg-wײw; $\n:A(a u H8D+NKҨRKѨRKѨBjɴ+G/U0I>iwQǰ$_ήJ7iMlPJXCDF%A#䃠BJШ$Jpki 'C/$--FH A#QT\_(!a /VE9&o{z$<`NHæ8\n2G#`xT-YK_NpnNcҝV0꾟nWV_ O$\K MKT0b\FH A#"JШ&H;m_3-&[D#: ZS|lMby:[w)}۶1QQ<)$) Yg .kX6Ű{s9V $]n-;0fFKź1?]go0QL( vB޺unV%Jd*MǽDy/w*jkV;2f&.Ps~y\>,Z#Yoj3;vR,VwBJ䅠pP2N! bXuiV_0瀿?m7w ?T0J&Js8TcXuٓ*YY~]n;_~K(4^=w^> stream xS8Wm-=-eʴ[Pa4$6Or8TOEwIЁ^}c  $_:.Hs ӿpݳ/O"y>f= [37,9IUǘ{}wt隓kH \v=dzA3οC@@p-Z #w؃SAHb(SQV9} eKB{$)Xߡ|>TS~8뙙3Y3sT/xO%!GWuNŠNgy@\\eZpsk6גXQpyw/St3=_sa('geYT"KKJ mk%Bf iJWUA_q/U--dK uBZ#ʜzr(I<V[ *s[R1$UJֲ5$Y&j'klԂdtn s bN5^}Fq.ۉ.v,?&&*`AmwKM @mkR%p/u} ̭T^H8ːWX#]/ XŽL17orZlziӷ_of40Q('k4r7]UT*FrX)ud4+9=&΀ 7##5$Ukh9Д@H6L>I~ endstream endobj 136 0 obj << /Length 1461 /Filter /FlateDecode >> stream xIsHT.#}tddH-2̧l0'@FߣǓGባ,򖖐H*f9 ~8xԷ)^TLLBLu[.Z֘!GRkpm{&blGešGsO-rŕ%FI D#!/f3Tƥ ׭痤+4_gyEI|?Mg@zGW:̃gZǡt>/~,YϮ|te;'F;_֌1qb~);eyθT"I}/:nD$r ԋ=7t\De3*gqs+cg79BV&Bkwȩ/ԟ_&+CF?fĔ=&c6|ڔ;Ilsމ8\'Nu~, »ҹ~5$znss+n/jy >ϯ>Q+a"dpA+v?!AgL؏TLYF>:rɋG|maGris!xT#DFҥ]UX$-\LVQ::sxEʳr^ChgW|2Ljl69K .mb\~-LDDE8΃SKnʏB8-ߙ͍t%+6ɮ4j},fMNW>Q5.Lr{3dwZ3 [%K.&a鍭$F {wܩCV 1 +NSe./)! !a Wi)gtDt); ,Q <2BvoJ#勤7ED Hl&pC4=0&#oJJqZ^36چ饅yQ7gK(޽(3TRG2DmM8RJlQIl E_%?(nNQ]hD[l>WAl J} ['rͼ/:U(rCuPCUбAIjPTOn|VE_QbU=z~44;Օ뽵%~ٸ5>-.KN:EDB2og_c*$mӦw6x<]]LϕuF8be# ;,)|!F.M>`w?йvQb="{Wc0s& aKp`Ja,TXڥX1eDȾ-z`Fr~}] H,$jfp@.w>󽵗]%EpW`$R_n ]5coJ\gcID:5 fJO$ ֕B"O9#άv9CK endstream endobj 191 0 obj << /Length 514 /Filter /FlateDecode >> stream xo0+ Ͽ) TѬTvǩu++RAɧ8~}po84P^f|1woeKtXeL"UݼU:(gC6j4GfQU)uI~8]q eFF8ӮL*| `FY(ӡK&}Ok_6YW0b;F)ڹUonBq@`/*}o6 )u lA{M]}'-(> stream xZMoϯ19bUX,] XDk d2jRVWMD0iU$;.9UG9:qX#u\p˕TRr1ଇ0ZKSuqG@ E*nј]S=Ă..\0 Z.frl(9\J K*?' IԊ |8Q<J^Ap=94T:0,T ]p$.^B (xKRpQ\JQ.K5D,9e.SnpfsCNxNx"aO/ x~<S$YJ )$"rD`5V묮 ^5[jb}jAcA>'G{1@ A9Ɔ 2>ぢ #ib 5$ٓk؉y&X?V8<>Pp:u4djزH Oʶy8,dc6ؘY52r=|;v?_vǗ~qW?7p?Gpw x,V@/+7z?Dɳ! >jW+ u6WpEÂ/W;n8|sŭ;@_r7oݽ/z ˧N@> _t;2LD;y|quu sg 54<5Ԛؚ(ִf%6+YfnVYfnVYfiVYfEiVYfEiVYfE>q8W>yq3y3:M?0mQ1MW81Se{,?|ĝWhFg!x/=!P2{{,DqJE'JIֱ,}$/-2 U_QcX dhayFc$ʾ;,YV1nDsB0Y6JߤV$a'0˓4SmDHm"6qզ>Roco[mmWgCgC]EQEkn.:ӝ{'vK].R|U_/uݗ{+O3p9fF_&ddJSP#('jL!:;T3 ۪PPrfmZGRG9* w*&1A C1!#3:7?%&d)q@csF%Bo ,|p>Ejf>.4p.XDPu({=e E1>2@cBE5?F0v*)Bq- Aش#+a@b2Ca7mH#D]6r^; `#R{>π װ pRT 1 vyV qPvӅ+>||1@=+@"V!^Ň-̝P.fP> stream xݖM09;o"mnJt@  $[e{k&>3 A.#95IgDi k7we^ W@\q=s7Rt]\ʿ,gZYɤT]ST6L?UCUiس俳UUK[հ},7D! rl|W`s_} pSo"eWI.;q6'U k.sS endstream endobj 237 0 obj << /Length 3281 /Filter /FlateDecode >> stream xڭZY6~_PUSrʮ_4= !\dz8amIps/EEK"pQ -՚;Ӟ02Z\J&Rګ}E혛eSSe}Q<ӞXڷp o<КnPH?Ow97eUq'1~CsR xTf60H \]U=ز/:*mS_SӮ ܋+]/r9&\S:LUtfF$=n*:f_nLI@76tR+kOZ7Hխ_8w)c`\E'ɉɖeU<,PXڶNX0${w4ԛIS7"A{ [,ɝ+JBoh6F=ٙR:L]+dҚP/մXR bF-=EVa*?U~yMn5 U&S: E"j댷9;} Z1Ҏ2R|>TKaRm;<4 ݉훮GQy"' JQ*?|i].MX\8]lq Lq$h,E 6$+>\tɐ> ^^ i@Vޚ)qcrq o4i0]W\8rdJPnͶQe8zҟ|NPiEn+L>6p$ :ޫThD@5Ą{{{V[g+F"}#;[m|lqz85'\n|f+|TX-|fQq)0"h1yQ\ LΟfqB!"LRuz~̡͌.Cg@Y2(_ f6dI># >3}rN*hgt,(z`#!K8A6h)F0_`YJv~4͡G{ACX5NXQ][CW^EO \E{p)͞id.ΛB/mP>6 q6H:'croGH /ڻ gٻE&7l'i'R$wOj,>* u$qE'1O2̔:eTn_ lb6[n !{h ^#,e$/EUԗOTyO/hWr`HrΥ -w+=J9:7tL!e)}28%lCO/&7PӔb{XE)xߣ|߀0;eiC} `#h>:R$`(#1U('C1AٟU$ + 9oDk?)3|>'JW3\))Oݭ;raauq> stream x\ݏ6߿Km[TW M"A]hIv,mi%Zko$Qԏ 烤z'?}wqUNBJ*'W! $b~O?% <}(~Q1Le\|9bZ6"}xВih0kT 5}f.L/^^~F -)&&xwo&PMnꚫ Q&>;%(b,lcaЋUHu33yyil8@1oV l">`L7E][$>yX+xe:.$. b 显4P9 r$j@],D E!p'W{ o"uǧ  0ѵ}-GCZǒ( 뉹Ъ ߗ0*Ɗv1UWV\_}~Zߚh|[St;*Vvo0?'>oVqV}IE\U^ X|e k%"xZob;w}= LU@Q'h͗i5%.ivLgj \/C5pbxob+Cy+cv85XKhbOiQdɤAN#M|O&%+3(QeLT#c)'ֺ@, L1<7&5 J拫\8Z|fK+U2ߤ*2;GvP6.{/M774B'kIBf=)͢*fDLTs'~,5 7eU~W|Q\B#SK<ҡF0e 4^ER ? O/t0ΪWĶA`0O~ cɡrDi՝yec __j \o]/{]gU&[*.+slj en)MMfT9$=*Y7;*ʯQ.Mh_Nji b Y.S3Wk$M*6sJ ET$$"`ӰspFe{0EN@"$G*㭐LX+blNyTL%&-ydAp<%l{*7G(!NJ%O2'Є?_RP?&Á\wxR§U?Jd,JBdKT)7>PWlSi-R 4\}tLUV}5dJnɄVv]d'=Զ",$t)9_q-Ⲭ?$[@+Ge^-M7^MA#]x]+imw{|,obw2Kϋgǘ9'z;|cPܝyqSjvu=z=nGOLh O\58_9N-aW:W xƫv}g]ew= &*+?RTx~yۗз[f7@8" 9v侟x@]ֆ_x0KWdk g|qX$ZÒ#^ !m˃)Ѿx)i_ܛǭs s Wp2wUwTm5Dmp2 (}zMK["K`Ud=z(z|F ,;tUO<xLChY?9䥚.񵕚BM]lس9jvP#-My}8Uߝ-H޸zUxs4uYR$p3q[!t0Ѹa] *S6; qt ëϔS3hTFA7o5 C4ұO`w[9)tf-딇ݶ5pv.fJ!>`n~i Dxm#ƮqT5rTH%⻹l7Q&dNy$޿z1'T%PwAz뱹a7[?m endstream endobj 294 0 obj << /Length 3637 /Filter /FlateDecode >> stream xr6_Ki J43nt;v&-62T\(7 DNsp`<ɷG?;:}ʉD2b"H6 %c=M,_nmNJ0:̡9GMu@fO5+|5+}:fȞ@ϳlt a.Ert 2B݋Ú=j 4 苈BY'v$G1gmX #Robi?kqZ‡"3W,{ob!E8[j[7}sT L_ _~hdE,O=e̛=d, U-cqQ6/'̲e$';|wc]N{zF]XN "oG`me^afvhxHF'CLjVD=ڛ!3 DA܊H:V~t/v u2PzR 15'QXuc"YhL c:Krw&Wq!;ӫDDWhe,,x}gE3\=VW%r=בU\Vb+qd"C]~JIh3v-aQ=:WR:b+& xP/yg[CGG<fU 0 ,U"./n(Vt1 $q!fCaBux\͑֟ˊ P4i!D8& tmT W&0OmR_69AA)y C)_AH2^ Sݛ񘲸VEi[`d\F=rrv\ߊjJzwx9Qc98c2bghrӱ34-Z^9]ir PӀ)4_uT0b%j_VIU[<5Ju}-a8~ED+~ӹG~om0S[}M℣/Z>}E|R`:o S|W`J#>9`(:I? {9Ed |?'vx,$89U<MZ)(]5Y7n>dݭæw఩DӀ}wL~#N;awڽs$BLޭ<#zk/fzvݩfw1X6^BS&v:點]r8ӧ:'`fr9#$i%7o*Wyj&w6$2ҭM]QUݥ8IAb(O>#mAƷV 8d^ίϹ*fyr*3;0Qh a|-mH@*)!BBjė ?f&=noM} lVovGl7vP/qwɴ?Z}R@a;YĆ+UW`-l$-Vs{B`^Y ՙf9zByn7QxӨt;xNA QK,{,uj@Vi답$+s7f48N<ͽ@뼋Q!Fv䙜p? TG| [7$)`wgnX2+[ZDۿUenzg[oyzi`dnpW#0L n;Ͼ}KSs&Z̊"97@ad^^U_Օ{]IrL5jR@U MLcԱ 98dj{]$r h,]i3a՝EMf%UE]zH ơ J;4O{AFu:FvՈ9 1[ev%@GZG:D8bs9 Y"PGhX=,] R@׫6ĹȵGsBQ$ks3Tż_$~} Te1'H㫣ӊ:/Ag endstream endobj 207 0 obj << /Type /ObjStm /N 100 /First 883 /Length 1759 /Filter /FlateDecode >> stream xڵY[oG~9TR JJ6ʃ.Ц6JA}6!u2!doΜeCUB T5fT|-?0jjB# )RΓxS8cs*P-L WJvN_ .aCm,xj ? '4sp BB6AXAA6*[6BA 26 S1 Xl(...’یq@*+'y1iV\*AO?л+Ҩ8&(!6j3׮* R͕Đ '6x̐BVb.\Vq -PpC.,c94e@"\j0$C4a8r%lpCWkg 1\M3LrqTq#Ejqk0DˤV&VCfAwE|II1I噀V6%e34! /|Ž@ٝS@4Wå0kfſa8\.Wz߳ㄸL'Ó1fóūu86頶X\[i,; axV/Vax9x?8vx- ?Rn-GR,>>S8+X6a4VNHkLK$ǖ(E+}A15Y~-Эb0,/NOO>VxW:Bx Gax'tz1b>Gr)立W捏~^vCb DR>=w{unּ]vp|W 1woR9vY_.o-"_ CcD άudO\ָ0Cˋ[\ςţb5rي2J2 cB<y^n>sz|:{zx/΄ꇉĐgwo1'Pݢ=*Z{LOMwo狣†\bC7pJ0{{7"X߿悝|5Obˀ3#-g,l}rà ;_r['%wv0HOZ@y e(a\hj&`Q {Ca.(FXOeDGo6TU;&D% )zץg:⨟rigv1[hMkVRly:[Lae@e1M%),懤څ"NlNub1NV"s d*4XZ'6K,,tIbMtJr'];hxg7 oXlVac cAїܧ_ݴlBH|։  D҇%EI3vb=E][h86QGW_ϷOy}*l/*l{VF0 3BjT4~Mb녾[f.^Wܡc NawI쎺:UW;tW]S]uu [x컰s֐sbg}2> stream x]ݓ4" -s UpPp˃'V&.{߯a'vc,rn_gvg_\)Q12VQ0s;"gnz%`;3L s\U0*]uJˊrPWb 8? |fAL1QNn!t UgRA9Md:h #1hؓk{ij9=yTކdcҤ?pL3{bXstz no8܃=cC\S(t^^[}jDeGb @j-F[";6λ?zcj?ko+MfoMm0=썰 (!{CZ$d r-a'&C4٣B$GB A=9P+AZw:@6 Q25hC ,l T "GmBԓNfA)};3, ˼d7Ş<R^E&nk2   QeU8L:llwLtl@kbask}pM/s ENloWՍ'ne0<)ĺS_%UʹU8wq%%WmN^cLmBLagd- Ҳ]un /FXy4Z Ԏdv|+&r}T'i`mwGmڶƾK v?* g|z!r6"L+h޲BՄw񭘙 1do $1hKhPS57wSJ w.˔`e̗:̑8 9svZd[skUw">lq ᯝԠª^C y3Q@CJb\ \q07hye M^1qCh*Y]cB A-}R\ӞE9/Z뒃BZm"y2܋q( qޮA 9BLKCj!TTQj3Vά;0=Yߑt0AQ)Ҫ_՞Tsy˔"^uDr͈n=%"81qaݦV|OcY:!1S”"wsSaX}V?'ϭ@B~[E'N Ҽɶ糖ցCCbcmױ~+I-sb8m՛a)WY?8.;֛.0Eܹ^_TgYi۵g-qȜ{Ih$)@A@)Pgr*9f2xZƎL W5 Ϣ'2w]Z*znBLFk}7;0y"'ؓBK;ưcQ8AQhZWuEeedJY4.H;6qOk^8 ;w͌+Mı>o:UI;T46'yzR1Rfdh Xn{)I3쫳6v)7ڧoT1x),LIN`~8%_jﳕL%Bir`IN#lOI~ؖ`Vj0p!WSj|W//ߏށ\ FBNw ߢ?Pu3QG(I'DP2>E ej #MQ`@X۞Ϗ_{vBiϓ( tETq{=:eQD5Ee fea!"yoZiҰߺmO<J"5!.EfECXgƼNaM5l(Gv&-Q8!̪Em emIH BmOy.6'&v;3.b ԷYbOJvNKiv#=nl8O_0G lP@|?- \h|(Y#%jz.pxeo?:%KLJU}xe|s3LB%B'Q%5&cCu*Jo+$ ]~~c%\KKԌ ,U?=uI~釗 8g=٤&tn|VwZ,J]&f7g>1 endstream endobj 367 0 obj << /Length 2964 /Filter /FlateDecode >> stream x\[~_AKpմF$ <\jg'd=٪>ж,/T*=GH 6Xf.@>}:;7vޝ};?{ g~pD?Oi\)hF{7܍te!?8w n/o-A*~&T7_dyqg:v{#/ *#'ϰimg@PNPiBԥc~Q7ZTP~QZ\+d**,-tZ9P(/,f5?^|?U 0|M|KY ;|ЀI+ѯoI?kaGOրy_pst!-|v) ϠyoV62Z]d|!=H; y1K=Z]j-.ԮZ]zkf1~ ߲ca(9گ+St"7\uQ߲El!< fp&N={pFeigߢnmdh[Z-hFU`HlG-{7eԱLF&%9a1C,}Kd d*b/p"́I1fy@sFmdķ0kaGtX@'PP&~pķ1:{B ")d[w^*40xYEW Eu{iBu+LjA& ^"? RC,73("z|"[TĽ;s].lҫ>IdUkT'+i:,2J!F4iͺV *iվ+8bU jmh҄hc'0_m&o"JqY亢nޓ^xY߼:"-%e~`E}v7&a֛|`P#4!( Bp ]=5}BjCX|k.j*QDNRSO6YH %>)0#J>=;_F ŰX0oka(X/lzKmCǍr˭]+mn)ѝ·>K/yH,a/_6?3_f)c.[7ڞ, ά}蕍NֵDWfR"--'3w(:<4}/-V cZv-}:Vi5ߋKKEt %7 'GK8.w@]-ˏ{ٛRi e .:a4X ߒŽ`<^!.pbUr0?W~&#\ޒo (^u1ж9nCd2E7-5m-숑@uد3<:F_ū';ZmҞNvlGiPгv#̇JsN`,*u >|nj[vg1D7P2.r`y2r#HB eI endstream endobj 412 0 obj << /Length 3794 /Filter /FlateDecode >> stream x]Ys6~P%r&\NMT%K %*$壶o H"MyhSq6"6Fw6]W(!Iģ(HQ5f_H#suc50 %?[S}񷒎$aȘH*a5X""Ӄ?‰h2?5M%A"GWr> %\DPF'>o_H '*tqqx$_̔ĦۦJ\\B2aƾs/UNUH!ap&&j ̱ lYiQP. i{M*Wű0U#IJeѫeOۣsL SPת-ڌ$AF7&Xt&--{rcs]i?46ׯ`JЇOvΚKIs֌xigK2[F@HC7]>VƿPYŜn ILoM8<(Sa,q0dTΩ+4bd u_.-lEO eW\8?5W~IHDYmuZ ba2O([%^L4\Y_zz=`eMeuv"6ָci,Ce 0ӨoNL'7efbkϼp7l>jJ:YiDFzԑ_7ϻ5vӍ)at{n_aojXv0nwmYKXI8Yj¢o֍}b魸|GOZxˎZ<Ä0 u=ݽ=y?{+KXpOo'nggk-ذ u=]X|̝J~vOSWml- ]֍;~?Yo0#:; ᜥοá-g!U Ee˙ה81g߫"}7vT:u=]\%0VBĝ\5X1r5`aX)`*V<{,S=XD$C@"mXl !iY!MA|M aNMupމ4WdM|[ -BpA$uёexx:ORX "> t4_Yi8m%zDRF"('-\aVda c-K3Cn &faqw)o/{mˈx-4# ImHȀƓk,׌%u N= 䈊4WՊW碋 1B7y fg)F;aEH=֢ulQ9ZD#;HV=2Tp)(&B4葒>i\8`g c)C] بG:3U)Jyt$ڪ lϨ+1 f1n6`u"L-PV!a^E e]V_ݐV%=BZ0DgUgo[V&$;G 8KO-ls-H$>WL]櫢|V1^1m{רV! =M;an!7wrr_WmPe霶z-Sy^̻k}ҍsTL)dWݞ#J5[yw?S?bvp~Tӏ ۏXH".>QZԑ(t=>ӑ ۑh@lGHԼ| x3GᾃM= %q BNaD4~]|0 4+'k2>޷Guzt(\Lx0顽࡜_F\rDžqy/TDu0}woacFby?$<e, S t="j{]_Q"J${s :-=\%;ܣwws\s~miy l3I4Hsvw֠ b!,οEv'(3@`BB~,>RClT1V&-OXTͳ5b?"hjvTB+#aS;*kv79yQx$ޢb_ﱰ}#OUA rf$`1eNr$@%BMl%>i'Y^)/̎_ |\87K Pye͋lL[HxMB ƌFH-,R;JgmQe WK PMkl[zLakPESG"#ŕ3!C0k=ɶQ66L;;f S!vf> stream xڭXَT7}c_沥&DJ$se+ \*/%d I&0Vh)tOhhW#`|iU[X#^Ob׸q [Vi9qu9,9:h4\z5`\t 7RXV}``,x)g7+`ޭj k`^W\EĖY4Lѽh$I0INlKʹTDIn5 IЏK[4tf1f.m,*܆5kb9,Q%YlJ˅L%+^0W$+g.}.Z/^"EA?([M_{QXn l}&xۜx~dɊ-__[.^/Vri{F$l|'-v4FOqqUn|TYyׯU+obn6,dXwh?kЍ5lQ"^q^7v^M7-pejftKwoePX* endstream endobj 450 0 obj << /Length 3577 /Filter /FlateDecode >> stream xk 5_C}{˴)9W1OXSBR> E{g!uL\gf絏GtŗA8 Is4I!\6F/ETW\K.ǑyQŧRKs*ŗM_?Ut/kЁ_񍋛 @myD 9..^FG3x|Dу\$ \P;}\!& 2Rm2Jܼ46` #l}JTt޼Tsvizuxf:|aA E}AEBKM\ =Dݒ3oI(ݎ{s#`3Jߍ ]\> yxf5:k[< ,Wi2j?jʧy2ۃ@VqY\Fռ|mʽTy?(޼*㮓c:k5^p&Sdz3'} x%Z>6b\V:i,]?q4-O[A6i7ȿuX%UwD 㘯 / {0_}09J"u?m6ksvV6f-נW{5|?e3~ߛك{24w kNO٧-Y/ޏY MwK(a.  IEU?GoΧEU_tޯ> Sp嗢vPp]?tSҤnqSlڜ 5^"߸ lPnM٫%1WYFO ^3"4wrgܭӏ=s1{j7'9N'1u@'>L5=uܗRDʨI㴾wrL?u=ԛrN- |sKNv9!3\S˽=Y@|Q s\~&JWQ1u:C46LOζ>ѮϸW~1ox<P, 8eO}p{|t=)ˆy?f`S=uoW٩xJIO7S HTH,6[pWܧ3ḚێחK&kӱ̓* Tt*ӽ*M4_5 4ogڪIJG4=۪)HWN2ӏcK_%E\6P{ }̀WJdw.4Mcry%Sd |w ^iEYT s󀢋F e6#x`8* y٧U"*PzN`3:0"FII#`jIZ$`AUb%ΪB@ӈ-f Fwk۫C#MW; e,guŒh7 pRǐ[re@Y 3{4ZM(-sS2skrPF++Ton4c:!T1 /BA6jnӵKtڴ'XTıN_ۜ[#=M , WHߛvlХFK f[LrjvgfcDg=Lа5 (,dM"zePgTqq#ñ"HTϕcaӆb!(2M$GWpv&GؾkؔQl#hdhך㯅ω E=zQ|Cݙ-wtCCn;wN288+ x} SΆ&T$W7o[}:MWF8XXY ~RD ~/]ۖ?n cS;cP >\C wo۟"zjzǯ\wHpz3ZrW?H]r {49o㛨kA!Zy<S)p w3e`SHR}9w7~8ρ'Rå4{:WofO=JX]ǖp9}ֹpۂ;%%pLJ+mi=IT<p&UӤ2{xwZU1L D#,TgIr6FI<62gIjD`ncҽkrY\>=~nh]On^e۳O<&;|$n^n%x7+xܝكNc֌vn~ nUGpwi Yz {K?L.G%tuGp%MO %Ȉ<$a*9g3`DˠaZWp-J|UoG.7u>ZYn1\AӞ(^H|_Ag0b]$M@r/xkJ,] 0FYQEq6$.1hNLMVF m5@R`.T3(*S'j[%Ql+VnɢmQdeAsvɺζ=޻!p%e endstream endobj 488 0 obj << /Length 2814 /Filter /FlateDecode >> stream x]۸}p20"%>w {I^"@ZjK>Iwȡ,y0҇!9̚ks^m~E8 IB:+$L|'J3||r?y!S="1Ͻ1Pn/wR>/='ԡ/QqDbχfG ?P8;vFaZ},6g>{.IܪS0?g>G.C>IH'U/GkM Cv8%_XvW-D(2b(s"NI [-b.|0_4zm{Փ,iyn.n]eE).- :5Ï@ta kbDp4J-v ]e*#\h:.-lK1OdƄz:1wRt&ע9Ӿ͘ &dOf&~K5&v೫.yן'|++zqFuZ-%kMq-H7-Ų9GԞ;uO{/zǐ5A=*vU)RmwL^Bhd'XMŻ~;;SɓpʛnV?I!55Z-jMitVT>&R:N:Vi\B &R2< cZ<LX͌ɘad5ûE<EkckEվgN&~K&vDB]п,O7 Tn.G1L Lj`MgT}n`_Ǫ:j ]/u+39;qKoiOĎIR@#JQK `*&1ٚ^gqyc6?L&vbokw{?n+X~vNND<#D 'tzK$ @Hwb6LX d0[2Úuzez^)J!]m-tߙ Bd24[*-*p%tw1県w&~KHޱrA&k1?Ga  ZպkJ9ç4[tbfV3(:5~[5>U=OpD__6BgykZǁbۈtcp:iᷳ{b4.jyOv,۶xB~HC6p^W@w};+u_?i~ĎOyEBM( S.*|>',*kz?)_ƇG"(79O}J2B; *+UV]d?~%.䛺,s|Eٮ4+7pe7YY_t73]Q߮D)ÑtDZ4W}2K nzXƵWYܹeW$6p] ҇xJG&2xN/I{a2}'Ϝ+|u7+[Z/REI$s 9+p/%lW@10໔0KujϠ~#Jjd[t-`@=NkezI^KCi %haR YdDh`s-W#B]:()F*Z3<.DVN?#Ht0'TA{_,f4vk|[|vۭVEM[6] N/VbJ\GN ]BkoʑBӻTb^"ԾVEh[:(zB<̨g@ VWt>qՖ \(EPY8C W^ŜN 7luvpbD ByVvQFn3D=!DHIULzj9902TEmQAH w/NUz `%I h=5B]Ў)F32![*-3o.ĉXfzZxEQ2)f1 v"rUApiUUZjP OKP5 D P\Nl'S'(\^mUZ=)}:2!/x} ^.GaLx~ׅ.&wR7,Wzv3z۽+pi/|-*D 'rZҿSU`lV# cM'sWH=U:Af`/*b]?@֍H>| endstream endobj 447 0 obj << /Type /ObjStm /N 100 /First 872 /Length 1222 /Filter /FlateDecode >> stream xڥWM\7ϯ՟,>$fod8Cݰ^Sb;K"s'VWTktxS#ڜx6\majh'7SS,;)[SIf,jn#!r mSbmF-Vv G̜`{`# t V@vPA| | A} ?)A~ >gЏ"OD^hX^M'FtB8& "YM'Xc)2&%/ KFSgH#%/DL@̔!DY(HRP`v%Z F#)clY$( זm4MH{*'t~nQ=o Kݛ7/OO| kku²` UZڥ KX瞊-amU:amV@=F_hc}R2G'&.}h^ 1%h ^PJ,R6;)ݽ6^҃j=6FYk;ɵV> Pa 4.A]CjkxZ5\m~^8,^pyh7u;hr xt.oN痷w__yo{n;0c-O}!rGEcT7#7sac=Z:kX̊ٙq9:{qYA >f51ag԰F5uK#VjpN#ǏZ+py`qF~C԰J#Q5t*6j( hD^GMM'O5BۊX0||hmQ[W՞w[QqTfe[lklYie3[Kg1~3WłY $vRJE5>v'6l;wX{Gae}e;Kk,\w;]j0wXﰦ[GAU "^k1 "Zk}Z+ŵ}9SN-O O8 endstream endobj 532 0 obj << /Length 2650 /Filter /FlateDecode >> stream xY[6~_dQ1$dn6dOMȒx(Y&v{vDQ$_x$[d"x.XęZ$<XmO~,vyDF/߻[iccQ5co՛RX!0MD+8R`uzbqru%B"Ub7 _Y+H"P1?/|?3 XDHH,$X&ܮKn5K{Kⱐ# *&Sq譫ɛȫ4ukPMcx. -r\r!/i1'I #ȩoźkj/(E DYQoX^Ƒ z key{EVO /)$aihvp0b%GB"㟉9EQ:KIDhpfF1E.6ʆwhz\Ѩ|AXxVuݐ$A&gPxq :UkOCn X jGSPƀP^7: WnߎRijߗl[D.mP7BQL`ϡu43~V|iO2M # q4ߍITӈ- 5iwƹw -UNqM:h&HfطGll llҿ_*Bu_9YrfV쵏yR0&XtCUssZ E(D8N!5>]…(q^`Ŷr"NIƍ?MlN*%⨏J䯋ٷw m ?Al̡JD&귊ޅʨ,J]pɛ<&tK_0nbip_\;r_܂[dTŮ <6½%ORz`F@ M0Չ)wSӄH]=x:LR.t"➑Pȿ,m~Lr.\5.MDU])[Jr5f`l 7*# 憳n" ߝ2o)]:@Pun̤ Kќ]O:7""¾R\%"<כT :FCie"NcXi˹6Eޗ}!r̰o(cC1|IfD_Anx2.~X(LS J!_r?Y#fHȿ7D7" nsgLO{L߯†LC'= qHEwW'6A;:1!|u@&=?U2f?:kpnD"(I(}6k>)3d} endstream endobj 543 0 obj << /Length 2877 /Filter /FlateDecode >> stream xڝZ[w6~[s" ^MҞekg{>PmqC*Iѿ̀$8Vu0f=^dLdqg&qgIDֳ~T|P_ rjsc?u1O :(:JCRIq՛35*W5̤thGngFIW7(MD'(EQ(BanBI)ޑwTmQPHyhGG)g0:6$Nm A_t=L+UQksOmp԰wEAMA% 譆thԴq5]8ݼoX}Z*sCWUub0YWZU9_%IDEe=N/ t]]ufB)V݌I6D)B wH~+jKCW!FAuTȑ/}YoQQe6ZY >r~$FwP0WiCusLR;y/KT\ၳND',#pٱH"mG.hj]X$'3rGA. k!s.f' &ٴ>HEJknXcj >ؠ~-]@8I~.rDcBϝ߳ĜaL/:g׀]*2\!8-B)lq"ɃˢE7*xF lÎ¥\#xXI(Z7h׿ ʜiB%Os*(آ-X3oc -` , 8\y{E+p}^13Lڃf,\ԮyB:ekM0ptJK½q8u~Z&iSC_+Ḝk`@}n(r:5e]~P1tдu$̾y\٪p p8Qrc$r-Vw1ȗc014=\g^/T4 v،[z{ Uy 9rC8-X6Mӹ.~th;G9~{KH,:==Ĵ$q&0оs2o'lF "!cu|O,tۄ-4N32A$9|MMR.RCrSkssqT<&.(9COOS枺Ç*K!0릷1c8J̦hq'v̮f&GaJǑ+/3WMK'0om?0t,0]B+Iϙ^OH?yZ8Q2ƖCF3ƮpOf%,:Ȃ- iv젡}Ⴗ! ss,?lw?JL"T]@ 6˷$?3biݬtY sɊ($wT#Y1$Kϋ:b5Ƃg cbЁ0aM'1զ!{P)]%b -Xa%r*ZoP v 痓ɩrdj"s b5% 'n S;,AZlƣl:&,'zR9WdIE4"Hgd>јl=-nf=<;SHDEϻy͘q_Vpn?嫾i>-2%MʧOpwBR3.5Tny:F$}`^; 'i$;b\Ƣ)mU}&V?/\0ίVC| *p" E=pH>#-ˬmՐI(mPh{G VZqnE*3K'^H#sΨ$X(/q >KWQ:6(bdB#?#!d#؝LD[u^;fr^C-do9"51I\ MB;N7 5S)l|9a㎈Yk:Ԕ4by[ǭ$Vb^oP*3L{o- 5R 7>'Dk zSeױ,xѣr Cu>$ԘQA|V˿gSLvd66HY$*CyjIhgOg<"9uP]Lo-Y/ȳ<:)bS|8 X%{OO{a4]Rま}"K'YEUCQhߌ6 endstream endobj 556 0 obj << /Length 2934 /Filter /FlateDecode >> stream xZY~_!j EQG`p|ŽϬN7gZX֑TF;;/AT"bXWwA|}_eEP"6ЩH d<3\_7}.eQ]CC#;~:ݸ9"*1+d\ɍiψT);#ӹk\\Bتʺo%ސaPr||r;%( =Ḗe"hmp;@@7 W(#ÜW 7X|.YJTDJ 9Iɟ%߇ X)8F1x~Qcb[f+I&}q\aBWt>qdD1pt]҅ *NNF=zv .@n2?oD[G甂E4YDBf)D6EbWi 4춀sۜm?xܳڷÖ@n +f ǤcGAR,IJo힗U-M</G7OГ0VU?bw~zFMI9N!o6z$ **I×iQ+]a. 5P,$[18_pwt/zTg3'~"ei =l!ۆdq[whC{v-cUӭ7yd!Q+]Q(w?:o\O<v۪N,-UI6r¶&}sk³WCeo~W ^JbУoV92t *a7 #  8@X0 ՗2c`%^ R7v@{cPđo3v7iexqPhJE/'6U/;9 "1ƣmgiz.Tkʖ7t+ɶ4MT7M L]uXhv,EJ0t3Hu恉fkvKGq.T\NGPP8C=,i)|#JÇf@_TsB'80jbcq DüߺϓyxGpɄbqP@\Ȱ< eҮdr)ɴ5h(g%;E ;9pȽK3RPϝ7x{\^ ՜Io@%POC"tX3lz%Θh7cz7 /fކ>Y;UB+:Y~/`ݫ?.R޸948[;Y莓Qu  %*@.ZBhSLW?!@ ye}8e9sSw Oơp253x;3a,vadt@aF,ï.a t.i9R]/$Lslmt6'68c:s\zzZ՜022PZgkp_|݆%f?Xսbu˅[& FhTaHG"3[1U=n~4MK66CaUc[+$LO"EObe+:D$45<94OD񳚞TO5s f (y*OB$Ř{}:"bVd/"X?Į>{eb1^`r` >h]jeer'Tf\~g ] ;[۶'?̊> stream xڵ\ܶb/C}  µρ8M3 ݮVVJZ;{^ӞDQp8̓#R|q7hȗv}GjDp zu>i}q)5_~_Ht!2ƚUR~}:o^|i_^(Y(BR(߼ he^Tz>wXóyQl[nZp&l8 w,BwGE h[eRpΗ/..)MVI ޫgTR &~z WˤL]wiNrMi 0QJ3UIiʁeєi3U~+Jz(e|URH%^ϛ961>UWԁZɱ]tE/X(2N K  `:{a/`^e =aat%UR}Wu#D{(CcDgoG¨q%#IZW߸UBCzHbu[`Ei E+@ぇ=^bZ)͐?}f~07.F?ߎJ34Xu F5E>Iy]vkb=E/Sqۼ'mOB/-Fu8rRT),G[Cl8ZVI7&U%xSg08w2Y)é!jK9"c!L4p2``I|[qBXbQ{nxg?jStM_~$YB@a/AdZkզgkp&dMK ]/bkl~J*P(ʵu* T%YV*MLfGγqI^XsC T]6G7xKp] ޕRG'<#}-t ||1O5Mhf ,^ڌaPVr2a,vU32+ƂG]iܕS*dހqަS-{qis|wQPkUTMH Sz ,$~@ ާxBݰea5kIMgxE< DʃاLvL\t5S2:|a_202(€43/C l͉`VrM|k=2E'#~BFG#;GT TO*E mzhl0CykJgl("7,k¹ _)KƕǛ"[Sedm P]hl?(fYgNj4Z 2ШZMBR&!zuH$0r$| Do$Z@䷊w>ı|Q6p>0!= N__~2MR&OUh lۘVY;$ƖiNeH ;†˻M}Xe:-xWرX{hJ6"sL.WWXA6ܕB%^%>a=AW+zu ->_nӥlBġ׮+)s4\/]eY]: L Qy@J aivD?p/`&NXU@PD3P Kw30ɿ0IXEh07@0/-9;6]{[8^m8 ěSG\mbZE/3t>Ax!n%*+LFljZLSK3x/j$~'2ݎЇŔצ;ƂL CFZ# =GN#au_6\:W<1-EvK9D9igR"'!o0EM YD<0oytח1KB<ʍ78nc bu߲xA li$4nE4*$XI)4" 5@h q(5fDovcҚqxsCAJĉj=>*PN? P}@f.u)*XEx eRB0Mxc!YR+mwxamui=>O J]Z>,y/%WwK = ^& ulpwq fj0S&H?XٓF=O}3q0 8BP5 }h[̮L? c҄Eූg,ſQ呞na) oμ0_<|̙ 3 ;M6#8~(`/ bo*l`ݭ>lgl̢0d$u6a |f됄8:nkQF{S:߳qǕφ>H1Ei3H3T(|ӧ?szf3;AEw wWNj/~՛ׯ]3m~ ꑟ9?3Y=n_} 6੮[zҞMr}3Ew6fI O0f3{Sc%6'zv_&mKQY(ηПĹǝLpqIgMv}3e7٣I+)"4q&>;( ats.a |f'D -88>O>~~!1<;gi6')i7)g>:/G&"]gb6'"Ls^6q|D{&{kG@^{< φЙ*ЗǔGKا'q!Dާ?S泙=A]A@* Q5)Z4׈L7"Ώ7vۥi!6f ZY<| !O&2;b\thYI\?VYׄOcs_[Q _jaմӰyțgjp=v0en6lu 6k, +PHwfZLv3e.JBϝKwDU養Xu] <"4\5zM_)vC$jx axQ820߶=T=t/- =B6b[v~Khf>xr[0]&Y}l›e}ˑ=:蟺W7oQuzfi}ao0-vO,.}'k>~unMB}!(S98L`xJoLB@e݆B7}W1SPeVU5qkvWׯXD1l br4<4) 9V"=LS endstream endobj 607 0 obj << /Length 1934 /Filter /FlateDecode >> stream xKo8m|7MGv"PHYIno#-bIJLg9#[GGި0Q( H,P!\ WiRNTGq@bYr߽zqAZJk4`V% +}j~azh8>@3 oxS FI?҆$Q(l[46Z .C a$'L!1 ZhWy{lHsڏQJD6eiQFiٶt~lJMy<ɾyus&҇Z׫˝+$SMq1ɓ2Rך$H5/A`bT5GBD8t8tl*&b# $1*HK:!^RL"q:;9z?;]~5!Z'Cxnz:\>P AN^24E&)bF)A('3}8IGƐ|nx<'f+lL=Q=d،[ǓiG6۾<&Sc|.7֛4+{J 1H]DQƂ]D %$PL#)B@J\5Ћ>A6{90DX3M~YgPyk/A,-$a__l;z5_c\!TOCu(~85id{kTɳt Ɛe4 bb*nId b:iXt1!FFT}WAiq \0iKorM'\DUCR|e!oRtcN"[Iòe LP5_kC("o·wuzуuJ)To0Fa*9ЬUesMʸ()qqy)4_TA"I#8({R?&V!۱d:u$ddfɲ45&ϦF,_]EcA4Uuf_h4J1m!]ۆټm9FrũBփ WiEF-H*:vm@mZ&4 8, 9ǁL i3zz؇ᛋ/ׇoJ_M=7pxOqubl 2[キY@sWǢ47W)v\W"ÅaT]O?-Tk {'-a!IB~ ()?*zPE=.x8у}{nk4&0NRp W\/Ä#X[/(]p$. ю qw-h#ȗ?mtӺcz'm袱@GJ窼ɖT7+ëӻ˛pb:}x`N ف)Ro?e<Î::k:s5I1~.%Z3ÕDo%4FN.O&BUjQ'7ĸiЙͦw<.ٯ-eYZ_G`$_.-Uk}'-aIЏGA+[f[Zj˂/|-ڑݴ(e((npt3E{iJ$.A1AV[ 43sHȌ2]< +WwA[ HLo|l Cp&u>&htXb\jQ> stream xY[o7~_Gx>7UE)HFO9S[d|;.6.B$V?`TG1ג5ƣnŷ)WH[? m|`EiL0`FDeSJ L2&`K&0/PŤ8U) Wa 08bn^f{{I8g/˯`Lg߿=xp+T4&ˍb^DbDRLX y \^a50VkD4αxx~<>a?us|8z3 |^GR|<;=3i~c80H*9 I%(Βl#|Ѹn6^?]9-OOtx6<>zCRn1yDa~V"2@ VXv|6) R!ӱ>+ģ)2<#qmz =FO"Av0[EWـx:tEF`Ǯ7>V,G]X6S~ +S$zRsV:>~8]XN@p9]S5ޡ:j[Q[z~>e@s/Fp'Sm;6?/QU{cjRX8^V c1/رs"(2b_Q_J۰0$p:=[,' m+aL0 w \)ԇEWs9¢jmXYOnr| uDdS*J"K*(!>Ud=Q*2*V>U䂐)O~'[~C؊;ح؋-f}4XӴ,ߥ?+u-}Uk]9݋]_%\ $ endstream endobj 614 0 obj << /Length 2226 /Filter /FlateDecode >> stream x͛o8WmmԽe7M簇J"nJjYԇ-p/P3#̐sg?0Q( fOH,P!\ fd?~G}E]i,hyK٧44̈"M4(Τ͕/LG.&.a ٤Hʫ $F ImD+9$Û̘OPdXǑei h* M>Of><1#0;š>or!`2gՆ01A&-ٿY&OfzjiwO~.IH᜾"t*WHÊQT#jy~9vsB(Y}`!wXtBY. ``!ST0+ႍOzSF?|x4C$}[?۲h&9bKb_&U,~iO~#! i>/Wo/-|9dBшz aAC rZ?@`!^>w77i|¥=k%xIVKK܍} _lxII|dFV'i\kil?~6=4Xr܃a+6qnGIkyAt%]|{⣤ӉӵWPgIHKK z D ,mS mU 'K.&^f`*罳\~4*#L%m7Zj#*$"K$rNo(BaaLrJ+dDWXOf9Vy~lqfDD ~Ӳ%r ɪWޭ]F-|ql^E[3c6J~ݻTD{7eR'xCJM*ϹNk94:ɳ]#APҏp ^B,wY&83\ehZ[xiw֖ CC Cv:wZ?hgjt!-${9Փ.:ڗr6::Nvm>È繽NBZKG/!@\gk02TH%j) ga@y[|<^߇q}|(2*Nǀ{OUه~U{ BaxA;$[a sYbIk1{31?<?E endstream endobj 621 0 obj << /Length 2833 /Filter /FlateDecode >> stream x\Yo~PHuc؞ Hƚdb@KŅ$:5Aj6I!-m&y$7*uuwxp?g]Pf`TƳH6P ޾9x:Q<w#!k&Ư/^h<-"M4PJ/܅I{gyRndb0YLoq@(y1=$CxP>PpU`20Ȁ V'7sӅ)B %[ D#J0D2Sۆh4J@ȪԫxNUZ<0!Ǒ8٦*; '0 a]|j9@d7NI_:0)U5p ٘(0?0}sȾ$($Os)RPt+ahMs@iˇW7޽ǫ,`wƗ^ތ;##Ciq = ֐qT>LͬDF$#axHbu/a&ɟȤ=RM& PpըBj( 4UA8j&UܢaM {}9f#Y2KM)EZ9%pX^0ӋV'VPp.L4h&O9y7JGuTtᖪS@Hc<)> ()$a8>Q)0Il#@F ǐOD]~{CxһSbSEs#ru=xx9" #ÆMg % R͘_ٓ^E{/4"^s2*yg)14"# H=f!U׺ k* ѾyV0#a &h(Ǽu}bnNy,&w/2 {v5ko*X  5%'fHŒM%β xr5L!rw*8sqa0/@uAJA"ZZ߲=!Ic,YYǦ?T=9SP, оEFAdZ^x;mgd+_2p<3݅so7PQ X霌j\qFנV."!Tk`h:ǫu/n݃d#*'*{ 1N:bvh%zH^ӄV3|=Nh '-wd72_.I??'Caiivx _FL.iIHK0$@i ?*#[g>c>d\f_e #^tPztjy:/T=l]bF8^j<n`4`2*:',#)}NfwoQRۗUhO?~wx-; _3}PbGk6 -qVpC`R d8//t?F{y'bQΣɼ%Z+,DZ^%[S䂂r0Aav#OaڷɂuSf7o 9R)g2Gd F06YTjJn=S!̽0 "ȯ@Eb-(dކ<(v^'(ߔm,`^<Ē =ś %*<}I͟>~j6nt)ټȯ0eq_Vڟrꑫ}Z-TΣ.5Q]j7s,b4b*/n~ ܅QUf7x`EIgrP+^wіZXZEk߯WGY()"xgos&2 Uzݯb3ջxպ1&̽Zx,PTGN6U|ϰVRC<’CME Iz!ms޴bEd endstream endobj 644 0 obj << /Length 2635 /Filter /FlateDecode >> stream x[[sH~PeM鋺=o+)#ʤ( M-Lm@M!'GΓOϞrR 'xt@B1S'9?/GyjS/NZ_E$&\&z;_׏%v$@ ]I$ޡ{c ?5!{cw/?agUy]pe/ŷ "1Gx%Xn W)z^#6 <عP?a QƮ?>p9 Sf`)z>&c C[YxFBd OA@\uklә446)[U&ZϗOx;Rz8kO,r Ap]^þ]J٦9V7ns(Іw.d{s}׳¼Vd|u2M~|]ZY8lRcn]16M9.93LpބSAn+[SV-aC IY@2_&A{t!@=e#n'=Fa~wVI7 ׇKxBӹ#0?7iYby+2E/侔~}q^0@hXSx;`t3}(vԳ,-!mAd&sG2mh]VJrD:]Of"|GT!E^I Θ@GA.=]~zrF^MQ%Cކg E2>^bgr?+y,6F D%*oaeGT J\7 }p|%7܎,ZS o]R@`0ð /H]T2lZ-f8n?iEss<*"8@ aX8E\fJ9̃uˑ`v4ꭦ%F1Tv 9,h ZICs!' ^M~< =;`4r3O-M/~nAn=}{Mn(oA9<+T侽K? Mx N+3GCC8!y=uLwJf(Q53lҴR0:,Vbᢺ BC0;xQ/\L_vR= a"o)U.q\1C:~[|x¥%IErJXΉKC62>%=l> stream x][o8~ϯ: ëH.KvE([:rFVKJ",xse; w4! $! 5 6GI88o?IGv)R18ˮ=}O7 @"ED%jo,,9CL{ãLp06qU F,86TB7Z*܊+?&^[.LܞDYl$./h @ؖZO㎶PbIj$;ڠ4Ʃ'‚`S lgqxՐQ(8n C@bB! ) "Jx@V:`JhWW*57 ,0)JCak SCiZ2?ΞS:\f j*tԵ@LE#j{ \_))&>PSPaa2 m4BS*{ţ8^ $Ey2OXyޠ @*B ULϟwCJxـ:!PT7>(h>uEW,e pL3#M8! (Β˺ $HѵpnBh}PPa(aT?8RxEgGg{9xNwp%$uC nB eyz''äDJ&TX7&\<0);Q8Ԃ!¡?\.pZ#&I:Ï~oZV,pum1GeJ MMت3$Gȹ  Nn3S"dh,q (N"HKM>WQ,͹8N)|y3_뵇BP-|`؁ &옮wH!"aDWX F1MH.f`a.nv9L8AT[`M0bM7uK1g0F"v@zȱa5.֍10cby7\\dޜt.n)_w`G֍9<)ϵ|i\5|l|V}};O('8&seO1ъFlG˧㯼֜%#5$o+GVۘ4Ae{s9%.2g=F/v)_~ *]穭fZ5˔t-V岡=`8j IYfXn*)"04M4l_K.*kfj}_b`N&"ZOdC>(RF /b#B\I\]ϳb=)$7<_fYU"ɧ]2jmTE쐋2 ierj ީas|9ƐPI`Sl` 6 #ll_T׊<h2+8cRNdLZWSZF<`R8`4hrQ(MN4-|[*>Ό+}H$Qe )NF|p!dx22zq7+"o ̳ `~yoFϓ?/~)j<I)-|` &ȘhCA$<$>;u:uc2tA )o^ :ALK$td]r_$So\8MЄ:PT7Bc)F3ɐVP: ۟(fR"뾫WGOΎNO_J>P=Pa0HC_`"* *`VvU0@uddXVz"qKlͧ h#ZM0`FTANYyxϙ#M|BucB ,Kr73j,lrBE'}u47e5ʂ &# (7M5񁚂 &Q[`l~Ak$ܪ>0@ud)۰]U } m*Q@BBejXX76H% ?L%th0A,Q1\D$Z@OMH_:` OP>rD2 zߗ?'"Y}j; Er?νyQ`#~܁ba)[7&ؽ7'tPo1AՊBZnNS&ur{Ei{m7{CLow9flk9Uoo}?d1ܧ{є+.ӨvA\\![Zd﵏PeuBtl2å=a:[3b-mL@Z">yGu>b![q`ιI+zUmsWU‡C/wkW8B_ể!8(57LZ[ڬs%yWDY%鴻큃Yp { endstream endobj 701 0 obj << /Length 3277 /Filter /FlateDecode >> stream x\[s6~NBҝ*]w'n3iCKũL$7})% q6psb82WO^>AIICgj9?J(0{\XNSv8E*⹸\"5` ^9&_ZNq -هwSKi0ۙ}؎.9/53a9$lE8`o*;C[' @__. X. hpb A^k{9J}D}d[EBG.*̲w*;KhIyqr ITt 8+G[bMl m@K<Ĩvl4-aV8\ԓ8ozWrHz{=5Ƿ@\x!bAG>GT4n'{]1W_֞gh|Qmoߪ'n}AGU=KRP(<]hߟtV |z=LoޞMo7]LƗUio[J–gD2TM+Q'َ(V6 \{ARޫ99ݢ@TX ɰo kb;D L_-Dv{[^$y\i%q*Y Iɪ?Y~7U[-bݎ'[U%`"~)gSxρ#%vI2eY_<.OGC9!n=#i{Uљh%!HehK|X?(YP|W!lUmɽ:J8A)!TKB5';k#LJ Zb4AtK@j U3x14p!kiu{ [@T._"#)>/ADH͸I@l%\ =W0?çx$(\`*#BEC.DQځibp܊T[,KRI*Uf׷?/On?\.?޼N./g&>>!KM$ؕN''R>@(Vqd»$rQ:, GZvhe$Gū7UST;ebit"KfjPo2PɃȗ0h/|#V/4NKvzN`?c +6p)Z۝POh],zVaGz}҃J_Z/Hx|eUfMqqe]C{yBqE |K`Kq5t!{~U C鯪o)kbx#]Dزfytz V #,!ED2f@Lď)%Z\cs[fT* 5lزƚ ʊ  q^etrŒ+=mLet4:EنMN1>b4%c]/3V^sX_&ΣilYA)N%eHS1 r)tTK]$M5=J.MJi~̜ZfH>dRxj1rn5HRc 鴨s g%rWłNPv {a" KatHL vJV xZVdËU-a&T[~Tءi~~ 6˚ş]N; 1|I )J upZlfԹ~iImK|B&~H}'M 8/m~g endstream endobj 611 0 obj << /Type /ObjStm /N 100 /First 882 /Length 1446 /Filter /FlateDecode >> stream xXˎ\5߯Q<4 dȣAQ7S!Lf$6ݾ˧e[kZu.]BTޣ-I)xb`˰aJb`9zC16aMX2, ˳0Q4썊(EC7e-V 3Qh9:ZGϿNYo/޾}ܻ訬w9k١XdlX6LONwnIY<:.ͻagm!Fl`I9]+z7/ޕ:1ڋSnwxO-RO/^}YN_oN#I?o0)QNP^gHBߓ "_ws uPt'bH0u"`F4R笳O82:"AB oz 5\_* ,[YjKuYS)y;YNwrv["6A ]ZraȌ{. ^ ̅!3d(ZjɌڥFذTs % qGI5quE:85,xgcfTw^yyS0o{S0@HacFJѡc6eL?^I{䰑-XKǥ_F) endstream endobj 744 0 obj << /Length 3076 /Filter /FlateDecode >> stream x]o8=ח$ +{{^ ٦cae)鯿!%EjpM%]΂٫_YL␅L$,fn6OŒ`:Y`axh8{FaL0Gb`"ݯl}g}#3Ixl_$#}\QLRCLv^|JWFp"]JD3bEs572fj:9$YMu9VOo zZ{3-ҵ->zAtĸ"_:1Ksh97N@Ɛ F4$#1Lg֡wQ Co.$ nDLKoBWHbO'tSeIObOW//gx K33{ #y]T4W^H@'b#9EaWߜRA$I>Y?}б0›H_6HO Xr)8 G`6ˮt#ɡ2 n 9/ʍh7Q+n ]Tk쓺)rR7m B v4$}58&Cf ;:$O fbD!)v镻hjC#LL2*:-t KE[Mْń.DhoВu87c~Ӛ6 6gư >@sbݵ؝ע!l7>[8X.T׿YVȑg@:);l;g@o\y|ϫ!s@!`B|-5|\uQk$ a #za ӕ?#smxwx"{&&^uur2uF=7LW@Na9r| %ĀrƆyo@j ` 4K*|Ń;B_7K -Dvn0;'-h2=Ai>c{)N Ol,VuyXE9fI GGDF|=ţ8kK|PUV:AF c  j\]YQ`zdRxG rZD7hS A<u/t=shv`3;, UP$oܝ>q9j7s7&_cwGE{J5 =;&y`-Rǐu)ՎTT'>P&>8qoPT1rmnp!K< 6|x{VzbM ^L",#r&4lɍvۆ7Q C"VۗG3 Bqc*"*=3HA3QZj!3ږr*;f@CZ.v ݐ ՝Nę fU66[!K8 ܘѹmDHTJ_$~)RݔO7y# GH%[} endstream endobj 776 0 obj << /Length 3123 /Filter /FlateDecode >> stream x[o_13VH]J@HC:v$%lϱiB`%&q;nVtgϿfeIx^$F_V럾yC\ss uȷWϿL*҉peIRStD↜ņh᱑3N!NB/ev^L AAn& o¾)"O'BQ/eMrlJhb˺~( ? $ȀeهSH(KBxV34CG&z]Uǽ_<ڏ+QUr:%*q) R%>DI''tL2;/i =UGI1+ 5ez:[/ե·گ/ K)1PK8蓆v+ :"P Yaļ>J8'uLOlCy2a c)4NLkoL1_Ϋ&Dv}eJGz1SEX!ބQ)BԓjL.evTGԕFz2%*q)F UDJ!J=c %yI'H;0JB`+ޏ/4ԥuň#\]er2Fp!FRtzҠα"8c HqCfN B%L~ʛ)´xC1G ˁ 3w\G/ /7EVn?_nNJX"Hu7Ubݝ37v΋<+jmOYnǝ-'XdB$e4˯mma9/˛P-uMck Y]Hzu. ./@ݝHSC~dB$Svvă$d Q.(&xT[ia>`:to~Y2&g^ >0a0L-,tѐpGW($NSˮ| Rݛ>lZ5\BB yy7`<ԡ 8X*IԃŎHL_? f'@E!e%~<Ta{6:4b\jz2^B0e& 0lޝbH "iDޗ!(|r]wWb6b.]%"C$7V3iG'.M:c1C熭ڧPT x˵& M]!L5 #`>/<@h\rT(d%$rjƧn¸*|} ~nB;! W[l{> stream x\o6bq_2zzɹp6q"w.+iC徲q{!p8jvDF/O>9{%$HB#aGQψgwx6 dCz)ԼTթro?(CC1ab=*=gC| }Ȉ\˓w ~@$A\$%!7' 4HNN8e4Y6%Un7~P*7;4jj̖ TyYa_(M7Ütb8iXާeV"bPyʀ3 uoey)%t"LNggq M(JJCX9 917%\.L.'tqm輶*TeXj^&˦ |]KXPYZٹW-KKuYIUk:J+C$Yd[['>d 8U*a1Yڔ\c^؊RM ]zPfBƗfuRiI,ԡH"nΊw=**X vT-FXmj]dUm+gKzR N O[}jl03]搎gl'd!uRuCOq@/iPj`lSHm4esS1[TYSZbhmNp4LKe-O(b03͋nɠ.,PgZi("vu8孈FB 0] :IP瘇yňzO>2u+fTlL ' }Y¦{OuaI%(wҷh8xׅG[c.tVyǿ" {h-kir5MЕ'ghQX›Ĵ+'W{BXlIl6 } b"! 0I3>@z0 ,xڝhl8|I-{ZoܧIFk qب J<)Ƿ+| yā[?#]n\go ZÞ0#},J6|h:#kl?k"`Ss"Z/H)QiCC1#:#(s:~Vd -@">^vGc ApDxR1bA+hɘ׮O%M`-K3Cw,8v;XjULlStUlVɁ74z^B|+a+q-y'W F>{m87uKl{wy׻_ l7?~n*{[&{U΋riI`(-I-vc#XΖf XxYYv46GL ӻ4U!mK"Q*ÀG"f֩\f5~zfp=Z@ -|U j:9x39X?GYjZAvn͊ZYM86ƉР>oY=Y&a5w51E)%]7x5< A$8kQ;h xW6m€YV8uBuwzXe=j#t 0!W o_V]ft|xj3jbOρZ28MHe|έ| n߆Qwπ0 >'RB 2z:IDž'|d~A* _/H@*Qt7XR0Bm8dwܧ9:HS8FT?r4( To}R*Os?h t`&2Pa\ctbX&BJmGk?n;VZI U@ē)`xbP!be}};^/ٳ2^4;7;Xs]ξ=U}i'pcgp^> stream xX[k7}~ۇjhF`BHI_ =c60HHɚV"R⎦΄J P] FS^'11JQ/jF\b’ ~Q&(βL.n1e=Ko0ظXaoXft`҉-cb8r`s[갅b K̉aR0t{0CGe1qt&%v 0^" 31 iqԁ3[ 0f9 M^ "cfwf?9 3Dٰ a_ĴLď"C#-YFag 3 坓 /C[<`fAӂ< `;"͜hŗ!(2@efQ$xh%/ ^]TpӯwOl?XPC>gkvz)z?ldWbIQa۽Q_nY^^}~ײz={>Y^~Zzzwa[lgZU/mA[:uEݏ`Z|( +}" @ծ Q,8f-EE2>GPF\Qm;x2CzzR79hX7w}hP9h&s$e$$$vV9(A4*&'gNatâ:rXs܋ tȅ 6Qg02Iνɧ$Z.:CsPIυB&s[!s/LKbu$%%dljoV (u]֯Zj> 3dSx"0${zq~&0vuEH:-v<;RM\3q@8R7%=B1Ύ(uƻ@fARv\qo{p&kb}W,>J$伸q%!a?E7GOa~;<εʢv 3~ ;:DuQar(Hr8tƃBi-b ƃBdBƹPY.b(3X|AoQؑ`o 7qw{r_ &d BB_'յ6 endstream endobj 828 0 obj << /Length 3815 /Filter /FlateDecode >> stream xks~'pNg:v;XJ'4N$$^MwGɚN{{ILܙ~n.&h߯Ξr"I<ŒNO'Wӟ޽1 £Ds§RO+u6W\y*& *!0]$Wg0̉&C^|2;h2Dd`fM81P0Dn&gyʛsMәNL$ }HrMO1 )E"b1FR Qs#لFT? 0ֳC; 7g>I"W:JJ/DX@J[>iX/(`"ٺ"xr?ہ懒؎q|:d:bD\CE7qڮɘՁZ&dr3g8MSv g >)y*~#M©K^,yYJt}Uʭ<( fry ƥ,Ba[o|ܨR32r{?2+Vi#a~^"{N(b`폦ԁR[7@dU_Ci # "yQg7IT:[۳ N(Qtf3=-x~(- CLFvI OlQ;BjUٚR˸+|ɈC| E6LD& Ma%qj?SȆq r.S$A1{5Sٽ:N =K/$ _t@E8!E,w>@jvUG'su_BYe=lAQDNwd1h B=-b$?-EaPQ= {*",m%d,A0{a] 1TU}UGn`+-yuvo'+r?٭w[}I",tI=orHG־iNMVY;1dueNEMF Ƕx(5AM8]a,J ];0֓KީzQEJ.%ivṾw-5ڍةoT'4Q9@cxXۆYuJ\ ӖS0CnCG\ ZA197R-biMp1ZAMOBE3![Ů:]"" TQ=7UYhgsm-֫\ꢴve~o,4G[=E /"mGYjs_20I|*$!՘U}&b@y k.TzbJP|lb\xQ6=:} ^ΰ[xd!A k%We wl1/J /JX8oFܜr%Jw1ׄ4kaD)c-"'SHO[`ˣ5W ZL(cJ z"m{amr ୸"bs5&{X)FP70 ´]Z륳tYof#ól0.-6Ab< {;{I}:hzLY;'M0ӻ\>1M"wg"Z-hW;^7.HՅ,>mD£֍2F׹v7SC-@~8c~Ye #(sazauwע.(kk.:uftj(&m{]l""ZEC1O D]Pew2v[ttúҸK SDY~YoA{fk{:AW@cIPO/=Yќ= ozE*$ 9d*)"7_?M5c.XF*<4Mҵ:^c7ykկ|&OOk(.u gj5~2`dPl!dB(Xه'p=dsu\#!o=%D'Hl #l2XFZ~#l l+y ~@'aF ǺGYv'm.S] r9)Jw a-aU>Ԡ> stream xZ[6~_!6axӭ( d4m&Hk+K$g:{oX31EŦ(\?FW^wi$Ox]l8!I.4E[tZ-9]|\xlV7_,x症b]~]F H'Iˌe O3'r3chzf`o4Zû%2Ϣk3rŌ.hћQA:h"h1#, &9.C,?ڍdHs.HbKmUd#M} h8Euet@$giַU7Z@ԇf爥$GKG7 (mz+jzUlnp~{m ;zZ֢$e,,PaIh)v]u\da/klmVkFP־6~1AH&&dt9 E/ˏq[ʂŽyJ)[8zqBXM ;ސAYB2p+ֳ~.66BBŀ ءi&M(HZn7l?XJr-?s h.>\N~=fqNرVkۅ`i]w67/spU '(ނÆ PTwX\wKFMC7# ̤!Lemݫ1ߠ2ķ2d[MHuVZrsVnƶ`M}:7z5<_oh"1ɩ<#q(.pI &K+OWU8OFd !Baiұkbcl6 'Pf7Qkm R4>WI7ԇʱryHwI6#'3QhGkUp [ӫX2ľUV.@ߥsdX>5(/I!gW6uYZ{2vRcV3cui`Q2ԙ9m }Yw7gd(]N>7 S0Eۙsx> QΛqzτ(SI N,msDmxAA_f8T1a ҏ)`K0 y~:d 4b9(~4(S]~\;g x >wcR橡j|M0Iv/'μsa#Ab&e C : UL8azbuJ]zϪW%eLSBќjB4:[JfA2ťjPn$Mc'TfoyyX3E-PBڻףaJBź0 ;!>!#aaf3G$!A} W!^{{r"虤$c94}z#/HO:g.xF( cc%>{3FzܥbFIemZ l}'|7RQ`\NT4g\ 6N/m)gS 7a8_ C,վՁlW,n[~>(g*gb:ZV䬯's&r@\r\hl : Uhrk@ÂP,'YY-b;n[\1,,,O*Uexr~cʠoꕩ6-Je <lqE2dZP=I_0_?O`\aT|'X5e\Xԇp3e9 ?*m,o p2+C⋕)cnXoA/`:QN? +hm{{ukٴ8E~OcƉ,P&?8"n$(QŪ54M{ >W&(JgՕv}[ 5W=;OΝ"LM2"!~y1#ؔToTeaǜל9x| ӟZczZ(:քp/ur2 f`w 6yIC endstream endobj 882 0 obj << /Length 2682 /Filter /FlateDecode >> stream xڽko8{~ne`͐wE4N{-tq{bӱ7 eɑlh8MqӣgQ$, Й. da"(H8ӹwE֌^ݗ)Bժin7\>N_?Pj~Xİ1PO9:u$;b L3[}ȝ9{p'sm0WN 8dܹ8ywpci%>r$Y|Å| ι;ܭTd{:=%eG|fMV5>Q]5Ay9WugI^9͌C( #k/V봺Ǧ9R5BYK_DRG N"BhH#(Ur=ꣅjVгBJjռ*JM2=[ HXS\S9)槓ɀ3ޮqSnPgiUYUSfp̠> q(ߙ)aqhM6K†&;!Y}'!2Ҧ'|cI8r][QZ Lƣ.>>}kYK+dάq 9-&$f2 QM/?Q[2ܼq"#_.n~|ٹJ9}D D ,S>Pf+h$XG} G]UǏOJ U*Jq} V-#T1S:NCd{j湉۱N#A ~?qtYϖiXSY @>#63H_L̽r4Rgd%]AѬ\sde̅'("{Lc+Uܷ/uA`o +D1b>|&LF@75}@DOq?}[&[jXEuzc sHMǠ䣭Fy 'VYm!PADWi3 T2je&7IlrLcjKcMK)` G3}@sR|0 |G<^>;~-o2Vܬ áj⎒AxjYR +=XQ!tQ'reH9F_|ʣBS'3 _XP;av=hޅZɄ}&/ܖ$im!FXZG^ʊn{+ tP/$0J|cp~@u93k$pAuA $&vgl:EVo3H.0i5ld݃ړ8}~kVʊꫛ:ck;=yBXlBr{JcOSCRvvJojpsؤ#PTVT>l8&mNqFԆ՜,MF)bWGP;XW654T)@o;`+.B{6nrG5no' =ۜPj'J/ds*q羳}ϚZvl/Rju4˩.sces__x b^ ?؊$Jem,^ԓH ۰ uYיi~i[ń A+s0 دLZZ9u]goiT{l >!:73)Ϭ;ߏ'̠AF;/ޛPXD pE]- MQWM4ѽ|s"o oc_CȾ6D'J-b۫@~m5  xh$)&ھ8v/ձ̿h`ݒU!lpEZ&<ym[l$. ~PNlЇS>w  endstream endobj 821 0 obj << /Type /ObjStm /N 100 /First 879 /Length 1575 /Filter /FlateDecode >> stream xڽX]k\7}BCuh&Z0Iښ<8ɶb!=^uzDZ)ؖhZTMkOVKDu$RMVKbZaʩ5jKt*Iq;'긑Jm$#/w<4bTɈS%@ E+uCfUdܰ͒qj۝R{ذCrT? p:$b8 niM\i[ǦW";jD;.I ԠV/R# Qׯ7E8U0SK0%7XA^i렩P)t#47dSCM!p` q^+nNظ-:<2M7hP4>Ԡ76bPcw9(۽wl &2dq-v Z-[ hP.EHqap O1<Ȏ |aÝi_1( aEXa.oq=8Qw y@rw@ \2LGG柿Wi~^ov_lsau~Zri~~Zwj~NZfWkyh&R:$=MGGi~7o6i~۞Ʉ'"Zi*Ϳ{jv@͹O͊#SٌbFkcؑ;B8ef agL,5WdV+V b-ER|[²fU)~( AT5#Io۝#{oJ#Zi(,{| 1i~M_N ۓ?W ݁+H>eٳ gA|VӓsH]v {sJ^|&ʰ"QHwLHL%틉ǭ8L$R%=fE.͏u,_g1Yf,v޻<Ű a,#,R [{cݶz&1 { +3UB"! a\(131S@Jnb(Cc.ٛV[n=fk1=B[/(VrLg誳RkLg#~D(RȨݯK֒w%FВ%OVxRH3^'HŒ:q~k :B+pIOCՅLJOv}zyV j5cCDYn315FfABxb,E|E E+>0M{y_Gkh;_zL!}!"X)D4eDs5HUKil }Ӗ ܆,XV >1 {T,"ʧ]$+!,9]oȪu ~1@oEVľ|Q+`B QC_Pk/ts?:vo'JF-u=AG4:F%,n[z㱈EeVjFm)wŰTSV-b:Q/f`RUCIb~Ѓ"r|u"&/ uC endstream endobj 909 0 obj << /Length 3170 /Filter /FlateDecode >> stream x\_۶Of" ^$q4I&-HJRܷ%"uAg<~8"bwbizw~(Tz7HĻ^zM~$.3*pJÔIhͣ싩~/?̿SS@Z2M+Q0pI h\].^\_@=3Б ow-FG 6M VDSt(4ePM^dt&P1 0hӞdG}/-̸ae,k"MGt;a1_g-#Bv2 e:zgSC^DV,m.చw^zuLQfox%G^I7ҍ`c@U".Vy\adb} KF(^W'8&d!=H%";H`ߤ`0s͢uG2h 0p׭T ᾊ?tZGW -\I<]Y+!#ۄ NQ'Rgے0DmipJuqakԸڸ~ ;FS}q8sqĀAhF_Wwf jd.Y(jw4Pgf_ 'ÿk"j0/UӠh">D+@w`3gCtw,xH߾yمI ko㓀x }GsfQ4d+5eld1_|6}`d`dgI AuJq,\]7.,E`JR\lyhh;w}YCW DN ]Q#.I45Qtxalaw$A ҀOEȮL *8ԏVb=`Ξ*c'&WzGh.˪K+i@ݙvmf~ ~Mb{8(K`Zse<¼EI ~ [I;gyp 9vk;PVZιB1Gg%x9 J n۾;! ~s(8c(`a}Gufv29Gq ǃ. &l'(T9r|`<w4@gfcgNbԱ3lL1p12fpY8Rae6/uۑpiV<)!YVР4;x!n=!@;u( }G `W?>Oo#|^)"LM s%ˉ́ C%?+ߩmoZZ‡_j]gU`|)80>^""L4<e sf h?^ZA@ݙob".Y۵k(-^J'5;Z3,C1$1 *)5;3!=)\~ꐞJL;q#!ՀRQx1[E2m\ Fh#&soarhfSPFc #nupfv6Muݦ!&z7k;ٙr ͘h#4v6vtou.CCx nsgv3f:U1?tO4߄݀E:a6vi(xߞ;+Dxk?@wCxwT3t {`UXq4;Ι)juVahǮv@Ӥ\:2W'&*C`%B+a:}lZ iCqЬf2m5j"Ζ})6 ->FƜA//P]*)T%@B6\zv*3VDIYֆTތ"U},|W~*.bnlH((/MV+n3'\`cV%1]ш@,xZ, +N\_7i.>e8Yc(S`& endstream endobj 960 0 obj << /Length 2587 /Filter /FlateDecode >> stream xڵZs6_K 5׾s;ܴ}%"UH zbŮa}{1݃1!/>|z27ɄbiBO,pWj.䷻{L""š*0cV(jmV%R !J 9Aq`)ӿkU"R6͏W0;ٮ56l]ԋㆢM!tazzrc"|?202oyR'Yxx&$'s_T o6AoL;"f6 FS1VИ`)Wy<Oti 3؛19c_ Yf]4=zҼȥ`a $$rKbR-(> ;3MvKrvc1G<yT8+"b ^zYei bpH7%`gt,Foj;$D1~5nk!:Mf=04IYiq?Yf?v Fzйb=7eVN/װNyoݭTs04 [ P1C@DEO;˨L& hŤܬ=07 م6t+eWJsŗ#`ց1ޞe:_Z^VvhEрS8u+oD))zS2Y9Ar ُ +C˙}؆(3d;hT&8Sm 3ֿuֵ"ƌG먺Md(TD]0t}0Me;CAt;@́QʝgIUma6ec_@X3ӺmUµ$\v`b׫rXN}X6Vuۍd2uZߕUt2> h4NW}Vg/[zKPl{` r]s۬di3 l8J-Z݁) W$( 16C{]68:Jap`㦔 FNY wyۏZ5 6"lNyUpcU@A 4"c1\:u\?d,`\ h#R˫臺dkިJ"U9@FHd |)RJ3ۄ eGL,T'usX_K.SKؓD:r|1vr *| Ubn,T~=2]a0 ?@O̾}:':ݼ# &DjX&}'df_ F) 6H2zɓtt٧.Ofd*_Ѵə\7 y GOD잲CCm|_Lcm{ @2OeW|ozeuSpU%ڝˀ+UXȝNŨCNVۤI]¥aO\6D: *$ Ed1'GOzڴlnZڷUQI]JS;3c2(ìH'MT휇$s*`A0{U,tހ(m;ꂭ:5H气ۢYZ@i)u5o,e.f\̯U:!~}|g%SZF9y;bڻ>_[߫lMR; bÊJ4,sVv\tgҺ%w PGNcٱ֠_^y`}9zƘރwe6ᠵ"M%$U<?$($2d m9Kܫ~YddW̿:~ 75jhҗʦB+wG CqȫSפ9L*CY0(ֿ:jb:(٠Ƽ[c[k~ t.+Ss/QNKkȲ^etI"X . endstream endobj 981 0 obj << /Length 2707 /Filter /FlateDecode >> stream xZKs8WRcITLd]́` EjHʶ6MR,ک=~̣ޛN^c/&q֓ b1!>i=>咎.11[>VXQ߬ҩ~uuDHB#N"†*ax^`!=c7&ϿSo }%~yf“.go'12T2% 7|(ER>[I9nW =fR@"/𘶆G|JtE G5VI TIfe=W92Hv` B%%r&=(raaĉOX˚ #Q)scrJ[ 4˰L8c]1q 5H$ nK`-&sͷA#bVM0/LZJ'i? F !Hha, ܀Yw^ 8-&F9 XaMT8q\X5Ę("mKc'E2̣=퇰=lHW:p,Agy8(zI.T>ڂ湝 Ac$#j.i>cb P$j卪kBO5<4mqA~(6.b 6nyYw$ NQ'smL%3eڿkr[+2>)ꮱsR,Y<n0DiN`X,[ˢ o+h.`\c.mח-啥W;o:t.H=;wibK@noΗEݠ!ręܫ,#Bx$K!/-bJc rFП.Zkl _%dWX}pq:7v{X ]m1 TMlA(P=P bXTZCw97U7YgРl*.;3z&ݴTv5" F?0WY=3`@7wakwF: e'|JC·cA8JݗwmVb'wcP$s5P1Ң&D[kc,žjX``O(viV Y>;rm$F z ^Po N\O[o'OU+xH cBZ ޶=݋t#l嬵l NØP0bk%"‚ D'W#6!1!6"lx; g6nڤ{'!O!t YW /?@a#]!a]&% 'eK@R 4n'#"(s3+I&wfջDi}ےe4Ic[y|)0{Џk6Pr,@ny, f{3ovo}P&_'G}?x='D3I.yf8HÿpĆ@=`Ta%cRRE;XZfy>V=-0_s[D>"<£ ?gi.w=} d鮈 F胜~_"N[>>v@7cӼVBjs?O'mVuCNg= ]z^L; Z}fPn endstream endobj 902 0 obj << /Type /ObjStm /N 100 /First 885 /Length 1858 /Filter /FlateDecode >> stream xڭ[oF)ypH[`(ȃ"smʤ*ɗ~-{]GLb9$WXTge0fVrQU'&(;('U)fPp\s03T"9($U5GF[08S{ p"B̦>+·Ϊ4U҅ʡIG.B(nj8()  9 ʹB9@iEkHPcUވ; *hG4Z"I3QObY-RR>#\' WU Q^,\Qe1b+<ń0Yb 8sBldyS6DSh2JDQE+D'<"Z ,pyU:1Fb&uhkVa7#*~0VWBD-h-9QK%:J(Zu߫R,Vu7 Z#/ E{nڐu ꂛfXuo|TX֢R[CLP;Hu?Ż~NToTݪ]y1?gw 0}>j7StLٟ^_D65jq~`'S$]fݻgq}گ͇; tj.d,W.jR;0zm A6Lg$0Ĉ[FLPag`R%눫zo4idLt'ݫbw/ooηշ]wssbs4An1^^^l}NǛa9O|Ց(Mn/Gv6|{|\Ar֙<יHa6r]|՗NۧRzB Ei,@5ibҌ-Z 벶ɵa-d6Af,A.lbAئaS@&lFKoFknVmChzFO׆f KkWem 6ybF\\ :6IΊMg.]J# m)lhO@T4f=Q8sjitF{9&D[c|dc#6]sXHL9$$_4ޱ$3NIXbS&e|v=cH_{T6fv*7J_jдFMqӯ/PG~YQqo2m|x#ڈ%VkhZrF6Pj"P ڈ꾂.Q &CmX[h /MXW%D(اsx_LW'.f݄wwLdr tw=!3c &1HӁ?mxG$k{WjR%DFnḩa dܜa ݥ%9n!uCڳ֢"#8mh9,RΘ c4DiVK[lfmzv:څ>T~ 0NŦ.?IL;SZHF9}ՙ˺_o]g3cqYzk^T2׾ 8Lղc|_<?t }G$bڰrB6fR MX0I5Tb^T:r<5f߱K^m4HL^ބu&}ßU endstream endobj 1003 0 obj << /Length 3217 /Filter /FlateDecode >> stream x\ݓ۶J۴SxI4wZIL$J!_)N!znbbA/~s(b *ۀ $bD1\#L?|.K%x&S<S>JlͻBs>*M:U_n޼V@i4PR$ U*y$G/ l&ˋ`  Me F (/^`7@bIA CMXWs"%a@Q, W^J$U0ѭ};ɴWR8&-b3k ~~A^jxDOQ[Ԙ8b2$W4PO|I6ƜF4զ)_f4%1h8<,`<Il{&R}:Q9&Q$Mԗ:) =ay`:# ˀsaW˃M̞"BG6a:@xĀENM00VDϒz 6}Ox3{|)aշ 9sFe D79)gN x,kZc hpDf0|=KʙF?w9r$T=O x$UQ&iᝎLxHX'Ldd:BC̞ay_:uznѡofE7%A;Y߭h2pɷv;)cC0 Q>5;׽'_CX-jMv3WKJc`Ӽ(My?&x7KUWި1ᣇ/)'.l lV%-Q)Eh[e1͏?خ긿C--:o hNm9DfE4d!kCK\R>ZB8P~Ge*-'ͷ9" D/pˡ0 j=$%,ErXdIQ7Ӳ©/`U]K7%qNy:!1phbgхзuk6teM6]deD!#"ey,ǠZ\w̴?11Lw*{؛Zъmَ趹$P07ҟmI͇XVJM Ձ}o+%WJ)R2+eٳRej u(a9ID Ny<)_:Ɋ%ȟjL+*+?ޑ0@ G_*̟7|)ZҨ%־]ݶ_ޗm6y-?w;Mvn$(mD/'L<}dbNA/~B}f_LW+L0Q@=cR䕉-Nп Fn͉X”͉SUIjz'M”/:rm"Z?Yۤʜ nQ:7ܛ:#$vaL$G4OwԌ!ƒe^[E1l[ִ}wIZDoazmK{),4nn(䴽&cvLVINme|x5:CAGʑYn. LޮJUub[VlPѐyjӬBcI7)(BXU}gΎ~3B񰘉ߞN/ endstream endobj 1042 0 obj << /Length 3404 /Filter /FlateDecode >> stream x[[o8~ϯ020Dҹi:̴tPm:֮,y$Iv}Ed:XESDG7#:'gd$l$B&(Jt"kƧޛtS^{>ԲX?vuLG1$c,U?QK┓O#f&b4YM7#JxnH0J v>z }tkNi ck37Hhg*HY҅DPkRILfǧԴ,V"V}F avL ?QAkP"oxT`GDa@i ֫@ҸEj@ QZYp _šZX3qŀ#󕬉u&\ RHTLS<\!͑+ugfptS]c7fJ-Tk7zm-s .f\FjYY--O&UU 41Ն^ iIir sYܨM@+}B)D0*Q㷽HZLR ;ffвA›y#(v騑b<*I 3*6| 4F֍Rj$FMB %?O2+wo*u»r\-i!V s%3~nӗRQiH3kn|ODD|XEPlT-;jtX*Yw!_>wW]w]וzַpsCk>%WHgcg|@*9uj>)wJY@f8PZ"vХ)OYE;Sh'h%Q "A>4H~= )Un3vjg?.}۰fa/vQ[E6O,;JyUO,wxQmߞ[!~a};zM# =FCY=pMV~eKBcOϫ,♸*s'ASpxubvĔx|bJ(W󗴞3ֳxz:s yq'NR{g\O}`fP{fq=yzww ?yvPt1?hGcx?P3H]>~<,OcF#nk ) G1v$˴7Y!wӫ{jlm{_S?O@{8=O̓PzRR-Ü|3L>z4f0>M} `fq ۬|;+b^t#˴{4>Za~vS?~ ^t,w?[fuwgO8ZV4wZ%{D?~^V:ݒ-E{L4vfvƌp8nߞ__]W{>&5ujTlpCK4'C^>F$a{1 ZWr!uknl{V{gE[YI}) _O10%W 6hfݏ/u3SP Toa.̬La fz@e4=H! `Jq"T&b0YǬBKݨW۽]F 05[  / 05,]"_=infOeA\*A6$!ZA۟~x jՖBbFԠ\$X;w/>xp%F >oҪ1eăטH!50pDT,-:)j 5j |GK 3ef^%jV j?^C0c帲.]B 8p NV2֚[,'vpʪ} ,xH>sCN*SCtkS<&\Ha $S4 $fk'w{s,2@Rw^Bv)X zà_˺rCJ!rwcHuR I܆J_gj֊rkzHAAU!:ĀP2%(fY}}1 |8i7bgFz^ ER::Agzӥ KCM۸}0;z7N9˄#ת)o @hq..H\`n <څؗsO-HFdxh=k;rh+  6Շv}m(^K0E!lG$^UJlyU\hJԫ*oXRUY6z033QMbcm4T{Pȵfԅ~?@-m+ٔ*H-wP/&{ۍ }mv`sTt<3=]2h5>e^ gFUU}U8duf':Xxx -vOjۅ1S:V92+)"r}$S~0>W6|p̮Gt( BV/5JʵWD(I>*)P}J 1tʿ 4 endstream endobj 1063 0 obj << /Length 3395 /Filter /FlateDecode >> stream x\ms6_闣ob z]8(ܤZ,N)R%8_XMm94Bb/r'ώ> I &XI3|y$ҁD^HD*9 #2)Ggӣߏ1?'sń3/&Wzj \@;97FbB?,%yf;?/2"'_]F2csKSTi~Ib:p6ryNY~jv1ϕyZY]bCT(UӋt,R^V/O^ƼmV*[e;><_l-C;T3ɉ1}RxS,FD,E+W/H[d`7i;=r+zۍK= `|z$3{1F_Œ\Y6K3?כ[jw#,ns.f 1o 2qZ՜z~>7`z 1?iA.r*$#Y MALeVh]i'y:U3k'ȔiYR'h}ofM>͈G, j:RU}P刑EQ RndBk ;邞I">|{$|! kg8w?қ`) ,y4B IWL |Kl7SSz @cnxwo^Ǯ35$W'7/ot#qF 1R;eXcD)$w7 4lb̖EejLfjDE1gQa䏠0(GŹ->k}6e:UgE!CE^3X ֵxlLM3V.&&› xjՒ^z\fŸ|dĸy`$ 8Jf4*[SKCiD`.<:|HY/ab0Dx𱁆d@,PƣGƱs1F@wb{gɠ؂X$ Lpd#I ZoZس=7 ;>Zs^W=/@:zR"C˦ӹQ$ne:KMBFdU/7=FLQ<a ǓPZۨ'XɁsJ lGFF`4q!=V3*&~kF>4ySR؏/Բ~wQIb(>ČLz8JM%?Q9dsaPBᘷ.·!GDel6(os^%(dH%\CW^гKcGJpГ\cz#8Eg1UhBMC !)fūt Sz2%fW-MD35Jqa:nV >dRA0۪@f.-%Ă@'uY€ zQd$!MLh|q!7 L>5vm$;˒ > y˹\.+ؑ֏\yH ]aBATC!W't.UR1ݼ;Fvșui \] Z-{T|ѫR/ٕ1pl($c WӦg=.ZcY݀!e tLBt1`Nm(U,e1Oy? hş20;}˖EҀV|T@2Iz>?[awL< 0 hr/^)*{vU8&9cv4oQ@" \*w3n͋"/!z#Abkr9cϲq\`HkNmWU/81${J#Us# Q-ZQ cl6OE :8iC0goͤB`sn"Plu EPh7LW;`PUV*'EaS;v{b _q+ģyHԬ8L( O"aH[:” 1&ywb` Ŭf_ױh=>U׵26L-`SzPj4E vQyBa.^5X#5e+2/V PyKM!C!#-#&!ormZn.*A` enЖT  U,d LY7T[`Y0݊VC h|SR8+L@S& kq3.3Snio ]@nV0)ƒL 7k[ru˖@FRtBG5d!&nAY*} ',Ej7gY mKn7W]Ч؍홽 obscB$OH|0U_.6[CveANQCeNQd~ YYP8B -Жٽbsa,6urU\I{^Q'PȳgΧp„b`3 o/^ˆ^9>t0[*֖h6YW?G~v/KȎI=ؾ}qξ-ۖ7l-gt3ӇjwER jr奡{uB&=#"KgC e|Co9*օFnwǽ?|R2fs9Y# eB~o0lG:ߚtLB٢o)[f˄ˢM׏t~E|#888=Ȑ%&2LBu'}w;$ogb F_gZIq|6s|iNvÎͽG/r~g"< ȽO'tCz endstream endobj 1089 0 obj << /Length 3046 /Filter /FlateDecode >> stream x]mo_AܗfߗlEs$sU)(DD:QwEH/}M>;;;3\cޫg^ P +OH$>KIOgTɛpJIhϢS!&Modć TF''g~Ȅ؜}%|Èw[x`DaB_W )=}$>ExD]䭎"ķK)%Q@wa 39̮ӛҕ$eƉKh$>iK7?Xtn:~~q~yJ+7;ZWa0G\0 ϕ. u|G ; Aw%e,mPPkpVaca`5wvc{{1zh mn.lV>@b V7xQW xy-g6ڭfޜ?Ꮟmv?T>x׻>~:*f:#PĘ2p9.1 u XD}н|deQkQOdU}X;Ɠ+(;dk*x* ^)m_ );s$yhRshS5 f.:9A#s,sT~KJLXKٮEF!V U]_:h)VU>B5=2{8U&qNNt]2^; { 6< )i=ga02oN>It U{݇. ^O`k;YD=#L288lTߑSQL*2/88j;zw4;vU0oq8K`OȤDxFuxGݍ%&|Uu,lk%I{A ]eEs;7]aw0ă1zi[6Nܻmx޾i? *u|G : -s#C5aތ je]WwKF:#AEP6CUyPq?Ť۫(^v=y_89\hl;UܑI:ǜz4*q?H OW18o;wԝu|Wf5>hګ;jUأ[N_ٓy .X8a}GibGeP6 IqTB> (ճpw\_- /͑T~T8e4Le2ZU iӂ9놟IQ'7c Zl0luUJH@Vο2EY ե*%V)y2Yjje[?,ufrߔ{?v$?ӥA0^Fxi.V'hF|S" 8]CZB9xѬ@ ;HE'`g/XGuίD:j'in\tv.'#σ:>>EXPwdðŧ_+ k9 "h<;Uax2M@x,# |ǩ*0Sv\!_ <-x(8 =?I.J:-Tkf6ޥ=6ꚇGpߑã kA;U+ WaPF(8N6p62ę<b8`f%&WjN78Oqܑ?y-Wv -iє ;- I5שݸv[1їmOsN3xqv_LV|[aS7N 7y*0<R6iRA m gGM$l&Lk_w3G*`CaB |Qp{H]:aӡo5RJDUOqa0X~X (_6YxK׷Em'0:c )Sʜ+q "I@ %2w8=ml?Lci+F1.̯o0i%w-"j]EwWF[:rUnU[F{u518 Ah53ԎiXó^:J̕E_Tj OEJZ=4cXN^ʜQW:KUqZ z2^&p,"MÎMU/ -CaX${Η Q tx}[vz d*!֙ endstream endobj 999 0 obj << /Type /ObjStm /N 100 /First 968 /Length 1464 /Filter /FlateDecode >> stream xXIkGϯcrH]U0xAI cDe&f&hU,)Y2yzU]{Ϝ=DړHs13QDpݢ dBJEKMXj]a1RٶSI.4Nfq"4|Tc0&Kl (Ib\1PcPctL82 2xA0hv|hͰmA(A1a" %v `n,f7ؕNa3 d1im{;6 ZbnOV8O1c1c11Șݱ[]'͡*р5N}3.vUlo5Tn4-]`_)ÅBdh f',*AH]֐: 8V1A"qzDnCI˥yPvHº%q9 c dL +eSD3d 9!Nɭ*M>WEЉk*^Y;F;wm?SS3C(^4p]3h{v}") #ed |f̒G̳)xzd`/_c&C@53?e+أl?g_,5HV#dnQ5ji\l5=FQ*&F.%[ۘ!HOArY3&[44Zܢs͝>AE} 4DB 4J6,ȵU5[I}1rz'hZ) ^ jsAb̃EzOn&d-[ᶺz bܵ~-"m&ctgXu"bi{Z~ESϾrC"#oB:[ٞi!NGe'K0f{ȨxLɨbQH`Q:DFڃ>B* ^\0<7۝ܟ~\{ì!g>}zcTL Px vԠB| )uDx#(ھGЯҊ~05cP[Tkzۼd \iYĢ>JFF ȌXփ֐QnLjTwFl*GA+3&/F&CvȄz%)!OSE%J%C$chf iAe-A`t?ۏn̯[iDu>m}u%fHe[\9(Hy/$ endstream endobj 1138 0 obj << /Length 3232 /Filter /FlateDecode >> stream xڵ[[~_x5ëDl2δ:@M2=#Ԗ\IYM73vyMṓ3JXVOh;&u,q.BO },r59#sB HB_r#͍HNif ;gTG4e6~z5bzSʛ'i٥$2N٥*e 1d:ܒ#ˆ8m2Um$i:5^})yC+Y`.$iaV90#pep1=փh ŘÌ@̨>ªd; .ջD}%OY?W!"X;q-3hK\01K [L^{/z7;OLp`L/ԚKԽszCnROh .19pBzl PM.% -a:@˒l?J8LF*N+jp bbpII%V:$2_56?]E]<4T- p ubbOQD+Tt{pRB;/f!t\ETsǯf>۟緿^x!htbbk(97Ro8컔أKX䎞ʻƻRb&V< 菓6I1o8̻Tkr.vr[Ujs#N<2BNn?{aWӛͥ}*ϾCݨPq=y7m&#vع邞mtn5΄[߻Q!nFB8s7IE="DJj#$A²jAVie=Yߴctv#m2~|63L1&ڰ5KˀHF>0&>S7c 6&Sӣي_> *>$hv&֗lZOHk"5tT2$X4꽅C )3c+ s.3D 9,nnMi1zP$mSv4 f7n4`$ic?]g1Ze^at{IK_wTg|=J! !&!@w$_{pt" *7& gtHD1X"{Cy|%$:lbk>ij#ׁ&$D?ʽiD1N=V-hl;{A>eSR`N0 D8/C {rGP_;Q<*Pp4*![P4U>z#65C#<(ߤ֝CM-o^v^HЀwFSVw3ip endstream endobj 1159 0 obj << /Length 2779 /Filter /FlateDecode >> stream xڥZ[s~eB\yNS٭d+A,b-uK\;>uΝ;~Y=dNeq;c/ΘdLg]:޾T<:_mr7ǑRt?8w%_RX~2DLK>2˓}Wήgwa3"N;0(K{rBCv>̧ユMN/pK&[Χ}E|}6w{ՖxЉ}<<s x#Kݶ 6{ C?xbxӖb-dDŽCJzH #AG fJj!YMcCEQӡiEp0B˶8D#-`K8<9\w7ɼ^qނp${YU]WzZOk런kmnۜV[xaRXRXF_lQ^s >(EW~|״y-ۅ*4FQUWW!4.?Ձݵ/Tl񠒥PL*;( AxI: BfǼXGۼ[ArA 1TmNv!C'|l B7bW1aqlX'@%ٺ4y0*஘[7_tbDY5U:2`9 O2U~8mϤbMrf࿽W-aka*M&>[ԍiJgG{+JPГN`* 8YccDZ242nMjK"¤zeMvO{E}7:tZٛ=d# ]SkCޔ&Ds:uÝ&E7<@6OnYv!S/iߧ5pQxtQKlwU r 5t""hZ $v*gSB љ0 0^^|D<ˀ&~:='36wڻaԃk:ndk!qN:<:Հ4[ 7=pFGC3#:qsWTN\0UQ<1=%(@㣜[>)wY`)u[|= Mioap:6p Ȥ.e2}/c {?o.ܼ?//}r}TdzċR>঱ƒ΄h el¬2rsvy"r71$Ŷ =de*p]}1-Kwa& "[US 0&%{Uxtx2&0n,,4~*4x0D Ra۫ū/7o@HgJ^> Ҹ[I4X(زdqJY۔рձfe!CfF)z1AYdw/}r Q55̮F =uI aT72H#N-Ų+\a\iK!P@\/ċ# h+7m?N*%U+.iXӴNPhw(@T*W kp*.S C]xɜ ۂ':7C?:@8dO P\_'G f"Z}[>Fvx 0;k9d=@q\bc1/)HRɟI]F" by# D, 2 61a/G3JENlLiʩ(}DƻO`|e?Z?w*Jb?0tR*1&O:fq%qcWs"`t ŐGjR4dS85<Q3-04p4H<^8 wj/_`;48--]!QnUZfouפ^g2EVꍵh5H2PVU5N4WhMK]՜QaIae72 W ˉt9IuV,PLsݚC t곚"xTcoI"tLR(4pN K3 e&R3dXn¬j#ۡ_IUcB5DPIaXoP.$&Dub1\f0P-ޥOSo8 §iTvzy9ZjR" umwi_(2?'b7 endstream endobj 1178 0 obj << /Length 3150 /Filter /FlateDecode >> stream xZms6_ɗf";c;=MnN&!P ,c}g]3:I6HlE1`dٲ}zc]vQ}}qc+ͣkc_b)0u')Kaa=kI?A>9Xq:cA4˷'FgF@:4eع#FwtJ.g_R]9;k5Ipu 1BUyDweVVo{n'J6؀9M[x^UY5J#k3QO6ʧ;sᛟDvqOn1fZ6Q₺8-8P;ހ8#EBPÆM>gypJJƴTs5w=rU%͚Zc{öNnP~xe׊j5_stkб9GxJlu5r;P0o7+)?c eT6} 5hWJ}u6\/nDYϰAۧz) G/-$bO{uN\ٚϒQΆZ¼XR APZ/Ù ӢKG{S(&, y5teW,sW#x8dj Jfta a%Ϳ{"#`lRۊ\`{fr&@Omo m%rMoZQZlvr<}YG3BzO7o-ϰE>w8iT'WFu gUND.ka>QkF #Hyͺ.Vh_7s\ʺ}c N;3v6F 5 a(D,HzathdBǏEG Y.A5;; Z!z::ۃqn%< /۶7a~y/P I!庖 pO8~ e},eV)(LBfQHyyϒ&~pqW60Pu]S^t &a #.YF_lR#bv"ܰ`1pF6k"J Ahe܁zX@yCTo #_5,#4K|9v$N {R~׻0bڑ!9[>Ժ&$ Tg A"g" {RxP'➫hN&h9dM['uh'VCy+ܚ4A*DGde91MM cj- ]V^ZanU>(HGc33[ETPi],}s*'beNySjJ|dpALH<"g3$rb-OA $M,,bhZse=,YBZA@ \C- ϟeF'VtYJ:Y'&ӂ(L^3`X^8[ASd]s"3AI$֑%FKTڈ?^dh= -Q3Kb$VmHH$  BWe`Lxu3&ޫ~mʆ>T7|,.=TP)B.#2(shlR]^Oh 0t~~W{Y֝h.s~,ou]B/'cCƥyQehi b RSt7m޹*۶Ey5dd`G y ˷6}xZv=(>rr?̨ "gBTst jM% HhBCǤ u+~<"$FBد[IKWq2:#qƁoA*.YY0RQ72ʘ­WlO| [HӃmaDbao! w9Z^KCHtJ3E8x3JxkSH7`\Mo[XIM`U*7 Ƕ@~:_0h9TZSuڳF\T(ai4x(0ꌣh`!!V [Գ>JЎ>ISQ!ty .?1$( d),u m,IhY嶑ƹp)]QRXΧ0P:ފ]߽[}], IxWC#+Ueɪ!$01 )SuxL?4[i^.鄆.1.!2a@.^5`ўy>UwS'DN ;سs^Ҵm ͋z: c};M P GJ]p9߯ߝ_}>`,P`KH#ZmDxc hb [(}!k +%*}ٔ"Vj <ގ׎ ~Ҋpݝ*,@rv~> stream xYYs8~`eB<͉3'c˻J\KSh}(5b Fږ2,?4Bz31/¯on~rv%l]͸eϸkF4rU◙%?I,g,>? ,#=[;gjWt?0ӏp6̱  Xna1|lX Am1.'2B#5ZƜ;Lx.sw .ZQ[=vo_t|-x€ق7 Rd݅`r$`߉mAE<(]̳WlSYi&ͅmeYvhl9f1A%LjL0kї+*lN>MZٟ%p'dqQKV/Q)kKTYICQ\X.ହ 8u5Bp3*ʖG 8;V sVF#^ݓ| ĕm.Wm]tN~KGy`o_ifly$2[`5) .L6U, Pp4BN776lPqڀG;%Η5wV3:ϭLHJf=h ~6)vwNlU> ^7cu4y%>R45..Klԩk2q '9{ߢ؈e%i9vـ9X iFQr{+ >KJPr:"卢tUu 1pYD7qr% *:!͟] !Uo 9 CPODX3Kxquq~}5!ڱ9-GXӓ_^7)> ;GN`LzܸUI^Sgmo:j+dMN  t`%x #%Y%-c_A_j^|q>$pͪI{gy`P%So$wxy,KS'3r3ZRO19Opnaӳ~͞oښ'= + nL砧5D/ZS GU) ]F֒B/Ec60<|b~V% ً6| <(]NORVǏ& (g`*2 Nb^_ukwΆr8۹Gū#L02R6H2(N^{)a%6rYer/Gy X^qM&C1.sP)@tc]'ZjUI<(h LbRBNq<_JL`u=+i3d /01*iVʬ>v/^ݛNS93QQ',/ZԂ`uoP\qg3 A'TxR"Oae՛ʅ<1T$V[vEHH\orjF_"=r8OmQ]]ondEy5QcDp CO?5]Uۥܱh2R!3Q[ ŐV)Ptq$yDTGN(ae^qN#\R*aV$7`ch,Ƃ)B']=D-L[  :4{~EEZ˪P5PAzX (7Tx_O8G= ^ p]R щb14q! U̷^ PS5 ^>B׊I!>2#M" oKiEi:q.gPAe\RnD7ԡT+fjGf͉If_e>DAfI0Mr jDRpF)Ӄ "r%غҚp'# ^<}4٢{$}k,~"oR0BE?Bi2XTiTv$݀d5YOyga.v Gt6BgLnJչ>EsmJn@p``_^ᗞ]N`\_j <>K#RKCvN ){;fc5[[0y@ #a[-Q5 ۴ٲ^p=bqjo8`&) g4~GxZxSJ-?&zEśc[պ:|qb&ogd 8]JA: ի=[:2MB@^A H endstream endobj 1132 0 obj << /Type /ObjStm /N 100 /First 961 /Length 1874 /Filter /FlateDecode >> stream xڽY[o\~ׯ?p@I$U FHG+Wͬ|;JhI`Ιùߨr5o ݟ[\ !+rJ ȁc(pv ǔPPcpnw ŽiAczr Uɀ4;Vphx3H%PCo; |:g2A\ꑲ=X8qJ`@w>;*8(q!8j?lVk&8!hM{dAJ-y,ǀ 8 9N`SQǁdTC /8bfZ Ӵ9 \ip8ā-F b'jF B2lR y𨈥Cg Pp 5@EV~ŵ)z$C{D#>W$)`)z)ZBI!E)^UUHR8j  eTɰAA7Mp&SĴzFf|(>1"!w+NALի_ĩE!b,5Oc-G SD KT 'Ejc1FL52x8K,iΩDCĨ:F( )"k5 pVY[abA !2tGmĹF椱AoSGMbQb-ȠZb t02K cČr$يs %# NͪѠO|P>f a:B;Gh~Wa|+Wh[q~(LWov.V`Vk5tzs=]O'͛plL@5c/dC>韬zL;?|f{:bq tV4U)vhZj z{nol/^b,y?匠PRpG}- qmѥaXTc^ꂋĂ"Ԍ cem##.m#fZEbHb.bn]E>@nĵb~ C?MI*4h e41Ŭw_5k:%V+ۭ􏫙ݫYq|3Dz8o ')b- Jok/x|xvGyP1`s*?QoR;wGWڻWv%X90}^ay ׷bT endstream endobj 1212 0 obj << /Length 2125 /Filter /FlateDecode >> stream xY[o8~ϯed`RHIInƝ6.@;%(=dKQ;C\M/ ^==;M #d2/!xdM`^(u32A7zD'BvPo4Ht2F)r ˓4|–GAg6r,6G~AsoJ, nM "J^G =h,Y""Y,1 >M#Jib_&An}jA'"M:FB) ͲrVWmT{<[*(D"Az*ovʟCn0a4ϵk,,+\^N7+kDw.UokըƟw|Ӟ[ظ5.6yZ6z5z-[ioXvx k,`%5c76޹wp;Fej`>SnC(PymV,prf㒲,xNXF=(f[WkW;S 14ˑ;2͋xӣ,~ܮ,cNn}4@>6`ʂ$KLI*vU}ʜC F;lT sUDpavJY)?7]48Y >jw7uPUY@ "! aS'a0IP@(%T$NUހ1֪\KG uMJU<>eajLiX6W68coj;iW1ECV 7 Yj'<6*ZccSk^:əAS-uA&$&2"zV`<sBn5U[s9sgΤ3d(µ882ϼoҦ+LKXe֓Єnw?PVpA$"Mt0d2|ĂŔٙ_^޾<9?_ğ`~C ?'bW}NjQUbnw`R;(!q)*q0ԍMFp}ZZ@3gc oM8ߞ@T….IYwB˻`8ɖJ|/ڗJ|9:,=v=p=Ex.A2<y&ᛉ1aW9 "o0MCZ a~"[`z^g4u)$$ˢG܍$iHW!ΡUWUtIÕcuqP9|=Ρqv(IvV-;]_3ծF'hgrRzp6MX+q6qb6S?1f~N;ZV-n,V9jڿnAB^_͈kT⧑"GJA^1%/ЪoM2i#2cv9[ TбD,3#hl_J77H{m4G2!^Dr: ãunZ{Vʓn;{Lּox4Oh}g_5=ȏ endstream endobj 1208 0 obj << /Type /ObjStm /N 100 /First 974 /Length 1758 /Filter /FlateDecode >> stream xڽWYoG~_O?+2 s8p|'Ȋa:;U.`gcjJhД(L$I` ZDҙe61EBې§Kv 52>~4*dF)}fYafV2F (`&=) C6`"q&`…?4}M`*f$DZk,`ZYʁ̂SA9fAܥ "1 >f, B6a~ |!6f2K Rؤ( (#"mY{1@yf7c<KjLL(bS0츤 oQ.=yb+6 + `a@lKljeH)X1ƒ9K,VRd]0e]ũ싋B`7(Trp>(X1@3O T9H3#)XCđ8a9P@!8q?kzy)X#B높 d{.x=Uחn2ٻ!ldzL*t:~WO1v ޢ(R(|PxR::mMQDTO~r;$I3_n;)6 ,!>D67j+]Ua~2]3TôoU7Wɬ?8n=WtD";Nfp@eCwʼnƼ U:hr%:Sxd0iUߐhOR^y3L슓!D{!A01D$VdLFܙ/v<0et O'>4|`T|)-^!Gǻw5a!dee WXo6HZХ~>zYMp-+\"0yu0 OR ?D4|Ta-4EÒ}B \4Y4}aTO[{:Ob!b$Ϗv^zLg˄1=֎"a,NyD %^ekzd$uc3Ъ0@楘W/vs_/ޜՃhңZf۬\!oO^A&?iOgӶ;- -BI''~WXˣz\@5K2&~ sÝz8+RHse?]ͦo޸'i5K6is ! 7t|LN6ss338^:}ZgiׯVS={Kě'ȤW+,|h @scs,zuS"(̳nhki%g܉v/ޜhz\ ֶ endstream endobj 1296 0 obj << /Type /ObjStm /N 100 /First 967 /Length 1293 /Filter /FlateDecode >> stream xڥV]oX}Wxi[BH.. W&V;_g픲vUi&=gΌ-dj("!ӄtNZ*$"I`I8LBR$zC_QT†&eR6,41in>,#2dt0Ґ %ˑ*MR,Pb IdB)0ҘTrL)j9p (4)j@l_ wSbXaPF*fReR'0LI9*#6ނņbPDG1wIň b&U1P,d82W4I9GRܛR $@F1A#VHA(al*I J5p"T7 ?)u_)9 I,K ,:BX"(JԆhX E D`4\FEi(RZ Ē[Mʘ8,c ɘuςN%$#Z̓|%8Uˊ^,Y~?yULszɳ%kyG7u^n}o=.NOg[KWD^n-KWrmK76U*}]G}7Oƻ.-MYn~^ֻYѺf*o\i"̧pzl΃ԟ@bKQ՛٪m|U1;2CC۳nBTmw{<37ʣ\,/ܚ/@m=jըC|%5;ڕySպH+/M[g|>/n26g7.%{q>X;'`OiQZ$aՄe1+qY;|9Dǭn^Wn m/;ʻ7mmܱ8ܚz6w/>9|ݥ]=VY3ZX`bf`yQ,; 97yоg$f{To!f)f(k螠c*_Aœ6Q#S-4=ŧS°:WwnsTa/yk)e/=7e䴺}zdo0=>%ԏ;{~u~eyffD8<ԧ?NuR1])V`^c_%_ -m=8> stream xڍwT}?RP i7FtwH66K.%U[JIARiPQ:"vh}[q(#DK$~X 76sMA@o1 /q`KoK\DܯUgHNIJ!  R(I}u{N[O"YSIr#Zӄ ^7uB Zʋ3ohk.K0QbuDAu]C@W|wo4}ǫ@߸Cq=j̧\S乑ռ|?`yik_bMgDhg!=zQ%0EV׭5oOCh}U',D?9>4n`W0~igOPƿʓ2tqeMZYI:7*N n H-5$Z|=I(d C4aj7=&XM5,"Z徰[`*{Ahr2a4Jȕt528Y1Ŏ]XbG;LoPM$ i:&P<}E|II' „w wePxxcO%[ !{˃KJ~—^|h(Iy~!'m˅b@*[O?en;ng_j:|Sa7ϟ;zwv:ҥڬ5] apb>ϦVW#Z j~ߨ:$]jR]v[T| miyoq)u'hf rIViIrbRTxxq9tI׶Wv1p*Ror\BV? PsXG\sl} T ; ]GMIs-j*zѨLd- ﵪlL^D ,-1Sfܤʝ,kӁjd$NH-y2ހ>$ ^9o]@ o42թJ-ctxCj徹x8Z7cmCvkQ#(w+D>amϻˬ܈c>P&GЏ_r_D oz-n-SP j݉)"dh!̝q=xfd Vu_ZsɯrY2`ٓx1B)q3W5},a4fl_`E3`U4tA*-_ɏB IԦFҐÎTHsfb\EKy$w/.>@X!(YG1Nf&wcy:VtdKU}j>Ҽxn"D~DE=<9w{wa2l,U6JLDZbH , '_EӖkW/A(9SYO^ӉpSd#AIz'Òx ]_Bb7Z|sR„d鸎ӊUeK阸g) 2on,cO;¸մԵbq$a9)ƭc%4}%$D{iÎ?nN bL[NIno=ʩO: 9Sd:vy%?GVNgN!c =^lh x$*'Vz*LVrRyjPq 9yv}~@hd'z]{M=) >ޘW$3WķMD޵eCvWkK]RY|V޷mic6SYiA}+ACi9|"LIb {})͟r(=״rEUR@s@_ڔGZ\dDiHܭzpvjV#p$faVee!a2Nw}v47E@[ךW;.DQ<(y6]clV z1% 5NKuf!g(@maXFV"e}O <0/i*U׷wO@t"jngZ1$sWg)Jpsˈ 7n4[k_ )}Rb|se jiH܁ꁷH\(~hݨ.eM. qC6Xʏy``T}9Wa(!Rvy]fޜ}<6=1WъGK2TPV<oCnR> Q㛰_byOOKMǸ7kbh0mf^}ؖ B>}2 s5D?jVF\҄4SM8ujjTw]U̻<_ft+Z*ZI/bbcLKh-UվlrH)LRY]fu= 8^džx_QS*kLTqh)W'oTʖ~8"sM{kfGOcM)ًꯟ}uִۃpJqnؚ_.H\V̳5~唞$F15hOcnER+b&%V\Z] cdX:%]s N7.? * MJ>q=]\JDe ^~ls&}zFWw#knIgٍȏJ!גkԨGnH^xlKΑc^.I\u{y;&?VBY(MӛNR߰={;CN*nC{- ^"w)`آS_0@M?^-FG?__<8S9/ܸu4Vbȇڣ=Y]tm9<lqݘp~.T^`oƷ]lK K鍋aiV)xؽ|qZ{-B R\ 2͋3NT{zkT i$/k̎1Oq\]knt4BtvZ]ū7+{I@^.QmM%lZ,Y7Ȥs F5- P)UNrgLܝw!?}Q_1Ŵrm*w@כ󪏗Jx;t?SmU ]ڙEߍI\o+hYQ2 %{)%ogMz-nW $uܫ{ (ti& Q}9R]q]{9OV)堇I9 9ႜ35'H4yC+#\w*-RFA_'_e[ =N݁}L ry[HQ'{W4vRDM^Ph)@w"R6lxk9Tٯ5ޖVH.s3KRL.J+(ZVxCnp8zݢF<4 rOm?OB":Y=J+x/LGRR%DXiiwnnj 0= A窦\$S,GZ+\-7=rݜ 4;™?44|%FwJғ-; ȋVж5Lm7?+2aկQO.R+{]7_eRK-qY`(%N{XmLJ,8V4\Ce/$E+\ӣ#ީ6%J' 1{R烠@Nް"Q3 ®v lt%{f=ǣ%RBM#r(1fUm+o5+<lL [E/f 40' s߫V"At3-<V^6:kp띎k:mbBh2y̑Wa/#׷oѥk~;˓eƖ1PwhjLz :c-xخ25Us9.FˢLRI$xZ B꜒f95*qÊXFKkc39exW3zeKWOx[?ېfg endstream endobj 1514 0 obj << /Length1 1406 /Length2 5979 /Length3 0 /Length 6938 /Filter /FlateDecode >> stream xڍxTS6ҤW{I B$t^7A)QtA4i_xZ߷~gywڜl&H'z&V" D89Mah8o;9 C"dqDcljh Px1@DBFDRDA 鿁H/@ wSsqEc} yiiIဲ vDzhW" 0)x\hOaa___!GEW]c q~Q=  q0_$08 A0!gh_`ݿDDOD0`G0!\(  4t~h G!1>0{뎀a DP0/¿`Yᬊ (_SyAs\w C8Cp6CyC`0&\ h@$-!!.@?f @O'ЀàQ !HDpр wvka'~}ugQ37w5ULM PSE J @DDD_'7VCG؟GFm HEsz ?c >goy~+*ޑ7;z={1L⿡Z o63#En ne4`~gCn"Q_L_>1e]d:t5?F8(UgoB$`8PѯƊ˜(/Er#= 3uC ~0,,^Wps oe;.j VQ.~='7GWEO1}URXW9,vdp|N9Ո׆;b2JWlҫnԖe1 XqG'Qzd{jM\a?%t&_IyUC:'Tw GӆiR۪-`5鈓W#1Hfˮܽo(% :o=Ʌ3!EݚJbdŦoMu dCgI.J~$*) 4yxMS2y oRE t]*oZR.U%疋T~SU޿[Musrc5<.g:dk.$3/NqF4e#5-ilu;OZ z_C[N؜n@I=%-D/K:8Ir~zalqTӖ<}+c=Ϊn.SB/O&c$@yR/A$t|Wd-Dz< 1h[AVSm0<Ħ}#̳[7SGX=sj٣_65*8R~4- 'kl˓?HTG#̑| ?)6ff7ȏS\^%3nxj|n+=錒@h>Y;%m}nh]ݘ㻐KU=>?dP3^5b">,2y¦Mfz'{|s>o EPdJivbqRhߨ<&̀7$9q40R"811t'0fҎB݉FI`NΚJ\ŋB([y~S%[0TzAwk: (͙i >c#$cȆ]dG[\ GR6ȗ)kkvl$ث a]&ŲۙvX&qDG9*K%>u'9 QSgUTxc!SJE3Yra׮㞈$UX]9Qߪw C=U'ۊGNbxz+DOX^ <׍)&>Ƴ6/ΗeY=>,IZi joׁt7}ICHyNAel YceƆz;ܕ~k~̙oQ(0%)8f1ahT%C.ċDqz6>OWMλB .3:ʊQNrTjQ]1rO4LVM*Kɜ7XRDXED[4[טV nqo\_1Y$a+_^}J'ہyD g^}MoN$ؿ*(=yч3" fpқg 8iz{o2p. M;>[H mER^$dml~Y׬K@-krWyzne6D~ز0gve ٷUgK |ʼz/ycg@;-KJzB3lo2GRYWSa9糗8Bqb*WRkPg ز.3{,O:1M9C;W÷fӋd`}Zyqp:qqִ3/'eIw|;5XM J]h/e<[x#Sf Â.&bmrT$ol1x* rP:K9<yЎF%#p($ m- !ve!5 }٘<:lRoX i<"VfDӣb~rPZ6M聭"P]{ΞL\LevJF'ծ{&GXW^,OnƏg)M<yjX`dHd:# /}>w67|O; ;YY;!\6~uJwc9pO^B讱vU oK(-,< v)si{<,|XG&ˈ8'ywr$e]B僟Mepgڶ1tzd!-&%Ze- rg*쒘R/JXdk7/ɭ>d=J7ZI#y|in))\i==*7?K|AO-0%=x&O!m⮳V!twiO/X!gPVja/.zj" Xp}z7ݓ )ޏ|{\ކYzzD͍$}ZFTW6ZtQށsO*`~EaX.e$P[cKF1gbf0$uU)Pc nq^VRՅ=jY쀹6Wά[%)]-yq-F/YShnLgi~uɆC <ۣ_ ;&є93?ŽƳ::aQ7,PDޜw3$^;zž%]% a|` DV(٠*][ oDAݟExoX9^8 J m5BB _ N7_c!亁c_tCxӱ[c+*7u>5ȏK []o3KGR)^v y/"z.CTߑH84+9@>yr"i^V]^ ehh؆fF7޵;Ɋw).N#gV],ZToLPNCb}#fؾkō6kɛQXS&֔ɤ8wnr??x竿Y87_1܈Sų,MmN}sCUd1sDϪ|@9BPᙖغR-As#[_A_n S,S'%ǩ~پ˙4ZsgPOWju% yκXgjóg|hRv6J~f̞^؉A<Ѻ3U5|1<%_[hyhno;,vdb>Cky>uL-|.6M[/FA 艹n] H.ϊ?t6V3"B0vC"n_6 ߯dYr*NUp6.JU1o>g:.uyoȞr}0Dæ$ԔHniMmsƕMd~XcN|U\&S:L: VOdg謹rt7ӫ]R/#lxHc?'s>ﶰ‹jD4szT7uC[[NFNMEn@ =C=r}N!*[gsi y=:$/`RiѩVaطz,E\}Jg]zΨ\y7??AiW~O2fN]eqĈF|pU[GP>3F`J:X)4m##~ W<^m1/LT; H\gКL-\Xj )O~y=3oT8?/M([I[ß~ XOyTp:#&_.,fv?m 56a D旽nWV(qw%Mt^\['cqlUEw#6H;D4 WWk -{eI { v*r^tD9KGJ+S-:1xjt eWlbt>5Qkq mTIDtI٥'3I˻Q[ҼJЀV_u8 Y#I9~ ൗ5򢎤WAv endstream endobj 1516 0 obj << /Length1 1606 /Length2 11505 /Length3 0 /Length 12329 /Filter /FlateDecode >> stream xڭveTݒ5ܥn5x5%xpwwOXV(rǭv։X&ny ?J>GzB ;*?6j kY!qMz`Q״/Oߚ쯑}oG4bQݡZN(}e !f4j 1lb0w&X|dcZ, e!h,k/'}.˩ Dy(/шH2?hKGCuwKIXۨz A)|qbeE ?cO+Ahvǐ?cZqBh72*>7~˨IEmŀSG˲^ȘE;28[ZjQ_p0pC]2M50}# ;,LTz~MATZq0 '5e G9>1h_v`]XtENOil o$ݠd9sK'yUDsl&{el~}WwDHp$H3]!g(ylPx;-3]J@%)n8V\˷HK=ޜ[͸]el-b(}|UFpW%HZׯ̈́nܱۢ Yn>z&ihbrJy#{H,(aYnd6 -鴧o﨓D+`v]~{?پ\*. x:!I3@Si5"Ɓ}qllXd;oq -bU`)8N}6l95 _qd&sXyG[ız%E1"U@rðh-j6N uR1Q w/́G=*jhJ ց5Az!+JWFBN\ulP-kx*#flyL8]h?WW8 , EA,6jF)y Ig8FXט؏աWş1UaW<SЇ6qpٌ\ܹiX35Iz ?16tb )ӦEEt51`pӟ` ϡ@+,[} yP'DY`w[9tjFζ6MpNZ/pa'&dn '`R4²AZm3p(yY4xUK>2u -*>Aop"2ѷ_3OB pNxzOE>@y3ԎE@ *D G_07u\>3,*S#42SdA$ɼ 3ϋ Q=1L=p}wh/H)ߝr}.uJa5G"xF2b>y!z|9>x%N'dqkjxD:B{{P6K"ӊYR"b&Ag&a D oB|Q^ɷЩ*!=iA/=ǮqaűBZWobtMB䲩sTlj:)GQ$^Gʅ޿ߎ:p:ĝ(1Fߧ;kJVCk,[$;-E=3[dXTP3fU2]}Әo28g~8l([& Fgɿ䵙b(5 EZ:B \wk6Ը=[RGO&c3QY6P5~(X])#prDU4L3gi1&H^.M%5U!֊U h#;Awp2VI)F1f1+ =()<Qًަ~??tlB$o)@,[ lmC_nhEa57>|n~Wh꒑H0%/oCȗ  /Nzا1Is53&QWXh҆_@%%jv:د‰З#\g*Ǥ}&iU0of4'@]:9}n~EN@}IGXAX%̓/ S2J݄w{:gNLa[ TȈH EScSkUFb NZ}uՇ~LIF1Iͧ c{4ҒA--N]3ϲcݣFMM}PHQncA{5Jun+ԲE[?, {Nlɾd8%cQeGFcS},㉥aBMtm >yfXϮmAmYBiPN(V-$Ox3n?!;1#%|}uD}RCLNv6H93_,ToUas8L#!kK}߲.v~׺]bG(Z)-\)d cϘ.V V|Ş߰}hќFa2.A5t.#2M0Lb [hߐ{9bw#ܔnz}pQOYo{YFsù)#eI9O9_ҳE"4BoZ54~yHm1cOUTx5@Yڠ¥{rݒ:5i,ՙ95Z"Fł8oZd\m"['tc+:rwgP-4Kl=LB}jʱh^B1&,f W*ݖK;< lDI >Mi~67o=+x+;I#AȖֲǭDJ'HAhW)e⃟~ǂo$PKJtʾCĠT.2BIآKo]* ,4(R[4VtN+Զ@}DfbcŽ;rs7X'v$qFsj$l?CfZ;Xa.r`Ӳ9~olUT8 ǗSۚ-|לw/[5BpɹD3Qe ؏^M}-Z<9Y=ôc nO f(4&ѫg5( X6<ɾeAWG&3n]?~&@F[^2fmRޮS76-c|[k&?:t/jKZ*, \ȇn~=U§,OyG:riΣ8bޔNyČǷ`oUudo'-?mY"3u\EyHKSߣaLFՖvBE:𖉩C|n0ض7_-w 9j*X7I Z>Ĵ~Ə2 Y0>Kb^9 !AWߨepAy>El69"QMYew%9aEbi7?4u~Wu&ǟF:_]#L bFwCgh }S60̌1uHm^U^45ֱm|Q:XEUό acv-\\U?am%́YX0ϣ7TNٸ6CMa6 ͟9_OcҚj硗Q\$1 )ګ ExN4w| hoAp= vd: J:쭍 2Tm*X"0e֤UT?γُ@]Q*w"ܬf+7]]zx&wE HwDЇT뭾˚ jtaɾYYd4YVoxggeX)( <<n"45ĊH͓fRC?zƝRA#.[t2骫 lzܨ aܰDk{8ox0u4n;zR!QMT _8`Zu{᮹GQ"B}ĥB@< A11u)~돪\L蛳_jԣ21'n\;+ɳ&1XtGW ³";0nw0kVrnP~ܵ5,6eՇFQ71bGEscyMg-dKG( j|.M^EQDxwׅY7 ]')[G@cmv' l#Ox B,amBrY%*,qN1w Ĩ1M͔c~c:wߦ]ȝn]1JbcЎ/d<~ZK k%]_Ȝh*dR': eeR45|@ow٭\ʰc섍iEE<ڀ/2W3@C^N"lGEi1sQP!}RPѢoA9ܱObni_Cknz2d!_xKN~;<X=@Q+EGON9=XܦT%,B|U^G;=Rq2Q[<<Z\OLQ.b"=<͛ܗSl~ӻˏ6݇IT1< `#Bai uM_^粋$vľAFZ+|ksCgM.^Z_3ASo_Q0^,9rel !B ^\w7Y]gdh}C[j#s@&Iw񸶋ro!anf߸3@U\:tD*{N튳UԒG=^رPwhC% FoVLS,V'e#Ux©LȀo<*{Ukb?IfY4&D&[ { ^(}VT#μ~+ҷ/uvWtVem/Й;qJyijI6HI0{y)4BT+ ^F6WAa]#*8v ݲ(55hW+G{e!uًtCa-yQtb)ذJSjj{O^rq|ʜ K-3tۄFcSN9aR7ƎÝ4D\dǧ@'%RC}qg 4RN:"琙Y52QWިeT֕Ẃدaz"݇u!hݼ=>,`.Ȉ=X%!L!`̶;]w>\)AGT{=U<{b ; # e$Q/4_{RmH>%$(RS|>Ro2 ,Eh'tn,V;aK+b߀Y # aȇTGm窚}QEɟ{׳M˯_p}_N8F k~TD quQpg_S[Yl8%ߓ|(_ׅ6qMljzo{ny'r5&&C*-e_~ԩ&U%yd+L} 3s`)؁p(ԇ{]n| ˢ6 BcnPcjUP[<*p܄#1 ռXBܿ$,_C *+UEى븮X!Xe.z/ awZ;\~N_PT.G-JH߻д!duM6nռQ!۸M4tÒMĵ+ יܖK Vy/Z9S)0 kuJ"gI/=oˁ?i!,kA' wt6jtdu\d&:琢wq9RCQUR9/%3sɱ-btikI7>5Uc ,t D'@%5yXX&̎oi-3n5?*n]ٯsдȷLu䖺<]d,|*^)}[nlt&htɨ6ג2үW50)V|r z in8p;"7yZs'm^8GIU[|U< ^BE'f=:#Iv6Z1He2ԉ`OpZF'c+z6H!ڻ[#f`F'ěCfIAFԀBsr c >< (AD8qIA|57pStR 6 !2V65r85oߋwq%Cl:aDos.nƮ0ԙIi4,+PJ/,\5+Л!xؚVjN nGXF~;}G D'%} (A{l, /,2 4N[GUw"G<O!*nfP OpP}" }Cze 1|Jqm܍;GJK_E$%.8#nKyom9s/$C11I^^o:5RKE/!J9qca;~L$ µ9/΀jU4JALyפEJq8KDoGZH*[3A%ߧC$9SYh g @9A+Pf"9ʋ֥IA3aM<~ F[H/NKQ0@"Kj&bu!K'K {5.g^w5[#M֍;]t%(E˂ycmyMC| +/(w܈V=UsKqvd{b|:~5#10~.'`e}*\slw}+rxFD}{9P8#񂲯1yOmиִfq=Ɏ/T j(ADHUka:T 0ۏF|Lυh똈rZ\85Y:'EԔ?0!d_+{?4X=Tw ˙QIs;͈46W+thP5[u nЩ3g՞?T5Nu3۲t=Pz+c柅?.DX&1Oq!8kg6zqֺ%ql>`,oFC$Bv-doٖqYUm7K[3Q0>Mvp<6L<y]`I.}D=#>>"e`6 v Թ>/@)g2NյkR{p%kÀ5m7Imթxf! ӯg"Nߺȗ,-KT؍`UGnrw7URو.#d4 : v 8$T"N VBAѽ.`,3kK U8AC>9 c ĺ?!M߯T+hvUM[q 7sLr4&r54G㧣nwƖ!N9Ԣ nCTTzʹBP1V (/5ϦR& *L{Oݰ[ $ufE}I}=f?ۍAq^2eM (w< U`|>J[F@59daA'h& Ԛ㥤lBS\2JXd!H!^f)LS jZ5@z?zj!< qIhm endstream endobj 1518 0 obj << /Length1 1612 /Length2 18556 /Length3 0 /Length 19390 /Filter /FlateDecode >> stream xڬst_.vVlvtǶtbFǶm۶;m}{XcSOSsEA hlkqb`adș[:;0(Mp@'s[' @h XY,pa[;wsS3': Y NGsS5/Fe d[ rjq9U8``Pp627Șm4[տ#[cJsd%08nq掎S=pY9C_lFXSutr4r0sͪ "oNfNv4ؚ45r:8nN2 f`/Ύ6ŀ45p0::OwNVv+?9;9LXX4rA10nl>ÿD%a`lkc01:M SNG]=ZJal2+WaTfژFsG1s7oeW1:X*fXOֳ1/L*bjtNW_T_ !![7'7! !ۿ`Xk-k`[23˿ _Z7Q#[fEx_Uu :4[]5 HHse+iP)M]&q}@pˊ'xGMFӗIIwWvy v;WE8Ə%(>2#P d>]v ,O)IOOWN]^@JpƳHQ'+!&O_8isكNe9XcEy~KbM47SFX v mCHL?J"m{9oI%xΝۣ}{@k`\ ` XodֿYK'뚶zcic 6daC^^~: 2.pZjejWgGo_>P`,4 ol}ʼnlYs8Btk{عdnTW2CU*'+mzμa$cD- `˄ BWG+xфbgzţ"),:ހʽO1"Nm1 +NO}r,ha@R[+{ߙYv^J!P'M}J*Q9"$5} }v'-~( Ԯ /i?{th 6TC4j3元[ v_m.b+Q'+t> W@OUYݝ\c G!KP8tQopr8~I-(=QgwӟJm̱6*h 7'%m9MZpؚ(R%\Ӝ܍T3!V_jG^ő$P84.&$I~Lh)(o"ɽfƴ͇a99ex&$vv%O($l4q0bUǢ11#6zc-ෟzN.\)gc2 o /{\}iTza bs? w  y Bo<*r)`%[^HZ"օ(s+~SwGfʧ]M҃k9Yg?.>`(ߖa>Cw +BynϏoo읶+8/FU>^U{7Y>ѐ )L"Uޤ޼Ω-ztW#s`~8PIy>3]H$s4"TG҇Ab2qrc<ON!HJfkgn@P>AVc~BkaWNr av?q5= cXF^P߁ F)? HC!Wo@qʙk5r.ڎ9q qu!t2H5傞iۗUo9pk]Me|8?Pb\m;e/=~(xObkt9vL_% %}A@xco_6onL"x wfH$&3}\p lf+|s'16oOeZo>;ggIf *\+EH>%jއR7BV_lZcB0[>.*m9KWinj0X.S BnkN8_ZVW0#_{:eDl/ڂ2M%Y( c+mkDueuPlG0cXzbى &.K}5WTZ")Is5 <F}@#@H@Do_0{"/YU8ކFe\E`!Bo05tb ('Kp4t>QmO"fsvSKR_ʳP6yƑq/nZ'7pu؇uC5D 0!pR 1 xFmp#2昤 u xŖ+ϳrP Fq#"xV4F6^HR5'*T8Ъi@ Z6X1ÀXQ:Q+OCfFySU %KA>^h#5Y6ܦ.3;^-zn*NM|X߇&(n-6X<3q$r(z J!sX1:5A@3~~:TR&ǗH׊ MT0m³BqSaha__)dpQ؁{kK~ )0pתTsrʶ 000n`^*ZHJtt}T ۂ)*&Ґ[sbBkpGT݃uu_B·Yw0/ii4F1 %$Tle&ԣ踍 ݼ,HF^HN=sP0x.7؎U0)v'G;-mF Y'IJl0D(}/ucP8˦hij5;J1ٰr==ĆG_:p&68Kp4-48%.HbEUgXO7US~!tl2ϧ<}aes+?sH܁6۴gCXBT7ߞ ؤ-AkEoE2Mz$p?L13 }F@m:'.- 1д#O2I4?P>71%OV$ak&z쏊 4 +kRBE9]N |w] b;E>먒=S+l z͟oL9,Ӂr rݶFig_NBJ*Y L(k׎މ*v bAF5R>3h4pxYIݧ٫7ku&~~ LZ+iuԮXtizn}TRx{I") ~cȀoXh~kWPT5w+t&,׈IWFRA" j%s^ǯkr!մǡ$p!Ic5db )ᯩ٭t9}:i(¢G' eD 8U`縍"p8~ơL2-/C`n#1^*#")cFcj`b}ɑF(f23w3Upe]Ou!I#d>;RJ_mɏwxa1qo{9:nbj'j sm-\P5qS?L L(}.ǰ94%r(9.ϨQN1WQ59u_Do%*@vRUm̝o\=^]Qvqs!-m%*Cjcn0$*vUd>xHN7lXW|O \|vzz43gk#XAI FU]V[AU ȹ0Hm|Ɋ;7*c Us|aeb;%kЛg=QŋFe.j!P:PNKB!ɬVoτ8uL{z?}f=n_Lb8x>A9קudZ68H9 iiޓ+=a6&Rԍ4 OrmE6y~LQ\_h=Vx өãeLjٝ^-鼯ovn"F4Boq{_lDD5씪LrN3'j]h.v#vANs.=' ,bHBן+NfxU{֌ϰ\{% Ɨ81rJP2Se veU-da ?q3wbNbO*Oj?~&e"ZA[֛^qlu4X^ |x߫{lk<>)'~0?%,-EX:;{.!I]ݓoFaz S5r >QG&ix@-yD@O uo t^NI-lk/irz@U4AH'{އ! x.>^߽zm0{r̕-^z| )_7g-+Z`PqSWacXV[SꗯqHi! *澭P%H3G;y$#>`{`1z[2uֺN; lBFrHGu:~?',&w<췍"dcؚ-Q@x==˓x4HeBc2^d>SZwuL d=QpuHl ꁴ[lVM>iGPoSUc3esv>SS|nZmVtICP3ԛ2*O="#eZq1$_3`/M(q._ӀDgx_Ey' *7"yYG2)(\j[ȱ1{QX8*Π6$x!k$:;>:׼V7M{ MM;?G(J`ٕUBt/j8<H*Q ($m(㲊^vnw$U2|z{#RT&` 28ZN)Ioqel.̭+Xz1܈$ܥDҪNy'b3h_Yse0{ ȍpZL'Mw" M- #R-9cd!RW7r{g1FS٥܄/8XqHxu{6 t<NK/cȽw~n |B_sgjI:0"S11٘C&hﭡ&,sCB_mjb>.JWkW9V*uj8v6\ >"h #vݒh!{*נjKӦ^!B6I1"qMHvJ~b@ܭb' /tK=ݏlJ0&u5[B 9LU.jodt#dMd656ɞ]3cb#Jeqrv{\u-/7##a&ٯ\,6Ib^\44SχqYCu0'*Gܣn,@b;QvpXn̞Z]fg7Ǝ,ZCNGp 5e:=%#i]^J[WZi9a&߁Ĺɮ>U7]H_TWg1^|D?72=֠ӍtZ}o#+FhVC8{ :P>HP^%6$R֊)аc=D1C*쏨Niе+yŦ~IDӟe,hwnE|Y9pzAngC% FV{IL?o(F`v ݸfHT>䐃L Ȃ@Pc6 gLh~JӇĪ g? ]B> M^ozZ]fl=Yjpapv{nEx@ëNa`?% ۆ4!Hqx=3)3j;܃v"n4fYQ|l U<,QdVOOq +\#V -+dJcv;u)Vpt.] ū]}31tC rF8C&l,QcD i⣫窟˶hy1~aj> |Sf^a=HVϷ",x cDԒJ&euM,Fc&TOHp*7̋nVoW*Kgm.=>m× J+@FX_)  ~D[4MʝXXb0,ԓ*ݚ$f~eyD>[%0R/FHkTQEbE8͋YXw xtől|f`Z,m^hvb3d.! w"&RɾWu/bKn]/8½pq"8as0fNbܮ3zZ%.sQ]O#gG~P%=(2$J8CXz:8x)':N4keolOԲ7|krmeu3 #MwHm!Dz"G~#0\ar:;[U=MXgDNMr;Tp_T5l[?1ˬGv:x]Eh9ooYAlݠtve k>^2 nt5m.:+Ⱥq,;•f34xtl9pxIlG5)ȑ1T_׃S)WuKi+S vĻ{9.=@L;'w-H AjfU)Hi%ϳ6Kϊ q/I&"4O01^iSP1=BtQs#jZ";;]Շ^{8}3$"PugH(Tj)"BЇ' V T̛(2L^ipg'~e$M[~ᅄ4#л18j cM02'&\:/k]A0?DE09m?ՂՑ ~- <"k'p sZcOgy2Fv*XHG_U'hif{Y2lBZ5@tv~;$K,1jS"3tJ͉i?kf]L !" O*s1%ۄ0$Ei?A5aO[WUX/w̢-q H2٢*ftR'>p} PU5䟹 m"NUX c(w8t u3+SI/x8;,^H+Wz6ǨozonƭIl4uv-߽hYLAh >pi 8u0PG%ӵ,dH7':8ln%f!|Ս=.r e&Ln20cv1O̜0]4Ic;uUv_56RL/My.@omNu*˜jzZ`^--89'?g6 ]7opW3*Rga~^ޒ: >Qk٦_vNPv=385) PPEyVW> }γl2U/='hS YVBkY3F x%ߥ⠬x`^WaxM%^svhJiФYbKhFXO!PUk֛chðDSK^ovcFLpp?xc:J3lEx6h!OUG_;@|T8g-}8s+Pr K^Lm: MXtl;K.+ْ`JhWUXDjiR\!NSz8(k=qgNVpl=\(KKU)YK`1 tMZ6A\'v8ل H6oT |ӋTH4bdNY`~ ;$33,ITpٽ ~i#(!l-iX Lak_ m*>QN.('UuwoPV|2/HG&Ɩ@U[ŵ.e2b-B×\G ^nN9۩ͤnЉ6ҏQOurg`^r~tϽ@3h|/xG> T+"rGǩM&1v/AGc,2IC)v/KU󼶇Dv3! ݮR.5?;MSrQTRqʊ.SײvO".Zt:`}蒾[(nHgAa[-PvzCHd=N.Inj֍B 98vЍBmJ3_ q/Wjy&B:cz3#9QϷ;ns5t HMlz4n!)3yޓd͊9?5j!tm+ڸ>̀^kny ASh%lKpvSD?iIDL|B^`nLlʿ,‚$n=(~!S{Gdvgϼ ?53lr&``nFmN,}0-3PM` B9[,l̅o:cg6X$:R~g[I'j9 D ]Ȫgf÷}fNZ[o6ZVp;[G/E1iC& -t!ngI'48k0Je]m!4,`UlEv=,T0E2O2:˾\R;yѥކb" gRՓcR``QLB 9:!))FUSz5v-^Cr~aiZKna~ HNi'rRNpq` hi[+.idA4;3#hB7Cf|uQ (9Go| ՕI 8aɧX|}h̽*{e6:PxH%YŧI=BLB J&ޑqw{DR9uB>֡a'Lt 4QCud G%TJe1eM57el(^kt>AU61֗:r>B9JLmL4^.d#pCwV+á1}= eQm؎cz̒MOݎϏ/I ($Oh٭GP]cR{kW+">H^1 M0NHKK-[]q"nat\yv#!Oyq c+UQyG|}|RøVݲnjM9X]q#to[M kZ`(rDӼ D9dJ!ևABIN;AnNo0&mFbƈlN]S|be2W +^!*&ȣ )=(.j'F&فC!Kc:xBczs` V)}mu-$<'"W^/_~mK\T\â=t(#S[D*DVbӚ型B-~I{v-gJD*M^S5%A?Qב'a>{@|>;=X6nYd9` :IQ.k^BHS_C^3Ee ε,Y>[U_}wF~}~am~@\)Pj1f/D ũ*Cq gdž]%,~SGYi<'ǺTUF Y̏lN[Wǭ" d,@ρ({ lU5bwZ;T ;tKek VY&t+CKɁ:0r=vUh[27 uRKoA0ۄTMGlToy(—ck ݉[܊w{[s}\V< g{fw؋={x.H$] &?o-M̈́cڶZm,Gb8po `tN$ JlZ7 y23F&8EZ±;4ǮHb]2߅ݸ)i hgٻjp{*,A|Fă,cdBi0ޤIjZlM@'8xPx ɣYs4d4dWSߣ' "I}U.Zo?ffEv|%$#- sŨ -[K:ϡ'<^QPS-|R8(ZN)k"2&Y[BgpKK56㠫ۋuo灞6<LfN.ÎގPYp[Uq#L%C.HYQkEQ%gpO@fKi{N[K׭T5r.}jKG1^ppo l$jnb%̎蚐W.XĬ`GdQ#n~D*LN|ζ~Yc:I1תm~IDh: .6A_NI $XUD-}AXoT7`F^x6KTD0@1@Ũm#]_:[{T!6FhFXyYW$+-?s])3Ѣ%x t|'<$8YO|qCyOT2?衿Wt& VcD1;-2-= MSwa7_9VE+q{OZ- cd^zɞ7%G+./0qފk~QgZ4y7}7卜]8PFqs#jc 1/첊=p(-Vꆤ1B [: Sh @^3 #춥{C\lZ c$Γ"'??>ЏGz/ EB {cq ͕0R/L _;`9 #U6SiM<n[nt8I4XbQԽIc/i'!qàMu# K}R`ssP/OG?ӬsBS b.;|m.ape؅`ị±1`0eլٿ3"=_3rw̭m-3R3{#?t{hzRNs?/V7Հ:*X R\?G `Lg7N*LHSˀ-[%(aPj-mz d9&J~n,:v{,Cre X#O?HG*RPb92wX&ėힸj4FJ!s^q6҈8!6bL+hj7_Nh 7CgkթHlsn@QܛBbbcQl)J"mLL X'ӕlXҁKÜY0W)Oщ֦\+QxE.ܷ @ab1)LVV¬9Y.|Ut,0D/榬{sz h\9$<ɒZr489xDOz;ՈW^(nem/$1=t0Ē{v?"|]0`}.b- W #s:^oQٱ6@+}Nw/z;&.+W-G~*zݿcyz 8HLmxa+ oUTI hOvOBP{UuDĕ\ۙQ_옏L xc{XK?Oa~8qg?]C8XA46CAiFPǝD1x،c*5?O+J~At[^5b%4!`BLsF46%[4(qe #5ت yk+I}9v9=8;rljb 1H~iؙGtVq;k˾8w [Y.yp;ݭɢ[lޣۗ wG1? Jm7?P55 ;-pB[GcHzuKXh'TdѾi>VJ-s&V^HzD҄۲y̋ @n >U!|N,N;GK"3%z$}c1mIV9fbKvy _+]0-nEE(^䄂gi6SA6喲a;O%<>gK[jl3GNV:vXǵo0җ,K<vvښy`auQWM tDY#253qGSU}9gXͮns1㔱A2µun \# Zʫ┛tHAϏ%Փn|| />IJPwzf_0M] Y0o 5@"fb7O l,5t`; Qq1={eP2qRĄ )w- isկd7ObQ=g60EaiRWװ_$l>64d` hEnv̙v93) 8mV yRDW7E~];zu[ܞͽT A. D!BdHC N}[8F/>'B*@&C? h?:*%:9tu+vЪ uB \VqRF'ᕂ7~2OѯGزB-균DZ-0pwE;cy1h#%#G}""ymHsL?(E-~]sa9 ,xo>μuC0,=%;~YA0-9X'xH\Fsd^ԽlB)rJkW qፅXxj;Ƹ$#~ ܱyA\"(l憬JGa& oqFnۘ#1ր'^پ@88V-lc AԱLk|Ԧ\#nb -p BޮY>lkY%h@zr,Zgn:|jb~"ۇ.Rg-?pZS 2҅0Eբ΃nI\K7'>S$L)S4~d,nt 2vru͞*DzQoM; ftR,A^W &eŒQ} dNIҪEL;No=zDϕ3ZXP/"ߦ+/𞜥t&1$eQb`pc&VxHߔ8$)4p[%k=p*Rҋhg&da)E_;_;b|902V6?e10>xEZȡ0{S1s*"+њ*XXmѧΆ¹G1 ԏ1 Dit=! 1G@X'D@zv!?4d-gP{iQf7VT,{"kE:9|DuSlUS:h0;]ySVXﴛh0'tO|RÖX1WViGf/l5u)$7"^N,yheUTu3U8کtK8}~ͫR ?G B|zTG\+Φ5,5' 1$°hrй>"]R; 6A"r߇l Q{>25?յ>_[KRLlq. ZWM|b~/WJ9ߘdDW\n57(Uյ$> stream xڭweT]ے5ww;;]`8 !hN@pw q}cUUk֬؛R]U l;rqTA-s'V)ȋLK+ 4wd݀B=@h p "ޮ [7#33?-,qнxΎ@'7F- f Xi5uEUy@t5w[8,* KX@a{Òg%m tuA o`j70dn75oBή7:t9޲_!77lit}o0o^7sr+`8;{~svMr'+a՝ o՛;;;xwqAlȜ\o9-rۀE'k0v+wy]n_3F Z#Rw*D7Ho"MyWh9wUsǷxa _wkr5P[$lޤ`yAnksmq:oZF+'/mAN5Oo_7ov9i}ee=MRSMl_0^_VN~n+7a+ `V4ߥϕɿ:Y-7s'/_nKwW7E>o%! s`KP/nc2F=]0K ~wG =U}bzi=t~VbwLS3vZkg fPq{Ke ֐CwgcLCCSZ WѠtHHٞrEY6,#>P 1qOimfl7xjIOQ= f=hR v,vFzrvkaEm:D!#-Hџ;7O01к䅐7s[^R h}ybВ@J1(ͣIm"sWwsa|)GW4`Fߩ 5-QˑpTj~bJb -έ;[8q8Pq>mRؑ5d'JyJ5~Lz?sIJ]z1*Z_}=<ZHd7r|HSFBZ;~uO&Ŋlt4׺ǧ<5EE6ǫ _ܖ8ְTIwhCcaʄ ȥWCJ*[0&uPUķLUM[AF $}Rr$? ᓟPd4)N~Qd}$E% j4N_U@wmQͭ)q:ws,(SRac5Ѱ7 j!/9/M_ܦk+mU:ᢧj) {M_8],dqSQt)R.EO󩆰t #@b{ꭝ"52 LZk}u!`f,N0,v|56d R6=;V,R Ho v,&X#+g$P=z 6L]W=Visa;`6HѾ2}P]16QΪQ=vVx P1)΂Jli74n0kB:6NŰ Y㺪PD0è8AܨRe ʺ?,X-n~WT}LCR$zOD\;[Ru!dU\O kmIio% llF|v4}uiCP!>7=2c #KZ"1`g0QaSԊaVmJ9I)J.Bm qxȳ,#r{Jp7AVDEcXđCuP3Ay6bp2p%54-`Co/3--ǀ|N.rXR:,>)Έ9?` >y)bR;]OIyx/5 { ,ffmg|4}kF5k32y";b{ڊd6+bGK}hR>6WԲv㗑eϺ߶OxNq<))a!BtHT`)s/ήU\N+cMes-O)ڋm a )ڜe0*UI2DVF, Z`o;t F-y{p N! I ZWWx,UhȔ&}jgb>4|W.t|y_Z+mOdj?e!AL%fMFKuw GϓeR/Y!,%NΆUs|E[pfOc4 DQAmsOqj72_ܱ ԨՆV&AP!tyӐt("z1.D z9:!)fPl߉ YG͝()zAV:N.SW`9c~_!VcBXn}W^ U^d]ǘVJN^I6*o#tFW%w֟+~yfN&p?|%ܩLxpF0Ld*:HvG@v#b^byI]91_v)2 mp"7N/v]ܩSWHqLLWM) Kj߈%=֠0f_"be4&Pz_3mq'_RDM,@,BMg@r',KP~Jc#۱!nX̔=|l*acb>$% vwCq(!3UEOvG|* &Y^I5?*Au#!;Lz 0"mOe^ @ET]/%lk}]匇% ]wWLp+Jؓe*1F/{,,Nmj%G|NrYE <9.Ǹ3j 4ob-05 QNuOZq?}=flRl6N{GT#!`iҬ͝ !'umcfhs[.M#Y8\P/ӸZ+4< ^!kmz_,LVDٸ,&>( 84Ų<1OlPCXIk}!ҕ† Wn+txc]z.f_p~ۺFlW_t`qyj̒WB,zl)^$\ .OꐔdRM'W.)ݽ3^!;qƼo&Oy{({j  BGT2W!3e?f̻\2S[`q\=asa$VֈE&, ^ɳ'p"k Ž#/ ?٨8MgZ8T#/-R2=|ԫqveggWpĆ}Rn:gg>^sj?.9vʌn2BWȓPR8xP[TC αawk5_>rT L]T.;sYMv[k 7)%!NÈ>'puXʟZAJ媊խT967Xj6Hb(tMB٠G2BeBΫ|Kˉve!]JvSxhpjRCcR[ CfPX)D|c=y^ zv~섺<-2祷'x识Þ2yEy$sUnLWl1uҵQxHRU&3Fj e3{٪^B$!'񠔞r!o{fHtAhg"FdW8:UEe51: :T: y?OsgϏb<@~tLms0"a06OFW^ juRsA FCN둪&&a.4 i4^=LKmgsZ0eIan MQT9qaGZm3 Wdlɛ{XNmbx $kv/]oI ǺnH'8~ s,[au 9U6" 8YǪƿ_ sIIyI`6aIa:O"bcI%| ]lSsx|OŅeB|ЦPάAr_ {IJ5DbW98ǭM P#}"؝–wZL CUǛ?*L3l{j,-h)84)&Sg!U ӷ ߂9_tB5%T[UlT&,a.{ԧ/zI]uq0> o)FMg kNf>S7=?X+ B7`w\J\=v(.1:2Ur}bQ6Ɣټnhp<ɏD@q=j*dzҒ6}=A\ d⥮/nbfA$BIB =OoӢrŢ` uxЙh,2_˿J-6+ǎwqA" {'N"Ģo))D^veeXl($z|>Tq:BR 4sl"ʯ*%Ĉs>bg%iz| ')z[FfY~ıl(a|eAUOwJ_+7(Q{Ci=Ħ!Q9־1zz/,=LpK?⬈ ~'8iT?_Zk AP^`qO.38Bx1ʳpy@~!2ފJtȗ>Uh]?z45zNrV\n@e>lO!,(J -)Wo'gX>ᙣL]Rz'4 bf]߆^Oa-.ZP'Up5|Ӈc =?ќzFq V;Nv^C<MSTP/T;WDiǾHٚPPM9[v[x*1@ u'#)ɡ)hgM|X4idpJ!r"c 4gw{ڄЍ߿{V-N?4,_>>}IɬPq(0L^/N:t|v]7QihOE0ԊRt5^5 d7 Ϯeu½x70i _; t/K1 Kt=eqCjx s͜;q'G$㙚"ΆaVv+koCT[FIN م Mxj b;Y7vA&\_!BAd@V>_TѐwJjC8L 4%\B--乣q9:1uvƌŽEV]b9`CQK~?~9$:>sl vcK!??u*"76K0:a'*>rˎu}Tak {5sҪ`c`Ry&y.07j QB ?؋*<2K6趠|0$2)92ѰxYc?5.('/}d]Ԉޮ3o7BAuIb3[(N=C k`oC9X]vrτd*?@11Ӭd-t=4; ;}EĬ\0clga>F0gO!fapfMh ^*SjJ8H{uO :#u+egR_aLB[ 61Pb6e.s,dѤp|b#hc(3 hyȀ&lXS149YhNLiNS0{?$qN"9^G틤)6^R_ ܻţ lvշ! SYx,(nGOL;Oj5siJUxm:թ ^qGmyx,CBW*O{_ϐYj (4?|đ-`?YvD kkWZl#!z@ӢKU~=x:_W@<)MBcQH2:Acb8OI`]5V9)VӋ$ַ!<\û_sxA1YpP h1' =Nttaxh@Htm4 s%9ؿ3A.>DN?5jJ2o*Ox3{khF,VVM1K'ƶ_o?Fv]H>:|Z=PoM;!RBJ4a^!Wh> stream xuweT\ٺ-Pnw C$¥>NFkOk]tTjl Y((PۛhA4@֮g N ~&Af0T kٸ͜\@'P( r,Ps;x@03 },\AP# 8:[\VϝwU3`ebaa{A.`k(y88Q9[ܴjVf2`m`0G!G+33b8ZJ9ͤ <8=`+0ϑ,]9`'W?Cc r@N %<AfPK/GG=?0\@+%s [ c;3 ^ vف@N߿WFjxbfphʪ骱sGIJ:;9 /?3[tU3oSCYǿFr9<{_; /5Rg YWOU%YE fnfx/3P/ ` 52]d K50_vKe?!HMqrӲ[AA..g'Z  hž]iloY?ݿVA w҂ś`ۺ l;}|O<1-v^"O<RrFmD CIeb Jv!E`V:Ec:$DHy ȝP_ӉJi:;RV/0 A|ՅP憻ai  0~Wv/||7VwjN_k3׽Mx|7| 1sX(DI +XDDJo,H3 tN3c |eW\OC䪄v}mbW kW-_2u_Iv)nlվS'sN tqh:4){c+(Q$|`몊ID<%ߛ<1>7GfU+HP'TԱI tB [R9£b>ʹyw)Pwҋ6$u+?ιLpN4ޒz~lMR `е)B/`~3WjxV5??:A:gwh\7.. f?O戨AL+7nSKRt1Dǵ.{0G/PfkTr2>5ҨI djHƫצ"ؑGxCUرa$ S5X{Sh{\ݒB mG^q6g `$go#Է֮"zx6p`GuT'\ȴ+~bFi*Fm^R]DO,58VoFO˼M MN XMy$(c2[S]+KoQ0A0Z!t$ ?䨽4Zjy>+t+5j' zH|@)EH8ZudW~b[`}-NW*o4$_A"$/t[ .Gw]Gg˼{Q:m;EcR9Yi@A75>a Iې2Ξ)|мI* _%B; 4=gT>wȲi؊$#t^n '-=U+`2*}-ՀWnBc=Yu""vf^ٙ,XBkjX)2Iz\ "5PH9R[H4 ` D{JBap؁jUTeG)yTpqaWJ쬫V#09A)<|HT欛R#=!b۲IL܊9tjG4=?bs \="κBu$P-)CgSo!HLgރf^'I$ml`d4Һrs-a {s9f8aD$0]!.r VijpI; O%9p 4ٵ^!Jɽ8 #YIcj! }LL&G&S_,̂e!cߊ:}*Paз3-XGa]18jȹ﷉K!(extKxU|]SƠ-$!&"" $(-BnJCGǻ;9_,XFԻĮLO[efLJF~PTz'U3҆փ#å^Lm\(2 Z)̹l{!"XֻG$XY0%mQYTw#T5cbOMWtiޗfm iyho8A"=n ah?: tG(VN>G){ Go􇟚t;Zc:E*R-Z bׂe\!0m7͛O#y} 0垒l+/Kj)ԭ5;T'g(a'{,xvܝ53(y:linwB\m8t|zJK6t]fA1kkG[8^.uH}^ %":Ƒ;y1Q >~5ڝP7_o{vmVfM_tZV`N-'#B5Pͫorx0B/ ;?L& <D*b3G rv"&yz5"!l,\2?Y#V:V]$3xJD:r |o)^ϲ{eE*^-4rd""+~jy#v1fjϾhHbF&uU{ :t:LRj>z3L\ ?2ʩ Ճ8~UF~*Ss%\Ce)A`Hj:AJ*YƄl q;~s1Q "!fp.)zE~-)va Gs Q/2G4),ܜ]'9d/NgU_胝|Ɨ*W_ܷ*_~:g`:Eϕ o%&b:[-JҤgRR!lV|1ӟ -z:fD ޥy},GhGȭxmqɼeЃ@C~e:%Ap 1T8J.Dq{#hU{7L<Xq?U/ljho9Rth,+vnVqSx#+ͤ&hqnɃojPCli[A~I ro'5eAjə8 ~t|=w sI0BV&$}`o ?Ra?,UQ|m ;+T^sϊEE.|m ŰDTϤz=:݆UC1jPhi狂C1=)9H6n-Z(c;_]KrmFYQTN]㑚@=M;Z)Uzםd弘lU<|BE[_k ʥbr*;\ByF?ˢ Ctc"y$Η>%Et^ʊ쿃MdS!D l֋\ e]_Ig_#QF,*Cg 2.MGp&>/9,ۣ}ezU_4tdmR9dPdU߽vO010ܾ-Mʑ]ҍGTOT|7MrJ_KSH5 ~玬~k Crdlok6tU'n":E 9(p/ ))2B8-Ujy2O^r[Ld@\))?F5~\j*ϒX ?)qkdNsDf(wXPUvv&蟿8#$[Zi>K'}KD#}JP0PGE)hЅWi9VXَrH$۷ ˤr e˨TwrGMy5G-اk穌&/Z1}[ouC4he'r7m=G,I֮&/] h ^P'h.#ggask__|#0H@-lKxz@8,$ybaEc/C?oHN1\fYגJ(m!|p콣jaDw ReVR3;ӊQ/łPa}POAA{0i%3q3Ys LWa)ʹghK=ApF媳V)~~feR RBU?;0uNELd\w44Bg>yhn\B;_ԼOaXYU`mσ[\xz[,5df6~j̄2\L^l"}e41k>4;i1@~ i7m1v % ̦`$b #֙E&P3a]fL̮| %Og*%ĸPPW;B#q] {z8Ae0ٲհ-ΌHd W"tGQFD.bBoyU_`LUdu0yrJؙdD5i&[xjM1m$uj0gN ]Jp:͂@ġ 6ϗ6C3nGamTITPF괋%Bq7J"7w>L[)ҏ,"k&KSBb?Q ſ }ZԘb8D#nR@p~_a+{UੳGQKH֟ܭhX\DfX0eufk]3|YIZyqLeZ y yi3R58-Ea&<2SK5dif6$Q#^.w,v}N$١mIJoQ+R(6 r2uТ$W]zt;ī+5p&&8;`gT$cNo0[HXJ Gy>4Q$3\I;fJ&#?ݢ^)Rb yC77.Њ]R)B׊wΆ)/ w >|x%fqUEvYDkNQ`Uz[lBU!v(mA箣g~GrD(=b3tR8h/_k۾qEReCvO q7Ph<2p/MuP%8= =sT߶9?˖߽ay/d?f:; }rꢠ1E߿`E}p;[EgyE{~FBn24%_P'[ ㍊P.ٟm{r{4?1\n^Be7<ڨ^-x-G endstream endobj 1524 0 obj << /Length1 1199 /Length2 3288 /Length3 0 /Length 4042 /Filter /FlateDecode >> stream xmSy99ϑۍqT3As"#[(JvcTK `ZXn99'х@,"L/ D!t43bOG"Ib) "0 S"(@;d"z?gL$/PtvpQRQQeAP(;'dgNl |1>X3 Nƞ F_7n$C8 c[=YNs]iby>CZvo@1( [q|d.!UuJkqQzݷMx6*ti"tENc>D nJݬ[k-Vn3^L)U\P|ݭ<. {ɻ]LQuSp_WwX͈ptʻ&X&UXEI՚+4z }yM$Xf1+3^nkGq8;ťRV!oa`#$8NL1ʔG,Pʹ#hbe]C&WUsגQ&UBUIKzo"6xġTzxp#W`("{QC]P@O?=ɬJOWːhZ0~TuUmM|[oD78y"m6kLCr$)jcW;~+(s,|׃6xJc[d,ӽh"ZlGޢX}k%[?r=J{z‘mpO0xޤKf1ea|–_UW?I0V?Z:-5ݚT&=ث=1Ad֩SoVz{>N6 ^^e.=gu 9L$.8DI|々ϥØbt$eYȕr r&eί#Hl3 w;3Vl2<0ej w[ea ]6%#su̳'L aѯiH33a[>*ţ9(toD*12ȼKWw+Gt?+ɬPhnӜpӒQeLnt1ʧzq.O⣍% *e`gYJ `PpKK \smj i.4r‚ѭm)XZfQfBOW-{]P5~(|: M1e͏&u&9:TL#2I8cٛWh߽UL qjMރiŮ(!s՟w9n뒊cKD-%)kM`0ׅjYLD#G#pelhMՠQr[Ͻs/ۗ<6M_ylyޥ؂tm7ƈxQ,%J+r 61n6gKju[iF/ &Rmf:Qrmkcn8*/yTݜ}&JtW% \08Gcrup>, W|j0챴~N3aSs}aѼe7㢦tR4k s-$TϺ%7 euƥx'VPxzt No%spu8|fs檜;`JhЭk&UЫ5(ih"3777YH) vg& w >ɼZx"~bH4D*A!m֜<eL{ȍ7!Kd`¢ ' :zSݵ)4Iό:)i.׭fz)( O~ۋ>s\y¨CkyzEE1\Ir6R+)mqWRf%𮹸9P[.ۤv|$Wt1Ӧc;֕E^UWUͩ+OM%(X h, _"5W&e\VF,yG9p6!Zk-36ëgZ:ףm)j39ħ?1Ш>k]'5[`'5f?׾i۫)y46$)"EWk~=u UW0R|/7\3 %zO&\ g}8lw<စ˃Ml YBe%tҼȩW޿!F%7B[~^;w ؟ћ}fܣK^;Y|g)hH2[H_hzpS[.͛Lgxbi)7X?LĻ endstream endobj 1526 0 obj << /Length1 1626 /Length2 12031 /Length3 0 /Length 12863 /Filter /FlateDecode >> stream xڭteTے-wwhwgcwwBpwww?ιߟ~[%fլ(ITMAf ;gzf&Id▣73>𔔢fF@@ ff`a0sssSDA@ Kg5 jZZY {# haq5ۚ9@UΖfs@TQIKZAERA ifghdPr1&fvNfs# WkN XN# #/:,?f Ll\L"a7MaS99;8U$N7di 2q}0^g#ZfSG0{G4\vb@p40r41srk:_7;w?9lY>j8Զ3+v 3?.s5s{@_F ;9<$Le=$_E?q]rCK(~,?#cdxgr+hJh ٘Oc$v010t*M,F6ۮfgjh3虙hbmpٙ;&Ϩ-M<*},Wnъ<`DD@/zf.=+ ߔ_gy#gG;@o&濻_'36FvO_nG]73s73_Y[f9 v3C ؗ4|-uosWԄ04NX8ݗ9ƶܕlvGCNݓA Q)L#j^n RI`g\YEh'#?k?&Ž=IJ7vF0ڂ3#C]P=(yp|NI= 7A=rڃkˬ2|7mjK7ڠ`죭dYORԋ/Sv9tl_ kK'-zR~ir 9dbgamaэqFOY*7ž4UPgˮ"`~eG$R+sW01jf95Ix%܉~aVwzI>=&KgɒB@7̲/wΨRB5`?y-,iz,~AA.wYz7/LLpWg~EY;ɪ4ъGsy[^ |Eʌ*1mKWyӎu:˯aƝGO݈? 'KѴ/s^ЪvZR^Y'Xs~-_Je #[nH~ Gpw"ץNvXЦ>CqcVK*P?QIҰݵi."Zl$ H0A(ae!;RKw^Qʅ_2|}qA:h zKZig7##0*Vo5P~!$C$v!2G*dw]E-G*1aҩ[A8@N q?w V]@2sb*,>2Ԗ_ݻ2 O!;5Y}:4GlhynE8j3hQ-^Ӛ,YRP91uʟthђ͍#B;WSp#!1R ] =: ejjF-K7hF/">/0=Mo_\~,AtIU\HKK(anXjqm }Wja,ӻȑZnDzD=?9S~#N2nS+@pQ Bn^e2f O8YL{!&nUРFz,D1{#@ӝ)&cD:HG=,)Wjzj[("J3wzSL~ YARsx >‰P{C(|0| IGjܦr2ueN1+Y;?38]TDM_Y_<_UTҺ_fھ@E&|YI/6CD>mU 6A݃u\8^TbMd-4$S&2YPѶ U[r4JzS8E 98l~##v RoXī՝UVNN,G0~e1H4p)ϏYHC{PULdjbE :a p~#Vz&"So |>JKeťPUR՝U٤lkW{4/Jyj1}Iow;Ss/|y7)wưG K]-?hטtNTL.]/e{čgzW^CBA;$<_9</ xrX>u2L "umE\Ϥ콣/}u3ZGf0I<$b0ČU2Jkk-%!Sиd*]on~rwr)QMem4QsLRl E&9[`m13}yAHq({3޶/=~T`_hZ^/C53Fc'}LiщW TND\'D=@vˢt!A 6`,_Ds^_M ֺ݈57$;?r㝚A3ywD+΅2Y\-R Oo3uo 0``+^+T*@Ko@2_3k:139{e }u|D~Ւ(HӚMh*佴aѻW.#p{n :Id&ޗ 5HaXzտCx#"PX0"r.9ŴwNn:'sVFQJD2)c#\[{w_τMv@(Z$Yu[U$5rSvpql<^XUI:"FeJD|ptSB#`L}yV9IDC V1͢f"cިh̏>7 v]&|aܠdY테WCܐJr1 z\bM9 ̂D/M7ݧA `͵mo‰oTwv`|򍤥֣쮰"y<=YdxF2w*TDV*ؙb Rrޜ6&L9fJgS*Ȑ~+?'frZ,#&u%v!$2hn7 ([8UC@wׇW҈rFEzG 8cM;=$'97j;W씿:gb!  K*00Z-#(asA0j腵mEE&#}h_u 9IUi.ktfĐm)kֆx˅K^wA _@ِ`| 8}Z;'Y?\\꬟WwXv<9G,",{a=1Wڡ\6Ee4=L]YMcSoYFHJCK\L6"fk62m`7{w7k$n͟~Ȗ"Uz[_9RRk^+^+9=WVZe,NʫbLpT5$.o/b<-V+_z&=TS{.w bMU;DŽ D'̩MJ0pkA엟||d ~"b=l+p,He|Hy[#fQtC]`_4$%܏!ƣ#zJۍ @lòkYbD!|ZyAXQ`dja# 'v6y?+é>g22u;:R=uolX:z:<&c]d( Mf݊3냠י0yZ)C`<6n|A1K zLa{vgM1kORW|#Pb0zy}1]akD-.&*5v*Į g6pHK# |{O+K?I%ReP#+LG.G-v\mɾUw99{]d6-ِ2+v9|h=WQjnRv=;O2--ZFIP :GMF)9- 'AT#!oGSk Ѩd RQL\BRRJl s1-p2UI cOvV٬4g ?,jWfQQR ʢU }scl$Yz3zh <}M:6qM#"bBty5^ 8 O2xI۱A$b΄* ]su%r=Sd.U&u y8j k^ӸvXM P+{O<8PK*2yh;Vܣ\*=8r5dxPmRID.MF KM$gU 9|E7T7H˴o ];3BjAa&k7-6rbi G@~Q/WdDY6} Z;"{HK"ȂZrKuvRG89T}lOkyB2~=:s9C, } ͮ?/uT~|;tt7m6l.j]֘˫6)Fe^B_ Yo7TfT0DϏc[2Yp.AR+p0^X$)z"@ߜv7Es4qe}^iYZq |1=sSc;|ImEJn7:uIɝӞDbQ<ֆ/x.9*T-^жg:|D6y7zCp6Zty }bAx61)WՇ*z=QN\G~.guLݞdG#`>w%qJf^]_V$g~)3H4R} b.U!s*iͣQ=|5ְQ/vO |:Uf4ѻw&bh =9ukFe$DڵJ;N$*,1q䳑>W0KRwF~{mWىJRml' EO6Pd6uu7/B~I! 6" ~ PcsvlL{uŞ}}9̫t5Pij:*̞،Y2C>-ݴ ]W_wՁ$W5JIY%!1.|%М)qo|vM#s^./eV ݉D^Ӏ'0K[/F\ap6K}ʆ]$ +eSTœg#uFŜsl7 š 2-78zc=Vl ɋ"}i> m}vR3dNV;ō[x-[Dq]s7Q\dENee&~W&/p`# Wi`P}5 /?K&#hpW/X|WCQI->ky~\qB ͈D滧'8~/itJ帙4}y#͖ A|* =P kgʥRG;%ɜKXh2v&,2BKS^?]S-;g/gWz&BE0K&:AD6sl_–n"z-=ֿڜ+ 9el#qq]PdT$i泘{Bt2w{тƫP3[B`(dЄz,a"|߫6c]|Rzjq /s ŢGv8`Wiq0=vB~;ry[ݔ'{!x/L(R*w?bn;{"VUO8efNߒAtҙlZ7wŜiꝅSM;hvMULފOJn;sb$,A Uit,zfieCys~GF>Sb9WRS llD$l+o@Vk=SՎI|I"TX뒠uޙb|ʠجR /DtIM \I:r+uo;)=yqH-ANL([$"_dNJ+jJ(w/ q`O]\ANy%$%vCn`~0S76;D f~1nZnBW&9]2 VA,+3i!;4Tk~6 WrX۸W\ vּM {&9afʇRZ.{_߿BfT,ԊPkq"=gd?T:jI] "15P30<NZFxOkqVl+Y>1m)q(ڮM.(ZHX+ppmi$CFˏ]0`mrl_x6uJ[*or v<M(n s[lR'́X|$[Cv`ϭ5K3LoD.vdh;bْY-&g=c"&tHQ!@g&G4Zs4ڥ=0b2mX[=b>!=s^Úvp~[s(y;}*;]=]go<} [4dCaMc'2[Cģ (ݬ#}`#3{=Balm{i/5P&} 7dFt&HˣX Vg܅PpIQ^ނ^=5w}ݢøQ*̵-#ґsFGCJ#(urBau%AQQ# s7ȧQf"W >=iD/RaD)sGdU1YJvЭ~5BIDsaaEMRnuVH³':\ő?.6h'Q ޥbOk7ɪm߶jnVt6H(?yly{CHɤ#^I>w$^;5p~`yuѻIb:M&%#|EQm5VaR7` ,M*ܴd,V M Pu $A!\p€njʯ]:Yk6oo0U-W-L^]˾sfz(z^'nI~/ _ޓJ [V:וc>lbZMXm=f)-|~WtB`Q-)c0(9+|C֞+0i/'lE"b (ATMfWqQdY=7Bj|:oLV=lё"}%;'ײ'&8PjLSu%i nfҶ1sɾ7ɑF]MIY ~SW0K/gnz|vAEO;[^ã]f!wJnd&S&& rkx"ņXJHTgMD'0e1!fBUE:ẃ@# _oہww_2"Ȭ5eKť,i\ak{1^+]s07(x/$Z`=OK)٢qdT|W); k#Mo [Oq8MژbKgPk%|HCmE52d5|2Ej.l~7Pw **AR5qM3G~XO6aI8)JC,jhu}~ _QXh?XPGX-IB3#Ax ad闓[j 7['ik.n1uFe&+PQk#_KnwMGȕ[/kӷ 'C*6zF 0.0$)q}3|ɵ\xBRNfMcat;p W'ݲfA挅ӏ['éŅ .E7}Y1Nq4Xx' ;SPCy24=@2d$IE+ßR`g4Vj~.{1θ%xD&*?o4"d_eWگo:g9~ޅYVT~7iw~T39\ck 2#$BaGqoŜb+GInPVvp|='Vb_gvJE ɿs f`#y?kl uIXP,g Nc-" jnPw@1 71p"_ʈ)x hS8L,SrAŢo@?jr>`apC'PAZj I.sT\R@HeR`Z)A6KbXy-0Ta]Fj6`jlEJ:$"q\ӺzfFbL4,h$zcNtE4j |X$͔I\Ro6MǸ7S"@{,yf-~M0ݳͥJz2> stream xڬctf]&;ݩmv*c۶Tl۶mv}8_c5q5&%S7m$bm@COKZvWfQ02q3Ñ ;8F!# hmj41u++RPQQoX8YXXY9vT428FAY9uqQ2@N hYQ> fO12u3r10GE 1}&vV{` ZX8_kaWLhUNHy:;W 6kihmOIuZ\`w/_i8L3j_t?/X_V31-=ߘc~3+Vz mCdd337pv@2 B -ha!owcHY4v?}KZ;߶[BP`` 0ַ۳ɕ ,VFW[4ttEd 40 kWD%e7 _rAonikyF@NCa`{& / $6H^ɵ ygdO?Gn {qaI9O\ >!_X2T+Q╝p IS/ LAGK:_G1P N)IԴ/-p/GES7?QT5b TؐSU¹Xj U&D9xpwT(d ri"m͂("_Jxw%mc N@B4DuT9K^2mXrDgy%4uivt3!MK u^KDx1%j}  ?.hHJ cMO<^1a9*&"#E TJ>28Tpick> [E^zX|SK(s^i '#كprJ_ɮ XpnpOR)вKYrXK1m>lP4! [d(!?fm$E)AU!jH\6-V[ )TllTRcoNGD~,"_d#P[7 kW 0Gf 1N&EhɊ^ɴzfcFs.L3ߓ+lS :"u"u| t|[1/B!4֝#FYJcn}bHO ;lrr)!X-:q[w4ԝaZNBT-6CVD`H*f[CRu,%$EV~5DY|2=+ Wgѳ P n{F SZ4pP3$Ot ֥6(QHCgQ qݎ : jy]@+.w bf_%IN-ᇮ]uWtģ%Rs98m!! ]F'[wn)`Fusޘa ٳkt ,I^D[(M`gY6@kԴmcn{e0Y"O`XݕRe~!?]`T2 D3[Zn[d08x=YxNG)u[A_߭rŲ.( ܿ(gJRơ&Fr ^S<=$z╻l X]1s;P)oqNȻ%#]wKdLt{iT@RU_~0ՒϻJ_Ìwkq+􆂞YalPNɖd^v ErǼxC-s/-ϙ:}%`tig?ż+&tǥ0A$ ByB;5?w |SOY/f9kvsWmr,c!CG`\uAϵ&- `JAtY"DiMPYQnm_g@4;Z VjMC}͐ɏb |p5CFpu~imD 9.9i32hR:$9tHJ2ϜWeE?͗XRt˰Ǟ˪pwp]Yix+(yLG|eaڜIJ69[CSsn$֨+z: LQ_C?(M?Q1`3Ȑ?z2ť55 v^]zShls$2>ڜf^s[,IXBa xX1g΃tIcG K&]׬ZTaM\cv]*ЗZ | s\wաjnQr9A-d_^GۮkSQ%wu@A~[%dJW~4.x' STg6YxWK}|CX V=#(Qϝ? QN]s_f#-? .ͮȑCz"舞H-g.ˆ2cǛˋ&uGK%4Nhtn5=R<%!G7p&`PjE MAJ܏Rwvo24 +Wb1t BrS'JJ!x,kPbg+f|Iyi~V W4ٲp 'JzZX |Ee.#. 9qʶ @g,5 GOxCk~B$eƳ]_} d~$h1U}2g!Ã-Φe!Tm4q:`jY _`ใf> tK/] *z/yLo_@ha}-@R@SPA뫐[61iddq . QF8005&"0CԪT4[^k"jlGz 6+ݢ e+'&M-TXi7'RJ2 7weM~Q"#=$A5ZU"G 2~g1 |aic+b$jrHej& MLǪBT,>.D|HZMCsq)=8_M# {>kq{{Suh-f:q:VY <-ZoSErІJ /~{TRie^wtIm k NgzHKfKsv%-DE@i=t09bG^af-gfx]q5)&i ?#*|2w-&^s0 Z8}%jgt->-|=KTY9ZY3y |-,xi4fw"uҚc]cv.=AC}8rjӖu?c.$IE! (%KB05cJɪ*a[ʥk ;i\Kd4$9ҟ+Yx)]Сނ+[z%yO]2wG^QM/;ޖ>8,e]Yu.ֈpEI7ŽZpB c&א'[iTnU5eS2]`L|5Hf|>cX㱞Q4b!QJ0vL2÷;/m0O"PDշ PqKۄ_`%I\Xok>{M,7u 'j;Xj5lb ,(G1~)򆂌s`;5\9md$ckO@ˀTp*1ڗl;C =DFW 0dpj6y=Ff|Z>=B ؟0E]2?8! ˏAõ /6YAXF"Hl3d ,6Etҩ3DK6JdӐ@' Fc!N v \;} gXLJB&Ӛ?[HʞqvW: qqs(ٌXJ,s,^m~x#YQ~v-[Bi~ *`&V0F%퓹wTBj҇DӹJ=)CG f6'bA!X`F6}8a2i5Y"z^\!eo6Yrt8PD p*Hrz2 h@wH4o(luNۆU6;d~u̺+,vmB^p:%R۫_{~kjKcf6d[9#Tt+b"Mφ;c: P]5Uru]I/qnrU>O?2niHcR+P>~n"o'ƿ- gKcWD=$(E<Dɋ\S#Ky\вd3ՔywU=?YP,A81$Ȟ-;6Ybwi(M#&] kM8(f Yv,443~ aS硱ӄ;q3^KE1I~ͻx:)Z4s:K1Nx|g~12LHc}YSlFn[u~V WLnݭBn)hq`/] Z~='W\ ?jY<__k&U%I趹h{#ٖ.1Fy%UOW,Po Q㧎@8YЊ0I,vߪ'k&4Q֕~Ѳ+AVq mp-бҢAS_I1}п,k*Ʀm;0% Hd Μ+-ۺ6,qn?g#cQqux$yNGZ@uQ;Ʊqx"󏆗.p}cY-1ȏYGY GcR6êza|~g +L9 -s_jL͔Ć}(3f'ئп~e>hTkG`Я`ǙWkinBiJV; K0j?F馼g#gΟv۝ިFivb ;!R3G-v6z|k; uI_yyHi%tJQ~ j mnr  ě `lpVnYeqM/TxқAkԫaT+_m ώKE(]C|( $Kua:ߋ`[82`c >N16j=SObWU6 KhѢeR(Å?})~ L9 @r ٤H :JLU?)va=}п:7$d"SVCT\b WK$@*D7 rl&ɓf /ҭ j?.F؅ƟFU&X%aGlW8\IA!c zĻV`Sn/:Hʒw/jGI|3rxdDz)=tz4pl- UQ"7Xq[~Ė"5::ŝRdʉ#* !pVZx }ih2M' YED6y_tWwC|ל.ӭ.N{w$`򜌕gNyW+xS i1]wݗ|NGBn]׏ Byu?Ƅ4ru({ Tԓ^IĿ7IklYď/WTj^Y0&>ΔFtmA&(ǢzzTݴfƒ3mD3Dq8 8Hq\!]OJ/6⃩vxs(a)~,)]iٔVARbCw XY1K'fҊżE?2h6RP ^,J%.ґ;0,$ @-{^^]t8͎ۦr̈ B6J:=d?0ɶ჎ƨf-]&R1 _?3}eHZ-NXR/nur?}`S1s DSԯ $fO,f+iڧ1}J͜~'RX]S [el^7S i-ݳu0L-b ;D4V^`3B5aRI)$Y)j>&bea !Lg.+PB6Є0,l);T5 -2wPLZT.CtQ .K|0xI 5d8VD>\tfѴ1zUZ.~ 0L3ФfM=aOt+s2Y0G$٢8c'{\wU ( %{E9kshX4e7Df3-5Ԍ~:01rWV]2vjθϜЇUQan3|.S4VJG_N ֶnLO=v"wܰG=xC̏_W5XQlkVi -~IBC ]'Ķ}~L?.8"bpܻi?iՐ?m^.+@5"^sBި~=2ζ{d4"qHЈVޥcDS>鼏V4=yAKp΢gD7qv!Jd"gpGUZ\Vv4Tg 2{QCXvpRz/Jʜu/>]?r®De;Wv#--NG_w}EJ$c"E~ N'eIPW>;upv,ۜ-kuK .} WÉ Z{䂽o畀n }p0ݢl :+>gDx{FOVE!~Z@ hy,D6ٿ/5[OgGXO l;c^@~8+ /ʥT|࠶VHvKX7U80=6!p߭r)5Փ|i褯%&CrnǐbL-–^];_jCpd~ff5疏b_׀m:>:aˎ?aY RβJV!IHNY3+ƿjgPY+S6jo F쮰G:"'C;Eytp(.zb/ ݵ#4hn'8f y{2w4ͤO_:9f+F>HΨy*8TKhPPT-R B}KK_thz w9H|#cɺ/hJ͇r.@cDEu('S^oapʧ$'"M5glm_c|:I Xv/]yOT+4fYs$_$P҄mwlc[>#ԉr!xhJ 9`Mc-O$>!mO)ʸHZfcjOgW}#)Qi >Q'zss]dq0izג-4}]rcwa.b/Rm\ '=Ac !J %CxqQȃ?f,˛T j6fA5gfz-.=aVR, _l0jPng;)jL}1HYXjN+M%B( \XcZƇ_rEeO U cIAɰ\w~ rP _khެ^N`O񆼈bߚUx5cj(EPG''y3T֠`0-fK@beA.yېǨ hn~3C" y d!tQKZTVS&$D91te th:,r/]71}1X0:hc~.m+\!4xr>1E|R߂ǜ& *%5oԶ9c-p]5tsdzLS8yo[[:M` Q_hς6d^ 89 1O+.r:U2u̸6(;)t\q8 oiVWdf_sLlY) Տ!us :*<0`%婾?S(5'@lAzq(*8"H_lb0r$[tq=C.̓CD[0gcKَ&5=42?GM`+̬D;`>ʀ҄.YMg +m툓u[x% 6 r~]%׉6+#Tsm8 m)S&$<)NcSO5Qӌf&T^W~qT̛W8؄iE6xK֖#sAPQeIk5`nHW$5zQu#nF<\lNS#+*ޘSMIg܂V_*MF6i_X<ێlS 籥`׈ ΆoΕ;gg!H9#v;32c}ո}e8JPW?P+jp;(` -gL=bȅ㺊_KH Ҋ /ZJ;Z,VP\F9U"ĴdҮ҂sZ&ETٴĚxY(q}z|2%Q3% *cIZe#5BVFf<qaZ#-q L{X{zBʅzԄ1cU0]i6UqۄRDy+ȱcjWkb2 dYK~!7u6rsIĆpG~c$YT`bO/dLFqaMRqOd_?l&}GUw*dB'Z;T7킚m15Y+W*LnI<`HNaZ6ȳi.@(^7AIL6Md}r,W^ m0(f "Į<[b6-"{1I1 )Wdֲ9β.,SL3<0=iT-+hO:}]byZ6X |l{9\+S7d0@ZL%X:&MdksXF-zUv}fIzb ;p,X8K<#{[q]|Gk\ft(Dv18bC&y%h8G!XET ̆37 ;s 2t'"_UvwqžWP9[RA?^jS3 򂷾L@~bh$x\1=驠0!`0Ki#)?* '(;DT{Atn8׺F~5w6K^(hhngwIhrr|_)x)t[FPXM[P( 8w3+w|jC JIIdMۀüʩЪUb8  zs.+bԜ}*F p%n"1͸WaԱكI$@#;k`lw[_V䞱}J=1#z[0g C_rRr~fG1dc *ܱEjh^}<} ^&?yЦm#, 43p } 렳n".u(l$x۠$kEZJg9eS/J#! XeDCduF"d eEkإ~Mi*fΔm Feq^ЀJMF0$[÷8j(fӀ(8|upJ/SS-Ya&_ڲk"Tf1;vf_4!*uC. &tր>mBZ MlkLĻ\7)U4d˰/i'd2%*3зRȖ[%2m@_x5z!P =N'5Sf6 b'g%Os돺Y~,S l""e)TA&6tDcy~2?5dSe7FW_a$,4c:G06w߭Y.ZZ&[lZRv S^)2 Xm3Q26$ӻ5ɏSkn[Ӛ}FX .-䖻$Fe ȋ?e5'tQ{A%/1ov #m.!?<[9ݣ=62pK`9?E7>f1A Q(N#E -oVp+NeD T@JEACրՌT0ѮQcnoLANu@v<%<龘<1F!b}Qzxk'BӞ/X6!Ai)|pQ xu zMؠ4'q?}B!x&O53|){tVrvV"l-_BԞMlC [4H(rW\H|d>p#LHJ r fZOM AZ *i$7:ou H`bc"]9Y؟= =|NrcPNswFѡŹ%#f!~=Q"`X=ϕ=&_K^XT[tTh4hU!)Xx@.D!_YWU..xbڕXvTbJwou*k G 9vțma:$;MpLbfڃ595} ojn9{N%9Bv݂\JMf; 0^*$]u" M5c5.Cu Mnpepw>6RnZ(|6 pAPJڱsTw2tRacMW|'^?4)0D;K?xɋ8КŨl_T\ B=/HYۣNz+8NdV`oP0ԫ o.b5ͮ,렝HZB4V~PW)O Qyq&$uza:TX᮪ t!OAVBkZCI~G[ScFߡ +}oIGvrY ]mhxQFRESz9y?9kt\*/:>OA )^Ep'MV 8ƮLǵ熈ǴX-`etf뛖ua~?-|d8KrS)b#esXV Jߢ#F=RUDV0!<"D/cG9bW^5&;-r[5ďQ1Ei:r}e`/^{&jݶ| |10ZShL'X[F#H?rNvϷXeĥ6o#T==n6wQ-PH *p:ðK*m@''6I-t(呾exixNs>} :rJwfU(> ,QL}j)`~y]wɵjzE@gpS1@jmY~k/50c=ԣpceL7I-iDuV%9&G47vx*z(0Xܻn|dM)j_,zZ qVl$f?qnc kR@~dx Λzw/:Zyބ{tR; 's q U25L_JWsfԜAB~ZqU-)E iij_$`iuwJiG붰 $d/ G5['|BN*vzStgQgDO(ph: W6}U}ꔿN i~-\ȴ+AOR} endstream endobj 1530 0 obj << /Length1 1647 /Length2 7493 /Length3 0 /Length 8337 /Filter /FlateDecode >> stream xڭWeT\ݒ NpiC ݂CCwn!<BpѠ%Hp' C{o73fޏ{=O:Nn&: mN)P qrVnZPGu*M fx0d\00"k 6Y5+,,:y@0>;;ǿ,<~;K `Pbf;G-OBN.#jvj' d ;mnK=<0K0z~DzlNɜ\psChgbtu}y}:_trr 9a@[.L^ǘְv`&~QB<mܜ]a{L qm1աǐ\> [KwspPt|l qXB =l,]Z:<7r+Q!N^.`WyF l-]btqC"9G'a: =A@kxOʆ˴Y+`:N@FWsJZp <^(_k5K ` x|o4rk>҆YBl[߰ˣcX@sv j-:95VE3$k΋TX_mKY. otCF_;K["0ʇ#`Y}Y!Nʞ~Ied#A!M-[Tf~l 9ČNIQO[kV1m_^t|m;FAɞ$jI꛰Kp9GqrzErO|~;~\x~D:ɵfTgH%U'!*H#x\HU!,#|ѳ-pcm~w̐ +V*qJ)vk$ܚ3ƌ:؜ V%j^1~Xn9:\17ޏlx&z~3zf#ewdxi}4a|  \@v*"kYQ꣗i#rrO8dJ Tznik8LǑ4/CY}%$&r|3ɴPioK M?.IL apl(֏<+e*[G(qZv|9>LF{Z41]ڈbaFSm$<#!,D|= P4% sCR\rf)[V)^9ϵ ,y ;ؐj*7Do7lB2 a9'9}e?Y_zYadg 7[d\J3tgڐ˗{0Fh7-ԓP#EO7}ԧݦ EaK 5Y@q5dR-࢕"vfԅӅ_׶}4,8C q?$N|^i%p gp{@\,ȾɌ;JVI GIhP,s's}jrF2iJ"1[Wt{U!<ڹ7:MZ%qqPUePo$Nd@^v1?49j#=L>][PCÐ?hBR_H3ᴓa~k4[OY49^`#"tE(a&Y;ʉ)55zx( J#}݄VZ z)Z؅{W;Wj"%7iQ0(dE:H4n.RH3jPN*mFEUvˮ~ίw}}]6+66[WfU%S9(*^vFpo"+,$!x#Ȕm/!]mYPe6&Hn~8WsFW Qۂ0hrqj'TRǓ\- j IEoÑ'|ɧpÛ;żF7G*LЯl,8~Ԏ%'U^8>QL7L`rj敎֩#$.Wc;;r'*iz3hg21T)'_c2cANx^t͙e#y7Ebf^[+W)%5E9ru;H9[fFm ULsfL,m|zp(AK}ȕ@؁8T.CD@AVrG_h$ 44M~M NP/?8< r~::stq$Ӌ}O\tQu_Fz#U)mh) /hZ1&D`ce@7$_\u+m]Kuy2RNw(w HŐQo;c?69h@\27+#fZF˝ _h} 1n? };w,uLuh*}8'iyoGБ/&&y m+bK("?U0Leh5Pdӹ$fA u*;qNSܪ^kZu£><-["eJxlMad*2v)K&n3ho1դͲ$~p>,`Mdix5sOtAM=7Ofiؗ)Qu>68)'mF5;]̲sʆ[P4W9b0fZ{jB`1Z aơEvPF3;.ooaP_FAˇ͕[9hbBouRI xw/s%ΩXt F{ohb""Nց/ \\k#sHNz+[1`O;2,|^9 pgpmdi[*9{ j 2>6߇X4GRw8;Jc36D[ښNY4.as7LpxF_l6\CAIo5M=HKD-8c 6;ҠOa2& N$؃54dl dJFYne1ueRTԈp4+նxȵ2. ELb4e2oфBtx)zo#k7$xϡ8]`+*!>p$2l٬'i1az穃qp ӅtD?r|${_q-ƁJjc:6L?O0]M#&Ca+~_R^0I;b`#M?~ Urwdi`KQ-fE+6:S.#)`4U< W68{ENPht  @ia}Z;󰢒}čXczN/9Ey+Fx;LEءTv9^"뼫ѭK9ywR{,7t-ŔE,&ek[yx&ʹ ԧWLeX,HUa$oAkDX}`W_ygylfݢ!߱-3j:fYjނ΢ahTNIZg/QτeH4O~r_eYT\1X)bTYZk:|ؠ%v)NWsmdAXCͩvh3}A ~)3+ {K(;Ulg8I\C4&rg4[AʆC-QhQ@D . t,MKv\ӌp~ ķ̴/O4O]n.Ybk/PьފiŋRIn~)@񢬊'.06rj |y`;SͫsRnWYioR#TQ5Ƿ2#kBGd+v?gǽ/0`\>#-a,L&BPGNK^RK?C-Da>lIcD U*x\^n m(8*|x]M`CFIƔzf2\lcE"I/=ϿUװ vp8~ݮ+'GήpcU7w;HȋS/m[(`/go0P\uҝ;#6md(Fɣ\v ]=*I'LJřR&_)fj̏:VUkV9 )JܛXJZ5%ҊEDT^q]^֐vB{бW@D^Y*F5OBSDȩ-vG/Cwߑ+fi:wLL|2~ y2:}u^ Yo?:\n3!gT&(.y:boO)Q^o1edznu؜bĄVD9Iic}r~xs CCd JԾi>_YIKx0gl;S3A= j}oto@:QyxG!vr=׶%} {rTRFT>R"^%/u6jDV\f>jLL}IפӍBe$nΝ& COY ,A/srʋR@h2KOp71I R-UPqTJ~8.g5˕KbC>VD}^\a;au[ Qxl<zI^7;RݰJ\oPpM. kekUR0^=ojk0;;WSƻ͡IeCA.u)KDSɑ NTP.SȇgFȂG[#YG-%%n%ngj5_\o4/P}3߂#j$ Z n >_XLlkouˤwe^ fM $א2=ͼ"* ԑ>}=$iK4^vΆ}g'YMyyUwk 8JAoZFFamN{EY=6VyBdi7;j6miA**tZ wՈ"\s[cLL{`8Bʳo_ZP6`&>qϾf׸D5l"V<i) `@cO}xprWid&p:@w2 BuzȞùuh,H{BgÛZ9:Vp 6뺱E˚^5*=ZNudA3WS4#Q5U<${'ԵVTv YÑ .)X+o@֏7BCv%y?-z\@>4~YɈR <8PJLtOM=?^ȠUZ| UUmJOB'qn,MGXzcݣ<9.R*a>[z8ͻ@NN\, nhhEʭ| Y9^\qJǐ?<)"Y  _?RΗ%"62((̫WTsXX#Mzq^~xXF.Jo%+Di sI>OIE^-@+#eY+f-kN#3 V.AV/ǫgrW뵹Kl1AUQxȏ)PbJLtIBe(fN>޽$A"8RsCZ$KpWZ1Ety)3'gwS&٘~|s{eK^^ SZ=TƠ ֲhL_[$i?x $-*$T#d)# )]<}R,#'C*L?w4йUVw{_Kfzڔ4~7}H/ "e!l?))<Ϧp *4%b'6ARM鏟9$6cN).uaE 7wM Gx_6X>%=]uU/ T+bͯ1H{Nw)=Ӽs;R9qSy{/.y, Fňs?g/L~Fr/uy@ *Ph~. $#h6-22Ok@ Nx\!4ӻ@ps*;T+j w݃3XXFu ˨;m? endstream endobj 1447 0 obj << /Type /ObjStm /N 100 /First 963 /Length 3121 /Filter /FlateDecode >> stream xZ[S~_1I`4WTRN[Aދ=H+ 8u^R4ӗn,bRj&%YeDxZ< 8Á"Lh¬jp'5#ܪ 8 jIZdP6DJ*H:sFTq V(sMth&[5 4'NrJ B"&C]FP18/%dˈe9HVԂuC-hjCM* =QLaBȡF;#1B*y A`(JZrܺ ^pR)"3W1s€=%ʏQ6s#@ '- Fr0<P 7 #Ԁ18%ya \XAx0VeH\4Hha`5Al30N G Y& >@=wTahJ ی3$j2 :ŅU@af UE#xYAb(F| (Wcp${&}b(g:C'hR'74JJ9WB}ZWN~/Ot16?~tX~'{eXwZ(WlQLdwyA7}abzQ"N ¶9YTpw1Cl3/kwV_ m=q^:/.fȇ8/|jyWw1C=yu_C׃+^!:mgx%x`g>1~y,xu8Wcg8>Gj5lu}+2;r8_p 8 Z?<h(R9 h 16 ]sz^&z k, hpjMf- W_ʝ1>,:rQ,Rc¼z(Fx"#(x'ܳh=zr(iKF. v2⁶Z#Z :R##x([r8G<E|Oٛ,{,D x-F#܋D{x(M9<'nL|Qu4[CĈ`#[YRv8\Өe}PLցXaT'Fr֥X $m*0;6+3q gfev[ʌ~ gPGuI^5]^OgE5 }|'Y&ŜH?cw!'[-QLw%t'r|q F(g[ >_bRq(F}X%!\h1U/ -zߌk{ov_kG\lYâVsMfîMWrwp|uTjr5?&8 N)zuӻ,6uΦ'w}N=zL=^K:_^)5.f!#9.g.~7G 2 dǯyBXvC0x&dy { )blnl>?v/+ 0|G#`Y\8bzF`>)N'F3z2+NKyTq\ԷajRMWWcʙK˯bB˛Iq䝏r4^6?/)uRxZ_ &zӯ벜բ<;A1/y 7;YY g9=լQDLfB0rH@PgwM i8:8 'Ɠ!L7R`9MVgMNTE 6(whwR>Z-Fg4J$|&ɇè.hHNfލC+5]o={=ՇAq!o`~)ȷ woog58-/ظAO&/WNg=f]>i/^N0*7J銦^Ϟytu + iX#YorOIL@dMUNߪ{W~x {-k)E 2*7#Iٱ'8i[Wi1?xzK洜Oܑ7 c3YVl6Hz:bߍׄIs[e/IW 1> stream xڕZK7WsCT*[f-qqښi"x In% E>R@:.uZ_0WX'$-qI04t$y3: ¹)F`\2gh ܐů %胸 |#\К5R0Do4~8h:`PB8#<%qdIN@Ji;vmCB?pHw3ф,P%hgi*ȈF,~!1!3b:aT@O KBp!@O%fp;-!1ɡ'ѹFI =)4ҝTbZXN8'%XhS^bNHuCO*=CG(ɉzR)ozR):x@O*-q82 \66(Z8$$'9FUf4>|0a~ Tז[ )Tdѽ[e^YMȭs2Zq4MȊi/2L(?DX>!W6H^ν5D|%'Rhq#,FQǧY65݊l59Y}s7eKiq~ ~}Ndz|_װw?**R"Nϝ9hKPXJWd "vP'8m eA%b!pউ [6(H$fN9Y)L'r0 SJ$w_ G2gqpF Ui>pEUN7 guAo?ºJamG煪GDfh.*For-#,ѨgjSc Ne rD Aj"$yQGj'*?B' 5*5L:,]f,¸u r*,,dgedQ `vQE%`?-<T3d?Ff$ad01d"D!dX^ᐨ˪Dhh2I4ay'/tj#HDY"f $K-#@%%d 3yB% Y,$KFfZ$-9sۘ@G!r$xNL' :?,O ^> stream xڅ\MWq0b&Xa=edC^zdfFL&YJQq)y񐎜`G?<#ax#x?;[GgS;Ra )E>ڴg 4L>HRH1 $+a"1Y>T%fL Aʴj{7!.#r]BTjj,_Z h:&f n9IRYKycXҸᐳ]+2.e@KP/ 3-ӦioG0]!`4,dT/e222-ymsj\IdPB vJQB A& UV!\56ă~2Z$;`T%΅z*~imY]U:JUd]V:YenReUа%ea6>F A A^%'$sU$HTi+ Pꀲfvۘg'Y$92$92$9 K^N>z$@R$HJ$IIlRMms$KdR,9XJ%&-L7um<%yXA,`9W۵RW[Ěm͂Y%%c+DFl,yXˢh|Ҟ -, fa )0b  L%}Ru T"@JzЈ"@^i+iMm샻t7ݤlQ0VEIgWM r1mgfl1"El"kcXWpSaOnJ- c} p;;ƿDQiqK]|5AN-/RKX5(bfI3V [7Ellnȋؾܐmy[6Elltؾj x4 !BZJ]d\pSng Bj&cbd8^SB:> d?y'ܩbVJ7Xq(bgP֫w\ŖzEiOYq(jm(3dPfȪա̐m]׫@P$Z" .IW inUncvek}R,aWVd BU AJ2mCT*ca4*HmTp**@5m{xu;*&ݧ*r@UBPPW[.:vUFsUۗʁWM3Sz= bc_ }`LSNhHB$/&[淫00 2cU.ˌOM*[mtV69qn٤*m80VHbYEZ *.I,Vp2iUǬV X%cū -/m!*gFUZrh5 {*x2M?&2]*':F: Q R'>yk˳EliϠrzZTڈ?}-Fr$V^\R)41OE>y(3ĉMH# O7R%J[l#w_Zyh $_]Iߊ sssece lJ#Ie* UA4.OyP\)|zX咖3bIbN&>SXvlƼ5Zv+:BGl0eB,u*{CƼ4NHP_'*wUVԯsrcA{ԭǴ!dJnzЈSa3New3LW3JԤ *q( *3,,˷Ҽ6cJ|1ĩ@Ʉ86CѦ/Uצ/r&mE]6PYDM"Tr&"9biO'ut"xwE?.v=. ڌ(3cӊz98j\9f\A7shc 'Ҿ$3I3#GHw@=Qf=EDϫrܳB1>FxDO. {Iן?ϯ_ayEӼ/}O痟~XO?/G5wXu}#igoV7|%.*Oעw :op m~op9 ..e \?_b[ /Go N҃ŵ^믴^H!*DmoxF |ۣ H1WZ5gZ5\sc>7`:W`s-~vw:=nX|cAJ3h&>ium{4c/#/xQ/ۯ?A?ɿF6o>BROz}e~o6K% am"D޲w=o/?}גzfS=+J?.M@{~#c=nҝ=_xY?e5] endstream endobj 1717 0 obj << /Author(Tim Bacon and Stefan Bodewig)/Title(XMLUnit Java User's Guide)/Subject()/Creator(DBLaTeX-0.3.4-3)/Producer(pdfTeX-1.40.14)/Keywords() /CreationDate (D:20141231160108+01'00') /ModDate (D:20141231160108+01'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.1415926-2.5-1.40.14 (TeX Live 2013/Debian) kpathsea version 6.1.1) >> endobj 1670 0 obj << /Type /ObjStm /N 47 /First 463 /Length 1738 /Filter /FlateDecode >> stream xڝYMo7W"sH Am>6 9*ˆ>Zw'HZrmbfޛp%CVQV;1:(WɒAE? (+s. %%ɖ8p6Yec.>F,+(素%Rλr]T.K cr1&Z-aZGol9mɒ.U&CV9*/J'#rV>pf*hǸQdڪ`˩8[^/ڢ*4uE-%+Vf+[0m:'XE.C+ D"; [۫u}3_N$+Zvb+~Pswrv6}?ojN|;lIqB9ʼ~Mϔys Lٸq1q1Qr% +] X JIW^aTP<zzqzz(r% h J@0ȍ wH֌ B|Ћl *MQWC½%$STXW +X*dP1@Q( ZA*d 11*=–6ՐTZA|+(22w ɮF]*VPzihR+(]5q~Rs*Owuet<)^phKn/bNxɟ˰WBպE 7ݟb̗׮--ō#GS]uk' #4Dh(l'+6NI J@+1Ⱦaz2ASM %V܂^B" E`e6_<4Z(Pȡ|5DEaF-O?Hiœ&IJ~l)&T( dJ6X=K0? ''0? hld9νzKDЁIG@}0,0n "jIYߑ6؃B&.EC04)}08a-;3%0_ R~A aNSF%0i 0i)X#s2a&mĤڼlFĜ7[0F|Fh턨(jL39 P{׭ye%sNM67~V_4wտ\ߑgY~Yw8Ϯbq{cbEMyؔr9x endstream endobj 1718 0 obj << /Type /XRef /Index [0 1719] /Size 1719 /W [1 3 1] /Root 1716 0 R /Info 1717 0 R /ID [ ] /Length 3762 /Filter /FlateDecode >> stream x%9%U{j7=KڳLtd; K TDHTlK%lD@_=w{˲,(FYn͌L,[h-%E.\ RnŹ_\6Z K`)L2X+` +a5zal-v]^aA8GplxC^ \;Oyγt ·yhjrX+a5 {` pkmԯ5py'aNi8g6g! \koM܅{pCx g_ǫv L{Ur o5Lr*X`+lvؗ3#pv;)NawjmO\vSJ)NwjS,Th7NY1 tNwʾۡc;ߩNwA*:)NvR63w>꼛KAg),7W=TOz:өNt((Nt S8INϢp:)Nt S8Nӟ');o SGsP3KPVU:eIN:vұtc';ؕ>Ϳ F0rX`1,0`9y8nzVDZXalMVavn{ai8MY8 s.E \kpnM܅{p#x O<^kxo/3Ŭ}L ķ–v +anO_&τEʾ#B[[[[[[[[[[[[[9[[[[[[[[[[[[[[[[[%2V~xz[[[[[[VRAd` ;J0 X SoRe6xM\UlSlS[a6mvnk퇃`4e3e35øa8Gf$[Y׌bv8 ^z]tG>e薄WӪwI/zUK^Lvd=ݽmYy/#zEܳ+؞^tttttttt[S-^r*789!''''''''''缧g*$$$$&ҁ㼧ޅ^ 'טftz&B45rX`1,0`9S~a Y8Rzd-6& [`+l `7> 0G(Sp8҇&,f/% W*\- ]1faJ76r%g_x|O= M+BɔX1XqYXS)}/V+V+V+V+q5 hhhhhhhhhhhhhhhhhhhh4N(I-|EmEmXB\2ҟ^gҳW*++++++++++j`)'ALW$WVN*+G Ӄ G1WY"@@@@@@@@@@@@@@@@@@@@@@@@@@@`D45٘F=~Fc`C^[   oL{3?y搲qzٜ9fsNa 4h. u6|4W1K eI!?c%`[sGL' a<#,imIn %RtBFwJoNrF( w˜Λ8d$aFF|qF~aacߝ'@.<` R3Z˅:  UB<^ k:NAXmMBͰEh+ln3/;[v .h{= #7 txcp\b|Ro> NC|LYHSt#Ƒ!bH))nB)jB")yw)+cy F!R>2%Dn"#B#{`ߌ>W_~6u&~]邋kQwqC\{:$ׯ=Fm^jsUX3ff~Ԕ^~ג6,eNT-jSW˿ZN@6ϵ̮e{|_3.Comparing Pieces of XML

3.Comparing Pieces of XML

3.1.The Difference Engine

At the center of XMLUnit's support for comparisons is the DifferenceEngine class. In practice you rarely deal with it directly but rather use it via instances of Diff or DetailedDiff classes (see Section3.5, “Diff and DetailedDiff).

The DifferenceEngine walks two trees of DOM Nodes, the control and the test tree, and compares the nodes. Whenever it detects a difference, it sends a message to a configured DifferenceListener (see Section3.3, “DifferenceListener) and asks a ComparisonController (see Section3.2, “ComparisonController) whether the current comparison should be halted.

In some cases the order of elements in two pieces of XML may not be significant. If this is true, the DifferenceEngine needs help to determine which Elements to compare. This is the job of an ElementQualifier (see Section3.4, “ElementQualifier).

The types of differences DifferenceEngine can detect are enumerated in the DifferenceConstants interface and represented by instances of the Difference class.

A Difference can be recoverable; recoverable Differences make the Diff class consider two pieces of XML similar while non-recoverable Differences render the two pieces different.

The types of Differences that are currently detected are listed in Table1, “Document level Differences detected by DifferenceEngine to Table4, “Other Differences detected by DifferenceEngine (the first two columns refer to the DifferenceConstants class).

Table1.Document level Differences detected by DifferenceEngine

IDConstantrecoverableDescription
HAS_DOCTYPE_DECLARATION_IDHAS_DOCTYPE_DECLARATIONtrueOne piece of XML has a DOCTYPE declaration while the other one has not.
DOCTYPE_NAME_IDDOCTYPE_NAMEfalseBoth pieces of XML contain a DOCTYPE declaration but the declarations specify different names for the root element.
DOCTYPE_PUBLIC_ID_IDDOCTYPE_PUBLIC_IDfalseBoth pieces of XML contain a DOCTYPE declaration but the declarations specify different PUBLIC identifiers.
DOCTYPE_SYSTEM_ID_IDDOCTYPE_SYSTEM_IDtrueBoth pieces of XML contain a DOCTYPE declaration but the declarations specify different SYSTEM identifiers.
NODE_TYPE_IDNODE_TYPEfalseThe test piece of XML contains a different type of node than was expected. This type of difference will also occur if either the root control or test Node is null while the other is not.
NAMESPACE_PREFIX_IDNAMESPACE_PREFIXtrueTwo nodes use different prefixes for the same XML Namespace URI in the two pieces of XML.
NAMESPACE_URI_IDNAMESPACE_URIfalseTwo nodes in the two pieces of XML share the same local name but use different XML Namespace URIs.
SCHEMA_LOCATION_IDSCHEMA_LOCATIONtrueTwo nodes have different values for the schemaLocation attribute of the XMLSchema-Instance namespace. The attribute could be present on only one of the two nodes.
NO_NAMESPACE_SCHEMA_LOCATION_IDNO_NAMESPACE_SCHEMA_LOCATIONtrueTwo nodes have different values for the noNamespaceSchemaLocation attribute of the XMLSchema-Instance namespace. The attribute could be present on only one of the two nodes.

Table2.Element level Differences detected by DifferenceEngine

IDConstantrecoverableDescription
ELEMENT_TAG_NAME_IDELEMENT_TAG_NAMEfalseThe two pieces of XML contain elements with different tag names.
ELEMENT_NUM_ATTRIBUTES_IDELEMENT_NUM_ATTRIBUTESfalseThe two pieces of XML contain a common element, but the number of attributes on the element is different.
HAS_CHILD_NODES_IDHAS_CHILD_NODESfalseAn element in one piece of XML has child nodes while the corresponding one in the other has not.
CHILD_NODELIST_LENGTH_IDCHILD_NODELIST_LENGTHfalseTwo elements in the two pieces of XML differ by their number of child nodes.
CHILD_NODELIST_SEQUENCE_IDCHILD_NODELIST_SEQUENCEtrueTwo elements in the two pieces of XML contain the same child nodes but in a different order.
CHILD_NODE_NOT_FOUND_IDCHILD_NODE_NOT_FOUNDfalseA child node in one piece of XML couldn't be matched against any other node of the other piece.
ATTR_SEQUENCE_IDATTR_SEQUENCEtrueThe attributes on an element appear in different order[a] in the two pieces of XML.

[a] Note that the order of attributes is not significant in XML, different parsers may return attributes in a different order even if parsing the same XML document. There is an option to turn this check off - see Section3.8, “Configuration Options” - but it is on by default for backwards compatibility reasons


Table3.Attribute level Differences detected by DifferenceEngine

IDConstantrecoverableDescription
ATTR_VALUE_EXPLICITLY_SPECIFIED_IDATTR_VALUE_EXPLICITLY_SPECIFIEDtrueAn attribute that has a default value according to the content model of the element in question has been specified explicitly in one piece of XML but not in the other.[a]
ATTR_NAME_NOT_FOUND_IDATTR_NAME_NOT_FOUNDfalseOne piece of XML contains an attribute on an element that is missing in the other.
ATTR_VALUE_IDATTR_VALUEfalseThe value of an element's attribute is different in the two pieces of XML.

[a] In order for this difference to be detected the parser must have been in validating mode when the piece of XML was parsed and the DTD or XML Schema must have been available.


Table4.Other Differences detected by DifferenceEngine

IDConstantrecoverableDescription
COMMENT_VALUE_IDCOMMENT_VALUEfalseThe content of two comments is different in the two pieces of XML.
PROCESSING_INSTRUCTION_TARGET_IDPROCESSING_INSTRUCTION_TARGETfalseThe target of two processing instructions is different in the two pieces of XML.
PROCESSING_INSTRUCTION_DATA_IDPROCESSING_INSTRUCTION_DATAfalseThe data of two processing instructions is different in the two pieces of XML.
CDATA_VALUE_IDCDATA_VALUEfalseThe content of two CDATA sections is different in the two pieces of XML.
TEXT_VALUE_IDTEXT_VALUEfalseThe value of two texts is different in the two pieces of XML.

Note that some of the differences listed may be ignored by the DifferenceEngine if certain configuration options have been specified. See Section3.8, “Configuration Options” for details.

DifferenceEngine passes differences found around as instances of the Difference class. In addition to the type of of difference this class also holds information on the nodes that have been found to be different. The nodes are described by NodeDetail instances that encapsulate the DOM Node instance as well as the XPath expression that locates the Node inside the given piece of XML. NodeDetail also contains a "value" that provides more information on the actual values that have been found to be different, the concrete interpretation depends on the type of difference as can be seen in Table5, “Contents of NodeDetail.getValue() for Differences”.

Table5.Contents of NodeDetail.getValue() for Differences

Difference.getId()NodeDetail.getValue()
HAS_DOCTYPE_DECLARATION_ID"not null" if the document has a DOCTYPE declaration, "null" otherwise.
DOCTYPE_NAME_IDThe name of the root element.
DOCTYPE_PUBLIC_IDThe PUBLIC identifier.
DOCTYPE_SYSTEM_IDThe SYSTEM identifier.
NODE_TYPE_IDIf one node was absent: "not null" if the node exists, "null" otherwise. If the node types differ the value will be a string-ified version of org.w3c.dom.Node.getNodeType().
NAMESPACE_PREFIX_IDThe Namespace prefix.
NAMESPACE_URI_IDThe Namespace URI.
SCHEMA_LOCATION_IDThe attribute's value or "[attribute absent]" if it has not been specified.
NO_NAMESPACE_SCHEMA_LOCATION_IDThe attribute's value or "[attribute absent]" if it has not been specified.
ELEMENT_TAG_NAME_IDThe tag name with any Namespace information stripped.
ELEMENT_NUM_ATTRIBUTES_IDThe number of attributes present turned into a String.
HAS_CHILD_NODES_ID"true" if the element has child nodes, "false" otherwise.
CHILD_NODELIST_LENGTH_IDThe number of child nodes present turned into a String.
CHILD_NODELIST_SEQUENCE_IDThe sequence number of this child node turned into a String.
CHILD_NODE_NOT_FOUND_IDThe name of the unmatched node or "null". If the node is an element inside an XML namespace the name will be Java5-QName-like {NS-URI}LOCAL-NAME - in all other cases it is the node's local name.
ATTR_SEQUENCE_IDThe attribute's name.
ATTR_VALUE_EXPLICITLY_SPECIFIED_ID"true" if the attribute has been specified, "false" otherwise.
ATTR_NAME_NOT_FOUND_IDThe attribute's name or "null". If the attribute belongs to an XML namespace the name will be Java5-QName-like {NS-URI}LOCAL-NAME - in all other cases it is the attribute's local name.
ATTR_VALUE_IDThe attribute's value.
COMMENT_VALUE_IDThe actual comment.
PROCESSING_INSTRUCTION_TARGET_IDThe processing instruction's target.
PROCESSING_INSTRUCTION_DATA_IDThe processing instruction's data.
CDATA_VALUE_IDThe content of the CDATA section.
TEXT_VALUE_IDThe actual text.

As said in the first paragraph you won't deal with DifferenceEngine directly in most cases. In cases where Diff or DetailedDiff don't provide what you need you'd create an instance of DifferenceEngine passing a ComparisonController in the constructor and invoke compare with your DOM trees to compare as well as a DifferenceListener and ElementQualifier. The listener will be called on any differences while the control method is executing.

Example16.Using DifferenceEngine Directly

class MyDifferenceListener implements DifferenceListener {
    private boolean calledFlag = false;
    public boolean called() { return calledFlag; }

    public int differenceFound(Difference difference) {
        calledFlag = true;
        return RETURN_ACCEPT_DIFFERENCE;
    }

    public void skippedComparison(Node control, Node test) {
    }
}

DifferenceEngine engine = new DifferenceEngine(myComparisonController);
MyDifferenceListener listener = new MyDifferenceListener();
engine.compare(controlNode, testNode, listener,
               myElementQualifier);
System.err.println("There have been "
                   + (listener.called() ? "" : "no ")
                   + "differences.");

3.2.ComparisonController

The ComparisonController's job is to decide whether a comparison should be halted after a difference has been found. Its interface is:

    /**
     * Determine whether a Difference that the listener has been notified of
     *  should halt further XML comparison. Default behaviour for a Diff
     *  instance is to halt if the Difference is not recoverable.
     * @see Difference#isRecoverable
     * @param afterDifference the last Difference passed to <code>differenceFound</code>
     * @return true to halt further comparison, false otherwise
     */
    boolean haltComparison(Difference afterDifference);

Whenever a difference has been detected by the DifferenceEngine the haltComparison method will be called immediately after the DifferenceListener has been informed of the difference. This is true no matter what type of Difference has been found or which value the DifferenceListener has returned.

The only implementations of ComparisonController that ship with XMLUnit are Diff and DetailedDiff, see Section3.5, “Diff and DetailedDiff for details about them.

A ComparisonController that halted the comparison on any non-recoverable difference could be implemented as:

Example17.A Simple ComparisonController

public class HaltOnNonRecoverable implements ComparisonController {
    public boolean haltComparison(Difference afterDifference) {
        return !afterDifference.isRecoverable();
    }
}

3.3.DifferenceListener

DifferenceListener contains two callback methods that are invoked by the DifferenceEngine when differences are detected:

    /**
     * Receive notification that 2 nodes are different.
     * @param difference a Difference instance as defined in {@link
     * DifferenceConstants DifferenceConstants} describing the cause
     * of the difference and containing the detail of the nodes that
     * differ
     * @return int one of the RETURN_... constants describing how this
     * difference was interpreted
     */
    int differenceFound(Difference difference);

    /**
     * Receive notification that a comparison between 2 nodes has been skipped
     *  because the node types are not comparable by the DifferenceEngine
     * @param control the control node being compared
     * @param test the test node being compared
     * @see DifferenceEngine
     */
    void skippedComparison(Node control, Node test);

differenceFound is invoked by DifferenceEngine as soon as a difference has been detected. The return value of that method is completely ignored by DifferenceEngine, it becomes important when used together with Diff, though (see Section3.5, “Diff and DetailedDiff). The return value should be one of the four constants defined in the the DifferenceListener interface:

    /** 
     * Standard return value for the <code>differenceFound</code> method.
     * Indicates that the <code>Difference</code> is interpreted as defined 
     * in {@link DifferenceConstants DifferenceConstants}.
     */
    int RETURN_ACCEPT_DIFFERENCE;
    /** 
     * Override return value for the <code>differenceFound</code> method.
     * Indicates that the nodes identified as being different should be 
     * interpreted as being identical.
     */
    int RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
    /** 
     * Override return value for the <code>differenceFound</code> method.
     * Indicates that the nodes identified as being different should be 
     * interpreted as being similar.
     */
    int RETURN_IGNORE_DIFFERENCE_NODES_SIMILAR;
    /** 
     * Override return value for the <code>differenceFound</code> method.
     * Indicates that the nodes identified as being similar should be 
     * interpreted as being different.
     */
    int RETURN_UPGRADE_DIFFERENCE_NODES_DIFFERENT = 3;

The skippedComparison method is invoked if the DifferenceEngine encounters two Nodes it cannot compare. Before invoking skippedComparison DifferenceEngine will have invoked differenceFound with a Difference of type NODE_TYPE.

A custom DifferenceListener that ignored any DOCTYPE related differences could be written as:

Example18.A DifferenceListener that Ignores DOCTYPE Differences

public class IgnoreDoctype implements DifferenceListener {
    private static final int[] IGNORE = new int[] {
        DifferenceConstants.HAS_DOCTYPE_DECLARATION_ID,
        DifferenceConstants.DOCTYPE_NAME_ID,
        DifferenceConstants.DOCTYPE_PUBLIC_ID_ID,
        DifferenceConstants.DOCTYPE_SYSTEM_ID_ID
    };

    static {
        Arrays.sort(IGNORE);
    }

    public int differenceFound(Difference difference) {
        return Arrays.binarySearch(IGNORE, difference.getId()) >= 0
            ? RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL
            : RETURN_ACCEPT_DIFFERENCE;
    }
    
    public void skippedComparison(Node control, Node test) {
    }
}

Apart from Diff and DetailedDiff XMLUnit ships with an additional implementation of DifferenceListener.

3.3.1.IgnoreTextAndAttributeValuesDifferenceListener

IgnoreTextAndAttributeValuesDifferenceListener doesn't do anything in skippedComparison. It "downgrades" Differences of type ATTR_VALUE, ATTR_VALUE_EXPLICITLY_SPECIFIED and TEXT_VALUE to recoverable differences.

This means if instances of IgnoreTextAndAttributeValuesDifferenceListener are used together with Diff then two pieces of XML will be considered similar if they have the same basic structure. They are not considered identical, though.

Note that the list of ignored differences doesn't cover all textual differences. You should configure XMLUnit to ignore comments and whitespace and to consider CDATA sections and text nodes to be the same (see Section3.8, “Configuration Options”) in order to cover COMMENT_VALUE and CDATA_VALUE as well.

3.4.ElementQualifier

When DifferenceEngine encounters a list of DOM Elements as children of another Element it will ask the configured ElementQualifier which Element of the control piece of XML should be compared to which of the test piece. Its contract is:

    /**
     * Determine whether two elements are comparable
     * @param control an Element from the control XML NodeList
     * @param test an Element from the test XML NodeList
     * @return true if the elements are comparable, false otherwise
     */
    boolean qualifyForComparison(Element control, Element test); 

For any given Element in the control piece of XML DifferenceEngine will cycle through the corresponding list of Elements in the test piece of XML until qualifyForComparison has returned true or the test document is exhausted.

When using DifferenceEngine or Diff it is completely legal to set the ElementQualifier to null. In this case any kind of Node is compared to the test Node that appears at the same position in the sequence.

Example19.Example Nodes for ElementQualifier (the comments are not part of the example)

<!-- control piece of XML -->
<parent>
  <child1/>                        <!-- control node 1 -->
  <child2/>                        <!-- control node 2 -->
  <child2 foo="bar">xyzzy</child2> <!-- control node 3 -->
  <child2 foo="baz"/>              <!-- control node 4 -->
</parent>

<!-- test piece of XML -->
<parent>
  <child2 foo="baz"/>              <!-- test node 1 -->
  <child1/>                        <!-- test node 2 -->
  <child2>xyzzy</child2>           <!-- test node 3 -->
  <child2 foo="bar"/>              <!-- test node 4 -->
</parent>

Taking Example19, “Example Nodes for ElementQualifier (the comments are not part of the example)” without any ElementQualifier DifferenceEngine will compare control node n to test node n for n between 1 and 4. In many cases this is exactly what is desired, but sometimes <a><b/><c/></a> should be similar to <a><c/><b/></a> because the order of elements doesn't matter - this is when you'd use a different ElementQualifier. XMLUnit ships with several implementations.

3.4.1.ElementNameQualifier

Only Elements with the same name - and Namespace URI if present - qualify.

In Example19, “Example Nodes for ElementQualifier (the comments are not part of the example)” this means control node 1 will be compared to test node 2. Then control node 2 will be compared to test node 3 because DifferenceEngine will start to search for the matching test Element at the second test node, the same sequence number the control node is at. Control node 3 is compared to test node 3 as well and control node 4 to test node 4.

3.4.2.ElementNameAndAttributeQualifier

Only Elements with the same name - and Namespace URI if present - as well as the same values for all attributes given in ElementNameAndAttributeQualifier's constructor qualify.

Let's say "foo" has been passed to ElementNameAndAttributeQualifier's constructor when looking at Example19, “Example Nodes for ElementQualifier (the comments are not part of the example)”. This again means control node 1 will be compared to test node 2 since they do have the same name and no value at all for attribute "foo". Then control node 2 will be compared to test node 3 - again, no value for "foo". Control node 3 is compared to test node 4 as they have the same value "bar". Finally control node 4 is compared to test node 1; here DifferenceEngine searches from the beginning of the test node list after test node 4 didn't match.

There are three constructors in ElementNameAndAttributeQualifier. The no-arg constructor creates an instance that compares all attributes while the others will compare a single attribute or a given subset of all attributes.

3.4.3.ElementNameAndTextQualifier

Only Elements with the same name - and Namespace URI if present - as well as the same text content nested into them qualify.

In Example19, “Example Nodes for ElementQualifier (the comments are not part of the example)” this means control node 1 will be compared to test node 2 since they both don't have any nested text at all. Then control node 2 will be compared to test node 4. Control node 3 is compared to test node 3 since they have the same nested text and control node 4 to test node 4.

3.4.4.org.custommonkey.xmlunit.examples.RecursiveElementNameAndTextQualifier

All ElementQualifiers seen so far only looked at the Elements themselves and not at the structure nested into them at a deeper level. A frequent user question has been which ElementQualifier should be used if the pieces of XML in Example20, “Example for RecursiveElementNameAndTextQualifier (the comments are not part of the example)” should be considered similar.

Example20.Example for RecursiveElementNameAndTextQualifier (the comments are not part of the example)

<!-- control -->
<table>
  <tr>            <!-- control row 1 -->
    <td>foo</td>
  </tr>
  <tr>            <!-- control row 2 -->
    <td>bar</td>
  </tr>
</table>

<!-- test -->
<table>
  <tr>            <!-- test row 1 -->
    <td>bar</td>
  </tr>
  <tr>            <!-- test row 2 -->
    <td>foo</td>
  </tr>
</table>

At first glance ElementNameAndTextQualifier should work but it doesn't. When DifferenceEngine processed the children of table it would compare control row 1 to test row 1 since both tr elements have the same name and both have no textual content at all.

What is needed in this case is an ElementQualifier that looks at the element's name, as well as the name of the first child element and the text nested into that first child element. This is what RecursiveElementNameAndTextQualifier does.

RecursiveElementNameAndTextQualifier ignores whitespace between the elements leading up to the nested text.

3.4.5.org.custommonkey.xmlunit.examples.MultiLevelElementNameAndTextQualifier

MultiLevelElementNameAndTextQualifier has in a way been the predecessor of Section3.4.4, “org.custommonkey.xmlunit.examples.RecursiveElementNameAndTextQualifier. It also matches element names and those of nested child elements until it finds matches, but unlike RecursiveElementNameAndTextQualifier, you must tell MultiLevelElementNameAndTextQualifier at which nesting level it should expect the nested text.

MultiLevelElementNameAndTextQualifier's constructor expects a single argument which is the nesting level of the expected text. If you use an argument of 1, MultiLevelElementNameAndTextQualifier is identical to ElementNameAndTextQualifier. In Example20, “Example for RecursiveElementNameAndTextQualifier (the comments are not part of the example)” a value of 2 would be needed.

By default MultiLevelElementNameAndTextQualifier will not ignore whitespace between the elements leading up to the nested text. If your piece of XML contains this sort of whitespace (like Example20, “Example for RecursiveElementNameAndTextQualifier (the comments are not part of the example)” which contains a newline and several space characters between <tr> and <td>) you can either instruct XMLUnit to ignore whitespace completely (see Section3.8.1, “Whitespace Handling”) or use the two-arg constructor of MultiLevelElementNameAndTextQualifier introduced with XMLUnit 1.2 and set the ignoreEmptyTexts argument to true.

In general RecursiveElementNameAndTextQualifier requires less knowledge upfront and its whitespace-handling is more intuitive.

3.5.Diff and DetailedDiff

Diff and DetailedDiff provide simplified access to DifferenceEngine by implementing the ComparisonController and DifferenceListener interfaces themselves. They cover the two most common use cases for comparing two pieces of XML: checking whether the pieces are different (this is what Diff does) and finding all differences between them (this is what DetailedDiff does).

DetailedDiff is a subclass of Diff and can only be constructed by creating a Diff instance first.

The major difference between them is their implementation of the ComparisonController interface: DetailedDiff will never stop the comparison since it wants to collect all differences. Diff in turn will halt the comparison as soon as the first Difference is found that is not recoverable. In addition DetailedDiff collects all Differences in a list and provides access to it.

By default Diff will consider two pieces of XML as identical if no differences have been found at all, similar if all differences that have been found have been recoverable (see Table1, “Document level Differences detected by DifferenceEngine to Table4, “Other Differences detected by DifferenceEngine) and different as soon as any non-recoverable difference has been found.

It is possible to specify a DifferenceListener to Diff using the overrideDifferenceListener method. In this case each Difference will be evaluated by the passed in DifferenceListener. By returning RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL the custom listener can make Diff ignore the difference completely. Likewise any Difference for which the custom listener returns RETURN_IGNORE_DIFFERENCE_NODES_SIMILAR will be treated as if the Difference was recoverable.

There are several overloads of the Diff constructor that allow you to specify your piece of XML in many ways. There are overloads that accept additional DifferenceEngine and ElementQualifier arguments. Passing in a DifferenceEngine of your own is the only way to use a ComparisonController other than Diff.

Note that Diff and DetailedDiff use ElementNameQualifier as their default ElementQualifier. This is different from DifferenceEngine which defaults to no ElementQualifier at all.

To use a custom ElementQualifier you can also use the overrideElementQualifier method. Use this with an argument of null to unset the default ElementQualifier as well.

To compare two pieces of XML you'd create a Diff instance from those two pieces and invoke identical to check that there have been no differences at all and similar to check that any difference, if any, has been recoverable. If the pieces are identical they are also similar. Likewise if they are not similar they can't be identical either.

Example21.Comparing Two Pieces of XML Using Diff

Diff d = new Diff("<a><b/><c/></a>", "<a><c/><b/></a>");
assertFalse(d.identical()); // CHILD_NODELIST_SEQUENCE Difference
assertTrue(d.similar());

The result of the comparison is cached in Diff, repeated invocations of identical or similar will not reevaluate the pieces of XML.

Note: calling toString on an instance of Diff or DetailedDiff will perform the comparision and cache its result immediately. If you change the DifferenceListener or ElementQualifier after calling toString it won't have any effect.

DetailedDiff provides only a single constructor that expects a Diff as argument. Don't use DetailedDiff if all you need to know is whether two pieces of XML are identical/similar - use Diff directly since its short-cut ComparisonController implementation will save time in this case.

Example22.Finding All Differences Using DetailedDiff

Diff d = new Diff("<a><b/><c/></a>", "<a><c/><b/></a>");
DetailedDiff dd = new DetailedDiff(d);
dd.overrideElementQualifier(null);
assertFalse(dd.similar());
List l = dd.getAllDifferences();
assertEquals(2, l.size()); // expected <b/> but was <c/> and vice versa

3.6.MatchTracker

Sometimes you might be interested in any sort of comparison result and want to get notified of successful matches as well. Maybe you want to provide feedback on the amount of differences and similarities between two documents, for example.

The interface MatchTracker can be implemented to get notified on each and every successful match, note that there may be a lot more comparisons going on than you might expect and that your callback gets notified a lot.

Example23.The MatchTracker interface

package org.custommonkey.xmlunit;

/**
 * Listener for callbacks from a {@link DifferenceEngine#compare
 * DifferenceEngine comparison} that is notified on each and every
 * comparision that resulted in a match.
 */
public interface MatchTracker {
    /**
     * Receive notification that 2 match.
     * @param match a Difference instance as defined in {@link
     * DifferenceConstants DifferenceConstants} describing the test
     * that matched and containing the detail of the nodes that have
     * been compared
     */
    void matchFound(Difference difference);
}

Despite its name the Difference instance passed into the matchFound method really describes a match and not a difference. You can expect that the getValue method on both the control and the test NodeDetail will be equal.

DifferenceEngine provides a constructor overload that allows you to pass in a MatchTracker instance and also provides a setMatchTracker method. Diff and DetailedDiff provide overrideMatchTracker methods that fill the same purpose.

Note that your MatchTracker won't receive any callbacks once the configured ComparisonController has decided that DifferenceEngine should halt the comparison.

3.7.JUnit 3.x Convenience Methods

XMLAssert and XMLTestCase contain quite a few overloads of methods for comparing two pieces of XML.

The method's names use the word Equal to mean the same as similar in the Diff class (or throughout this guide). So assertXMLEqual will assert that only recoverable differences have been encountered where assertXMLNotEqual asserts that some differences have been non-recoverable. assertXMLIdentical asserts that there haven't been any differences at all while assertXMLNotIdentical asserts that there have been differences (recoverable or not).

Most of the overloads of assertXMLEqual just provide different means to specify the pieces of XML as Strings, InputSources, Readers[7] or Documents. For each method there is a version that takes an additional err argument which is used to create the message if the assertion fails.

If you don't need any control over the ElementQualifier or DifferenceListener used by Diff these methods will save some boilerplate code. If CONTROL and TEST are pieces of XML represented as one of the supported inputs then

Diff d = new Diff(CONTROL, TEST);
assertTrue("expected pieces to be similar, " + d.toString(),
           d.similar());

and

assertXMLEqual("expected pieces to be similar", CONTROL, TEST);

are equivalent.

If you need more control over the Diff instance there is a version of assertXMLEqual (and assertXMLIdentical) that accepts a Diff instance as its argument as well as a boolean indicating whether you expect the Diff to be similar (identical) or not.

XMLTestCase contains a couple of compareXML methods that really are only shortcuts to Diff's constructors.

There is no way to use DifferenceEngine or DetailedDiff directly via the convenience methods.

3.8.Configuration Options

Unless you are using Document or DOMSource overrides when specifying your pieces of XML, XMLUnit will use the configured XML parsers (see Section2.4.1, “JAXP”) and EntityResolvers (see Section2.4.2, “EntityResolver). There are configuration options to use different settings for the control and test pieces of XML.

In addition some of the other configuration settings may lead to XMLUnit using the configured XSLT transformer (see Section2.4.1, “JAXP”) under the covers.

3.8.1.Whitespace Handling

Two different configuration options affect how XMLUnit treats whitespace in comparisons:

  • Element Content Whitespace (see Section2.4.3, “Element Content Whitespace”)

    If XMLUnit has been configured to ignore element content whitespace it will trim any text nodes found by the parser. This means that there won't appear to be any textual content in element <foo> for the following example. If you don't set XMLUnit.setIgnoreWhitespace there would be textual content consisting of a new line character.

    <foo>
    </foo>
    

    At the same time the following two <foo> elements will be considered identical if the option has been enabled, though.

    <foo>bar</foo>
    <foo> bar </foo>
    

    When this option is set to true, Diff will use the XSLT transformer under the covers.

  • "Normalizing" Whitespace

    If you set XMLUnit.setNormalizeWhitespace to true then XMLUnit will replace any kind of whitespace found in character content with a SPACE character and collapse consecutive whitespace characters to a single SPACE. It will also trim the resulting character content on both ends.

    The following two <foo> elements will be considered identical if the option has been set:

    <foo>bar baz</foo>
    <foo> bar
                baz</foo>
    

    Note that this is not related to "normalizing" the document as a whole (see Section3.8.2, “"Normalizing" Documents”).

3.8.2."Normalizing" Documents

"Normalize" in this context corresponds to the normalize method in DOM's Document class. It is the process of merging adjacent Text nodes and is not related to "normalizing whitespace" as described in the previous section.

Usually you don't need to care about this option since the XML parser is required to normalize the Document when creating it. The only reason you may want to change the option via XMLUnit.setNormalize is that your Document instances have not been created by an XML parser but rather been put together in memory using the DOM API directly.

3.8.3.Ignoring Comments

Using XMLUnit.setIgnoreComments you can make XMLUnit's difference engine ignore comments completely.

When this option is set to true, Diff will use the XSLT transformer under the covers.

3.8.4.Treating CDATA Sections and Text Nodes Alike

It is not always necessary to know whether a text has been put into a CDATA section or not. Using XMLUnit.setIgnoreDiffBetweenTextAndCDATA you can make XMLUnit consider the following two pieces of XML identical:

<foo>&lt;bar&gt;</foo>
<foo><![CDATA[<bar>]]></foo>

3.8.5.Entity Reference Expansion

Normally the XML parser will expand character references to their Unicode equivalents but for more complex entity definitions the parser may expand them or not. Using XMLUnit.setExpandEntityReferences you can control the parser's setting.

3.8.6.Comparison of Unmatched Elements

When XMLUnit cannot match a control Element to a test Element (the configured ElementQualifier - see Section3.4, “ElementQualifier - doesn't return true for any of the test Elements) it will try to compare it against the first unmatched test Element (if there is one). Starting with XMLUnit 1.3 one can use XMLUnit.setCompareUnmatched to disable this behavior and generate CHILD_NODE_NOT_FOUND differences instead.

If the control document is

<root>
  <a/>
</root>

and the test document is

<root>
  <b/>
</root>

the default setting will create a single ELEMENT_TAG_NAME Difference ("expected a but found b"). Setting XMLUnit.setCompareUnmatched to false will create two Differences of type CHILD_NODE_NOT_FOUND (one for "a" and one for "b") instead.



[7] See Section2.5, “Providing Input to XMLUnit” for some advice on choosing your input format.

xmlunit-1.6/userguide/html/ar01s05.html0000644000000000000000000003276712451007656016524 0ustar rootroot5.XPath Tests

5.XPath Tests

5.1.XPath Engines

Central to XMLUnit's XPath support is the XpathEngine interface which consists of only three methods:

    /**
     * Execute the specified xpath syntax <code>select</code> expression
     * on the specified document and return the list of nodes (could have
     * length zero) that match
     * @param select
     * @param document
     * @return list of matching nodes
     */
    NodeList getMatchingNodes(String select, Document document)
        throws XpathException;
    
    /**
     * Evaluate the result of executing the specified XPath syntax
     * <code>select</code> expression on the specified document
     * @param select
     * @param document
     * @return evaluated result
     */
    String evaluate(String select, Document document)
        throws XpathException;

    /**
     * Establish a namespace context.
     */
    void setNamespaceContext(NamespaceContext ctx);

The first two methods expect an XPath expression that selects content from the DOM document that is the second argument. The result of the selection can be either a DOM NodeList or a String. The later form tries to flatten the result, the value is said to be "String-ified".

The third method is part of XMLUnit's support for XML Namespaces in XPath expressions. See Section5.2, “Using XML Namespaces in XPath Selectors” for more details.

There are two implementations of the interface, org.custommonkey.xmlunit.SimpleXpathEngine and org.custommonkey.xmlunit.jaxp13.Jaxp13XpathEngine. The first implementation is the only one available in XMLUnit 1.0 and uses the configured JAXP XSLT transformer. The second is new to XMLUnit 1.1 and only available if JAXP 1.3 or later is supported, which should be the case for Java 5 and later.

XpathException is an Exception that will be thrown for invalid XPath expressions or other problems with the underlying XPath engine. It will typically wrap a javax.xml.xpath.XPathExpressionException in the Jaxp13XpathEngine case or a javax.xml.transform.TransformerException when SimpleXpathEngine is used.

The XMLUnit.newXpathEngine method will first try to create an instance of Jaxp13XpathEngine and fall back to SimpleXpathEngine if JAXP 1.3 is not supported.

One example of using the XPath support is included inside it org.custommonkey.xmlunit.examples package. It asserts that the string-ified form of an XPath selection matches a regular expression. The code needed for this is:

Example31.Matching an XPath Selection Against a Regular Expression

    XpathEngine engine = XMLUnit.newXpathEngine();
    String value = engine.evaluate(xpath, doc);
    Assert.assertTrue(message, value.matches(regex));

5.2.Using XML Namespaces in XPath Selectors

Starting with XMLUnit 1.1 XML Namespaces are supported for XPath queries.

The NamespaceContext interface provides a mapping from prefix to namespace URI and is used by the XPath engine. XPath selections then use the mapping's prefixes where needed. Note that a prefix used in the document under test and a prefix given as part of the NamespaceContext are not related at all; the context only applies to the XPath expression, the prefix used in the document is ignored completely.

Right now XMLUnit provides only a single implementation of the NamespaceContext interface: SimpleNamespaceContext. This implementation expects a java.util.Map as its constructor argument. The Map must contain (String) prefixes as keys and (String) namespace URIs as values.

Note there is nothing like a default namespace in XPath selectors. If you are using namespaces in your XPath, all namespaces need a prefix (of length greater than zero). This is independent of the prefixes used in your document.

The following example is taken from XMLUnit's own tests. It demonstrates that the namespace prefix of the document under test is irrelevant and shows how to set up the namespace context.

Example32.Using Namespaces in XPath Tests

    String testDoc = "<t:test xmlns:t=\"urn:foo\"><t:bar/></t:test>";
    Document d = XMLUnit.buildControlDocument(testDoc);
    HashMap m = new HashMap();
    m.put("foo", "urn:foo");

    NamespaceContext ctx = new SimpleNamespaceContext(m);
    XpathEngine engine = XMLUnit.newXpathEngine();
    engine.setNamespaceContext(ctx);

    NodeList l = engine.getMatchingNodes("//foo:bar", d);
    assertEquals(1, l.getLength());
    assertEquals(Node.ELEMENT_NODE, l.item(0).getNodeType());

In some cases the "stringified" value of an XPath evaluation is a qualified name - a string that encodes a namespace URI together with a local name. There are two common formats for such qualified names, one used by Java5's QName in the format {NS-URI}LOCAL-NAME and one using PREFIX:LOCAL-NAME. Starting with XMLUnit 1.6 a new QualifiedName class can parse either representation. The assertXpathEvaluatesTo overloads where the expected value is a QualifiedName try to parse the stringified value in either format - using the documents namespace context when parsing the actual value.

It is possible to set a global NamespaceContext, see Section5.4, “Configuration Options” for details.

5.3.JUnit 3.x Convenience Methods

XMLTestCase and XMLAssert provide several overloads for the following common types of assertions:

  • Two XPath expression should return the same DOM NodeList as result: assertXpathsEqual. There are methods that use two different expressions on the same document and others that compare expressions selecting from two different documents.

    The NodeLists are wrapped into a surrogate root XML element and the resulting DOM Documents are compared using Diff.similar().

  • The opposite of the above, the expressions should yield different results: assertXpathsNotEqual.
  • Two XPath expression should return the same "String-ified" result: assertXpathValuesEqual. There are methods that use two different expressions on the same document and others that compare expressions selecting from two different documents.
  • The opposite of the above, the expressions should yield different results: assertXpathValuesNotEqual.
  • The XPath expression should return an expected value when "String-ified" or interpreted as qualified name: assertXpathEvaluatesTo.
  • The NodeList selected by an XPath expression is not empty: assertXpathExists.
  • The NodeList selected by an XPath expression is empty: assertXpathNotExists.

Neither method provides any control over the message of the AssertionFailedError in case of a failure.

5.4.Configuration Options

When using XpathEngine directly you are responsible for creating the DOM document yourself. If you use the convenience methods of XMLTestCase or XMLAssert you have several options to specify the input; XMLUnit will use the control or test parser that has been configured (see Section2.4.1, “JAXP”) to create a DOM document from the given piece of XML in that case - using the configured EntityResolver(s) (see Section2.4.2, “EntityResolver) if any.

If JAXP 1.3 is not available, SimpleXpathEngine will use the configured JAXP XSLT transformer (see Section2.4.1, “JAXP”) under the covers.

When using JAXP 1.3 you can chose the actual XPathFactory implementation using XMLUnit.setXPathFactory.

It is possible to establish a global NamespaceContext with the help of the XMLUnit.setXpathNamespaceContext method. Any XpathEngine created by XMLUnit.newXpathEngine will automatically use the given context. Note that the JUnit 3 convenience methods use XMLUnit.newXpathEngine implicitly and will thus use the configured NamespaceContext.

xmlunit-1.6/userguide/html/apas03.html0000644000000000000000000001100612451007660016472 0ustar rootrootA.3.Changes from XMLUnit 1.2 to 1.3

A.3.Changes from XMLUnit 1.2 to 1.3

A.3.1.Breaking Changes

A.3.2.New Features

  • If XMLUnit doesn't find a matching Element for a control Element, it will match it against the first unmatched test Element (if there is one) instead of creating a CHILD_NODE_NOT_FOUND Difference. There now is a new configuration option compareUnmatched in the XMLUnit class that can be used to turn off this behavior - as a result two CHILD_NODE_NOT_FOUND Differences (one for the unmatched control Element and one for an unmatched test Element) will be created instead of a single Difference comparing the two likely unrelated nodes. See Section3.8.6, “Comparison of Unmatched Elements”. Issue 2758280.

A.3.3.Important Bug Fixes

  • If XMLUnit couldn't match attributes (i.e. it encountered a ATTR_NAME_NOT_FOUND_ID kind of difference), the XPath expressions of the node details have been random. Issue 2386807.
  • In some cases XMLUnit matched test nodes to multiple control nodes and then created a "missing child" difference for remaining test nodes even though they would have been valid targets for control node matches as well. Issue 2807167.
xmlunit-1.6/userguide/html/apas06.html0000644000000000000000000001032212451007660016475 0ustar rootrootA.6.Changes from XMLUnit 1.5 to 1.6

A.6.Changes from XMLUnit 1.5 to 1.6

A.6.1.Breaking Changes

  • In cases of ATTR_NAME_NOT_FOUND and CHILD_NODE_NOT_FOUND differences the value used to be the local name of the missing attribute or node. It will now be a Java5-QName-like {NS-URI}LOCAL-NAME string if the attribute or node belonged to an XML namespace. Issue 65

A.6.2.New Features

  • New assertXpathEvaluatesTo overloads in XMLAssert and a new QualifiedName class can be used to assert the stringified result of an XPath expression is actually a qualified name. Feature Request 25

A.6.3.Important Bug Fixes

  • The JAXP 1.3 based validator ignored xsi:namespaceLocation and xsi:noNamespaceLocation attributes. They will now be used if you don't specify any sources at all, but are still ignored if you specify any schema sources - since this is the way javax.xml.validation works. Issue 64
  • When an attribute cannot be found (a ATTR_NAME_NOT_FOUND difference) the XPath on the side where the attribute exists will now point to the attribute itself rather than its owning element. Feature Request 33
xmlunit-1.6/userguide/html/ar01s06.html0000644000000000000000000003505012451007656016511 0ustar rootroot6.DOM Tree Walking

6.DOM Tree Walking

Sometimes it is easier to test a piece of XML's validity by traversing the whole document node by node and test each node individually. Maybe there is no control XML to validate against or the expected value of an element's content has to be calculated. There may be several reasons.

XMLUnit supports this approach of testing via the NodeTest class. In order to use it, you need a DOM implementation that generates Document instances that implement the optional org.w3c.traversal.DocumentTraversal interface, which is not part of JAXP's standardized DOM support.

6.1.DocumentTraversal

As of the release of XMLUnit 1.1 the Document instances created by most parsers implement DocumentTraversal, this includes but is not limited to Apache Xerces, the parser shipping with Sun's JDK 5 and later or GNU JAXP. One notable exception is Apache Crimson, which also means the parser shipping with Sun's JDK 1.4 does not support traversal; you need to specify a different parser when using JDK 1.4 (see Section2.4.1, “JAXP”).

You can test whether your XML parser supports DocumentTraversal by invoking org.w3c.dom.DOMImplementation's hasFeature method with the feature "Traversal".

6.2.NodeTest

The NodeTest is instantiated with a piece of XML to traverse. It offers two performTest methods:

    /**
     * Does this NodeTest pass using the specified NodeTester instance?
     * @param tester
     * @param singleNodeType note <code>Node.ATTRIBUTE_NODE</code> is not
     *  exposed by the DocumentTraversal node iterator unless the root node
     *  is itself an attribute - so a NodeTester that needs to test attributes
     *  should obtain those attributes from <code>Node.ELEMENT_NODE</code>
     *  nodes
     * @exception NodeTestException if test fails
     */
    public void performTest(NodeTester tester, short singleNodeType);

    /**
     * Does this NodeTest pass using the specified NodeTester instance?
     * @param tester
     * @param nodeTypes note <code>Node.ATTRIBUTE_NODE</code> is not
     *  exposed by the DocumentTraversal node iterator unless the root node
     *  is itself an attribute - so a NodeTester that needs to test attributes
     *  should obtain those attributes from <code>Node.ELEMENT_NODE</code>
     *  nodes instead
     * @exception NodeTestException if test fails
     */
    public void performTest(NodeTester tester, short[] nodeTypes);

NodeTester is the class testing each node and is described in the next section.

The second argument limits the tests on DOM Nodes of (a) specific type(s). Node types are specified via the static fields of the Node class. Any Node of a type not specified as the second argument to performTest will be ignored.

Unfortunately XML attributes are not exposed as Nodes during traversal. If you need access to attributes you must add Node.ELEMENT_NODE to the second argument of performTest and access the attributes from their parent Element.

Example33.Accessing Attributes in a NodeTest

    ...
    NodeTest nt = new NodeTest(myXML);
    NodeTester tester = new MyNodeTester();
    nt.performTest(tester, Node.ELEMENT_NODE);
    ...

class MyNodeTester implements NodeTester {
    public void testNode(Node aNode, NodeTest test) {
        Element anElement = (Element) aNode;
        Attr attributeToTest = anElement.getAttributeNode(ATTRIBUTE_NAME);
        ...
    }
    ...
}

Any entities that appear as part of the Document are expanded before the traversal starts.

6.3.NodeTester

Implementations of the NodeTester interface are responsible for the actual test:

    /**
     * Validate a single Node
     * @param aNode
     * @param forTest
     * @exception NodeTestException if the node fails the test
     */
    void testNode(Node aNode, NodeTest forTest) throws NodeTestException ;

    /**
     * Validate that the Nodes passed one-by-one to the <code>testNode</code>
     * method were all the Nodes expected.
     * @param forTest
     * @exception NodeTestException if this instance was expecting more nodes
     */
    void noMoreNodes(NodeTest forTest) throws NodeTestException ;

NodeTest invokes testNode for each Node as soon as it is reached on the traversal. This means NodeTester "sees" the Nodes in the same order they appear within the tree.

noMoreNodes is invoked when the traversal is finished. It will also be invoked if the tree didn't contain any matched Nodes at all.

Implementations of NodeTester are expected to throw a NodeTestException if the current not doesn't match the test's expectations or more nodes have been expected when noMoreNodes is called.

XMLUnit ships with two implementations of NodeTest that are described in the following to sections.

6.3.1.AbstractNodeTester

AbstractNodeTester implements testNode by testing the passed in Node for its type and delegating to one of the more specific test... Methods it adds. By default the new test... methods all throw a NodeTestException because of an unexpected Node.

It further implements noMoreNodes with an empty method - i.e. it does nothing.

If you are only testing for specific types of Node it may be more convenient to subclass AbstractNodeTester. For example Example33, “Accessing Attributes in a NodeTest could be re-written as:

Example34.Accessing Attributes in a NodeTest - AbstractNodeTester version

    ...
    NodeTest nt = new NodeTest(myXML);
    NodeTester tester = new AbstractNodeTester() {
        public void testElement(Element element) throws NodeTestException {
            Attr attributeToTest = element.getAttributeNode(ATTRIBUTE_NAME);
            ...
        }
    };
    nt.performTest(tester, Node.ELEMENT_NODE);
    ...

Note that even though AbstractNodeTester contains a testAttribute method it will never be called by default and you still need to access attributes via their parent elements.

Note also that the root of the test is the document's root element, so any Nodes preceding the document's root Element won't be visited either. For this reason the testDocumentType, testEntity and testNotation methods are probably never called either.

Finally, all entity references have been expanded before the traversal started. EntityReferences will have been replaced by their replacement text if it is available, which means testEntityReference will not be called for them either. Instead the replacement text will show up as (part of) a Text node or as Element node, depending on the entity's definition.

6.3.2.CountingNodeTester

org.custommonkey.xmlunit.examples.CountingNodeTester is a simple example NodeTester that asserts that a given number of Nodes have been traversed. It will throw a NodeTestException when noMoreNodes is called before the expected number of Nodes has been visited or the actual number of nodes exceeded the expected count.

6.4.JUnit 3.x Convenience Methods

XMLAssert and XMLTestCase contain overloads of assertNodeTestPasses methods.

The most general form of it expects you to create a NodeTest instance yourself and lets you specify whether you expect the test to fail or to pass.

The other two overloads create a NodeTest instance from either String or a SAX InputSource and are specialized for the case where you are only interested in a single Node type and expect the test to pass.

Neither method provides any control over the message of the AssertionFailedError in case of a failure.

6.5.Configuration Options

The only configurable option for NodeTest is the XML parser used if the piece of XML is not specified as a Document or DocumentTraversal. NodeTest will use the "control" parser that has been configured - see Section2.4.1, “JAXP” for details.

It will also use the EntityResolver configured for the control parser if one has been set - see Section2.4.2, “EntityResolver.

xmlunit-1.6/userguide/html/apas05.html0000644000000000000000000000655212451007660016506 0ustar rootrootA.5.Changes from XMLUnit 1.4 to 1.5

A.5.Changes from XMLUnit 1.4 to 1.5

A.5.1.Breaking Changes

  • If one node in the comparison has children while the other one has not, XMLUnit 1.5 will signal a CHILD_NODELIST_LENGTH difference and CHILD_NODE_NOT_FOUND differences for each child node of the node that has children in addition to a HAS_CHILD_NODES difference. Issue 60

A.5.2.New Features

A.5.3.Important Bug Fixes

  • RecursiveElementNameAndTextQualifier had some indices reversed leading to wrong results in some cases. Issue 62
xmlunit-1.6/userguide/html/apas02.html0000644000000000000000000001713112451007660016476 0ustar rootrootA.2.Changes from XMLUnit 1.1 to 1.2

A.2.Changes from XMLUnit 1.1 to 1.2

A.2.1.Breaking Changes

  • If XMLUnit detects that it cannot match a certain node (i.e. it encounters a CHILD_NODE_NOT_FOUND kind of difference) the XPath for the "missing" node will be null. It used to be some random XPath of a different node.
  • XMLUnit.setIgnoreDiffBetweenTextAndCDATA now also sets DocumentBuilderFactory.setCoalescing. This has been done so that whitespace differences can be resolved according to the corresponding flags even in the presence of CDATA sections. Issue 1903923.
  • Two protected methods in SimpleXPathEngine (which you shouldn't extend anyway) have added XpathException to their throws list.

A.2.2.New Features

  • The SAXParserFactory used by Validator can now be configured completely. Issue 1903928.
  • A new class org.custommonkey.xmlunit.jaxp13.Validator can be used to validate schema definitions and schema instances using the javax.xml.validation package of JAXP 1.3. Depending on your JAXP implementation this may allow you to validate documents against schema definitions written in RELAX NG or other schema languages in addition to W3C XML Schema. See Section4.4, “JAXP 1.3 Validation” for details.
  • DifferenceListener can now "upgrade" recoverable differences to non-recoverable by returning RETURN_UPGRADE_DIFFERENCE_NODES_DIFFERENT in the differenceFound method. Issue 1854284.
  • A new callback interface MatchTracker is now notified on successful matches of Nodes. For more details see Section3.6, “MatchTracker. Issue 1860491.
  • It is now possible to have more control over whether the parser expand entity references or not by using XMLUnit.setExpandEntityReferences, see Section3.8.5, “Entity Reference Expansion”. Issue 1877458.
  • New examples have been added:
    • RecursiveElementNameAndTextQualifier - a more flexible ElementQualifier that fills the same need as MultiLevelElementNameAndTextQualifier See Section3.4.4, “org.custommonkey.xmlunit.examples.RecursiveElementNameAndTextQualifier for more details.
    • CaseInsensitiveDifferenceListener a - DifferenceListener that ignores case when comparing texts.
    • FloatingPointTolerantDifferenceListener a - DifferenceListener that tries to parse texts as floating point numbers and compares them using a configurable tolerance.

A.2.3.Important Bug Fixes

  • If XMLUnit couldn't match nodes (i.e. it encountered a CHILD_NODE_NOT_FOUND kind of difference), the XPath expressions of the node details have been random. Issue 1860681.
xmlunit-1.6/userguide/html/apa.html0000644000000000000000000003147412451007660016157 0ustar rootrootA.Changes

A.Changes

A.1.Changes from XMLUnit 1.0 to 1.1

XMLUnit 1.1's main focus was to add two features that have been asked for repeatedly:

  • Support for XML Namespaces in XPath processing
  • Support for XML Schema validation.

In addition some JAXP features that have been added after the release of XMLUnit 1.0 are now supported - most notably XPath support - and all reported bugs and feature requests have been addressed.

A.1.1.Breaking Changes

  • XMLTestCase is now abstract. You probably have never created instances of this class without subclassing it, but if you did, your code will now break. You will most likely want to look at the XMLAssert class.
  • All methods that have been deprecated in XMLUnit 1.0 have been removed.

  • All methods that had been declared to throw TransformerConfigurationException or ParserConfigurationException now no longer declare it. Exceptions of these types cannot be recovered from anyway, so XMLUnit will now wrap them in a org.custommonkey.xmlunit.exceptions.ConfigurationException which is an unchecked exception.

    This change doesn't have a big impact on your tests, but if you tried to catch these exceptions they will now bypass your catch blocks.

  • A new type of Difference (CHILD_NODE_NOT_FOUND_ID) has been added. It will be raised for the excess children if the control element has more child nodes than the test element - or vice versa.

    Prior to XMLUnit 1.1 a Difference of either ELEMENT_TAG_NAME_ID or NODE_TYPE_ID would have been raised if the control element had more children. The excess children were compared to the very first child node of the test element. Excess children of the test element were not reported at all.

  • The schemaLocation and noNamespaceSchemaLocation attributes of the XMLSchema-Instance Namespace are now treated in a different way from "normal" attributes. They will be flagged as new kinds of Difference that is recoverable.

    This means that two pieces of XML that were different in XMLUnit 1.0 because they differed in one of the two attributes will be similar in XMLUnit 1.1.

  • When comparing two elements that differ on attributes the comparison is now symmetric.

    In XMLUnit 1.0 if an attribute was present on the test but not the control element this wasn't flagged as a Difference; in XMLUnit 1.1 it is.

    In most practical cases this doesn't cause any problems since the two elements either have a different number of attributes or there are attributes in the control element that are missing in the test element - so the pieces of XML have been flagged as different before as well. If you are using DetailedDiff this change may lead to more detected Differences, though.

A.1.2.New Features

  • XMLUnit 1.0 shipped with rudimentary support for XML Schema validation (it worked with Apache Xerces-J but no other parsers). XMLUnit 1.1 supports Schema validation for any JAXP compliant XML parser (that supports Schema itself). You can also tell XMLUnit where to look for the XML Schema definitions. See Section4.1.2, “XML Schema Validation” for details.
  • XPath support has undergone significant changes, see Section5, “XPath Tests” for more details. In particular XMLUnit will now use javax.xml.xpath if it is available (which also helps to avoid the buggy XSLTC version that is the default transformer engine in Java 5) and supports XML namespaces.
  • Several new configuration options have been added, see Section3.8, “Configuration Options”.
  • It is now possible to provide a custom org.xml.sax.EntityResolver for control and test parsers.
  • It is now possible to provide a custom javax.xml.transform.URIResolver for transformations.
  • New overloads have been added that allow org.xml.sax.InputSource to be used as a "piece of XML" in many classes.
  • Validator will now use the custom EntityResolver configured for the "control" parser as a fallback.
  • A new package org.custommonkey.xmlunit.examples has been added that showcases some of XMLUnit's abilities. It currently contains two classes:

    1. MultiLevelElementNameAndTextQualifier see Section3.4.5, “org.custommonkey.xmlunit.examples.MultiLevelElementNameAndTextQualifier for a description.
    2. XPathRegexAssert that provides a JUnit 3.x like assertXPathMatches method to verify that the string-ified value of an XPath match matches a given regular expression (requires JDK 1.4 or above).

A.1.3.Important Bug Fixes

  • ElementNameAndAttributeQualifier would throw an NullPointerException if the control piece of XML contained attributes that were missing in the test piece of XML. Issue 952920.
  • XMLTestCase.assertXMLNotEqual(String, Reader, Reader) delegated to assertXMLEqual instead of assertXMLNotEqual internally, negating the assertion's logic. Issue 956372.
  • XMLTestCase.assertXMLIdentical(Diff, boolean) delegated to assertXMLEqual, weakening the assertion.
  • Under certain circumstances the reported XPath expressions for nodes that showed differences were wrong. XMLUnit could lose the root element or erroneously append an extra attribute name. Issues 1047364 and 1027863.
  • TolerantSaxParser's logic in characters was broken and could cause StringIndexOutOfBoundsExceptions. Issue 1150234.
xmlunit-1.6/userguide/html/index.html0000644000000000000000000013474312451007660016530 0ustar rootrootXMLUnit Java User's Guide

XMLUnit Java User's Guide

Tim Bacon

Stefan Bodewig

Revision History
Revision 1.0January 2003 Tim
Bacon
Documentation for XMLUnit Java 1.0
Revision 1.1April 2007
Documentation for XMLUnit Java 1.1
Revision 1.2June 2008
Documentation for XMLUnit Java 1.2
Revision 1.3September 2009
Documentation for XMLUnit Java 1.3
Revision 1.4February 2013
Documentation for XMLUnit Java 1.4
Revision 1.5September 2013
Documentation for XMLUnit Java 1.5
Revision 1.6December 2014
Documentation for XMLUnit Java 1.6

Table of Contents

1. A Tour of XMLUnit
1.1. What is XMLUnit?
1.2. Quick tour
1.3. Glossary
1.4. Configuring XMLUnit
1.5. Writing XML comparison tests
1.6. Comparing XML Transformations
1.7. Validation Tests
1.8. XPath Tests
1.9. Testing by Tree Walking
2. Using XMLUnit
2.1. Requirements
2.2. Basic Usage
2.3. Using XMLUnit With JUnit 3.x
2.4. Common Configuration Options
2.5. Providing Input to XMLUnit
3. Comparing Pieces of XML
3.1. The Difference Engine
3.2. ComparisonController
3.3. DifferenceListener
3.4. ElementQualifier
3.5. Diff and DetailedDiff
3.6. MatchTracker
3.7. JUnit 3.x Convenience Methods
3.8. Configuration Options
4. Validating XML Documents
4.1. The Validator Class
4.2. JUnit 3.x Convenience Methods
4.3. Configuration Options
4.4. JAXP 1.3 Validation
5. XPath Tests
5.1. XPath Engines
5.2. Using XML Namespaces in XPath Selectors
5.3. JUnit 3.x Convenience Methods
5.4. Configuration Options
6. DOM Tree Walking
6.1. DocumentTraversal
6.2. NodeTest
6.3. NodeTester
6.4. JUnit 3.x Convenience Methods
6.5. Configuration Options
A. Changes
A.1. Changes from XMLUnit 1.0 to 1.1
A.1.1. Breaking Changes
A.1.2. New Features
A.1.3. Important Bug Fixes
A.2. Changes from XMLUnit 1.1 to 1.2
A.2.1. Breaking Changes
A.2.2. New Features
A.2.3. Important Bug Fixes
A.3. Changes from XMLUnit 1.2 to 1.3
A.3.1. Breaking Changes
A.3.2. New Features
A.3.3. Important Bug Fixes
A.4. Changes from XMLUnit 1.3 to 1.4
A.4.1. Breaking Changes
A.4.2. New Features
A.4.3. Important Bug Fixes
A.5. Changes from XMLUnit 1.4 to 1.5
A.5.1. Breaking Changes
A.5.2. New Features
A.5.3. Important Bug Fixes
A.6. Changes from XMLUnit 1.5 to 1.6
A.6.1. Breaking Changes
A.6.2. New Features
A.6.3. Important Bug Fixes

1.A Tour of XMLUnit

This first section contains a tour through XMLUnit's features, the next sections will cover them in more detail.

Note that it has a strong focus on using the XMLTestCase class which is one option to use XMLUnit, but not the only one. XMLUnit's features can be fully used without any dependency on JUnit at all.

1.1.What is XMLUnit?

XMLUnit enables JUnit-style assertions to be made about the content and structure of XML[1]. It is an open source project hosted at http://xmlunit.sourceforge.net/ that grew out of a need to test a system that generated and received custom XML messages. The problem that we faced was how to verify that the system generated the correct message from a known set of inputs. Obviously we could use a DTD or a schema to validate the message output, but this approach wouldn't allow us to distinguish between valid XML with correct content (e.g. element <foo>bar</foo>) and valid XML with incorrect content (e.g. element <foo>baz</foo>). What we really wanted was an assertXMLEqual() method, so we could compare the message that we expected the system to generate and the message that the system actually generated. And that was the beginning of XMLUnit.

1.2.Quick tour

XMLUnit provides a single JUnit extension class, XMLTestCase, and a set of supporting classes that allow assertions to be made about:

  • The differences between two pieces of XML (via Diff and DetailedDiff classes)
  • The validity of a piece of XML (via Validator class)
  • The outcome of transforming a piece of XML using XSLT (via Transform class)
  • The evaluation of an XPath expression on a piece of XML (via classes implementing the XpathEngine interface)
  • Individual nodes in a piece of XML that are exposed by DOM Traversal (via NodeTest class)

XMLUnit can also treat HTML content, even badly-formed HTML, as valid XML to allow these assertions to be made about web pages (via the HTMLDocumentBuilder class).

1.3.Glossary

As with many projects some words in XMLUnit have particular meanings so here is a quick overview. A piece of XML is a DOM Document, a String containing marked-up content, or a Source or Reader that allows access to marked-up content within some resource. XMLUnit compares the expected control XML to some actual test XML. The comparison can reveal that two pieces of XML are identical, similar or different. The unit of measurement used by the comparison is a difference, and differences can be either recoverable or unrecoverable. Two pieces of XML are identical if there are no differences between them, similar if there are only recoverable differences between them, and different if there are any unrecoverable differences between them.

1.4.Configuring XMLUnit

There are many Java XML parsers available, and XMLUnit should work with any JAXP compliant parser library, such as Xerces-J [2] from the Apache Software Foundation. To use the XSLT and XPath features of XMLUnit a Trax (the XSLT portion of JAXP) compliant transformation engine is required, such as Xalan-J[3], from the Apache Software Foundation. To configure XMLUnit to use a specific parser and transformation engine set three System properties before any tests are run, e.g.

Example1.Configuring JAXP via System Properties

System.setProperty("javax.xml.parsers.DocumentBuilderFactory",
    "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
System.setProperty("javax.xml.parsers.SAXParserFactory",
    "org.apache.xerces.jaxp.SAXParserFactoryImpl");
System.setProperty("javax.xml.transform.TransformerFactory",
    "org.apache.xalan.processor.TransformerFactoryImpl");

You may want to read Section2.4.1, “JAXP” for more details - in particular if you are using Java 1.4 or later.

Alternatively there are static methods on the XMLUnit class that can be called directly. The advantage of this approach is that you can specify a different parser class for control and test XML and change the current parser class at any time in your tests, should you need to make assertions about the compatibility of different parsers.

Example2.Configuring JAXP via XMLUnit class

XMLUnit.setControlParser("org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
XMLUnit.setTestParser("org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
XMLUnit.setSAXParserFactory("org.apache.xerces.jaxp.SAXParserFactoryImpl");
XMLUnit.setTransformerFactory("org.apache.xalan.processor.TransformerFactoryImpl");

1.5.Writing XML comparison tests

Let's say we have two pieces of XML that we wish to compare and assert that they are equal. We could write a simple test class like this:

Example3.A simple comparison test

public class MyXMLTestCase extends XMLTestCase {
    public MyXMLTestCase(String name) {
        super(name);
    }

    public void testForEquality() throws Exception {
        String myControlXML = "<msg><uuid>0x00435A8C</uuid></msg>";
        String myTestXML = "<msg><localId>2376</localId></msg>";
        assertXMLEqual("Comparing test xml to control xml",
                       myControlXML, myTestXML);
    }
}

The assertXMLEqual test will pass if the control and test XML are either similar or identical. Obviously in this case the pieces of XML are different and the test will fail. The failure message indicates both what the difference is and the XPath locations of the nodes that were being compared:

Comparing test xml to control xml
[different] Expected element tag name 'uuid' but was 'localId' - comparing <uuid...> at /msg[1]/uuid[1] to <localId...> at /msg[1]/localId[1]

When comparing pieces of XML, the XMLTestCase actually creates an instance of the Diff class. The Diff class stores the result of an XML comparison and makes it available through the methods similar() and identical(). The assertXMLEqual() method tests the value of Diff.similar() and the assertXMLIdentical() method tests the value of Diff.identical().

It is easy to create a Diff instance directly without using the XMLTestCase class as below:

Example4.Creating a Diff instance

public void testXMLIdentical()throws Exception {
    String myControlXML =
        "<struct><int>3</int><boolean>false</boolean></struct>";
    String myTestXML =
        "<struct><boolean>false</boolean><int>3</int></struct>";
    Diff myDiff = new Diff(myControlXML, myTestXML);
    assertTrue("XML similar " + myDiff.toString(),
               myDiff.similar());
    assertTrue("XML identical " + myDiff.toString(),
               myDiff.identical());
}

This test fails as two pieces of XML are similar but not identical if their nodes occur in a different sequence. The failure message reported by JUnit from the call to myDiff.toString() looks like this:

[not identical] Expected sequence of child nodes '0' but was '1' - comparing <int...> at /struct[1]/int[1] to <int...> at /struct[1]/int[1]

For efficiency reasons a Diff stops the comparison process as soon as the first difference is found. To get all the differences between two pieces of XML an instance of the DetailedDiff class, a subclass of Diff, is required. Note that a DetailedDiff is constructed using an existing Diff instance.

Consider this test that uses a DetailedDiff:

Example5.Using DetailedDiff

public void testAllDifferences() throws Exception {
    String myControlXML = "<news><item id=\"1\">War</item>"
        + "<item id=\"2\">Plague</item>"
        + "<item id=\"3\">Famine</item></news>";
    String myTestXML = "<news><item id=\"1\">Peace</item>"
        + "<item id=\"2\">Health</item>"
        + "<item id=\"3\">Plenty</item></news>";
    DetailedDiff myDiff = new DetailedDiff(new Diff(myControlXML, myTestXML));
    List allDifferences = myDiff.getAllDifferences();
    assertEquals(myDiff.toString(), 2, allDifferences.size());
}

This test fails with the message below as each of the 3 news items differs between the control and test XML:

[different] Expected text value 'War' but was 'Peace' - comparing <item...>War</item> at /news[1]/item[1]/text()[1] to <item...>Peace</item> at /news[1]/item[1]/text()[1]
[different] Expected text value 'Plague' but was 'Health' - comparing <item...>Plague</item> at /news[1]/item[2]/text()[1] to <item...>Health</item> at /news[1]/item[2]/text()[1]
[different] Expected text value 'Famine' but was 'Plenty' - comparing <item...>Famine</item> at /news[1]/item[3]/text()[1] to <item...>Plenty</item> at /news[1]/item[3]/text()[1]
expected <2> but was <3>

The List returned from the getAllDifferences() method contains Difference instances. These instances describe both the type[4] of difference found between a control node and test node and the NodeDetail of those nodes (including the XPath location of each node). Difference instances are passed at runtime in notification events to a registered DifferenceListener, an interface whose default implementation is provided by the Diff class.

However it is possible to override this default behaviour by implementing the interface in your own class. The IgnoreTextAndAttributeValuesDifferenceListener class is an example of how to implement a custom DifferenceListener. It allows an XML comparison to be made that ignores differences in the values of text and attribute nodes, for example when comparing a skeleton or outline piece of XML to some generated XML.

The following test illustrates the use of a custom DifferenceListener:

Example6.Using a custom DifferenceListener

public void testCompareToSkeletonXML() throws Exception {
    String myControlXML = "<location><street-address>22 any street</street-address><postcode>XY00 99Z</postcode></location>";
    String myTestXML = "<location><street-address>20 east cheap</street-address><postcode>EC3M 1EB</postcode></location>";
    DifferenceListener myDifferenceListener = new IgnoreTextAndAttributeValuesDifferenceListener();
    Diff myDiff = new Diff(myControlXML, myTestXML);
    myDiff.overrideDifferenceListener(myDifferenceListener);
    assertTrue("test XML matches control skeleton XML",
               myDiff.similar());
}

The DifferenceEngine class generates the events that are passed to a DifferenceListener implementation as two pieces of XML are compared. Using recursion it navigates through the nodes in the control XML DOM, and determines which node in the test XML DOM qualifies for comparison to the current control node. The qualifying test node will match the control node's node type, as well as the node name and namespace (if defined for the control node).

However when the control node is an Element, it is less straightforward to determine which test Element qualifies for comparison as the parent node may contain repeated child Elements with the same name and namespace. So for Element nodes, an instance of the ElementQualifier interface is used determine whether a given test Element node qualifies for comparison with a control Element node. This separates the decision about whether two Elements should be compared from the decision about whether those two Elements are considered similar. By default an ElementNameQualifier class is used that compares the nth child <abc> test element to the nth child <abc> control element, i.e. the sequence of the child elements in the test XML is important. However this default behaviour can be overridden using an ElementNameAndTextQualifier or ElementNameAndAttributesQualifier.

The test below demonstrates the use of a custom ElementQualifier:

Example7.Using a custom ElementQualifier

public void testRepeatedChildElements() throws Exception {
    String myControlXML = "<suite>"
        + "<test status=\"pass\">FirstTestCase</test>"
        + "<test status=\"pass\">SecondTestCase</test></suite>";
    String myTestXML = "<suite>"
        + "<test status=\"pass\">SecondTestCase</test>"
        + "<test status=\"pass\">FirstTestCase</test></suite>";
    assertXMLNotEqual("Repeated child elements in different sequence order are not equal by default",
                      myControlXML, myTestXML);
    Diff myDiff = new Diff(myControlXML, myTestXML);
    myDiff.overrideElementQualifier(new ElementNameAndTextQualifier());
    assertXMLEqual("But they are equal when an ElementQualifier controls which test element is compared with each control element",
                    myDiff, true);
}

Note: calling toString on an instance of Diff or DetailedDiff will perform the comparision and cache its result immediately. If you change the DifferenceListener or ElementQualifier after calling toString it won't have any effect.

1.6.Comparing XML Transformations

XMLUnit can test XSLT transformations at a high level using the Transform class that wraps an javax.xml.transform.Transformer instance. Knowing the input XML, input stylesheet and expected output XML we can assert that the output of the transformation matches the expected output as follows:

Example8.Testing the Result of a Transformation

public void testXSLTransformation() throws Exception {
    String myInputXML = "...";
    File myStylesheetFile = new File("...");
    Transform myTransform = new Transform(myInputXML, myStylesheetFile);
    String myExpectedOutputXML = "...";
    Diff myDiff = new Diff(myExpectedOutputXML, myTransform);
    assertTrue("XSL transformation worked as expected", myDiff.similar());
}

The getResultString() and getResultDocument() methods of the Transform class can be used to access the result of the XSLT transformation programmatically if required, for example as below:

Example9.Using Transform programmatically

public void testAnotherXSLTransformation() throws Exception {
    File myInputXMLFile = new File("...");
    File myStylesheetFile = new File("...");
    Transform myTransform = new Transform(
        new StreamSource(myInputXMLFile),
        new StreamSource(myStylesheetFile));
    Document myExpectedOutputXML =
       XMLUnit.buildDocument(XMLUnit.getControlParser(),
                             new FileReader("..."));
    Diff myDiff = new Diff(myExpectedOutputXML,
    myTransform.getResultDocument());
    assertTrue("XSL transformation worked as expected", myDiff.similar());
}

1.7.Validation Tests

XML parsers that validate a piece of XML against a DTD are common, however they rely on a DTD reference being present in the XML, and they can only validate against a single DTD. When writing a system that exchanges XML messages with third parties there are times when you would like to validate the XML against a DTD that is not available to the recipient of the message and so cannot be referenced in the message itself. XMLUnit provides a Validator class for this purpose.

Example10.Validating Against a DTD

public void testValidation() throws Exception {
    XMLUnit.getTestDocumentBuilderFactory().setValidating(true);
    // As the document is parsed it is validated against its referenced DTD
    Document myTestDocument = XMLUnit.buildTestDocument("...");
    String mySystemId = "...";
    String myDTDUrl = new File("...").toURL().toExternalForm();
    Validator myValidator = new Validator(myTestDocument, mySystemId,
                                          myDTDUrl);
    assertTrue("test document validates against unreferenced DTD",
               myValidator.isValid());
}

Starting with XMLUnit 1.1, the Validator class can also validate against one or more XML Schema definitions. See Section4.1.2, “XML Schema Validation” for details.

XMLUnit 1.2 introduces a new Validator class that relies on JAXP 1.3's javax.xml.validation package. This Validator can validate against W3C XML Schema, but may support different Schema languages like RELAX NG if your JAXP implementation supports it. See Section4.4, “JAXP 1.3 Validation” for details.

1.8.XPath Tests

One of the strengths of XML is the ability to programmatically extract specific parts of a document using XPath expressions. The XMLTestCase class offers a number of XPath related assertion methods, as demonstrated in this test:

Example11.Using XPath Tests

public void testXPaths() throws Exception {
    String mySolarSystemXML = "<solar-system>"
        + "<planet name='Earth' position='3' supportsLife='yes'/>"
        + "<planet name='Venus' position='4'/></solar-system>";
    assertXpathExists("//planet[@name='Earth']", mySolarSystemXML);
    assertXpathNotExists("//star[@name='alpha centauri']",
                         mySolarSystemXML);
    assertXpathsEqual("//planet[@name='Earth']",
                      "//planet[@position='3']", mySolarSystemXML);
    assertXpathsNotEqual("//planet[@name='Venus']",
                         "//planet[@supportsLife='yes']",
                         mySolarSystemXML);
}

When an XPath expression is evaluated against a piece of XML a NodeList is created that contains the matching Nodes. The methods in the previous test assertXpathExists, assertXpathNotExists, assertXpathsEqual, and assertXpathsNotEqual use these NodeLists. However, the contents of a NodeList can be flattened (or String-ified) to a single value, and XMLUnit also allows assertions to be made about this single value, as in this test[5]:

Example12.Testing XPath Values

public void testXPathValues() throws Exception {
    String myJavaFlavours = "<java-flavours>"
        + "<jvm current='some platforms'>1.1.x</jvm>"
        + "<jvm current='no'>1.2.x</jvm>"
        + "<jvm current='yes'>1.3.x</jvm>"
        + "<jvm current='yes' latest='yes'>1.4.x</jvm></javaflavours>";
    assertXpathEvaluatesTo("2", "count(//jvm[@current='yes'])",
                           myJavaFlavours);
    assertXpathValuesEqual("//jvm[4]/@latest", "//jvm[4]/@current",
                           myJavaFlavours);
    assertXpathValuesNotEqual("//jvm[2]/@current",
                              "//jvm[3]/@current", myJavaFlavours);
}

XPaths are especially useful where a document is made up largely of known, unchanging content with only a small amount of changing content created by the system. One of the main areas where constant "boilerplate" markup is combined with system generated markup is of course in web applications. The power of XPath expressions can make testing web page output quite trivial, and XMLUnit supplies a means of converting even very badly formed HTML into XML to aid this approach to testing.

The HTMLDocumentBuilder class uses the Swing HTML parser to convert marked-up content to Sax events. The TolerantSaxDocumentBuilder class handles the Sax events to build up a DOM document in a tolerant fashion i.e. without mandating that opened elements are closed. (In a purely XML world this class would have no purpose as there are plenty of Sax event handlers that can build DOM documents from well formed content). The test below illustrates how the use of these classes:

Example13.Working with non well-formed HTML

public void testXpathsInHTML() throws Exception {
    String someBadlyFormedHTML = "<html><title>Ugh</title>"
        + "<body><h1>Heading<ul>"
        + "<li id='1'>Item One<li id='2'>Item Two";
    TolerantSaxDocumentBuilder tolerantSaxDocumentBuilder =
        new TolerantSaxDocumentBuilder(XMLUnit.getTestParser());
    HTMLDocumentBuilder htmlDocumentBuilder =
        new HTMLDocumentBuilder(tolerantSaxDocumentBuilder);
    Document wellFormedDocument =
        htmlDocumentBuilder.parse(someBadlyFormedHTML);
    assertXpathEvaluatesTo("Item One", "/html/body//li[@id='1']",
                           wellFormedDocument);
}

One of the key points about using XPaths with HTML content is that extracting values in tests requires the values to be identifiable. (This is just another way of saying that testing HTML is easier when it is written to be testable.) In the previous example id attributes were used to identify the list item values that needed to be testable, however class attributes or span and div tags can also be used to identify specific content for testing.

1.9.Testing by Tree Walking

The DOM specification allows a Document to optionally implement the DocumentTraversal interface. This interface allows an application to iterate over the Nodes contained in a Document, or to "walk the DOM tree". The XMLUnit NodeTest class and NodeTester interface make use of DocumentTraversal to expose individual Nodes in tests: the former handles the mechanics of iteration, and the latter allows custom test strategies to be implemented. A sample test strategy is supplied by the CountingNodeTester class that counts the nodes presented to it and compares the actual count to an expected count. The test below illustrates its use:

Example14.Using CountingNodeTester

public void testCountingNodeTester() throws Exception {
    String testXML = "<fibonacci><val>1</val><val>2</val><val>3</val>"
        + "<val>5</val><val>9</val></fibonacci>";
    CountingNodeTester countingNodeTester = new CountingNodeTester(4);
    assertNodeTestPasses(testXML, countingNodeTester, Node.TEXT_NODE);
}

This test fails as there are 5 text nodes, and JUnit supplies the following message:

Expected node test to pass, but it failed! Counted 5 node(s) but
expected 4
      

Note that if your DOM implementation does not support the DocumentTraversal interface then XMLUnit will throw an IllegalArgumentException informing you that you cannot use the NodeTest or NodeTester classes. Unfortunately even if your DOM implementation does support DocumentTraversal, attributes are not exposed by iteration: however they can be examined from the Element node that contains them.

While the previous test could have been easily performed using XPath, there are times when Node iteration is more powerful. In general, this is true when there are programmatic relationships between nodes that can be more easily tested iteratively. The following test uses a custom NodeTester class to illustrate the potential:

Example15.Using a Custom NodeTester

public void testCustomNodeTester() throws Exception {
    String testXML = "<fibonacci><val>1</val><val>2</val><val>3</val>"
        + "<val>5</val><val>9</val></fibonacci>";
    NodeTest nodeTest = new NodeTest(testXML);
    assertNodeTestPasses(nodeTest, new FibonacciNodeTester(),
                         new short[] {Node.TEXT_NODE,
                                      Node.ELEMENT_NODE},
                         true);
}

private class FibonacciNodeTester extends AbstractNodeTester {
    private int nextVal = 1, lastVal = 1, priorVal = 0;

    public void testText(Text text) throws NodeTestException {
        int val = Integer.parseInt(text.getData());
        if (nextVal != val) {
            throw new NodeTestException("Incorrect value", text);
        }
        nextVal = val + lastVal;
        priorVal = lastVal;
        lastVal = val;
    }

    public void testElement(Element element) throws NodeTestException {
        String name = element.getLocalName();
        if ("fibonacci".equals(name) || "val".equals(name)) {
            return;
        }
        throw new NodeTestException("Unexpected element", element);
    }

    public void noMoreNodes(NodeTest nodeTest) throws NodeTestException {
    }
}

The test fails because the XML contains the wrong value for the last number in the sequence:

Expected node test to pass, but it failed! Incorrect value [#text: 9]
      


[1] For more information on JUnit see http://www.junit.org

[4] A full set of prototype Difference instances - one for each type of difference - is defined using final static fields in the DifferenceConstants class.

[5] Each of the assertXpath...() methods uses an implementation of the XpathEngine interface to evaluate an XPath expression.

xmlunit-1.6/userguide/html/apas04.html0000644000000000000000000000714012451007660016477 0ustar rootrootA.4.Changes from XMLUnit 1.3 to 1.4

A.4.Changes from XMLUnit 1.3 to 1.4

A.4.1.Breaking Changes

A.4.2.New Features

  • xsi:type attributes now have their value interpreted as a QName and will compare as identical if their namespace URI and local names match even if they use different prefixes. Issue 3602981

A.4.3.Important Bug Fixes

  • XMLTestCase's and XMLAssert's assertXpathsEqual methods threw an exception when at least one XPath matched an attribute. Issue 377768.
  • FloatingPointTolerantDifferenceListener expected numbers to differ by less than the given tolerance rather than "less or equal" than as the docs said. Issue 3593368
xmlunit-1.6/userguide/html/ar01s04.html0000644000000000000000000004307512451007656016515 0ustar rootroot4.Validating XML Documents

4.Validating XML Documents

4.1.The Validator Class

The Validator class encapsulates XMLUnit's validation support. It will use the SAXParser configured in XMLUnit (see Section2.4.1, “JAXP”).

The piece of XML to validate is specified in the constructor. The constructors using more than a single argument are only relevant if you want to validate against a DTD and need to provide the location of the DTD itself - for details see the next section.

By default, Validator will validate against a DTD, but it is possible to validate against a (or multiple) Schema(s) as well. Schema validation requires an XML parser that supports it, of course.

4.1.1.DTD Validation

Validating against a DTD is straight forward if the piece of XML contains a DOCTYPE declaration with a SYSTEM identifier that can be resolved at validation time. Simply create a Validator object using one of the single argument constructors.

Example24.Validating Against the DTD Defined in DOCTYPE

InputSource is = new InputSource(new FileInputStream(myXmlDocument));
Validator v = new Validator(is);
boolean isValid = v.isValid();

If the piece of XML doesn't contain any DOCTYPE declaration at all or it contains a DOCTYPE but you want to validate against a different DTD, you'd use one of the three argument versions of Validator's constructors. In this case the publicId argument becomes the PUBLIC and systemId the SYSTEM identifier of the DOCTYPE that is implicitly added to the piece of XML. Any existing DOCTYPE will be removed. The systemId should be a URL that can be resolved by your parser.

Example25.Validating a Piece of XML that doesn't Contain a DOCTYPE

InputSource is = new InputSource(new FileInputStream(myXmlDocument));
Validator v = new Validator(is,
                            (new File(myDTD)).toURI().toURL().toString(),
                            myPublicId);
boolean isValid = v.isValid();

If the piece of XML already has the correct DOCTYPE declaration but the declaration either doesn't specify a SYSTEM identifier at all or you want the SYSTEM identifier to resolve to a different location you have two options:

  • Use one of the two argument constructors and specify the alternative URL as systemId.

    Example26.Validating Against a Local DTD

    InputSource is = new InputSource(new FileInputStream(myXmlDocument));
    Validator v = new Validator(is,
                                (new File(myDTD)).toURI().toURL().toString());
    boolean isValid = v.isValid();
    

  • Use a custom EntityResolver via XMLUnit.setControlEntityResolver together with one of the single argument constructor overloads of Validator.

    This approach would allow you to use an OASIS catalog[8] in conjunction with the Apache XML Resolver library[9] to resolve the DTD location as well as the location of any other entity in your piece of XML, for example.

    Example27.Validating Against a DTD Using Apache's XML Resolver and an XML Catalog

    InputSource is = new InputSource(new FileInputStream(myXmlDocument));
    XMLUnit.setControlEntityResolver(new CatalogResolver());
    Validator v = new Validator(is);
    boolean isValid = v.isValid();
    
    #CatalogManager.properties
    
    verbosity=1
    relative-catalogs=yes
    catalogs=/some/path/to/catalog
    prefer=public
    static-catalog=yes
    catalog-class-name=org.apache.xml.resolver.Resolver
    
    <!-- catalog file -->
    
    <catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
      <public publicId="-//Some//DTD V 1.1//EN"
              uri="mydtd.dtd"/>
    </catalog>
    

4.1.2.XML Schema Validation

In order to validate against the XML Schema language Schema validation has to be enabled via the useXMLSchema method of Validator.

By default the parser will try to resolve the location of Schema definition files via a schemaLocation attribute if it is present in the piece of XML or it will try to open the Schema's URI as an URL and read from it.

The setJAXP12SchemaSource method of Validator allows you to override this behavior as long as the parser supports the http://java.sun.com/xml/jaxp/properties/schemaSource property in the way described in "JAXP 1.2 Approved CHANGES"[10].

setJAXP12SchemaSource's argument can be one of

  • A String which contains an URI.
  • An InputStream the Schema can be read from.
  • An InputSource the Schema can be read from.
  • A File the Schema can be read from.
  • An array containing any of the above.

If the property has been set using a String, the Validator class will provide its systemId as specified in the constructor when asked to resolve it. You must only use the single argument constructors if you want to avoid this behavior. If no systemId has been specified, the configured EntityResolver may still be used.

Example28.Validating Against a Local XML Schema

InputSource is = new InputSource(new FileInputStream(myXmlDocument));
Validator v = new Validator(is);
v.useXMLSchema(true);
v.setJAXP12SchemaSource(new File(myXmlSchemaFile));
boolean isValid = v.isValid();

4.2.JUnit 3.x Convenience Methods

Both XMLAssert and XMLTestCase provide an assertXMLValid(Validator) method that will fail if Validator's isValid method returns false.

In addition several overloads of the assertXMLValid method are provided that directly correspond to similar overloads of Validator's constructor. These overloads don't support XML Schema validation at all.

Validator itself provides an assertIsValid method that will throw an AssertionFailedError if validation fails.

Neither method provides any control over the message of the AssertionFailedError in case of a failure.

4.3.Configuration Options

4.4.JAXP 1.3 Validation

JAXP 1.3 - shipping with Java5 or better and available as a separate product for earlier Java VMs - introduces a new package javax.xml.validation designed for validations of snippets of XML against different schema languages. Any compliant implementation must support the W3C XML Schema language, but other languages like RELAX NG or Schematron may be supported as well.

The class org.custommonkey.xmlunit.jaxp13.Validator can be used to validate a piece of XML against a schema definition but also to validate the schema definition itself. By default Validator will assume your definition uses the W3C XML Schema language, but it provides a constructor that can be used to specify a different language via an URL supported by the SchemaFactory class. Alternatively you can specify the schema factory itself.

The schema definition itself can be given via Source elements, just like the pieces of XML to validate are specified as Source as well.

Note the Validator class of javax.xml.validation will ignore all xsi:namespaceLocation and xsi:noNamespaceLocation attributes of the XML document you want to validate if you specify at least one schema source.

The following example uses org.custommonkey.xmlunit.jaxp13.Validator to perform the same type of validation shown in Example28, “Validating Against a Local XML Schema”.

Example29.Validating Against a Local XML Schema

Validator v = new Validator();
v.addSchemaSource(new StreamSource(new File(myXmlSchemaFile)));
StreamSource is = new StreamSource(new File(myXmlDocument));
boolean isValid = v.isInstanceValid(is);

Validating a schema definition is shown in the next example.

Example30.Validating an XML Schema Definition

Validator v = new Validator();
v.addSchemaSource(new StreamSource(new File(myXmlSchemaFile)));
boolean isValid = v.isSchemaValid();

There is no explicit JUnit 3 support for org.custommonkey.xmlunit.jaxp13.Validator.

xmlunit-1.6/userguide/html/ar01s02.html0000644000000000000000000004717112451007656016514 0ustar rootroot2.Using XMLUnit

2.Using XMLUnit

2.1.Requirements

XMLUnit requires a JAXP compliant XML parser virtually everywhere. Several features of XMLUnit also require a JAXP compliant XSLT transformer. If it is available, a JAXP compliant XPath engine will be used for XPath tests.

To build XMLUnit at least JAXP 1.2 is required, this is the version provided by the Java class library in JDK 1.4. The JAXP 1.3 (i.e. Java5 and above) XPath engine can only be built when JAXP 1.3 is available.

As long as you don't require support for XML Namespaces or XML Schema, any JAXP 1.1 compliant implementations should work at runtime. For namespace and schema support you will need a parser that complies to JAXP 1.2 and supports the required feature. The XML parser shipping with JDK 1.4 (a version of Apache Crimson) for example is compliant to JAXP 1.2 but doesn't support Schema validation.

XMLUnit is supposed to build and run on any Java version after 1.3 (at least no new hard JDK 1.4 dependencies have been added in XMLUnit 1.1), but it has only been tested on JDK 1.4.2 and above.

To build XMLUnit JUnit 3.x (only tested with JUnit 3.8.x) is required. It is not required at runtime unless you intend to use the XMLTestCase or XMLAssert classes.

2.2.Basic Usage

XMLUnit consists of a few classes all living in the org.custommonkey.xmlunit package. You can use these classes directly from your code, no matter whether you are writing a unit test or want to use XMLUnit's features for any other purpose.

This section provides a few hints of where to start if you want to use a certain feature of XMLUnit, more details can be found in the more specific sections later in this document.

2.2.1.Comparing Pieces of XML

Heart and soul of XMLUnit's comparison engine is DifferenceEngine but most of the time you will use it indirectly via the Diff class.

You can influence the engine by providing (custom) implementations for various interfaces and by setting a couple of options on the XMLUnit class.

More information is available in Section3, “Comparing Pieces of XML”.

2.2.2.Validating

All validation happens in the Validator class. The default is to validate against a DTD, but XML Schema validation can be enabled by an option (see Validator.useXMLSchema).

Several options of the XMLUnit class affect validation.

More information is available in Section4, “Validating XML Documents”.

2.2.3.XSLT Transformations

The Transform class provides an easy to use layer on top of JAXP's transformations. An instance of this class is initialized with the source document and a stylesheet and the result of the transformation can be retrieved as a String or DOM Document.

The output of Transform can be used as input to comparisons, validations, XPath tests and so on. There is no detailed sections on transformations since they are really only a different way to create input for the rest of XMLUnit's machinery. Examples can be found in Section1.6, “Comparing XML Transformations”.

It is possible to provide a custom javax.xml.transform.URIResolver via the XMLUnit.setURIResolver method.

You can access the underlying XSLT transformer via XMLUnit.getTransformerFactory.

2.2.4.XPath Engine

The central piece of XMLUnit's XPath support is the XpathEngine interface. Currently two implementations of the interface exist, SimpleXpathEngine and org.custommonkey.xmlunit.jaxp13.Jaxp13XpathEngine.

SimpleXpathEngine is a very basic implementation that uses your XSLT transformer under the covers. This also means it will expose you to the bugs found in your transformer like the transformer claiming a stylesheet couldn't be compiled for very basic XPath expressions. This has been reported to be the case for JDK 1.5.

org.custommonkey.xmlunit.jaxp13.Jaxp13XpathEngine uses JAXP 1.3's javax.xml.xpath package and seems to work more reliable, stable and performant than SimpleXpathEngine.

You use the XMLUnit.newXpathEngine method to obtain an instance of the XpathEngine. As of XMLUnit 1.1 this will try to use JAXP 1.3 if it is available and fall back to SimpleXpathEngine.

Instances of XpathEngine can return the results of XPath queries either as DOM NodeList or plain Strings.

More information is available in Section5, “XPath Tests”.

2.2.5.DOM Tree Walking

To test pieces of XML by traversing the DOM tree you use the NodeTester class. Each DOM Node will be passed to a NodeTester implementation you provide. The AbstractNodeTester class is provided as a NullObject Pattern base class for implementations of your own.

More information is available in Section6, “DOM Tree Walking”.

2.3.Using XMLUnit With JUnit 3.x

Initially XMLUnit was tightly coupled to JUnit and the recommended approach was to write unit tests by inheriting from the XMLTestCase class. XMLTestCase provides a pretty long list of assert... methods that may simplify your interaction with XMLUnit's internals in many common cases.

The XMLAssert class provides the same set of assert...s as static methods. Use XMLAssert instead of XMLTestCase for your unit tests if you can't or don't want to inherit from XMLTestCase.

All power of XMLUnit is available whether you use XMLTestCase and/or XMLAssert or the underlying API directly. If you are using JUnit 3.x then using the specific classes may prove to be more convenient.

2.4.Common Configuration Options

2.4.1.JAXP

If you are using a JDK 1.4 or later, your Java class library already contains the required XML parsers and XSLT transformers. Still you may want to use a different parser/transformer than the one of your JDK - in particular since the versions shipping with some JDKs are known to contain serious bugs.

As described in Section1.4, “Configuring XMLUnit” there are two main approaches to choose the XML parser of XSLT transformer: System properties and setters in the XMLUnit class.

If you use system properties you have the advantage that your choice affects the whole JAXP system, whether it is used inside of XMLUnit or not. If you are using JDK 1.4 or later you may also want to review the Endorsed Standards Override Mechanism to use a different parser/transformer than the one shipping with your JDK.

The second option - using the XMLUnit class - allows you to use different parsers for control and test documents, it even allows you to use different parsers for different test cases, if you really want to stretch it that far. It may also work for JDK 1.4 and above, even if you don't override the endorsed standards libraries.

You can access the underlying JAXP parser by XMLUnit.newControlParser, XMLUnit.newTestParser, XMLUnit.getControlDocumentBuilderFactory, XMLUnit.getTestDocumentBuilderFactory and XMLUnit.getSAXParserFactory (used by Validator). Note that all these methods return factories or parsers that are namespace aware.

The various build... methods in XMLUnit provide convenience layers for building DOM Documents using the configured parsers.

You can also set the class name for the XPathFactory to use when using JAXP 1.3 by passing the class name to XMLUnit.setXPathFactory.

2.4.2.EntityResolver

You can provide a custom org.xml.sax.EntityResolver for the control and test parsers via XMLUnit.setControlEntityResolver and XMLUnit.setTestEntityResolver. Validator uses the resolver set via setControlEntityResolver as well.

2.4.3.Element Content Whitespace

Element content whitespace - also known as ignorable whitespace - is whitespace contained in elements whose content model doesn't allow text content. I.e. the newline and space characters between <foo> and <bar> in the following example could belong into this category.

<foo>
  <bar/></foo>

Using XMLUnit.setIgnoreWhitespace it is possible to make the test and control parser ignore this kind of whitespace.

Note that setting this property to true usually doesn't have any effect since it only works on validating parsers and XMLUnit doesn't enable validation by default. It does have an effect when comparing pieces of XML, though, since the same flag is used for a different purpose as well in that case. See Section3.8.1, “Whitespace Handling” for more details.

2.4.4.XSLT Stylesheet Version

Some features of XMLUnit use XSLT stylesheets under the covers, in particular XSLT will be used to strip element content whitespace or comments as well as by SimpleXpathEngine. These stylesheets only require a XSLT transformer that supports XSLT 1.0 and will say so in the stylesheet element.

If your XSLT transformer supports XSLT 2.0 or newer it may[6] issue a warning for these stylesheets which can be annoying. You can use XMLUnit.setXSLTVersion to make XMLUnit change the version attribute to a different value. Note that XMLUnit hasn't been tested with a value other than "1.0".

2.5.Providing Input to XMLUnit

Most methods in XMLUnit that expect a piece of XML as input provide several overloads that obtain their input from different sources. The most common options are:

  • A DOM Document.

    Here you have all control over the document's creation. Such a Document could as well be the result of an XSLT transformation via the Transform class.

  • A SAX InputSource.

    This is the most generic way since InputSource allows you to read from arbitrary InputStreams or Readers. Use an InputStream wrapped by an InputSource if you want the XML parser to pick up the proper encoding from the XML declaration.

  • A String.

    Here a DOM Document is built from the input String using the JAXP parser specified for control or test documents - depending on whether the input is a control or test piece of XML.

    Note that using a String assumes that your XML has already been converted from its XML encoding to a Java String upfront.

  • A Reader.

    Here a DOM Document is built from the input Reader using the JAXP parser specified for control or test documents - depending on whether the input is a control or test piece of XML.

    Note that using a Reader is a bad choice if your XML encoding is different from your platform's default encoding since Java's IO system won't read your XML declaration. It is a good practice to use one of the other overloads rather than the Reader version to ensure encoding has been dealt with properly.



[6] The W3C recommendation says it SHOULD.

xmlunit-1.6/tests/0000755000000000000000000000000012451007364012722 5ustar rootrootxmlunit-1.6/tests/java/0000755000000000000000000000000012451007364013643 5ustar rootrootxmlunit-1.6/tests/java/org/0000755000000000000000000000000012451007364014432 5ustar rootrootxmlunit-1.6/tests/java/org/custommonkey/0000755000000000000000000000000012451007364017167 5ustar rootrootxmlunit-1.6/tests/java/org/custommonkey/xmlunit/0000755000000000000000000000000012451007364020667 5ustar rootrootxmlunit-1.6/tests/java/org/custommonkey/xmlunit/jaxp13/0000755000000000000000000000000012451007364021775 5ustar rootrootxmlunit-1.6/tests/java/org/custommonkey/xmlunit/jaxp13/test_Validator.java0000644000000000000000000002434012451007364025627 0ustar rootroot/* ****************************************************************** Copyright (c) 2008,2011,2014, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.jaxp13; import java.io.File; import java.io.IOException; import java.util.Iterator; import java.util.List; import javax.xml.transform.stream.StreamSource; import junit.framework.TestCase; import org.custommonkey.xmlunit.test_Constants; import org.custommonkey.xmlunit.exceptions.XMLUnitRuntimeException; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; public class test_Validator extends TestCase { public void testGoodSchemaIsValid() throws Exception { Validator v = new Validator(); v.addSchemaSource(new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/Book.xsd"))); assertTrue(v.isSchemaValid()); } public void testGoodSchemaIsValidWithCustomPrefix() throws Exception { Validator v = new Validator(); v.addSchemaSource(new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/Book.xsd2"))); assertTrue(v.isSchemaValid()); } public void testGoodSchemaHasNoErrors() throws Exception { Validator v = new Validator(); v.addSchemaSource(new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/Book.xsd"))); assertEquals(0, v.getSchemaErrors().size()); } public void testBrokenSchemaIsInvalid() throws Exception { Validator v = new Validator(); v.addSchemaSource(new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/broken.xsd"))); assertFalse(v.isSchemaValid()); } public void testBrokenSchemaHasErrors() throws Exception { Validator v = new Validator(); v.addSchemaSource(new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/broken.xsd"))); List l = v.getSchemaErrors(); for (Iterator i = l.iterator(); i.hasNext(); ) { Object ex = i.next(); assertTrue(ex instanceof SAXParseException); /* System.err.println(ex); */ } assertTrue(l.size() > 0); } public void testGoodInstanceIsValid() throws Exception { Validator v = new Validator(); v.addSchemaSource(new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/Book.xsd"))); StreamSource s = new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/BookXsdGenerated.xml")); assertTrue(v.isInstanceValid(s)); } public void testGoodInstanceIsValidNoSchemaSource() throws Exception { Validator v = new Validator(); StreamSource s = new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/BookXsdGeneratedWithFixedSchemaLocation.xml")); assertTrue(v.isInstanceValid(s)); } public void testBadInstanceIsInvalid() throws Exception { Validator v = new Validator(); v.addSchemaSource(new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/Book.xsd"))); StreamSource s = new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/invalidBook.xml")); assertFalse(v.isInstanceValid(s)); } public void testBadInstanceHasErrors() throws Exception { Validator v = new Validator(); v.addSchemaSource(new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/Book.xsd"))); StreamSource s = new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/invalidBook.xml")); List l = v.getInstanceErrors(s); for (Iterator i = l.iterator(); i.hasNext(); ) { Object ex = i.next(); assertTrue(ex instanceof SAXParseException); /* System.err.println(ex); */ } assertTrue(l.size() > 0); } public void testInstanceValidationOfBrokenSchema() { Validator v = new Validator(); v.addSchemaSource(new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/broken.xsd"))); StreamSource s = new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/BookXsdGenerated.xml")); try { v.isInstanceValid(s); fail("expected exception because schema is invalid"); } catch (XMLUnitRuntimeException e) { assertTrue(e.getCause() instanceof SAXException); } } public void testInstanceValidationOfMissingFile() { Validator v = new Validator(); v.addSchemaSource(new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/Book.xsd"))); StreamSource s = new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/not there.xml")); try { v.isInstanceValid(s); fail("expected exception because instance doesn't exist"); } catch (XMLUnitRuntimeException e) { assertTrue(e.getCause() instanceof IOException); } } /** * fails unless you manage to setup JAXP 1.3 and RELAX NG support * *

The setup that worked for Stefan when he wrote this test: * JDK 1.5.0_09, isorelax-jaxp-bridge-1.0, together with msv.jar, * isorelax.jar, relaxngDatatype.jar and xsdlib.jar from msv's * latest nightly build (2008-02-13, actually). The same jars do * not work with Java6.

* * @see http://weblogs.java.net/blog/kohsuke/archive/2006/02/validate_xml_us.html */ public void XtestGoodRelaxNGSchemaIsValid() throws Exception { Validator v = new Validator(javax.xml.XMLConstants.RELAXNG_NS_URI); v.addSchemaSource(new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/Book.rng"))); assertTrue(v.isSchemaValid()); } /** * fails unless you manage to setup JAXP 1.3 and RELAX NG support * @see #XtestGoodRelaxNGSchemaIsValid() */ public void XtestGoodInstanceIsValidRNG() throws Exception { Validator v = new Validator(javax.xml.XMLConstants.RELAXNG_NS_URI); v.addSchemaSource(new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/Book.rng"))); StreamSource s = new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/BookXsdGeneratedNoSchema.xml")); assertTrue(v.isInstanceValid(s)); } /** * fails unless you manage to setup JAXP 1.3 and RELAX NG support * @see #XtestGoodRelaxNGSchemaIsValid() */ public void XtestBadInstanceIsInvalidRNG() throws Exception { Validator v = new Validator(javax.xml.XMLConstants.RELAXNG_NS_URI); v.addSchemaSource(new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/Book.rng"))); StreamSource s = new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/invalidBook.xml")); List l = v.getInstanceErrors(s); for (Iterator i = l.iterator(); i.hasNext(); ) { Object ex = i.next(); assertTrue(ex instanceof SAXParseException); /* System.err.println(ex); */ } assertTrue(l.size() > 0); } /** * fails even using the setup in XtestGoodRelaxNGSchemaIsValid() * since a SAXParser is trying to read the compact syntax * definition and chokes on it not being XML. * @see #XtestGoodRelaxNGSchemaIsValid() */ public void XtestGoodRelaxNGCompactSyntaxIsValid() throws Exception { Validator v = new Validator(javax.xml.XMLConstants.RELAXNG_NS_URI); v.addSchemaSource(new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/Book.rngc"))); assertTrue(v.isSchemaValid()); StreamSource s = new StreamSource(new File(test_Constants.BASEDIR + "/tests/etc/BookXsdGeneratedNoSchema.xml")); assertTrue(v.isInstanceValid(s)); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/jaxp13/test_Jaxp13XpathEngine.java0000644000000000000000000000412112451007364027076 0ustar rootroot/* ****************************************************************** Copyright (c) 2006-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.jaxp13; import org.custommonkey.xmlunit.AbstractXpathEngineTests; import org.custommonkey.xmlunit.XpathEngine; /** * JUnit test for Jaxp13XpathEngine */ public class test_Jaxp13XpathEngine extends AbstractXpathEngineTests { protected XpathEngine newXpathEngine() { return new Jaxp13XpathEngine(); } public test_Jaxp13XpathEngine(String name) { super(name); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/jaxp13/test_XMLUnitNamespaceContext2Jaxp13.java0000644000000000000000000001503112451007364031432 0ustar rootroot/* ****************************************************************** Copyright (c) 200, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.jaxp13; import org.custommonkey.xmlunit.NamespaceContext; import org.custommonkey.xmlunit.SimpleNamespaceContext; import java.util.HashMap; import java.util.Iterator; import javax.xml.XMLConstants; import junit.framework.TestCase; /** * JUnit test for XMLUnitNamespaceContext2Jaxp13 */ public class test_XMLUnitNamespaceContext2Jaxp13 extends TestCase { private static final String[] PREFIXES = {"foo", "bar"}; private static final String[] STANDARD_PREFIXES = { XMLConstants.XML_NS_PREFIX, XMLConstants.XMLNS_ATTRIBUTE }; private static final String[] STANDARD_URIS = { XMLConstants.XML_NS_URI, XMLConstants.XMLNS_ATTRIBUTE_NS_URI }; private static final String URI = "urn:example"; public void testBasics() { XMLUnitNamespaceContext2Jaxp13 ctx = new XMLUnitNamespaceContext2Jaxp13(new SimpleNamespaceContext(setupMap())); validate(ctx); } public void testCannotOverrideStandardPrefixes() { HashMap m = setupMap(); for (int i = 0; i < STANDARD_PREFIXES.length; i++) { m.put(STANDARD_PREFIXES[i], URI); } XMLUnitNamespaceContext2Jaxp13 ctx = new XMLUnitNamespaceContext2Jaxp13(new SimpleNamespaceContext(m)); validate(ctx); } public void testCannotOverrideStandardURIs() { HashMap m = setupMap(); for (int i = 0; i < STANDARD_PREFIXES.length; i++) { m.put(STANDARD_PREFIXES[i] + "1", STANDARD_URIS[i]); } XMLUnitNamespaceContext2Jaxp13 ctx = new XMLUnitNamespaceContext2Jaxp13(new SimpleNamespaceContext(m)); validate(ctx); } public void testDefaultNamespaceHandling() { HashMap m = setupMap(); m.put("", URI); XMLUnitNamespaceContext2Jaxp13 ctx = new XMLUnitNamespaceContext2Jaxp13(new SimpleNamespaceContext(m)); // no matter how many prefixes map to it, DEFAULT_NS must be // the first prefix assertEquals(XMLConstants.DEFAULT_NS_PREFIX, ctx.getPrefix(URI)); Iterator it = ctx.getPrefixes(URI); assertTrue(it.hasNext()); assertEquals(XMLConstants.DEFAULT_NS_PREFIX, it.next()); assertAllPrefixesFound(it); } private static HashMap setupMap() { HashMap map = new HashMap(); for (int i = 0; i < PREFIXES.length; i++) { map.put(PREFIXES[i], URI); } return map; } private static void validate(XMLUnitNamespaceContext2Jaxp13 ctx) { for (int i = 0; i < PREFIXES.length; i++) { assertEquals(URI, ctx.getNamespaceURI(PREFIXES[i])); } for (int i = 0; i < STANDARD_PREFIXES.length; i++) { assertEquals(STANDARD_URIS[i], ctx.getNamespaceURI(STANDARD_PREFIXES[i])); } assertEquals(XMLConstants.NULL_NS_URI, ctx.getNamespaceURI(PREFIXES[0] + PREFIXES[0])); assertEquals(XMLConstants.NULL_NS_URI, ctx.getNamespaceURI(XMLConstants.DEFAULT_NS_PREFIX)); boolean foundThisPrefix = false; String prefix = ctx.getPrefix(URI); for (int i = 0; !foundThisPrefix && i < PREFIXES.length; i++) { if (PREFIXES[i].equals(prefix)) { foundThisPrefix = true; } } assertTrue("getPrefix returned a known prefix for " + URI, foundThisPrefix); for (int i = 0; i < STANDARD_PREFIXES.length; i++) { assertEquals(STANDARD_PREFIXES[i], ctx.getPrefix(STANDARD_URIS[i])); } assertAllPrefixesFound(ctx.getPrefixes(URI)); for (int i = 0; i < STANDARD_PREFIXES.length; i++) { Iterator it = ctx.getPrefixes(STANDARD_URIS[i]); assertTrue("One element for " + STANDARD_URIS[i], it.hasNext()); assertEquals(STANDARD_PREFIXES[i], it.next()); assertFalse("Only one element for " + STANDARD_URIS[i], it.hasNext()); } assertNull(ctx.getPrefix(URI + URI)); assertFalse(ctx.getPrefixes(URI + URI).hasNext()); } private static void assertAllPrefixesFound(Iterator it) { boolean[] found = new boolean[PREFIXES.length]; int count = 0; while (it.hasNext()) { count++; String p = (String) it.next(); boolean foundThisPrefix = false; for (int i = 0; !foundThisPrefix && i < PREFIXES.length; i++) { if (PREFIXES[i].equals(p)) { found[i] = foundThisPrefix = true; } } if (!foundThisPrefix) { fail("Prefix " + p + " should not be in this context"); } } assertEquals(PREFIXES.length, count); for (int i = 0; i < PREFIXES.length; i++) { assertTrue("Context contained " + PREFIXES[i], found[i]); } } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_ElementNameQualifier.java0000644000000000000000000001161012451007364026624 0ustar rootroot/* ****************************************************************** Copyright (c) 2001, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import org.w3c.dom.Document; import org.w3c.dom.Element; import junit.framework.TestCase; import junit.framework.TestSuite; /** * JUnit testcase for ElementNameEqualifier */ public class test_ElementNameQualifier extends TestCase { private Document document; private ElementNameQualifier elementNameQualifier; private static final String NAME_A = "nameA"; private static final String NAME_B = "nameB"; public void testElementsNoNamespace() throws Exception { Element control = document.createElement(NAME_A); Element test = document.createElement(NAME_A); assertTrue("nameA comparable to nameA", elementNameQualifier.qualifyForComparison(control, test)); test = document.createElement(NAME_B); assertFalse("nameA not comparable to nameB", elementNameQualifier.qualifyForComparison(control, test)); } public void testElementsWithNamespace() throws Exception { String anURI = "gopher://example.com"; String qnameQualifierA = "qnq:"; Element control = document.createElementNS(anURI, qnameQualifierA + NAME_A); Element test = document.createElementNS(anURI, qnameQualifierA + NAME_A); assertTrue("qualified nameA comparable to nameA", elementNameQualifier.qualifyForComparison(control, test)); test = document.createElementNS(anURI, qnameQualifierA + NAME_B); assertFalse("qualified nameA not comparable to nameB", elementNameQualifier.qualifyForComparison(control, test)); String qnameQualifierB = "pgp:"; test = document.createElementNS(anURI, qnameQualifierB + NAME_A); assertTrue("qualified nameA comparable to requalified nameA", elementNameQualifier.qualifyForComparison(control, test)); test = document.createElementNS(anURI, qnameQualifierB + NAME_B); assertFalse("qualified nameA not comparable to requalifiednameB", elementNameQualifier.qualifyForComparison(control, test)); String anotherURI = "ftp://example.com"; test = document.createElementNS(anotherURI, qnameQualifierA + NAME_A); assertFalse("qualified nameA not comparable to anotherURI nameA", elementNameQualifier.qualifyForComparison(control, test)); test = document.createElementNS(anotherURI, qnameQualifierB + NAME_A); assertFalse("qualified nameA comparable to requalified-anotherURI nameA", elementNameQualifier.qualifyForComparison(control, test)); test = document.createElementNS(anotherURI, qnameQualifierB + NAME_B); assertFalse("qualified nameA not comparable to requalified-anotherURI nameB", elementNameQualifier.qualifyForComparison(control, test)); } public void setUp() throws Exception { document = XMLUnit.newControlParser().newDocument(); elementNameQualifier = new ElementNameQualifier(); } public static TestSuite suite() { return new TestSuite(test_ElementNameQualifier.class); } public test_ElementNameQualifier(String name) { super(name); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_Diff.java0000644000000000000000000012343512451007364023451 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2013, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.Reader; import java.util.HashSet; import java.util.Set; import junit.framework.TestCase; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.Text; /** * Test a Diff */ public class test_Diff extends TestCase{ private static final String[] control = new String[]{ "", "", "test", "test", "", "test", "", "", "testtest", "testtest", "Yo this is a test!", "" }; private static final String[] test = new String[]{ "", "", "test", "test", "", "fail", "", "test", "failtest", "testtest", "Yo this isn't a test!", "orgapachetest" }; private Document aDocument; public void setUp() throws Exception { aDocument = XMLUnit.newControlParser().newDocument(); } public void testToString(){ Diff diff = buildDiff(aDocument, aDocument); String[] animals = {"Monkey", "Chicken"}; String tag = "tag"; Element elemA = aDocument.createElement(tag); Text textA = aDocument.createTextNode(animals[0]); elemA.appendChild(textA); Element elemB = aDocument.createElement(tag); Difference difference = new Difference(DifferenceConstants.HAS_CHILD_NODES, new NodeDetail(Boolean.TRUE.toString(), elemA, "/tag"), new NodeDetail(Boolean.FALSE.toString(),elemB, "/tag")); diff.differenceFound(difference); assertEquals(diff.getClass().getName() +"\n[different] Expected " + DifferenceConstants.HAS_CHILD_NODES.getDescription() + " 'true' but was 'false' - comparing at /tag to at /tag\n", diff.toString()); diff = buildDiff(aDocument, aDocument); Text textB = aDocument.createTextNode(animals[1]); elemB.appendChild(textB); difference = new Difference(DifferenceConstants.TEXT_VALUE, new NodeDetail(animals[0], textA, "/tag/text()"), new NodeDetail(animals[1], textB, "/tag/text()")); diff.differenceFound(difference); assertEquals(diff.getClass().getName() +"\n[different] Expected " + DifferenceConstants.TEXT_VALUE.getDescription() + " 'Monkey' but was 'Chicken' - comparing Monkey " + "at /tag/text() to Chicken at /tag/text()\n", diff.toString()); } /** * Tests the compare method */ public void testSimilar() throws Exception { for(int i=0;ipass", "pass"); assertEquals("same should be identical", true, diff.identical()); assertEquals("same should be similar", true, diff.similar()); } public void testMissingElement() throws Exception { Diff diff = buildDiff("", ""); assertEquals("should not be identical", false, diff.identical()); assertEquals("and should not be similar", false, diff.similar()); } public void testExtraElement() throws Exception { Diff diff = buildDiff("", ""); assertEquals("should not be identical", false, diff.identical()); assertEquals("and should not be similar", false, diff.similar()); } public void testElementsInReverseOrder() throws Exception { Diff diff = buildDiff("", ""); assertEquals("should not be identical", false, diff.identical()); assertEquals("but should be similar", true, diff.similar()); } public void testMissingAttribute() throws Exception { Diff diff = buildDiff("pass", "pass"); assertEquals("should not be identical", false, diff.identical()); assertEquals("and should not be similar", false, diff.similar()); } public void testExtraAttribute() throws Exception { Diff diff = buildDiff("pass", "pass"); assertEquals("should not be identical", false, diff.identical()); assertEquals("and should not be similar", false, diff.similar()); } public void testAttributesInReverseOrder() throws Exception { Diff diff = buildDiff("pass", "pass" ); if (diff.identical()) { System.out.println(getName() + " - should not ideally be identical " + "but JAXP implementations can reorder attributes inside NamedNodeMap"); } assertEquals(diff.toString() + ": but should be similar", true, diff.similar()); } public void testDiffStringWithAttributes() throws Exception { final String fruitBat = "", longEaredBat = ""; Diff diff = buildDiff(fruitBat, longEaredBat); assertEquals(diff.getClass().getName() +"\n[different] Expected " + DifferenceConstants.ATTR_VALUE.getDescription() + " 'fruit' but was 'longeared' - comparing " + " at /bat[1]/@type to at /bat[1]/@type\n", diff.toString()); } public void testXMLWithDTD() throws Exception { String aDTDpart = "" + "" + ""; String aDTD = aDTDpart + "]>"; String xmlWithoutDTD = "" + "" + "" + ""; String xmlWithDTD = aDTD + xmlWithoutDTD; Diff diff = buildDiff(xmlWithDTD, xmlWithoutDTD); assertTrue("similar. " + diff.toString(), diff.similar()); assertTrue("not identical. " + diff.toString(), !diff.identical()); File tempDtdFile = File.createTempFile(getName(), "dtd"); tempDtdFile.deleteOnExit(); FileWriter dtdWriter = new FileWriter(tempDtdFile); dtdWriter.write(aDTD); try { String xmlWithExternalDTD = "" + xmlWithoutDTD; diff = buildDiff(xmlWithDTD, xmlWithExternalDTD); assertTrue("similar again. " + diff.toString(), diff.similar()); assertTrue("not identical again. " + diff.toString(), !diff.identical()); } finally { tempDtdFile.delete(); } String anotherDTD = aDTDpart + "" + "]>"; String xmlWithAnotherDTD = anotherDTD + xmlWithoutDTD; diff = buildDiff(xmlWithDTD, xmlWithAnotherDTD); assertTrue("similar. " + diff.toString(), diff.similar()); assertTrue("amd identical as DTD content is not compared. " + diff.toString(), diff.identical()); } /** * Raised by aakture 25.04.2002 * Despite the name under which this defect was raised the issue is really * about managing redundant whitespace */ public void testXMLUnitDoesNotWorkWellWithFiles() throws Exception { // to avoid test sequencing issues we need to restore whitespace setting boolean startValueIgnoreWhitespace = XMLUnit.getIgnoreWhitespace(); try { XMLUnit.setIgnoreWhitespace(false); Diff whitespaceAwareDiff = buildDiff(test_Constants.XML_WITHOUT_WHITESPACE, test_Constants.XML_WITH_WHITESPACE); assertTrue(whitespaceAwareDiff.toString(), !whitespaceAwareDiff.similar()); XMLUnit.setIgnoreWhitespace(true); Diff whitespaceIgnoredDiff = buildDiff(test_Constants.XML_WITHOUT_WHITESPACE, test_Constants.XML_WITH_WHITESPACE); assertTrue(whitespaceIgnoredDiff.toString(), whitespaceIgnoredDiff.similar()); } finally { XMLUnit.setIgnoreWhitespace(startValueIgnoreWhitespace); } } public void testCommentHandlingDoesntAffectWhitespaceHandling() throws Exception { try { XMLUnit.setIgnoreComments(true); testXMLUnitDoesNotWorkWellWithFiles(); } finally { XMLUnit.setIgnoreComments(false); } } public void testNormalizationDoesntAffectWhitespaceHandling() throws Exception { try { XMLUnit.setNormalize(true); testXMLUnitDoesNotWorkWellWithFiles(); } finally { XMLUnit.setNormalize(false); } } /** * Raised 15.05.2002 */ public void testNamespaceIssues() throws Exception { String control = "" + "text"; Replacement replace = new Replacement("control", "test"); String test = replace.replace(control); Diff diff = buildDiff(control, test); assertTrue("a-"+diff.toString(), diff.similar()); assertTrue("b-"+diff.toString(), !diff.identical()); Diff reverseDiff = buildDiff(test, control); assertTrue("c-"+reverseDiff.toString(), reverseDiff.similar()); assertTrue("d-"+reverseDiff.toString(), !reverseDiff.identical()); } /** * Raised 16.05.2002 */ public void testDefaultNamespace() throws Exception { String control = "" + "text"; Replacement replace = new Replacement("control:", ""); Replacement trim = new Replacement("xmlns:control", "xmlns"); String test = trim.replace(replace.replace(control)); Diff diff = buildDiff(control, test); assertTrue("a-"+diff.toString(), diff.similar()); assertTrue("b-"+diff.toString(), !diff.identical()); Diff reverseDiff = buildDiff(test, control); assertTrue("c-"+reverseDiff.toString(), reverseDiff.similar()); assertTrue("d-"+reverseDiff.toString(), !reverseDiff.identical()); } public void testSameNameDifferentQName() throws Exception { String control = "" + "In namespace 1" + "In namespace 2" + ""; String test = "" + "In namespace 2" + "In namespace 1" + ""; Diff diff = buildDiff(control, test); assertTrue("a-"+diff.toString(), diff.similar()); assertTrue("b-"+diff.toString(), !diff.identical()); Diff reverseDiff = buildDiff(test, control); assertTrue("c-"+reverseDiff.toString(), reverseDiff.similar()); assertTrue("d-"+reverseDiff.toString(), !reverseDiff.identical()); } public void testOverrideDifferenceListener() throws Exception { String control = "ford fiesta" +"citroen xsara"; String test = "nissan primera" +"peugot 206"; Diff diff = buildDiff(control, test); assertTrue("initially " + diff.toString(), !diff.similar()); Diff diffWithIdenticalOverride = buildDiff(control, test); diffWithIdenticalOverride.overrideDifferenceListener( new OverrideDifferenceListener( DifferenceListener.RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL)); assertTrue("now identical" + diffWithIdenticalOverride.toString(), diffWithIdenticalOverride.identical()); Diff diffWithSimilarOverride = buildDiff(control, test); diffWithSimilarOverride.overrideDifferenceListener( new OverrideDifferenceListener( DifferenceListener.RETURN_IGNORE_DIFFERENCE_NODES_SIMILAR)); assertTrue("no longer identical" + diffWithSimilarOverride.toString(), !diffWithSimilarOverride.identical()); assertTrue("but still similar" + diffWithSimilarOverride.toString(), diffWithSimilarOverride.similar()); Diff diffWithOverride = buildDiff(control, test); diffWithOverride.overrideDifferenceListener( new OverrideDifferenceListener( DifferenceListener.RETURN_ACCEPT_DIFFERENCE)); assertTrue("default behaviour" + diffWithOverride.toString(), !diffWithOverride.similar()); } public void testNamespacedAttributes() throws Exception { FileReader control = new FileReader(test_Constants.BASEDIR + "/tests/etc/controlNamespaces.xml"); FileReader test = new FileReader(test_Constants.BASEDIR + "/tests/etc/testNamespaces.xml"); Diff diff = buildDiff(control, test); diff.overrideDifferenceListener( new ExpectedDifferenceListener(DifferenceConstants.NAMESPACE_PREFIX_ID)); assertEquals(diff.toString(), false, diff.identical()); assertEquals(diff.toString(), true, diff.similar()); } public void testDifferentStructure() throws Exception { String control = "text"; String test = "text"; Diff myDiff = buildDiff(control, test); assertEquals(myDiff.toString(), false, myDiff.similar()); } public void testRepeatedElementNamesWithAttributeQualification1() throws Exception { Diff diff = buildDiff("", ""); diff.overrideElementQualifier(new ElementNameAndAttributeQualifier("id")); assertFalse("should not be identical: " + diff.toString(), diff.identical()); assertTrue("should be similar: " + diff.toString(), diff.similar()); } public void testRepeatedElementNamesWithAttributeQualification2() throws Exception { Diff diff = buildDiff("", ""); diff.overrideElementQualifier(new ElementNameAndAttributeQualifier("id")); assertFalse("should not be identical: " + diff.toString(), diff.identical()); assertFalse("should not be similar: " + diff.toString(), diff.similar()); } public void testRepeatedElementNamesWithAttributeQualification3() throws Exception { Diff diff = buildDiff("", ""); diff.overrideElementQualifier(new ElementNameAndAttributeQualifier()); assertFalse("should not be identical: " + diff.toString(), diff.identical()); assertTrue("should be similar: " + diff.toString(), diff.similar()); } public void testRepeatedElementNamesWithAttributeQualification4() throws Exception { Diff diff = buildDiff("", ""); diff.overrideElementQualifier(new ElementNameAndAttributeQualifier()); assertFalse("should not be identical: " + diff.toString(), diff.identical()); assertFalse("should not be similar: " + diff.toString(), diff.similar()); } public void testRepeatedElementNamesWithNamespacedAttributeQualification() throws Exception { Diff diff = buildDiff("" + "", "" + ""); diff.overrideElementQualifier(new ElementNameAndAttributeQualifier()); diff.overrideDifferenceListener(new ExpectedDifferenceListener( new int[] {DifferenceConstants.NAMESPACE_PREFIX_ID, DifferenceConstants.CHILD_NODELIST_SEQUENCE_ID})); assertFalse("should not be identical: " + diff.toString(), diff.identical()); assertTrue("should be similar: " + diff.toString(), diff.similar()); } public void testRepeatedElementNamesWithTextQualification() throws Exception { Diff diff = buildDiff("12", "21"); diff.overrideElementQualifier(new ElementNameAndTextQualifier()); diff.overrideDifferenceListener( new ExaminingExpectedDifferenceListener(DifferenceConstants.CHILD_NODELIST_SEQUENCE_ID) { private int i=0; protected void examineDifferenceContents(Difference difference) { ++i; assertEquals("/root[1]/node[" + i +"]", difference.getControlNodeDetail().getXpathLocation()); } }); assertFalse("should not be identical: " + diff.toString(), diff.identical()); assertTrue("should be similar: " + diff.toString(), diff.similar()); } // defect raised by Kevin Krouse Jan 2003 public void testXMLNSNumberOfAttributes() throws Exception { Diff diff = buildDiff("", ""); assertTrue(diff.toString(), diff.similar()); assertFalse(diff.toString(), diff.identical()); } protected Diff buildDiff(Document control, Document test) { return new Diff(control, test); } protected Diff buildDiff(String control, String test) throws Exception { return new Diff(control, test); } protected Diff buildDiff(Reader control, Reader test) throws Exception { return new Diff(control, test); } protected Diff buildDiff(String control, String test, DifferenceEngine engine) throws Exception { return new Diff(XMLUnit.buildControlDocument(control), XMLUnit.buildTestDocument(test), engine); } /** * Construct a test * @param name Test name */ public test_Diff(String name){ super(name); } private class OverrideDifferenceListener implements DifferenceListener { private final int overrideValue; private OverrideDifferenceListener(int overrideValue) { this.overrideValue = overrideValue; } public int differenceFound(Difference difference) { return overrideValue; } public void skippedComparison(Node control, Node test) { } } private class ExpectedDifferenceListener implements DifferenceListener { private final Set expectedIds; private ExpectedDifferenceListener(int expectedIdValue) { this(new int[] {expectedIdValue}); } private ExpectedDifferenceListener(int[] expectedIdValues) { this.expectedIds = new HashSet(expectedIdValues.length); for (int i=0; i < expectedIdValues.length; ++i) { expectedIds.add(new Integer(expectedIdValues[i])); } } public int differenceFound(Difference difference) { assertTrue(difference.toString(), expectedIds.contains(new Integer(difference.getId()))); examineDifferenceContents(difference); return RETURN_ACCEPT_DIFFERENCE; } public void skippedComparison(Node control, Node test) { } protected void examineDifferenceContents(Difference difference) { } } private abstract class ExaminingExpectedDifferenceListener extends ExpectedDifferenceListener { private ExaminingExpectedDifferenceListener(int expectedIdValue) { super(expectedIdValue); } protected abstract void examineDifferenceContents(Difference difference) ; } public void testIssue1189681() throws Exception { String left = "" + "\n" + "100\n" + " \n" + "Cow\n" + " \n" + " \n" + "Sheep\n" + " \n" + ""; String right = "" + "\n" + " \n" + "Sheep\n" + " \n" + " 100\n" + " \n" + " Cow\n" + " \n" + ""; assertFalse(buildDiff(left, right).similar()); } public void testCDATANoIgnore() throws Exception { String expected = "Hello"; String actual = ""; assertFalse(buildDiff(expected, actual).similar()); assertFalse(buildDiff(expected, actual).identical()); } public void testCDATAIgnore() throws Exception { try { XMLUnit.setIgnoreDiffBetweenTextAndCDATA(true); String expected = "Hello"; String actual = ""; assertTrue(buildDiff(expected, actual).similar()); assertTrue(buildDiff(expected, actual).identical()); } finally { XMLUnit.setIgnoreDiffBetweenTextAndCDATA(false); } } public void testCommentHandling() throws Exception { String xml1 = " "; String xml2 = " "; try { assertFalse(buildDiff(xml1, xml2).identical()); assertFalse(buildDiff(xml1, xml2).similar()); XMLUnit.setIgnoreComments(true); assertTrue(buildDiff(xml1, xml2).identical()); assertTrue(buildDiff(xml1, xml2).similar()); } finally { XMLUnit.setIgnoreComments(false); } } public void testWhitespaceHandlingDoesntAffectCommentHandling() throws Exception { try { XMLUnit.setIgnoreWhitespace(true); testCommentHandling(); } finally { XMLUnit.setIgnoreWhitespace(false); } } public void testNormalizationDoesntAffectCommentHandling() throws Exception { try { XMLUnit.setNormalize(true); testCommentHandling(); } finally { XMLUnit.setNormalize(false); } } public void testNormalization() throws Exception { Document control = XMLUnit.newControlParser().newDocument(); Element root = control.createElement("root"); control.appendChild(root); root.appendChild(control.createTextNode("Text 1")); root.appendChild(control.createTextNode(" and 2")); Element inner = control.createElement("inner"); root.appendChild(inner); inner.appendChild(control.createTextNode("Text 3 and 4")); Document test = XMLUnit.newTestParser().newDocument(); root = test.createElement("root"); test.appendChild(root); root.appendChild(test.createTextNode("Text 1 and 2")); inner = test.createElement("inner"); root.appendChild(inner); inner.appendChild(test.createTextNode("Text 3")); inner.appendChild(test.createTextNode(" and 4")); assertFalse(buildDiff(control, test).identical()); try { XMLUnit.setNormalize(true); assertTrue(buildDiff(control, test).identical()); assertTrue(buildDiff(control, test).similar()); } finally { XMLUnit.setNormalize(false); } assertFalse(buildDiff(control, test).similar()); } // fails with Java 5 and later public void XtestWhitespaceHandlingDoesntAffectNormalization() throws Exception { try { XMLUnit.setIgnoreWhitespace(true); testNormalization(); } finally { XMLUnit.setIgnoreWhitespace(false); } } // fails with Java 5 and later public void XtestCommentHandlingDoesntAffectNormalization() throws Exception { try { XMLUnit.setIgnoreComments(true); testNormalization(); } finally { XMLUnit.setIgnoreComments(false); } } public void testNormalizedWhitespace() throws Exception { String xml1 = "a = b;"; String xml2 = "\r\n\ta =\tb; \r\n"; try { assertFalse(buildDiff(xml1, xml2).identical()); assertFalse(buildDiff(xml1, xml2).similar()); XMLUnit.setNormalizeWhitespace(true); assertTrue(buildDiff(xml1, xml2).identical()); assertTrue(buildDiff(xml1, xml2).similar()); } finally { XMLUnit.setNormalizeWhitespace(false); } } /** * inspired by {@link * http://day-to-day-stuff.blogspot.com/2007/05/comparing-xml-in-junit-test.html * Erik von Oosten's Weblog}, made us implement special handling * of schemaLocation. */ public void testNamespacePrefixDiff() throws Exception { String xml1 = "" + "" + "" + ""; String xml2 = "" + "" + "" + ""; Diff d = buildDiff(xml1, xml2); assertFalse(d.toString(), d.identical()); assertTrue(d.toString(), d.similar()); } /** * Bug Report 1779701 * @see http://sourceforge.net/tracker/index.php?func=detail&aid=1779701&group_id=23187&atid=377768 */ public void testWhitespaceAndNamespaces() throws Exception { String control = "" + "\r\n " + "\r\n "; String test = "" + "" + ""; XMLUnit.setIgnoreWhitespace(true); try { Diff diff = buildDiff(control, test); assertTrue(diff.toString(), diff.identical()); } finally { XMLUnit.setIgnoreWhitespace(false); } } /** * Bug Report 1863632 * @see http://sourceforge.net/tracker/index.php?func=detail&aid=1863632&group_id=23187&atid=377768 */ public void testBasicWhitespaceHandling() throws Exception { String control = ""; String test = "\r\n \r\n"; XMLUnit.setIgnoreWhitespace(true); try { Diff diff = buildDiff(control, test); assertTrue(diff.toString(), diff.identical()); } finally { XMLUnit.setIgnoreWhitespace(false); } } public void testUpgradingOfRecoverableDifference() throws Exception { String control = ""; String test = ""; Diff diff = buildDiff(control, test); assertFalse(diff.toString(), diff.identical()); assertTrue(diff.toString(), diff.similar()); diff = buildDiff(control, test); diff.overrideDifferenceListener(new DifferenceListener() { public int differenceFound(Difference d) { return RETURN_UPGRADE_DIFFERENCE_NODES_DIFFERENT; } public void skippedComparison(Node c, Node t) { fail("skippedComparison shouldn't get invoked"); } }); assertFalse(diff.toString(), diff.identical()); assertFalse(diff.toString(), diff.similar()); } public void testMatchTrackerSetViaOverride() throws Exception { Diff diff = buildDiff("", ""); final int[] count = new int[1]; diff.overrideMatchTracker(new MatchTracker() { public void matchFound(Difference d) { count[0]++; } }); assertTrue(diff.identical()); // NODE_TYPE (not null), NODE_TYPE(Document), NAMESPACE_URI(none), // NAMESPACE_PREFIX(none), HAS_DOCTYPE_DECLARATION(no), // HAS_CHILD_NODES(true) // // NODE_TYPE(Element), NAMESPACE_URI(none), // NAMESPACE_PREFIX(none), ELEMENT_TAG_NAME(foo), // ELEMENT_NUM_ATTRIBUTE(none), HAS_CHILD_NODES(false), // CHILD_NODELIST_LENGTH(0) assertEquals(13, count[0]); } public void testMatchTrackerSetViaEngine() throws Exception { final int[] count = new int[1]; DifferenceEngine engine = new DifferenceEngine(new ComparisonController() { public boolean haltComparison(Difference afterDifference) { fail("haltComparison invoked"); // NOTREACHED return false; } }, new MatchTracker() { public void matchFound(Difference d) { count[0]++; } }); Diff diff = buildDiff("", "", engine); assertTrue(diff.identical()); // NODE_TYPE (not null), NODE_TYPE(Document), NAMESPACE_URI(none), // NAMESPACE_PREFIX(none), HAS_DOCTYPE_DECLARATION(no), // HAS_CHILD_NODES(true) // // NODE_TYPE(Element), NAMESPACE_URI(none), // NAMESPACE_PREFIX(none), ELEMENT_TAG_NAME(foo), // ELEMENT_NUM_ATTRIBUTE(none), HAS_CHILD_NODES(false), // CHILD_NODELIST_LENGTH(0) assertEquals(13, count[0]); } public void testMatchTrackerSetViaOverrideOnEngine() throws Exception { DifferenceEngine engine = new DifferenceEngine(new ComparisonController() { public boolean haltComparison(Difference afterDifference) { fail("haltComparison invoked"); // NOTREACHED return false; } }); Diff diff = buildDiff("", "", engine); final int[] count = new int[1]; diff.overrideMatchTracker(new MatchTracker() { public void matchFound(Difference d) { count[0]++; } }); assertTrue(diff.identical()); // NODE_TYPE (not null), NODE_TYPE(Document), NAMESPACE_URI(none), // NAMESPACE_PREFIX(none), HAS_DOCTYPE_DECLARATION(no), // HAS_CHILD_NODES(true) // // NODE_TYPE(Element), NAMESPACE_URI(none), // NAMESPACE_PREFIX(none), ELEMENT_TAG_NAME(foo), // ELEMENT_NUM_ATTRIBUTE(none), HAS_CHILD_NODES(false) // CHILD_NODELIST_LENGTH(0) assertEquals(13, count[0]); } public void testCDATAAndIgnoreWhitespace() throws Exception { String control = "" + ""; String test = "" +"" +" " +" " +" " +" " +" " +""; XMLUnit.setIgnoreWhitespace(true); XMLUnit.setIgnoreDiffBetweenTextAndCDATA(true); try { Diff diff = buildDiff(control, test); assertTrue(diff.toString(), diff.similar()); } finally { XMLUnit.setIgnoreWhitespace(false); XMLUnit.setIgnoreDiffBetweenTextAndCDATA(false); } } /** * Not a real test. Need something that actually fails unless I * set the flag. */ public void testEntityExpansion() throws Exception { String control = "bla bla"; String test = "bla bla"; //XMLUnit.setExpandEntityReferences(true); try { Diff diff = buildDiff(control, test); assertTrue(diff.toString(), diff.similar()); } finally { XMLUnit.setExpandEntityReferences(false); } } /** * @see https://sourceforge.net/tracker/?func=detail&aid=2807167&group_id=23187&atid=377768 */ public void testIssue2807167() throws Exception { String test = "" + "" + "" + "" + "" + "" + ""; String control = "" + "" + "" + "" + "" + "" + ""; Diff diff = new Diff(control, test); diff.overrideElementQualifier(new ElementNameAndAttributeQualifier()); assertTrue(diff.toString(), diff.similar()); } /** * @see http://sourceforge.net/tracker/?func=detail&atid=377768&aid=3602981&group_id=23187 */ public void testXsiTypeSpecialCase() throws Exception { String test = ""; String control = ""; Diff diff = new Diff(control, test); assertTrue(diff.toString(), diff.similar()); } public void testXsiTypeSpecialCaseShortLocalName() throws Exception { String test = ""; String control = ""; Diff diff = new Diff(control, test); assertTrue(diff.toString(), diff.similar()); } public void testXsiTypeSpecialCaseWorksWithDefaultNs() throws Exception { String test = ""; String control = ""; Diff diff = new Diff(control, test); assertTrue(diff.toString(), diff.similar()); } public void testXsiTypeSpecialCaseInheritsParentNs() throws Exception { String test = "" + "" + ""; String control = "" + "" + ""; Diff diff = new Diff(control, test); assertTrue(diff.toString(), diff.similar()); } public void testXsiTypeSpecialCaseDoesntIgnorePrefix() throws Exception { String test = ""; String control = ""; Diff diff = new Diff(control, test); assertFalse(diff.toString(), diff.similar()); } public void testXsiNil() throws Exception { String test = ""; String control = ""; Diff diff = new Diff(control, test); assertFalse(diff.toString(), diff.similar()); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_XMLUnit.java0000644000000000000000000001242312451007364024073 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import javax.xml.parsers.DocumentBuilderFactory; import junit.framework.TestCase; import junit.framework.TestSuite; import org.custommonkey.xmlunit.exceptions.ConfigurationException; import org.w3c.dom.Document; import org.xml.sax.EntityResolver; import org.xml.sax.helpers.DefaultHandler; /** * Test case for XMLUnit */ public class test_XMLUnit extends TestCase{ /** * Contructs a new test case. */ public test_XMLUnit(String name){ super(name); } private String getDocumentBuilderFactoryImplClass() { return DocumentBuilderFactory.newInstance().getClass().getName(); } /** * Test overiding the SAX parser used to parse control documents */ public void testSetControlParser() throws Exception { Object before = XMLUnit.newControlParser(); XMLUnit.setControlParser(getDocumentBuilderFactoryImplClass()); assertEquals("should be different", false, before == XMLUnit.newControlParser()); } public void testIgnoreWhitespace() throws Exception { assertEquals("should not ignore whitespace by default", false, XMLUnit.getIgnoreWhitespace()); XMLUnit.setIgnoreWhitespace(true); String test=" monkey "; String control="monkey"; assertEquals("Should be similar", true, new Diff(control, test).similar()); try { XMLUnit.setIgnoreWhitespace(false); assertEquals("Should be different", false, new Diff(control, test).similar()); } finally { // restore default setting XMLUnit.setIgnoreWhitespace(false); } } /** * Test overiding the SAX parser used to parse test documents */ public void testSetTestParser() throws Exception { Object before = XMLUnit.newTestParser(); XMLUnit.setTestParser(getDocumentBuilderFactoryImplClass()); assertEquals("should be different", false, before==XMLUnit.newTestParser()); } public void testSetTransformerFactory() throws Exception { Object before = XMLUnit.getTransformerFactory(); XMLUnit.setTransformerFactory(before.getClass().getName()); assertEquals("should be different", false, before==XMLUnit.getTransformerFactory()); } public void testStripWhitespaceTransform() throws Exception { Document doc = XMLUnit.buildTestDocument( test_Constants.XML_WITH_WHITESPACE); Transform transform = XMLUnit.getStripWhitespaceTransform(doc); Diff diff = new Diff(test_Constants.XML_WITHOUT_WHITESPACE, transform); assertTrue(diff.similar()); } public void testXSLTVersion() { try { assertEquals("1.0", XMLUnit.getXSLTVersion()); assertEquals(XSLTConstants.XSLT_START, XMLUnit.getXSLTStart()); XMLUnit.setXSLTVersion("2.0"); assertTrue(XMLUnit.getXSLTStart() .startsWith(XSLTConstants.XSLT_START_NO_VERSION)); assertTrue(XMLUnit.getXSLTStart().endsWith("\"2.0\">")); try { XMLUnit.setXSLTVersion("foo"); fail("foo is not a number"); } catch (ConfigurationException expected) { } try { XMLUnit.setXSLTVersion("-1.0"); fail("-1.0 is negative"); } catch (ConfigurationException expected) { } } finally { XMLUnit.setXSLTVersion("1.0"); } } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_NodeDescriptor.java0000644000000000000000000001602712451007364025523 0ustar rootroot/* ****************************************************************** Copyright (c) 200, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.io.File; import junit.framework.TestCase; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.Text; /** * JUnit test for NodeDescriptor */ public class test_NodeDescriptor extends TestCase { private StringBuffer stringBuffer; private Document aDocument; private NodeDetail nodeDetail; public void testAppendDocumentDetail() throws Exception { nodeDetail = new NodeDetail("", aDocument, "/"); NodeDescriptor.appendNodeDetail(stringBuffer, nodeDetail); assertEquals("<" + NodeDescriptor.DOCUMENT_NODE_DESCRIPTION + "<...>> at /", stringBuffer.toString()); } public void testAppendAttributeDetail() throws Exception { String attrName = "attrName"; String attrValue = "attrValue"; Attr attr = aDocument.createAttribute(attrName); attr.setValue(attrValue); String tagName = "elemTag"; Element element = aDocument.createElement(tagName); element.setAttributeNode(attr); nodeDetail = new NodeDetail("", attr, "/elemTag"); NodeDescriptor.appendNodeDetail(stringBuffer, nodeDetail); assertEquals("<" + tagName + " " + attrName + "=\"" + attrValue + "\"...> at /elemTag", stringBuffer.toString()); } public void testAppendElementDetail() throws Exception { String tagName = "elemTag"; Element element = aDocument.createElement(tagName); nodeDetail = new NodeDetail("", element, "/elemTag"); NodeDescriptor.appendNodeDetail(stringBuffer, nodeDetail); assertEquals("<" + tagName + "...> at /elemTag", stringBuffer.toString()); } public void testAppendTextDetail() throws Exception { String textString = "some text"; Text text = aDocument.createTextNode(textString); String tagName = "elemTag"; Element element = aDocument.createElement(tagName); element.appendChild(text); nodeDetail = new NodeDetail("", text, "/elemTag/text()"); NodeDescriptor.appendNodeDetail(stringBuffer, nodeDetail); assertEquals("<" + tagName + " ...>" + textString + " at /elemTag/text()", stringBuffer.toString()); } public void testAppendProcessingInstructionDetail() throws Exception { String target = "PItarget"; String data = "PIdata"; Node processingInstruction = aDocument.createProcessingInstruction(target, data); nodeDetail = new NodeDetail("", processingInstruction, "/processing-instruction()"); NodeDescriptor.appendNodeDetail(stringBuffer, nodeDetail); assertEquals(" at /processing-instruction()", stringBuffer.toString()); } public void testAppendCommentDetail() throws Exception { String comments = "This is a comment"; Node comment = aDocument.createComment(comments); nodeDetail = new NodeDetail("", comment, "/comment()"); NodeDescriptor.appendNodeDetail(stringBuffer, nodeDetail); assertEquals(" at /comment()", stringBuffer.toString()); } public void testAppendCDataDetail() throws Exception { String cData = "<>& etc"; Node cDataNote = aDocument.createCDATASection(cData); nodeDetail = new NodeDetail("", cDataNote, "/text()"); NodeDescriptor.appendNodeDetail(stringBuffer, nodeDetail); assertEquals(" at /text()", stringBuffer.toString()); } public void testAppendDocTypeDetail() throws Exception { File dtdA = File.createTempFile(getName() + "A", "dtd"); dtdA.deleteOnExit(); String systemOnlyDTD = ""; String someContent = "ignored"; String xmlWithExternalDTD = systemOnlyDTD + someContent; aDocument = XMLUnit.buildControlDocument(xmlWithExternalDTD); Node doctypeA = aDocument.getDoctype(); nodeDetail = new NodeDetail("", doctypeA, "/"); NodeDescriptor.appendNodeDetail(stringBuffer, nodeDetail); assertEquals(systemOnlyDTD + " at /", stringBuffer.toString()); stringBuffer = new StringBuffer(); File dtdB = File.createTempFile(getName() + "B", "dtd"); dtdB.deleteOnExit(); String publicDTD = ""; String someOtherContent = ""; String xmlWithPublicDTD = publicDTD + someOtherContent; Document bDocument = XMLUnit.buildControlDocument(xmlWithPublicDTD); Node doctypeB = bDocument.getDoctype(); nodeDetail = new NodeDetail("", doctypeB, "/"); NodeDescriptor.appendNodeDetail(stringBuffer, nodeDetail); assertEquals(publicDTD + " at /", stringBuffer.toString()); } public void setUp() throws Exception { aDocument = XMLUnit.newControlParser().newDocument(); stringBuffer = new StringBuffer(); } /** * Constructor for test_NodeDescriptor. * @param name */ public test_NodeDescriptor(String name) { super(name); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_SimpleXpathEngine.java0000644000000000000000000000630212451007364026156 0ustar rootroot/* ****************************************************************** Copyright (c) 200, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import javax.xml.transform.OutputKeys; import org.w3c.dom.Node; /** * JUnit test for SimpleXpathEngine */ public class test_SimpleXpathEngine extends AbstractXpathEngineTests { private SimpleXpathEngine simpleXpathEngine = new SimpleXpathEngine(); protected XpathEngine newXpathEngine() { return simpleXpathEngine; } public void testGetXPathResultNode() throws Exception { Node result = simpleXpathEngine.getXPathResultNode("test", testDocument); SimpleSerializer serializer = new SimpleSerializer(); serializer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); assertEquals(testString, serializer.serialize(result.getFirstChild())); } public void testGetMatchingNodesMatchText() throws Exception { if (isJava5OrNewer()) { // fails with "more recent" version of Xalan shipping with Java5 return; } super.testGetMatchingNodesMatchText(); } public void testEvaluate() throws Exception { if (isJava5OrNewer()) { // fails with "more recent" version of Xalan shipping with Java5 return; } super.testEvaluate(); } public test_SimpleXpathEngine(String name) { super(name); } private static boolean isJava5OrNewer() { try { Class.forName("java.net.Proxy"); return true; } catch (Exception e) { return false; } } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/AbstractDoctypeTests.java0000644000000000000000000001122112451007364025645 0ustar rootroot/* ****************************************************************** Copyright (c) 200, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.io.IOException; import junit.framework.TestCase; /** * JUnit test for DoctypeReader and DoctypeInputStream */ public abstract class AbstractDoctypeTests extends TestCase { private static final String COMMENT = ""; protected static final String NO_DTD = "one"; public abstract void testGetContent() throws IOException; protected abstract void assertEquals(String expected, String input, String docType, String systemId) throws IOException; public void testRead() throws IOException { String oz = "Chirurgische Verbesserungen sind g\u00fcnstig"; assertEquals("" + oz, oz, "Kylie", "bumJob"); } public void testInternalDTD() throws IOException { assertEquals("", test_Constants.CHUCK_JONES_RIP_DTD_DECL, "ni", "shrubbery"); } public void testExternalDTD() throws IOException { assertEquals("", "", "ni", "shrubbery"); } public void testNoDTD() throws IOException { assertEquals("" + NO_DTD, NO_DTD, "ni", "shrubbery"); } public void testNoDTDButXMLDecl() throws IOException { assertEquals(test_Constants.XML_DECLARATION + "" + NO_DTD, test_Constants.XML_DECLARATION + NO_DTD, "ni", "shrubbery"); } public void testInternalDTDWithComment() throws IOException { assertEquals(test_Constants.XML_DECLARATION + "" + COMMENT, test_Constants.XML_DECLARATION + COMMENT + test_Constants.CHUCK_JONES_RIP_DTD_DECL, "ni", "shrubbery"); } public void testExternalDTDWithComment() throws IOException { assertEquals("" + COMMENT, COMMENT + "", "ni", "shrubbery"); } public void testNoDTDWithComment() throws IOException { assertEquals("" + COMMENT + NO_DTD, COMMENT + NO_DTD, "ni", "shrubbery"); } public void testNoDTDButXMLDeclWithComment() throws IOException { assertEquals(test_Constants.XML_DECLARATION + "" + COMMENT + NO_DTD, test_Constants.XML_DECLARATION + COMMENT + NO_DTD, "ni", "shrubbery"); } public AbstractDoctypeTests(String name) { super(name); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_Validator.java0000644000000000000000000002775412451007364024535 0ustar rootroot/* ****************************************************************** Copyright (c) 200, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import junit.framework.AssertionFailedError; import junit.framework.TestSuite; import org.w3c.dom.Document; import org.xml.sax.InputSource; import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.StringBufferInputStream; import java.io.StringReader; /** * JUnit test for Validator * Also includes tests for XMLTestCase assertValidXML methods * because test values etc are here */ public class test_Validator extends XMLTestCase { private Validator validator; private File tempDTDFile; public void testXSchema() throws Exception{ File xsdFile = new File(test_Constants.BASEDIR + "/tests/etc/Book.xsd"); assertTrue("xsdFile " + xsdFile.getAbsolutePath() + " exists", xsdFile.exists()); File xmlFile = new File(test_Constants.BASEDIR + "/tests/etc/BookXsdGenerated.xml"); assertTrue("xmlFile " + xmlFile.getAbsolutePath() + " exists", xmlFile.exists()); validator = new Validator(new InputSource(new FileInputStream(xmlFile))); validator.useXMLSchema(true); assertTrue("Schema " + validator.toString(), validator.isValid()); } public void testIsValidGood() throws Exception { String toonXML = test_Constants.XML_DECLARATION + test_Constants.CHUCK_JONES_RIP_DTD_DECL + test_Constants.CHUCK_JONES_RIP_XML; validator = new Validator(new StringReader(toonXML)); assertTrue("toonXML " + validator.toString(), validator.isValid()); // test XMLTestCase passXMLTestCaseTest(toonXML); passXMLTestCaseTest(validator); String noXMLDeclaration = test_Constants.CHUCK_JONES_RIP_DTD_DECL + test_Constants.CHUCK_JONES_RIP_XML; validator = new Validator(new StringReader(noXMLDeclaration)); assertEquals("noXMLDeclaration " + validator.toString(), true, validator.isValid()); // test XMLTestCase passXMLTestCaseTest(noXMLDeclaration); passXMLTestCaseTest(validator); } public void testIsValidExternalSystemId() throws Exception { writeTempDTDFile(); assertTrue(tempDTDFile.getAbsolutePath(), tempDTDFile.exists()); String externalDTD = test_Constants.XML_DECLARATION + test_Constants.DOCUMENT_WITH_GOOD_EXTERNAL_DTD; String tempDTDUrl = tempDTDFile.toURL().toExternalForm(); validator = new Validator(new StringReader(externalDTD), tempDTDUrl); assertTrue("externalDTD " + validator.toString(), validator.isValid()); // test XMLTestCase passXMLTestCaseTest(externalDTD, tempDTDFile.toURL().toExternalForm()); passXMLTestCaseTest(validator); String noDTD = test_Constants.XML_DECLARATION + test_Constants.DOCUMENT_WITH_NO_EXTERNAL_DTD; validator = new Validator(new StringReader(noDTD), tempDTDFile.toURL().toExternalForm()); assertFalse("noDTD " + validator.toString(), validator.isValid()); // test XMLTestCase failXMLTestCaseTest(noDTD, tempDTDFile.toURL().toExternalForm()); failXMLTestCaseTest(validator); } public void testIsValidNoDTD() throws Exception { writeTempDTDFile(); assertTrue(tempDTDFile.getAbsolutePath(), tempDTDFile.exists()); String noDTD = test_Constants.CHUCK_JONES_RIP_XML; String systemid = tempDTDFile.toURL().toExternalForm(); String doctype = "cartoons"; String notDoctype = "anima"; validator = new Validator(new StringReader(noDTD), systemid, doctype); assertTrue(validator.toString(), validator.isValid()); // test XMLTestCase passXMLTestCaseTest(noDTD, systemid, doctype); passXMLTestCaseTest(validator); // and Document constructor Document document = getDocument(noDTD); validator = new Validator(document, systemid, doctype); assertTrue("Document " + validator.toString(), validator.isValid()); validator = new Validator(new StringReader(noDTD), systemid, notDoctype); assertFalse(validator.toString(), validator.isValid()); // test XMLTestCase failXMLTestCaseTest(noDTD, systemid, notDoctype); failXMLTestCaseTest(validator); // and Document constructor validator = new Validator(document, systemid, notDoctype); assertFalse("Document " + validator.toString(), validator.isValid()); } public void testIsValidBad() throws Exception { String noDTD = test_Constants.XML_DECLARATION + test_Constants.CHUCK_JONES_RIP_XML; validator = new Validator(new StringReader(noDTD)); assertFalse("noDTD " + validator.toString(), validator.isValid()); // test XMLTestCase failXMLTestCaseTest(noDTD); failXMLTestCaseTest(validator); String dtdTwice = test_Constants.XML_DECLARATION + test_Constants.CHUCK_JONES_RIP_DTD_DECL + test_Constants.CHUCK_JONES_RIP_DTD_DECL + test_Constants.CHUCK_JONES_RIP_XML; validator = new Validator(new StringReader(dtdTwice)); assertFalse("dtdTwice " + validator.toString(), validator.isValid()); // test XMLTestCase failXMLTestCaseTest(dtdTwice); failXMLTestCaseTest(validator); String invalidXML = test_Constants.XML_DECLARATION + test_Constants.CHUCK_JONES_RIP_DTD_DECL + test_Constants.CHUCK_JONES_SPINNING_IN_HIS_GRAVE_XML; validator = new Validator(new StringReader(invalidXML)); assertFalse("invalidXML " + validator.toString(), validator.isValid()); // test XMLTestCase failXMLTestCaseTest(invalidXML); failXMLTestCaseTest(validator); } private Document getDocument(String fromXML) throws Exception { return XMLUnit.buildControlDocument(fromXML); } private void removeTempDTDFile() throws Exception { if (tempDTDFile.exists()) { tempDTDFile.delete(); } } private void writeTempDTDFile() throws Exception { FileWriter writer = new FileWriter(tempDTDFile); writer.write(test_Constants.CHUCK_JONES_RIP_DTD); writer.close(); } public void setUp() throws Exception { tempDTDFile = new File(test_Constants.EXTERNAL_DTD); removeTempDTDFile(); } public void tearDown() throws Exception { removeTempDTDFile(); } // ---- XMLTestCase methods ---- private void passXMLTestCaseTest(String xml) throws Exception { assertXMLValid(xml); assertXMLValid(new InputSource(new StringReader(xml))); assertXMLValid(new InputSource(new StringBufferInputStream(xml))); } private void passXMLTestCaseTest(String xml, String systemId) throws Exception { assertXMLValid(xml, systemId); assertXMLValid(new InputSource(new StringReader(xml)), systemId); assertXMLValid(new InputSource(new StringBufferInputStream(xml)), systemId); } private void passXMLTestCaseTest(String xml, String systemId, String doctype) throws Exception { assertXMLValid(xml, systemId, doctype); assertXMLValid(new InputSource(new StringReader(xml)), systemId, doctype); assertXMLValid(new InputSource(new StringBufferInputStream(xml)), systemId, doctype); } private void passXMLTestCaseTest(Validator validator) throws Exception { assertXMLValid(validator); } private void failXMLTestCaseTest(String xml, String systemId) throws Exception { try { assertXMLValid(xml, systemId); fail("Expected assertion to fail!"); } catch (AssertionFailedError e) { // expecting this } try { assertXMLValid(new InputSource(new StringReader(xml)), systemId); fail("Expected assertion to fail!"); } catch (AssertionFailedError e) { // expecting this } try { assertXMLValid(new InputSource(new StringBufferInputStream(xml)), systemId); fail("Expected assertion to fail!"); } catch (AssertionFailedError e) { // expecting this } } private void failXMLTestCaseTest(String xml) throws Exception { try { assertXMLValid(xml); fail("Expected assertion to fail!"); } catch (AssertionFailedError e) { // expecting this } try { assertXMLValid(new InputSource(new StringReader(xml))); fail("Expected assertion to fail!"); } catch (AssertionFailedError e) { // expecting this } try { assertXMLValid(new InputSource(new StringBufferInputStream(xml))); fail("Expected assertion to fail!"); } catch (AssertionFailedError e) { // expecting this } } private void failXMLTestCaseTest(String xml, String systemId, String doctype) throws Exception { try { assertXMLValid(xml, systemId, doctype); fail("Expected assertion to fail!"); } catch (AssertionFailedError e) { // expecting this } try { assertXMLValid(new InputSource(new StringReader(xml)), systemId, doctype); fail("Expected assertion to fail!"); } catch (AssertionFailedError e) { // expecting this } try { assertXMLValid(new InputSource(new StringBufferInputStream(xml)), systemId, doctype); fail("Expected assertion to fail!"); } catch (AssertionFailedError e) { // expecting this } } private void failXMLTestCaseTest(Validator validator) throws Exception { try { assertXMLValid(validator); fail("Expected assertion to fail!"); } catch (AssertionFailedError e) { // expecting this } } public test_Validator(String name) { super(name); } public static TestSuite suite() { return new TestSuite(test_Validator.class); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_XMLTestCase.java0000644000000000000000000006440012451007364024671 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2011,2014 Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.StringReader; import java.util.HashMap; import java.util.Map; import junit.framework.AssertionFailedError; import junit.framework.TestSuite; import org.w3c.dom.Document; import org.w3c.dom.Node; /** * Test case used to test the XMLTestCase */ public class test_XMLTestCase extends XMLTestCase { private static final String PREFIX = "foo"; private static final String TEST_NS = "urn:org.example"; private static final NamespaceContext NS_CONTEXT; static { HashMap m = new HashMap(); m.put(PREFIX, TEST_NS); NS_CONTEXT = new SimpleNamespaceContext(m); } private final String[] control = new String[]{ "", "", "test", "test", "", "test", "", "", "testtest", "testtest" }; private final String[] test = new String[]{ "", "", "test", "test", "", "fail", "", "test", "failtest", "testtest" }; /** * Test for the compareXML method. */ public void testCompareXMLStrings() throws Exception { for(int i=0;i" + "controlDocument"; private static final String xpathValuesTestXML = "" + "testDocument"; private static final String xpathValuesControlXMLNS = addNamespaceToDocument(xpathValuesControlXML); private static final String xpathValuesTestXMLNS = addNamespaceToDocument(xpathValuesTestXML); public void testXpathValuesEqualUsingDocument() throws Exception { Document controlDocument = XMLUnit.buildControlDocument(xpathValuesControlXML); Document testDocument = XMLUnit.buildTestDocument(xpathValuesTestXML); assertXpathValuesEqual("//text()", "//inner/text()", controlDocument); assertXpathValuesEqual("//inner/@attr", controlDocument, "//outer/@attr", testDocument); assertXpathValuesNotEqual("//inner/text()", "//outer/@attr", controlDocument); assertXpathValuesNotEqual("//inner/text()", controlDocument, "//text()", testDocument); } public void testXpathValuesEqualUsingDocumentNS() throws Exception { Document controlDocument = XMLUnit.buildControlDocument(xpathValuesControlXMLNS); Document testDocument = XMLUnit.buildTestDocument(xpathValuesTestXMLNS); assertXpathValuesNotEqual("//text()", "//inner/text()", controlDocument); XMLUnit.setXpathNamespaceContext(NS_CONTEXT); assertXpathValuesEqual("//text()", "//" + PREFIX + ":inner/text()", controlDocument); assertXpathValuesEqual("//" + PREFIX + ":inner/@attr", controlDocument, "//" + PREFIX + ":outer/@attr", testDocument); assertXpathValuesNotEqual("//" + PREFIX + ":inner/text()", "//" + PREFIX + ":outer/@attr", controlDocument); assertXpathValuesNotEqual("//" + PREFIX + ":inner/text()", controlDocument, "//text()", testDocument); } public void testXpathValuesEqualUsingString() throws Exception { assertXpathValuesEqual("//text()", "//inner/text()", xpathValuesControlXML); assertXpathValuesEqual("//inner/@attr", xpathValuesControlXML, "//outer/@attr", xpathValuesTestXML); assertXpathValuesNotEqual("//inner/text()", "//outer/@attr", xpathValuesControlXML); assertXpathValuesNotEqual("//inner/text()", xpathValuesControlXML, "//text()", xpathValuesTestXML); } public void testXpathValuesEqualUsingStringNS() throws Exception { assertXpathValuesNotEqual("//text()", "//inner/text()", xpathValuesControlXMLNS); XMLUnit.setXpathNamespaceContext(NS_CONTEXT); assertXpathValuesEqual("//text()", "//" + PREFIX + ":inner/text()", xpathValuesControlXMLNS); assertXpathValuesEqual("//" + PREFIX + ":inner/@attr", xpathValuesControlXMLNS, "//" + PREFIX + ":outer/@attr", xpathValuesTestXMLNS); assertXpathValuesNotEqual("//" + PREFIX + ":inner/text()", "//" + PREFIX + ":outer/@attr", xpathValuesControlXMLNS); assertXpathValuesNotEqual("//" + PREFIX + ":inner/text()", xpathValuesControlXMLNS, "//text()", xpathValuesTestXMLNS); } public void testXpathEvaluatesTo() throws Exception { assertXpathEvaluatesTo("urk", "//outer/@attr", xpathValuesControlXML); try { assertXpathEvaluatesTo("yum", "//inner/@attr", xpathValuesControlXML); fail("Expected assertion to fail #1"); } catch (AssertionFailedError e) { } assertXpathEvaluatesTo("2", "count(//@attr)", xpathValuesControlXML); Document testDocument = XMLUnit.buildTestDocument(xpathValuesTestXML); assertXpathEvaluatesTo("ugh", "//inner/@attr", testDocument); try { assertXpathEvaluatesTo("yeah", "//outer/@attr", testDocument); fail("Expected assertion to fail #2"); } catch (AssertionFailedError e) { } } public void testXpathEvaluatesToNS() throws Exception { try { assertXpathEvaluatesTo("urk", "//outer/@attr", xpathValuesControlXMLNS); fail("Expected assertion to fail #1"); } catch (AssertionFailedError e) { } XMLUnit.setXpathNamespaceContext(NS_CONTEXT); assertXpathEvaluatesTo("urk", "//" + PREFIX + ":outer/@attr", xpathValuesControlXMLNS); try { assertXpathEvaluatesTo("yum", "//" + PREFIX + ":inner/@attr", xpathValuesControlXMLNS); fail("Expected assertion to fail #2"); } catch (AssertionFailedError e) { } assertXpathEvaluatesTo("2", "count(//@attr)", xpathValuesControlXMLNS); Document testDocument = XMLUnit.buildTestDocument(xpathValuesTestXMLNS); assertXpathEvaluatesTo("ugh", "//" + PREFIX + ":inner/@attr", testDocument); try { assertXpathEvaluatesTo("yeah", "//" + PREFIX + ":outer/@attr", testDocument); fail("Expected assertion to fail #3"); } catch (AssertionFailedError e) { } } public void testNodeTest() throws Exception { NodeTester tester = new CountingNodeTester(1); assertNodeTestPasses(xpathValuesControlXML, tester, Node.TEXT_NODE); try { assertNodeTestPasses(xpathValuesControlXML, tester, Node.ELEMENT_NODE); fail("Expected node test failure #1!"); } catch (AssertionFailedError e) { } NodeTest test = new NodeTest(new StringReader(xpathValuesTestXML)); tester = new CountingNodeTester(4); assertNodeTestPasses(test, tester, new short[] {Node.TEXT_NODE, Node.ELEMENT_NODE}, true); assertNodeTestPasses(test, tester, new short[] {Node.TEXT_NODE, Node.COMMENT_NODE}, false); try { assertNodeTestPasses(test, tester, new short[] {Node.TEXT_NODE, Node.ELEMENT_NODE}, false); fail("Expected node test failure #2!"); assertNodeTestPasses(test, tester, new short[] {Node.TEXT_NODE, Node.COMMENT_NODE}, true); fail("Expected node test failure #3!"); } catch (AssertionFailedError e) { } } public void testXMLValid() { // see test_Validator class } private static final String TREES_OPEN = ""; private static final String TREES_CLOSE = ""; private static final String xpathNodesControlXML = TREES_OPEN + "oak" + "ash" + "scots pine" + "spruce" + "" + "magnolia" + "" + "" + "apple" + "" + TREES_CLOSE; private static final String xpathNodesTestXML = TREES_OPEN + "oak" + "ash" + "scots pine" + "spruce" + "cherry" + "apple" + "" + "magnolia" + "" + "apple" + TREES_CLOSE; public void testXpathsEqual() throws Exception { Document controlDoc = XMLUnit.buildControlDocument(xpathNodesControlXML); Document testDoc = XMLUnit.buildTestDocument(xpathNodesTestXML); String[] controlXpath = new String[]{"/trees/tree[@evergreen]", "//tree[@evergreen='false']", "/trees/favourite", "//fruit/apples"}; String[] testXpath = {controlXpath[0], controlXpath[1], "//favourite", "//apples"}; // test positive passes for (int i=0; i < controlXpath.length; ++i) { assertXpathsEqual(controlXpath[i], controlDoc, testXpath[i], testDoc); assertXpathsEqual(controlXpath[i], xpathNodesControlXML, testXpath[i], xpathNodesTestXML); assertXpathsEqual(controlXpath[i], testXpath[i], controlDoc); assertXpathsEqual(controlXpath[i], testXpath[i], xpathNodesControlXML); } // test negative fails for (int i=0; i < controlXpath.length; ++i) { try { assertXpathsNotEqual(controlXpath[i], controlDoc, testXpath[i], testDoc); fail("should not be notEqual!"); } catch (AssertionFailedError e) { } try { assertXpathsNotEqual(controlXpath[i], xpathNodesControlXML, testXpath[i], xpathNodesTestXML); fail("should not be notEqual!"); } catch (AssertionFailedError e) { } try { assertXpathsNotEqual(controlXpath[i], testXpath[i], controlDoc); fail("should not be notEqual!"); } catch (AssertionFailedError e) { } try { assertXpathsNotEqual(controlXpath[i], testXpath[i], xpathNodesControlXML); fail("should not be notEqual!"); } catch (AssertionFailedError e) { } } } public void testXpathsNotEqual() throws Exception { Document controlDoc = XMLUnit.buildControlDocument(xpathNodesControlXML); Document testDoc = XMLUnit.buildTestDocument(xpathNodesTestXML); String[] controlXpath = new String[]{"/trees/tree[@evergreen]", "//tree[@evergreen='false']", "/trees/favourite", "//fruit/apples"}; String[] testXpath = {"//tree", "//tree[@evergreen='true']", "//favourite/apples", "//apples/tree"}; // test positive passes for (int i=0; i < controlXpath.length; ++i) { assertXpathsNotEqual(controlXpath[i], controlDoc, testXpath[i], testDoc); assertXpathsNotEqual(controlXpath[i], xpathNodesControlXML, testXpath[i], xpathNodesTestXML); assertXpathsNotEqual(controlXpath[i], testXpath[i], controlDoc); assertXpathsNotEqual(controlXpath[i], testXpath[i], xpathNodesControlXML); } // test negative fails for (int i=0; i < controlXpath.length; ++i) { try { assertXpathsEqual(controlXpath[i], controlDoc, testXpath[i], testDoc); fail("should not be Equal!"); } catch (AssertionFailedError e) { } try { assertXpathsEqual(controlXpath[i], xpathNodesControlXML, testXpath[i], xpathNodesTestXML); fail("should not be Equal!"); } catch (AssertionFailedError e) { } try { assertXpathsEqual(controlXpath[i], testXpath[i], controlDoc); fail("should not be Equal!"); } catch (AssertionFailedError e) { } try { assertXpathsEqual(controlXpath[i], testXpath[i], xpathNodesControlXML); fail("should not be Equal!"); } catch (AssertionFailedError e) { } } } public void testDocumentAssertXpathExists() throws Exception { Document controlDoc = XMLUnit.buildControlDocument(xpathNodesControlXML); assertXpathExists("/trees/fruit/apples/yum", controlDoc); assertXpathExists("//tree[@evergreen='false']", controlDoc); try { assertXpathExists("//tree[@evergreen='idunno']", controlDoc); fail("Xpath does not exist"); } catch (AssertionFailedError e) { // expected } } public void testStringAssertXpathExists() throws Exception { assertXpathExists("/trees/fruit/apples/yum", xpathNodesControlXML); assertXpathExists("//tree[@evergreen='false']", xpathNodesControlXML); try { assertXpathExists("//tree[@evergreen='idunno']", xpathNodesControlXML); fail("Xpath does not exist"); } catch (AssertionFailedError e) { // expected } } public void testDocumentAssertNotXpathExists() throws Exception { Document controlDoc = XMLUnit.buildControlDocument(xpathNodesControlXML); assertXpathNotExists("//tree[@evergreen='idunno']", controlDoc); try { assertXpathNotExists("/trees/fruit/apples/yum", controlDoc); fail("Xpath does exist, once"); } catch (AssertionFailedError e) { // expected } try { assertXpathNotExists("//tree[@evergreen='false']", controlDoc); fail("Xpath does exist many times"); } catch (AssertionFailedError e) { // expected } } public void testStringAssertNotXpathExists() throws Exception { assertXpathNotExists("//tree[@evergreen='idunno']", xpathNodesControlXML); try { assertXpathNotExists("/trees/fruit/apples/yum", xpathNodesControlXML); fail("Xpath does exist, once"); } catch (AssertionFailedError e) { // expected } try { assertXpathNotExists("//tree[@evergreen='false']", xpathNodesControlXML); fail("Xpath does exist many times"); } catch (AssertionFailedError e) { // expected } } // Bug 585555 public void testUnusedNamespacesDontMatter() throws Exception { boolean startValueIgnoreWhitespace = XMLUnit.getIgnoreWhitespace(); try { XMLUnit.setIgnoreWhitespace(true); String a = "\n" + "\n" + " 5\n" + "\n"; String b = "\n" + "\n" + " 5\n" + "\n"; assertXMLEqual(a, b); } finally { XMLUnit.setIgnoreWhitespace(startValueIgnoreWhitespace); } } // Bug 585555 public void testNamespaceMatters() throws Exception { boolean startValueIgnoreWhitespace = XMLUnit.getIgnoreWhitespace(); try { XMLUnit.setIgnoreWhitespace(true); String a = "\n" + "\n" + ""; String b = "\n" + "\n" + "\n"; assertXMLNotEqual(a, b); } finally { XMLUnit.setIgnoreWhitespace(startValueIgnoreWhitespace); } } // Bug 741636 public void testXpathCount() throws Exception { assertXpathEvaluatesTo("25", "count(//td)", "

" + "

" + "

" + "

" + "

" + "

" + "

" + "

" + "

" + "

" + "

" + "

" + "

" + "

" + "

" + "

" + "

" + "

" + "

" + "

" + "

" + "

" + "

" + "

" + "

" + "

" + "

"); } // bug 1418497 public void testAssertXpathExistsFails() throws Exception { String xmlDocument = " "; assertXpathExists("/axrtable/schema", xmlDocument); } // bug 3290264 public void testAssertXpathEqualsAndAttributes() throws Exception { assertXpathsNotEqual("/foo/Bar/@a", "/foo/Bar", ""); assertXpathsNotEqual("/foo/Bar/@a", "/foo/Bar/@b", ""); assertXpathsEqual("/foo/Bar/@a", "/foo/Bar/@a", ""); } // https://sourceforge.net/p/xmlunit/feature-requests/25/ public void testXpathEvaluatesToQualifiedName() throws Exception { String faultDocument = "" + "env:Server" + "marche pas" + ""; Map namespaces = new HashMap(); namespaces.put("env11", "http://schemas.xmlsoap.org/soap/envelope/"); XMLUnit.setXpathNamespaceContext(new SimpleNamespaceContext(namespaces)); XMLAssert.assertXpathEvaluatesTo(QualifiedName.valueOf("env11:Server"), "//env11:Envelope/env11:Body/" + "env11:Fault/faultcode", faultDocument); } public test_XMLTestCase(String name) { super(name); } private static String addNamespaceToDocument(String original) { int pos = original.indexOf(">"); return original.substring(0, pos) + " xmlns='" + TEST_NS + "'" + original.substring(pos); } public void tearDown() { XMLUnit.setXpathNamespaceContext(null); } /** * returns the TestSuite containing this test */ public static TestSuite suite(){ return new TestSuite(test_XMLTestCase.class); } } ././@LongLink0000644000000000000000000000015100000000000011600 Lustar rootrootxmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_IgnoreTextAndAttributeValuesDifferenceListener.javaxmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_IgnoreTextAndAttributeValuesDifferenceListener.0000644000000000000000000002272212451007364033174 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2008,2013 Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.util.Iterator; import java.util.List; import junit.framework.TestCase; /** * @author TimBacon */ public class test_IgnoreTextAndAttributeValuesDifferenceListener extends TestCase { private DifferenceListener listener; public void testDifferenceFound() { assertCorrectInterpretation( DifferenceConstants.ATTR_NAME_NOT_FOUND, DifferenceListener.RETURN_ACCEPT_DIFFERENCE); assertCorrectInterpretation( DifferenceConstants.ATTR_SEQUENCE, DifferenceListener.RETURN_ACCEPT_DIFFERENCE); assertCorrectInterpretation( DifferenceConstants.ATTR_VALUE, DifferenceListener.RETURN_IGNORE_DIFFERENCE_NODES_SIMILAR); assertCorrectInterpretation( DifferenceConstants.ATTR_VALUE_EXPLICITLY_SPECIFIED, DifferenceListener.RETURN_IGNORE_DIFFERENCE_NODES_SIMILAR); assertCorrectInterpretation( DifferenceConstants.CDATA_VALUE, DifferenceListener.RETURN_ACCEPT_DIFFERENCE); assertCorrectInterpretation( DifferenceConstants.CHILD_NODELIST_LENGTH, DifferenceListener.RETURN_ACCEPT_DIFFERENCE); assertCorrectInterpretation( DifferenceConstants.CHILD_NODELIST_SEQUENCE, DifferenceListener.RETURN_ACCEPT_DIFFERENCE); assertCorrectInterpretation( DifferenceConstants.COMMENT_VALUE, DifferenceListener.RETURN_ACCEPT_DIFFERENCE); assertCorrectInterpretation( DifferenceConstants.DOCTYPE_NAME, DifferenceListener.RETURN_ACCEPT_DIFFERENCE); assertCorrectInterpretation( DifferenceConstants.DOCTYPE_PUBLIC_ID, DifferenceListener.RETURN_ACCEPT_DIFFERENCE); assertCorrectInterpretation( DifferenceConstants.DOCTYPE_SYSTEM_ID, DifferenceListener.RETURN_ACCEPT_DIFFERENCE); assertCorrectInterpretation( DifferenceConstants.ELEMENT_NUM_ATTRIBUTES, DifferenceListener.RETURN_ACCEPT_DIFFERENCE); assertCorrectInterpretation( DifferenceConstants.ELEMENT_TAG_NAME, DifferenceListener.RETURN_ACCEPT_DIFFERENCE); assertCorrectInterpretation( DifferenceConstants.HAS_CHILD_NODES, DifferenceListener.RETURN_ACCEPT_DIFFERENCE); assertCorrectInterpretation( DifferenceConstants.HAS_DOCTYPE_DECLARATION, DifferenceListener.RETURN_ACCEPT_DIFFERENCE); assertCorrectInterpretation( DifferenceConstants.NAMESPACE_PREFIX, DifferenceListener.RETURN_ACCEPT_DIFFERENCE); assertCorrectInterpretation( DifferenceConstants.NAMESPACE_URI, DifferenceListener.RETURN_ACCEPT_DIFFERENCE); assertCorrectInterpretation( DifferenceConstants.NODE_TYPE, DifferenceListener.RETURN_ACCEPT_DIFFERENCE); assertCorrectInterpretation( DifferenceConstants.PROCESSING_INSTRUCTION_DATA, DifferenceListener.RETURN_ACCEPT_DIFFERENCE); assertCorrectInterpretation( DifferenceConstants.PROCESSING_INSTRUCTION_TARGET, DifferenceListener.RETURN_ACCEPT_DIFFERENCE); assertCorrectInterpretation( DifferenceConstants.TEXT_VALUE, DifferenceListener.RETURN_IGNORE_DIFFERENCE_NODES_SIMILAR); } private void assertCorrectInterpretation( Difference difference, int returnValue) { assertEquals(difference.toString(), returnValue, listener.differenceFound(difference)); } public void testClassInUse() throws Exception { String control = "fluffy"; String similarTest = "wispy"; Diff diff = new Diff(control, similarTest); diff.overrideDifferenceListener(listener); assertTrue("similar " + diff.toString(), diff.similar()); assertTrue("but not identical " + diff.toString(), !diff.identical()); DetailedDiff detailedDiff = new DetailedDiff( new Diff(control, similarTest)); assertEquals("2 attribute and 1 text values", 3, detailedDiff.getAllDifferences().size()); String dissimilarTest = ""; Diff dissimilarDiff = new Diff(control, dissimilarTest); dissimilarDiff.overrideDifferenceListener(listener); assertTrue("not similar " + dissimilarDiff.toString(), !dissimilarDiff.similar()); DetailedDiff dissimilarDetailedDiff = new DetailedDiff( new Diff(control, dissimilarTest)); dissimilarDetailedDiff.overrideDifferenceListener(listener); List differences = dissimilarDetailedDiff.getAllDifferences(); assertEquals("wrong number of attributes, missing attribute, different attribute value, presence of subnodes, " + "number of subnodes, missing text node. " + dissimilarDetailedDiff.toString(), 6, differences.size()); int recoverable = 0; for (Iterator iter = differences.iterator(); iter.hasNext(); ) { Difference aDifference = (Difference) iter.next(); if (aDifference.isRecoverable()) { recoverable++; } } assertEquals("attribute value difference has been overridden as similar", 1, recoverable); } public void testIssue771839() throws Exception { String xmlString1 = "" + "22 any street" + "XY0099Z" + ""; String xmlString2 = "" + "EC3M 1EB" + "20 east cheap" + ""; Diff d = new Diff(xmlString1, xmlString2); d.overrideDifferenceListener(listener); assertFalse(d.similar()); assertTrue("postcode was matched against postcode1", d.toString().indexOf("Expected element tag name 'postcode'" + " but was 'postcode1'") > -1); } public void setUp() { listener = new IgnoreTextAndAttributeValuesDifferenceListener(); } /** * Constructor for test_IgnoreTextAndAttributeValuesDifferenceListener. * @param name */ public test_IgnoreTextAndAttributeValuesDifferenceListener(String name) { super(name); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/util/0000755000000000000000000000000012451007364021644 5ustar rootrootxmlunit-1.6/tests/java/org/custommonkey/xmlunit/util/test_IntegerBuffer.java0000644000000000000000000001311612451007364026277 0ustar rootroot/* ****************************************************************** Copyright (c) 200, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.util; import junit.framework.TestCase; /** * Tests for IntegerBuffer */ public class test_IntegerBuffer extends TestCase { public void testToArrayEmpty() { assertNotNull((new IntegerBuffer()).toIntArray()); assertEquals(0, (new IntegerBuffer()).toIntArray().length); } public void testSingleIntAppend() { IntegerBuffer b = new IntegerBuffer(); b.append(1); assertNotNull(b.toIntArray()); assertEquals(1, b.toIntArray().length); assertEquals(1, b.toIntArray()[0]); } public void testArrayAppend() { IntegerBuffer b = new IntegerBuffer(); b.append(new int[] {1, 2}); assertNotNull(b.toIntArray()); assertEquals(2, b.toIntArray().length); for (int i = 0; i < 2; i++) { assertEquals(i + 1, b.toIntArray()[i]); } } public void testSingleIntAppendWithGrowth() { IntegerBuffer b = new IntegerBuffer(1); for (int i = 0; i < 2; i++) { b.append(i); } assertNotNull(b.toIntArray()); assertEquals(2, b.toIntArray().length); for (int i = 0; i < 2; i++) { assertEquals(i, b.toIntArray()[i]); } } public void testArrayAppendWithGrowth() { IntegerBuffer b = new IntegerBuffer(1); b.append(new int[] {1, 2}); assertNotNull(b.toIntArray()); assertEquals(2, b.toIntArray().length); for (int i = 0; i < 2; i++) { assertEquals(i + 1, b.toIntArray()[i]); } } public void testSize() { IntegerBuffer b = new IntegerBuffer(); assertEquals(0, b.size()); b.append(0); assertEquals(1, b.size()); b.append(new int[] {1, 2}); assertEquals(3, b.size()); } public void testCapacity() { IntegerBuffer b = new IntegerBuffer(1); assertEquals(1, b.capacity()); b.append(0); assertEquals(1, b.capacity()); b.append(0); assertTrue(b.capacity() > 1); } public void testIndexOfSimple() { IntegerBuffer b = new IntegerBuffer(); int[] test = new int[] {1, 2, 3}; assertEquals(-1, b.indexOf(test)); b.append(test); assertEquals(0, b.indexOf(test)); b.append(test); assertEquals(0, b.indexOf(test)); } public void testIndexOfWithOffset() { IntegerBuffer b = new IntegerBuffer(); int[] test = new int[] {1, 2, 3}; b.append(0); assertEquals(-1, b.indexOf(test)); b.append(test); assertEquals(1, b.indexOf(test)); } public void testIndexOfWithRepeatedInts() { IntegerBuffer b = new IntegerBuffer(); int[] test = new int[] {1, 2, 3}; b.append(1); assertEquals(-1, b.indexOf(test)); b.append(test); assertEquals(1, b.indexOf(test)); } public void testIndexOfSupSequenceIsThere() { IntegerBuffer b = new IntegerBuffer(); int[] test = new int[] {1, 2, 3}; b.append(new int[] {1, 2}); b.append(4); assertEquals(-1, b.indexOf(test)); } public void testAllBytes() { IntegerBuffer buf = new IntegerBuffer(); for (byte b = Byte.MIN_VALUE; b < Byte.MAX_VALUE; b++) { buf.append(b); } buf.append(Byte.MAX_VALUE); int[] is = buf.toIntArray(); for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) { assertEquals((byte) i, is[i + Math.abs(Byte.MIN_VALUE)]); } } public void testAllChars() { IntegerBuffer buf = new IntegerBuffer(); for (char c = Character.MIN_VALUE; c < Character.MAX_VALUE; c++) { buf.append(c); } buf.append(Character.MAX_VALUE); int[] is = buf.toIntArray(); for (int i = Character.MIN_VALUE; i <= Character.MAX_VALUE; i++) { assertEquals((char) i, is[i + Math.abs(Character.MIN_VALUE)]); } } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_DoctypeInputStream.java0000644000000000000000000001002712451007364026374 0ustar rootroot/* ****************************************************************** Copyright (c) 200, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import junit.framework.TestCase; import junit.framework.TestSuite; /** * JUnit test for DoctypeInputStream */ public class test_DoctypeInputStream extends AbstractDoctypeTests { private File testFile; public void tearDown() { if (testFile != null) { testFile.delete(); } } private FileInputStream testDocument(String content) throws IOException { testFile = File.createTempFile("xmlunit_", ".xml"); FileOutputStream fos = new FileOutputStream(testFile); OutputStreamWriter w = new OutputStreamWriter(fos, "ISO-8859-1"); w.write(content); w.close(); return new FileInputStream(testFile); } private static String readFully(DoctypeInputStream dis) throws IOException { StringBuffer buf = new StringBuffer(); char[] ch = new char[1024]; int numChars; InputStreamReader reader = new InputStreamReader(dis, "ISO-8859-1"); while ((numChars = reader.read(ch))!=-1) { buf.append(ch, 0, numChars); } return buf.toString(); } protected void assertEquals(String expected, String input, String docType, String systemId) throws IOException { FileInputStream fis = null; try { fis = testDocument(input); DoctypeInputStream doctypeInputStream = new DoctypeInputStream(fis, "ISO-8859-1", docType, systemId); assertEquals(expected, readFully(doctypeInputStream)); } finally { if (fis != null) { fis.close(); } } } public void testGetContent() throws IOException { String source = "WooPDeDoO!\nGooRanga!\n plIng! "; DoctypeInputStream dis = new DoctypeInputStream(new java.io.StringBufferInputStream(source), null, "nonsense", "words"); assertEquals(source, dis.getContent(null)); // can get content indefinitely from this stream assertEquals(source, dis.getContent("UTF-8")); } public test_DoctypeInputStream(String name) { super(name); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/SimpleSerializer.java0000644000000000000000000001050412451007364025015 0ustar rootroot/* ****************************************************************** Copyright (c) 200, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.util.Properties; import org.w3c.dom.Node; /** * Simple serialization class that uses a NodeInputStream (and hence * a DOM-to-Stream identity transformation) to perform the work. * This is not an efficient process for serialization, but it is portable * across underlying transform implementations which always comes in handy... * Only used so far for testing hence it's position in the test side of the * source tree. */ public class SimpleSerializer { private final int bufferSize; private final Properties outputProperties; /** * Construct a new instance with default buffer size */ public SimpleSerializer() { this(1024); } /** * Construct a new instance with specific buffer size (handy for large * documents) */ public SimpleSerializer(int bufferSize) { this.bufferSize = bufferSize; outputProperties = new Properties(); } /** * Set an output property for the serialied form * @param name * @param value * @see javax.xml.transform.OutputKeys */ public void setOutputProperty(String name, String value) { outputProperties.setProperty(name, value); } /** * Serialize a Node to a String with default String encoding * @param domNode * @return full String representation of Node * @throws IOException * @throws UnsupportedEncodingException */ public String serialize(Node domNode) throws IOException, UnsupportedEncodingException { return serialize(domNode, null); } /** * Serialize a Node to a String with specific String encoding * @param domNode * @return full String representation of Node * @throws IOException * @throws UnsupportedEncodingException */ public String serialize(Node domNode, String encoding) throws IOException, UnsupportedEncodingException { NodeInputStream nodeStream = new NodeInputStream(domNode, outputProperties); InputStreamReader reader; if (encoding == null) { // use default encoding reader = new InputStreamReader(nodeStream); } else { reader = new InputStreamReader(nodeStream, encoding); } StringBuffer buf = new StringBuffer(); char[] chars = new char[bufferSize]; int len; while ((len = reader.read(chars))!=-1) { buf.append(chars, 0, len); } reader.close(); return buf.toString(); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_SimpleSerializer.java0000644000000000000000000000572012451007364026060 0ustar rootroot/* ****************************************************************** Copyright (c) 200, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import javax.xml.transform.OutputKeys; import junit.framework.TestCase; import junit.framework.TestSuite; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Text; /** * JUnit test for SimpleSerializer */ public class test_SimpleSerializer extends TestCase { private SimpleSerializer serializer ; public void testNode() throws Exception { String simpleXML = "daffodils"; Document doc = XMLUnit.buildControlDocument(simpleXML); serializer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); assertEquals(false, simpleXML.equals(serializer.serialize(doc))); serializer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); assertEquals(simpleXML, serializer.serialize(doc)); Element testElem = doc.createElement("eg"); Text lamb = doc.createTextNode("lamb"); testElem.appendChild(lamb); assertEquals("lamb", serializer.serialize(testElem)); } public void setUp() { serializer = new SimpleSerializer(); } public test_SimpleSerializer(String name) { super(name); } public static TestSuite suite() { return new TestSuite(test_SimpleSerializer.class); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_SimpleNamespaceContext.java0000644000000000000000000000731012451007364027205 0ustar rootroot/* ****************************************************************** Copyright (c) 200, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.util.HashMap; import java.util.Iterator; import junit.framework.TestCase; /** * JUnit test for SimpleNamespaceContext */ public class test_SimpleNamespaceContext extends TestCase { private static final String[] PREFIXES = {"foo", "bar"}; private static final String URI = "urn:example"; public void testEmptyContextIsEmpty() { assertFalse(SimpleNamespaceContext.EMPTY_CONTEXT.getPrefixes() .hasNext()); } public void testSimpleMap() { validate(new SimpleNamespaceContext(setupMap())); } public void testCopyOfMap() { HashMap map = setupMap(); SimpleNamespaceContext ctx = new SimpleNamespaceContext(map); // change a mapping map.put(PREFIXES[0], URI + PREFIXES[0]); // add a new one map.put(PREFIXES[0] + PREFIXES[0], URI); validate(ctx); } private static HashMap setupMap() { HashMap map = new HashMap(); for (int i = 0; i < PREFIXES.length; i++) { map.put(PREFIXES[i], URI); } return map; } private static void validate(NamespaceContext ctx) { for (int i = 0; i < PREFIXES.length; i++) { assertEquals(URI, ctx.getNamespaceURI(PREFIXES[i])); } Iterator it = ctx.getPrefixes(); boolean[] found = new boolean[PREFIXES.length]; int count = 0; while (it.hasNext()) { count++; String p = (String) it.next(); boolean foundThisPrefix = false; for (int i = 0; !foundThisPrefix && i < PREFIXES.length; i++) { if (PREFIXES[i].equals(p)) { found[i] = foundThisPrefix = true; } } if (!foundThisPrefix) { fail("Prefix " + p + " should not be in this context"); } } assertEquals(PREFIXES.length, count); for (int i = 0; i < PREFIXES.length; i++) { assertTrue("Context contained " + PREFIXES[i], found[i]); } } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_BugFixes.java0000644000000000000000000000572512451007364024316 0ustar rootroot/* ****************************************************************** Copyright (c) 200, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Regression test class for various bug fixes */ public class test_BugFixes extends TestCase { public void setUp() throws Exception { XMLUnit.setControlParser("org.apache.xerces.jaxp.DocumentBuilderFactoryImpl"); XMLUnit.setTestParser("org.apache.xerces.jaxp.DocumentBuilderFactoryImpl"); XMLUnit.setSAXParserFactory("org.apache.xerces.jaxp.SAXParserFactoryImpl"); XMLUnit.setTransformerFactory("org.apache.xalan.processor.TransformerFactoryImpl"); } /** * Return the test suite containing the bug fix tests */ public static TestSuite suite() { TestSuite suite = new TestSuite(); suite.addTest(new test_XMLUnit("testStripWhitespaceTransform")); suite.addTest(new test_Diff("testXMLUnitDoesNotWorkWellWithFiles")); suite.addTest(new test_Transform("testXSLIncludeWithoutSystemId")); suite.addTest(new test_Diff("testNamespaceIssues")); suite.addTest(new test_Diff("testDefaultNamespace")); suite.addTest(new test_DetailedDiff("testLargeFiles")); suite.addTest(new test_DetailedDiff("testDifferentStructure")); suite.addTest(new test_Diff("testXMLNSNumberOfAttributes")); return suite; } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/examples/0000755000000000000000000000000012451007364022505 5ustar rootrootxmlunit-1.6/tests/java/org/custommonkey/xmlunit/examples/test_XPathRegexAssert.java0000644000000000000000000000452512451007364027616 0ustar rootroot// -*- Mode: JDE -*- /* ****************************************************************** Copyright (c) 2001, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.examples; public class test_XPathRegexAssert extends junit.framework.TestCase { public void testMatch() throws Exception { XPathRegexAssert.assertXPathMatches("\\d", "/foo/text()", "5"); } public void testNoMatch() throws Exception { try { XPathRegexAssert.assertXPathMatches("\\d", "/foo/text()", "a"); } catch (junit.framework.AssertionFailedError e) { return; } // if we get here, the assertion has passed fail("a is not a number"); } } ././@LongLink0000644000000000000000000000015000000000000011577 Lustar rootrootxmlunit-1.6/tests/java/org/custommonkey/xmlunit/examples/test_RecursiveElementNameAndTextQualifier.javaxmlunit-1.6/tests/java/org/custommonkey/xmlunit/examples/test_RecursiveElementNameAndTextQualifier.j0000644000000000000000000002666312451007364033150 0ustar rootroot/* ****************************************************************** Copyright (c) 2008-2009,2013 Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.examples; import org.w3c.dom.Document; import org.w3c.dom.Element; import junit.framework.TestCase; import junit.framework.TestSuite; import org.custommonkey.xmlunit.Diff; import org.custommonkey.xmlunit.XMLAssert; import org.custommonkey.xmlunit.ElementNameAndTextQualifier; import org.custommonkey.xmlunit.ElementQualifier; import org.custommonkey.xmlunit.XMLUnit; /** * JUnit testcase for RecursiveElementNameAndTextQualifier with most * tests copied from MultiLevelElementNameAndTextQualifier' test. * @see test_Diff#testRepeatedElementNamesWithTextQualification() */ public class test_RecursiveElementNameAndTextQualifier extends TestCase { private static final String TAG_NAME = "tagYoureIt"; private static final String TAG_NAME2 = "tagYoureIt2"; private static final String TEXT_A = "textA"; private static final String TEXT_B = "textB"; private Document document; // copy of ElementNameAndTextQualifier test public void testSingleTextValue() throws Exception { ElementQualifier qualifier = new RecursiveElementNameAndTextQualifier(); Element control = document.createElement(TAG_NAME); control.appendChild(document.createTextNode(TEXT_A)); Element test = document.createElement(TAG_NAME); assertFalse("control text not comparable to empty text", qualifier.qualifyForComparison(control, test)); test.appendChild(document.createTextNode(TEXT_A)); assertTrue("control textA comparable to test textA", qualifier.qualifyForComparison(control, test)); test = document.createElement(TAG_NAME); test.appendChild(document.createTextNode(TEXT_B)); assertFalse("control textA not comparable to test textB", qualifier.qualifyForComparison(control, test)); } // copy of ElementNameAndTextQualifier test public void testMultipleTextValues() throws Exception { ElementQualifier qualifier = new RecursiveElementNameAndTextQualifier(); Element control = document.createElement(TAG_NAME); control.appendChild(document.createTextNode(TEXT_A)); control.appendChild(document.createTextNode(TEXT_B)); Element test = document.createElement(TAG_NAME); test.appendChild(document.createTextNode(TEXT_A + TEXT_B)); assertTrue("denormalised control text comparable to normalised test text", qualifier.qualifyForComparison(control, test)); } // three levels public void testThreeLevels() throws Exception { ElementQualifier qualifier = new RecursiveElementNameAndTextQualifier(); Element control = document.createElement(TAG_NAME); Element child = document.createElement(TAG_NAME2); control.appendChild(child); Element child2 = document.createElement(TAG_NAME); child.appendChild(child2); child2.appendChild(document.createTextNode(TEXT_B)); Element test = document.createElement(TAG_NAME); child = document.createElement(TAG_NAME2); test.appendChild(child); child2 = document.createElement(TAG_NAME); child.appendChild(child2); child2.appendChild(document.createTextNode(TEXT_B)); assertTrue(qualifier.qualifyForComparison(control, test)); } /** * @see https://sourceforge.net/forum/forum.php?thread_id=1440169&forum_id=73274 */ public void testThread1440169() throws Exception { String s1 = "foobar"; String s2 = "barfoo"; Diff d = new Diff(s1, s2); assertFalse(d.similar()); // reset d = new Diff(s1, s2); d.overrideElementQualifier(new ElementNameAndTextQualifier()); assertFalse(d.similar()); // reset once again d = new Diff(s1, s2); d.overrideElementQualifier(new RecursiveElementNameAndTextQualifier()); assertTrue(d.similar()); } public void testUserGuideExample() throws Exception { String control = "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
foo
bar
\n"; String test = "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
bar
foo
\n"; Diff d = new Diff(control, test); d.overrideElementQualifier(new RecursiveElementNameAndTextQualifier()); assertTrue(d.toString(), d.similar()); } /** * @see "http://sourceforge.net/p/xmlunit/bugs/62/" */ public void testMultipleTextValuesWithAdditionalElement() throws Exception { ElementQualifier qualifier = new RecursiveElementNameAndTextQualifier(); Element control = document.createElement(TAG_NAME); control.appendChild(document.createTextNode(TEXT_A)); control.appendChild(document.createTextNode(TEXT_B)); control.appendChild(document.createElement(TAG_NAME)); Element test = document.createElement(TAG_NAME); test.appendChild(document.createTextNode(TEXT_A + TEXT_B)); test.appendChild(document.createElement(TAG_NAME)); assertTrue("denormalised control text comparable to normalised test text", qualifier.qualifyForComparison(control, test)); } public void setUp() throws Exception { document = XMLUnit.newControlParser().newDocument(); } /** * @see https://sourceforge.net/forum/forum.php?thread_id=2948005&forum_id=73273 */ public void testOpenDiscussionThread2948995_1() throws Exception { Diff myDiff = new Diff("" + " " + " " + " 1" + " " + " " + " 2" + " " + " " + " " + " " + " 3" + " " + " " + " 4" + " " + " " + "", "" + " " + " " + " 2" + " " + " " + " 1" + " " + " " + " " + " " + " 3" + " " + " " + " 4" + " " + " " + ""); myDiff.overrideElementQualifier(new RecursiveElementNameAndTextQualifier()); XMLAssert.assertXMLEqual("Not similar", myDiff, true); } /** * @see https://sourceforge.net/forum/forum.php?thread_id=2948005&forum_id=73273 */ public void testOpenDiscussionThread2948995_2() throws Exception { Diff myDiff = new Diff("" + " " + " " + " 1" + " " + " " + " 2" + " " + " " + " " + " " + " 3" + " " + " " + " 4" + " " + " " + "", "" + " " + " " + " 1" + " " + " " + " 2" + " " + " " + " " + " " + " 4" + " " + " " + " 3" + " " + " " + ""); myDiff.overrideElementQualifier(new RecursiveElementNameAndTextQualifier()); XMLAssert.assertXMLEqual("Not similar", myDiff, true); } } ././@LongLink0000644000000000000000000000015100000000000011600 Lustar rootrootxmlunit-1.6/tests/java/org/custommonkey/xmlunit/examples/test_MultiLevelElementNameAndTextQualifier.javaxmlunit-1.6/tests/java/org/custommonkey/xmlunit/examples/test_MultiLevelElementNameAndTextQualifier.0000644000000000000000000001611012451007364033073 0ustar rootroot/* ****************************************************************** Copyright (c) 2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.examples; import org.w3c.dom.Document; import org.w3c.dom.Element; import junit.framework.TestCase; import junit.framework.TestSuite; import org.custommonkey.xmlunit.Diff; import org.custommonkey.xmlunit.ElementNameAndTextQualifier; import org.custommonkey.xmlunit.ElementQualifier; import org.custommonkey.xmlunit.XMLUnit; /** * JUnit testcase for MultiLevelElementNameAndTextQualifier * @see test_Diff#testRepeatedElementNamesWithTextQualification() */ public class test_MultiLevelElementNameAndTextQualifier extends TestCase { private static final String TAG_NAME = "tagYoureIt"; private static final String TAG_NAME2 = "tagYoureIt2"; private static final String TEXT_A = "textA"; private static final String TEXT_B = "textB"; private Document document; // copy of ElementNameAndTextQualifier test public void testSingleTextValue() throws Exception { ElementQualifier qualifier = new MultiLevelElementNameAndTextQualifier(1); Element control = document.createElement(TAG_NAME); control.appendChild(document.createTextNode(TEXT_A)); Element test = document.createElement(TAG_NAME); assertFalse("control text not comparable to empty text", qualifier.qualifyForComparison(control, test)); test.appendChild(document.createTextNode(TEXT_A)); assertTrue("control textA comparable to test textA", qualifier.qualifyForComparison(control, test)); test = document.createElement(TAG_NAME); test.appendChild(document.createTextNode(TEXT_B)); assertFalse("control textA not comparable to test textB", qualifier.qualifyForComparison(control, test)); } // copy of ElementNameAndTextQualifier test public void testMultipleTextValues() throws Exception { ElementQualifier qualifier = new MultiLevelElementNameAndTextQualifier(1); Element control = document.createElement(TAG_NAME); control.appendChild(document.createTextNode(TEXT_A)); control.appendChild(document.createTextNode(TEXT_B)); Element test = document.createElement(TAG_NAME); test.appendChild(document.createTextNode(TEXT_A + TEXT_B)); assertTrue("denormalised control text comparable to normalised test text", qualifier.qualifyForComparison(control, test)); } // three levels public void testThreeLevels() throws Exception { ElementQualifier qualifier = new MultiLevelElementNameAndTextQualifier(3); Element control = document.createElement(TAG_NAME); Element child = document.createElement(TAG_NAME2); control.appendChild(child); Element child2 = document.createElement(TAG_NAME); child.appendChild(child2); child2.appendChild(document.createTextNode(TEXT_B)); Element test = document.createElement(TAG_NAME); child = document.createElement(TAG_NAME2); test.appendChild(child); child2 = document.createElement(TAG_NAME); child.appendChild(child2); child2.appendChild(document.createTextNode(TEXT_B)); assertTrue(qualifier.qualifyForComparison(control, test)); } /** * @see https://sourceforge.net/forum/forum.php?thread_id=1440169&forum_id=73274 */ public void testThread1440169() throws Exception { String s1 = "foobar"; String s2 = "barfoo"; Diff d = new Diff(s1, s2); assertFalse(d.similar()); // reset d = new Diff(s1, s2); d.overrideElementQualifier(new ElementNameAndTextQualifier()); assertFalse(d.similar()); // reset once again d = new Diff(s1, s2); d.overrideElementQualifier(new MultiLevelElementNameAndTextQualifier(2)); assertTrue(d.similar()); } public void testUserGuideExample() throws Exception { String control = "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
foo
bar
\n"; String test = "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
bar
foo
\n"; Diff d = new Diff(control, test); d.overrideElementQualifier(new MultiLevelElementNameAndTextQualifier(2)); assertFalse(d.toString(), d.similar()); try { XMLUnit.setIgnoreWhitespace(true); d = new Diff(control, test); d.overrideElementQualifier(new MultiLevelElementNameAndTextQualifier(2)); assertTrue(d.toString(), d.similar()); } finally { XMLUnit.setIgnoreWhitespace(false); } d = new Diff(control, test); d.overrideElementQualifier(new MultiLevelElementNameAndTextQualifier(2, true)); assertTrue(d.toString(), d.similar()); } public void setUp() throws Exception { document = XMLUnit.newControlParser().newDocument(); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/examples/test_CountingNodeTester.java0000644000000000000000000001013612451007364030173 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.examples; import java.io.StringReader; import junit.framework.TestCase; import org.custommonkey.xmlunit.NodeTest; import org.custommonkey.xmlunit.NodeTestException; import org.w3c.dom.Node; /** * JUnit test for CountingNodeTester */ public class test_CountingNodeTester extends TestCase { private NodeTest test; private CountingNodeTester tester; public void testPositivePath() throws Exception { test = new NodeTest(new StringReader("c")); tester = new CountingNodeTester(2); test.performTest(tester, Node.ELEMENT_NODE); tester = new CountingNodeTester(1); test.performTest(tester, Node.TEXT_NODE); tester = new CountingNodeTester(3); test.performTest(tester, new short[] {Node.TEXT_NODE, Node.ELEMENT_NODE}); tester = new CountingNodeTester(0); test.performTest(tester, Node.COMMENT_NODE); } public void testNegativePath() throws Exception { test = new NodeTest(new StringReader("c")); try { tester = new CountingNodeTester(2); test.performTest(tester, Node.TEXT_NODE); fail("Expected NodeTestException"); } catch (NodeTestException e) { // failure, as expected } try { tester = new CountingNodeTester(1); test.performTest(tester, Node.ELEMENT_NODE); fail("Expected NodeTestException"); } catch (NodeTestException e) { // failure, as expected } try { tester = new CountingNodeTester(2); test.performTest(tester, new short[] {Node.TEXT_NODE, Node.ELEMENT_NODE}); fail("Expected NodeTestException"); } catch (NodeTestException e) { // failure, as expected } try { tester = new CountingNodeTester(1); test.performTest(tester, Node.COMMENT_NODE); fail("Expected NodeTestException"); } catch (NodeTestException e) { // failure, as expected } try { tester = new CountingNodeTester(0); test.performTest(tester, Node.TEXT_NODE); fail("Expected NodeTestException"); } catch (NodeTestException e) { // failure, as expected } } public test_CountingNodeTester(String name) { super(name); } } ././@LongLink0000644000000000000000000000015300000000000011602 Lustar rootrootxmlunit-1.6/tests/java/org/custommonkey/xmlunit/examples/test_FloatingPointTolerantDifferenceListener.javaxmlunit-1.6/tests/java/org/custommonkey/xmlunit/examples/test_FloatingPointTolerantDifferenceListene0000644000000000000000000000667712451007364033274 0ustar rootroot/* ****************************************************************** Copyright (c) 2008, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.examples; import java.util.Locale; import junit.framework.TestCase; import org.custommonkey.xmlunit.Diff; import org.custommonkey.xmlunit.Difference; import org.custommonkey.xmlunit.DifferenceListener; import org.w3c.dom.Node; public class test_FloatingPointTolerantDifferenceListener extends TestCase { public void testFloatingPointTolerance() throws Exception { String control = ""; String test = ""; Diff d = new Diff(control, test); FloatingPointTolerantDifferenceListener c = new FloatingPointTolerantDifferenceListener(new DifferenceListener() { public int differenceFound(Difference d) { fail("differenceFound shouldn't get invoked, but" + " was with type " + d.getId()); return -42; } public void skippedComparison(Node c, Node t) { fail("skippedComparison shouldn't get invoked"); } }, 1e-2); d.overrideDifferenceListener(c); assertTrue(d.identical()); c = new FloatingPointTolerantDifferenceListener(new DifferenceListener() { public int differenceFound(Difference d) { fail("differenceFound shouldn't get invoked, but" + " was with type " + d.getId()); return -42; } public void skippedComparison(Node c, Node t) { fail("skippedComparison shouldn't get invoked"); } }, 1e-3); d = new Diff(control, test); d.overrideDifferenceListener(c); assertFalse(d.identical()); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/examples/test_TextDifferenceListenerBase.java0000644000000000000000000001364512451007364031620 0ustar rootroot/* ****************************************************************** Copyright (c) 2008, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.examples; import junit.framework.TestCase; import org.custommonkey.xmlunit.Diff; import org.custommonkey.xmlunit.Difference; import org.custommonkey.xmlunit.DifferenceListener; import org.w3c.dom.Node; public class test_TextDifferenceListenerBase extends TestCase { private static final String C_ATTR = "controlAttr"; private static final String T_ATTR = "testAttr"; private static final String C_CDATA = "controlCdata"; private static final String T_CDATA = "testCdata"; private static final String C_CMMT = "controlComment"; private static final String T_CMMT = "testComment"; private static final String C_TEXT = "controlText"; private static final String T_TEXT = "testText"; public void testTextDifferenceDelegations() throws Exception { final int[] invocations = new int[4]; String control = getDoc(C_ATTR, C_CDATA, C_CMMT, C_TEXT); String test = getDoc(T_ATTR, T_CDATA, T_CMMT, T_TEXT); TextDifferenceListenerBase b = new TextDifferenceListenerBase(null) { protected int attributeDifference(Difference d) { assertEquals(C_ATTR, d.getControlNodeDetail().getValue()); assertEquals(T_ATTR, d.getTestNodeDetail().getValue()); invocations[0]++; return 1; } protected int cdataDifference(Difference d) { assertEquals(C_CDATA, d.getControlNodeDetail().getValue()); assertEquals(T_CDATA, d.getTestNodeDetail().getValue()); invocations[1]++; return 1; } protected int commentDifference(Difference d) { assertEquals(C_CMMT, d.getControlNodeDetail().getValue()); assertEquals(T_CMMT, d.getTestNodeDetail().getValue()); invocations[2]++; return 1; } protected int textDifference(Difference d) { assertEquals(C_TEXT, d.getControlNodeDetail().getValue()); assertEquals(T_TEXT, d.getTestNodeDetail().getValue()); invocations[3]++; return 1; } }; Diff d = new Diff(control, test); d.overrideDifferenceListener(b); assertTrue(d.identical()); for (int i = 0; i < invocations.length; i++) { assertEquals(1, invocations[i]); } } public void testTextualDifference() throws Exception { final int[] invocations = new int[1]; String control = getDoc(C_ATTR, C_CDATA, C_CMMT, C_TEXT); String test = getDoc(T_ATTR, T_CDATA, T_CMMT, T_TEXT); TextDifferenceListenerBase b = new TextDifferenceListenerBase(null) { protected int textualDifference(Difference d) { invocations[0]++; return 1; } }; Diff d = new Diff(control, test); d.overrideDifferenceListener(b); assertTrue(d.identical()); assertEquals(4, invocations[0]); } public void testFullDelegation() throws Exception { final int[] invocations = new int[1]; String control = getDoc(C_ATTR, C_CDATA, C_CMMT, C_TEXT); String test = getDoc(T_ATTR, T_CDATA, T_CMMT, T_TEXT); TextDifferenceListenerBase b = new TextDifferenceListenerBase(new DifferenceListener() { public int differenceFound(Difference d) { invocations[0]++; return 1; } public void skippedComparison(Node c, Node t) { fail("skippedComparison shouldn't get invoked"); } }) {}; Diff d = new Diff(control, test); d.overrideDifferenceListener(b); assertTrue(d.identical()); assertEquals(4, invocations[0]); } private static String getDoc(String attr, String cdata, String comment, String text) { return "" + "" + text + ""; } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/examples/test_CaseInsensitiveDifferenceListener.java0000644000000000000000000000677412451007364033202 0ustar rootroot/* ****************************************************************** Copyright (c) 2008, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.examples; import java.util.Locale; import junit.framework.TestCase; import org.custommonkey.xmlunit.Diff; import org.custommonkey.xmlunit.Difference; import org.custommonkey.xmlunit.DifferenceListener; import org.w3c.dom.Node; public class test_CaseInsensitiveDifferenceListener extends TestCase { private static final String ATTR = "aTtr"; private static final String CDATA = "C Data"; private static final String CMMT = "a Comment"; private static final String TEXT = "some Text"; public void testCaseInsensitive() throws Exception { String control = getDoc(ATTR, CDATA, CMMT, TEXT); String test = getDoc(ATTR.toUpperCase(Locale.US), CDATA.toUpperCase(Locale.US), CMMT.toUpperCase(Locale.US), TEXT.toUpperCase(Locale.US)); Diff d = new Diff(control, test); CaseInsensitiveDifferenceListener c = new CaseInsensitiveDifferenceListener(new DifferenceListener() { public int differenceFound(Difference d) { fail("differenceFound shouldn't get invoked, but" + " was with type " + d.getId()); return -42; } public void skippedComparison(Node c, Node t) { fail("skippedComparison shouldn't get invoked"); } }); d.overrideDifferenceListener(c); assertTrue(d.identical()); } private static String getDoc(String attr, String cdata, String comment, String text) { return "" + "" + text + ""; } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_TolerantSaxDocumentBuilder.java0000644000000000000000000001441512451007364030050 0ustar rootroot/* ****************************************************************** Copyright (c) 200, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.io.StringReader; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import junit.framework.TestSuite; import org.w3c.dom.Document; import org.xml.sax.InputSource; /** * JUnit test for TolerantSaxDocumentBuilder */ public class test_TolerantSaxDocumentBuilder extends XMLTestCase { private TolerantSaxDocumentBuilder builder; private SAXParser parser; private static final String SIMPLEST_XML = "text"; public void testSimpleDocument() throws Exception { String simpleXML = XML_DECLARATION + SIMPLEST_XML; Document simpleXMLDocument = XMLUnit.buildControlDocument( simpleXML); assertParsedDocumentEqual(simpleXMLDocument, simpleXML); assertTrue(builder.getTrace(), builder.getTrace().indexOf("WARNING") == -1); } private void assertParsedDocumentEqual(Document control, String test) throws Exception { InputSource parseSource = new InputSource(new StringReader(test)); parser.setProperty("http://xml.org/sax/properties/lexical-handler", builder); parser.parse(parseSource, builder); assertXMLEqual(control, builder.getDocument()); } public void testSimpleDocumentWithComments() throws Exception { String xmlWithComments = XML_DECLARATION + "" + SIMPLEST_XML + "" + SIMPLEST_XML + ""; Document documentWithComments = XMLUnit.buildControlDocument( xmlWithComments); assertParsedDocumentEqual(documentWithComments, xmlWithComments); assertTrue(builder.getTrace(), builder.getTrace().indexOf("WARNING") == -1); } public void testSimpleDocumentWithProcessingInstruction() throws Exception { String xmlWithProcInstruction = XML_DECLARATION + "" + SIMPLEST_XML + "" + SIMPLEST_XML + ""; Document documentWithProcInstruction = XMLUnit.buildControlDocument( xmlWithProcInstruction); assertParsedDocumentEqual(documentWithProcInstruction, xmlWithProcInstruction); assertTrue(builder.getTrace(), builder.getTrace().indexOf("WARNING") == -1); } public void testStartElementWithNoEnd() throws Exception { builder.startDocument(); builder.startElement(null, null, "root", null); Document oneElementDocument = XMLUnit.buildControlDocument(""); assertXMLEqual(oneElementDocument, builder.getDocument()); assertTrue(builder.getTrace(), builder.getTrace().indexOf("WARNING") == -1); } public void testEndElementWithNoStart() throws Exception { builder.startDocument(); builder.startElement(null, null, "root", null); builder.endElement(null, null, "node"); builder.endElement(null, null, "root"); Document oneElementDocument = XMLUnit.buildControlDocument(""); assertXMLEqual(oneElementDocument, builder.getDocument()); assertTrue(builder.getTrace(), builder.getTrace().indexOf("WARNING") != -1); } public void testEndElementBeforeStart() throws Exception { builder.startDocument(); builder.endElement(null, null, "root"); builder.startElement(null, null, "root", null); Document oneElementDocument = XMLUnit.buildControlDocument(""); assertXMLEqual(oneElementDocument, builder.getDocument()); assertTrue(builder.getTrace(), builder.getTrace().indexOf("WARNING") != -1); } public void testTextBeforeStartElement() throws Exception { String someText = "how could this happen?!"; builder.startDocument(); builder.characters(someText.toCharArray(), 0, someText.length()); builder.startElement(null, null, "root", null); Document oneElementDocument = XMLUnit.buildControlDocument(""); assertXMLEqual(oneElementDocument, builder.getDocument()); assertTrue(builder.getTrace(), builder.getTrace().indexOf("WARNING") != -1); } public void setUp() throws Exception { builder = new TolerantSaxDocumentBuilder(XMLUnit.newTestParser()); parser = SAXParserFactory.newInstance().newSAXParser(); } public test_TolerantSaxDocumentBuilder(String name) { super(name); } public static TestSuite suite() { return new TestSuite(test_TolerantSaxDocumentBuilder.class); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_HTMLDocumentBuilder.java0000644000000000000000000000741012451007364026345 0ustar rootroot/* ****************************************************************** Copyright (c) 200, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import junit.framework.TestSuite; import org.w3c.dom.Document; /** * JUnit test for HTMLDocumentBuilder */ public class test_HTMLDocumentBuilder extends XMLTestCase { private static final String xHtml = "test" + "

hello

world


" + "
  • one
  • two
"; private Document xHtmlDocument; private HTMLDocumentBuilder parser; private TolerantSaxDocumentBuilder builder; public void testParseGoodHtml() throws Exception { assertParsedDocumentEqual(xHtmlDocument, xHtml); assertEquals(parser.getTrace(),-1, parser.getTrace().indexOf("WARNING")); } public void testParseOldHtml() throws Exception { String oldHTML= "test" + "

hello

world


" + "
  • one
  • two
"; assertParsedDocumentEqual(xHtmlDocument, oldHTML); assertEquals(parser.getTrace(),-1, parser.getTrace().indexOf("WARNING")); } public void testParsePoorHtml() throws Exception { String poorHTML= "test" + "

hello

world


" + "
  • one
  • two"; assertParsedDocumentEqual(xHtmlDocument, poorHTML); assertEquals(parser.getTrace(),-1, parser.getTrace().indexOf("WARNING")); } private void assertParsedDocumentEqual(Document control, String test) throws Exception { assertXMLEqual(control, parser.parse(test)); } public test_HTMLDocumentBuilder(String name) { super(name); } public void setUp() throws Exception { xHtmlDocument = XMLUnit.buildControlDocument(xHtml); builder = new TolerantSaxDocumentBuilder(XMLUnit.newTestParser()); parser = new HTMLDocumentBuilder(builder); } public static TestSuite suite() { return new TestSuite(test_HTMLDocumentBuilder.class); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_ElementNameAndAttributeQualifier.java0000644000000000000000000002721612451007364031144 0ustar rootroot/* ****************************************************************** Copyright (c) 2001, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import org.w3c.dom.Document; import org.w3c.dom.Element; import junit.framework.TestCase; import junit.framework.TestSuite; /** * JUnit testcase for ElementNameAndAttributeQualifier * @see test_Diff#testRepeatedElementNamesWithAttributeQualification() */ public class test_ElementNameAndAttributeQualifier extends TestCase { private Document document; private ElementNameAndAttributeQualifier elementNameAndAttributeQualifier; private static final String TAG_NAME = "qwerty"; public void testSingleQualifyingAttribute() throws Exception { final String attrName = "id"; elementNameAndAttributeQualifier = new ElementNameAndAttributeQualifier(); testAssertionsFor(attrName, new boolean[] {false, false}); elementNameAndAttributeQualifier = new ElementNameAndAttributeQualifier(attrName); testAssertionsFor(attrName, new boolean[] {true, true}); } private void testAssertionsFor(String attrName, boolean[] expectedValues) throws Exception { Element control = document.createElement(TAG_NAME); control.setAttribute(attrName, "1"); Element test = document.createElement(TAG_NAME); assertFalse("qwerty id 1 not comparable to qwerty with no attributes", elementNameAndAttributeQualifier.qualifyForComparison(control, test)); test.setAttribute(attrName, "1"); assertTrue("qwerty id 1 comparable to qwerty id 1", elementNameAndAttributeQualifier.qualifyForComparison(control, test)); control.setAttribute("uiop","true"); assertEquals("qwerty id 1 && uiop comparable to qwerty id 1", expectedValues[0], elementNameAndAttributeQualifier.qualifyForComparison(control, test)); test.setAttribute("uiop", "false"); assertEquals("qwerty id 1 && uiop comparable to qwerty id 1 && !uiop", expectedValues[1], elementNameAndAttributeQualifier.qualifyForComparison(control, test)); test.setAttribute(attrName, "2"); assertFalse("qwerty id 1 && uiop NOT comparable to qwerty id 2 && !uiop", elementNameAndAttributeQualifier.qualifyForComparison(control, test)); } public void testMultipleQualifyingAttributes() throws Exception { final String[] attrNames = {"id", "uid"}; elementNameAndAttributeQualifier = new ElementNameAndAttributeQualifier(); testAssertionsFor(attrNames, new boolean[] {false, false}); elementNameAndAttributeQualifier = new ElementNameAndAttributeQualifier(attrNames); testAssertionsFor(attrNames, new boolean[] {true, true}); } private void testAssertionsFor(String[] attrNames, boolean[] expectedValues) throws Exception { Element control = document.createElement(TAG_NAME); for (int i=0; i < attrNames.length; ++i) { control.setAttribute(attrNames[i], "1"); } Element test = document.createElement(TAG_NAME); assertFalse("qwerty id/uid 1 not comparable to qwerty with no attributes", elementNameAndAttributeQualifier.qualifyForComparison(control, test)); for (int i=0; i < attrNames.length; ++i) { test.setAttribute(attrNames[i], "1"); } assertTrue("qwerty id/uid 1 comparable to qwerty id/uid 1", elementNameAndAttributeQualifier.qualifyForComparison(control, test)); control.setAttribute("oid", "0x2394b3456df"); assertEquals("qwerty id/uid 1 with oid comparable to qwerty id/uid 1", expectedValues[0], elementNameAndAttributeQualifier.qualifyForComparison(control, test)); test.setAttribute("oid", "0xfd6543b4932"); assertEquals("qwerty id/uid 1 with oid comparable to qwerty id/uid 1 with different oid", expectedValues[1], elementNameAndAttributeQualifier.qualifyForComparison(control, test)); test.setAttribute(attrNames[0], "2"); assertFalse("qwerty id/uid 1 not comparable to qwerty id 2 /uid 1", elementNameAndAttributeQualifier.qualifyForComparison(control, test)); test.setAttribute(attrNames[0], "1"); test.setAttribute(attrNames[1], "2"); assertFalse("qwerty id/uid 1 not comparable to qwerty id 1 /uid 2", elementNameAndAttributeQualifier.qualifyForComparison(control, test)); test.setAttribute(attrNames[0], "2"); assertFalse("qwerty id/uid 1 not comparable to qwerty id/uid 2", elementNameAndAttributeQualifier.qualifyForComparison(control, test)); } public void testNamespacedQualifyingAttribute() throws Exception { final String attrName = "id"; final String nsURI = "http://xmlunit.sourceforge.net/tests"; elementNameAndAttributeQualifier = new ElementNameAndAttributeQualifier(); testAssertionsFor(attrName, nsURI, new boolean[] {false, false}); elementNameAndAttributeQualifier = new ElementNameAndAttributeQualifier(attrName); testAssertionsFor(attrName, nsURI, new boolean[] {true, true}); } private void testAssertionsFor(String attrName, String nsURI, boolean[] expectedValues) throws Exception { Element control = document.createElement(TAG_NAME); control.setAttributeNS(nsURI, attrName, "1"); Element test = document.createElement(TAG_NAME); assertFalse("qwerty id 1 not comparable to qwerty with no attributes", elementNameAndAttributeQualifier.qualifyForComparison(control, test)); test.setAttributeNS(nsURI, attrName, "1"); assertTrue("qwerty id 1 comparable to qwerty id 1", elementNameAndAttributeQualifier.qualifyForComparison(control, test)); String otherNsURI = nsURI + "/2"; test.setAttributeNS(otherNsURI, attrName, "2"); assertTrue("qwerty id 1 comparable to qwerty id 1 and other-NS id 2", elementNameAndAttributeQualifier.qualifyForComparison(control, test)); control.setAttributeNS(nsURI, "uiop","true"); assertEquals("qwerty id 1 && uiop comparable to qwerty id 1", expectedValues[0], elementNameAndAttributeQualifier.qualifyForComparison(control, test)); test.setAttributeNS(nsURI, "uiop", "false"); assertEquals("qwerty id 1 && uiop comparable to qwerty id 1 && !uiop", expectedValues[1], elementNameAndAttributeQualifier.qualifyForComparison(control, test)); test.setAttributeNS(nsURI, attrName, "2"); assertFalse("qwerty id 1 && uiop NOT comparable to qwerty id 2 && !uiop", elementNameAndAttributeQualifier.qualifyForComparison(control, test)); } // Bug 952920 public void testQualifyingAttributeMissingInControl() throws Exception { elementNameAndAttributeQualifier = new ElementNameAndAttributeQualifier("foo"); assertQualifyingAttributeMissingInControl(); elementNameAndAttributeQualifier = new ElementNameAndAttributeQualifier(new String[] {"foo", "bar"}); assertQualifyingAttributeMissingInControl(); } private void assertQualifyingAttributeMissingInControl() throws Exception { Element control = document.createElement(TAG_NAME); Element test = document.createElement(TAG_NAME); assertTrue("empty elements match", elementNameAndAttributeQualifier.qualifyForComparison(control, test)); test.setAttribute("id", "1"); assertTrue("extra attribute on test matches", elementNameAndAttributeQualifier.qualifyForComparison(control, test)); control.setAttribute("id", "2"); assertTrue("differerent values for extra attribute still matches", elementNameAndAttributeQualifier.qualifyForComparison(control, test)); control.setAttribute("uid", "1"); assertTrue("extra attribute on control matches", elementNameAndAttributeQualifier.qualifyForComparison(control, test)); test.setAttribute("foo", "1"); assertFalse("no match if attribute is present in test", elementNameAndAttributeQualifier.qualifyForComparison(control, test)); } /** * @see https://sourceforge.net/forum/forum.php?thread_id=1135716&forum_id=73274l */ public void testHelpForumThread1135716() throws Exception { String control = " " + " " + " " + " " + " " + " " + " " + " " + ""; String test = " " + " " + " " + " " + " " + " " + " " + " " + ""; Diff d = new Diff(control, test); assertFalse(d.similar()); // reset d = new Diff(control, test); d.overrideElementQualifier(new ElementNameAndAttributeQualifier()); assertTrue(d.similar()); } public void setUp() throws Exception { document = XMLUnit.newControlParser().newDocument(); } public static TestSuite suite() { return new TestSuite(test_ElementNameAndAttributeQualifier.class); } /** * Constructor for test_ElementNameAndAttributeQualifier. * @param name */ public test_ElementNameAndAttributeQualifier(String name) { super(name); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_DoctypeReader.java0000644000000000000000000001157212451007364025331 0ustar rootroot/* ****************************************************************** Copyright (c) 200, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.io.IOException; import java.io.StringReader; import junit.framework.TestCase; import junit.framework.TestSuite; /** * JUnit test for DoctypeReader */ public class test_DoctypeReader extends AbstractDoctypeTests { private DoctypeReader doctypeReader; private StringReader sourceReader; private static final String NEWLINE = System.getProperty("line.separator"); public void testGetContent() throws IOException { String source = "WooPDeDoO!" + NEWLINE + "GooRanga!" + NEWLINE + " plIng! "; sourceReader = new StringReader(source); doctypeReader = new DoctypeReader(sourceReader, "nonsense", "words"); assertEquals(source, doctypeReader.getContent()); // can get content indefinitely from this reader assertEquals(source, doctypeReader.getContent()); } private void initDummyDoctypeReader() { sourceReader = new StringReader("yabba"); doctypeReader = new DoctypeReader(sourceReader, "yabba", "don\'t"); } public void testReplaceDoctypeInternalDTD() { initDummyDoctypeReader(); StringBuffer buf = new StringBuffer(test_Constants.CHUCK_JONES_RIP_DTD_DECL); assertEquals("", doctypeReader.replaceDoctype(buf, "ni", "shrubbery")); } public void testReplaceDoctypeExternalDTD() { initDummyDoctypeReader(); StringBuffer buf = new StringBuffer( ""); assertEquals("", doctypeReader.replaceDoctype(buf, "ni", "shrubbery")); } public void testReplaceDoctypeNoDTD() { initDummyDoctypeReader(); StringBuffer buf = new StringBuffer(NO_DTD); assertEquals("" + NO_DTD, doctypeReader.replaceDoctype(buf, "ni", "shrubbery")); } public void testReplaceDoctypeNoDTDButXMLDecl() { initDummyDoctypeReader(); StringBuffer buf = new StringBuffer(test_Constants.XML_DECLARATION + NO_DTD); assertEquals(test_Constants.XML_DECLARATION + "" + NO_DTD, doctypeReader.replaceDoctype(buf, "ni", "shrubbery")); } private static String readFully(DoctypeReader reader) throws IOException { StringBuffer buf = new StringBuffer(); char[] ch = new char[1024]; int numChars; while ((numChars = reader.read(ch))!=-1) { buf.append(ch, 0, numChars); } return buf.toString(); } protected void assertEquals(String expected, String input, String docType, String systemId) throws IOException { DoctypeReader doctypeReader = new DoctypeReader(new StringReader(expected), docType, systemId); assertEquals(expected, readFully(doctypeReader)); } public test_DoctypeReader(String name) { super(name); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/AbstractXpathEngineTests.java0000644000000000000000000002100512451007364026451 0ustar rootroot/* ****************************************************************** Copyright (c) 2007-2008, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.util.HashMap; import junit.framework.TestCase; import org.custommonkey.xmlunit.exceptions.ConfigurationException; import org.custommonkey.xmlunit.exceptions.XpathException; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public abstract class AbstractXpathEngineTests extends TestCase { protected static final String[] testAttrNames = {"attrOne", "attrTwo"}; protected static final String testString = "intellectual property rights" + " " + "make us all poorer " + "free your code from its chains" + ""; protected Document testDocument; protected abstract XpathEngine newXpathEngine(); public void testGetMatchingNodesNoMatches() throws Exception { NodeList nodeList = newXpathEngine().getMatchingNodes("toast", testDocument); assertEquals(0, nodeList.getLength()); } public void testGetMatchingNodesMatchRootElement() throws Exception { NodeList nodeList = newXpathEngine().getMatchingNodes("test", testDocument); assertEquals(1, nodeList.getLength()); assertEquals(Node.ELEMENT_NODE, nodeList.item(0).getNodeType()); } public void testGetMatchingNodesMatchElement() throws Exception { NodeList nodeList = newXpathEngine() .getMatchingNodes("test/nodeWithoutAttributes", testDocument); assertEquals(2, nodeList.getLength()); assertEquals(Node.ELEMENT_NODE, nodeList.item(0).getNodeType()); } public void testGetMatchingNodesMatchText() throws Exception { NodeList nodeList = newXpathEngine().getMatchingNodes("test//text()", testDocument); assertEquals(3, nodeList.getLength()); assertEquals(Node.TEXT_NODE, nodeList.item(0).getNodeType()); } public void testGetMatchingNodesCheckSubNodes() throws Exception { NodeList nodeList = newXpathEngine() .getMatchingNodes("test/nodeWithAttributes", testDocument); assertEquals(1, nodeList.getLength()); Node aNode; aNode = nodeList.item(0); assertEquals(Node.ELEMENT_NODE, aNode.getNodeType()); assertEquals(true, aNode.hasAttributes()); assertEquals(true, aNode.hasChildNodes()); NodeList children = aNode.getChildNodes(); int length = children.getLength(); assertEquals(1, length); for (int i=0; i < length; ++i) { assertEquals(Node.TEXT_NODE, children.item(i).getNodeType()); } NamedNodeMap attributes = aNode.getAttributes(); int numAttrs = attributes.getLength(); assertEquals(testAttrNames.length, numAttrs); for (int i=0; i < testAttrNames.length; ++i) { Node attrNode = attributes.getNamedItem(testAttrNames[i]); assertNotNull(attrNode); assertEquals(Node.ATTRIBUTE_NODE, attrNode.getNodeType()); } } public void testEvaluate() throws Exception { String result = newXpathEngine().evaluate("count(test//node())", testDocument); assertEquals("3 elements and 3 text nodes", "6", result); } public void testXpathPrefixChange() throws Exception { String testDoc = ""; Document d = XMLUnit.buildControlDocument(testDoc); HashMap m = new HashMap(); m.put("foo", "urn:foo"); NamespaceContext ctx = new SimpleNamespaceContext(m); XpathEngine engine = newXpathEngine(); engine.setNamespaceContext(ctx); NodeList l = engine.getMatchingNodes("//foo:bar", d); assertEquals(1, l.getLength()); assertEquals(Node.ELEMENT_NODE, l.item(0).getNodeType()); String s = engine.evaluate("count(foo:test//node())", d); assertEquals("1", s); } // http://sourceforge.net/forum/forum.php?thread_id=1832061&forum_id=73274 public void testXpathExistsWithNsAndLocalNameSelector() throws Exception { String testDoc = " " + " " + " " + "Timestamp " + "2007-07-26T11:59:00 " + " " + " " + " " + " " + " " + "CIECABMSAssignmentAddRq " + " " + " " + " " + " " + "3744f84b-ac18-5303-0082-764bdeb20df9 " + " " + " " + " " + " " + " " + ""; Document d = XMLUnit.buildControlDocument(testDoc); XpathEngine engine = newXpathEngine(); NodeList l = engine.getMatchingNodes("//*[local-name()='RqUID'][namespace-uri()='http://www.cieca.com/BMS']", d); assertEquals(1, l.getLength()); } public void setUp() throws Exception { testDocument = XMLUnit.buildControlDocument(testString); } public AbstractXpathEngineTests(String name) { super(name); } public void testEvaluateInvalidXPath() throws Exception { String xpath = "count(test//*[@attrOne='open source])"; try { String result = newXpathEngine().evaluate(xpath, testDocument); fail("expected Exception to be thrown but wasn't"); } catch (XpathException ex) { // expected } catch (ConfigurationException ex) { // acceptable in the JAXP 1.2 case } } // https://sourceforge.net/forum/forum.php?thread_id=3292605&forum_id=73274 public void testDefaultNamespace() throws Exception { String t = "bv"; Document d = XMLUnit.buildControlDocument(t); HashMap m = new HashMap(); m.put("", "http://www.acme.com"); m.put("q", "http://www.acme.com"); NamespaceContext ctx = new SimpleNamespaceContext(m); XpathEngine engine = newXpathEngine(); engine.setNamespaceContext(ctx); // fails assertEquals("bv", engine.evaluate("//b", d)); assertEquals("bv", engine.evaluate("//q:b", d)); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_Replacement.java0000644000000000000000000001121612451007364025031 0ustar rootroot/* ****************************************************************** Copyright (c) 200, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import junit.framework.TestCase; import junit.framework.TestSuite; /** * JUnit test for Replacement */ public class test_Replacement extends TestCase { private Replacement replacement; public void testCharReplacement() { char[] ch = {'h','o','a','g'}; replacement = new Replacement(new char[] {'o','a'}, new char[] {'u'}); assertEquals("hug", new String(replacement.replace(ch))); replacement = new Replacement(new char[] {'g'}, new char[] {'r', 's', 'e'}); assertEquals("hoarse", new String(replacement.replace(ch))); assertEquals("hasReplaceBy", true, replacement.hasReplaceBy()); } public void testSimpleString() { replacement = new Replacement("x", "y"); // 1st char assertEquals("yen", replacement.replace("xen")); // last char assertEquals("boy", replacement.replace("box")); // not 1st or last assertEquals("aye", replacement.replace("axe")); // no replacement assertEquals("bag", replacement.replace("bag")); // multiple replacements assertEquals("yoyo", replacement.replace("xoxo")); // multiple concurrent replacements assertEquals("yyjykyy", replacement.replace("xxjxkxx")); assertEquals("hasReplaceBy", true, replacement.hasReplaceBy()); } public void testComplexString() { replacement = new Replacement(" a whole bunch of words", "some other words altogether"); assertEquals("Here aresome other words altogether...", replacement.replace("Here are a whole bunch of words...")); replacement = new Replacement("pp", "p"); assertEquals("hapy", replacement.replace("happy")); assertEquals("happy", replacement.replace("happppy")); assertEquals("tap", replacement.replace("tapp")); assertEquals("paw", replacement.replace("ppaw")); } public void testNullReplacebyString() { replacement = new Replacement(" ", null); assertEquals("hasReplaceBy", false, replacement.hasReplaceBy()); assertEquals("wedon'twantnowhitespace", replacement.replace("we don't want no whitespace")); } public void testNullReplacebyChars() { replacement = new Replacement(new char[] {'a'}, null); assertEquals("hasReplaceBy", false, replacement.hasReplaceBy()); assertEquals("ntidisestblishmentrinism", replacement.replace("antidisestablishmentarianism")); } public void testEmptyReplaceby() { replacement = new Replacement(new char[] {'w'}, new char[0]); assertEquals("hasReplaceBy", false, replacement.hasReplaceBy()); assertEquals("ibbleobble", replacement.replace("wibblewobble")); } public test_Replacement(String name) { super(name); } public static TestSuite suite() { return new TestSuite(test_Replacement.class); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_DetailedDiff.java0000644000000000000000000004712612451007364025107 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2008,2010,2013 Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.io.File; import java.io.FileReader; import java.io.Reader; import java.util.Iterator; import java.util.List; import org.custommonkey.xmlunit.examples.MultiLevelElementNameAndTextQualifier; import org.w3c.dom.Document; import org.xml.sax.InputSource; /** * Test a DetailedDiff. Extend the test case class for Diff so we can rerun those * tests with a DetailedDiff and assert that behaviour has not changed. */ public class test_DetailedDiff extends test_Diff { private String firstForecast, secondForecast; public void testAllDifferencesFirstForecastControl() throws Exception { Diff multipleDifferences = new Diff(firstForecast, secondForecast); DetailedDiff detailedDiff = new DetailedDiff(multipleDifferences); List differences = detailedDiff.getAllDifferences(); assertExpectedDifferencesFirstForecastControl(differences, detailedDiff); } private void assertExpectedDifferencesFirstForecastControl(List differences, DetailedDiff detailedDiff) { assertEquals("size: " + detailedDiff, 7, differences.size()); assertEquals("first: " + detailedDiff, DifferenceConstants.ELEMENT_NUM_ATTRIBUTES, differences.get(0)); assertEquals("second: " + detailedDiff, DifferenceConstants.ATTR_NAME_NOT_FOUND, differences.get(1)); assertEquals("third: " + detailedDiff, DifferenceConstants.ATTR_VALUE, differences.get(2)); assertEquals("fourth: " + detailedDiff, DifferenceConstants.ATTR_SEQUENCE, differences.get(3)); assertEquals("fifth: " + detailedDiff, DifferenceConstants.HAS_CHILD_NODES, differences.get(4)); assertEquals("sixth: " + detailedDiff, DifferenceConstants.CHILD_NODELIST_LENGTH, differences.get(5)); assertEquals("seventh: " + detailedDiff, DifferenceConstants.CHILD_NODE_NOT_FOUND, differences.get(6)); } public void testAllDifferencesSecondForecastControl() throws Exception { Diff multipleDifferences = new Diff(secondForecast, firstForecast); DetailedDiff detailedDiff = new DetailedDiff(multipleDifferences); List differences = detailedDiff.getAllDifferences(); assertEquals("size: " + detailedDiff, 7, differences.size()); assertEquals("first: " + detailedDiff, DifferenceConstants.ELEMENT_NUM_ATTRIBUTES, differences.get(0)); assertEquals("second: " + detailedDiff, DifferenceConstants.ATTR_VALUE, differences.get(1)); assertEquals("third: " + detailedDiff, DifferenceConstants.ATTR_SEQUENCE, differences.get(2)); assertEquals("forth: " + detailedDiff, DifferenceConstants.ATTR_NAME_NOT_FOUND, differences.get(3)); assertEquals("fifth: " + detailedDiff, DifferenceConstants.HAS_CHILD_NODES, differences.get(4)); assertEquals("sixth: " + detailedDiff, DifferenceConstants.CHILD_NODELIST_LENGTH, differences.get(5)); assertEquals("seventy: " + detailedDiff, DifferenceConstants.CHILD_NODE_NOT_FOUND, differences.get(6)); } public void testPrototypeIsADetailedDiff() throws Exception { Diff multipleDifferences = new Diff(firstForecast, secondForecast); DetailedDiff detailedDiff = new DetailedDiff( new DetailedDiff(multipleDifferences)); List differences = detailedDiff.getAllDifferences(); assertExpectedDifferencesFirstForecastControl(differences, detailedDiff); } public void testLargeFiles() throws Exception { int i = 0; String expr = null; File test, control; control = new File(test_Constants.BASEDIR + "/tests/etc/controlDetail.xml"); test = new File(test_Constants.BASEDIR + "/tests/etc/testDetail.xml"); DetailedDiff differencesWithWhitespace = new DetailedDiff( new Diff(new InputSource(new FileReader(control)), new InputSource(new FileReader(test))) ); List l = differencesWithWhitespace.getAllDifferences(); int unmatchedNodeDiffs = 0; int hasChildNodeDiffs = 0; for (Iterator iter = l.iterator(); iter.hasNext();) { Difference d = (Difference) iter.next(); if (d.getId() == DifferenceConstants.CHILD_NODE_NOT_FOUND_ID) { unmatchedNodeDiffs++; } else if (d.getId() == DifferenceConstants.HAS_CHILD_NODES_ID) { hasChildNodeDiffs++; } } assertEquals(1402 + hasChildNodeDiffs + unmatchedNodeDiffs, differencesWithWhitespace.getAllDifferences().size()); try { XMLUnit.setIgnoreWhitespace(true); Diff prototype = new Diff(new FileReader(control), new FileReader(test)); DetailedDiff detailedDiff = new DetailedDiff(prototype); List differences = detailedDiff.getAllDifferences(); unmatchedNodeDiffs = 0; hasChildNodeDiffs = 0; for (Iterator iter = differences.iterator(); iter.hasNext();) { Difference d = (Difference) iter.next(); if (d.getId() == DifferenceConstants.CHILD_NODE_NOT_FOUND_ID) { unmatchedNodeDiffs++; } else if (d.getId() == DifferenceConstants.HAS_CHILD_NODES_ID) { hasChildNodeDiffs++; } } assertEquals(40 + hasChildNodeDiffs + unmatchedNodeDiffs, differences.size()); SimpleXpathEngine xpathEngine = new SimpleXpathEngine(); Document controlDoc = XMLUnit.buildControlDocument( new InputSource(new FileReader(control))); Document testDoc = XMLUnit.buildTestDocument( new InputSource(new FileReader(test))); Difference aDifference; String value; for (Iterator iter = differences.iterator(); iter.hasNext();) { aDifference = (Difference) iter.next(); if (aDifference.equals(DifferenceConstants.ATTR_VALUE) || aDifference.equals(DifferenceConstants.CDATA_VALUE) || aDifference.equals(DifferenceConstants.COMMENT_VALUE) || aDifference.equals(DifferenceConstants.ELEMENT_TAG_NAME) || aDifference.equals(DifferenceConstants.TEXT_VALUE)) { expr = aDifference.getControlNodeDetail().getXpathLocation(); if (expr==null || expr.length()==0) { System.out.println(aDifference); } else { value = xpathEngine.evaluate(expr, controlDoc); assertEquals(i + " control " + aDifference.toString(), value, aDifference.getControlNodeDetail().getValue()); } expr = aDifference.getTestNodeDetail().getXpathLocation(); if (expr == null || expr.length()==0) { System.out.println(aDifference); } else { value = xpathEngine.evaluate(expr, testDoc); assertEquals(i + " test " + aDifference.toString(), value, aDifference.getTestNodeDetail().getValue()); } } ++i; } } catch (Exception e) { System.out.println("eek@" + i + ":" + expr); throw e; } finally { XMLUnit.setIgnoreWhitespace(false); } } public void testSeeAllDifferencesEvenIfDiffWouldSayHaltComparison() throws Exception { String control = ""; String test = ""; Diff d = new Diff(control, test); DetailedDiff dd = new DetailedDiff(d); List l = dd.getAllDifferences(); // number of children is different, didn't find , wrong // sequence of nodes assertEquals(3, l.size()); } public void testSeeAllDifferencesEvenIfDiffSaysHaltComparison() throws Exception { String control = ""; String test = ""; Diff d = new Diff(control, test); d.similar(); DetailedDiff dd = new DetailedDiff(d); List l = dd.getAllDifferences(); // number of children is different, didn't find , wrong // sequence of nodes assertEquals(3, l.size()); } /** * @see http://sourceforge.net/forum/forum.php?thread_id=1691528&forum_id=73274 */ public void testHelpForumThread1691528() throws Exception { String control = "" + "" + "" + "" + "" + "" + "" + "
    News
    Newsitem 1
    "; String test = "" + "" + "" + "" + "" + "" + "" + "" + "
    News
    Newsitem 2Newsitem 1
    "; DetailedDiff diff = new DetailedDiff(new Diff(control, test)); List changes = diff.getAllDifferences(); // number of children, text of first child, unexpected second // test child assertEquals(3, changes.size()); } /** * Bug 1860681 * @see https://sourceforge.net/tracker/index.php?func=detail&aid=1860681&group_id=23187&atid=377768 */ public void testXpathOfMissingNode() throws Exception { String control = "" + " " + " Kabale und Liebe" + " " + " " + " Schuld und Suehne" + " " + ""; String test = "" + " " + " Schuld und Suehne" + " " + ""; XMLUnit.setIgnoreWhitespace(true); try { Diff diff = new Diff(control, test); diff.overrideElementQualifier(new MultiLevelElementNameAndTextQualifier(2)); DetailedDiff dd = new DetailedDiff(diff); List l = dd.getAllDifferences(); assertEquals(3, l.size()); // (0) number of children, (1) node not found, (2) order different Difference d = (Difference) l.get(1); assertEquals(DifferenceConstants.CHILD_NODE_NOT_FOUND_ID, d.getId()); assertEquals("/books[1]/book[1]", d.getControlNodeDetail().getXpathLocation()); assertNull("should be null but is " + d.getTestNodeDetail().getXpathLocation(), d.getTestNodeDetail().getXpathLocation()); // and reverse diff = new Diff(test, control); diff.overrideElementQualifier(new MultiLevelElementNameAndTextQualifier(2)); dd = new DetailedDiff(diff); l = dd.getAllDifferences(); assertEquals(3, l.size()); // (0) number of children, (1) order different, (2) node not found d = (Difference) l.get(2); assertEquals(DifferenceConstants.CHILD_NODE_NOT_FOUND_ID, d.getId()); assertEquals("/books[1]/book[1]", d.getTestNodeDetail().getXpathLocation()); assertNull(d.getControlNodeDetail().getXpathLocation()); } finally { XMLUnit.setIgnoreWhitespace(false); } } protected Diff buildDiff(Document control, Document test) { return new DetailedDiff(super.buildDiff(control, test)); } protected Diff buildDiff(String control, String test) throws Exception { return new DetailedDiff(super.buildDiff(control, test)); } protected Diff buildDiff(Reader control, Reader test) throws Exception { return new DetailedDiff(super.buildDiff(control, test)); } protected Diff buildDiff(String control, String test, DifferenceEngine engine) throws Exception { return new DetailedDiff(super.buildDiff(control, test, engine)); } public test_DetailedDiff(String name) { super(name); firstForecast = "" + "unsettled"; secondForecast = ""; } /** * https://sourceforge.net/tracker/?func=detail&aid=2758280&group_id=23187&atid=377768 */ public void testCompareUnmatched() throws Exception { String control = "1" + "1" + "1" + "1" + "1"; String test = "1" + "1" + "1" + "1" + "1"; DetailedDiff d = (DetailedDiff) buildDiff(control, test); List l = d.getAllDifferences(); assertEquals(1, l.size()); Difference diff = (Difference) l.get(0); assertEquals(DifferenceConstants.ELEMENT_TAG_NAME_ID, diff.getId()); } /** * https://sourceforge.net/tracker/?func=detail&aid=2758280&group_id=23187&atid=377768 */ public void testDontCompareUnmatched() throws Exception { String control = "1" + "1" + "1" + "1" + "1"; String test = "1" + "1" + "1" + "1" + "1"; try { XMLUnit.setCompareUnmatched(false); DetailedDiff d = (DetailedDiff) buildDiff(control, test); List l = d.getAllDifferences(); assertEquals(2, l.size()); Difference diff = (Difference) l.get(0); assertEquals(DifferenceConstants.CHILD_NODE_NOT_FOUND_ID, diff.getId()); assertNotNull(diff.getControlNodeDetail().getNode()); assertNull(diff.getTestNodeDetail().getNode()); diff = (Difference) l.get(1); assertEquals(DifferenceConstants.CHILD_NODE_NOT_FOUND_ID, diff.getId()); assertNull(diff.getControlNodeDetail().getNode()); assertNotNull(diff.getTestNodeDetail().getNode()); } finally { XMLUnit.setCompareUnmatched(true); } } /** * @see https://sourceforge.net/tracker/index.php?func=detail&aid=3062518&group_id=23187&atid=377768 */ public void testIssue3062518() throws Exception { String control = "" + "" + "" + "" + ""; String test = "" + "" + "" + ""; try { XMLUnit.setCompareUnmatched(false); DetailedDiff d = (DetailedDiff) buildDiff(control, test); List l = d.getAllDifferences(); assertEquals(4, l.size()); // expected 3 children is 2 Difference diff = (Difference) l.get(0); assertEquals(DifferenceConstants.CHILD_NODELIST_LENGTH_ID, diff.getId()); assertEquals("3", diff.getControlNodeDetail().getValue()); assertEquals("2", diff.getTestNodeDetail().getValue()); assertEquals("/Fruits[1]", diff.getControlNodeDetail().getXpathLocation()); assertEquals("/Fruits[1]", diff.getTestNodeDetail().getXpathLocation()); // didn't find the second Apple element diff = (Difference) l.get(1); assertEquals(DifferenceConstants.CHILD_NODE_NOT_FOUND_ID, diff.getId()); assertEquals("Apple", diff.getControlNodeDetail().getValue()); assertEquals("null", diff.getTestNodeDetail().getValue()); assertEquals("/Fruits[1]/Apple[2]", diff.getControlNodeDetail().getXpathLocation()); assertEquals(null, diff.getTestNodeDetail().getXpathLocation()); // Banana's size attribute doesn't match diff = (Difference) l.get(2); assertEquals(DifferenceConstants.ATTR_VALUE_ID, diff.getId()); assertEquals("10", diff.getControlNodeDetail().getValue()); assertEquals("11", diff.getTestNodeDetail().getValue()); assertEquals("/Fruits[1]/Banana[1]/@size", diff.getControlNodeDetail().getXpathLocation()); assertEquals("/Fruits[1]/Banana[1]/@size", diff.getTestNodeDetail().getXpathLocation()); // Banana is the third child in control but the second one in test diff = (Difference) l.get(3); assertEquals("2", diff.getControlNodeDetail().getValue()); assertEquals("1", diff.getTestNodeDetail().getValue()); assertEquals("/Fruits[1]/Banana[1]", diff.getControlNodeDetail().getXpathLocation()); assertEquals("/Fruits[1]/Banana[1]", diff.getTestNodeDetail().getXpathLocation()); } finally { XMLUnit.setCompareUnmatched(true); } } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_Difference.java0000644000000000000000000001505412451007364024630 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2009,2014, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.util.List; import junit.framework.TestCase; import org.w3c.dom.Document; import org.w3c.dom.Node; /** * @author TimBacon */ public class test_Difference extends TestCase { private final Difference ORIGINAL = DifferenceConstants.ATTR_NAME_NOT_FOUND; public void testCopyConstructor() { Difference copy = new Difference(ORIGINAL, null, null); assertEquals("id", ORIGINAL.getId(), copy.getId()); assertEquals("description", ORIGINAL.getDescription(), copy.getDescription()); assertEquals("recoverable", ORIGINAL.isRecoverable(), copy.isRecoverable()); assertEquals("precondition", false, ORIGINAL.isRecoverable()); copy.setRecoverable(true); assertEquals("recoverable again", !ORIGINAL.isRecoverable(), copy.isRecoverable()); } public void testEquals() { assertTrue("not equal to null", !ORIGINAL.equals(null)); assertTrue("not equal to other class", !ORIGINAL.equals("aString")); assertEquals("equal to self", ORIGINAL, ORIGINAL); Difference copy = new Difference(ORIGINAL, null, null); assertEquals("equal to copy", ORIGINAL, copy); } public void testToString() throws Exception { String originalAsString = "Difference (#" + ORIGINAL.getId() + ") " + ORIGINAL.getDescription(); assertEquals("Original", originalAsString, ORIGINAL.toString()); Document document = XMLUnit.newControlParser().newDocument(); Node controlNode = document.createComment("control"); NodeDetail controlNodeDetail = new NodeDetail(controlNode.getNodeValue(), controlNode, "/testToString/comment()"); Node testNode = document.createComment("test"); NodeDetail testNodeDetail = new NodeDetail(testNode.getNodeValue(), testNode, "/testToString/comment()"); Difference difference = new Difference(DifferenceConstants.COMMENT_VALUE, controlNodeDetail, testNodeDetail); StringBuffer buf = new StringBuffer("Expected ") .append(DifferenceConstants.COMMENT_VALUE.getDescription()) .append(" 'control' but was 'test' - comparing "); NodeDescriptor.appendNodeDetail(buf, controlNodeDetail); buf.append(" to "); NodeDescriptor.appendNodeDetail(buf, testNodeDetail); assertEquals("detail", buf.toString(), difference.toString()); } // bug 2386807 public void testXpathOfMissingAttribute() throws Exception { Diff d = new Diff("", ""); DetailedDiff dd = new DetailedDiff(d); List diffs = dd.getAllDifferences(); assertEquals(2, diffs.size()); Difference d1 = (Difference) diffs.get(0); assertEquals(DifferenceConstants.ELEMENT_NUM_ATTRIBUTES_ID, d1.getId()); assertEquals("/foo[1]/bar[1]", d1.getControlNodeDetail().getXpathLocation()); assertEquals("/foo[1]/bar[1]", d1.getTestNodeDetail().getXpathLocation()); Difference d2 = (Difference) diffs.get(1); assertEquals(DifferenceConstants.ATTR_NAME_NOT_FOUND_ID, d2.getId()); assertEquals("/foo[1]/bar[1]/@y", d2.getControlNodeDetail().getXpathLocation()); assertEquals("/foo[1]/bar[1]", d2.getTestNodeDetail().getXpathLocation()); } public void testXpathOfMissingTestAttribute() throws Exception { Diff d = new Diff("", ""); DetailedDiff dd = new DetailedDiff(d); List diffs = dd.getAllDifferences(); assertEquals(2, diffs.size()); Difference d1 = (Difference) diffs.get(0); assertEquals(DifferenceConstants.ELEMENT_NUM_ATTRIBUTES_ID, d1.getId()); assertEquals("/foo[1]/bar[1]", d1.getControlNodeDetail().getXpathLocation()); assertEquals("/foo[1]/bar[1]", d1.getTestNodeDetail().getXpathLocation()); Difference d2 = (Difference) diffs.get(1); assertEquals(DifferenceConstants.ATTR_NAME_NOT_FOUND_ID, d2.getId()); assertEquals("/foo[1]/bar[1]", d2.getControlNodeDetail().getXpathLocation()); assertEquals("/foo[1]/bar[1]/@y", d2.getTestNodeDetail().getXpathLocation()); } /** * Constructor for test_Difference. * @param name */ public test_Difference(String name) { super(name); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_DifferenceEngine.java0000644000000000000000000014045612451007364025763 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2008,2013-2014 Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import javax.xml.parsers.DocumentBuilder; import junit.framework.TestCase; import junit.framework.TestSuite; import org.w3c.dom.Attr; import org.w3c.dom.CDATASection; import org.w3c.dom.Comment; import org.w3c.dom.Document; import org.w3c.dom.DocumentType; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.ProcessingInstruction; import org.w3c.dom.Text; import org.xml.sax.SAXException; /** * JUnit test for DifferenceEngine */ public class test_DifferenceEngine extends TestCase implements DifferenceConstants { private CollectingDifferenceListener listener; private DifferenceEngine engine; private Document document; private final ComparisonController PSEUDO_DIFF = new SimpleComparisonController(); private final ComparisonController PSEUDO_DETAILED_DIFF = new NeverHaltingComparisonController(); private final static ElementQualifier DEFAULT_ELEMENT_QUALIFIER = new ElementNameQualifier(); private final static String TEXT_A = "the pack on my back is aching"; private final static String TEXT_B = "the straps seem to cut me like a knife"; private final static String COMMENT_A = "Im no clown I wont back down"; private final static String COMMENT_B = "dont need you to tell me whats going down"; private final static String[] PROC_A = {"down", "down down"}; private final static String[] PROC_B = {"dadada", "down"}; private final static String CDATA_A = "I'm standing alone, you're weighing the gold"; private final static String CDATA_B = "I'm watching you sinking... Fools Gold"; private final static String ATTR_A = "These boots were made for walking"; private final static String ATTR_B = "The marquis de sade never wore no boots like these"; private void assertDifferentText(Text control, Text test, Difference difference) { try { engine.compareText(control, test, listener); } catch (DifferenceEngine.DifferenceFoundException e) { } assertEquals(difference.getId(), listener.comparingWhat); assertEquals(true, listener.different); resetListener(); } public void testCompareText() throws Exception { String expected = TEXT_A; String actual = TEXT_B; Text control = document.createTextNode(expected); Text test = document.createTextNode(actual); assertDifferentText(control, test, TEXT_VALUE); } private void assertDifferentProcessingInstructions ( ProcessingInstruction control, ProcessingInstruction test, Difference difference) { try { engine.compareProcessingInstruction(control, test, listener); } catch (DifferenceEngine.DifferenceFoundException e) { } assertEquals(difference.getId(), listener.comparingWhat); assertEquals(true, listener.different); resetListener(); } public void testCompareProcessingInstruction() throws Exception { String[] expected = PROC_A; String[] actual = PROC_B; ProcessingInstruction control = document.createProcessingInstruction( expected[0], expected[1]); ProcessingInstruction test = document.createProcessingInstruction( actual[0], actual[1]); assertDifferentProcessingInstructions(control, test, PROCESSING_INSTRUCTION_TARGET); ProcessingInstruction control2 = document.createProcessingInstruction( expected[0], expected[1]); ProcessingInstruction test2 = document.createProcessingInstruction( expected[0], actual[1]); assertDifferentProcessingInstructions(control2, test2, PROCESSING_INSTRUCTION_DATA); } private void assertDifferentComments(Comment control, Comment test, Difference difference) { try { engine.compareComment(control, test, listener); } catch (DifferenceEngine.DifferenceFoundException e) { } assertEquals(difference.getId(), listener.comparingWhat); assertEquals(true, listener.different); resetListener(); } public void testCompareComment() throws Exception { String expected = COMMENT_A; String actual = COMMENT_B; Comment control = document.createComment(expected); Comment test = document.createComment(actual); assertDifferentComments(control, test, COMMENT_VALUE); } private void assertDifferentCDATA(CDATASection control, CDATASection test, Difference difference) { try { engine.compareCDataSection(control, test, listener); } catch (DifferenceEngine.DifferenceFoundException e) { } assertEquals(difference.getId(), listener.comparingWhat); assertEquals(true, listener.different); resetListener(); } public void testCompareCDATA() throws Exception { String expected = CDATA_A ; String actual = CDATA_B ; CDATASection control = document.createCDATASection(expected); CDATASection test = document.createCDATASection(actual); assertDifferentCDATA(control, test, CDATA_VALUE); } private void assertDifferentDocumentTypes(DocumentType control, DocumentType test, Difference difference, boolean fatal) { try { engine.compareDocumentType(control, test, listener); if (fatal) { fail("Expected fatal difference!"); } } catch (DifferenceEngine.DifferenceFoundException e) { if (!fatal) { fail("Expected similarity not fatal difference!"); } } assertEquals(difference.getId(), listener.comparingWhat); assertEquals(fatal, listener.different); resetListener(); } public void testCompareDocumentType() throws Exception { File tmpFile = File.createTempFile("Roses","dtd"); tmpFile.deleteOnExit(); String tmpDTD = ""; new FileWriter(tmpFile).write(tmpDTD); String rosesDTD = tmpFile.toURL().toExternalForm(); File altTmpFile = File.createTempFile("TheCrows", "dtd"); altTmpFile.deleteOnExit(); new FileWriter(altTmpFile).write(tmpDTD); String theCrowsDTD = altTmpFile.toURL().toExternalForm(); Document controlDoc = XMLUnit.buildControlDocument( "" + ""); Document testDoc = XMLUnit.buildTestDocument( "" + ""); DocumentType control = controlDoc.getDoctype(); DocumentType test = testDoc.getDoctype(); assertDifferentDocumentTypes(control, test, DOCTYPE_NAME, true); test = XMLUnit.buildTestDocument("" + "").getDoctype(); assertDifferentDocumentTypes(control, test, DOCTYPE_PUBLIC_ID, true); test = XMLUnit.buildTestDocument("" + "").getDoctype(); assertDifferentDocumentTypes(control, test, DOCTYPE_PUBLIC_ID, true); test = XMLUnit.buildTestDocument("" + "").getDoctype(); assertDifferentDocumentTypes(control, test, DOCTYPE_SYSTEM_ID, false); test = XMLUnit.buildTestDocument("" + "").getDoctype(); assertDifferentDocumentTypes(control, test, DOCTYPE_PUBLIC_ID, true); control = XMLUnit.buildTestDocument("" + "").getDoctype(); assertDifferentDocumentTypes(control, test, DOCTYPE_SYSTEM_ID, false); } private void assertDifferentAttributes(Attr control, Attr test, Difference difference, boolean fatal) { try { engine.compareAttribute(control, test, listener); if (fatal) { fail("Expecting fatal difference!"); } } catch (DifferenceEngine.DifferenceFoundException e) { if (!fatal) { fail("Expecting similarity not fatal difference!"); } } assertEquals(difference.getId(), listener.comparingWhat); assertEquals(fatal, listener.different); resetListener(); } public void testCompareAttribute() throws Exception { String expected = ATTR_A; String actual = ATTR_B; Attr control = document.createAttribute(getName()); control.setValue(expected); Attr test = document.createAttribute(getName()); test.setValue(actual); assertDifferentAttributes(control, test, ATTR_VALUE, true); String doctypeDeclaration = "" + "]>"; Document controlDoc = XMLUnit.buildControlDocument(doctypeDeclaration + ""); control = (Attr) controlDoc.getDocumentElement().getFirstChild() .getAttributes().getNamedItem("sorted"); Document testDoc = XMLUnit.buildTestDocument(doctypeDeclaration + ""); test = (Attr) testDoc.getDocumentElement().getFirstChild() .getAttributes().getNamedItem("sorted"); assertDifferentAttributes(control, test, ATTR_VALUE_EXPLICITLY_SPECIFIED, false); } private void assertDifferentElements(Element control, Element test, Difference difference) { try { engine.compareElement(control, test, listener); } catch (DifferenceEngine.DifferenceFoundException e) { } assertEquals(difference.getId(), listener.comparingWhat); assertEquals(true, listener.different); resetListener(); } public void testCompareElements() throws Exception { Document document = XMLUnit.buildControlDocument( "" + ""); Element control = (Element) document.getDocumentElement(); Element test = (Element) control.getFirstChild(); assertDifferentElements(control, test, ELEMENT_TAG_NAME); // compare im#1 to im#2 control = test; test = (Element) control.getNextSibling(); assertDifferentElements(control, test, ELEMENT_NUM_ATTRIBUTES); // compare im#1 to im#3 test = (Element) test.getNextSibling(); assertDifferentElements(control, test, ATTR_NAME_NOT_FOUND); // compare im#3 to im#4 control = test; test = (Element) control.getNextSibling(); assertDifferentElements(control, test, ATTR_VALUE); } public void testCompareNode() throws Exception { Document controlDocument = XMLUnit.buildControlDocument("" + "" + "" + "" + TEXT_A + ""); Document testDocument = XMLUnit.buildTestDocument("" + "" + "" + "" + TEXT_B + ""); engine.compare(controlDocument, testDocument, listener, null); Node control = controlDocument.getDocumentElement().getFirstChild(); Node test = testDocument.getDocumentElement().getFirstChild(); do { resetListener(); engine.compare(control, test, listener, null); assertEquals(true, -1 != listener.comparingWhat); assertEquals(false, listener.nodesSkipped); resetListener(); engine.compare(control, control, listener, null); assertEquals(-1, listener.comparingWhat); control = control.getNextSibling(); test = test.getNextSibling(); } while (control != null); } private void assertDifferentNamespaceDetails(Node control, Node test, Difference expectedDifference, boolean fatal) { try { engine.compareNodeBasics(control, test, listener); if (fatal) { fail("Expected fatal difference"); } } catch (DifferenceEngine.DifferenceFoundException e) { if (!fatal) { fail("Not expecting fatal difference!"); } } assertEquals(expectedDifference.getId(), listener.comparingWhat); assertEquals(fatal, listener.different); resetListener(); } public void testCompareNodeBasics() throws Exception { String namespaceA = "http://example.org/StoneRoses"; String namespaceB = "http://example.org/Stone/Roses"; String prefixA = "music"; String prefixB = "cd"; String elemName = "nowPlaying"; Element control = document.createElementNS(namespaceA, prefixA + ':' + elemName); engine.compareNodeBasics(control, control, listener); Element test = document.createElementNS(namespaceB, prefixA + ':' + elemName); assertDifferentNamespaceDetails(control, test, NAMESPACE_URI, true); test = document.createElementNS(namespaceA, prefixB + ':' + elemName); assertDifferentNamespaceDetails(control, test, NAMESPACE_PREFIX, false); } private void assertDifferentChildren(Node control, Node test, Difference expectedDifference, boolean fatal) { try { engine.compareHasChildNodes(control, test, listener); engine.compareNodeChildren(control, test, listener, DEFAULT_ELEMENT_QUALIFIER); if (fatal) { fail("Expected fatal difference"); } } catch (DifferenceEngine.DifferenceFoundException e) { if (!fatal) { fail("Not expecting fatal difference " + listener.comparingWhat + ": expected " + listener.expected + " but was " + listener.actual); } } assertEquals(expectedDifference==null ? -1 : expectedDifference.getId(), listener.comparingWhat); assertEquals(fatal, listener.different); resetListener(); } public void testCompareNodeChildren() throws Exception { document = XMLUnit.buildControlDocument( "you all" + "sinking"); // compare im #1 to itself Node control = document.getDocumentElement().getFirstChild(); Node test = control; assertDifferentChildren(control, control, null, false); // compare im #1 to im #2 test = control.getNextSibling(); assertDifferentChildren(control, test, HAS_CHILD_NODES, true); // compare im #2 to im #3 control = test; test = control.getNextSibling(); assertDifferentChildren(control, test, CHILD_NODELIST_LENGTH, true); } private void assertDifferentNodeLists(Node control, Node test, Difference expectedDifference, boolean fatal) { try { engine.compareNodeList(control.getChildNodes(), test.getChildNodes(), control.getChildNodes().getLength(), listener, DEFAULT_ELEMENT_QUALIFIER); if (fatal) { fail("Expected fatal difference"); } } catch (DifferenceEngine.DifferenceFoundException e) { if (!fatal) { fail("Not expecting fatal difference!"); } } assertEquals(expectedDifference==null ? -1 : expectedDifference.getId(), listener.comparingWhat); assertEquals(fatal, listener.different); resetListener(); } public void testCompareNodeList() throws Exception { document = XMLUnit.buildControlDocument( "aloneyou all" + "you sinking"); // compare im #1 to itself Node control = document.getDocumentElement().getFirstChild(); Node test = control; assertDifferentNodeLists(control, test, null, false); // compare im #1 to im #2 test = control.getNextSibling(); assertDifferentChildren(control, test, ELEMENT_TAG_NAME, true); // compare im #2 to im #3 control = test; test = control.getNextSibling(); assertDifferentChildren(control, test, TEXT_VALUE, true); } public void testCompareNodeListElements() throws Exception { Element control = document.createElement("root"); control.appendChild(document.createElement("leafElemA")); control.appendChild(document.createElement("leafElemB")); Element test = document.createElement("root"); test.appendChild(document.createElement("leafElemB")); test.appendChild(document.createElement("leafElemA")); assertDifferentChildren(control, test, CHILD_NODELIST_SEQUENCE, false); assertDifferentChildren(test, control, CHILD_NODELIST_SEQUENCE, false); } public void testCompareNodeListMixedContent() throws Exception { Element control = document.createElement("root"); control.appendChild(document.createTextNode("text leaf")); control.appendChild(document.createElement("leafElem")); Element test = document.createElement("root"); test.appendChild(document.createElement("leafElem")); test.appendChild(document.createTextNode("text leaf")); assertDifferentChildren(control, test, CHILD_NODELIST_SEQUENCE, false); assertDifferentChildren(test, control, CHILD_NODELIST_SEQUENCE, false); } public void testBasicCompare() throws Exception { try { engine.compare("black", "white", null, null, listener, ATTR_NAME_NOT_FOUND); fail("Expected difference found exception"); } catch (DifferenceEngine.DifferenceFoundException e) { assertEquals(true, listener.different); assertEquals(ATTR_NAME_NOT_FOUND.getId(), listener.comparingWhat); } resetListener(); try { engine.compare("black", "white", null, null, listener, NAMESPACE_PREFIX); assertEquals(false, listener.different); assertEquals(NAMESPACE_PREFIX.getId(), listener.comparingWhat); } catch (Exception e) { fail("Not expecting difference found exception"); } } public void testXpathLocation1() throws Exception { String control = ""; String test = ""; listenToDifferences(control, test); assertEquals("1st control xpath", "/dvorak[1]", listener.controlXpath); assertEquals("1st test xpath", "/qwerty[1]", listener.testXpath); } public void testXpathLocation2() throws Exception { String control = ""; String test = ""; String start = "", end = ""; listenToDifferences(start + control + end, start + test + end); assertEquals("2nd control xpath", "/a[1]/dvorak[1]", listener.controlXpath); assertEquals("2nd test xpath", "/a[1]/qwerty[1]", listener.testXpath); } public void testXpathLocation3() throws Exception { String control = ""; String test = ""; listenToDifferences(control, test); assertEquals("3rd control xpath", "/stuff[1]/wood[1]/@type", listener.controlXpath); assertEquals("3rd test xpath", "/stuff[1]/wood[1]/@type", listener.testXpath); } public void testXpathLocation4() throws Exception { String control = ""; String test = "";; listenToDifferences(control, test); assertEquals("4th control xpath", "/stuff[1]/glass[2]/@colour", listener.controlXpath); assertEquals("4th test xpath", "/stuff[1]/glass[2]/@colour", listener.testXpath); } public void testXpathLocation5() throws Exception { String control = "mapleoak"; String test = "mapleash"; listenToDifferences(control, test); assertEquals("5th control xpath", "/stuff[1]/wood[2]/text()[1]", listener.controlXpath); assertEquals("5th test xpath", "/stuff[1]/wood[2]/text()[1]", listener.testXpath); } public void testXpathLocation6() throws Exception { String control = ""; String test = "description"; listenToDifferences(control, test); assertEquals("6th control xpath", "/stuff[1]/item[1]", listener.controlXpath); assertEquals("6th test xpath", "/stuff[1]/item[1]", listener.testXpath); } public void testXpathLocation7() throws Exception { String control = ""; String test = ""; listenToDifferences(control, test); assertEquals("7th control xpath", "/stuff[1]/list[1]/wood[1]", listener.controlXpath); assertEquals("7th test xpath", "/stuff[1]/list[1]/glass[1]", listener.testXpath); } public void testXpathLocation8() throws Exception { String control = ""; String test = ""; listenToDifferences(control, test); assertEquals("8th control xpath", "/stuff[1]/list[1]/comment()[1]", listener.controlXpath); assertEquals("8th test xpath", "/stuff[1]/list[1]/comment()[1]", listener.testXpath); } public void testXpathLocation9() throws Exception { String control = ""; String test = ""; listenToDifferences(control, test); assertEquals("9th control xpath", "/stuff[1]/processing-instruction()[1]", listener.controlXpath); assertEquals("9th test xpath", "/stuff[1]/processing-instruction()[1]", listener.testXpath); } public void testXpathLocation10() throws Exception { String control = "list"; String test = "list"; listenToDifferences(control, test); assertEquals("10th control xpath", "/stuff[1]/text()[2]", listener.controlXpath); assertEquals("10th test xpath", "/stuff[1]/text()[2]", listener.testXpath); } public void testXpathLocation11() throws Exception { String control = ""; String test = "item text"; listenToDifferences(control, test); assertEquals("11th control xpath", "/stuff[1]/list[1]/item[1]", listener.controlXpath); assertEquals("11th test xpath", "/stuff[1]/list[1]/text()[1]", listener.testXpath); } public void testXpathLocation12() throws Exception { engine = new DifferenceEngine(PSEUDO_DETAILED_DIFF); String control = ""; String test = ""; listenToDifferences(control, test); assertEquals("12th control xpath", "/stuff[1]/item[2]", listener.controlXpath); assertEquals("12th test xpath", "/stuff[1]/item[1]", listener.testXpath); } public void testXpathLocation13() throws Exception { engine = new DifferenceEngine(PSEUDO_DETAILED_DIFF); String control = ""; String test = ""; listenToDifferences(control, test); // mutiple Differences, we only see the last one, missing second element assertEquals("item", listener.expected); assertEquals("null", listener.actual); assertEquals("13 difference type", DifferenceConstants.CHILD_NODE_NOT_FOUND_ID, listener.comparingWhat); assertEquals("13th control xpath", "/stuff[1]/item[2]", listener.controlXpath); assertNull("13th test xpath", listener.testXpath); } public void testMissingChildNS() throws Exception { engine = new DifferenceEngine(PSEUDO_DETAILED_DIFF); String control = "" + ""; String test = ""; listenToDifferences(control, test); // mutiple Differences, we only see the last one, missing second element assertEquals("{http://example.org/}item", listener.expected); assertEquals("null", listener.actual); assertEquals("13 difference type", DifferenceConstants.CHILD_NODE_NOT_FOUND_ID, listener.comparingWhat); assertEquals("13th control xpath", "/stuff[1]/item[2]", listener.controlXpath); assertNull("13th test xpath", listener.testXpath); } public void testXpathLocation14() throws Exception { engine = new DifferenceEngine(PSEUDO_DETAILED_DIFF); String control = ""; String test = ""; listenToDifferences(control, test); assertEquals("14th control xpath", "/stuff[1]/item[1]/@id", listener.controlXpath); assertEquals("14th test xpath", "/stuff[1]/item[2]/@id", listener.testXpath); } public void testIssue1027863() throws Exception { engine = new DifferenceEngine(PSEUDO_DETAILED_DIFF); String control = ""; String test = ""; listenToDifferences(control, test); assertEquals("15th difference type", DifferenceEngine.CHILD_NODE_NOT_FOUND_ID, listener.comparingWhat); assertEquals("15th difference control value", "thing", listener.expected); assertEquals("15th difference test value", "null", listener.actual); assertEquals("15th control xpath", "/stuff[1]/item[1]/thing[1]", listener.controlXpath); assertNull("15th test xpath should be null but is " + listener.testXpath, listener.testXpath); } public void testNormalizeWhitespace() { assertEquals("a b", DifferenceEngine.normalizeWhitespace("a\rb")); assertEquals("a b", DifferenceEngine.normalizeWhitespace("a b")); assertEquals("a b c d e f", DifferenceEngine .normalizeWhitespace("a\rb c\nd\te\r\n \tf")); } public void testAttributeSequence() throws Exception { testAttributeSequence(ATTR_SEQUENCE_ID); resetListener(); XMLUnit.setIgnoreAttributeOrder(true); try { testAttributeSequence(-1); } finally { XMLUnit.setIgnoreAttributeOrder(false); } } private void testAttributeSequence(int expected) throws Exception { Element control = document.createElement("foo"); Element test = document.createElement("foo"); OrderPreservingNamedNodeMap controlMap = new OrderPreservingNamedNodeMap(); OrderPreservingNamedNodeMap testMap = new OrderPreservingNamedNodeMap(); for (int i = 0; i < 2; i++) { int j = 1 - i; Attr attrI = document.createAttribute("attr" + i); attrI.setValue(String.valueOf(i)); Attr attrJ = document.createAttribute("attr" + j); attrJ.setValue(String.valueOf(j)); control.setAttributeNode(attrI); controlMap.add(attrI); test.setAttributeNode(attrJ); testMap.add(attrJ); } engine.compareElementAttributes(control, test, controlMap, testMap, listener); assertEquals(expected, listener.comparingWhat); } public void testAttributeSequenceNS() throws Exception { testAttributeSequenceNS(ATTR_SEQUENCE_ID); resetListener(); XMLUnit.setIgnoreAttributeOrder(true); try { testAttributeSequenceNS(-1); } finally { XMLUnit.setIgnoreAttributeOrder(false); } } private void testAttributeSequenceNS(int expected) throws Exception { Element control = document.createElementNS("ns", "foo"); Element test = document.createElementNS("ns", "foo"); OrderPreservingNamedNodeMap controlMap = new OrderPreservingNamedNodeMap(); OrderPreservingNamedNodeMap testMap = new OrderPreservingNamedNodeMap(); for (int i = 0; i < 2; i++) { int j = 1 - i; Attr attrI = document.createAttributeNS("ns", "attr" + i); attrI.setValue(String.valueOf(i)); Attr attrJ = document.createAttributeNS("ns", "attr" + j); attrJ.setValue(String.valueOf(j)); control.setAttributeNode(attrI); controlMap.add(attrI); test.setAttributeNode(attrJ); testMap.add(attrJ); } engine.compareElementAttributes(control, test, controlMap, testMap, listener); assertEquals(expected, listener.comparingWhat); } public void testExtraComment() { testExtraComment(true); resetListener(); XMLUnit.setIgnoreComments(true); try { testExtraComment(false); } finally { XMLUnit.setIgnoreComments(false); } } private void testExtraComment(boolean expectDifference) { Element control = document.createElement("foo"); Element test = document.createElement("foo"); Comment c = document.createComment("bar"); control.appendChild(c); Element cChild = document.createElement("baz"); control.appendChild(cChild); Element tChild = document.createElement("baz"); test.appendChild(tChild); engine.compare(control, test, listener, null); assertEquals(expectDifference, listener.different); resetListener(); engine.compare(test, control, listener, null); assertEquals(expectDifference, listener.different); } public void testCommentContent() { testCommentContent(true); resetListener(); XMLUnit.setIgnoreComments(true); try { testCommentContent(false); } finally { XMLUnit.setIgnoreComments(false); } } private void testCommentContent(boolean expectDifference) { Element control = document.createElement("foo"); Element test = document.createElement("foo"); Comment c = document.createComment("bar"); control.appendChild(c); Comment c2 = document.createComment("baz"); test.appendChild(c2); engine.compare(control, test, listener, null); assertEquals(expectDifference, listener.different); } public void testMissingSchemaLocation() throws Exception { testMissingXSIAttribute(XMLConstants .W3C_XML_SCHEMA_INSTANCE_SCHEMA_LOCATION_ATTR, DifferenceConstants.SCHEMA_LOCATION_ID); } public void testMissingNoNamespaceSchemaLocation() throws Exception { testMissingXSIAttribute(XMLConstants .W3C_XML_SCHEMA_INSTANCE_NO_NAMESPACE_SCHEMA_LOCATION_ATTR, DifferenceConstants.NO_NAMESPACE_SCHEMA_LOCATION_ID); } private void testMissingXSIAttribute(String attrName, int expectedDifference) throws Exception { Element control = document.createElement("foo"); control.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, attrName, "bar"); Element test = document.createElement("foo"); engine.compare(control, test, listener, null); assertEquals(expectedDifference, listener.comparingWhat); resetListener(); engine.compare(test, control, listener, null); assertEquals(expectedDifference, listener.comparingWhat); } public void testDifferentSchemaLocation() throws Exception { testDifferentXSIAttribute(XMLConstants .W3C_XML_SCHEMA_INSTANCE_SCHEMA_LOCATION_ATTR, DifferenceConstants.SCHEMA_LOCATION_ID); } public void testDifferentNoNamespaceSchemaLocation() throws Exception { testDifferentXSIAttribute(XMLConstants .W3C_XML_SCHEMA_INSTANCE_NO_NAMESPACE_SCHEMA_LOCATION_ATTR, DifferenceConstants.NO_NAMESPACE_SCHEMA_LOCATION_ID); } private void testDifferentXSIAttribute(String attrName, int expectedDifference) throws Exception { Element control = document.createElement("foo"); control.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, attrName, "bar"); Element test = document.createElement("foo"); test.setAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, attrName, "baz"); engine.compare(control, test, listener, null); assertEquals(expectedDifference, listener.comparingWhat); } public void testMissingAttribute() throws Exception { Element control = document.createElement("foo"); control.setAttribute("bar", "baz"); Element test = document.createElement("foo"); test.setAttribute("baz", "bar"); engine.compare(control, test, listener, null); assertEquals(ATTR_NAME_NOT_FOUND_ID, listener.comparingWhat); assertEquals("bar", listener.expected); assertEquals("null", listener.actual); assertEquals("/foo[1]/@bar", listener.controlXpath); assertEquals("/foo[1]", listener.testXpath); } public void testMissingAttributeNS() throws Exception { Element control = document.createElement("foo"); control.setAttributeNS("http://example.org/", "bar", "baz"); Element test = document.createElement("foo"); test.setAttributeNS("http://example.org/", "baz", "bar"); engine.compare(control, test, listener, null); assertEquals(ATTR_NAME_NOT_FOUND_ID, listener.comparingWhat); assertEquals("{http://example.org/}bar", listener.expected); assertEquals("null", listener.actual); assertEquals("/foo[1]/@bar", listener.controlXpath); assertEquals("/foo[1]", listener.testXpath); } public void testMatchTrackerSetViaConstructor() throws Exception { Element control = document.createElement("foo"); Element test = document.createElement("foo"); final int[] count = new int[1]; DifferenceEngine d = new DifferenceEngine(new SimpleComparisonController(), new MatchTracker() { public void matchFound(Difference d) { count[0]++; } }); d.compare(control, test, listener, null); // NODE_TYPE (not null), NODE_TYPE(Element), NAMESPACE_URI(none), // NAMESPACE_PREFIX(none), ELEMENT_TAG_NAME(foo), // ELEMENT_NUM_ATTRIBUTE(none), HAS_CHILD_NODES(false), // CHILD_NODELIST_LENGTH(0) assertEquals(8, count[0]); } public void testMatchTrackerSetViaSetter() throws Exception { Element control = document.createElement("foo"); Element test = document.createElement("foo"); final int[] count = new int[1]; engine.setMatchTracker(new MatchTracker() { public void matchFound(Difference d) { count[0]++; } }); engine.compare(control, test, listener, null); // NODE_TYPE (not null), NODE_TYPE(Element), NAMESPACE_URI(none), // NAMESPACE_PREFIX(none), ELEMENT_TAG_NAME(foo), // ELEMENT_NUM_ATTRIBUTE(none), HAS_CHILD_NODES(false), // CHILD_NODELIST_LENGTH(0) assertEquals(8, count[0]); } /** * @see http://sourceforge.net/forum/forum.php?thread_id=3284504&forum_id=73274 */ public void testNamespaceAttributeDifferences() throws Exception { String control = "" + "" + "" + "9999" + "1243409665297" + "1.0" + "TEST-EVENT" + "TEST" + "2009-01-01T12:00:00" + "anything" + "anything" + "F" + "" + "" + "" + "" + "A" + "B" + "" + "" + "" + ""; String test = "" + "" + "9999" + "1243409665297" + "1.0" + "TEST-EVENT" + "TEST" + "2009-01-01T12:00:00" + "anything" + "anything" + "F" + "" + "" + "" + "" + "A" + "B" + "" + "" + "" + ""; listenToDifferences(control, test); assertFalse(listener.different); } /** * @see https://sourceforge.net/forum/message.php?msg_id=4817456 */ public void testForumMessage4817456() throws Exception { String control = "" + "123" + "a" + "b" + "c" + "d" + "1" + "2" + "3" + ""; String test = "" + "123" + "a" + "b" + "c" + "d" + "e" + "2" + "3" + ""; Document controlDoc = XMLUnit.buildControlDocument(control); Document testDoc = XMLUnit.buildTestDocument(test); engine.compare(controlDoc, testDoc, listener, new ElementNameAndTextQualifier()); assertTrue(listener.different); assertEquals(DifferenceConstants.ELEMENT_TAG_NAME_ID, listener.comparingWhat); } private void listenToDifferences(String control, String test) throws SAXException, IOException { Document controlDoc = XMLUnit.buildControlDocument(control); Document testDoc = XMLUnit.buildTestDocument(test); engine.compare(controlDoc, testDoc, listener, DEFAULT_ELEMENT_QUALIFIER); } private void resetListener() { listener = new CollectingDifferenceListener(); } public void setUp() throws Exception { resetListener(); engine = new DifferenceEngine(PSEUDO_DIFF); DocumentBuilder documentBuilder = XMLUnit.newControlParser(); document = documentBuilder.newDocument(); } private class SimpleComparisonController implements ComparisonController { public boolean haltComparison(Difference afterDifference) { return !afterDifference.isRecoverable(); } } private class NeverHaltingComparisonController implements ComparisonController { public boolean haltComparison(Difference afterDifference) { return false; } } private class CollectingDifferenceListener implements DifferenceListener { public String expected; public String actual; public Node control; public Node test; public int comparingWhat = -1; public boolean different = false; public boolean nodesSkipped = false; public String controlXpath; public String testXpath; private boolean tracing = false; public int differenceFound(Difference difference) { if (tracing) { System.out.println(difference.toString()); } assertNotNull("difference not null", difference); assertNotNull("control node detail not null", difference.getControlNodeDetail()); assertNotNull("test node detail not null", difference.getTestNodeDetail()); this.expected = difference.getControlNodeDetail().getValue(); this.actual = difference.getTestNodeDetail().getValue(); this.control = difference.getControlNodeDetail().getNode(); this.test = difference.getTestNodeDetail().getNode(); this.comparingWhat = difference.getId(); this.different = !difference.isRecoverable(); this.controlXpath = difference.getControlNodeDetail().getXpathLocation(); this.testXpath = difference.getTestNodeDetail().getXpathLocation(); return RETURN_ACCEPT_DIFFERENCE; } public void skippedComparison(Node control, Node test) { nodesSkipped = true; } public void setTrace(boolean active) { tracing = active; } } private class OrderPreservingNamedNodeMap implements NamedNodeMap { private ArrayList/* Attr */ nodes = new ArrayList(); void add(Attr attr) { nodes.add(attr); } public int getLength() { return nodes.size(); } public Node item(int index) { return (Node) nodes.get(index); } public Node getNamedItem(String name) { for (Iterator iter = nodes.iterator(); iter.hasNext(); ) { Attr a = (Attr) iter.next(); if (a.getName().equals(name)) { return a; } } return null; } public Node getNamedItemNS(String ns, String localName) { for (Iterator iter = nodes.iterator(); iter.hasNext(); ) { Attr a = (Attr) iter.next(); if (a.getLocalName().equals(localName) && a.getNamespaceURI().equals(ns)) { return a; } } return null; } // not implemented, not needed in our case public Node removeNamedItem(String n) { return fail(); } public Node removeNamedItemNS(String n1, String n2) { return fail(); } public Node setNamedItem(Node n) { return fail(); } public Node setNamedItemNS(Node n) { return fail(); } private Node fail() { throw new RuntimeException("not implemented"); } } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/Replacement.java0000644000000000000000000000770112451007364023776 0ustar rootroot/* ****************************************************************** Copyright (c) 200, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; /** * Performs replacement of one String by another String * within one or more Strings. * This was once required but a code refactoring made it redundant and I don't have * the heart to kill it off...! */ public class Replacement { private final char[] ofChars; private final char[] byChars; public Replacement(String ofString, String byString) { this(ofString.toCharArray(), byString == null ? null : byString.toCharArray()); } public Replacement(char[] ofChars, char[] byChars) { this.ofChars = ofChars; this.byChars = byChars; } public final String replace(String inString) { StringBuffer buf = replaceAndAppend(inString.toCharArray(), new StringBuffer(inString.length())); return buf.toString(); } public final char[] replace(char[] inChars) { StringBuffer buf = replaceAndAppend(inChars, new StringBuffer(inChars.length)); char[] replacement = new char[buf.length()]; buf.getChars(0, buf.length(), replacement, 0); return replacement; } public final StringBuffer replaceAndAppend(char[] inChars, StringBuffer toBuffer) { int ofPos = 0; int falseStartPos = -1; for (int i=0; i < inChars.length; ++i) { if (inChars[i] == ofChars[ofPos]) { if (falseStartPos == -1) { falseStartPos = i; } ++ofPos; } else { ofPos = 0; if (falseStartPos != -1) { for (; falseStartPos < i; ++falseStartPos) { toBuffer.append(inChars[falseStartPos]); } falseStartPos = -1; } toBuffer.append(inChars[i]); } if (ofPos == ofChars.length) { if (hasReplaceBy()) { toBuffer.append(byChars); } ofPos = 0; falseStartPos = -1; } } return toBuffer; } public boolean hasReplaceBy() { return byChars != null && byChars.length > 0; } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_NodeInputStream.java0000644000000000000000000000602512451007364025655 0ustar rootroot/* ****************************************************************** Copyright (c) 200, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.io.InputStreamReader; import java.io.StringReader; import junit.framework.TestCase; import junit.framework.TestSuite; import org.w3c.dom.Document; /** * JUnit test for NodeInputStream */ public class test_NodeInputStream extends TestCase { private NodeInputStream nodeStream; private final String frog = ""; private final String ribbit = "" + "fertilised egg" + ""; public void setUp() throws Exception { Document document = XMLUnit.buildControlDocument(ribbit); nodeStream = new NodeInputStream(document); } public void testRead() throws Exception { InputStreamReader reader = new InputStreamReader(nodeStream); Diff diff = new Diff(new StringReader(ribbit), reader); assertEquals(diff.toString(), true, diff.identical()); } public void testAvailable() throws Exception { int available = nodeStream.available(); assertEquals("available="+available, true, available > 0); nodeStream.read(); assertEquals(available - 1, nodeStream.available()); } public test_NodeInputStream(String name) { super(name); } public static TestSuite suite() { return new TestSuite(test_NodeInputStream.class); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_XpathNodeTracker.java0000644000000000000000000002053012451007364025777 0ustar rootroot/* ****************************************************************** Copyright (c) 2001, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import junit.framework.TestCase; import org.w3c.dom.Attr; import org.w3c.dom.CDATASection; import org.w3c.dom.Comment; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.ProcessingInstruction; import org.w3c.dom.Text; /** * Testcase for XpathNodeTracker */ public class test_XpathNodeTracker extends TestCase { private XpathNodeTracker xpathNodeTracker; private static final Node DUMMY_NODE = null; public void testRootNode() { xpathNodeTracker.visitedNode(DUMMY_NODE, "diary"); assertEquals("root node", "/diary[1]", xpathNodeTracker.toXpathString()); } public void testOneLevelOfChildren() { xpathNodeTracker.visitedNode(DUMMY_NODE, "diary"); xpathNodeTracker.indent(); assertEquals("before first child", "/diary[1]", xpathNodeTracker.toXpathString()); xpathNodeTracker.visitedNode(DUMMY_NODE, "event"); assertEquals("first child", "/diary[1]/event[1]", xpathNodeTracker.toXpathString()); xpathNodeTracker.visitedNode(DUMMY_NODE, "event"); assertEquals("2nd child", "/diary[1]/event[2]", xpathNodeTracker.toXpathString()); xpathNodeTracker.visitedNode(DUMMY_NODE, "event"); assertEquals("3rd child", "/diary[1]/event[3]", xpathNodeTracker.toXpathString()); xpathNodeTracker.visitedNode(DUMMY_NODE, "reminder"); assertEquals("4th child", "/diary[1]/reminder[1]", xpathNodeTracker.toXpathString()); xpathNodeTracker.visitedNode(DUMMY_NODE, "event"); assertEquals("5th child", "/diary[1]/event[4]", xpathNodeTracker.toXpathString()); } public void testTwoLevelsOfChildren() { xpathNodeTracker.visitedNode(DUMMY_NODE, "diary"); xpathNodeTracker.indent(); xpathNodeTracker.visitedNode(DUMMY_NODE, "event"); xpathNodeTracker.indent(); xpathNodeTracker.visitedNode(DUMMY_NODE, "details"); assertEquals("indented", "/diary[1]/event[1]/details[1]", xpathNodeTracker.toXpathString()); xpathNodeTracker.outdent(); xpathNodeTracker.visitedNode(DUMMY_NODE, "event"); assertEquals("outdented", "/diary[1]/event[2]", xpathNodeTracker.toXpathString()); xpathNodeTracker.indent(); xpathNodeTracker.visitedNode(DUMMY_NODE, "details"); assertEquals("re-indented", "/diary[1]/event[2]/details[1]", xpathNodeTracker.toXpathString()); xpathNodeTracker.outdent(); assertEquals("re-outdented", "/diary[1]/event[2]", xpathNodeTracker.toXpathString()); xpathNodeTracker.outdent(); assertEquals("outdented to root node", "/diary[1]", xpathNodeTracker.toXpathString()); } public void testNodes() throws Exception { Document doc = XMLUnit.newControlParser().newDocument(); Element element = doc.createElementNS("http://example.com/xmlunit", "eg:root"); xpathNodeTracker.visited(element); assertEquals("root element", "/root[1]", xpathNodeTracker.toXpathString()); Attr attr = doc.createAttributeNS("http://example.com/xmlunit", "eg:type"); attr.setValue("qwerty"); element.setAttributeNodeNS(attr); xpathNodeTracker.visited(attr); assertEquals("root element attribute", "/root[1]/@type", xpathNodeTracker.toXpathString()); xpathNodeTracker.indent(); Comment comment = doc.createComment("testing a comment"); xpathNodeTracker.visited(comment); assertEquals("comment", "/root[1]/comment()[1]", xpathNodeTracker.toXpathString()); ProcessingInstruction pi = doc.createProcessingInstruction("target","data"); xpathNodeTracker.visited(pi); assertEquals("p-i", "/root[1]/processing-instruction()[1]", xpathNodeTracker.toXpathString()); Text text = doc.createTextNode("some text"); xpathNodeTracker.visited(text); assertEquals("text", "/root[1]/text()[1]", xpathNodeTracker.toXpathString()); CDATASection cdata = doc.createCDATASection("some characters"); xpathNodeTracker.visited(cdata); assertEquals("cdata", "/root[1]/text()[2]", xpathNodeTracker.toXpathString()); } public void testRepeatNodesForTestTracker() throws Exception { Document doc = XMLUnit.newControlParser().newDocument(); final Element element = doc.createElement("repeated"); final Element copy = doc.createElement("repeated"); NodeList nodeList = new NodeList() { public Node item(int index) { switch(index) { case 0: return element; case 1: return copy; default: return null; } } public int getLength() { return 2; } }; xpathNodeTracker.preloadNodeList(nodeList); xpathNodeTracker.visited(element); assertEquals("root element", "/repeated[1]", xpathNodeTracker.toXpathString()); xpathNodeTracker.visited(element); assertEquals("visited root element again", "/repeated[1]", xpathNodeTracker.toXpathString()); xpathNodeTracker.visited(copy); assertEquals("visited copy of root element", "/repeated[2]", xpathNodeTracker.toXpathString()); } public void testRepeatNodesForControlTracker() throws Exception { Document doc = XMLUnit.newControlParser().newDocument(); Element element = doc.createElement("repeated"); xpathNodeTracker.visited(element); assertEquals("root element", "/repeated[1]", xpathNodeTracker.toXpathString()); xpathNodeTracker.visited(element); assertEquals("visited root element again", "/repeated[2]", xpathNodeTracker.toXpathString()); } // bug 1047364 public void testEmptyIndentOutdentRootNode() { xpathNodeTracker.indent(); xpathNodeTracker.outdent(); xpathNodeTracker.visitedNode(DUMMY_NODE, "diary"); assertEquals("root node", "/diary[1]", xpathNodeTracker.toXpathString()); } public void setUp() { xpathNodeTracker = new XpathNodeTracker(); xpathNodeTracker.reset(); } /** * Constructor for test_XpathNodeTracker. * @param name */ public test_XpathNodeTracker(String name) { super(name); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_Constants.java0000644000000000000000000000750212451007364024551 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Not actually a test container, but conforms to the semantics */ public class test_Constants extends TestCase implements XMLConstants, XSLTConstants { static { if (System.getProperty("basedir")==null) { System.setProperty("basedir", "d:/projects/sourceforge/xmlunit"); } } public static final String BASEDIR = System.getProperty("basedir"); public static final String LINE_SEPARATOR = System.getProperty("line.separator"); public static final String CHUCK_JONES_RIP_DTD = " \n" + " \n" + " \n"; public static final String CHUCK_JONES_RIP_DTD_DECL = ""; private static final String DOCUMENT_START = ""; private static final String DOCUMENT_END = ""; public static final String CHUCK_JONES_RIP_XML = DOCUMENT_START + "bugs bunny" + "roadrunnner" + DOCUMENT_END; public static final String EXTERNAL_DTD = BASEDIR + "/cartoons.dtd"; public static final String DOCUMENT_WITH_GOOD_EXTERNAL_DTD = "" + CHUCK_JONES_RIP_XML; public static final String DOCUMENT_WITH_NO_EXTERNAL_DTD = CHUCK_JONES_RIP_XML; public static final String CHUCK_JONES_SPINNING_IN_HIS_GRAVE_XML = DOCUMENT_START + "Yo ho ahoy" + DOCUMENT_END; public static final String XML_WITH_WHITESPACE = " text\ttext2 \n "; public static final String XML_WITHOUT_WHITESPACE = "texttext2"; public test_Constants(String name) { super(name); } /** * Return an empty test suite as this pseudo test class has no tests */ public static TestSuite suite() { return new TestSuite(); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_ElementNameAndTextQualifier.java0000644000000000000000000001061212451007364030115 0ustar rootroot/* ****************************************************************** Copyright (c) 2001, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import org.w3c.dom.Document; import org.w3c.dom.Element; import junit.framework.TestCase; import junit.framework.TestSuite; /** * JUnit testcase for ElementNameAndTextQualifier * @see test_Diff#testRepeatedElementNamesWithTextQualification() */ public class test_ElementNameAndTextQualifier extends TestCase { private static final String TAG_NAME = "tagYoureIt"; private static final String TEXT_A = "textA"; private static final String TEXT_B = "textB"; private Document document; private ElementNameAndTextQualifier elementNameAndTextQualifier; public void testSingleTextValue() throws Exception { Element control = document.createElement(TAG_NAME); control.appendChild(document.createTextNode(TEXT_A)); Element test = document.createElement(TAG_NAME); assertFalse("control text not comparable to empty text", elementNameAndTextQualifier.qualifyForComparison(control, test)); test.appendChild(document.createTextNode(TEXT_A)); assertTrue("control textA comparable to test textA", elementNameAndTextQualifier.qualifyForComparison(control, test)); test = document.createElement(TAG_NAME); test.appendChild(document.createTextNode(TEXT_B)); assertFalse("control textA not comparable to test textB", elementNameAndTextQualifier.qualifyForComparison(control, test)); } public void testMultipleTextValues() throws Exception { Element control = document.createElement(TAG_NAME); control.appendChild(document.createTextNode(TEXT_A)); control.appendChild(document.createTextNode(TEXT_B)); Element test = document.createElement(TAG_NAME); test.appendChild(document.createTextNode(TEXT_A + TEXT_B)); assertTrue("denormalised control text comparable to normalised test text", elementNameAndTextQualifier.qualifyForComparison(control, test)); } public void setUp() throws Exception { document = XMLUnit.newControlParser().newDocument(); elementNameAndTextQualifier = new ElementNameAndTextQualifier(); } /** * Constructor for test_ElementNameAndTextQualifier. */ public test_ElementNameAndTextQualifier() { super(); } /** * Constructor for test_ElementNameAndTextQualifier. * @param name */ public test_ElementNameAndTextQualifier(String name) { super(name); } public static TestSuite suite() { return new TestSuite(test_ElementNameAndTextQualifier.class); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_AbstractNodeTester.java0000644000000000000000000001330612451007364026334 0ustar rootroot/* ****************************************************************** Copyright (c) 2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import junit.framework.Assert; import junit.framework.TestCase; import org.w3c.dom.CDATASection; import org.w3c.dom.Comment; import org.w3c.dom.DocumentType; import org.w3c.dom.Element; import org.w3c.dom.Entity; import org.w3c.dom.EntityReference; import org.w3c.dom.Node; import org.w3c.dom.Notation; import org.w3c.dom.ProcessingInstruction; import org.w3c.dom.Text; /** * JUnit test for AbstractNodeTester */ public class test_AbstractNodeTester extends TestCase { public void testExactlyOncePerMethod() throws Exception { String testXml = "" + "" + "" + "" + "]>" + "" + "" + "" + "bar" + "&my;" + "" + ""; NodeTest nt = new NodeTest(testXml); ExactlyOncePerMethod tester = new ExactlyOncePerMethod(); nt.performTest(tester, new short[] { Node.ATTRIBUTE_NODE, Node.CDATA_SECTION_NODE, Node.COMMENT_NODE, Node.DOCUMENT_FRAGMENT_NODE, Node.DOCUMENT_NODE, Node.DOCUMENT_TYPE_NODE, Node.ELEMENT_NODE, Node.ENTITY_NODE, Node.ENTITY_REFERENCE_NODE, Node.NOTATION_NODE, Node.PROCESSING_INSTRUCTION_NODE, Node.TEXT_NODE, }); tester.verify(); } private class ExactlyOncePerMethod extends AbstractNodeTester { private boolean cdataCalled; private boolean commentCalled; private boolean elementCalled; private boolean piCalled; private boolean textCalled; private boolean noMoreNodesCalled; public void testCDATASection(CDATASection cdata) { Assert.assertFalse("testCDATASection called", cdataCalled); cdataCalled = true; Assert.assertEquals("baz", cdata.getNodeValue()); } public void testComment(Comment comment) { Assert.assertFalse("testComment called", commentCalled); commentCalled = true; Assert.assertEquals("comment", comment.getNodeValue()); } public void testElement(Element element) { Assert.assertFalse("testElement called", elementCalled); elementCalled = true; Assert.assertEquals("foo", element.getNodeName()); Assert.assertEquals("value", element.getAttribute("attr")); } public void testProcessingInstruction(ProcessingInstruction instr) { Assert.assertFalse("testProcessingInstruction called", piCalled); piCalled = true; Assert.assertEquals("target", instr.getTarget()); Assert.assertEquals("processing-instruction", instr.getData()); } public void testText(Text text) { Assert.assertFalse("testText called", textCalled); textCalled = true; Assert.assertEquals("barhello", text.getNodeValue()); } public void noMoreNodes(NodeTest t) { Assert.assertFalse("noMoreNodes called", noMoreNodesCalled); noMoreNodesCalled = true; } void verify() { Assert.assertTrue("testCDATASection not called", cdataCalled); Assert.assertTrue("testComment not called", commentCalled); Assert.assertTrue("testElement not called", elementCalled); Assert.assertTrue("testProcessingInstruction not called", piCalled); Assert.assertTrue("testText not called", textCalled); Assert.assertTrue("noMoreNodes not called", noMoreNodesCalled); } } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_ForumMessage4406472.java0000644000000000000000000001123612451007364025744 0ustar rootroot/* ****************************************************************** Copyright (c) 2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.util.Arrays; import junit.framework.TestCase; import org.w3c.dom.Node; /** * @see http://sf.net/forum/message.php?msg_id=4406472 */ public class test_ForumMessage4406472 extends TestCase { private static final String doc1 = "" + " String" + " String" + " String" + " String" + " 34.50" + ""; private static final String doc2 = "" + " Bla" + " Bla" + " Bla" + " Bla" + " 0.00" + ""; private class OriginalDifferenceListener implements DifferenceListener { private int[] IGNORE = new int[] { DifferenceConstants.ATTR_VALUE_ID, DifferenceConstants.ATTR_VALUE_EXPLICITLY_SPECIFIED_ID, DifferenceConstants.TEXT_VALUE_ID, DifferenceConstants.NAMESPACE_PREFIX_ID, DifferenceConstants.NAMESPACE_URI_ID }; public int differenceFound(Difference difference) { Arrays.sort(IGNORE); return Arrays.binarySearch(IGNORE, difference.getId()) >= 0 ? RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL : RETURN_ACCEPT_DIFFERENCE; } public void skippedComparison(Node control, Node test) { } } private class ModifiedDifferenceListener implements DifferenceListener { private int[] IGNORE = new int[] { DifferenceConstants.ATTR_VALUE_ID, DifferenceConstants.ATTR_VALUE_EXPLICITLY_SPECIFIED_ID, DifferenceConstants.TEXT_VALUE_ID, DifferenceConstants.NAMESPACE_PREFIX_ID, }; private ModifiedDifferenceListener() { Arrays.sort(IGNORE); } public int differenceFound(Difference difference) { return Arrays.binarySearch(IGNORE, difference.getId()) >= 0 ? RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL : difference.isRecoverable() ? RETURN_IGNORE_DIFFERENCE_NODES_SIMILAR : RETURN_ACCEPT_DIFFERENCE; } public void skippedComparison(Node control, Node test) { } } public void testOriginal() throws Exception { Diff d = new Diff(doc1, doc2); d.overrideDifferenceListener(new OriginalDifferenceListener()); assertTrue(d.toString(), d.similar()); } public void testModified() throws Exception { Diff d = new Diff(doc1, doc2); d.overrideDifferenceListener(new ModifiedDifferenceListener()); assertTrue(d.toString(), d.similar()); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_JAXP_1_2_Schema_Validation.java0000644000000000000000000001573212451007364027436 0ustar rootroot/* ****************************************************************** Copyright (c) 200, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import org.xml.sax.InputSource; import junit.framework.TestCase; public class test_JAXP_1_2_Schema_Validation extends TestCase { private Validator validator; public void testUsingStringURI() throws Exception { File xsdFile = new File(test_Constants.BASEDIR + "/tests/etc/Book.xsd"); assertTrue("xsdFile " + xsdFile.getAbsolutePath() + " exists", xsdFile.exists()); File xmlFile = new File(test_Constants.BASEDIR + "/tests/etc/BookXsdGeneratedNoSchema.xml"); assertTrue("xmlFile " + xmlFile.getAbsolutePath() + " exists", xmlFile.exists()); validator = new Validator(new FileReader(xmlFile)); validator.useXMLSchema(true); validator.setJAXP12SchemaSource(xsdFile.getAbsolutePath()); validator.assertIsValid(); } public void testUsingInputStream() throws Exception { File xsdFile = new File(test_Constants.BASEDIR + "/tests/etc/Book.xsd"); assertTrue("xsdFile " + xsdFile.getAbsolutePath() + " exists", xsdFile.exists()); File xmlFile = new File(test_Constants.BASEDIR + "/tests/etc/BookXsdGeneratedNoSchema.xml"); assertTrue("xmlFile " + xmlFile.getAbsolutePath() + " exists", xmlFile.exists()); validator = new Validator(new FileReader(xmlFile)); validator.useXMLSchema(true); validator.setJAXP12SchemaSource(new FileInputStream(xsdFile)); validator.assertIsValid(); } public void testUsingInputSource() throws Exception { File xsdFile = new File(test_Constants.BASEDIR + "/tests/etc/Book.xsd"); assertTrue("xsdFile " + xsdFile.getAbsolutePath() + " exists", xsdFile.exists()); File xmlFile = new File(test_Constants.BASEDIR + "/tests/etc/BookXsdGeneratedNoSchema.xml"); assertTrue("xmlFile " + xmlFile.getAbsolutePath() + " exists", xmlFile.exists()); validator = new Validator(new FileReader(xmlFile)); validator.useXMLSchema(true); validator .setJAXP12SchemaSource(new InputSource(new FileReader(xsdFile))); validator.assertIsValid(); } public void testUsingAFile() throws Exception { File xsdFile = new File(test_Constants.BASEDIR + "/tests/etc/Book.xsd"); assertTrue("xsdFile " + xsdFile.getAbsolutePath() + " exists", xsdFile.exists()); File xmlFile = new File(test_Constants.BASEDIR + "/tests/etc/BookXsdGeneratedNoSchema.xml"); assertTrue("xmlFile " + xmlFile.getAbsolutePath() + " exists", xmlFile.exists()); validator = new Validator(new FileReader(xmlFile)); validator.useXMLSchema(true); validator.setJAXP12SchemaSource(xsdFile); validator.assertIsValid(); } public void testUsingObjectArrayContainingStringURI() throws Exception { File xsdFile = new File(test_Constants.BASEDIR + "/tests/etc/Book.xsd"); assertTrue("xsdFile " + xsdFile.getAbsolutePath() + " exists", xsdFile.exists()); File xmlFile = new File(test_Constants.BASEDIR + "/tests/etc/BookXsdGeneratedNoSchema.xml"); assertTrue("xmlFile " + xmlFile.getAbsolutePath() + " exists", xmlFile.exists()); validator = new Validator(new FileReader(xmlFile)); validator.useXMLSchema(true); validator.setJAXP12SchemaSource(new Object[] { xsdFile.getAbsolutePath() }); validator.assertIsValid(); } public void testUsingNonExistentFile() throws Exception { File xsdFile = new File(test_Constants.BASEDIR + "/tests/etc/BookDoesNotExist.xsd"); assertFalse("xsdFile " + xsdFile.getAbsolutePath() + " exists", xsdFile.exists()); File xmlFile = new File(test_Constants.BASEDIR + "/tests/etc/BookXsdGeneratedNoSchema.xml"); assertTrue("xmlFile " + xmlFile.getAbsolutePath() + " exists", xmlFile.exists()); validator = new Validator(new FileReader(xmlFile)); validator.useXMLSchema(true); validator.setJAXP12SchemaSource(xsdFile); assertFalse("Isn't valid since no schema can be found", validator.isValid()); } public void testUsingInvalidXML() throws Exception { File xsdFile = new File(test_Constants.BASEDIR + "/tests/etc/Book.xsd"); assertTrue("xsdFile " + xsdFile.getAbsolutePath() + " exists", xsdFile.exists()); File xmlFile = new File(test_Constants.BASEDIR + "/tests/etc/InvalidBookXsdGeneratedNoSchema.xml"); assertTrue("xmlFile " + xmlFile.getAbsolutePath() + " exists", xmlFile.exists()); validator = new Validator(new FileReader(xmlFile)); validator.useXMLSchema(true); validator.setJAXP12SchemaSource(xsdFile); assertFalse("Isn't valid since no schema can be found", validator.isValid()); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_QualifiedName.java0000644000000000000000000001033612451007364025300 0ustar rootroot/* ***************************************************************** Copyright (c) 2014 Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.util.HashMap; import java.util.Map; import junit.framework.Assert; import junit.framework.TestCase; public class test_QualifiedName extends TestCase { public void testParseBareLocalName() { Assert.assertEquals(new QualifiedName("foo"), QualifiedName.valueOf("foo")); } public void testParseQNameToStringStyle() { Assert.assertEquals(new QualifiedName("foo", "bar"), QualifiedName.valueOf("{foo}bar")); } public void testParsePrefixStyle() { Map m = new HashMap(); m.put("pre", "foo"); NamespaceContext ctx = new SimpleNamespaceContext(m); Assert.assertEquals(new QualifiedName("foo", "bar"), QualifiedName.valueOf("pre:bar", ctx)); } public void testParsePrefixStyleImplicitContext() { Map m = new HashMap(); m.put("pre", "foo"); XMLUnit.setXpathNamespaceContext(new SimpleNamespaceContext(m)); try { Assert.assertEquals(new QualifiedName("foo", "bar"), QualifiedName.valueOf("pre:bar")); } finally { XMLUnit.setXpathNamespaceContext(null); } } public void testValueMustNotBeNull() { shouldThrowWithMessage(null, "null"); } public void testLocalPartMustNotBeEmptyQNameStyle() { shouldThrowWithMessage("{foo}", "must not be empty"); } public void testLocalPartMustNotBeEmptyPrefixStyle() { shouldThrowWithMessage("foo:", "must not be empty"); } public void testPrefixStyleRequiresNamespaceContext() { shouldThrowWithMessage("foo:bar", "without a NamespaceContext"); } public void testPrefixMustBeKnownToContext() { Map m = new HashMap(); XMLUnit.setXpathNamespaceContext(new SimpleNamespaceContext(m)); try { shouldThrowWithMessage("foo:bar", "foo is unknown"); } finally { XMLUnit.setXpathNamespaceContext(null); } } private void shouldThrowWithMessage(String valueToParse, String messagePart) { try { QualifiedName.valueOf(valueToParse); Assert.fail("expected IllegalArgumentException"); } catch (IllegalArgumentException ex) { String msg = ex.getMessage(); Assert.assertTrue("exception message should contain '" + messagePart + "' but was " + msg, msg.indexOf(messagePart) >= 0); } } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_NodeTest.java0000644000000000000000000001037512451007364024324 0ustar rootroot/* ****************************************************************** Copyright (c) 200, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.io.StringReader; import junit.framework.TestCase; import junit.framework.TestSuite; import org.w3c.dom.Node; /** * JUnit test for NodeTest */ public class test_NodeTest extends TestCase { private NodeTest nodeTest; private class NodeTypeTester implements NodeTester { private short type; public NodeTypeTester(short type) { this.type = type; } public void testNode(Node aNode, NodeTest forTest) { assertEquals(type, aNode.getNodeType()); } public void noMoreNodes(NodeTest forTest) { } } private class RejectingNodeTester implements NodeTester { public boolean completed; public void testNode(Node aNode, NodeTest forTest) throws NodeTestException { throw new NodeTestException("Reject all nodes", aNode); } public void noMoreNodes(NodeTest forTest) throws NodeTestException { completed = true; throw new NodeTestException("Rejection"); } } public void testFiltering() throws Exception { nodeTest = new NodeTest( new StringReader("folks")); short nodeType = Node.ELEMENT_NODE; nodeTest.performTest(new NodeTypeTester(nodeType), nodeType); nodeType = Node.TEXT_NODE; nodeTest.performTest(new NodeTypeTester(nodeType), nodeType); nodeType = Node.COMMENT_NODE; nodeTest.performTest(new NodeTypeTester(nodeType), nodeType); short[] nodeTypes = new short[] {Node.TEXT_NODE, Node.COMMENT_NODE}; nodeTest.performTest(new NodeTypeTester(Node.TEXT_NODE), nodeTypes); } public void testNodeTesting() throws Exception { nodeTest = new NodeTest( new StringReader("standard")); RejectingNodeTester tester = new RejectingNodeTester(); try { nodeTest.performTest(tester, Node.TEXT_NODE); fail("Expected NodeTestException"); } catch (NodeTestException e) { assertEquals("not completed", false, tester.completed); } try { nodeTest.performTest(tester, Node.CDATA_SECTION_NODE); fail("Expected NodeTestException"); } catch (NodeTestException e) { assertEquals("completed", true, tester.completed); } } public test_NodeTest(String name) { super(name); } public static TestSuite suite() { return new TestSuite(test_NodeTest.class); } } xmlunit-1.6/tests/java/org/custommonkey/xmlunit/test_Transform.java0000644000000000000000000001511112451007364024543 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.io.File; import java.io.FileReader; import org.custommonkey.xmlunit.exceptions.ConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Source; import javax.xml.transform.URIResolver; import junit.framework.TestCase; import junit.framework.TestSuite; import org.w3c.dom.Document; /** * Test a Transform */ public class test_Transform extends TestCase{ private static final String FLEABALL = "dog"; private static final String DOG = "" ; private Transform transform; private File animal; public void testGetResultString() throws Exception { transform = new Transform(FLEABALL, animal); assertEquals(DOG, stripLineFeeds(transform.getResultString())); } public void testGetResultDocument() throws Exception { transform = new Transform(FLEABALL, animal); Diff diff = new Diff(DOG, transform); assertEquals(diff.toString(), true, diff.identical()); } public void testIdentityTransform() throws Exception { Document control = XMLUnit.buildControlDocument(FLEABALL); transform = new Transform(control); Document test = transform.getResultDocument(); Diff diff = new Diff(control, test); assertEquals(diff.toString(), true, diff.identical()); } public void testOutputProperty() throws Exception { transform = new Transform(FLEABALL, animal); transform.setOutputProperty(OutputKeys.METHOD, "html"); assertNotEquals(DOG, transform.getResultString()); } public void testDOMSourceAndFile() throws Exception { transform = new Transform(XMLUnit.buildControlDocument(FLEABALL), animal); assertEquals(DOG, stripLineFeeds(transform.getResultString())); } public void testDOMSourceAndString() throws Exception { FileReader reader = new FileReader(animal); try { char[] animalXSL = new char[1024]; int length = reader.read(animalXSL); transform = new Transform(XMLUnit.buildControlDocument(FLEABALL), new String(animalXSL, 0, length)); assertEquals(DOG, stripLineFeeds(transform.getResultString())); } finally { reader.close(); } } /** * Raised by Craig Strong 04.04.2002 */ public void testXSLIncludeWithoutSystemId() throws Exception { String input = "creepycrawly"; String xslWithInclude = test_Constants.XML_DECLARATION + test_Constants.XSLT_START + test_Constants.XSLT_XML_OUTPUT_NOINDENT + "" + "" + test_Constants.XSLT_END; Transform transform = new Transform(input, xslWithInclude); transform.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); assertEquals("", transform.getResultString()); } /** * Issue 1742826 */ public void testURIResolverForStylesheet() throws Exception { TestResolver tr = new TestResolver(); try { XMLUnit.setURIResolver(tr); String s = ""; String xsl = test_Constants.XML_DECLARATION + test_Constants.XSLT_START + "" + test_Constants.XSLT_END; try { Transform transform = new Transform(s, xsl); fail("should fail because of unknown include URI"); } catch (ConfigurationException tce) { // expected exception } assertTrue("URIResolver has been called", tr.called); } finally { XMLUnit.setURIResolver(null); } } private void assertNotEquals(Object expected, Object actual) { if (expected.equals(actual)) { fail("Expected " + expected + " different to actual!"); } } public test_Transform(String name) { super(name); } public void setUp() throws Exception { animal = new File(test_Constants.BASEDIR + "/tests/etc/animal.xsl"); } private static String stripLineFeeds(String s) { int index = s.indexOf(test_Constants.LINE_SEPARATOR); while (index > -1) { s = s.substring(0, index) + s.substring(index + test_Constants.LINE_SEPARATOR.length()) ; index = s.indexOf(test_Constants.LINE_SEPARATOR); } return s; } private static class TestResolver implements URIResolver { private boolean called = false; public Source resolve(String h, String b) { called = true; return null; } } } xmlunit-1.6/tests/etc/0000755000000000000000000000000012451007364013475 5ustar rootrootxmlunit-1.6/tests/etc/broken.xsd0000644000000000000000000000124412451007364015476 0ustar rootroot xmlunit-1.6/tests/etc/controlNamespaces.xml0000644000000000000000000000063512451007364017703 0ustar rootroot jUnit eclipse ant xalan xerces xmlunit-1.6/tests/etc/InvalidBookXsdGeneratedNoSchema.xml0000644000000000000000000000060312451007364022333 0ustar rootroot Chicken Soup for the Soul Jack Canfield Mark Victor Hansen 1993 1-55874-262-X Billy Health Communications, Inc. xmlunit-1.6/tests/etc/animal.xsl0000644000000000000000000000053712451007364015473 0ustar rootroot xmlunit-1.6/tests/etc/Book.rngc0000644000000000000000000000032712451007364015244 0ustar rootrootnamespace b = "http://www.publishing.org" element b:Book { element b:Title { text }, element b:Author { text }+, element b:Date { text }, element b:ISBN { text }, element b:Publisher { text } } xmlunit-1.6/tests/etc/controlDetail.xml0000644000000000000000000005202312451007364017024 0ustar rootroot xmlunit-1.6/tests/etc/BookXsdGeneratedNoSchema.xml0000644000000000000000000000055412451007364021031 0ustar rootroot Chicken Soup for the Soul Jack Canfield Mark Victor Hansen 1993 1-55874-262-X Health Communications, Inc. xmlunit-1.6/tests/etc/BookXsdGeneratedWithFixedSchemaLocation.xml0000644000000000000000000000073212451007364024037 0ustar rootroot Chicken Soup for the Soul Jack Canfield Mark Victor Hansen 1993 1-55874-262-X Health Communications, Inc. xmlunit-1.6/tests/etc/testNamespaces.xml0000644000000000000000000000070112451007364017174 0ustar rootroot jUnit eclipse ant xalan xerces xmlunit-1.6/tests/etc/testAnimal.xml0000644000000000000000000000020212451007364016312 0ustar rootroot dog xmlunit-1.6/tests/etc/test2.xml0000644000000000000000000000021612451007364015257 0ustar rootroot caterpillar xmlunit-1.6/tests/etc/test.blame.html0000644000000000000000000000025512451007364016423 0ustar rootroot Don't blame it on the...
    • sunshine
    • moonlight
    • good times
    • boogie...?
    xmlunit-1.6/tests/etc/invalidBook.xml0000644000000000000000000000066512451007364016467 0ustar rootroot Chicken Soup for the Soul Jack Canfield Mark Victor Hansen 1993 1-55874-262-X xmlunit-1.6/tests/etc/test1.xml0000644000000000000000000000021212451007364015252 0ustar rootroot rabbit xmlunit-1.6/tests/etc/Book.xsd0000644000000000000000000000150612451007364015111 0ustar rootroot xmlunit-1.6/tests/etc/Book.xsd20000644000000000000000000000146512451007364015177 0ustar rootroot xmlunit-1.6/tests/etc/BookXsdGenerated.xml0000644000000000000000000000074412451007364017414 0ustar rootroot Chicken Soup for the Soul Jack Canfield Mark Victor Hansen 1993 1-55874-262-X Health Communications, Inc. xmlunit-1.6/tests/etc/Book.rng0000644000000000000000000000057712451007364015110 0ustar rootroot xmlunit-1.6/tests/etc/test.dtd0000644000000000000000000000000012451007364015137 0ustar rootrootxmlunit-1.6/tests/etc/testDetail.xml0000644000000000000000000005314412451007364016330 0ustar rootroot xmlunit-1.6/src/0000755000000000000000000000000012451007364012347 5ustar rootrootxmlunit-1.6/src/user-guide/0000755000000000000000000000000012451007666014425 5ustar rootrootxmlunit-1.6/src/user-guide/org/0000755000000000000000000000000012451007364015207 5ustar rootrootxmlunit-1.6/src/user-guide/org/custommonkey/0000755000000000000000000000000012451007364017744 5ustar rootrootxmlunit-1.6/src/user-guide/org/custommonkey/xmlunit/0000755000000000000000000000000012451007364021444 5ustar rootrootxmlunit-1.6/src/user-guide/org/custommonkey/xmlunit/examples/0000755000000000000000000000000012451007364023262 5ustar rootrootxmlunit-1.6/src/user-guide/org/custommonkey/xmlunit/examples/ATourOfXMLUnit.java0000644000000000000000000002654512451007364026701 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.examples; import java.io.File; import java.io.FileReader; import java.util.List; import javax.xml.transform.stream.StreamSource; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.Text; import org.custommonkey.xmlunit.*; /** * All code snippets from the "A Tour of XMLUnit" section of the the * User Guide. */ public class ATourOfXMLUnit extends XMLTestCase { public ATourOfXMLUnit(String name) { super(name); } // never invoked private void configure() { System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl"); System.setProperty("javax.xml.parsers.SAXParserFactory", "org.apache.xerces.jaxp.SAXParserFactoryImpl"); System.setProperty("javax.xml.transform.TransformerFactory", "org.apache.xalan.processor.TransformerFactoryImpl"); XMLUnit .setControlParser("org.apache.xerces.jaxp.DocumentBuilderFactoryImpl"); XMLUnit .setTestParser("org.apache.xerces.jaxp.DocumentBuilderFactoryImpl"); XMLUnit .setSAXParserFactory("org.apache.xerces.jaxp.SAXParserFactoryImpl"); XMLUnit .setTransformerFactory("org.apache.xalan.processor.TransformerFactoryImpl"); } public void testForEquality() throws Exception { String myControlXML = "0x00435A8C"; String myTestXML = "2376"; assertXMLEqual("Comparing test xml to control xml", myControlXML, myTestXML); } public void testXMLIdentical()throws Exception { String myControlXML = "3false"; String myTestXML = "false3"; Diff myDiff = new Diff(myControlXML, myTestXML); assertTrue("XML similar " + myDiff.toString(), myDiff.similar()); assertTrue("XML identical " + myDiff.toString(), myDiff.identical()); } public void testAllDifferences() throws Exception { String myControlXML = "War" + "Plague" + "Famine"; String myTestXML = "Peace" + "Health" + "Plenty"; DetailedDiff myDiff = new DetailedDiff(new Diff(myControlXML, myTestXML)); List allDifferences = myDiff.getAllDifferences(); assertEquals(myDiff.toString(), 2, allDifferences.size()); } public void testCompareToSkeletonXML() throws Exception { String myControlXML = "22 any streetXY00 99Z"; String myTestXML = "20 east cheapEC3M 1EB"; DifferenceListener myDifferenceListener = new IgnoreTextAndAttributeValuesDifferenceListener(); Diff myDiff = new Diff(myControlXML, myTestXML); myDiff.overrideDifferenceListener(myDifferenceListener); assertTrue("test XML matches control skeleton XML", myDiff.similar()); } public void testRepeatedChildElements() throws Exception { String myControlXML = "" + "FirstTestCase" + "SecondTestCase"; String myTestXML = "" + "SecondTestCase" + "FirstTestCase"; assertXMLNotEqual("Repeated child elements in different sequence order are not equal by default", myControlXML, myTestXML); Diff myDiff = new Diff(myControlXML, myTestXML); myDiff.overrideElementQualifier(new ElementNameAndTextQualifier()); assertXMLEqual("But they are equal when an ElementQualifier controls which test element is compared with each control element", myDiff, true); } public void testXSLTransformation() throws Exception { String myInputXML = "..."; File myStylesheetFile = new File("..."); Transform myTransform = new Transform(myInputXML, myStylesheetFile); String myExpectedOutputXML = "..."; Diff myDiff = new Diff(myExpectedOutputXML, myTransform); assertTrue("XSL transformation worked as expected", myDiff.similar()); } public void testAnotherXSLTransformation() throws Exception { File myInputXMLFile = new File("..."); File myStylesheetFile = new File("..."); Transform myTransform = new Transform( new StreamSource(myInputXMLFile), new StreamSource(myStylesheetFile)); Document myExpectedOutputXML = XMLUnit.buildDocument(XMLUnit.getControlParser(), new FileReader("...")); Diff myDiff = new Diff(myExpectedOutputXML, myTransform.getResultDocument()); assertTrue("XSL transformation worked as expected", myDiff.similar()); } public void testValidation() throws Exception { XMLUnit.getTestDocumentBuilderFactory().setValidating(true); // As the document is parsed it is validated against its referenced DTD Document myTestDocument = XMLUnit.buildTestDocument("..."); String mySystemId = "..."; String myDTDUrl = new File("...").toURL().toExternalForm(); Validator myValidator = new Validator(myTestDocument, mySystemId, myDTDUrl); assertTrue("test document validates against unreferenced DTD", myValidator.isValid()); } public void testXPaths() throws Exception { String mySolarSystemXML = "" + "" + ""; assertXpathExists("//planet[@name='Earth']", mySolarSystemXML); assertXpathNotExists("//star[@name='alpha centauri']", mySolarSystemXML); assertXpathsEqual("//planet[@name='Earth']", "//planet[@position='3']", mySolarSystemXML); assertXpathsNotEqual("//planet[@name='Venus']", "//planet[@supportsLife='yes']", mySolarSystemXML); } public void testXPathValues() throws Exception { String myJavaFlavours = "" + "1.1.x" + "1.2.x" + "1.3.x" + "1.4.x"; assertXpathEvaluatesTo("2", "count(//jvm[@current='yes'])", myJavaFlavours); assertXpathValuesEqual("//jvm[4]/@latest", "//jvm[4]/@current", myJavaFlavours); assertXpathValuesNotEqual("//jvm[2]/@current", "//jvm[3]/@current", myJavaFlavours); } public void testXpathsInHTML() throws Exception { String someBadlyFormedHTML = "Ugh" + "

    Heading
      " + "
    • Item One
    • Item Two"; TolerantSaxDocumentBuilder tolerantSaxDocumentBuilder = new TolerantSaxDocumentBuilder(XMLUnit.getTestParser()); HTMLDocumentBuilder htmlDocumentBuilder = new HTMLDocumentBuilder(tolerantSaxDocumentBuilder); Document wellFormedDocument = htmlDocumentBuilder.parse(someBadlyFormedHTML); assertXpathEvaluatesTo("Item One", "/html/body//li[@id='1']", wellFormedDocument); } public void testCountingNodeTester() throws Exception { String testXML = "123" + "59"; CountingNodeTester countingNodeTester = new CountingNodeTester(4); assertNodeTestPasses(testXML, countingNodeTester, Node.TEXT_NODE); } public void testCustomNodeTester() throws Exception { String testXML = "123" + "59"; NodeTest nodeTest = new NodeTest(testXML); assertNodeTestPasses(nodeTest, new FibonacciNodeTester(), new short[] {Node.TEXT_NODE, Node.ELEMENT_NODE}, true); } private class FibonacciNodeTester extends AbstractNodeTester { private int nextVal = 1, lastVal = 1, priorVal = 0; public void testText(Text text) throws NodeTestException { int val = Integer.parseInt(text.getData()); if (nextVal != val) { throw new NodeTestException("Incorrect value", text); } nextVal = val + lastVal; priorVal = lastVal; lastVal = val; } public void testElement(Element element) throws NodeTestException { String name = element.getLocalName(); if ("fibonacci".equals(name) || "val".equals(name)) { return; } throw new NodeTestException("Unexpected element", element); } public void noMoreNodes(NodeTest nodeTest) throws NodeTestException { } } } xmlunit-1.6/src/user-guide/org/custommonkey/xmlunit/examples/ComparingPiecesOfXML.java0000644000000000000000000001147712451007364030055 0ustar rootroot/* ****************************************************************** Copyright (c) 2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.examples; import java.util.Arrays; import java.util.List; import org.custommonkey.xmlunit.*; import org.w3c.dom.*; /** * Code from "Comparing Pieces of XML" section of User's Guide */ public class ComparingPiecesOfXML extends XMLTestCase { class MyDifferenceListener implements DifferenceListener { private boolean calledFlag = false; public boolean called() { return calledFlag; } public int differenceFound(Difference difference) { calledFlag = true; return RETURN_ACCEPT_DIFFERENCE; } public void skippedComparison(Node control, Node test) { } } private void usingDifferenceEngineDirectly() { ComparisonController myComparisonController = null; Node controlNode = null; Node testNode = null; ElementQualifier myElementQualifier = null; DifferenceEngine engine = new DifferenceEngine(myComparisonController); MyDifferenceListener listener = new MyDifferenceListener(); engine.compare(controlNode, testNode, listener, myElementQualifier); System.err.println("There have been " + (listener.called() ? "" : "no ") + "differences."); } public class HaltOnNonRecoverable implements ComparisonController { public boolean haltComparison(Difference afterDifference) { return !afterDifference.isRecoverable(); } } public static class IgnoreDoctype implements DifferenceListener { private static final int[] IGNORE = new int[] { DifferenceConstants.HAS_DOCTYPE_DECLARATION_ID, DifferenceConstants.DOCTYPE_NAME_ID, DifferenceConstants.DOCTYPE_PUBLIC_ID_ID, DifferenceConstants.DOCTYPE_SYSTEM_ID_ID }; static { Arrays.sort(IGNORE); } public int differenceFound(Difference difference) { return Arrays.binarySearch(IGNORE, difference.getId()) >= 0 ? RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL : RETURN_ACCEPT_DIFFERENCE; } public void skippedComparison(Node control, Node test) { } } private void comparingTwoPiecesOfXMLUsingDiff() throws Exception { Diff d = new Diff("", ""); assertFalse(d.identical()); // CHILD_NODELIST_SEQUENCE Difference assertTrue(d.similar()); } private void FindingAllDifferencesUsingDetailedDiff() throws Exception { Diff d = new Diff("", ""); DetailedDiff dd = new DetailedDiff(d); dd.overrideElementQualifier(null); assertFalse(dd.similar()); List l = dd.getAllDifferences(); assertEquals(2, l.size()); // expexted but was and vice versa } private void junit3() throws Exception { String CONTROL = null; String TEST = null; Diff d = new Diff(CONTROL, TEST); assertTrue("expected pieces to be similar, " + d.toString(), d.similar()); assertXMLEqual("expected pieces to be similar", CONTROL, TEST); } } xmlunit-1.6/src/user-guide/org/custommonkey/xmlunit/examples/ValidatingXMLDocuments.java0000644000000000000000000000740112451007364030454 0ustar rootroot/* ****************************************************************** Copyright (c) 2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.examples; import java.io.*; import org.custommonkey.xmlunit.*; import org.xml.sax.*; /** * Code from "Validating XML Documents" section of User's Guide */ public class ValidatingXMLDocuments { private String myXmlDocument = null; private String myDTD = null; private void ValidatingAgainstTheDTDDefinedInDOCTYPE() throws Exception { InputSource is = new InputSource(new FileInputStream(myXmlDocument)); Validator v = new Validator(is); boolean isValid = v.isValid(); } private void ValidatingAPieceOfXMLThatDoesntContainADOCTYPE() throws Exception { String myPublicId = null; InputSource is = new InputSource(new FileInputStream(myXmlDocument)); Validator v = new Validator(is, (new File(myDTD)).toURI().toURL().toString(), myPublicId); boolean isValid = v.isValid(); } private void ValidatingAgainstALocalDTD() throws Exception { InputSource is = new InputSource(new FileInputStream(myXmlDocument)); Validator v = new Validator(is, (new File(myDTD)).toURI().toURL().toString()); boolean isValid = v.isValid(); } private void ValidatingAgainstDTDUsingApachesXMLResolverAndAXMLCatalog() throws Exception { InputSource is = new InputSource(new FileInputStream(myXmlDocument)); XMLUnit.setControlEntityResolver(new CatalogResolver()); Validator v = new Validator(is); boolean isValid = v.isValid(); } private void ValidatingAgainstALocalXMLSchema() throws Exception { String myXmlSchemaFile = null; InputSource is = new InputSource(new FileInputStream(myXmlDocument)); Validator v = new Validator(is); v.useXMLSchema(true); v.setJAXP12SchemaSource(new File(myXmlSchemaFile)); boolean isValid = v.isValid(); } private static class CatalogResolver implements EntityResolver { public InputSource resolveEntity(String p, String s) { return null; } } } xmlunit-1.6/src/user-guide/org/custommonkey/xmlunit/examples/DOMTreeWalking.java0000644000000000000000000000566612451007364026716 0ustar rootroot/* ****************************************************************** Copyright (c) 2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.examples; import junit.framework.TestCase; import org.custommonkey.xmlunit.*; import org.w3c.dom.*; /** * Code from "DOM Tree Walking" section of User's Guide */ public class DOMTreeWalking extends TestCase { private String myXML = null; static final String ATTRIBUTE_NAME = null; private void AccessingAttributesInANodeTest() throws Exception { NodeTest nt = new NodeTest(myXML); NodeTester tester = new MyNodeTester(); nt.performTest(tester, Node.ELEMENT_NODE); } private void AccessingAttributesInANodeTestAbstractNodeTesterVersion() throws Exception { NodeTest nt = new NodeTest(myXML); NodeTester tester = new AbstractNodeTester() { public void testElement(Element element) throws NodeTestException { Attr attributeToTest = element.getAttributeNode(ATTRIBUTE_NAME); } }; nt.performTest(tester, Node.ELEMENT_NODE); } static class MyNodeTester implements NodeTester { public void testNode(Node aNode, NodeTest test) { Element anElement = (Element) aNode; Attr attributeToTest = anElement.getAttributeNode(ATTRIBUTE_NAME); } public void noMoreNodes(NodeTest test) {} } } xmlunit-1.6/src/user-guide/org/custommonkey/xmlunit/examples/XPathTests.java0000644000000000000000000000572712451007364026207 0ustar rootroot/* ****************************************************************** Copyright (c) 2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.examples; import java.util.HashMap; import junit.framework.Assert; import junit.framework.TestCase; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.custommonkey.xmlunit.*; /** * Code from "XPath Tests" section of User's Guide */ public class XPathTests extends TestCase { private void MatchingAnXPathSelectionAgainstARegularExpression() throws Exception { Document doc = null; String regex = null; String xpath = null; String message = null; XpathEngine engine = XMLUnit.newXpathEngine(); String value = engine.evaluate(xpath, doc); Assert.assertTrue(message, value.matches(regex)); } private void UsingNamespacesInXPathTests() throws Exception { String testDoc = ""; Document d = XMLUnit.buildControlDocument(testDoc); HashMap m = new HashMap(); m.put("foo", "urn:foo"); NamespaceContext ctx = new SimpleNamespaceContext(m); XpathEngine engine = XMLUnit.newXpathEngine(); engine.setNamespaceContext(ctx); NodeList l = engine.getMatchingNodes("//foo:bar", d); assertEquals(1, l.getLength()); assertEquals(Node.ELEMENT_NODE, l.item(0).getNodeType()); } } xmlunit-1.6/src/user-guide/XMLUnit-Java.xml0000644000000000000000000051473712451007364017342 0ustar rootroot
      XMLUnit Java User's Guide Tim Bacon Stefan Bodewig 1.0 January 2003 Tim Bacon Documentation for XMLUnit Java 1.0 1.1 April 2007 Documentation for XMLUnit Java 1.1 1.2 June 2008 Documentation for XMLUnit Java 1.2 1.3 September 2009 Documentation for XMLUnit Java 1.3 1.4 February 2013 Documentation for XMLUnit Java 1.4 1.5 September 2013 Documentation for XMLUnit Java 1.5 1.6 December 2014 Documentation for XMLUnit Java 1.6
      A Tour of XMLUnit This first section contains a tour through XMLUnit's features, the next sections will cover them in more detail. Note that it has a strong focus on using the XMLTestCase class which is one option to use XMLUnit, but not the only one. XMLUnit's features can be fully used without any dependency on JUnit at all.
      What is XMLUnit? XMLUnit enables JUnit-style assertions to be made about the content and structure of XMLFor more information on JUnit see http://www.junit.org. It is an open source project hosted at http://xmlunit.sourceforge.net/ that grew out of a need to test a system that generated and received custom XML messages. The problem that we faced was how to verify that the system generated the correct message from a known set of inputs. Obviously we could use a DTD or a schema to validate the message output, but this approach wouldn't allow us to distinguish between valid XML with correct content (e.g. element bar]]>) and valid XML with incorrect content (e.g. element baz]]>). What we really wanted was an assertXMLEqual() method, so we could compare the message that we expected the system to generate and the message that the system actually generated. And that was the beginning of XMLUnit.
      Quick tour XMLUnit provides a single JUnit extension class, XMLTestCase, and a set of supporting classes that allow assertions to be made about: The differences between two pieces of XML (via Diff and DetailedDiff classes) The validity of a piece of XML (via Validator class) The outcome of transforming a piece of XML using XSLT (via Transform class) The evaluation of an XPath expression on a piece of XML (via classes implementing the XpathEngine interface) Individual nodes in a piece of XML that are exposed by DOM Traversal (via NodeTest class) XMLUnit can also treat HTML content, even badly-formed HTML, as valid XML to allow these assertions to be made about web pages (via the HTMLDocumentBuilder class).
      Glossary As with many projects some words in XMLUnit have particular meanings so here is a quick overview. A piece of XML is a DOM Document, a String containing marked-up content, or a Source or Reader that allows access to marked-up content within some resource. XMLUnit compares the expected control XML to some actual test XML. The comparison can reveal that two pieces of XML are identical, similar or different. The unit of measurement used by the comparison is a difference, and differences can be either recoverable or unrecoverable. Two pieces of XML are identical if there are no differences between them, similar if there are only recoverable differences between them, and different if there are any unrecoverable differences between them.
      Configuring XMLUnit There are many Java XML parsers available, and XMLUnit should work with any JAXP compliant parser library, such as Xerces-J http://xerces.apache.org/ from the Apache Software Foundation. To use the XSLT and XPath features of XMLUnit a Trax (the XSLT portion of JAXP) compliant transformation engine is required, such as Xalan-Jhttp://xalan.apache.org/, from the Apache Software Foundation. To configure XMLUnit to use a specific parser and transformation engine set three System properties before any tests are run, e.g. Configuring JAXP via System Properties You may want to read for more details - in particular if you are using Java 1.4 or later. Alternatively there are static methods on the XMLUnit class that can be called directly. The advantage of this approach is that you can specify a different parser class for control and test XML and change the current parser class at any time in your tests, should you need to make assertions about the compatibility of different parsers. Configuring JAXP via XMLUnit class
      Writing XML comparison tests Let's say we have two pieces of XML that we wish to compare and assert that they are equal. We could write a simple test class like this: A simple comparison test 0x00435A8C"; String myTestXML = "2376"; assertXMLEqual("Comparing test xml to control xml", myControlXML, myTestXML); } }]]> The assertXMLEqual test will pass if the control and test XML are either similar or identical. Obviously in this case the pieces of XML are different and the test will fail. The failure message indicates both what the difference is and the XPath locations of the nodes that were being compared: at /msg[1]/uuid[1] to at /msg[1]/localId[1] ]]> When comparing pieces of XML, the XMLTestCase actually creates an instance of the Diff class. The Diff class stores the result of an XML comparison and makes it available through the methods similar() and identical(). The assertXMLEqual() method tests the value of Diff.similar() and the assertXMLIdentical() method tests the value of Diff.identical(). It is easy to create a Diff instance directly without using the XMLTestCase class as below: Creating a <literal>Diff</literal> instance 3false"; String myTestXML = "false3"; Diff myDiff = new Diff(myControlXML, myTestXML); assertTrue("XML similar " + myDiff.toString(), myDiff.similar()); assertTrue("XML identical " + myDiff.toString(), myDiff.identical()); }]]> This test fails as two pieces of XML are similar but not identical if their nodes occur in a different sequence. The failure message reported by JUnit from the call to myDiff.toString() looks like this: at /struct[1]/int[1] to at /struct[1]/int[1] ]]> For efficiency reasons a Diff stops the comparison process as soon as the first difference is found. To get all the differences between two pieces of XML an instance of the DetailedDiff class, a subclass of Diff, is required. Note that a DetailedDiff is constructed using an existing Diff instance. Consider this test that uses a DetailedDiff: Using <literal>DetailedDiff</literal> War" + "Plague" + "Famine"; String myTestXML = "Peace" + "Health" + "Plenty"; DetailedDiff myDiff = new DetailedDiff(new Diff(myControlXML, myTestXML)); List allDifferences = myDiff.getAllDifferences(); assertEquals(myDiff.toString(), 2, allDifferences.size()); }]]> This test fails with the message below as each of the 3 news items differs between the control and test XML: War at /news[1]/item[1]/text()[1] to Peace at /news[1]/item[1]/text()[1] [different] Expected text value 'Plague' but was 'Health' - comparing Plague at /news[1]/item[2]/text()[1] to Health at /news[1]/item[2]/text()[1] [different] Expected text value 'Famine' but was 'Plenty' - comparing Famine at /news[1]/item[3]/text()[1] to Plenty at /news[1]/item[3]/text()[1] expected <2> but was <3> ]]> The List returned from the getAllDifferences() method contains Difference instances. These instances describe both the typeA full set of prototype Difference instances - one for each type of difference - is defined using final static fields in the DifferenceConstants class. of difference found between a control node and test node and the NodeDetail of those nodes (including the XPath location of each node). Difference instances are passed at runtime in notification events to a registered DifferenceListener, an interface whose default implementation is provided by the Diff class. However it is possible to override this default behaviour by implementing the interface in your own class. The IgnoreTextAndAttributeValuesDifferenceListener class is an example of how to implement a custom DifferenceListener. It allows an XML comparison to be made that ignores differences in the values of text and attribute nodes, for example when comparing a skeleton or outline piece of XML to some generated XML. The following test illustrates the use of a custom DifferenceListener: Using a custom <literal>DifferenceListener</literal> 22 any streetXY00 99Z"; String myTestXML = "20 east cheapEC3M 1EB"; DifferenceListener myDifferenceListener = new IgnoreTextAndAttributeValuesDifferenceListener(); Diff myDiff = new Diff(myControlXML, myTestXML); myDiff.overrideDifferenceListener(myDifferenceListener); assertTrue("test XML matches control skeleton XML", myDiff.similar()); }]]> The DifferenceEngine class generates the events that are passed to a DifferenceListener implementation as two pieces of XML are compared. Using recursion it navigates through the nodes in the control XML DOM, and determines which node in the test XML DOM qualifies for comparison to the current control node. The qualifying test node will match the control node's node type, as well as the node name and namespace (if defined for the control node). However when the control node is an Element, it is less straightforward to determine which test Element qualifies for comparison as the parent node may contain repeated child Elements with the same name and namespace. So for Element nodes, an instance of the ElementQualifier interface is used determine whether a given test Element node qualifies for comparison with a control Element node. This separates the decision about whether two Elements should be compared from the decision about whether those two Elements are considered similar. By default an ElementNameQualifier class is used that compares the nth child ]]> test element to the nth child ]]> control element, i.e. the sequence of the child elements in the test XML is important. However this default behaviour can be overridden using an ElementNameAndTextQualifier or ElementNameAndAttributesQualifier. The test below demonstrates the use of a custom ElementQualifier: Using a custom <literal>ElementQualifier</literal> " + "FirstTestCase" + "SecondTestCase"; String myTestXML = "" + "SecondTestCase" + "FirstTestCase"; assertXMLNotEqual("Repeated child elements in different sequence order are not equal by default", myControlXML, myTestXML); Diff myDiff = new Diff(myControlXML, myTestXML); myDiff.overrideElementQualifier(new ElementNameAndTextQualifier()); assertXMLEqual("But they are equal when an ElementQualifier controls which test element is compared with each control element", myDiff, true); }]]> Note: calling toString on an instance of Diff or DetailedDiff will perform the comparision and cache its result immediately. If you change the DifferenceListener or ElementQualifier after calling toString it won't have any effect.
      Comparing XML Transformations XMLUnit can test XSLT transformations at a high level using the Transform class that wraps an javax.xml.transform.Transformer instance. Knowing the input XML, input stylesheet and expected output XML we can assert that the output of the transformation matches the expected output as follows: Testing the Result of a Transformation The getResultString() and getResultDocument() methods of the Transform class can be used to access the result of the XSLT transformation programmatically if required, for example as below: Using <literal>Transform</literal> programmatically
      Validation Tests XML parsers that validate a piece of XML against a DTD are common, however they rely on a DTD reference being present in the XML, and they can only validate against a single DTD. When writing a system that exchanges XML messages with third parties there are times when you would like to validate the XML against a DTD that is not available to the recipient of the message and so cannot be referenced in the message itself. XMLUnit provides a Validator class for this purpose. Validating Against a DTD Starting with XMLUnit 1.1, the Validator class can also validate against one or more XML Schema definitions. See for details. XMLUnit 1.2 introduces a new Validator class that relies on JAXP 1.3's javax.xml.validation package. This Validator can validate against W3C XML Schema, but may support different Schema languages like RELAX NG if your JAXP implementation supports it. See for details.
      XPath Tests One of the strengths of XML is the ability to programmatically extract specific parts of a document using XPath expressions. The XMLTestCase class offers a number of XPath related assertion methods, as demonstrated in this test: Using XPath Tests " + "" + ""; assertXpathExists("//planet[@name='Earth']", mySolarSystemXML); assertXpathNotExists("//star[@name='alpha centauri']", mySolarSystemXML); assertXpathsEqual("//planet[@name='Earth']", "//planet[@position='3']", mySolarSystemXML); assertXpathsNotEqual("//planet[@name='Venus']", "//planet[@supportsLife='yes']", mySolarSystemXML); }]]> When an XPath expression is evaluated against a piece of XML a NodeList is created that contains the matching Nodes. The methods in the previous test assertXpathExists, assertXpathNotExists, assertXpathsEqual, and assertXpathsNotEqual use these NodeLists. However, the contents of a NodeList can be flattened (or String-ified) to a single value, and XMLUnit also allows assertions to be made about this single value, as in this testEach of the assertXpath...() methods uses an implementation of the XpathEngine interface to evaluate an XPath expression.: Testing XPath Values " + "1.1.x" + "1.2.x" + "1.3.x" + "1.4.x"; assertXpathEvaluatesTo("2", "count(//jvm[@current='yes'])", myJavaFlavours); assertXpathValuesEqual("//jvm[4]/@latest", "//jvm[4]/@current", myJavaFlavours); assertXpathValuesNotEqual("//jvm[2]/@current", "//jvm[3]/@current", myJavaFlavours); }]]> XPaths are especially useful where a document is made up largely of known, unchanging content with only a small amount of changing content created by the system. One of the main areas where constant "boilerplate" markup is combined with system generated markup is of course in web applications. The power of XPath expressions can make testing web page output quite trivial, and XMLUnit supplies a means of converting even very badly formed HTML into XML to aid this approach to testing. The HTMLDocumentBuilder class uses the Swing HTML parser to convert marked-up content to Sax events. The TolerantSaxDocumentBuilder class handles the Sax events to build up a DOM document in a tolerant fashion i.e. without mandating that opened elements are closed. (In a purely XML world this class would have no purpose as there are plenty of Sax event handlers that can build DOM documents from well formed content). The test below illustrates how the use of these classes: Working with non well-formed HTML Ugh" + "

      Heading
        " + "
      • Item One
      • Item Two"; TolerantSaxDocumentBuilder tolerantSaxDocumentBuilder = new TolerantSaxDocumentBuilder(XMLUnit.getTestParser()); HTMLDocumentBuilder htmlDocumentBuilder = new HTMLDocumentBuilder(tolerantSaxDocumentBuilder); Document wellFormedDocument = htmlDocumentBuilder.parse(someBadlyFormedHTML); assertXpathEvaluatesTo("Item One", "/html/body//li[@id='1']", wellFormedDocument); }]]> One of the key points about using XPaths with HTML content is that extracting values in tests requires the values to be identifiable. (This is just another way of saying that testing HTML is easier when it is written to be testable.) In the previous example id attributes were used to identify the list item values that needed to be testable, however class attributes or span and div tags can also be used to identify specific content for testing.

      Testing by Tree Walking The DOM specification allows a Document to optionally implement the DocumentTraversal interface. This interface allows an application to iterate over the Nodes contained in a Document, or to "walk the DOM tree". The XMLUnit NodeTest class and NodeTester interface make use of DocumentTraversal to expose individual Nodes in tests: the former handles the mechanics of iteration, and the latter allows custom test strategies to be implemented. A sample test strategy is supplied by the CountingNodeTester class that counts the nodes presented to it and compares the actual count to an expected count. The test below illustrates its use: Using <literal>CountingNodeTester</literal> 123" + "59"; CountingNodeTester countingNodeTester = new CountingNodeTester(4); assertNodeTestPasses(testXML, countingNodeTester, Node.TEXT_NODE); }]]> This test fails as there are 5 text nodes, and JUnit supplies the following message: Expected node test to pass, but it failed! Counted 5 node(s) but expected 4 Note that if your DOM implementation does not support the DocumentTraversal interface then XMLUnit will throw an IllegalArgumentException informing you that you cannot use the NodeTest or NodeTester classes. Unfortunately even if your DOM implementation does support DocumentTraversal, attributes are not exposed by iteration: however they can be examined from the Element node that contains them. While the previous test could have been easily performed using XPath, there are times when Node iteration is more powerful. In general, this is true when there are programmatic relationships between nodes that can be more easily tested iteratively. The following test uses a custom NodeTester class to illustrate the potential: Using a Custom <literal>NodeTester</literal> 123" + "59"; NodeTest nodeTest = new NodeTest(testXML); assertNodeTestPasses(nodeTest, new FibonacciNodeTester(), new short[] {Node.TEXT_NODE, Node.ELEMENT_NODE}, true); } private class FibonacciNodeTester extends AbstractNodeTester { private int nextVal = 1, lastVal = 1, priorVal = 0; public void testText(Text text) throws NodeTestException { int val = Integer.parseInt(text.getData()); if (nextVal != val) { throw new NodeTestException("Incorrect value", text); } nextVal = val + lastVal; priorVal = lastVal; lastVal = val; } public void testElement(Element element) throws NodeTestException { String name = element.getLocalName(); if ("fibonacci".equals(name) || "val".equals(name)) { return; } throw new NodeTestException("Unexpected element", element); } public void noMoreNodes(NodeTest nodeTest) throws NodeTestException { } }]]> The test fails because the XML contains the wrong value for the last number in the sequence: Expected node test to pass, but it failed! Incorrect value [#text: 9]
      Using XMLUnit
      Requirements XMLUnit requires a JAXP compliant XML parser virtually everywhere. Several features of XMLUnit also require a JAXP compliant XSLT transformer. If it is available, a JAXP compliant XPath engine will be used for XPath tests. To build XMLUnit at least JAXP 1.2 is required, this is the version provided by the Java class library in JDK 1.4. The JAXP 1.3 (i.e. Java5 and above) XPath engine can only be built when JAXP 1.3 is available. As long as you don't require support for XML Namespaces or XML Schema, any JAXP 1.1 compliant implementations should work at runtime. For namespace and schema support you will need a parser that complies to JAXP 1.2 and supports the required feature. The XML parser shipping with JDK 1.4 (a version of Apache Crimson) for example is compliant to JAXP 1.2 but doesn't support Schema validation. XMLUnit is supposed to build and run on any Java version after 1.3 (at least no new hard JDK 1.4 dependencies have been added in XMLUnit 1.1), but it has only been tested on JDK 1.4.2 and above. To build XMLUnit JUnit 3.x (only tested with JUnit 3.8.x) is required. It is not required at runtime unless you intend to use the XMLTestCase or XMLAssert classes.
      Basic Usage XMLUnit consists of a few classes all living in the org.custommonkey.xmlunit package. You can use these classes directly from your code, no matter whether you are writing a unit test or want to use XMLUnit's features for any other purpose. This section provides a few hints of where to start if you want to use a certain feature of XMLUnit, more details can be found in the more specific sections later in this document.
      Comparing Pieces of XML Heart and soul of XMLUnit's comparison engine is DifferenceEngine but most of the time you will use it indirectly via the Diff class. You can influence the engine by providing (custom) implementations for various interfaces and by setting a couple of options on the XMLUnit class. More information is available in .
      Validating All validation happens in the Validator class. The default is to validate against a DTD, but XML Schema validation can be enabled by an option (see Validator.useXMLSchema). Several options of the XMLUnit class affect validation. More information is available in .
      XSLT Transformations The Transform class provides an easy to use layer on top of JAXP's transformations. An instance of this class is initialized with the source document and a stylesheet and the result of the transformation can be retrieved as a String or DOM Document. The output of Transform can be used as input to comparisons, validations, XPath tests and so on. There is no detailed sections on transformations since they are really only a different way to create input for the rest of XMLUnit's machinery. Examples can be found in . It is possible to provide a custom javax.xml.transform.URIResolver via the XMLUnit.setURIResolver method. You can access the underlying XSLT transformer via XMLUnit.getTransformerFactory.
      XPath Engine The central piece of XMLUnit's XPath support is the XpathEngine interface. Currently two implementations of the interface exist, SimpleXpathEngine and org.custommonkey.xmlunit.jaxp13.Jaxp13XpathEngine. SimpleXpathEngine is a very basic implementation that uses your XSLT transformer under the covers. This also means it will expose you to the bugs found in your transformer like the transformer claiming a stylesheet couldn't be compiled for very basic XPath expressions. This has been reported to be the case for JDK 1.5. org.custommonkey.xmlunit.jaxp13.Jaxp13XpathEngine uses JAXP 1.3's javax.xml.xpath package and seems to work more reliable, stable and performant than SimpleXpathEngine. You use the XMLUnit.newXpathEngine method to obtain an instance of the XpathEngine. As of XMLUnit 1.1 this will try to use JAXP 1.3 if it is available and fall back to SimpleXpathEngine. Instances of XpathEngine can return the results of XPath queries either as DOM NodeList or plain Strings. More information is available in .
      DOM Tree Walking To test pieces of XML by traversing the DOM tree you use the NodeTester class. Each DOM Node will be passed to a NodeTester implementation you provide. The AbstractNodeTester class is provided as a NullObject Pattern base class for implementations of your own. More information is available in .
      Using XMLUnit With JUnit 3.x Initially XMLUnit was tightly coupled to JUnit and the recommended approach was to write unit tests by inheriting from the XMLTestCase class. XMLTestCase provides a pretty long list of assert... methods that may simplify your interaction with XMLUnit's internals in many common cases. The XMLAssert class provides the same set of assert...s as static methods. Use XMLAssert instead of XMLTestCase for your unit tests if you can't or don't want to inherit from XMLTestCase. All power of XMLUnit is available whether you use XMLTestCase and/or XMLAssert or the underlying API directly. If you are using JUnit 3.x then using the specific classes may prove to be more convenient.
      Common Configuration Options
      JAXP If you are using a JDK 1.4 or later, your Java class library already contains the required XML parsers and XSLT transformers. Still you may want to use a different parser/transformer than the one of your JDK - in particular since the versions shipping with some JDKs are known to contain serious bugs. As described in there are two main approaches to choose the XML parser of XSLT transformer: System properties and setters in the XMLUnit class. If you use system properties you have the advantage that your choice affects the whole JAXP system, whether it is used inside of XMLUnit or not. If you are using JDK 1.4 or later you may also want to review the Endorsed Standards Override Mechanism to use a different parser/transformer than the one shipping with your JDK. The second option - using the XMLUnit class - allows you to use different parsers for control and test documents, it even allows you to use different parsers for different test cases, if you really want to stretch it that far. It may also work for JDK 1.4 and above, even if you don't override the endorsed standards libraries. You can access the underlying JAXP parser by XMLUnit.newControlParser, XMLUnit.newTestParser, XMLUnit.getControlDocumentBuilderFactory, XMLUnit.getTestDocumentBuilderFactory and XMLUnit.getSAXParserFactory (used by Validator). Note that all these methods return factories or parsers that are namespace aware. The various build... methods in XMLUnit provide convenience layers for building DOM Documents using the configured parsers. You can also set the class name for the XPathFactory to use when using JAXP 1.3 by passing the class name to XMLUnit.setXPathFactory.
      <literal>EntityResolver</literal> You can provide a custom org.xml.sax.EntityResolver for the control and test parsers via XMLUnit.setControlEntityResolver and XMLUnit.setTestEntityResolver. Validator uses the resolver set via setControlEntityResolver as well.
      Element Content Whitespace Element content whitespace - also known as ignorable whitespace - is whitespace contained in elements whose content model doesn't allow text content. I.e. the newline and space characters between ]]> and ]]> in the following example could belong into this category. ]]> Using XMLUnit.setIgnoreWhitespace it is possible to make the test and control parser ignore this kind of whitespace. Note that setting this property to true usually doesn't have any effect since it only works on validating parsers and XMLUnit doesn't enable validation by default. It does have an effect when comparing pieces of XML, though, since the same flag is used for a different purpose as well in that case. See for more details.
      XSLT Stylesheet Version Some features of XMLUnit use XSLT stylesheets under the covers, in particular XSLT will be used to strip element content whitespace or comments as well as by SimpleXpathEngine. These stylesheets only require a XSLT transformer that supports XSLT 1.0 and will say so in the stylesheet element. If your XSLT transformer supports XSLT 2.0 or newer it mayThe W3C recommendation says it SHOULD. issue a warning for these stylesheets which can be annoying. You can use XMLUnit.setXSLTVersion to make XMLUnit change the version attribute to a different value. Note that XMLUnit hasn't been tested with a value other than "1.0".
      Providing Input to XMLUnit Most methods in XMLUnit that expect a piece of XML as input provide several overloads that obtain their input from different sources. The most common options are: A DOM Document. Here you have all control over the document's creation. Such a Document could as well be the result of an XSLT transformation via the Transform class. A SAX InputSource. This is the most generic way since InputSource allows you to read from arbitrary InputStreams or Readers. Use an InputStream wrapped by an InputSource if you want the XML parser to pick up the proper encoding from the XML declaration. A String. Here a DOM Document is built from the input String using the JAXP parser specified for control or test documents - depending on whether the input is a control or test piece of XML. Note that using a String assumes that your XML has already been converted from its XML encoding to a Java String upfront. A Reader. Here a DOM Document is built from the input Reader using the JAXP parser specified for control or test documents - depending on whether the input is a control or test piece of XML. Note that using a Reader is a bad choice if your XML encoding is different from your platform's default encoding since Java's IO system won't read your XML declaration. It is a good practice to use one of the other overloads rather than the Reader version to ensure encoding has been dealt with properly.
      Comparing Pieces of XML
      The Difference Engine At the center of XMLUnit's support for comparisons is the DifferenceEngine class. In practice you rarely deal with it directly but rather use it via instances of Diff or DetailedDiff classes (see ). The DifferenceEngine walks two trees of DOM Nodes, the control and the test tree, and compares the nodes. Whenever it detects a difference, it sends a message to a configured DifferenceListener (see ) and asks a ComparisonController (see ) whether the current comparison should be halted. In some cases the order of elements in two pieces of XML may not be significant. If this is true, the DifferenceEngine needs help to determine which Elements to compare. This is the job of an ElementQualifier (see ). The types of differences DifferenceEngine can detect are enumerated in the DifferenceConstants interface and represented by instances of the Difference class. A Difference can be recoverable; recoverable Differences make the Diff class consider two pieces of XML similar while non-recoverable Differences render the two pieces different. The types of Differences that are currently detected are listed in to (the first two columns refer to the DifferenceConstants class). Document level <literal>Difference</literal>s detected by <literal>DifferenceEngine</literal> ID Constant recoverable Description HAS_DOCTYPE_DECLARATION_ID HAS_DOCTYPE_DECLARATION true One piece of XML has a DOCTYPE declaration while the other one has not. DOCTYPE_NAME_ID DOCTYPE_NAME false Both pieces of XML contain a DOCTYPE declaration but the declarations specify different names for the root element. DOCTYPE_PUBLIC_ID_ID DOCTYPE_PUBLIC_ID false Both pieces of XML contain a DOCTYPE declaration but the declarations specify different PUBLIC identifiers. DOCTYPE_SYSTEM_ID_ID DOCTYPE_SYSTEM_ID true Both pieces of XML contain a DOCTYPE declaration but the declarations specify different SYSTEM identifiers. NODE_TYPE_ID NODE_TYPE false The test piece of XML contains a different type of node than was expected. This type of difference will also occur if either the root control or test Node is null while the other is not. NAMESPACE_PREFIX_ID NAMESPACE_PREFIX true Two nodes use different prefixes for the same XML Namespace URI in the two pieces of XML. NAMESPACE_URI_ID NAMESPACE_URI false Two nodes in the two pieces of XML share the same local name but use different XML Namespace URIs. SCHEMA_LOCATION_ID SCHEMA_LOCATION true Two nodes have different values for the schemaLocation attribute of the XMLSchema-Instance namespace. The attribute could be present on only one of the two nodes. NO_NAMESPACE_SCHEMA_LOCATION_ID NO_NAMESPACE_SCHEMA_LOCATION true Two nodes have different values for the noNamespaceSchemaLocation attribute of the XMLSchema-Instance namespace. The attribute could be present on only one of the two nodes.
      Element level <literal>Difference</literal>s detected by <literal>DifferenceEngine</literal> ID Constant recoverable Description ELEMENT_TAG_NAME_ID ELEMENT_TAG_NAME false The two pieces of XML contain elements with different tag names. ELEMENT_NUM_ATTRIBUTES_ID ELEMENT_NUM_ATTRIBUTES false The two pieces of XML contain a common element, but the number of attributes on the element is different. HAS_CHILD_NODES_ID HAS_CHILD_NODES false An element in one piece of XML has child nodes while the corresponding one in the other has not. CHILD_NODELIST_LENGTH_ID CHILD_NODELIST_LENGTH false Two elements in the two pieces of XML differ by their number of child nodes. CHILD_NODELIST_SEQUENCE_ID CHILD_NODELIST_SEQUENCE true Two elements in the two pieces of XML contain the same child nodes but in a different order. CHILD_NODE_NOT_FOUND_ID CHILD_NODE_NOT_FOUND false A child node in one piece of XML couldn't be matched against any other node of the other piece. ATTR_SEQUENCE_ID ATTR_SEQUENCE true The attributes on an element appear in different orderNote that the order of attributes is not significant in XML, different parsers may return attributes in a different order even if parsing the same XML document. There is an option to turn this check off - see - but it is on by default for backwards compatibility reasons in the two pieces of XML.
      Attribute level <literal>Difference</literal>s detected by <literal>DifferenceEngine</literal> ID Constant recoverable Description ATTR_VALUE_EXPLICITLY_SPECIFIED_ID ATTR_VALUE_EXPLICITLY_SPECIFIED true An attribute that has a default value according to the content model of the element in question has been specified explicitly in one piece of XML but not in the other.In order for this difference to be detected the parser must have been in validating mode when the piece of XML was parsed and the DTD or XML Schema must have been available. ATTR_NAME_NOT_FOUND_ID ATTR_NAME_NOT_FOUND false One piece of XML contains an attribute on an element that is missing in the other. ATTR_VALUE_ID ATTR_VALUE false The value of an element's attribute is different in the two pieces of XML.
      Other <literal>Difference</literal>s detected by <literal>DifferenceEngine</literal> ID Constant recoverable Description COMMENT_VALUE_ID COMMENT_VALUE false The content of two comments is different in the two pieces of XML. PROCESSING_INSTRUCTION_TARGET_ID PROCESSING_INSTRUCTION_TARGET false The target of two processing instructions is different in the two pieces of XML. PROCESSING_INSTRUCTION_DATA_ID PROCESSING_INSTRUCTION_DATA false The data of two processing instructions is different in the two pieces of XML. CDATA_VALUE_ID CDATA_VALUE false The content of two CDATA sections is different in the two pieces of XML. TEXT_VALUE_ID TEXT_VALUE false The value of two texts is different in the two pieces of XML.
      Note that some of the differences listed may be ignored by the DifferenceEngine if certain configuration options have been specified. See for details. DifferenceEngine passes differences found around as instances of the Difference class. In addition to the type of of difference this class also holds information on the nodes that have been found to be different. The nodes are described by NodeDetail instances that encapsulate the DOM Node instance as well as the XPath expression that locates the Node inside the given piece of XML. NodeDetail also contains a "value" that provides more information on the actual values that have been found to be different, the concrete interpretation depends on the type of difference as can be seen in . Contents of <literal>NodeDetail.getValue()</literal> for <literal>Difference</literal>s Difference.getId() NodeDetail.getValue() HAS_DOCTYPE_DECLARATION_ID "not null" if the document has a DOCTYPE declaration, "null" otherwise. DOCTYPE_NAME_ID The name of the root element. DOCTYPE_PUBLIC_ID The PUBLIC identifier. DOCTYPE_SYSTEM_ID The SYSTEM identifier. NODE_TYPE_ID If one node was absent: "not null" if the node exists, "null" otherwise. If the node types differ the value will be a string-ified version of org.w3c.dom.Node.getNodeType(). NAMESPACE_PREFIX_ID The Namespace prefix. NAMESPACE_URI_ID The Namespace URI. SCHEMA_LOCATION_ID The attribute's value or "[attribute absent]" if it has not been specified. NO_NAMESPACE_SCHEMA_LOCATION_ID The attribute's value or "[attribute absent]" if it has not been specified. ELEMENT_TAG_NAME_ID The tag name with any Namespace information stripped. ELEMENT_NUM_ATTRIBUTES_ID The number of attributes present turned into a String. HAS_CHILD_NODES_ID "true" if the element has child nodes, "false" otherwise. CHILD_NODELIST_LENGTH_ID The number of child nodes present turned into a String. CHILD_NODELIST_SEQUENCE_ID The sequence number of this child node turned into a String. CHILD_NODE_NOT_FOUND_ID The name of the unmatched node or "null". If the node is an element inside an XML namespace the name will be Java5-QName-like {NS-URI}LOCAL-NAME - in all other cases it is the node's local name. ATTR_SEQUENCE_ID The attribute's name. ATTR_VALUE_EXPLICITLY_SPECIFIED_ID "true" if the attribute has been specified, "false" otherwise. ATTR_NAME_NOT_FOUND_ID The attribute's name or "null". If the attribute belongs to an XML namespace the name will be Java5-QName-like {NS-URI}LOCAL-NAME - in all other cases it is the attribute's local name. ATTR_VALUE_ID The attribute's value. COMMENT_VALUE_ID The actual comment. PROCESSING_INSTRUCTION_TARGET_ID The processing instruction's target. PROCESSING_INSTRUCTION_DATA_ID The processing instruction's data. CDATA_VALUE_ID The content of the CDATA section. TEXT_VALUE_ID The actual text.
      As said in the first paragraph you won't deal with DifferenceEngine directly in most cases. In cases where Diff or DetailedDiff don't provide what you need you'd create an instance of DifferenceEngine passing a ComparisonController in the constructor and invoke compare with your DOM trees to compare as well as a DifferenceListener and ElementQualifier. The listener will be called on any differences while the control method is executing. Using <literal>DifferenceEngine</literal> Directly
      <literal>ComparisonController</literal> The ComparisonController's job is to decide whether a comparison should be halted after a difference has been found. Its interface is: differenceFound * @return true to halt further comparison, false otherwise */ boolean haltComparison(Difference afterDifference); ]]> Whenever a difference has been detected by the DifferenceEngine the haltComparison method will be called immediately after the DifferenceListener has been informed of the difference. This is true no matter what type of Difference has been found or which value the DifferenceListener has returned. The only implementations of ComparisonController that ship with XMLUnit are Diff and DetailedDiff, see for details about them. A ComparisonController that halted the comparison on any non-recoverable difference could be implemented as: A Simple <literal>ComparisonController</literal>
      <literal>DifferenceListener</literal> DifferenceListener contains two callback methods that are invoked by the DifferenceEngine when differences are detected: differenceFound is invoked by DifferenceEngine as soon as a difference has been detected. The return value of that method is completely ignored by DifferenceEngine, it becomes important when used together with Diff, though (see ). The return value should be one of the four constants defined in the the DifferenceListener interface: differenceFound method. * Indicates that the Difference is interpreted as defined * in {@link DifferenceConstants DifferenceConstants}. */ int RETURN_ACCEPT_DIFFERENCE; /** * Override return value for the differenceFound method. * Indicates that the nodes identified as being different should be * interpreted as being identical. */ int RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL; /** * Override return value for the differenceFound method. * Indicates that the nodes identified as being different should be * interpreted as being similar. */ int RETURN_IGNORE_DIFFERENCE_NODES_SIMILAR; /** * Override return value for the differenceFound method. * Indicates that the nodes identified as being similar should be * interpreted as being different. */ int RETURN_UPGRADE_DIFFERENCE_NODES_DIFFERENT = 3; ]]> The skippedComparison method is invoked if the DifferenceEngine encounters two Nodes it cannot compare. Before invoking skippedComparison DifferenceEngine will have invoked differenceFound with a Difference of type NODE_TYPE. A custom DifferenceListener that ignored any DOCTYPE related differences could be written as: A <literal>DifferenceListener</literal> that Ignores DOCTYPE Differences = 0 ? RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL : RETURN_ACCEPT_DIFFERENCE; } public void skippedComparison(Node control, Node test) { } } ]]> Apart from Diff and DetailedDiff XMLUnit ships with an additional implementation of DifferenceListener.
      <literal>IgnoreTextAndAttributeValuesDifferenceListener</literal> IgnoreTextAndAttributeValuesDifferenceListener doesn't do anything in skippedComparison. It "downgrades" Differences of type ATTR_VALUE, ATTR_VALUE_EXPLICITLY_SPECIFIED and TEXT_VALUE to recoverable differences. This means if instances of IgnoreTextAndAttributeValuesDifferenceListener are used together with Diff then two pieces of XML will be considered similar if they have the same basic structure. They are not considered identical, though. Note that the list of ignored differences doesn't cover all textual differences. You should configure XMLUnit to ignore comments and whitespace and to consider CDATA sections and text nodes to be the same (see ) in order to cover COMMENT_VALUE and CDATA_VALUE as well.
      <literal>ElementQualifier</literal> When DifferenceEngine encounters a list of DOM Elements as children of another Element it will ask the configured ElementQualifier which Element of the control piece of XML should be compared to which of the test piece. Its contract is: For any given Element in the control piece of XML DifferenceEngine will cycle through the corresponding list of Elements in the test piece of XML until qualifyForComparison has returned true or the test document is exhausted. When using DifferenceEngine or Diff it is completely legal to set the ElementQualifier to null. In this case any kind of Node is compared to the test Node that appears at the same position in the sequence. Example Nodes for <literal>ElementQualifier</literal> (the comments are not part of the example) xyzzy xyzzy ]]> Taking without any ElementQualifier DifferenceEngine will compare control node n to test node n for n between 1 and 4. In many cases this is exactly what is desired, but sometimes ]]> should be similar to ]]> because the order of elements doesn't matter - this is when you'd use a different ElementQualifier. XMLUnit ships with several implementations.
      <literal>ElementNameQualifier</literal> Only Elements with the same name - and Namespace URI if present - qualify. In this means control node 1 will be compared to test node 2. Then control node 2 will be compared to test node 3 because DifferenceEngine will start to search for the matching test Element at the second test node, the same sequence number the control node is at. Control node 3 is compared to test node 3 as well and control node 4 to test node 4.
      <literal>ElementNameAndAttributeQualifier</literal> Only Elements with the same name - and Namespace URI if present - as well as the same values for all attributes given in ElementNameAndAttributeQualifier's constructor qualify. Let's say "foo" has been passed to ElementNameAndAttributeQualifier's constructor when looking at . This again means control node 1 will be compared to test node 2 since they do have the same name and no value at all for attribute "foo". Then control node 2 will be compared to test node 3 - again, no value for "foo". Control node 3 is compared to test node 4 as they have the same value "bar". Finally control node 4 is compared to test node 1; here DifferenceEngine searches from the beginning of the test node list after test node 4 didn't match. There are three constructors in ElementNameAndAttributeQualifier. The no-arg constructor creates an instance that compares all attributes while the others will compare a single attribute or a given subset of all attributes.
      <literal>ElementNameAndTextQualifier</literal> Only Elements with the same name - and Namespace URI if present - as well as the same text content nested into them qualify. In this means control node 1 will be compared to test node 2 since they both don't have any nested text at all. Then control node 2 will be compared to test node 4. Control node 3 is compared to test node 3 since they have the same nested text and control node 4 to test node 4.
      <literal>org.custommonkey.xmlunit.examples.RecursiveElementNameAndTextQualifier</literal> All ElementQualifiers seen so far only looked at the Elements themselves and not at the structure nested into them at a deeper level. A frequent user question has been which ElementQualifier should be used if the pieces of XML in should be considered similar. Example for <literal>RecursiveElementNameAndTextQualifier</literal> (the comments are not part of the example)
      foo
      bar
      bar
      foo
      ]]>
      At first glance ElementNameAndTextQualifier should work but it doesn't. When DifferenceEngine processed the children of table it would compare control row 1 to test row 1 since both tr elements have the same name and both have no textual content at all. What is needed in this case is an ElementQualifier that looks at the element's name, as well as the name of the first child element and the text nested into that first child element. This is what RecursiveElementNameAndTextQualifier does. RecursiveElementNameAndTextQualifier ignores whitespace between the elements leading up to the nested text.
      <literal>org.custommonkey.xmlunit.examples.MultiLevelElementNameAndTextQualifier</literal> MultiLevelElementNameAndTextQualifier has in a way been the predecessor of RecursiveElementNameAndTextQualifier. It also matches element names and those of nested child elements until it finds matches, but unlike RecursiveElementNameAndTextQualifier, you must tell MultiLevelElementNameAndTextQualifier at which nesting level it should expect the nested text. MultiLevelElementNameAndTextQualifier's constructor expects a single argument which is the nesting level of the expected text. If you use an argument of 1, MultiLevelElementNameAndTextQualifier is identical to ElementNameAndTextQualifier. In a value of 2 would be needed. By default MultiLevelElementNameAndTextQualifier will not ignore whitespace between the elements leading up to the nested text. If your piece of XML contains this sort of whitespace (like which contains a newline and several space characters between <tr> and <td>) you can either instruct XMLUnit to ignore whitespace completely (see ) or use the two-arg constructor of MultiLevelElementNameAndTextQualifier introduced with XMLUnit 1.2 and set the ignoreEmptyTexts argument to true. In general RecursiveElementNameAndTextQualifier requires less knowledge upfront and its whitespace-handling is more intuitive.
      <literal>Diff</literal> and <literal>DetailedDiff</literal> Diff and DetailedDiff provide simplified access to DifferenceEngine by implementing the ComparisonController and DifferenceListener interfaces themselves. They cover the two most common use cases for comparing two pieces of XML: checking whether the pieces are different (this is what Diff does) and finding all differences between them (this is what DetailedDiff does). DetailedDiff is a subclass of Diff and can only be constructed by creating a Diff instance first. The major difference between them is their implementation of the ComparisonController interface: DetailedDiff will never stop the comparison since it wants to collect all differences. Diff in turn will halt the comparison as soon as the first Difference is found that is not recoverable. In addition DetailedDiff collects all Differences in a list and provides access to it. By default Diff will consider two pieces of XML as identical if no differences have been found at all, similar if all differences that have been found have been recoverable (see to ) and different as soon as any non-recoverable difference has been found. It is possible to specify a DifferenceListener to Diff using the overrideDifferenceListener method. In this case each Difference will be evaluated by the passed in DifferenceListener. By returning RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL the custom listener can make Diff ignore the difference completely. Likewise any Difference for which the custom listener returns RETURN_IGNORE_DIFFERENCE_NODES_SIMILAR will be treated as if the Difference was recoverable. There are several overloads of the Diff constructor that allow you to specify your piece of XML in many ways. There are overloads that accept additional DifferenceEngine and ElementQualifier arguments. Passing in a DifferenceEngine of your own is the only way to use a ComparisonController other than Diff. Note that Diff and DetailedDiff use ElementNameQualifier as their default ElementQualifier. This is different from DifferenceEngine which defaults to no ElementQualifier at all. To use a custom ElementQualifier you can also use the overrideElementQualifier method. Use this with an argument of null to unset the default ElementQualifier as well. To compare two pieces of XML you'd create a Diff instance from those two pieces and invoke identical to check that there have been no differences at all and similar to check that any difference, if any, has been recoverable. If the pieces are identical they are also similar. Likewise if they are not similar they can't be identical either. Comparing Two Pieces of XML Using <literal>Diff</literal> ", ""); assertFalse(d.identical()); // CHILD_NODELIST_SEQUENCE Difference assertTrue(d.similar()); ]]> The result of the comparison is cached in Diff, repeated invocations of identical or similar will not reevaluate the pieces of XML. Note: calling toString on an instance of Diff or DetailedDiff will perform the comparision and cache its result immediately. If you change the DifferenceListener or ElementQualifier after calling toString it won't have any effect. DetailedDiff provides only a single constructor that expects a Diff as argument. Don't use DetailedDiff if all you need to know is whether two pieces of XML are identical/similar - use Diff directly since its short-cut ComparisonController implementation will save time in this case. Finding All Differences Using <literal>DetailedDiff</literal> ", ""); DetailedDiff dd = new DetailedDiff(d); dd.overrideElementQualifier(null); assertFalse(dd.similar()); List l = dd.getAllDifferences(); assertEquals(2, l.size()); // expected but was and vice versa ]]>
      <literal>MatchTracker</literal> Sometimes you might be interested in any sort of comparison result and want to get notified of successful matches as well. Maybe you want to provide feedback on the amount of differences and similarities between two documents, for example. The interface MatchTracker can be implemented to get notified on each and every successful match, note that there may be a lot more comparisons going on than you might expect and that your callback gets notified a lot. The <literal>MatchTracker</literal> interface Despite its name the Difference instance passed into the matchFound method really describes a match and not a difference. You can expect that the getValue method on both the control and the test NodeDetail will be equal. DifferenceEngine provides a constructor overload that allows you to pass in a MatchTracker instance and also provides a setMatchTracker method. Diff and DetailedDiff provide overrideMatchTracker methods that fill the same purpose. Note that your MatchTracker won't receive any callbacks once the configured ComparisonController has decided that DifferenceEngine should halt the comparison.
      JUnit 3.x Convenience Methods XMLAssert and XMLTestCase contain quite a few overloads of methods for comparing two pieces of XML. The method's names use the word Equal to mean the same as similar in the Diff class (or throughout this guide). So assertXMLEqual will assert that only recoverable differences have been encountered where assertXMLNotEqual asserts that some differences have been non-recoverable. assertXMLIdentical asserts that there haven't been any differences at all while assertXMLNotIdentical asserts that there have been differences (recoverable or not). Most of the overloads of assertXMLEqual just provide different means to specify the pieces of XML as Strings, InputSources, ReadersSee for some advice on choosing your input format. or Documents. For each method there is a version that takes an additional err argument which is used to create the message if the assertion fails. If you don't need any control over the ElementQualifier or DifferenceListener used by Diff these methods will save some boilerplate code. If CONTROL and TEST are pieces of XML represented as one of the supported inputs then and are equivalent. If you need more control over the Diff instance there is a version of assertXMLEqual (and assertXMLIdentical) that accepts a Diff instance as its argument as well as a boolean indicating whether you expect the Diff to be similar (identical) or not. XMLTestCase contains a couple of compareXML methods that really are only shortcuts to Diff's constructors. There is no way to use DifferenceEngine or DetailedDiff directly via the convenience methods.
      Configuration Options Unless you are using Document or DOMSource overrides when specifying your pieces of XML, XMLUnit will use the configured XML parsers (see ) and EntityResolvers (see ). There are configuration options to use different settings for the control and test pieces of XML. In addition some of the other configuration settings may lead to XMLUnit using the configured XSLT transformer (see ) under the covers.
      Whitespace Handling Two different configuration options affect how XMLUnit treats whitespace in comparisons: Element Content Whitespace (see ) If XMLUnit has been configured to ignore element content whitespace it will trim any text nodes found by the parser. This means that there won't appear to be any textual content in element <foo> for the following example. If you don't set XMLUnit.setIgnoreWhitespace there would be textual content consisting of a new line character. ]]> At the same time the following two <foo> elements will be considered identical if the option has been enabled, though. bar bar ]]> When this option is set to true, Diff will use the XSLT transformer under the covers. "Normalizing" Whitespace If you set XMLUnit.setNormalizeWhitespace to true then XMLUnit will replace any kind of whitespace found in character content with a SPACE character and collapse consecutive whitespace characters to a single SPACE. It will also trim the resulting character content on both ends. The following two <foo> elements will be considered identical if the option has been set: bar baz bar baz ]]> Note that this is not related to "normalizing" the document as a whole (see ).
      "Normalizing" <literal>Document</literal>s "Normalize" in this context corresponds to the normalize method in DOM's Document class. It is the process of merging adjacent Text nodes and is not related to "normalizing whitespace" as described in the previous section. Usually you don't need to care about this option since the XML parser is required to normalize the Document when creating it. The only reason you may want to change the option via XMLUnit.setNormalize is that your Document instances have not been created by an XML parser but rather been put together in memory using the DOM API directly.
      Ignoring Comments Using XMLUnit.setIgnoreComments you can make XMLUnit's difference engine ignore comments completely. When this option is set to true, Diff will use the XSLT transformer under the covers.
      Treating CDATA Sections and Text Nodes Alike It is not always necessary to know whether a text has been put into a CDATA section or not. Using XMLUnit.setIgnoreDiffBetweenTextAndCDATA you can make XMLUnit consider the following two pieces of XML identical: <bar> ]]> <foo><![CDATA[<bar>]]></foo>
      Entity Reference Expansion Normally the XML parser will expand character references to their Unicode equivalents but for more complex entity definitions the parser may expand them or not. Using XMLUnit.setExpandEntityReferences you can control the parser's setting.
      Comparison of Unmatched Elements When XMLUnit cannot match a control Element to a test Element (the configured ElementQualifier - see - doesn't return true for any of the test Elements) it will try to compare it against the first unmatched test Element (if there is one). Starting with XMLUnit 1.3 one can use XMLUnit.setCompareUnmatched to disable this behavior and generate CHILD_NODE_NOT_FOUND differences instead. If the control document is ]]> and the test document is ]]> the default setting will create a single ELEMENT_TAG_NAME Difference ("expected a but found b"). Setting XMLUnit.setCompareUnmatched to false will create two Differences of type CHILD_NODE_NOT_FOUND (one for "a" and one for "b") instead.
      Validating XML Documents
      The <literal>Validator</literal> Class The Validator class encapsulates XMLUnit's validation support. It will use the SAXParser configured in XMLUnit (see ). The piece of XML to validate is specified in the constructor. The constructors using more than a single argument are only relevant if you want to validate against a DTD and need to provide the location of the DTD itself - for details see the next section. By default, Validator will validate against a DTD, but it is possible to validate against a (or multiple) Schema(s) as well. Schema validation requires an XML parser that supports it, of course.
      DTD Validation
      Validating against a DTD is straight forward if the piece of XML contains a DOCTYPE declaration with a SYSTEM identifier that can be resolved at validation time. Simply create a Validator object using one of the single argument constructors. Validating Against the DTD Defined in <literal>DOCTYPE</literal> If the piece of XML doesn't contain any DOCTYPE declaration at all or it contains a DOCTYPE but you want to validate against a different DTD, you'd use one of the three argument versions of Validator's constructors. In this case the publicId argument becomes the PUBLIC and systemId the SYSTEM identifier of the DOCTYPE that is implicitly added to the piece of XML. Any existing DOCTYPE will be removed. The systemId should be a URL that can be resolved by your parser. Validating a Piece of XML that doesn't Contain a <literal>DOCTYPE</literal> If the piece of XML already has the correct DOCTYPE declaration but the declaration either doesn't specify a SYSTEM identifier at all or you want the SYSTEM identifier to resolve to a different location you have two options: Use one of the two argument constructors and specify the alternative URL as systemId. Validating Against a Local DTD Use a custom EntityResolver via XMLUnit.setControlEntityResolver together with one of the single argument constructor overloads of Validator. This approach would allow you to use an OASIS cataloghttp://www.oasis-open.org/committees/download.php/14809/xml-catalogs.html in conjunction with the Apache XML Resolver libraryhttp://xml.apache.org/commons/components/resolver/index.html to resolve the DTD location as well as the location of any other entity in your piece of XML, for example. Validating Against a DTD Using Apache's XML Resolver and an XML Catalog ]]>
      XML Schema Validation
      In order to validate against the XML Schema language Schema validation has to be enabled via the useXMLSchema method of Validator. By default the parser will try to resolve the location of Schema definition files via a schemaLocation attribute if it is present in the piece of XML or it will try to open the Schema's URI as an URL and read from it. The setJAXP12SchemaSource method of Validator allows you to override this behavior as long as the parser supports the http://java.sun.com/xml/jaxp/properties/schemaSource property in the way described in "JAXP 1.2 Approved CHANGES"http://java.sun.com/webservices/jaxp/change-requests-11.html. setJAXP12SchemaSource's argument can be one of A String which contains an URI. An InputStream the Schema can be read from. An InputSource the Schema can be read from. A File the Schema can be read from. An array containing any of the above. If the property has been set using a String, the Validator class will provide its systemId as specified in the constructor when asked to resolve it. You must only use the single argument constructors if you want to avoid this behavior. If no systemId has been specified, the configured EntityResolver may still be used. Validating Against a Local XML Schema
      JUnit 3.x Convenience Methods Both XMLAssert and XMLTestCase provide an assertXMLValid(Validator) method that will fail if Validator's isValid method returns false. In addition several overloads of the assertXMLValid method are provided that directly correspond to similar overloads of Validator's constructor. These overloads don't support XML Schema validation at all. Validator itself provides an assertIsValid method that will throw an AssertionFailedError if validation fails. Neither method provides any control over the message of the AssertionFailedError in case of a failure.
      Configuration Options Validator uses a SAX parser created by the configured SAX parser factory (see ). It will use the "control" EntityResolver if one has been specified (see ). The location of a DTD can be specified via Validator's systemId constructor argument or a custom EntityResolver (see ). XML Schema validation is enabled via Validator.useXMLSchema(true). The location(s) of XML Schema document(s) can be specified via Validator.setJAXP12SchemaSource (see ).
      JAXP 1.3 Validation JAXP 1.3 - shipping with Java5 or better and available as a separate product for earlier Java VMs - introduces a new package javax.xml.validation designed for validations of snippets of XML against different schema languages. Any compliant implementation must support the W3C XML Schema language, but other languages like RELAX NG or Schematron may be supported as well. The class org.custommonkey.xmlunit.jaxp13.Validator can be used to validate a piece of XML against a schema definition but also to validate the schema definition itself. By default Validator will assume your definition uses the W3C XML Schema language, but it provides a constructor that can be used to specify a different language via an URL supported by the SchemaFactory class. Alternatively you can specify the schema factory itself. The schema definition itself can be given via Source elements, just like the pieces of XML to validate are specified as Source as well. Note the Validator class of javax.xml.validation will ignore all xsi:namespaceLocation and xsi:noNamespaceLocation attributes of the XML document you want to validate if you specify at least one schema source. The following example uses org.custommonkey.xmlunit.jaxp13.Validator to perform the same type of validation shown in . Validating Against a Local XML Schema Validating a schema definition is shown in the next example. Validating an XML Schema Definition There is no explicit JUnit 3 support for org.custommonkey.xmlunit.jaxp13.Validator.
      XPath Tests
      XPath Engines Central to XMLUnit's XPath support is the XpathEngine interface which consists of only three methods: select expression * on the specified document and return the list of nodes (could have * length zero) that match * @param select * @param document * @return list of matching nodes */ NodeList getMatchingNodes(String select, Document document) throws XpathException; /** * Evaluate the result of executing the specified XPath syntax * select expression on the specified document * @param select * @param document * @return evaluated result */ String evaluate(String select, Document document) throws XpathException; /** * Establish a namespace context. */ void setNamespaceContext(NamespaceContext ctx); ]]> The first two methods expect an XPath expression that selects content from the DOM document that is the second argument. The result of the selection can be either a DOM NodeList or a String. The later form tries to flatten the result, the value is said to be "String-ified". The third method is part of XMLUnit's support for XML Namespaces in XPath expressions. See for more details. There are two implementations of the interface, org.custommonkey.xmlunit.SimpleXpathEngine and org.custommonkey.xmlunit.jaxp13.Jaxp13XpathEngine. The first implementation is the only one available in XMLUnit 1.0 and uses the configured JAXP XSLT transformer. The second is new to XMLUnit 1.1 and only available if JAXP 1.3 or later is supported, which should be the case for Java 5 and later. XpathException is an Exception that will be thrown for invalid XPath expressions or other problems with the underlying XPath engine. It will typically wrap a javax.xml.xpath.XPathExpressionException in the Jaxp13XpathEngine case or a javax.xml.transform.TransformerException when SimpleXpathEngine is used. The XMLUnit.newXpathEngine method will first try to create an instance of Jaxp13XpathEngine and fall back to SimpleXpathEngine if JAXP 1.3 is not supported. One example of using the XPath support is included inside it org.custommonkey.xmlunit.examples package. It asserts that the string-ified form of an XPath selection matches a regular expression. The code needed for this is: Matching an XPath Selection Against a Regular Expression
      Using XML Namespaces in XPath Selectors Starting with XMLUnit 1.1 XML Namespaces are supported for XPath queries. The NamespaceContext interface provides a mapping from prefix to namespace URI and is used by the XPath engine. XPath selections then use the mapping's prefixes where needed. Note that a prefix used in the document under test and a prefix given as part of the NamespaceContext are not related at all; the context only applies to the XPath expression, the prefix used in the document is ignored completely. Right now XMLUnit provides only a single implementation of the NamespaceContext interface: SimpleNamespaceContext. This implementation expects a java.util.Map as its constructor argument. The Map must contain (String) prefixes as keys and (String) namespace URIs as values. Note there is nothing like a default namespace in XPath selectors. If you are using namespaces in your XPath, all namespaces need a prefix (of length greater than zero). This is independent of the prefixes used in your document. The following example is taken from XMLUnit's own tests. It demonstrates that the namespace prefix of the document under test is irrelevant and shows how to set up the namespace context. Using Namespaces in XPath Tests "; Document d = XMLUnit.buildControlDocument(testDoc); HashMap m = new HashMap(); m.put("foo", "urn:foo"); NamespaceContext ctx = new SimpleNamespaceContext(m); XpathEngine engine = XMLUnit.newXpathEngine(); engine.setNamespaceContext(ctx); NodeList l = engine.getMatchingNodes("//foo:bar", d); assertEquals(1, l.getLength()); assertEquals(Node.ELEMENT_NODE, l.item(0).getNodeType()); ]]> In some cases the "stringified" value of an XPath evaluation is a qualified name - a string that encodes a namespace URI together with a local name. There are two common formats for such qualified names, one used by Java5's QName in the format {NS-URI}LOCAL-NAME and one using PREFIX:LOCAL-NAME. Starting with XMLUnit 1.6 a new QualifiedName class can parse either representation. The assertXpathEvaluatesTo overloads where the expected value is a QualifiedName try to parse the stringified value in either format - using the documents namespace context when parsing the actual value. It is possible to set a global NamespaceContext, see for details.
      JUnit 3.x Convenience Methods XMLTestCase and XMLAssert provide several overloads for the following common types of assertions: Two XPath expression should return the same DOM NodeList as result: assertXpathsEqual. There are methods that use two different expressions on the same document and others that compare expressions selecting from two different documents. The NodeLists are wrapped into a surrogate root XML element and the resulting DOM Documents are compared using Diff.similar(). The opposite of the above, the expressions should yield different results: assertXpathsNotEqual. Two XPath expression should return the same "String-ified" result: assertXpathValuesEqual. There are methods that use two different expressions on the same document and others that compare expressions selecting from two different documents. The opposite of the above, the expressions should yield different results: assertXpathValuesNotEqual. The XPath expression should return an expected value when "String-ified" or interpreted as qualified name: assertXpathEvaluatesTo. The NodeList selected by an XPath expression is not empty: assertXpathExists. The NodeList selected by an XPath expression is empty: assertXpathNotExists. Neither method provides any control over the message of the AssertionFailedError in case of a failure.
      Configuration Options When using XpathEngine directly you are responsible for creating the DOM document yourself. If you use the convenience methods of XMLTestCase or XMLAssert you have several options to specify the input; XMLUnit will use the control or test parser that has been configured (see ) to create a DOM document from the given piece of XML in that case - using the configured EntityResolver(s) (see ) if any. If JAXP 1.3 is not available, SimpleXpathEngine will use the configured JAXP XSLT transformer (see ) under the covers. When using JAXP 1.3 you can chose the actual XPathFactory implementation using XMLUnit.setXPathFactory. It is possible to establish a global NamespaceContext with the help of the XMLUnit.setXpathNamespaceContext method. Any XpathEngine created by XMLUnit.newXpathEngine will automatically use the given context. Note that the JUnit 3 convenience methods use XMLUnit.newXpathEngine implicitly and will thus use the configured NamespaceContext.
      DOM Tree Walking Sometimes it is easier to test a piece of XML's validity by traversing the whole document node by node and test each node individually. Maybe there is no control XML to validate against or the expected value of an element's content has to be calculated. There may be several reasons. XMLUnit supports this approach of testing via the NodeTest class. In order to use it, you need a DOM implementation that generates Document instances that implement the optional org.w3c.traversal.DocumentTraversal interface, which is not part of JAXP's standardized DOM support.
      <literal>DocumentTraversal</literal> As of the release of XMLUnit 1.1 the Document instances created by most parsers implement DocumentTraversal, this includes but is not limited to Apache Xerces, the parser shipping with Sun's JDK 5 and later or GNU JAXP. One notable exception is Apache Crimson, which also means the parser shipping with Sun's JDK 1.4 does not support traversal; you need to specify a different parser when using JDK 1.4 (see ). You can test whether your XML parser supports DocumentTraversal by invoking org.w3c.dom.DOMImplementation's hasFeature method with the feature "Traversal".
      <literal>NodeTest</literal> The NodeTest is instantiated with a piece of XML to traverse. It offers two performTest methods: Node.ATTRIBUTE_NODE is not * exposed by the DocumentTraversal node iterator unless the root node * is itself an attribute - so a NodeTester that needs to test attributes * should obtain those attributes from Node.ELEMENT_NODE * nodes * @exception NodeTestException if test fails */ public void performTest(NodeTester tester, short singleNodeType); /** * Does this NodeTest pass using the specified NodeTester instance? * @param tester * @param nodeTypes note Node.ATTRIBUTE_NODE is not * exposed by the DocumentTraversal node iterator unless the root node * is itself an attribute - so a NodeTester that needs to test attributes * should obtain those attributes from Node.ELEMENT_NODE * nodes instead * @exception NodeTestException if test fails */ public void performTest(NodeTester tester, short[] nodeTypes); ]]> NodeTester is the class testing each node and is described in the next section. The second argument limits the tests on DOM Nodes of (a) specific type(s). Node types are specified via the static fields of the Node class. Any Node of a type not specified as the second argument to performTest will be ignored. Unfortunately XML attributes are not exposed as Nodes during traversal. If you need access to attributes you must add Node.ELEMENT_NODE to the second argument of performTest and access the attributes from their parent Element. Accessing Attributes in a <literal>NodeTest</literal> Any entities that appear as part of the Document are expanded before the traversal starts.
      NodeTester Implementations of the NodeTester interface are responsible for the actual test: testNode * method were all the Nodes expected. * @param forTest * @exception NodeTestException if this instance was expecting more nodes */ void noMoreNodes(NodeTest forTest) throws NodeTestException ; ]]> NodeTest invokes testNode for each Node as soon as it is reached on the traversal. This means NodeTester "sees" the Nodes in the same order they appear within the tree. noMoreNodes is invoked when the traversal is finished. It will also be invoked if the tree didn't contain any matched Nodes at all. Implementations of NodeTester are expected to throw a NodeTestException if the current not doesn't match the test's expectations or more nodes have been expected when noMoreNodes is called. XMLUnit ships with two implementations of NodeTest that are described in the following to sections.
      <literal>AbstractNodeTester</literal> AbstractNodeTester implements testNode by testing the passed in Node for its type and delegating to one of the more specific test... Methods it adds. By default the new test... methods all throw a NodeTestException because of an unexpected Node. It further implements noMoreNodes with an empty method - i.e. it does nothing. If you are only testing for specific types of Node it may be more convenient to subclass AbstractNodeTester. For example could be re-written as: Accessing Attributes in a <literal>NodeTest</literal> - <literal>AbstractNodeTester</literal> version Note that even though AbstractNodeTester contains a testAttribute method it will never be called by default and you still need to access attributes via their parent elements. Note also that the root of the test is the document's root element, so any Nodes preceding the document's root Element won't be visited either. For this reason the testDocumentType, testEntity and testNotation methods are probably never called either. Finally, all entity references have been expanded before the traversal started. EntityReferences will have been replaced by their replacement text if it is available, which means testEntityReference will not be called for them either. Instead the replacement text will show up as (part of) a Text node or as Element node, depending on the entity's definition.
      <literal>CountingNodeTester</literal> org.custommonkey.xmlunit.examples.CountingNodeTester is a simple example NodeTester that asserts that a given number of Nodes have been traversed. It will throw a NodeTestException when noMoreNodes is called before the expected number of Nodes has been visited or the actual number of nodes exceeded the expected count.
      JUnit 3.x Convenience Methods XMLAssert and XMLTestCase contain overloads of assertNodeTestPasses methods. The most general form of it expects you to create a NodeTest instance yourself and lets you specify whether you expect the test to fail or to pass. The other two overloads create a NodeTest instance from either String or a SAX InputSource and are specialized for the case where you are only interested in a single Node type and expect the test to pass. Neither method provides any control over the message of the AssertionFailedError in case of a failure.
      Configuration Options The only configurable option for NodeTest is the XML parser used if the piece of XML is not specified as a Document or DocumentTraversal. NodeTest will use the "control" parser that has been configured - see for details. It will also use the EntityResolver configured for the control parser if one has been set - see .
      Changes
      Changes from XMLUnit 1.0 to 1.1 XMLUnit 1.1's main focus was to add two features that have been asked for repeatedly: Support for XML Namespaces in XPath processing Support for XML Schema validation. In addition some JAXP features that have been added after the release of XMLUnit 1.0 are now supported - most notably XPath support - and all reported bugs and feature requests have been addressed.
      Breaking Changes XMLTestCase is now abstract. You probably have never created instances of this class without subclassing it, but if you did, your code will now break. You will most likely want to look at the XMLAssert class. All methods that have been deprecated in XMLUnit 1.0 have been removed. All methods that had been declared to throw TransformerConfigurationException or ParserConfigurationException now no longer declare it. Exceptions of these types cannot be recovered from anyway, so XMLUnit will now wrap them in a org.custommonkey.xmlunit.exceptions.ConfigurationException which is an unchecked exception. This change doesn't have a big impact on your tests, but if you tried to catch these exceptions they will now bypass your catch blocks. A new type of Difference (CHILD_NODE_NOT_FOUND_ID) has been added. It will be raised for the excess children if the control element has more child nodes than the test element - or vice versa. Prior to XMLUnit 1.1 a Difference of either ELEMENT_TAG_NAME_ID or NODE_TYPE_ID would have been raised if the control element had more children. The excess children were compared to the very first child node of the test element. Excess children of the test element were not reported at all. The schemaLocation and noNamespaceSchemaLocation attributes of the XMLSchema-Instance Namespace are now treated in a different way from "normal" attributes. They will be flagged as new kinds of Difference that is recoverable. This means that two pieces of XML that were different in XMLUnit 1.0 because they differed in one of the two attributes will be similar in XMLUnit 1.1. When comparing two elements that differ on attributes the comparison is now symmetric. In XMLUnit 1.0 if an attribute was present on the test but not the control element this wasn't flagged as a Difference; in XMLUnit 1.1 it is. In most practical cases this doesn't cause any problems since the two elements either have a different number of attributes or there are attributes in the control element that are missing in the test element - so the pieces of XML have been flagged as different before as well. If you are using DetailedDiff this change may lead to more detected Differences, though.
      New Features XMLUnit 1.0 shipped with rudimentary support for XML Schema validation (it worked with Apache Xerces-J but no other parsers). XMLUnit 1.1 supports Schema validation for any JAXP compliant XML parser (that supports Schema itself). You can also tell XMLUnit where to look for the XML Schema definitions. See for details. XPath support has undergone significant changes, see for more details. In particular XMLUnit will now use javax.xml.xpath if it is available (which also helps to avoid the buggy XSLTC version that is the default transformer engine in Java 5) and supports XML namespaces. Several new configuration options have been added, see . Treat CDATA sections and Texts alike. Issue 1262148. Ignore differences in Text whitespace. Issue 754812. Ignore comments completely. Issue 707255. Ignore the order of attributes. It is now possible to provide a custom org.xml.sax.EntityResolver for control and test parsers. It is now possible to provide a custom javax.xml.transform.URIResolver for transformations. New overloads have been added that allow org.xml.sax.InputSource to be used as a "piece of XML" in many classes. Validator will now use the custom EntityResolver configured for the "control" parser as a fallback. A new package org.custommonkey.xmlunit.examples has been added that showcases some of XMLUnit's abilities. It currently contains two classes: MultiLevelElementNameAndTextQualifier see for a description. XPathRegexAssert that provides a JUnit 3.x like assertXPathMatches method to verify that the string-ified value of an XPath match matches a given regular expression (requires JDK 1.4 or above).
      Important Bug Fixes ElementNameAndAttributeQualifier would throw an NullPointerException if the control piece of XML contained attributes that were missing in the test piece of XML. Issue 952920. XMLTestCase.assertXMLNotEqual(String, Reader, Reader) delegated to assertXMLEqual instead of assertXMLNotEqual internally, negating the assertion's logic. Issue 956372. XMLTestCase.assertXMLIdentical(Diff, boolean) delegated to assertXMLEqual, weakening the assertion. Under certain circumstances the reported XPath expressions for nodes that showed differences were wrong. XMLUnit could lose the root element or erroneously append an extra attribute name. Issues 1047364 and 1027863. TolerantSaxParser's logic in characters was broken and could cause StringIndexOutOfBoundsExceptions. Issue 1150234.
      Changes from XMLUnit 1.1 to 1.2
      Breaking Changes If XMLUnit detects that it cannot match a certain node (i.e. it encounters a CHILD_NODE_NOT_FOUND kind of difference) the XPath for the "missing" node will be null. It used to be some random XPath of a different node. XMLUnit.setIgnoreDiffBetweenTextAndCDATA now also sets DocumentBuilderFactory.setCoalescing. This has been done so that whitespace differences can be resolved according to the corresponding flags even in the presence of CDATA sections. Issue 1903923. Two protected methods in SimpleXPathEngine (which you shouldn't extend anyway) have added XpathException to their throws list.
      New Features The SAXParserFactory used by Validator can now be configured completely. Issue 1903928. A new class org.custommonkey.xmlunit.jaxp13.Validator can be used to validate schema definitions and schema instances using the javax.xml.validation package of JAXP 1.3. Depending on your JAXP implementation this may allow you to validate documents against schema definitions written in RELAX NG or other schema languages in addition to W3C XML Schema. See for details. DifferenceListener can now "upgrade" recoverable differences to non-recoverable by returning RETURN_UPGRADE_DIFFERENCE_NODES_DIFFERENT in the differenceFound method. Issue 1854284. A new callback interface MatchTracker is now notified on successful matches of Nodes. For more details see . Issue 1860491. It is now possible to have more control over whether the parser expand entity references or not by using XMLUnit.setExpandEntityReferences, see . Issue 1877458. New examples have been added: RecursiveElementNameAndTextQualifier - a more flexible ElementQualifier that fills the same need as MultiLevelElementNameAndTextQualifier See for more details. CaseInsensitiveDifferenceListener a - DifferenceListener that ignores case when comparing texts. FloatingPointTolerantDifferenceListener a - DifferenceListener that tries to parse texts as floating point numbers and compares them using a configurable tolerance.
      Important Bug Fixes If XMLUnit couldn't match nodes (i.e. it encountered a CHILD_NODE_NOT_FOUND kind of difference), the XPath expressions of the node details have been random. Issue 1860681.
      Changes from XMLUnit 1.2 to 1.3
      Breaking Changes
      New Features If XMLUnit doesn't find a matching Element for a control Element, it will match it against the first unmatched test Element (if there is one) instead of creating a CHILD_NODE_NOT_FOUND Difference. There now is a new configuration option compareUnmatched in the XMLUnit class that can be used to turn off this behavior - as a result two CHILD_NODE_NOT_FOUND Differences (one for the unmatched control Element and one for an unmatched test Element) will be created instead of a single Difference comparing the two likely unrelated nodes. See . Issue 2758280.
      Important Bug Fixes If XMLUnit couldn't match attributes (i.e. it encountered a ATTR_NAME_NOT_FOUND_ID kind of difference), the XPath expressions of the node details have been random. Issue 2386807. In some cases XMLUnit matched test nodes to multiple control nodes and then created a "missing child" difference for remaining test nodes even though they would have been valid targets for control node matches as well. Issue 2807167.
      Changes from XMLUnit 1.3 to 1.4
      Breaking Changes
      New Features xsi:type attributes now have their value interpreted as a QName and will compare as identical if their namespace URI and local names match even if they use different prefixes. Issue 3602981
      Important Bug Fixes XMLTestCase's and XMLAssert's assertXpathsEqual methods threw an exception when at least one XPath matched an attribute. Issue 377768. FloatingPointTolerantDifferenceListener expected numbers to differ by less than the given tolerance rather than "less or equal" than as the docs said. Issue 3593368
      Changes from XMLUnit 1.4 to 1.5
      Breaking Changes If one node in the comparison has children while the other one has not, XMLUnit 1.5 will signal a CHILD_NODELIST_LENGTH difference and CHILD_NODE_NOT_FOUND differences for each child node of the node that has children in addition to a HAS_CHILD_NODES difference. Issue 60
      New Features
      Important Bug Fixes RecursiveElementNameAndTextQualifier had some indices reversed leading to wrong results in some cases. Issue 62
      Changes from XMLUnit 1.5 to 1.6
      Breaking Changes In cases of ATTR_NAME_NOT_FOUND and CHILD_NODE_NOT_FOUND differences the value used to be the local name of the missing attribute or node. It will now be a Java5-QName-like {NS-URI}LOCAL-NAME string if the attribute or node belonged to an XML namespace. Issue 65
      New Features New assertXpathEvaluatesTo overloads in XMLAssert and a new QualifiedName class can be used to assert the stringified result of an XPath expression is actually a qualified name. Feature Request 25
      Important Bug Fixes The JAXP 1.3 based validator ignored xsi:namespaceLocation and xsi:noNamespaceLocation attributes. They will now be used if you don't specify any sources at all, but are still ignored if you specify any schema sources - since this is the way javax.xml.validation works. Issue 64 When an attribute cannot be found (a ATTR_NAME_NOT_FOUND difference) the XPath on the side where the attribute exists will now point to the attribute itself rather than its owning element. Feature Request 33
      xmlunit-1.6/src/site/0000755000000000000000000000000012451007364013313 5ustar rootrootxmlunit-1.6/src/site/index.php0000644000000000000000000000757712451007364015153 0ustar rootroot <xml-unit/>
      <xml-unit/> SourceForge Logo

      JUnit testing for XML

      For those of you who've got into it you'll no that test first coding is great. It gives you the confidence to hack around safe in the knowlegde that if something breaks you'll know about it. Except those bit's you don't know about. Until now XML has been one of them. Oh sure you can use "<stuff></stuff>".equals("<stuff></stuff>"); but is that really gonna work when some joker decides to output a <stuff/>, damned right it's not.

      XML can be used for just about anything so deciding if two documents are equal to each other isn't as easy as a character for character match. Somtimes
      <stuff-doc>
        <stuff>
          Stuff Stuff Stuff
        </stuff>
        <more-stuff>
          Some More Stuff
        </more-stuff>
      </stuff-doc> 
      equals
      <stuff-doc>
        <more-stuff>
          Some More Stuff</more-stuff>
        <stuff>Stuff Stuff Stuff</stuff>
      </stuff-doc> 
      and sometime it doesn't.

      XMLUnit allows you to assertXMLEquals("<stuff></stuff>", "<stuff/>"); or and you can do this assertXMLEquals(xmlFile, anotherXmlFile); and this assertXMLEquals(xmlStream, xmlFile);

      The list goes on and on. The Choice is yours.

      The current release is here -> XMLUnit 0.5, 10th April 2002 - Release Notes

      Javadocs

      Example

      Browse CVS -> CVS

      News

      Brought to you by, CustomMonkey.org and PrimeEight.co.uk

      xmlunit-1.6/src/site/example.html0000644000000000000000000007775312451007364015657 0ustar rootroot Example Code - xmlunit.sourceforge.net
      /*
      ******************************************************************
      Copyright (c) 2001, Jeff Martin, Tim Bacon
      All rights reserved.
      
      Redistribution and use in source and binary forms, with or without
      modification, are permitted provided that the following conditions
      are met:
      
          * Redistributions of source code must retain the above copyright
            notice, this list of conditions and the following disclaimer.
          * Redistributions in binary form must reproduce the above
            copyright notice, this list of conditions and the following
            disclaimer in the documentation and/or other materials provided
            with the distribution.
          * Neither the name of the xmlunit.sourceforge.net nor the names
            of its contributors may be used to endorse or promote products
            derived from this software without specific prior written
            permission.
      
      THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
      FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
      COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
      INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
      BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
      LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
      CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
      ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      POSSIBILITY OF SUCH DAMAGE.
      
      ******************************************************************
      */
      
      package org.custommonkey.xmlunit.examples;
      
      import java.io.File;
      import java.io.FileReader;
      import java.util.List;
      
      import javax.xml.transform.stream.StreamSource;
      
      import org.w3c.dom.Document;
      import org.w3c.dom.Element;
      import org.w3c.dom.Node;
      import org.w3c.dom.Text;
      
      import org.custommonkey.xmlunit.*;
      
      /**
       * Example XMLUnit XMLTestCase code
       * Demonstrates use of:<br />
       * <ul>
       * <li>XMLTestCase: assertXMLEqual(), assertXMLNotEqual(),
       * assertXpathExists(), assertXpathNotExists(), assertXpathEvaluatesTo(),
       * assertXpathsEqual(), assertXpathsNotEqual(), assertNodeTestPasses()</li>
       * <li>Diff: similar(), identical()</li>
       * <li>DetailedDiff: getAllDifferences()</li>
       * <li>DifferenceListener: use with Diff class,
       * IgnoreTextAndAttributeValuesDifferenceListener implementation</li>
       * <li>ElementQualifier: use with Diff class,
       * ElementNameAndTextQualifier implementation</li>
       * <li>Transform: constructors, getResultDocument(), use with Diff class</li>
       * <li>Validator: constructor, isValid()</li>
       * <li>TolerantSaxDocumentBuilder and HTMLDocumentBuilder usage</li>
       * <li>NodeTest: CountingNodeTester and custom implementations</li>
       * <li>XMLUnit static methods: buildDocument(), buildControlDocument(),
       * buildTestDocument(), setIgnoreWhitespace()</li>
       * </ul>
       * <br />Examples and more at <a href="http://xmlunit.sourceforge.net"/>xmlunit.sourceforge.net</a>
       */
      public class MyXMLTestCase extends XMLTestCase {
          public MyXMLTestCase(String name) {
              super(name);
          }
          public void testForEquality() throws Exception {
              String myControlXML = "<msg><uuid>0x00435A8C</uuid></msg>";
              String myTestXML = "<msg><localId>2376</localId></msg>";
              assertXMLEqual("comparing test xml to control xml", myControlXML, myTestXML);
      
              assertXMLNotEqual("test xml not similar to control xml", myControlXML, myTestXML);
          }
      
          public void testIdentical() throws Exception {
              String myControlXML = "<struct><int>3</int><boolean>false</boolean></struct>";
              String myTestXML = "<struct><boolean>false</boolean><int>3</int></struct>";
              Diff myDiff = new Diff(myControlXML, myTestXML);
              assertTrue("pieces of XML are similar " + myDiff, myDiff.similar());
              assertTrue("but are they identical? " + myDiff, myDiff.identical());
          }
      
          public void testAllDifferences() throws Exception {
              String myControlXML = "<news><item id=\"1\">War</item>"
                  + "<item id=\"2\">Plague</item><item id=\"3\">Famine</item></news>";
              String myTestXML = "<news><item id=\"1\">Peace</item>"
                  + "<item id=\"2\">Health</item><item id=\"3\">Plenty</item></news>";
              DetailedDiff myDiff = new DetailedDiff(compareXML(myControlXML, myTestXML));
              List allDifferences = myDiff.getAllDifferences();
              assertEquals(myDiff.toString(), 0, allDifferences.size());
          }
      
          public void testCompareToSkeletonXML() throws Exception {
              String myControlXML = "<location><street-address>22 any street</street-address><postcode>XY00 99Z</postcode></location>";
              String myTestXML = "<location><street-address>20 east cheap</street-address><postcode>EC3M 1EB</postcode></location>";
              DifferenceListener myDifferenceListener = new IgnoreTextAndAttributeValuesDifferenceListener();
              Diff myDiff = new Diff(myControlXML, myTestXML);
              myDiff.overrideDifferenceListener(myDifferenceListener);
              assertTrue("test XML matches control skeleton XML " + myDiff, myDiff.similar());
          }
      
          public void testRepeatedChildElements() throws Exception {
              String myControlXML = "<suite><test status=\"pass\">FirstTestCase</test><test status=\"pass\">SecondTestCase</test></suite>";
              String myTestXML = "<suite><test status=\"pass\">SecondTestCase</test><test status=\"pass\">FirstTestCase</test></suite>";
      
              assertXMLNotEqual("Repeated child elements in different sequence order are not equal by default",
                  myControlXML, myTestXML);
      
              Diff myDiff = new Diff(myControlXML, myTestXML);
              myDiff.overrideElementQualifier(new ElementNameAndTextQualifier());
              assertXMLEqual("But they are equal when an ElementQualifier controls which test element is compared with each control element",
                  myDiff, true);
          }
      
          public void testXSLTransformation() throws Exception {
              String myInputXML = "...";
              File myStylesheetFile = new File("...");
              Transform myTransform = new Transform(myInputXML, myStylesheetFile);
              String myExpectedOutputXML = "...";
              Diff myDiff = new Diff(myExpectedOutputXML, myTransform);
              assertTrue("XSL transformation worked as expected " + myDiff, myDiff.similar());
          }
      
          public void testAnotherXSLTransformation() throws Exception {
              File myInputXMLFile = new File("...");
              File myStylesheetFile = new File("...");
              Transform myTransform = new Transform(new StreamSource(myInputXMLFile), new StreamSource(myStylesheetFile));
              Document myExpectedOutputXML = XMLUnit.buildDocument(XMLUnit.getControlParser(), new FileReader("..."));
              Diff myDiff = new Diff(myExpectedOutputXML, myTransform.getResultDocument());
              assertTrue("XSL transformation worked as expected " + myDiff, myDiff.similar());
          }
      
          public void testValidation() throws Exception {
              XMLUnit.getTestDocumentBuilderFactory().setValidating(true);
              // As the document is parsed it is validated against its referenced DTD
              Document myTestDocument = XMLUnit.buildTestDocument("...");
              String mySystemId = "...";
              String myDTDUrl = new File("...").toURL().toExternalForm();
              Validator myValidator = new Validator(myTestDocument, mySystemId, myDTDUrl);
              assertTrue("test document validates against unreferenced DTD", myValidator.isValid());
          }
      
          public void testXPaths() throws Exception {
              String mySolarSystemXML = "<solar-system><planet name='Earth' position='3' supportsLife='yes'/>"
                  + "<planet name='Venus' position='4'/></solar-system>";
              assertXpathExists("//planet[@name='Earth']", mySolarSystemXML);
              assertXpathNotExists("//star[@name='alpha centauri']", mySolarSystemXML);
              assertXpathsEqual("//planet[@name='Earth']", "//planet[@position='3']", mySolarSystemXML);
              assertXpathsNotEqual("//planet[@name='Venus']", "//planet[@supportsLife='yes']", mySolarSystemXML);
          }
      
          public void testXPathValues() throws Exception {
              String myJavaFlavours = "<java-flavours><jvm current='some platforms'>1.1.x</jvm>"
                  + "<jvm current='no'>1.2.x</jvm><jvm current='yes'>1.3.x</jvm>"
                  + "<jvm current='yes' latest='yes'>1.4.x</jvm></java-flavours>";
              assertXpathEvaluatesTo("1.4.x", "//jvm[@latest='yes']", myJavaFlavours);
              assertXpathEvaluatesTo("2", "count(//jvm[@current='yes'])", myJavaFlavours);
              assertXpathValuesEqual("//jvm[4]/@latest", "//jvm[4]/@current", myJavaFlavours);
              assertXpathValuesNotEqual("//jvm[2]/@current", "//jvm[3]/@current", myJavaFlavours);
          }
      
          public void testXpathsInHTML() throws Exception {
              String someBadlyFormedHTML = "<html><title>Ugh</title><body><h1>Heading<ul><li id='1'>Item One<li id='2'>Item Two";
              TolerantSaxDocumentBuilder tolerantSaxDocumentBuilder = new TolerantSaxDocumentBuilder(XMLUnit.getTestParser());
              HTMLDocumentBuilder htmlDocumentBuilder = new HTMLDocumentBuilder(tolerantSaxDocumentBuilder);
              Document wellFormedDocument = htmlDocumentBuilder.parse(someBadlyFormedHTML);
              assertXpathEvaluatesTo("Item One", "/html/body//li[@id='1']", wellFormedDocument);
          }
      
          public void testCountingNodeTester() throws Exception {
              String testXML = "<fibonacci><val>1</val><val>2</val><val>3</val>"
                  + "<val>5</val><val>9</val></fibonacci>";
              CountingNodeTester countingNodeTester = new CountingNodeTester(4);
              assertNodeTestPasses(testXML, countingNodeTester, Node.TEXT_NODE);
          }
      
          public void testCustomNodeTester() throws Exception {
              String testXML = "<fibonacci><val>1</val><val>2</val><val>3</val>"
                  + "<val>5</val><val>9</val></fibonacci>";
              NodeTest nodeTest = new NodeTest(testXML);
              assertNodeTestPasses(nodeTest, new FibonacciNodeTester(),
                  new short[] {Node.TEXT_NODE, Node.ELEMENT_NODE}, true);
          }
      
          private class FibonacciNodeTester extends AbstractNodeTester {
              private int nextVal = 1, lastVal = 1, priorVal = 0;
              public void testText(Text text) throws NodeTestException {
                  int val = Integer.parseInt(text.getData());
                  if (nextVal != val) {
                      throw new NodeTestException("Incorrect sequence value", text);
                  }
                  nextVal = val + lastVal;
                  priorVal = lastVal;
                  lastVal = val;
              }
              public void testElement(Element element) throws NodeTestException {
                  String name = element.getLocalName();
                  if ("fibonacci".equals(name) || "val".equals(name)) {
                      return;
                  }
                  throw new NodeTestException("Unexpected element", element);
              }
              public void noMoreNodes(NodeTest nodeTest) throws NodeTestException {
              }
          }
      }
      
      
      xmlunit-1.6/src/site/index.html0000644000000000000000000001376012451007364015317 0ustar rootroot <XmlUnit/>
      <xml-unit/> Get XML Unit at SourceForge.net. Fast, secure and Free Open Source software downloads

      XMLUnit - JUnit and NUnit testing for XML

      For those of you who've got into it you'll know that test driven development is great. It gives you the confidence to change code safe in the knowledge that if something breaks you'll know about it. Except for those bits you don't know how to test. Until now XML has been one of them. Oh sure you can use "<stuff></stuff>".equals("<stuff></stuff>"); but is that really gonna work when some joker decides to output a <stuff/>? -- damned right it's not ;-)

      XML can be used for just about anything so deciding if two documents are equal to each other isn't as easy as a character for character match. Sometimes

      <stuff-doc>
      <stuff>
      Stuff Stuff Stuff
      </stuff>
      <more-stuff>
      Some More Stuff
      </more-stuff>
      </stuff-doc>
      equals
      <stuff-doc>
      <more-stuff>
      Some More Stuff</more-stuff>
      <stuff>Stuff Stuff Stuff</stuff>
      </stuff-doc>

      and sometimes it doesn't... With XMLUnit you get the control, and you get to decide.

      XMLUnit for Java

      JUnit.org

      The current stable release is XMLUnit 1.5, September 2013.

      XMLUnit for Java provides two JUnit extension classes, XMLAssert and XMLTestCase, and a set of supporting classes (e.g. Diff, DetailedDiff,Transform,SimpleXpathEngine,Validator,NodeTest) that allow assertions to be made about:

      • The differences between two pieces of XML
      • The outcome of transforming a piece of XML using XSLT
      • The evaluation of an XPath expression on a piece of XML
      • The validity of a piece of XML
      • Individual nodes in a piece of XML that are exposed by DOM Traversal

      XMLUnit for Java can also treat HTML content (even badly-formed HTML) as valid XML to allow these assertions to be made about the content of web pages too.

      Read the User's Guide (PDF or HTML) See some example code Browse the Javadocs Visit the Subversion repository

      XMLUnit for .Net

      NUnit.org

      The current release is XmlUnit .Net 0.4, April 2009

      XMLUnit for .Net provides NUnit extension classes written in C#, e.g. XmlAssertion and XmlDiff, that allow assertions to be made about the differences between two pieces of XML, the validity of a piece of XML, the evaluation of an XPath expression on a piece of XML, and the result of an XSL Transform.

      Please be aware that the .Net code base is not as advanced as its Java counterpart, in particular there is currently no explicit support for namespaces.

      News

      RSS feeds are available for RSS feedProject news releases and RSS feedProject file releases .

      An archive of the project news is available here.

      Brought to you by Tim Bacon and Jeff Martin

      xmlunit-1.6/src/site/xmlunit.xcf0000644000000000000000000047472012451007364015533 0ustar rootrootgimp xcf fileBB/ gimp-commentCreated with The GIMP3v New Layer#2     Nn333   4 L !6!"J"#^#-M/00I0{001- !¿¿¿¿¿¿ÿ¾~~ÿ}||¾~{zz~{xww?????????????????????????????????????????????????????????????~?|?z?w?????????????????????????????????????????????????????????????~?|?z?w?????????????????????????????????????????????????????????????~?|?z?w?????????????????????????????????????????????????????????????~?|?z?w?????????????????????????????????????????????????????????????~?|?z?w?????????????????????????????????????????????????????????????~?|?z?w      ༾ຼ߷ߴ߲߰߮߬ߪߨߥߣߡߟߝߛߙߗߕߓߑߏߍߋ߉߇߅߃~~||}zz{~wwx{}yvuuÿ{wtss¾}yurqq{wspoo~yuqnmmÿ|wsolkk¾}ytplihh|wsnjgff~zvqmhedd|xtokfcbbĿ{vrmida``þ~yupkgb_^^½}wsoie`]\\zuplgb^ZYY}ysoje`\XWW|wrmhc^ZVUUĿzupkga\XTSSþ}ytnje_[VRQQ½|wrmhc]YTPOO½zuqkfa\WRNMM~ytoid_ZUPLKK|wrmgb]WRMIHHĿzupke`[UPKGFFľytoid^YSNIEDDþ}wrmhb]WRLGCBBý}wqlga\WQKFBAAý}wqlga\WQKFBAAý}wqlga\WQKFBAAþ~xsnic^XRMHDCCľytoid^YSNIEDDĿzupke`[UPKGFF|wrmgb]WRMIHH}xsnid_YTOKJJzupje`[VQMLL½{vqlgb]XSONN½|wrmhc]YTPOOþ}ytnje_[VRQQĿzupkga\XTSS|xrnid_[WVV~ytpkfa]YXX½|wrnid`\[[þ}xtojfa^]]þzvqlhc`__Ŀ|wsnjebaa}yuplgdcc½|xtokhgg¾~zuqmjii}xtpmll{wspoo¾~zvsrr}yvuu|yxx¿|{{¾¿¿?u?s?q?o?m?k?h?f?d?b?`?^?\?Y?W?U?S?Q?O?M?K?H?F?D?BA?C?D?F?H?J?L?N?O?Q?S?V?X?[?]?_?a?c?g?i?l?o?r?u?x?{????????????u?s?q?o?m?k?h?f?d?b?`?^?\?Y?W?U?S?Q?O?M?K?H?F?D?BA?C?D?F?H?J?L?N?O?Q?S?V?X?[?]?_?a?c?g?i?l?o?r?u?x?{????????????u?s?q?o?m?k?h?f?d?b?`?^?\?Y?W?U?S?Q?O?M?K?H?F?D?BA?C?D?F?H?J?L?N?O?Q?S?V?X?[?]?_?a?c?g?i?l?o?r?u?x?{????????????u?s?q?o?m?k?h?f?d?b?`?^?\?Y?W?U?S?Q?O?M?K?H?F?D?BA?C?D?F?H?J?L?N?O?Q?S?V?X?[?]?_?a?c?g?i?l?o?r?u?x?{????????????u?s?q?o?m?k?h?f?d?b?`?^?\?Y?W?U?S?Q?O?M?K?H?F?D?BA?C?D?F?H?J?L?N?O?Q?S?V?X?[?]?_?a?c?g?i?l?o?r?u?x?{????????????u?s?q?o?m?k?h?f?d?b?`?^?\?Y?W?U?S?Q?O?M?K?H?F?D?BA?C?D?F?H?J?L?N?O?Q?S?V?X?[?]?_?a?c?g?i?l?o?r?u?x?{???????????   uvy~sstw|qqruz~ooptx}mmnrw{kklpuz~hhimrw|ffgkqvzddejoty~bbdhmrw|``bfkqv{^^`djoty~\\^chmrx}YY[`ekpv{WWY^diotyUUW\bhmrx~SSV[`fkqw|QQTY_djpu{OORW]cintzMMPU\aglsx~KKOTZ_ekqw|HHLQW]ciot{FFJOV\bgmtzDDHNTZ`flrx~BBFLRX^ekqw}AABEKQX^djpw}AABEKRX^djqw}AABELRX^dkqw}CCDHNTZ`flrxDDEIOU[agmszFFGKQW]ciou{HHIMSY_ekqw|JJKPU[agmrx~LLMRW]cintzNNOTY_ejpv|OOPU[`fkrw}QQRW]bhnsy~SSTY^djouzVVW\bgmrw}XXY_dinty[[]aglqw|]]_chnsx}__aekpuzaachmrw|ccejoty~gginrx|iikouy~llnsx|ooqvzrrty}uw{x{{~ध৫߫$??????????????????????????????????????????????????????????????????????????????????????????????????????????????????߯߳߸߼  Iy$< Background     4P4pvvv4Rw3IȂeI`l&9B^$begkvl.ptv75555555656666-,++**))(''&%$$#"    ¿¿¿ߛ¿ܜ¿۟؝ѭ׿ϩzzzzׯyyzzؿzyyzzՕv{yyzzs|yyzzÿzzyyzz{yyzz75555555656666-,++**))(''&%$$#" ¾󺻼   ýľƿýſ¼~||}~þ}yuropqtx|Ŀ~|xrmgb^^afntĿ~{wqkd]VRMNQWbkĿ{vqjc]VNGB=@BJTaÿ}xqjc]UNF>84/14:FT¾{tle\UOG@80*&""#(2>zrg_VNHB:1*#-8BypeZPIA;4,$ 0?JVzpaVLB;4-&':JVcĿzpbRH>5-'  (4ASdtǻyoaRD:/'  )=FUj|ƵyobRD6,"  +ARcuxijzqbRC6(  +FVr{zscSC4( )H]tzz{uhVE5'  %%(H]vxx|vkZH6' %++'F]sxx}wo_M:)  #)00+A\fzvxxǿ}yrdQ?,  &.45/=JI^uxxǾ}xsiWE2   (1794757UtwxxȽ~zum^K9% "+39<93Q2GowxxzvodR@- $-6;?>8F_?cvxxɹ~|xtm^L8#"/7>CC?=^aRtwxx|75555555656666-,++**))(''&%$$#" ¾󺻼   ýľƿýſ¼~||}~þ}yuropqtx|Ŀ~|xrmgb^^afntĿ~{wqkd]VRMNQWbkĿ{vqjc]VNGB=@BJTaÿ}xqjc]UNF>84/14:FT¾{tle\UOG@80*&""#(2>zrg_VNHB:1*#"-7ypeZPIA;4,$ %0;zpaVLB;4-&%1<ĿzpbRH>5-'  ",7ǻyoaRD:/'  )ƵyobRD6,"  0EijzqbRC6(  -BRezscSC4( !:Zghi{uhVE5'  #D`kgg|vkZH6'    &8egg}wo_M:)  !$+Xggƿ}yrdQ?,  YggǾ}xsiWE2  LggȽ~zum^K9%  =ggzvodR@- !-cggɹ~|xtm^L8#  "Pgghj(&%$#"!!              ¾ 庻 񲴸 渹 𱴵ﰲ¿갯띟iOᛝׯLOÿ֚sꡟF4)궲p7 >½m% eY;7' HeɯwaRLE<* ^y̲NҰida^YOB, GC;Ȣzuqmhd`XI1 +mϢoyyz{{vrnjd^R5 =,-ʆyyz{|xsojcY;"aȱszzyyz{|xsng_EDsW+`zyz{|wqlg@9zzyz{ŸzzyɼOzyzŹM szȾ򘙜 9µ% :(&%$#"!!ý · ·Ľú·Ľſ Ľ Ļ ķ ° Ƽ ò ǻ « þ » »ý| ޽} 漽} Ƽ浶~ ú箰~ 権 ʿ墣 ɿ䝟 䘚 »㔖ž򏒕󖡫ĽºzžrxiOükqxԮKOciqxnžOZfqx:3ü$EP^itS#6]_WLT`kqvwvw^6 džrlroi_TX_fi; FeýcyytmcT( [w¨@c8  E?0|T>4,$ (fƛi~vsfRA8/(4*+~}xxwvukYE:1(Nw]xxwvulYE:1&SP(MxxwvulZD9."4Cgx}Y:-xxwvuiTIapiP]nvzxxwv}oWSdpswz}~~?wxx헶t_JYhlostvuttx|~~xh~x~yhON^bcbba_]\ZZ\^aabeglry9 Z~}||~}x~lW>JOMLJGDB@><=>ABCGKQXaT ,~{zzy{||像n[<6=<951.*'%""%),28@HR] -|zwxxzy(&%$#"!!ý · 񄋗 ·Ľú·񏌇Ľ񏍈ſ Ľ Ļ󍇂 ķ󌆄 °󊅇 Ƽ ò ǻ « þ »~ »ý| ޽} 漽} Ƽ浶~ ú箰~ 権 ʿ墣 ɿ䝟 䘚 »㔖ž򏒕󖡫ĽºzžrxiOükqxԮKOciqxnžOZfqx:3ü$@DP^itS#6ELLJT`kqvwvw^6 džrHRTTRQX_fi; FeýcFTXYZYWN' [w¨@8HV]^D  E?0GJD. (fƛi~aM%  4*+~}e_L5  Nw]ggbO6  SP(MggdR8  )Cgx}Y:-gg_J2 >UWO]nvzggeWD)!Iac]XOSdpswz}~~?wgge[kvjVZa^XPIYhlostvuttx|~~xh~glrZUZ]XQGN^bcbba_]\ZZ\^aabeglry9 Z~}||~}govbQTZWPG>JOMLJGDB@><=>ABCGKQXaT ,~{zzy{||hokfdgihfZQORULC56=<951.*'%""%),28@HR] -|zwxxzy>;:988777666)%     yI1IbީUUPjBn֧0 z5 f*BѻG 0  J -z. `0   ?_t   .Sft $Dam< 8[is*C3LL .TfpxXɝ,0۪7 +Lcnv| B sO'Gamu|zp  :Falt|zyM  kalu|zyy: S}+Ƭmu|zyy {풽Ѷv|zyy  ,x,ٽ|zyyzn euf󵴴߿yyz\  O ߱yyz9  ﮭ= 񮱸ޛyyzz>;:988777666)%     ȽõȾwG/HaީUULe¸?fӥ. x5 X':̶D /lI'{*]һ/b`3 %.20 @1JJ+; S+/֣-(8Ki ? p? $5FdY  $3Eau:񘮰e }9 #4Dbuv+Jt'蹶tY%4E`uvxo釱~k~n6Gduvxx"k$ql|Kfuwxx~||~UUfXuerjuwxx{zz|}EAvufgyvwxxyxxz{).sgaoЕxx>;:988777666)%     ȽõȾwG/HaީUULe¸?fӥ. x5 X':̶D /lI'{*]һ/b`3 .3  @1JJ S+/֣-i ? p?  Y  :񘮰e }9 7+Jt'蹶tN 7So釱~jpT =Ye"k$qgmsV ?^gg~||~UUfXuegmquT H_gg{zz|}EAvufaglpsn?-Nfggyxxz{).퐏sg^chkprkjOQegg6332122211    ~Y@) Pf`P7 ike\E* ojbT7 qmf]C' spjbR19zvrmeZ; O¿{wtoi_F(y¾¿|yvqkbP1Ŀþ|zwrmeX:½ž{{xsog]A4þ{{ytpj`I[ü{{yuqkbRož{{zvrldXݽ{{zvrme\յü{|{yvrng_䳲q{|{yvrnhwԫüMq{{zxurniž= u{|{ywurnj {{ع|{yxvtrnjŽ {yٯ{zxvusqnjǿn zyٛzxvusqpmsӀwú] yyڅxvtsrpolƽ? yyyvtrqpnmjȿ/ yyݻwtrponmki· yy߻vspnmljifĺz zztqnljigfdǼj zzroligedc~ʦʾN zzuqmjfdba_5 8^hpuxzyB zztplgca_][5[gpty{zö zztoje`^YWS2Zfotx{ŷ6332122211  » Ļļƿ÷Ĺh`m{ô)K¯ dƿ!Ŕ(ün-%ľ~}c2+-x}{{Ľ>6/$Aüu~}|~󲙃A:3( jſ}q}v뭑˜F=6-"{¼zm|yw⤇ˑJ?80%嵹¼xgx|s{֛~{MB;2(vds~yq̏{pOC<4* 1uan{}pxZPD=5,!>t_gwun}xɭYOE>6.$U}r^`rzms}zşWMD>6/%lyp]Xm}}qku}Ä}UKC=7/&p{sn\Qgzujmx{YlƻwPGA<60'!iy}wpk\L`v|yogqvz|~~|}8,VLD?;50(-apyrkg[KZrz}tglpuxxyzwxx'6vȼPF@=94/(L\hx|tlfc[LTmxxmflruvvwutt @ZĵJB=:72.(SWaoy|~|tkd\[XLNiu~rfgnrsttsrpSkͿzD>:741-'cSZhruwy|~~ztkc\UTSJHesxlcknqrrqoGiu˺a?:642.+%%^OUampruwzxuog_XPOOHDaq~sdgloqpnm:uvɴJ;731/,)$3ZJQ]gjnqstroib[TLKKGC]pyibhmnnlj!vwů=73/-+(% ?UGO[ehkmnpmkf`YSMLMKEZoo_chkkifxǝ|:4/+)'$"BPFNXbcfhijigd]XSNNOOHWnvc_dhgfaxǝվy60,'%" KJELU\^`adeffd`\XUUXWLVm{hY_ccaBxǝһY3-'" LCBGOTUX[_bdeedb`]_a^PTlnYY]]V-xϷM0)#!D;>AGLORW[`dijjigihcTSk~s\QVUCxʹ6-& :69:AEINU ,4;?CJR +4<@?wcI>.xǯ~2( (/*+,28>FG )3 -wyy{ Aüsww~󲙁 jſ}otvxu뭑` {¼zkqtwsw⤇~{{|~P 嵹¼xfmqusq{֛~~{xuuwy5 vchnsrqq̏x{uspqru( 1uacjppmxx~|upoolnq >t__fmopmn}wy}vpronikY U}r^Ybilnmjs}tz~xqoxsmghM lyp]T]eimmiku}v{~|rnwvldf- p{sn\OXafjkigmx{YU}wnxvibd. iy}wpk\LT^bhjjggqvz|~~|}8s{qwve`c $apyrkg[KOZ`fhihdlpuxxyzwxx'Eussa`Z B[hx|tlfc[LLV]dgiieflruvvwutt vn^`H HUaoy|~|tkd\[XLHT[bfhifcgnrsttsrp ji`b@ WQZhruwy|~~ztkc\UTSJEQYaehjhecknqrrqoG vebd1 RMUampruwzxuog_XPOOHCOX`ehkif`gloqpnm:5u{deg "NIQ]gjnqstroib[TLKKGBMW_eikjibbhmnnlj!7Kǿthjk /KGO[ehkmnpmkf`YSMLMKELW_eilljd^chkkifU_¬plmP 4HFNXbcfhijigd]XSNNOOHKW_eillf^_dhgfadenprP AEELU\^`adeffd`\XUUXWLLW`fknmmf^Y_ccaBgnjоrtu3 D@BGOTUX[_bdeedb`]_a^PLW`gknnmg`UY]]V-gϺ|uww( ?:>AGLORW[`dijjigihcTMW`gknnlf_UQVUCgͷywzy 969:AEINU  le^TJNB- gʳwy}n &51349>CJR kc]RE>.gǮtw|W (/*+,28>FG 2gaZP;'3-*'%$"!   þYC.Er\ &@Ychvxyyxvsoê^  ٪ -E]dxy||{y՞S. ג 1L_z||{{|ԉlcM+ Ⱦd ,Ay||{{z{{upj_D (L Kx{|{zz{|ytog]7 < Vvz|{zz{|{vrldN, bվ otx|{zz{|xtoi_E$ uԏ mrx|{zyyzz{|yuqlzz_+ Հw ۷lrx|{zyyzz{|xT I (ܜlrx|{zyyz{{|ĺu 8 Totz{zyyz{ĸ@ J {rw|{zyyz{¸s m̟ (qv{{zyyz   5w|{zyyzļ D 'D|{zyyz 'C 0}zyyz{źצ 7 ;yyzǿ g &Kyz{q3-*'%$"!ðȶĨ˼ūɹèDZ˾į ʴ Ƨ ʸư߹èȻ~y{~~zttǼxpȼǾrswR?,BlY[L8;<=;61(umsDҢt復:>BEGFA=KH󍺽Њ{>CKRWWQKaD#\鄎JXdihfYL@7." Uз)2;GWdlki^QC:2) dΈĽ%0:GXfnml_RD<4,$'$qľg$/:HZipnl_PC;Jktcbtu=CýK#/;K]lqol]NB{xiZiz\,=F)3?UdosoiYUvgZiy.9]2.8F^krsnfUtd[hvZSƘc.7BUiqtsk`~n__lxk€s9CUesttsgfyh\do{v?HYhruutpa~q``ju}q{=(ʂdouuvusiYuh_gpz}kv;uvvuqc]zlafmw|}l1ft}avvtjZz}oadlt{|~]3-*'%$"!ðȶĨ˼酆ūɹèDZ˾į ʴ Ƨ ʸư߹èȻ~wwvtqtǼ}||}|{xtqpȼǾ~}zwrmswR?,BlY E+slmsDҢ`)vr=󍺽Њmm8m& \鄅`7!  #E'8  3@7   Uз}   dΈĽ~t    $qľY   'Mhc\btu=Cý>   Uqmid^Ziz\,=4 !pwsnid^Ziy.9]  u}xsojc][hvZSƘc  Q}ytnib[_lxk€s}g  ~ytnh`[do{v?zk  T~xsmf]`ju}q{=(; % |vpib_gpz}kv5('30- !ztme_fmw|}l1ft}S069;1& J{ung`dlt{|~]/2322111////.--,,+#" ēѤږyD 1G a/F &[ $4΢ "0@ɇD  /=LY侽2 ,=<. \ Dka`[N<( 7ӥ ec`YE1 i ,zN/  789;=kJ &>X_2& =; %8N^p[TLC:3+#F,AWafjfeddba_\W隝%:S_eknppqqponút4O^ejnqsuzz{||7{Xchmprtvw{{zzyŃ6{cimprtvwxzzyyX蛧yjnprsuvwzyygdhlmoppqr{{zzyy/2322111////.--,,+#"Ŀľüú ƾöĽƿ´‘ē̠wAF9.C¾YWzd¿ś|{>T*-¼=$.þ;;:98754X`½dž[¾B럠Aa6<F la {XlyxwO S;)UWYZ- s~{s "##$  G}|vndZ  *}{umdXN9&+  |xqg]RE8)6@7% !',16bypfZNA3$9FNROKA62 %Im}sh]QE7)JRVYVTNF> (HhwmaUI@ADFHIIGKQZfkh`\q"ZNA6*%+047:tx|~}|]SY^dgknpr}vm TH;/# %+/368;z~achmrtuuꀖ~4OA4)'+/2479P~gnrtuuvvwƵ:I;-!#&)+,./OvwwurnjdWU]djosuuvY/2322111////.--,,+#"Ŀľüú ƾöĽƿ´‘ē̠wAF9.C¾YWzd¿ś|{>T*-¼=$.þ;;:98754X`½dž[¾B럠Aa6<Fla {XlyxwOS;)UWYZ- s~{s "##$ G}|vndZ*}{umdXN9 |xqg]RE8  !',16bypfZNA3$%Im}sh]QE7) (HhwmaUI ȺQ  x1 Źǿv ?<;::988767654.,Ƶ*ë)ɼƶİӨ¬r hĽ 9ļ }ù 8ý ƿ0ÿmľܻĿ_þjƸĽ\ľ2ž$HpſHſKndM㻾)@OS󉭪/ (ǪK 'A󎝚}~&D~zvtstA &2{wrljefioA(|T~ytof`ZVVZ`lvEnzunhd]VOJGGLQ_lv~Iq惘ld[TQMHCA=;=CR^ju|Lo*\QF@?<:762.-/;I\jt|KjmិZJ934456762.47MXadZGZpz|n\I\muF j{ͼrXFINU]_^MIeq}{shYEWjt|f u?<;::988767654.,Ƶ*ë)ɼƶİӨ¬r hĽ 9ļ }ù 8ý ƿ0ÿmľܻĿ_þjƸĽ\ľ2ž$HpſHſKndM㻾)@OS󉭪/ (ǪK 'A󎝚}~&D~zvtstA &2{wrljefioA(|T~ytof`ZVVZ`lvEnzunhd]VOJGGLQ_lv~Iq惘ld[TQMHCA=;=CR^ju|Lo*\QF@?<:762.-/;I\jt|KjmិZJ934456762.05MXadZGLTX[[ZXSMI\muF j{ͼrXFINU]_^MDNRVWVUTQNJDWjt|f u     Զ ºüüļýýԣ¼»½ÿʰ¿    þîʿɼɼĩ߸̶÷٧ôә߷ŹΑƾˉǽLJƼŅŸ…òǽĵƽïŴ Ƹ ȺƻĹǾ¸ƾ·ƻ~~ɿ|xx{tpqtùvmjjmĺskecdhĻzfa\Z\axm[XTTW[ƿvmaRPNOQVϒľvkaUHIIJLPukaRI?BEFIM¾viZL<848=BEKzmXHCr`G-!)2;@鴲}p`G+"+35    þއîʿލɼቇɼ툆ĩ߸̶÷٧ôәŹΑςƾˉǽLJƼŅŸ…òǽĵƽïŴ Ƹ ȺƻĹǾ¸ƾ·ƻ~~ɿ|xx{tpqtùvmjjmĺskecdhĻzfa\Z\axm[XTTW[ƿvmaRPNOQVϒľvkaUHIIJLPukaRI?BEFIM¾viZL<848=BEKzmXHSoiivxx~~|{zwusndS>'-;CINNHEhltwx xӶ|zywvusok^K7(9EMSXSK]lvx xѵ|zwvtrqpleVD/2ALV[^YUvx xβ|yvtsrpnmf]M;& "9FQ\bgbgxxϬ|zvspomljg_UE2)>KVbjsi|͏xxw|yurolkifc_VL<)0BO[fovmŠxxw|xurnkhdb_\WMB2! 7FR_ksyv̯xxw~{wtqmie`]ZWSNC9))71$ 0?MZexù{vrolifc]WPJDB?<95-& '9ER^m~ȼ}ytplifc_ZTMFA<963/)! !2@LXdx}r|xsnida]WRLHB;72/-)$  ,9FQ\n~Ǿ}|xrkb\VQOJD@<73.)&"  (5?KVbx~ľpw||~|zwog]RLGA@;841.*&! &2;DOZm`gosvtrlcZOD>8420,)'$! &08@HRauQYbejigbYOC81+('%#    (06=DLXk|BJSX]]\WME8-%  %,17CJVeu*3AFO]kx~!*38==;6,"  '17:=?@BDMZgt}}~~ ).320)   &08=@CDDFKXft#'%" )07?DGJKKOYfs   !#%"! "'-48?GLQVY[`ht   (-/0.,,/49>AHOUaijmqx댉 '.69::989:=@DKOU[epwz{|~򊍐 ",5;ACCDFILQW]ckv~ ")08?DGIJJKLORVZ^cgs$*05:@FIMNOPRTY]bgkov󓖘!#*07=AFKNQSTVY]chnsz򖗙6/),39@FKPRUWZ]aflryHD>539?FMSUY\_bhnvSQKB8;BIPY\^bfls~][XPE>DLT\bekqzfeb[PEGOXbjpzomkeZOKWbnzwvtoi^T`m|~~{xocgwtm¾u}¿ˀ¿êDZ|¿ƭÿʳᲱ铊ɴ~ʷ~ü ˹Ƕǽ ˼˿Ż ȳĻ Һ Ʋ Ѹ ޿ ̳ʸ}{xurjYE0 '# 5ggi~|{zwusndS>' #)Og gҶ|zywvusok^K7 #'.cg gѵ|zwvtrqpleVD/ ! $Dg gcβ|yvtsrpnmf]M;&  !# \nigg[Ϭ|zvspomljg_UE2# !)lusqjggQ|yurolkifc_VL<) #*)>}|usvulggM|xurnkhdb_\WMB2!  !! ,95U|tux|zlggJ~~{wtqmie`]ZWSNC9)#&:GBhysv{~wngK~{wtrolg`\XUROJE9.   " 4HUPwuuy~}zwW|xuqolje_WSOMJGA;0# "%ATa^zsx|~{zg}yuqnljfaZTMKGDA>71$ " 2L`nktvz~|{p{vrolifc]WPJDB?<95-&  ! (@Xkyuķyty}~|v}ytplifc_ZTMFA<963/)!  !!5Lcu{~sw{}{|xsnida]WRLHB;72/-)$   /BXn{}x˺uvy~~}}|xrkb\VQOJD@<73.)&"  *>Neu}x¬ztx{~pw||~|zwog]RLGA@;841.*&!   :K[nx|z}ʴtuy|~`gosvtrlcZOD>8420,)'$!  0IWfrx{}wɽxsvy{}QYbejigbYOC81+('%#   %?Takrwz~zz°usvxyBJSX]]\WME8-%    5L_inqvy}}xwstv6?GLRSRLC9-"   )AWhmmqux{~|wxt*3d{tnmprtuwwxxyzz|~w !#$&&$")?Z}|tmnqsuvwxyyz{}~w  "#%%# '1;K`wxpmpsuwxyz{|}~w## !##"!1?JU^hwtloruwyz{|~~w''&#%;M^mx{pnqtwyz|~}w**)&!##()&=c|xrnnqux{}|wjL,&*!&>i{upmnoprtuxz|~}x|tZ:"+%7dzqmoqsvwxyz{}~{wxhH!)(.]wmorvxz{|~~|xxysV*&+ 'T{|x|{ooswy{}~{ywzye9#+"$Joqmonms}tmqvy{~~|zxw~{rI")$"Adqptwvtpmrxmptx{}~|yww~|{zZ-&%":\msy{{xuqmt|wpnrvy|~~|ywy}||i=#&#5U|zow|~|ytpmsuvsnmptxz}|yvz~}}wL %$4Rzuqy~}zwtponnpqtwz|~~zwy ~}~[!"&5Pxosz|}zwuttvxy{~|yw ~i,!'8Mwmu|{wxz}|{yy{|~}zwz w<"':Ktmv}~x}{zvz~~}}~~{xw ~}L"(Qlyow}zzwx{}~~}{yww z|}}|o2+@Uh{vpx~y³yvwxxwvwz xy{{xG,AWis{spx~x˿ vwyyzx[)BYklx~wqqx~xľƼ y·o Nyy 온yyz{|ɿ¼ Lyy{|ywuʾý ٟyz{zvsqppʾľ&Iyz{zurnmlmo;ݑyz|ytpmjffhkn¿;Hyz{zuqmidcbdhlq|^Րyy{{wrnic`]]adkps}vGz|ytokd_YUX\dkpsuÿh`{wrmgaZQPP^dkpsrqƿBFuojbZMFBS^empqƾN،{g_TH?KV`hmo¾FHpYJ5/(&#%)1:>AIHF@5!%,03551," FRUX[]`bb_udL=3,$ (1?LPQND7%(-25884/#  NQTWZ\\YugP?5-%#,?OWWOG6).158:6." BNQSUUSQNulWC8/&  !+2GXWQE7/157:6,  JNONLIDB_J=3* !*24DTRE:23685*  >HGD@940gB8/% !+0/-YQG<1354( @<5-%uR4*$+.5a\SH7122"5(10&znM+ %)@daZP@036==0" #  |qgA%Ghf`WI136JC7)   !uk]/Rggb[N50TLD8+ !*}scT1  ]egc]Q)WULD8+  '0Ŷ|g]aN ]aca\@  VXULD9+   %.6ê|d^c]AOV[ZQ HVVRJB7*   '17<˰t]a\TL:AIQM' /PTTRIA7+!;=@ʫeRPNI@7;> !HQTTPH@7-&!!" ;EGmMHIF>80&"  >IRTROG?8/+**0491LM׮tWBB=80+.10% '*,.2:CKSUSOHB=879;@EHLPRSSRQPPQ—{dJ;>;82,'(("#)-/16?HOVWSPJDBABFJOTX\]^]\[YXWVV̭mX=::940,'%%&,248=EMSZZVRMJIJMRX^cfijhfb`^][[ZZX{fN89;730,*,-39<@DLTX^]YVTRTV[afmtvxupid`_^]]\[Xs^D8<:8520129?BGKSZ^a`][[`cjt~ung`_^^]\XnY?;:98657:@EJNSZ`beeddefkp|{rjd^]^^`_^[Vž~iQ:;;:=@GMQV[`egiijlos{wnea\]^_aa]ZTͽxcG8:<=>CFLQVZ`dikmoqtz{tld^Z[^_aa\YSͳr\B9HGD@940K$  @<5-%X6"  "   10&fH)#(' #"  # tZ?% &+($ %$!    }mU=$#)-)&! %%"   }lW>%%,.)#  #$#     ~~}nX>$#)(#!"#"    z{{zpX?&$     wxxwq]<$   stuuts_C$!   |uttsjC%"  zxxwuti@%(% ե|xuti=,*" !! {utug3&.&    ##""#"!  |vtvx^)*+$   "#"!! !! vtvy{V#-(! !""! "#%"  ! Ƽxtuxzx?&/'!##$##$$&%!  ·ytuwy{f)*-% "$$%%&&'(&   ǽvsuwxzR!.*#'!#%&&'())&  ø|ustvwv>$/(  !#$&&()**&!"1.*ʿ~wssuwf)(-& "$%&()*)&!'@J>5#ɾytsuwS,+% ##%'(*)&",G[`M=)ȽzttuvA"-)" !#$&()*&"*Pfuv]F0%xstui-%,(" "$%'((&"(NqlP7* ·wstuV&*&!"#$&''%"#Kq|\?0%xussA'&#"$%%$# CmjI5+!Ǽ|xj,%!4bxT;2& øc !+P~bC8-$  a21% "BfrOA6+%""#""##$!aHF8) 8Xz\H>3+%$$%(+.12ʜ^[RA1  !;[}뭫lUK?5-*&'+/38:ːmgXF2"  "=]~waWKA50''+18=Bױzuo[F3! 4Tvykg[OC9-)+/:@E˗w}q[F0&Bcwnsl`RE5-+,7?E챁z~qZB,3PrunuyqdS@5/,4=DƗw|rYB.!0C`yzopv{ygRC:37:EӬ|ypV?'#'6G[jpv{~{qmrw{ycUMD:9Bٽv{zcL2.*2@Qcppomnponnpuy||trnfNEK̟~x|}lV>;7?KYkvwusqpprtx{~zwnVUҭvy{q^IIGNYet{{zyxvvuwxz}y|vbڹ|vyrcRTU\gr}~}|{zz{{}|wsĨzvuk^_`fox~~򀂃zw̳zvsmnosvy{|}{vѽ|}}{zywvwyz|~}zw|Ⱦȱ}xvwxzzyxvĸо⪬zyyzɼſ̾ξ-н;θ;̳;Ũ:һz >ޫ Eސyzz  Pܫ`yzzꠟ| sᤣ&/:zzk  r&.6AMY߾zz7  ݣH#+3HQZcjnrwsi_[ey|{|}~}{{|UPLHA;3( #(/8BKU^fkotpf\Wj||{{|~TNID<4+  #'+2;FOYbimqlcYUo}~~QKE>6.$   $(+08CMW`gjoibYZxOHA91'     $(,06@KU^ehkf`ZaLE=5-#     %).5@KT]cefb]]mKD:2*! !%+5@LU]aan\[`}KC91*"#! !   +5AKTY]\[Y^oNF=5/($ $*.20+%!   *5@INTWWX]kPIA93,)'+29=A?;6.('&&'(''%#!)37889;<=<<:976430/.-,-/6EMUXZWSOJDDEGJLMNNONMMLKJIHGFGGJMXfwŽliaXMEDJRZabda]YUOPQTWY[\]]^^_`_abcfjt}ɾstpk_TRX_gkmnkgd`[\]`cfikmnppqsux{ȸ{z{yoghmty{xyvqnkiknsx{ҽ}|~yw{}||}~}}ƶ˹мкꤦ|κŹ{˻˿|нȫ¹в»Ÿﻼθн˹ķ ͻ Ӿ  ÷ƹʼ̽;ο̿ɾƾĽvxz /-wnd[_dgjolgqmggtrrsvw?4oh`Z\adgknidxggonmns[[~|`@XWZ_aegkidcggmjhgmKw}{yxOCY]_bdhid`hƨggheb`d"~zxvtq$Z]_adge`\mǓggc`\XZ*}{yvsqlg  \_bdd`[]uLjgg]ZURQ)~}zxvspke_ )^aca]Y\rggWSOK@R|}~~|zxvsple]M E_a^ZW\sggLIFC)\xz||{yxurohaY6 E_\YUYmgg@=;9quwzz{zzxurof^W" ][XUTdzgg.--,  jnquvxxwurld\U [XURXmggUbimorsttsrld\T -ZWTQYmygg}5EUcimnpqppkc[T 5YUSQYmgg *8FR^ehijjf`Y7 CZWURScwgg (5ALU_dea]W6 R[YUSQYm|gg&1K`gg|w  "$%%$  EJMOSVXZ\WG5"+C\moonws  "&,39?EIMPTVXZUG4 "?\quxxwuro    "&-4:@EJORUVXQD3 /,Nkkihiihi #'*/5=CINQRSI=,6[onljjiijk  $(+.4CFFB>5(2_xxwutrponmn    &-4:>@?:5+ !Mtyyzywvtqpnn   &,14650+Foxz{{zywusqonn      #)*+*%?ixz|}}|{yvtronn%Ioxz|}{xvspoo3Xuxz}~~}~}{wuqpo ""#"  !!"#!4Rnxz|~}yyxz}|yvsqp  #%&'%$#"  !"#$%&&'(('&$" ,BYrz{|~}yvtssuy~~zwtrq !$(+*(&%"  !!""##"! "+:HZm|}}~|yvsu~}{suz|xus^ !&*.+'# $(/7AJUanz~~}{ywury{sx}zvr<0-*(#"'-+% %-52& kmpoponlihghjs\C+Ҙxqj`P9, +ШcU7%ώxqi`L4&Mj_F0{yqi^G/!_ofZ@+ϼ{yqh]C*tmcT:'д{yqg[>&yrj`N6'Ҙ{xpfY:!ޙ|wph^I6&֙{xpeW5:¾נz{uof\H4πy{xodP0N¾ٴy{{tmd\Fy{wnbK*ýyy{zsldY۽y{vm`D%ٌyy{xrkbާy{uk^< yyz|xqjy|siZ6%yyz|wpz{qfT0PݫՃyyz{vyzyodL*~yy{y{wmaE%Όyy{͞nZTQҨy     xĬg1&-'"#%*09C4 '2;Chx_8 xԿY0%  !*3> &1;DxlO- xϻ:/$ $/9 $0:DsZ>*% xʷ:."*5 #/:DdKBC?.xų}:.!   &0 !.9MkkY]ZXP?ÿs;. "'#%(+.14678764,#':BIOWltzph`Cd;. $%! (/133469;<=;84* !17ACDD@86M^kxxv_D6(~ytV:)RNI@7+ %0;DIKMMLG?;QaozzoWA3%{tT9(;WSMF=1#&1DKRVY\]^]]\VMDShzоwuq^C6{u^<)>xvtpjd]VPIC@>BFKQTWXZ[ZYYULDNcuپvuqZB|{uS8% O|xsng`YRLHEFGKOQSTUWWXXVOHLe|кz|ulV{~|VB2"&r|wqkb[TPKJIKMNOPRRTVWWRLLhδk{{j]O;I\kz|uld]XSQOO6NOOQRUXZ]YVUmָ~|~fZJ9K`n~vnga\YVURPOORTW[_dghfs׹{}xbWFPcr|vokgb^[ZY\_flw}ܽ}fYP??Pdtȼzuojeb`_dho|ó}eYO?ARfwŸxrmjiintϺ}wdYO?CThzõztrrs|Ĵ|pcYOAGWn˹~һlbYOBJ]uɺɷj`YODMc~ɾµuf_XOFRiǽӿmc^WOHWo¿л·}h`\UOKZrПďqd]YTOM]vؐ|h_[XRON^u蒍od\XVRPP]t~~耋f_YWTQQR]o~sb\XUTRRU^kzӌ}h]YVTTSUXcny~~큄ݸse\YWVWY[^kxmd\ZXXZ]afsĤ¿le^][]`cgn|ÿnhbaacfkpx¾qlfgginryһwsnnrv{ÿ}yuuwy~ǿ岵~ü߭ɿ¾˾ÿ÷¿ƴ߽ͱ⽗ԣ뮀󼕋 Ѫ껡 ܲ Ħ&䷣ gĪrvz@ -'"#%*09C4  9b]VE+g¨nqu4  !*3>  EZWH1gʿijo  $/9  PNB2 gƽdch *5  E6*gc\H  &0  -qbT;  "'#%(+.14678764,#!%%$!$$!򃷵fN(   (/133469;<=;84* #($򒳰e?  $*('%##   "(( 򘯭_2  " # "'򭫧Z      !&!" 󪥝T    $(#)" 󣜔|O   %)$.+ 󚓌|tK  !&*$33+󓌅}vnI   "'+' 5:7,  ~vohL  $(,)! 7@C<8  yqjiJ     %)-,%"7DLK;6  ztlhkE  #"!  !!"!! %),*" 5HSWNE+  }voijm>  %$#" !!""!#'(((BW^[W@/yrlhkm1  ''%# ! ! !!#'((5MabdQB' uniill$'()(&#   "&(' ,AVhodU@& qlhjm^ &()*(&$!  ##$!'0=K_qkdN@ niilnR $%'+,+(&" !%,69JOVkroa]T' jhjloD%  $$#!"$',-+)&#! !1:CAMSOesriaqeXiijml1#"$#! !#(-/-*'$"! "#!,?JOIKPHaqrmdwsnijln_ ""$"! $(./.+(&$"!  "$$ (;NX\SKMC[nrphu{|jlmoL# "$"#',/.,*'&$"  !$&%"7I[eg\NK?Tjrrlkwlmop8$#$!"%*//.+)&%$""#%'%#0DWiqreSJ:Ndqtpio~mnoj&### #'+//.*(&%%&(%!-6?Sfv{{o\OCE\ossnirnopZ!##! #(-.-+*(($*8AJNbs|wgVMDSjtusminpqH$"" %'*+**(& -=MX_`mw}|qaXQJ`swvrmopp6%"#))(##""4AMapwqpw}~xlb^IVkwxvropd$%"&/8;>5(+:HS^uwow|zsmkYMauzzvoqM#$")4>FIMF<0"#1>HUcqxov|zwvugWUiz}zop5%#!!,9CLSWZUPG<2+%$%(07?GOUarznv{{wy~tgW^q~~o]$"  161,($  #=|yyzú]PF@;73-' 4yyzŽØd^XRKGE>70',yyȆkd`^ZXVPJA7.$%߽zyyĺDqkgca`_]ZUK@5+!l/ۧ{yyǿDvplifedcb_]UISH՘|zyyĹ{uqnkjihgeci{* du|zyyzyȿŴ{vronmll~\QHCA@AB̒źŝEFGHJK·žŻĺûŻĹĹø¹»¿                         -apyqzxwwvurdQrddiry{|MB_owJxxwvun]Isdahnxy{~.A\nu|67xxwvukVBtd\cjrwy{~ C]nv{MxxwvugO^xfX]emsuwz}Kbpw}ymxxvubHXzjYW_gopsvy|h RhqxV 'xxvs]Cn}m\PX`hlnqux|W4dov|!,xxvoU>pucQPXahjkoruy|~*Amsz:62205:A0\wywwutrtuvj xxwvpV?_fO67871+&! %, erqomlln= xxwvoV?s_G//.*#  .DffYB- )ܙxxws\AqZA'%# wڍxxwubZkU:  >kvwxxwvkgjR9   M`wuvwxxvs{iQ8 &(*! h]j}muwxxwukR: #*.1," 2 lb]on_svwxxwvwW?# #*056+$  .7&ph^aqYOjuwxx]D+ $+18A6-'!  ' @qjcYdt5EbuwxxnN7 $-33T@7/)'%$#',1)! "$%Xsmg^WfwKZt󏍜YA+  %.5/tUKBCIEMG@:2*"(3CTagloqnjcZ[n}¿kL6 '08?EJOV^htia[WRNGA:3+! />P_fjnmjd^Sdv¿XA, !*3;BHOU_iv|kd^ZWSNH@81)*;N^eikkf`VZomK5!%.6>ELT\htqf_\YVRNG@8/&&7IZdfifbXPg~}W<2'&19AHP[es{nb^\YWTNH?5,#"4FXcfebZN^yiJA:0 %0;CKUamɆthb]ZYWQMD<3,& 2ETa`_ZOTr}ueSLE:*+6BNZg}{znic`_\WSJB;73) 2BT[^YPKkzo^WND70<5+!0ALVUOFe{{ve]TOE?APk}{wpkgec`YRIFD>8) /;FLJD^u{|kc_\[Y^n|~~ysmjfa]UNJFB7&*5<=3"#*--Ka|z|~yskcYQMF>1" #BW{xncUPIC9, 9Lx{˿yl[RLGA7," 1Brz}żweWPKD=5/(%"%,4EYýq]SLGA:7456=EM]{ĽgYOIE>>=AFMV_tĿxbVMHBBCIOWam¾l[QKEGHOWam¹Ŀ}f[RLLS[dqžrdZQQV^ixǿocZWV[blʾƿod`]`foȼĽ|nidgktǻ¼zrmos~Ǻƿvy}ƺȻĽ˾ýĽƾ򶷹ž» þ    #-apyqdȲUSKB1 b|vphadiry{|MB_owJݵd`TG2 t{uog`ahnxy{~.A\nu|6#ߜgeZK2 ytmg^\cjrwy{~ C]nv{2ݎggaP4 6qlf]X]emsuwz}Kbpw}y L|ggfX62ic]UW_gopsvy|h RhqxV Zgg^7Ka[SOX`hlnqux|W4dov|!{زggU+ P[TMPXahjkoruy|~*Amsz ~wפggdL [UMGPYacbcfintwy)Gqx}@ 3}vՋgg`BXQHGOWZWVTVX\bhm?w{z >}vsggX7 +TMDFKNLIFDDHOTO&y{~~}|{{|}r R|uggR1 8QI=?AC>:62205:A0\wywwutrtuvj fzwxѬggQ1 ?MB57871+&! %, erqomlln= ovyq˫ggfP2 SG:./.*#  .DffYB- "ksyurggfU:OA4&%#fovxqu`bfggZC+I;- >akrwunu2N^ff_N# /E6(   M\fmrvr_9ObfcZ33@2$  h]`hmqsN "@[eecB3=0"   2 lb[bilo>1RdfgR4971#   .7&ph^\bgj2 IbggaUG11$    ' @qjcY\beCaggn`./) G ! "$%Xsmg^V[a,1,(Wy~|xC+-!  77+&"&,)2-(#(3CTagloqnjcZV]bgcZ`++& !$JGKJD?:730*&! />P_fjnmjd^SX^dcYyG(* !%)]?>AA=:751-(#*;N^eikkf`VS[adXc*%"   $))iN33797531.*&  &7IZdfifbXNV^dWxB   $)&t_A'*.22o1/-(# "4FXcfebZNR\cW\) "&#rgO/!&(**+*'$  2ETa`_ZOMX_Vi< $!Djk[@$"#$$# 2BT[^YPHT\V|pQ+ ?ukidN6!0ALVUOEPXU}~sidE%# 'ZpnlbM5  /;FLJDLTS{ujjm]B!$#! 7^qtrpndN6$  *5<= %'&&b +QqsxvtsqdO>,  #*--8A?rvljnruuiQ7%*,+#bhkmoqsuwyzy g&())**+,.gľdfiknoqsuzz w!`bdgjlnprzz ý_bdgilnorzz)vz_bdfikmozz;O1ſ]`bdgikmozz`%Zž^acehklnpzzuĽ`bdgikmoqzzՔü`cehjlnprzzˏؕü}adfikmoq}zzAP żf`cfikmoqzzťX8 RǽZ_cfhkmoqzz P¶LX_dgjmoqzz ǽs9JY`dhlnp܄zzõB$4HZ`ejmp׮yzz1gʽ/.BX`fkՃyzzMN4Ŵ -BZbhyyzzNɣ (?XcyyzMORPPONN̓;77;>CQg~ҠyyzͻӾyyzȷӌyyz±Կzyyzŵ𤥤ԄzyyŷԬ{zyyöɅ{zzyyѢ|{zzyyȿ{|{{zyſɔ{|{{zyſʡ馫½        HB4'   "##$%Vnlkgc]VHBCKSZafjps͹7J;-  X^]ZVQH;4<7;?CJPV]dFB3%  LHFB=9,!:1*.26:>AGmwu8*  QE<2( ? $(,048KYfw';x_I42!%)-Wxx(IXgv?hS?& " $(-ixxAVeuθ݃t]J5 "',|҂xxWet׏ƦgS?% %*ȥxxhwcѳp\H/ !E€xx"yM1zfQ;BxxRJá|hS=Yvwxx!%*wOONMLţ~jV@ #%#"Z}vwxxuۻyeQ:)Khqvxn`buuvwxxšćͬq]J- 8Xsw{}|k[j{{tvwxx«ҹyfR<'Jiy}xe^s}jsuvxxƸʳzhU@!BdznXfvfgquvw®yhXD';]{v^Ynzw\dotvwπrdUD* 3Rp}taMary}WZcmtvyi^QA( 0Mj}~wobNUjqxx[NW`ksvyodZNA-#=Tkzb}ztnf]JC\cifBKU^ir~oj`^VM@0.AQ`hpmg_XQJD?;2)DOXF@IR\gqh[VVMC6#)7@HGG>4*!,.,9<>CTi|{ʺ~vqport{|pbTG6%0>@BBNdv}|Ͼžzk\O>+3BDEFL^q|z}Ÿ~~ƷscUE05EHIJO\ox}xhZH49ILMOVcv}̻ɲm^K6=LPPS]l~¹¼̴m_K7APTUYgvϽͶo_J7FSXZ_pηn^H;KV[_iz޾϶lZDAPX_dtܺҸlUAHU\dlɯѾnR?P[bjy弤ĩnOFX`gpūĴsSTcjrزw^fmu}纠ƽyu}~ 콥ú} §Ŀſ ¨Ŀ½ ᆭÿ 缥ܶ𾽺ִɮĬ荎вܷյԵ HB4'    &&%$#! esѼJ;- !__iyÞSB3%  #2lgcdsI8* 1 Avrnjhvg`',    !0 Vz{xtpogg$  .. c}}yvwgg  !9  z}~{zgg  ! ,B &z}~gg   2D 1y|gg  7> Qw{ugg !   ";0 Ptx~ީgg"   $<) mqtz~~}gg|  $&   %= knpvyzyxzؙgg %&!" 7  .hjlqsrsrtҼgg $'!!2 7fghkllp~gg$( % " Ieegfeedhzgg$)"1&  \edcda_^]`qqggr$)$NH%%$  fecba^\YWXh~fgg%*)LG>$(  +gfedc_[WUT^t_gg{ ++P@K!"*#  *ghfed`[VQOVkYTgg+Q&K #+$Efhggeb^XTOR\5Ofggy !SCCB<3OK%,% #%#Leghhge`[UQO90Kdgg쮹).'[ʀh<(*$)Khqvxn`[cfggffa\VQ?*Fbgg-$$e¶{T&",( 8Xsw{}|k[^cdffeb\WR#@^gg 1u`5(*$ 'Jiy}xeY_bdd`\VB :Yg?}vU2$*% BdznXY]`aa_ZU 5TpJĽmO,"*% ;]{v^SZ\^^]Y6 0O}Į|\<#'#3Rp}taLRWXZYL ,[ϥnM,#$!0Mj}~wobNKQSTP- (~ڬ{wcF("##=Tkz}ztnf]J?FGG> 4}stssqnmopqsoT3 !.AQ`hpmg_XQJD?;2)357  ]wqosttuvwyz{{|sR- )7@HGG>4*!$(*+-14;E`|rjhi{{||}~vT.     "'-27?ZzmijllzV.!    ")/3:Svwljmopo~}}}e?!#  "##"  %*,0Lotjjnprrqyyxxy{~{qO2$#" !$&(**+,,+&# !&$'Fgqhkorsttsz{{xvx|}yseR; !##$#! "%)*(# !Bdhlortuvvu|vzzvpmn`K4# "(*% Aglortvwwvv|}xtpmmofVK@81*&"%(,0221%"'(# Jlortvwxxwzx~|ywtssuvuqi`WLELRXYYTO?-$($*Xnqsuwxyzzyķxz~|yyxyy{|~~~|ysjiu|ul[F+!&&!8epsuwxzz{{z˽{x~~~|wpvkT6 %%! Goqsuwxz{||įw{΂|zxwxz~ypw{_@!$%" /Wprtuwy{{}}Ͼvxzzxvyw|~xn~lK&#%! !?fprtvwyz{}~~ĵåz|upyU-"$!#*Qoqrtuwyz{}~~Ѹ{zq{b8 $ "#=bpqrtuwxz{|~Ѱx}upvL$" "$ *Qoqrstuvxy{|~ާxyna7!$&?drrstuvxy{|}z|rzvQ(#%'%-Srttuvwxy{|~ޢ}~uqiB &)+#Dgvvwxyz{}ݱxxm~\: '-*!2Wuxxyz{}~~ vymtP.$+($Hk|{zz{|}~~{ ́zp볱a@0$"$2[z~}}||}~~~{x w{p|lSJ>0%?d}~~{xuwzo鮭~lkbVK7!(Klyz|}~}{xusxzo{ܩqpvwmaK8I\quvxz{||}}|{zwusz{xmۤwntz}s^ZqutuvxyyzyyxwusyҐ}wmvmrx|{r|ttuvwwvutsy}}vm|{{zxvrmorw{~xzttsyx|spzqmnoppqsux{z{Ŷ{vttwz͇~wm{smqtwyz{{z{|}zy̿~ә{ypyzmqwz}󂁁~yzȼΌ}ypzvmsy}|zxxyz{|{zxxƽɇ~ypvwmtz~~xydP,$ƺĸ^  ul_A"ùπɷ0  zzqfrƼϳztiX,  Cyy|tŽȪyyzyqd: Rzyyº{zyyz|viN *]zzyžìtx{{zyyznRK~zz|jelosv{|zyyz{m[aҡzǺF%=T_ejnrvz|zyyĩ̡z̺Y,1DU_dinqvz{zyyϷϿF &6FU^cimqv{{˸졩íΦ' ,9FU^cinƮdzI #.;HW_~ȷ5 %0<ίȹ C̽ c̭ߠįs !wɀտ̸hJɞDvѫƸHշ̾ι׾Ýǧʰ̵Ϲ闘ݫѺӽؽ޿ߣ徦⻩ڸ육Ѷȷº¾üúĻĽ        **+;z]UOSWXXO?Sdpz~vncMTlwG ό4# ̺zme^YSM=-ʾwiZOE8)9IS[ir|x]>2 )}~vlI3ɽ}r`N?2#!4?GOZgpvssgY>- 5^xyy|~xwsĸxgP:,*3;@HR_iqttsbK6DxxvrqpuwxxԶǾ|nP6$$-3:BO`ouvvud>(6`|zxwumhcdfxxʬyĿydC' "*18?L^nuvvuaA%4_rvyyxvupg_\[xὡnuW5'/6>L`puvwwvf^@]hqwwxvtqh\UOxөuo}iC&.6?Nbsuw~kMAXeqtvuspk^TNξ~]sf#&.6@Qj~{z_;BVenttrpmaUNşf]xp &3I`r|zuQ)FUanrrpmfWLkLfs{~y2  "<:7:FQY_el}|{~]/$4ETbmnmhVT7 +335/%!8GEDC?&.26:@=TZ^`b_]\[\_`bda_WV_fkqvwS%:Pfrsqiaþ|`H'2<@DINHVZ^befeca`_acgkpnkb^gmruzyk;DTXND2#ubHWp~}U::LPF<+|p`FfѺxN72EI?5#}m\OvθA*'=C:0|}jW_ͷJ4<5, ~|ugWmܺȳS,72)~~{rfZy޽Z %3.(}||~of^̦[.,&zohbΚX**%wokeэS# ''$woiby龼{B&"$$vnh`l澽դm1( !#vmeZViz̊`.+vlcXJKWbku|{O5/ xkbYK@BIOTX[_jv۫o:=4   ~mbZPD;:?CGILQZiw~}ݗcCG=, ocZSIA8768:?=54269BGIC ʾwiZOE8),/12.*''(,' )}~8 pɽ}r`N?2#!"#%#  5^xyy|~gB MĸxgP:,   DxxvrqpuwggW~Ǿ|nP6$  (7+ 6`|zxwumhcdfggʉuĿydC'  -00 4_rvyyxvupg_\[gt~~umuW5  "4E@*OYT3B:AXeqtvuspk^TMseneZsf# 0G]g@-:1BVenttrpmaUL¿mdbW]xp  !   !#%2Ufh\5"$4ETbmnmhV^,0% +335/% !#%"#JelhX(:K]kooj\~a" !#%&?_nlh)!5H\lppka~uc "#&(!4XpnkE3H^orqlexnd  !! "$'(%+Qpom\ 4Japsrkeujg !"#!  !"!"%()(%Lopnf":Pfrsqiarij!  !##$##"!""#!%)++" Gkrok4'DNW`krmptx{~|vͨ~wa %3.(iiHln'%' +3;@DHMQQRTX\_begkotwqnrvz}y{ԿtR" .,&ljjln_('!4?IRY`goruy~|rmquy||vs>' **%nmlnpL*'(!)&"~}}sN'$(*)('('())*+*'%!2Pkzyv~xwzwS!%(#}|{uT."%'&&''(*+,-+)% 2Ldty ۢ|ng8&'$ ~|{yvY6"#%&'(*,,)%0Gc xu|V*$&|zywviE+"%&()*)'#7X} ͕~|nmD}zxvutrV<"!#$&%#!3Vy wvyc>zwutswxsX9 !! !8Yz Ϩ}~qyV4wtsyzU5'/B_{oݷfttpL,$,5>Nj yyms~fA(&0:DM`{xzoⱧ|}V:.&/9DO]qx{q{㱰^MAHP\l}˂}tr尝Ŀyiouӕ}xmﯮļx{pt謲ƿx~umˉ}yqsx|vmy)yyrn})ȍ{|vor+wysmw߿ÿå񧬴㤣ĹߟļݝǾž¿Ӷʽžμҷϲýϫ»Ԥºݟȿй蚬ǽȻƽȿꠥ䢦饩馫 먬   﩮 嶺줪루ꥨ릩   |o_C) #*+zmXC-#  #!wePD5*~n[JD=1& zsaTLHD:1((**+'!puyucTQQKFB:57:960&_fpwzqdYTQPLJFGJNQMG?6)PR[gpux{}yj[VXXSQPNJQYbf_YOC7PQLS\hoqsuuvvwxxwm`XVXVTRUQMQ]hntqlaTGW`_VLP[djllmlmkd\RVY\WRUVXQP\ktz~{sgY[hmkgXHLU[[YWRKOXb]XQSXWWOYivvgXjrstmeYKDCCGMT\_]ZOMRWWSNVesqUkv||wofa`_cgjlk^OJUXXVOK^o|}Let}|ulfc``^YQDINSTRMGUjwI`r~~ske`\WM>;DNPRNJDM^pLVmzrf^UMA217>DCC@:=QarPMdqm^RE5$(.25664/,=Q`{{R?Xg~~hVG7 #&((&$+?Od}~M5I[r~zbO>)/=Pv|{F.9Ofwx]H5  0Bi{}}='+DYpx_F3 #7Lp29Mi{xbF3  -@U|'0CbywlR<, &9L^(=^zzjL=5'',7HWj#=]}hQJCFLTct">\xkbekw"?Z "@V #AU|#ATs "ASg !@P`v ==BLVdsf`ZWSPMJGEDBA@DNZixtje`\XSOLIGFEGP^ly|rkd\XSOLLKOVan{tia[VRTTZairz~zme`[^agnu}~}}{qkgjmu|~¿{vsw{~~~¿}缺| 缻| 鲮}뤞|o_C) $(zmXC-! wePD3( ~n[JD;/% zsaTJFB9/&" puyucTPMIFB:1-)$  _fpwzqdYSPNLJFCA=:4.&PR[gpux{}yj[UUTQQPNIIKMJC<3*LLKS\hoqsuuvvwxxwm`XTTSRRUQMLORSTPKB7-NQQMIP[djllmlmkd\RQRTSQUVXQMQVXYZXVPF;OTVTRLFLU[[YWRKKOSRQOSXWWNPVZ]]^\[WQENTVVSOKDBBCFJMOOKMRWWSNNTX\_\VVUVUKLTXXYWTPKIHHJMOQQNHJUXXVOIPU[^YSWRPPQNIQVXYWSNJFEDEFGGFBINSTRMFJRV[TSm|cA8IOTXSOIHFCA??<7;DNPRNJDEKQWQTk@LJQSKD>=>=:752-17>DCC@:8?DKGH|}nPEJKB;44762-)#(.25664/*06;;9o~ujkR=AB92*,0/*$#&((&$!(-.$^|yrlinrM5683*#$))#"CrqpnkijmptwF.+.,"#"  7ghiijlmpsvy|=' &&  /lnnoprsvwz}2!" 4mrsstuvwz|~{'"   !0kuvvwxxz{}~zw"# $2kxxyz{|}~}yvs !/'"%:kzz{|}}~{wuty 4<2"#%$Ak{{|}}~|ywtu| :PUB.,F_y}}~~}zxusx .M^YTPKR^jt{}~~~|ywtsy #Kfnsvxyz|}~~}{wqkhjv 9Yitxxz|}~~}yqf\UON[o  ?Ujtz{|~|xqg[PH>85M[i #&(&%$%%$"!+9HWdnw  "%(*++,,+)&$"! !)4CQ_muw  #$')+./.,*(&$"!#'0=JXgtyxz !$&(*++)'&$#!%,6BP^mz}zv  "$'))(&%"! %/:GTbr~{w "#&'('%#! &1=KXfw|w !#%&%%"!'3?N\kz}x !#%%#!,9FTcr~}x "$$"!1?M\jx}w  !#" *:IXft}}w !&6FUdr{~|w 2BRapy|}{w,=N]lwz|~zz$"%7HXgtxz|~~x $$#"!/@Qbowxz|~|w ##"! '9J[jswy{}z|!!  1CSdotwy|~}wG( );K]kruxz}y}qR3 %6FUdnsvy|~{wԈhG(!3BP^lrux{~|wݠ}[90BOZfotxz}~wޥc?*;IS`lsxz}~wߨgC&7DNYgqxz~}w૎jE!/¾9;<>|zw϶{wɰ|wí{w͸|wĭ{w͸|v|wv;>)*,-./8887876640,+Ѵ(̵*˵+,-/7888776653/,Ґysvx}||wz|~w(yz{|wwz{|w)xzxyz{~+w{yɥzyzz,ywyxЫ{yyz-Ӳ~yyz.нӹxyz7‰xyz7͑wy{7қwy|z6ӧxy~w7~yz{6x|x6ʔx~|w5Ξw{z3Σw}~yz/ͨ|{~}zw+åy|~|xw*κwz~~{yww~ 4 679<==<568:g=35ο6ƿ8;=<; $--.-..//0001222.ѵγ+ʲ,Ű-®--ྭ.ݽ.۽.ڼ.ڽ/ڽ/۾1ܿ11Ķ2ɸ2Ѽ2.Áyqt|ymry~}w {yqry{qov|~vx~xqpuysmrzx#v~xqnqrlpv|~y,x}wqmmotyz-y}wrmmnqw|~w-z}wrnopuz~z-Җ{|wtpqsx|w-ђ{}xuqsvz{.Ў|}xvsvx|v.ώ|}ywtxz~|/΍|}yxvy|x/Ϗ|~zywz~}x0Б{~{zy|z0Җz{{z}w0ӝy||{~w2x~}||}2v~~|z2|||y.,,-.//0012233455667.ջ.ij.Ժ.Ŵ/׼/ʷ0ݿ1Ϲ1´2һ2ŵ3ؼ4ɹ4´5Ҽ5Ÿ6ֿ7ͽ8Ȼ+}|wpnx,Ηxztnp{}w-z~|xqmsy}~{xqm.ȕx{upmqvvtqmp/}}}xtolopmmps/͚w{wromlnosw0{~zuqonpquy0Ҥw|xtqpqsw{1Îzzwsrsty|2x~}yvttvz}2̕y|xvvw{~3~}~{xx{~3Ѡw}zyy|4{|{z|~5w~~}{}~5Ȏy~|}6{}~~6Θx8{!"! "#" 􇒝 yrke^VSPPRTRPZpmptvvqmkd]Xqoswz}|qwtx{y~x{}wz~~w}y|{x}z| { { |~ }x wxz|{Iy$< New Layer     wwyyyx,x<xLx\xlx|xxxxxxxxy yy,y<yLy\yly|yy        Iy$<xmlunit-1.6/src/site/xmlunit.png0000644000000000000000000011141412451007364015523 0ustar rootrootPNG  IHDRKd:bKGDC pHYs  ~tIME .a IDATxG2m;!pګPJoh7V5sgșp8 c۔KY]9.3t|}^Y9x۟[B?yh?wIŶqx{ԝS?'~;C[pm ]hʯI2CYچ'\1wY&0|w0$+kέtŹ Ԥ˷gno" mTKq|7[~R}C@lw,$ݶ~! ǵm|[<}pǚ/0'N{^f }[ ,{Dp[ͱՇnY~aAe; `ӵv Z8HnGK)qD8Y0떆1b$F}(ݾ޾l`mI\Z?6~P A}KZKohH`϶ldn߭z |`nӌ2]:\AWYk){ %o.}Mߧ,($׳$n_뻖qWNT: ndBspgrA3E&lʺo}Y+Z{.{vaa"ys ;aI078pBOٖ!}ZdO2B |ifŚܤL]`3I M9jwE)P/V^gh8Bs(S26E(^*X +8h6q"=S״17W?w[]`}C꾆λĺr\+ӹwpn@Vb A,C׆\`.0m 8 Xn" Pe Kc=U pWS~'M$}lO$0dos^XL hߡJݺ8H_hfLm([;uശO9kJEm[93a R ~TWw_Y5Pξ") Jv;*.]-< џJ]uM2C?l,B[f 7zq.~o5-'lkkq({#g m @;_"{=啊r 7SHN~`%F%(`zEq9w/Cl'_a, F 1FRL]yz:u`Z7.)ayך5f=%Mt EX\ ᒏkr+K+YLˮ/<a0RZǺa3?;lj)PFC4$hRMA_FBg̀'1)%HPj-,*zO b${w-D|-j ,ı Eo ?xp"'2H)A@GCC2?!m<[cD}7 o"':$eEezgkC~'`ե ~4k8ٚQbC)Pm:H\{ܪ{R _H#Xb;RkXgCFǾ&w۴\Z#NxoANl^1`Þm=wߺ 'paf:P,c3w~/C!`(QIr `Wz\l8J6}}-slƀ֠/p7bN)R^(}z8)BM-UTi5h CmNWwsBCHT<\pյ  0S?ikdtw1M!3VVlax ',GPz^7+pPkCJo4>•:0L־eok PS+>2ʭH1L[3˷ YJ8iؚUx{{{sW2Fik.YcLHJ``9mVs[Kc=Hgټmqˣ%MYqKk '1p4>*(Ckvou},_eb29{6;,K5a).qn~:E \R">hX+%,(p^p!#!X}|΋/n3$Iˋ ^|h<&E;8 (̲o(|a|\(ˊLt9py ,ǣQ//]=c02a*o~M.뼲E2):'&f(,]r.TUE]^%RαX, (PWvk=`Q(NNNxq*Rg,PzSz iܐjsHcF<*u8RzI8\HjSf>os{@ԕalgx|Ǭj 5?Az ‚(kx,c7Ce{7[ċRz ۖ54RkIF}\y4'X->*wr&Cf`>޸/)J<k&V@R2}xlIνioCpȃ@V%5IE"ܦn(6N7ʄYig"Wu^$`c!V]o >ŋJ)>y HB&8}9[*,Q|)75`woLp4˜^ILҗĩ,;e-7IflǼmsƀ58pEC8 EZpPc$6yK RǬmWC[_5w{Op~*a!prk!aA6(iQ/2"MVV򟀈֚*(zPJC\$@),qY e52}w=>>f2t<}!`d#)WB8QVsw6m{2XjY2-SAhL1+Zx~ӺďDqK&xeέ)HE:'1RL]UG@vCR#9AuڃqYW Bk_GyP%%d ?kh%.&փvGBy\b~)f"^0^c\r n]pDZeX5FM hoʠ`x^7y=x=@Fчqacҡa@8'µ{xϚ}]l*Sq]]s;Ãs} cgrqv~WF͛7躎/_RU]P><%{{Oq.fu{={l6m[sd2q= oq%/i,zƀx ͅw}=F:-mg*NCJэO)>݈}e'| 0 |zp&FSdY o8<}ctO58EbSf3ꮣm[L(xkx5Q[Ç\ >NqrIγ\1nx~wCFi&zUm}*dX݁(g)A9ʑB&gL14łWgg,ۖ*hc١sAHǣ}0h>JW_ѣBݻܻyk2U06MkǏ*؛??g29@Uxu;oA2i31,gK*pYZ(3k}dGN6xLm_dh];.skfDhU9ӊq.+%N~,:fnoJE9FwH)pPI9F2i,ma0<=wSgg, S4{zBطvI.=r:nMv!|y|>?_cw.mWeaW|/}S3|8ѓ'cobo(,p[R@)K/$! .x$O)m3N;[ŭZUj jֻ9,u2cI'Q;.RJ\4ڀjcp1vIGy&7o͛e$pM~֘_~ɏ?m s>Ȟ?y}e(kۙNg?یE/~˭ <>>6[/~ŗ_|Au (F9;ѐm%Nt@Xѣa9|;+x&2%W_f >A@J,qoaB2,%%;Ik-Z>`pRV)2ϙF+wOJe9Z00)\?b 򒅔<|WI$1سئUrb>9eqF1|?fr ,~xG%n`9y}FWK)կ~V)~L$򰸫T]!y%Rn1x''\;:Y)ҝR;a=ȅs%x^&^s /xxeҥ@qpD%}b'Nkr 9,FVL sA&`\ FK}IWKs 2 DtFfe=2_cF1Yt!n]XۍS^n|67_~9_^Iɧ|æfe-a_OZ9,Kꫫ,_}R>CM:Ǐ?s~C 5v7Jbm 9B8!özQcZ.xkQ4Jf/WLjR!wk"8nkxs a,k-/?_Kz,.{s./&}tbJ XPm;&Jp4M$2ha&B Ҡ4M| mYΗiMY$lc>ޛBW?14OКd Rc(F#޿q{7os|rZv ^zg}Ƨ~cZ9mGq>V:XFbsW+x⑂dwb;5V&Iuk|9j>G4o.Bdr'[|d#1(a3 1#Ohvne=ŪD5~p &C_ЅPD{GGZދ܉e?cvwWI_yo_|&?۷{/Z,3)y4d#;3)Wa[8ڪ[t$6ȅqa;qe1n I%@,dnuNQr'D09_,X,kI,p?q~(5hۡ)@h$c븝X;,poC(]U! ]4Z3q.(J%SA"yr"(@y=uH g'bc>Akd+q5hc'''{@B.R 2cȅv]7||w]˗3桤vrz?4SJknc3rDiNWBVµoʆQF6O&w;l%Jd!@qԹiYXE b@M"Ą'̯Э)/_,'?dJ\}uf+"ƅ]s oYzJH <56)FkCX,j@=PUZL.}#(QakMSKh+ EJ,e؄ +q.%w89?Wa|@fR=`%+0ܸvL)6Xjiۘ2 Mqe━aG.Erx r;;n5jF7c&s?#]~:?0]GW׾dgX@HJ 1F,dдsA%Y{q|:<ӧW.ƈq޶sWC*V@͆qs K5f:^g*#L\=4b@H E9(`1h G;V"&jHth(Ǒi!7>Q,J7pŁHm}""<<,o,ߝL> O lo4r 7oV/}&9\MIU-)lG1cܵtT>##\bʢI ̵jtb:i-\,i}s<=?W[VۤfU$ JIwwN ЈU5u׭1Jȝ2̣o ˍx%X%aͱQJ 0NeѵW9_X.уR%e-QuM68eQED2CݟL9,m b uA@hr w'rOO;uѢDE9Dq=w/^CWQ9Zk zw{ѵ_Yg"G|,)cܴtmCu6-шiYW~wy}߫sh>7. /G7['CX 25u Ij6Jx嚲1ˤT(4YW¡rR9:FӜܯ3ߙ[RpZYa؃@i2S.;Z6YRl@ш;;5Ksso'd2nKɫ>^ AY~ڱ'}JlU%9E͐]l ӒgO$D!w g,NX9s"ùg;0tmaB+L`9Z.xw0moO СA.Pg4mn#^9…X\tl.}r4m+X#|$IdlB=/0ЀdUK[ieKc3t(o\;[,\,Z\ QU-U.0YP ajx,gF[+ kAU 3Ɠ}/s ;ve-B RSzūIs~}wB\II9̭(ئP&֑Z2DnɦU@AF/= N7*neBCY7)}dc@7a"k+a+0 T\"Ux吙CZ( (f^ݦO3n3VB=B9.W N[PmEWHwa˭XEYD-νhg[{{ުqxı"Ʉy _t0JZ3X=ʔUiQYf(MGCVJBAkܴ&kg Rr/`ZMhb-n=Z1\R }XE&=0u$_LCC֙e; `|:L,///Y,OF7u]ñ|qu{AtEԂq^V ].Z+ilMN92~5iuT0F;cy˶Pr3 _E-#!奏? @ daCQ Q=Wr]c2_K™Rj9E]{&vP5ʂ 5r#IՂ//f\VI%J3,iMkLdx莇J|J+U DLإ_gSl%-L= MzcD65T2tyds nXajjٌ(sLJeP8^IsdNS:pV(R#Ho<*~~rژS^zQܙRg5˰&9M.8XqI X=뫫Y.Y_wGJ&ՌPc*j$S{uLhW-i2u2]CVti/nm:\%Es.Ϲ:lCT_+[HŒb9'/ bu$1w8˨|c`o\7M " ./Xpr[BfR׆:"R>}R8~qg:.,a28m1ԆN\dk:e1e3F69.i@HvK~%UݙRNg\+2>ٹ )1/? gJQ}[Ae!;2YTdH(jbad R9Δvt4Hyf\PʅiYrsSU)}<)ͨ?13΅mʅDˉ^֗;ٌWnrm3hCfg T@YD{bWc?bf撩ROƌ ;21c,;L s̒p9gv/],Xf3>Jp#/b 縜YIEM -asbl EPR@KvsFlQ G+8@[]uey(6e>_`4v(۠pVx㰥.`, B\.tK6w׍s?/ȮA$g* U zՎv̬ji땕@*NtAd~lc*DIT)~1Up.doo|H^R׮#G#TH怏faFZ_ j늢5إd^8^ ui9Q)Uf'-Ojeo)%Ʉo5_GBH 1(<,37C3@>)߽G( m̆6>j4Nok+ [U»"CHJ;䎄`ÏFwc/p6̍|Q2c'%aTfr筯7)㈫Tq4E~4rw/V}߇3QYg:=#MQ|(FBJh{XM;=dBP,2άVh[ƙ /$,g90qp{`samnJQ(o-zuk4F{liYXprï^QMRtB )] $(F#exUQ0 [-`:$UDZ.<{"Ӕ8'+)˞}[pK}VWWO9z\ȠXY₧ L3rQLD iCABf8L1uGXnA4ᰉ282fWX_NJ! ; w2pO=D,-8:ܵk@CeVK^g<hrH5nw +3s~'%tr_Cmmɴ+=bUg8L*5doU9]o%A z2 3̆_rDm}|2&d/݊F,J aUK42A Be8L[n)1/:h8 +hLg* ),$O&v3L2%pj8_[P2f<]ǫmk_}nb}\Nk^]\e£"())WxqkѶ>?tڻ@f繠Iܨō ./;8̿XV\Yv9gQ-պ~["˜/Ȏk;h01@iy+BYG! (ʢ1E[K.LNm: szsίKNbB_g4M0tJX#󰻼%4YWZfux.e<~|mET e)w2$ j/N5f Hm:LbluT˥PZ0E3.x1 g sgO޾ \'(dt*XuM\rOpvƥsNF|P"7a{( 9)3k;;$cyג GizI3&yRJP(n{ eROB$ז6n) bQm_[.YT\_peeA dS "1' م%}3etHqw7KK-6Ome ;ݡW矿xvp uXBubOKj9 wuoS(G^*Lr )K r5#CL6 4-ݼW6/'>"C4e-Jw(aS3Ȥ( ¨ /e(̢ۜF|Z:߂_2qbVB~#`98.G`,ζ>k+9: dBk 6c|Ed'yyzxI(Ɗb"U \^3vt3I)jC2eo IDATY?DYCixs +}cWHY"nk VG7 7z҃ i"9$deVf-"`qo9B f޼uwsϾ3wߡ5dzҳ\!fhSZ36〝La Ӏ,8|m9pYJV%w+R0 +Q\9F;E;kXCCC: j%jhr!C0}_׾} ~=>+OGc;ʹ}C-&([j;eh)wX}v*S5gBF#Xgɸ]`48ʚ8 Mw\rƈv.|ו"]XTYVHfa,ʞ?"@qӈ&o\A"5ĮOuΝϱk(O(B^D4kiE,A ihg!q" SCu+1H#Nz*^S`#!&Uȋ/_ c_x5ݫf,vf?~k埿w;H< T4x}uӦy[^c|CDv;O <3~cO6#պYFlL1R1(B[\竽+DZ+k@Jœ@aj]]31G19@Sfg<(L+∮8 _!F78>gs@)F7`-b`TBhv<`{7p B̙|f7s):g[ݯo Pp}c*\̵ 'crۼbbvƚ< W\vXXW+TlNkB~~7AEg2*E bi=䙶0Fv鞻+ ;@VМ2`I?o 3e\hgyѫ3mO:q(6p{uǣg'f4YSƌejU@ Wqʀ'MaD1}3ۉ^(~{(4:x>/PiQ>6 Y埫?||YgTo a<<_>aOm6)yiW Zbhl"ÈH@1kiOuԕZ*@Ʌc,MF# `@PoYfqHW ώ6Jo8Ć-=VWRX rEnIv;0U ^'h٦g&blOs.h1b{x\\)Ē%vGa_I%aV~wss`'obwӑr8pةpH8I VeG$ Œh!g!taBνb<ᘑw (uT^Ax3ˁ Bm0"rA]g(qƷw}&8%QL `hNae>s<?T@2bDڹ l^iX w !k;mȆr,.llpE*0' 1BlpJLQk[=(ܒbF"fo÷b}Eĸ S c_Wׁzo[E5??<ŠiNO‡VϷϕzzt-ӄvwww#7\*uݮ6VCNPX J[PPw<<&_Qc.h^hxlE̡:\1(C #LUs?H~ Y +UKoC!doέ04<+<G n,y;㯁7 ;%7B(+z< #$TOv&sNBnKm!?>NW edFOG>)W6 ռ;o׀ 6d8w=?MS?7^^aVښV:.qE +zv$bZ`]#w /'Ub!.Lو rwFyEbf3BW$b ɣSJBM#&HZC[E 8xWޚ-7XTO#/~{cmy7 xYsZ|NH,F1Uc~a5j_O\Le>fWxY+"L{RJŖib|C ޼a7oΜ]&6, wͤVMOm<6{%3T F@7N6_ۉ' ;(ԣbc[#Y6 ՜Zuu}z=;>qg}RB *HFlJ.(3a~Ke*ޛďNR7 /ӝ; , {R ǻVRca-}R /}6 #[Sbivm4I/ZN?\-xWX9&M. ҚgF7B) C  P{mL/ BHɈUDtɅEXCCEC2 l yS戁uNmCIlPe6b@H.D d:D*ig \1@{x ?{2526$ !;MMs{190ic.!qMW U"i?3Iy+Z2?Q#tCK"1CZi 7-Jr>-3؎`!+dv~#3paȋwOdr6j5JicE6P<7(F~2 X ר۫obW=k ̃*f/FVd=4^\'Q;'T+a\8D -b5qd3#?MV}b-'؋PJ""׆L+u8CRgUU Hu{؜=oݢz#rXz*G-#ˁv*<a9B ҐEf#L.t$L#2%uY׾YquM\6"%RU ;PW#!qp n5gtNzp} !`=r\䧀b;?W}#]IShQ8A|P\tix F!q76+fpǑ>QK-՛%z(b }lx>IK밀٤cQ4$avJE6:xYnq٩^ !M'bXl`7V RCcBjBF3r.W%\%VF(h Hiv2 niEsNo-dFDĻ}!h+mlbǀlV+H{lU¢<%q9Olԏ+G|CPI1 a*]m!KhA];Q\W}v;67":NgxU8M?(뒷}BuקnFZ4${;zm=q{likXHUw.DJ~P,l10t@,/OA˧TyrZkfI|Ե"f!]"k%䗄[/oۑa?0  어?0/u1꣗.t2 3ˤh3fA(ۂ]dwYQ mqmwy$ ggYԄD?9u{#鞄}|/;0;8Vб^PM}]vA#lYկ'4 ВbbQa6X&C v'Q 6WJ*ظDmi k[XpmnYb!7 RkfX 6jf=GUcF&ֆe=)PKǔ6/Lǁ8GC%/͟7 9iI;gV*R  ΆO;WO2~ BƑ.M[,XPS2prfv@ IDATֳ[ r!X% GdM4[`$7F9Iv\V̹Vv)h )DH1_c5~GZˍ=암M}:| g4H'"|:sS,O_T =c,Dcw3qkBT,hz  SBݜO9{A&ۧKvgV{v${S @BQ@i8Uz٭FVWH -qX{٠ѻd#tVh i]ZyMskfjmō1`NNGY` A #l7naAU-S9ĽuwyԽ27M JH)4hS.-\ xaWȰ%uȧFg}$m8&Ĕ0Th+b}ʊoY0D$F$ wE|n4`kauE z8#<Ta;tĻxeRG~w7AC$6}ޓ?d~@^*|*;+d Jq]Wc ^'Xy=}p rOxܻ(0BTHXWv 0n/b'o0ZZ peFD|)C@G+.]E^{e*шˀlR;ⳇNA, gK`=@ɠ0r^!MwXEÂhȠ cC[AC@LOOfEh}T󸡟,:'VK@0A#J_3Q*Q́xuMBӄ$/!X*ޣb5kiS&m̵miuCOb4{4[i!S3S0:  &N>Lf-\ e=]+-j} Eh5a-9ޑ=*um&Y{ +)ATh@qYHII kH `#v4c3Ѫgfr>s`""+ie{s ?Cx '7;cI]%fXr, ި Ơ(0au8aa]"ېl豢d?Os|L;' Nt%W`^"!!,$VC0hT5h[ ;5:},n p mK;L$[i+Qnm| S=$8j eC<5-0dZ*ԡ.(L B  6a H\! H=VV"kfg05Oܚu-xӷWzLwg&v`<" 5!O(.m /;,F#p@PY+LTwe)锞\5 dQb6`sOqr|(x"ةM40gy^񜥜6e?<Tv*Y{ ÃjEր ՑV$ Mo0ޥ6X}#Kׂl_epx&U/dDCI}_ ; U(%Fsϰ@ ԕY D>'T!-n#H=WtIRÆ0^; 8>ֹ՜ 6,8$Ahݟw?Br.Vt謁a$=;OJoɶbԅ'oq5N>`}KXWr1 J @H8JW"x_}?<5Zq#O-Nݢп/Ħr${*Z;-7"솹Dd`/?NoǾ{=}"ԕd =`wWmi =' 5 a9+gN@lIi K 2> piRfƈ:!a1QCc'Ц=[8~la7rPl4yfG3ʷȒhM@cK *ܳtnD _ܧLzu\^ilͻk '^&!? Ѓ,זaXP=jDɻ:"Z;N:wDqg)! }<'Sk w*ԞpT׳KMl )hCrm6  99?۵0üG LFB ˾fT$AA "4sSmB늻SļA,:Au0OoN@`=|O)iɨg_{gǑl#" )Qmw4Y+%K-xd& H)@Te-ڔYoI<͏3ݿDP5Z{NŞc1ppOj~^P+*T ^;:NN8X w}ᴅIűy> ;[ 4K,/XWI{Ek{i a`gPPٔ7,Gc<9G +PvUt9ñK R}E]u={|tRU.JRhfW*W;oN 4 3HZC,TPj)bs΂ەn&w0>={A%6-V:%ڪ/Sy\~[3Kꄏ`+yME! #//ۨA=q0Lֱ!a%' Ixop|Ư!_@p=df"mGcvJGSA)ENk&+Z0#RfFokZi!e^Z S<`$zVNUMu#>eqȽr)ţlUT>~fo-\ݐd/ÑhLoLǷ K6Bu8*DYy"M6RUI詾!P lW؝Ic0 XđlJ)E=IYhj% hA$,.ATn09ᛪ:>|fm&%GI}q`>'S^]/M@u0NÉR,k$83/`jD ;ĵGH;H}Uid#/S9qTDaS8pn"Z$906"+KUjݨ ~]H]n }OwԜ%聲IM!mQ7Di (%n~T5;Be:5<*MxAFc^x[!٫RѲ9W{c6ݒ od $-Xxb#Vr*t RUc4|'Lhի7eόȣdQtq]sI iiJ2t,5,k}{=. )[kl6!5֍?T+$ɤnoJ%i,s"|yjs>2ޫ{tu "ec_@YgXDV/OpEB<Ɩih(9漿7`أɤO& - ;WUͶFUˎjn?ؿuW+rWl>7}Qn`:n caXWx,(L+uY}v"夞4^CX6wf]+N=i Ocw#m$_ .c_|>LHӀWOJ*irRuz<$,'gDRFv$H^MMELԅqv[NWnR{0]c+sSK\͛䴅𝩺) bc4CGq'cRVՉ$wDJ(]"r{9)uU *@HG))Pfr., 9xvv#7߱w7ƞHd e%/my/YOʕ|j<s[y#6n2MpOv8G eWaaX MC݀F}9HWO! |59i2 xMMS\l&*] _l/,r)E3^3;tAHG E~ۯ\7vJ f>ig^++0}xCe_;kFwk:螡^S/\I=L)=[Drs-`+ݻz~);vJqs] ҿ'ό9pa}S_|w^n3v͊} YDfb@{+[)6S[? ~1d:¦'QZ4RݙR`pYOSW 3WRQI4NkQO)= 4*b\6g0J LPsz#M6챃EæX;;{U`P kcshy? \UC &D)`JJN+`ÜtlJe,oo`9)Jiy3@#{un3a iIDATIsaP)\} !1 $N*L0׵4k%o}M6J v>-b+{ttߓ/ L ~5zelLdR0Tur"H뿧QEicEV^ `$\hBx K⠸;NHm% }ԳUb —\QZGvrgF+.Hgn4=UoHe޻59ùIH팷c ĉ5ýe.I;U 5>m0CMԡMǃTQ`CkoP y7/8>8o^c JZ'oq[ΰcƦ(}FJbS /gr]xV0 ǬjJ&-u2xl*mHY%[ ̿.C,;BdxSX6eJ*8I/w9EC74 Ԁ2cG +n"h5FTdzȸ ;YIXlgzn5KYHCL;75zQũqcUKKs YwŃαi~g .Oн[Fӡ(7ȳBXK㴘6NK$|EAZ8Vo8qgWj0Ƥl,psǪHUy ʶ#oNh)fogO|DW!E yz<@Dkҡ CV26NP:ƶΜ 8``L9Nk`=J(Hi̱OxbX[@z3KC[ 0ys axRTȧXԌL3;d6&WE( !Uy}$Z*_=$\ 1{ HҔ0s,f > +O ;PƑn%_la8xԂ(}7fǻ"8c6^BDPPG6w ä k -΂% |۔0NgblԫϜN8k$LRSKF <[ls%~w8e L>?טYWsp{ 5OmpE"du,k*Nf i.(5Eˮn.ޤkؤA%\Tk:YO,m>ع,x#]ޒU*шR&J\!2LV #k$ 5Kevk.]Ǣ&xE y1e}fj<˞G;xw%6lGژ;Xy?, e򦨢ᛎ9O(ł, h{XTmGf[A)Æ=yc$>=L`ykz,Ef$&Oy!\ kkzWpmhKX2YhM6`'wlFlcCG>%|5.;XF>taC zGD>nwhY4VE I!̅&_<#j+yOq9z*h2,47}aw/*qP}ȼM2S +A\buˇPݍ,V(&yPEѴ>O~|ahP,; pO߹w9 hq88)k-KLtJLXe)׵FGy$ /yq(O9Ö.axGmiQɊA*a("ڑw̒de yzl < 8Ү^SR*y"S,p~(OK_~|< %>mGtw~C q^#yEK*l<.IFX\fOw918q8fyØ=ޏn mNAՍNIX8G{RЊ(8QH#YG=,(-c :FU* iC֙ۅ6dKpiq!l72ˁvd3ZUnu%Q-@\J:*A5l:z\ccFHV( eإG9#`dHP9RޓaH_{f|Vjݷ9w{=WrpD}f鰮׼ej>~7|'܅p>Լuk=_rnGKzno"2FUALJ=)~>THT^{=YqKj+ JB.Bޢj`ڍ4f4w}RuChѽ>?/5kKk 5^+CU̬/!a+((CCޢO lb 5Cʧ> MV ^/;ԧY%0𹰊OPǦX/k UY0N7!o%@=MEU0T+73KXRFR$y*?r?vb\QocɱA3kL":LR|C)6P WZPYmB6?VBG+rVj!Tq:^vh `Ӣsr->,:?*}L@x:Y,FIjU!ES%<'(f',nSיRWۜOHLDQ?F O&l$ \EY8oRh^&S+[+ue2kuVl]JoJj֮z×'.kp$*V} ZiMDkVr] aiz-_ ִ`EkXg4 _k(gɂ)"w$U*{2`}c*6h[H.GҨP sr,q.hRK`ä>=|Wp_IXeeRsGY*/m9eLjA`&yJj} V16ˇz.߉Ҽtuf+y-S~]|uaت6˶tsj˖JDu[;ki!9[^xb5>8M),Ҟy%n(x~AT2%-D?+愌%.YUuhrܬ >miº1>0WRe*ZYUτBϏ'Ck+eF&YK\g0)+)%a-5JU< l1*a$Y EJL01GA(E<1w4?3Jl$_XrM fE(g. VKbM̕S˔sHTd{4."WҖFz(P+L&Zz!?π"k`SWz_E5":e/8ZܒZ//j>[-G=-׿{GBw"z>"?7{|&(#3?% J"⷟lOFc9EXouQG>GhO|Kyz`eK+NhuٰS!x:kf2~{õŏO;i _%RO~x}ܝ "<IENDB`xmlunit-1.6/src/site/xmlunit-small.png0000644000000000000000000003115512451007364016634 0ustar rootrootPNG  IHDR2"bKGD pHYs  d_tIME  , IDATx׳\u[(O+D=yN=⛰oX}?6~of~˗߉d,3s}ږbdT~7BxR$~2w"z4ozmEvDCh'?+g[WysG*1!B'D}F1Կ5ZWw:R#P!H_ڂ0Ƹk[A)+4J5m'caC`'d .>$%eGPBo~͎Ol_r 8`qkм`g1yӍg"BZUr=*1(ү;#ER/;qg#΃!i˶ ^hu!8}-( Bf.VDHZ:3n1>Jշ!Bm)h[D)RR $[bs~xc EքCDBvB[ mCi{ߦU}Z()EI1%@puľ4`l7>N7AcLEr[ HM$@acvSńc$mP0]&&{͒R"wnMkmm;b$!RPy`oAMe[p)[vi % 7[C 8wYe JeQ1߈I䆌1_]c$x_"W~{+RP_c7b̕@И;wP}B UDc[ ߺ!zoᶌ!EO*J(KD1CI a>Wdy[ߪk kWkZTWh+Aus^ihc^}ָ~G2;xw.q֯tJ:AňJ ׃ }nRֹ17XuC@ȝe<*].߰)KPK@+1V ;W9B G#]{p|֎E?kDSݾMJG_|Dm-ܿ*tQ`?,ڵMx &H! =g5 W.7K1ˉmʠ3= _#o%&굨|[i-_ B@CtBHw YѸH7{]Cۢ>OYD/#o+mIMC*KTQl@ fà÷g;^{_`Z20_]aj2@is|s{=R޿ p`g^RJD A/3[ z3oegW>D7=ɮH]T&! n DA;`.ȸņ \{0T JրG5fg0[ CQ1x|FՈ6m=,ft;[W0K^t}9D)@-(ٷ{~~d21nۘn1q/~X0_I!(S1wK`Js|p:gSܻ-WYsiئc.' }Xfr%daP͌u%n6Y#5 ҶTFnDp,7_^ \ܺu#9oxwɏ|嗹˒SbXO?.& eY [oz | P|$CjM[m%IUƼZVWo[\dPn6V+hT2҉QIP͒/qvqEp tóƣ0Jq~|';/>#@Zqy~N4Ĕ:^U(/ˇ}ħ}MPj\^>;5ݚv_w>14ESʩeAm[bJt]~ y7y筷O9=5sw>`}vXF=%%)i||hEj[y 2%bږ>B0IṘ`TUXcv6Q₾Q#ՂI(Lǜ'g'׬KJ\^^,H1Rđtt6,"8֚>hk1łO>o߾_.Y{O|//yvqAL _[oh2[^&`\,iؔJi-0wQCpy߲f[ /^])B192,K)FC)v1]Ƈwѵ)_>} /+a9襷.)qu}S!d2~Sf3DC6 C~?)FkL{Dqacs۴$iq2Di|D `o3{Ɛ o5H)2#~`v@=>zDϑ 5 G Gq%34'4xbΤ% c-{omwgMCyGShB$TYFuR{=(жF[0Eo^lLA$Yӭ<qL(\گVDף%blok=\欪(:$I0i#FD@cqq{JvTOFBq'c6(B+p}jYnX:|agynzC(@焫G?Zu?M55TEYbusEz 9\ϴR<׌gu4kܐVEArY/ۖe&ĨHCtYLGQPOWc=3Zv^x)}r0%!~JcE ma2*`Ҷ͚`z)>CqFr9>񎣵b.(ZOgfe-9b!V5ތ0S5,J<}UY?w o7/łxq|AQC=DG"wO41EeO{6W=˹p+X֎uHú@J*8&զZ1֞i7Q)Q{*c>/!Oi=IՈ{ ?W_AxXռ xAoO_HQRbj#']^ΞS H#&H P\"*-o:: ˅ǻJj1t_\ߋ6&N{&ۯ=Dm+%,G }5SbGtQ!]$@zPzO$4]Gvt޳_(MQXDM5BL* ba'srN?>%r"YHMձ, !Xױ9_VROlb?,85XўgzjŒD(4vEB;>Kڶ)#qiݑsGi}? R #/Ho#ebfOyUD߹CJ9ß ЪcE( CʫӰ:ׄ咴Xssy1"3#%@פ%*;3͒8̦EbU>[*X^̢ |xjK6)ў)a.Q_tp*/pe&HE N9~k!rX$lZb $T`D*b##bf;V8mz;X׬lD@H7qê|L߭A\7+jptt}6٤By(";S g^9 J5~x.WgϞaޭ[ 'g"&~tT[H!tkl CjBtiFM! =Ѡ&a8m 5Z!%z@5 h+1 IHwHDP~ ɬ%zƓY<+ȋKYo;H7džA\w=Jy~d#Oޏ𠲼΋FT+tuWW"\G@/;i=&8wHJ5Eۢ|G6uSO43BPB sL-9,^w_Z 61qAaGcie-%9\Clu ;}B]G8zόƭ4,˿çk9:v)\O{O=1 .-= ~y5ܦ* bג\n*2AYk&Uxcy+.EBj̕¥Dhgs<~S"JI S2ŰQmiB 6TH6`IuOlDn-] APCg9stמVsgft.TV (ń+%/{B 2__cb1bjM%f?P$v҉;j8KC败m^m?A RCB\?ci]d:yϭ) Nk Ǔ>%ME^hLϻ3b/w=)]p8'̣.^LL '4c<_=rnq%] F:(aR(# !eGl4q?";(ifhk5bL=!6?c{0F67b^EkRURXT49ub %"o`nXIE$jAI(IԅA&80Z=OtlV#Bw]L+:mȹ)OT ##6\\:lEf.QB:qG/TmT\ #_"]6M$@=Ջ/w4ZĨĒʒ/ M@b?4IgoTǴG+d6oDŽ͆Z+ŋD{Ľ['%#6XtHA}GJۮ;A<fHU^af{:?G.Q58żLj3cn'UĐljI m V -.H=F{X 軈^E ~$3H}lLU n/Z&#DK:]$4 EUa/IDATO Dt1M{TH#eܑ Ui$$I:/ eKc' i0G=taB}_$M!uOxOp@<& !VSRYYi#R%[5o⳼"!4O6\⏿1{bQW9MrYnψ\r٬Ŝ&s(R:OQc129"Ct0חPf 1&bfob ]OsZ0q{h55=R[E4c"MS(BJiBlS,}D vѵƎ4OķЛ5( !a<3]Py.@aFt(XRN(SLm)&jkPD܏QAoeE X*G5IE7IV zQүH$6-tUcOkªF6bC:7'JoZB蟲9ͅp,/#~T _|<oß̄;C-rqMZ"13%8509! BE  RW %e=1]_P|H!hS-zZeRPD0-)&oQPhhR J?m4u5qLB5AT& (:9h_?pBU+I$=?Vcl3O܃ʃQB, Q-'E7VĐH>/:06QܙP>lAL$&G[Rf'.VGTԒXw )"6 P4@wT |'vO5\E uќ?Ǻs0JT%v4"֔AB%X+gtPF~QpzϢKYOnU*bs3Knb-8rȌ(H,2_Qr@S`UG.AE6Ob a j/v5ۍMQyB bFHfBa\g+h; PF9{T D{ZX<܎޻]d=]b@t#3ӂR[ڒ">.1 c6AiHocrAT"4x,CVӶC( I [n5)q8acB[)?WI!RDÞ-KbRܪE"U޿6S?gYfBP{L N TѕCFd5,<ѓ)53+#t9B eG[E$@15D%zIKB7 RyMZ $aD꒮W}|A[Q!TUPIc4'n ?#[kʻuХBDjU#R"F <̞(AQe lJ8AHQ|$p4UfII)y%`,2BG(ҘJb(Г5f~LZBz(T Ɓ4`F E \@٠sRH@jХM5UF{&NѳY^{l4`m҂iL7P~LPUЕI$)!u @"FC@&LH6HtW"ʌ3xE*ZjHa4 !hrlruk̨$yܕ^DDf73 =ۄm]HQjZQ5j`R`gN[s3T'hnBK@ $uBqJycF@ ](6p@bɓ >_j]'`:':d:(]N_*#HX)B$n9$NЃV/I vP,B0v8K)!D&[@ ͳt_8 $ߦvT'(tԐf!kp9rI&1A36B*[Pd6RY#*( dA 2,wS7]yީJ!!Mm1% fozbcF32Z#@YPLjYzI2‘{K,p*Wu]qH%>ʑwj&*3fMLW@@(wMQ!3e ݂* Tbf2 |٥eFBRy=MFoA VN"U ~AaIWHHUt7I ǗfQ+Fh"e:83KI0JP (0mSSDFaq>ZąaWfvx$yB(SzI+ A\#R43!:AeGb}030P)2Xq5luT`;T9"!jko oɚ뮙f5 Ћ0$|+` vE"e-8Q[T5L+NP䶽6 /H&K=i)b]* }OZ>[К=,!i9QS{K.J@:mEra}c{ xAO*dkd 2moJĐ.qfƣ5y^}H銜 ʲ)TYɐLvj yџCU@e ggjsMZAl (:lA9j<~P~R&h/:z=AYj:L!y^ȩj{#(1/F7MFMǔ랶󈱼<բR)F%`_5b5vm|3I8$#o{mgky˿|}C\ȸJmM9Zwb6! kt{ ͝r014Mmm/v}d$Egkr5TJH/^oBc;[{z7:{!R $kH~'fB!4GLLJ1/3ĤE;/ W QAE.~!v4ͪ7]C7H 8-* LmhDȐzH9\]03G6=qד|Xj8_S'l2Β9" W|4,"DEb-fT Zi:ٌ#fҮ҅6aWm(؞\/q9DBhY0ivmF!TtP$ ۳m'DĴxcXC4s V*/ nsMap; public XMLUnitNamespaceContext2Jaxp13(NamespaceContext ctx) { nsMap = turnIntoMap(ctx); } public String getNamespaceURI(String prefix) { if (prefix == null) { throw new IllegalArgumentException("prefix must not be null"); } String uri = (String) nsMap.get(prefix); if (uri == null) { uri = XMLConstants.NULL_NS_URI; } return uri; } public Iterator getPrefixes(String uri) { if (uri == null) { throw new IllegalArgumentException("uri must not be null"); } // ensure that the empty string comes out first when asked for // the default namespace URI's prefix TreeSet/**/ ts = new TreeSet(); for (Iterator it = nsMap.entrySet().iterator(); it.hasNext(); ) { Map.Entry/**/ entry = (Map.Entry) it.next(); if (uri.equals(entry.getValue())) { ts.add(entry.getKey()); } } return ts.iterator(); } public String getPrefix(String uri) { Iterator i = getPrefixes(uri); return i.hasNext() ? (String) i.next() : null; } private static Map turnIntoMap(NamespaceContext ctx) { HashMap/**/ m = new HashMap(); for (Iterator i = ctx.getPrefixes(); i.hasNext(); ) { String prefix = (String) i.next(); String uri = ctx.getNamespaceURI(prefix); // according to the Javadocs only the constants defined in // XMLConstants are allowed as prefixes for the following // two URIs if (!XMLConstants.XML_NS_URI.equals(uri) && !XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(uri)) { m.put(prefix, uri); } } m.put(XMLConstants.XML_NS_PREFIX, XMLConstants.XML_NS_URI); m.put(XMLConstants.XMLNS_ATTRIBUTE, XMLConstants.XMLNS_ATTRIBUTE_NS_URI); return m; } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/jaxp13/Jaxp13XpathEngine.java0000644000000000000000000001015512451007364025470 0ustar rootroot/* ****************************************************************** Copyright (c) 2006-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.jaxp13; import org.custommonkey.xmlunit.NamespaceContext; import org.custommonkey.xmlunit.XMLUnit; import org.custommonkey.xmlunit.XpathEngine; import org.custommonkey.xmlunit.exceptions.ConfigurationException; import org.custommonkey.xmlunit.exceptions.XpathException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.w3c.dom.Document; import org.w3c.dom.NodeList; /** * XPath engine based on javax.xml.xpath. */ public class Jaxp13XpathEngine implements XpathEngine { private final XPath xpath; public Jaxp13XpathEngine() throws ConfigurationException { try { XPathFactory f = null; if (XMLUnit.getXPathFactory() != null) { f = (XPathFactory) Class.forName(XMLUnit.getXPathFactory()) .newInstance(); } else { f = XPathFactory.newInstance(); } xpath = f.newXPath(); } catch (Exception ex) { throw new ConfigurationException(ex); } } /** * Execute the specified xpath syntax select expression * on the specified document and return the list of nodes (could have * length zero) that match * @param select * @param document * @return list of matching nodes */ public NodeList getMatchingNodes(String select, Document document) throws XpathException { try { return (NodeList) xpath.evaluate(select, document, XPathConstants.NODESET); } catch (XPathExpressionException ex) { throw new XpathException(ex); } } /** * Evaluate the result of executing the specified xpath syntax * select expression on the specified document * @param select * @param document * @return evaluated result * @throws TransformerException * @throws TransformerConfigurationException */ public String evaluate(String select, Document document) throws XpathException { try { return xpath.evaluate(select, document); } catch (XPathExpressionException ex) { throw new XpathException(ex); } } public void setNamespaceContext(NamespaceContext ctx) { xpath.setNamespaceContext(new XMLUnitNamespaceContext2Jaxp13(ctx)); } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/jaxp13/Validator.java0000644000000000000000000002031712451007364024215 0ustar rootroot/* ****************************************************************** Copyright (c) 2008, 2014, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.jaxp13; import java.util.ArrayList; import java.util.List; import javax.xml.XMLConstants; import javax.xml.transform.Source; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import org.custommonkey.xmlunit.exceptions.XMLUnitRuntimeException; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; /** * Validator class based of {@link javax.xml.validation javax.xml.validation}. * *

      This class provides support for validating schema definitions as * well as instance documents. It defaults to the W3C XML Schema 1.0 * but can be used to validate against any schema language supported * by your SchemaFactory implementation.

      * *

      An implementation detail of {@code * javax.xml.validation.Validator} leaks into this class: any {@code * xsi:schemaLocation} or {@code xsi:noSchemaLocation} attribute of * the instance document will be ignored if any schema source has been * set. This means you must either specify all sources or none of * them to successfully validate instances.

      */ public class Validator { private final String schemaLanguage; private final SchemaFactory factory; private final ArrayList sources = new ArrayList(); /** * validates using W3C XML Schema 1.0. */ public Validator() { this(XMLConstants.W3C_XML_SCHEMA_NS_URI, null); } /** * validates using the specified schema language. * * @param schemaLanguage the schema language to use - see {@link * javax.xml.validation.SchemaFactory SchemaFactory}. */ public Validator(String schemaLanguage) { this(schemaLanguage, null); } /** * validates using the specified schema factory. */ public Validator(SchemaFactory factory) { this(null, factory); } /** * validates using the specified schema language or factory. * * @param schemaLanguage the schema language to use - see {@link * javax.xml.validation.SchemaFactory SchemaFactory}. * @param schemaFactory the concrete factory to use. If this is * non-null, the first argument will be ignored. */ protected Validator(String schemaLanguage, SchemaFactory factory) { this.schemaLanguage = schemaLanguage; this.factory = factory; } /** * Adds a source for the schema defintion. */ public void addSchemaSource(Source s) { sources.add(s); } /** * Is the given schema definition valid? */ public boolean isSchemaValid() { return getSchemaErrors().size() == 0; } /** * Obtain a list of all errors in the schema defintion. * *

      The list contains {@link org.xml.sax.SAXParseException * SAXParseException}s.

      */ public List/**/ getSchemaErrors() { final ArrayList l = new ArrayList(); try { parseSchema(new CollectingErrorHandler(l)); } catch (SAXException e) { // error should have been recorded in our ErrorHandler, at // least that's what the Javadocs say "SchemaFactory is // not allowed to throw SAXException without first // reporting it to ErrorHandler.". // // Unfortunately not all implementations seem to follow // this rule. In particular using the setup described in // org.custommonkey.xmlunit.jaxp13.test_Validator#XtestGoodRelaxNGCompactSyntaxIsValid() // an exception ("SAXParseException: Content is not // allowed in prolog.") will be thrown that never enters // our Errorhandler. if (l.size() == 0) { l.add(e); } } return l; } /** * Is the given schema instance valid according to the configured * schema definition(s)? * * @throws XMLUnitRuntimeException if the schema definition is * invalid or the Source is a SAXSource and the underlying * XMLReader throws an IOException (see {@link * javax.xml.validation.Validator#validate validate in * Validator}). */ public boolean isInstanceValid(Source instance) throws XMLUnitRuntimeException { return getInstanceErrors(instance).size() == 0; } /** * Obtain a list of all errors in the given instance. * *

      The list contains {@link org.xml.sax.SAXParseException * SAXParseException}s.

      * * @throws XMLUnitRuntimeException if the schema definition is * invalid or the Source is a SAXSource and the underlying * XMLReader throws an IOException (see {@link * javax.xml.validation.Validator#validate validate in * Validator}). */ public List/**/ getInstanceErrors(Source instance) throws XMLUnitRuntimeException { Schema schema = null; try { schema = parseSchema(null); } catch (SAXException e) { throw new XMLUnitRuntimeException("Schema is invalid", e); } final ArrayList l = new ArrayList(); javax.xml.validation.Validator v = schema.newValidator(); v.setErrorHandler(new CollectingErrorHandler(l)); try { v.validate(instance); } catch (SAXException e) { // error should have been recorded in our ErrorHandler, // but better double-check. if (l.size() == 0) { l.add(e); } } catch (java.io.IOException i) { throw new XMLUnitRuntimeException("Error reading instance source", i); } return l; } private Schema parseSchema(ErrorHandler h) throws SAXException { SchemaFactory fac = factory != null ? factory : SchemaFactory.newInstance(schemaLanguage); fac.setErrorHandler(h); try { return sources.size() > 0 ? fac.newSchema((Source[]) sources.toArray(new Source[sources.size()])) : fac.newSchema(); } finally { fac.setErrorHandler(null); } } private static final class CollectingErrorHandler implements ErrorHandler { private final List l; CollectingErrorHandler(List l) { this.l = l; } public void error(SAXParseException e) { l.add(e); } public void fatalError(SAXParseException e) { l.add(e); } public void warning(SAXParseException e) { l.add(e); } } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/ElementNameQualifier.java0000644000000000000000000000744512451007364025225 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import org.w3c.dom.Element; import org.w3c.dom.Node; /** * Simple interface implementation that tests two elements for name * comparability. This class provides the default behaviour within a * DifferenceEngine (for backwards compatibility) *
      Examples and more at * xmlunit.sourceforge.net * @see DifferenceEngine#compareNodeList(NodeList, NodeList, int, DifferenceListener, ElementQualifier) * @see Diff#overrideElementQualifier(ElementQualifier) */ public class ElementNameQualifier implements ElementQualifier { /** * Determine whether two elements qualify for further Difference comparison. * @param control * @param test * @return true if the two elements qualify for further comparison based on * their similar namespace URI and non-namespaced tag name, * false otherwise */ public boolean qualifyForComparison(Element control, Element test) { return control != null && test !=null && equalsNamespace(control, test) && getNonNamespacedNodeName(control).equals(getNonNamespacedNodeName(test)); } /** * Determine whether two nodes are defined by the same namespace URI * @param control * @param test * @return true if the two nodes are both defined by the same namespace URI * (including the default - empty - namespace), false otherwise */ protected boolean equalsNamespace(Node control, Node test) { String controlNS = control.getNamespaceURI(); String testNS = test.getNamespaceURI(); if (controlNS == null) { return testNS == null; } return controlNS.equals(testNS); } /** * Strip any namespace information off a node name * @param node * @return the localName if the node is namespaced, or the name otherwise */ protected String getNonNamespacedNodeName(Node node) { String name = node.getLocalName(); if (name == null) { return node.getNodeName(); } return name; } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/QualifiedName.java0000644000000000000000000001331012451007364023661 0ustar rootroot/* ***************************************************************** Copyright (c) 2014 Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; /** * Since javax.xml.namespace.QName is not present prior to Java5, this * is XMLUnit's own abstraction. */ public final class QualifiedName { private final String namespaceUri; private final String localName; public QualifiedName(String localName) { this(XMLConstants.NULL_NS_URI, localName); } public QualifiedName(String namespaceUri, String localName) { if (localName == null) { throw new IllegalArgumentException("localName must not be null"); } this.namespaceUri = namespaceUri == null ? XMLConstants.NULL_NS_URI : namespaceUri; this.localName = localName; } public String getNamespaceURI() { return namespaceUri; } public String getLocalName() { return localName; } public int hashCode() { return 7 * namespaceUri.hashCode() + localName.hashCode(); } public boolean equals(Object o) { if (!(o instanceof QualifiedName)) { return false; } QualifiedName other = (QualifiedName) o; return namespaceUri.equals(other.namespaceUri) && localName.equals(other.localName); } /** * Parses strings of the form "{NS-URI}LOCAL-NAME" or "prefix:localName" as QualifiedNames. * *

      When using the prefix-version the prefix must be defined * inside the current NamespaceContext.

      * * @see XMLUnit#setXpathNamespaceContext */ public static QualifiedName valueOf(String value) { return valueOf(value, XMLUnit.getXpathNamespaceContext()); } /** * Represents the QualifiedName as {NS-URI}LOCAL-NAME. * *

      If the NS-URI is equal to NULL_NS_URI only the local name is returned.

      */ public String toString() { return XMLConstants.NULL_NS_URI.equals(namespaceUri) ? localName : "{" + namespaceUri + "}" + localName; } /** * Parses strings of the form "{NS-URI}LOCAL-NAME" or "prefix:localName" as QualifiedNames. * *

      When using the prefix-version the prefix must be defined * inside the NamespaceContext given as argument.

      */ public static QualifiedName valueOf(String value, NamespaceContext ctx) { if (value == null) { throw new IllegalArgumentException("value must not be null"); } int colon = value.indexOf(':'); int closingBrace = value.indexOf('}'); boolean qnameToStringStyle = value.startsWith("{") && closingBrace > 0; if (!qnameToStringStyle && colon < 0) { return new QualifiedName(value); // null namespace } return qnameToStringStyle ? parseQNameToString(value, closingBrace) : parsePrefixFormat(value, colon, ctx); } private static QualifiedName parseQNameToString(String value, int closingBrace) { if (closingBrace + 1 == value.length()) { throw new IllegalArgumentException("localName must not be empty in " + value); } return new QualifiedName(value.substring(1, closingBrace), value.substring(closingBrace + 1)); } private static QualifiedName parsePrefixFormat(String value, int colon, NamespaceContext ctx) { if (colon + 1 == value.length()) { throw new IllegalArgumentException("localName must not be empty in " + value); } if (ctx == null) { throw new IllegalArgumentException("Cannot parse " + value + " without a NamespaceContext"); } String prefix = value.substring(0, colon); String nsUri = ctx.getNamespaceURI(prefix); if (nsUri == null) { throw new IllegalArgumentException(prefix + " is unknown to " + "NamespaceContext"); } return new QualifiedName(nsUri, value.substring(colon + 1)); } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/TolerantSaxDocumentBuilder.java0000644000000000000000000002704412451007364026440 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Comment; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.ProcessingInstruction; import org.w3c.dom.Text; import org.xml.sax.Attributes; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.ext.LexicalHandler; import org.xml.sax.helpers.DefaultHandler; /** * Uses Sax events from the ContentHandler and * LexicalHandler interfaces to build a DOM document in a tolerant * fashion -- it can cope with start tags without end tags, and end tags without * start tags for example. * Although this subverts the idea of XML being well-formed, it is intended * for use with HTML pages so that they can be transformed into DOM * trees, without being XHTML to start with. * Note that this class currently does not handle entity, DTD or CDATA tags. *
      Examples and more at xmlunit.sourceforge.net * @see HTMLDocumentBuilder#parse */ public class TolerantSaxDocumentBuilder extends DefaultHandler implements LexicalHandler { private final DocumentBuilder documentBuilder; private final StringBuffer traceBuffer; private Document currentDocument; private Element currentElement; /** * Constructor for specific JAXP parser * @param documentBuilder the JAXP parser to use to construct an empty * DOM document that will be built up with SAX calls * @throws ParserConfigurationException */ public TolerantSaxDocumentBuilder(DocumentBuilder documentBuilder) throws ParserConfigurationException { this.documentBuilder = documentBuilder; this.traceBuffer = new StringBuffer(); } /** * @return the Document built up through the Sax calls */ public Document getDocument() { return currentDocument; } /** * @return the trace of Sax calls that were used to build up the Document */ public String getTrace() { return traceBuffer.toString(); } /** * ContentHandler method * @throws SAXException */ public void startDocument() throws SAXException { traceBuffer.delete(0, traceBuffer.length()); trace("startDocument"); currentDocument = documentBuilder.newDocument(); currentElement = null; } /** * ContentHandler method * @throws SAXException */ public void endDocument() throws SAXException { trace("endDocument"); } /** * ContentHandler method. */ public void characters(char[] data, int start, int length) { if (length >= 0) { String characterData = new String(data, start, length); trace("characters:" + characterData); if (currentElement == null) { warn("Can't append text node to null currentElement"); } else { Text textNode = currentDocument.createTextNode(characterData); currentElement.appendChild(textNode); } } else { warn("characters called with negative length"); } } /** * ContentHandler method * @throws SAXException */ public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { trace("startElement:" + localName + "~" + qName); Element newElement = createElement(namespaceURI, qName, atts); appendNode(newElement); currentElement = newElement; } /** * ContentHandler method * @throws SAXException */ public void endElement(String namespaceURI, String localName, String qName) throws SAXException { trace("endElement:" + localName + "~" + qName); if (currentElement==null) { warn(qName + ": endElement before any startElement"); return; } Node parentNode = null; boolean atDocumentRoot = false, foundTagToEnd = false; Element startElement = currentElement; while (!(foundTagToEnd || atDocumentRoot)) { parentNode = currentElement.getParentNode(); if (parentNode.getNodeType()==Node.ELEMENT_NODE) { foundTagToEnd = isElementMatching(currentElement, qName); currentElement = (Element) parentNode; } else if (parentNode.getNodeType()==Node.DOCUMENT_NODE) { atDocumentRoot = true; if (startElement==currentDocument.getDocumentElement()) { foundTagToEnd = isElementMatching(startElement, qName); } else { currentElement = startElement; } } else { throw new IllegalArgumentException("Closing element " + qName + ": expecting a parent ELEMENT_NODE but found " + parentNode); } } if (!foundTagToEnd) { warn(qName + ": endElement does not match startElement!"); } } private boolean isElementMatching(Element anElement, String qname) { return anElement.getNodeName()!=null && anElement.getNodeName().equals(qname); } /** * Unhandled ContentHandler method * @throws SAXException */ public void endPrefixMapping(String prefix) throws SAXException { unhandled("endPrefixMapping"); } /** * Unhandled ContentHandler method * @throws SAXException */ public void ignorableWhitespace (char ch[], int start, int length) throws SAXException { unhandled("ignorableWhitespace"); } /** * ContentHandler method * @throws SAXException */ public void processingInstruction(String target, String data) throws SAXException { trace("processingInstruction"); ProcessingInstruction instruction = currentDocument.createProcessingInstruction(target, data); appendNode(instruction); } /** * Unhandled ContentHandler method */ public void setDocumentLocator (Locator locator) { unhandled("setDocumentLocator"); } /** * Unhandled ContentHandler method * @throws SAXException */ public void skippedEntity (String name) throws SAXException { unhandled("skippedEntity"); } /** * Unhandled ContentHandler method * @throws SAXException */ public void startPrefixMapping (String prefix, String uri) throws SAXException { unhandled("startPrefixMapping"); } /** * Unhandled LexicalHandler method. * DOM currently doesn't allow DTD to be retrofitted onto a Document. * @throws SAXException */ public void startDTD (String name, String publicId, String systemId) throws SAXException { unhandled("startDTD"); } /** * Unhandled LexicalHandler method * @throws SAXException */ public void endDTD () throws SAXException { unhandled("endDTD"); } /** * Unhandled LexicalHandler method * @throws SAXException */ public void startEntity (String name) throws SAXException { unhandled("startEntity"); } /** * Unhandled LexicalHandler method * @throws SAXException */ public void endEntity (String name) throws SAXException { unhandled("endEntity"); } /** * Unhandled LexicalHandler method * @throws SAXException */ public void startCDATA () throws SAXException { unhandled("startCDATA"); } /** * Unhandled LexicalHandler method * @throws SAXException */ public void endCDATA () throws SAXException { unhandled("endCDATA"); } /** * LexicalHandler method * @throws SAXException */ public void comment(char ch[], int start, int length) throws SAXException { String commentText = new String(ch, start, length); trace("comment:" + commentText); Comment comment = currentDocument.createComment(commentText); appendNode(comment); } /** * Log an unhandled ContentHandler or LexicalHandler method * @param method */ private void unhandled(String method) { trace("Unhandled callback: " + method); } /** * Log a warning about badly formed markup * @param msg */ private void warn(String msg) { trace("WARNING: " + msg); } /** * Log a handled ContentHandler or LexicalHandler method * for tracing / debug purposes * @param method */ private void trace(String method) { traceBuffer.append(method).append('\n'); } /** * Create a DOM Element for insertion into the current document * @param namespaceURI * @param qName * @param attributes * @return the created Element */ private Element createElement(String namespaceURI, String qName, Attributes attributes) { Element newElement = currentDocument.createElement(qName); if (namespaceURI != null && namespaceURI.length() > 0) { newElement.setPrefix(namespaceURI); } for(int i = 0; attributes != null && i < attributes.getLength(); ++i) { newElement.setAttribute(attributes.getQName(i), attributes.getValue(i)); } return newElement; } /** * Append a node to the current document or the current element in the document * @param appendNode */ private void appendNode(Node appendNode) { if (currentElement==null) { currentDocument.appendChild(appendNode); } else { currentElement.appendChild(appendNode); } } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/exceptions/0000755000000000000000000000000012451007364022475 5ustar rootrootxmlunit-1.6/src/java/org/custommonkey/xmlunit/exceptions/XMLUnitException.java0000644000000000000000000000442212451007364026521 0ustar rootroot/* ****************************************************************** Copyright (c) 2006-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.exceptions; /** * Base class of any checked exception that can be thrown within * XMLUnit. */ public abstract class XMLUnitException extends Exception { private final Throwable cause; /** * Inititializes the exeption. * * @param message the detail message * @param cause the root cause of the exception */ protected XMLUnitException(String message, Throwable cause) { super(message); this.cause = cause; } /** * Root cause of the exception, if any. */ public Throwable getCause() { return cause; } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/exceptions/ConfigurationException.java0000644000000000000000000000410112451007364030022 0ustar rootroot/* ****************************************************************** Copyright (c) 2006-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.exceptions; /** * Exception thrown when an XML parser-, transformer- or XPath-factory * throws a configuration exception. */ public class ConfigurationException extends XMLUnitRuntimeException { public ConfigurationException(Throwable t) { super(t != null ? t.getMessage() : null, t); } public ConfigurationException(String s) { super(s, null); } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/exceptions/XMLUnitRuntimeException.java0000644000000000000000000000474312451007364030073 0ustar rootroot/* ****************************************************************** Copyright (c) 2006-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.exceptions; /** * Base class of any RuntimeException that can be thrown within * XMLUnit. */ public class XMLUnitRuntimeException extends RuntimeException { private final Throwable cause; /** * Inititializes the exeption. * * @param message the detail message * @param cause the root cause of the exception */ public XMLUnitRuntimeException(String message, Throwable cause) { super(message); this.cause = cause; } /** * Inititializes an exeption without cause. * * @param message the detail message */ public XMLUnitRuntimeException(String message) { this(message, null); } /** * Root cause of the exception, if any. */ public Throwable getCause() { return cause; } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/exceptions/XpathException.java0000644000000000000000000000503212451007364026303 0ustar rootroot/* ****************************************************************** Copyright (c) 2006-2008, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.exceptions; /** * Exception an {@link org.custommonkey.xmlunit.XpathEngine * XpathEngine} is allowed to throw. */ public class XpathException extends XMLUnitException { /** * Inititializes the exeption. * * @param cause the root cause of the exception */ public XpathException(Throwable t) { this(t != null ? t.getMessage() : null, t); } /** * Inititializes the exeption. * * @param message the detail message * @param cause the root cause of the exception */ public XpathException(String message) { this(message, null); } /** * Inititializes the exeption. * * @param message the detail message * @param cause the root cause of the exception */ public XpathException(String message, Throwable t) { super(message, t); } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/XMLConstants.java0000644000000000000000000001105112451007364023512 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2013 Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; /** * A convenient place to hang constants relating to general XML usage */ public interface XMLConstants { /** * <?xml> declaration */ public static final String XML_DECLARATION = ""; /** * xmlns attribute prefix */ public static final String XMLNS_PREFIX = "xmlns"; /** * xmlns URI */ public static final String XMLNS_ATTRIBUTE_URI = "http://www.w3.org/2000/xmlns/"; /** * "<" */ public static final String OPEN_START_NODE = "<"; /** * "</" */ public static final String OPEN_END_NODE = ""; /** * "![CDATA[" */ public static final String START_CDATA = "![CDATA["; /** * "]]" */ public static final String END_CDATA = "]]"; /** * "!--" */ public static final String START_COMMENT = "!--"; /** * "--"" */ public static final String END_COMMENT = "--"; /** * "?" */ public static final String START_PROCESSING_INSTRUCTION = "?"; /** * "?" */ public static final String END_PROCESSING_INSTRUCTION = "?"; /** * "!DOCTYPE" */ public static final String START_DOCTYPE = "!DOCTYPE "; /** * "/" */ public static final String XPATH_SEPARATOR = "/"; /** * "[" */ public static final String XPATH_NODE_INDEX_START = "["; /** * "]" */ public static final String XPATH_NODE_INDEX_END = "]"; /** * "comment()" */ public static final String XPATH_COMMENT_IDENTIFIER = "comment()"; /** * "processing-instruction()" */ public static final String XPATH_PROCESSING_INSTRUCTION_IDENTIFIER = "processing-instruction()"; /** * "text()" */ public static final String XPATH_CHARACTER_NODE_IDENTIFIER = "text()"; /** * "&at;" */ public static final String XPATH_ATTRIBUTE_IDENTIFIER = "@"; /** * http://www.w3.org/2001/XMLSchema */ public static final String W3C_XML_SCHEMA_NS_URI = "http://www.w3.org/2001/XMLSchema"; /** * http://www.w3.org/2001/XMLSchema-instance */ public static final String W3C_XML_SCHEMA_INSTANCE_NS_URI = "http://www.w3.org/2001/XMLSchema-instance"; /** * "schemaLocation" */ public static final String W3C_XML_SCHEMA_INSTANCE_SCHEMA_LOCATION_ATTR = "schemaLocation"; /** * "noNamespaceSchemaLocation" */ String W3C_XML_SCHEMA_INSTANCE_NO_NAMESPACE_SCHEMA_LOCATION_ATTR = "noNamespaceSchemaLocation"; /** * "type" */ public static final String W3C_XML_SCHEMA_INSTANCE_TYPE_ATTR = "type"; /** * "" */ String NULL_NS_URI = ""; } xmlunit-1.6/src/java/org/custommonkey/xmlunit/DetailedDiff.java0000644000000000000000000001043512451007364023466 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2008, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.util.List; import java.util.ArrayList; /** * Compares and describes all the differences between two XML documents. * The document comparison does not stop once the first unrecoverable difference * is found, unlike the Diff class. * Note that because the differences are described relative to some control XML * the list of all differences when A is compared to B will not * necessarily be the same as when B is compared to A. *
      Examples and more at xmlunit.sourceforge.net */ public class DetailedDiff extends Diff { private final List allDifferences; /** * Create a new instance based on a prototypical Diff instance * @param prototype the Diff instance for which more detailed difference * information is required */ public DetailedDiff(Diff prototype) { super(prototype); allDifferences = new ArrayList(); } /** * DifferenceListener implementation. * Add the difference to the list of all differences * @param expected * @param actual * @param control * @param test * @param comparingWhat * @return the value supplied by the superclass implementation */ public int differenceFound(Difference difference) { final int returnValue = super.differenceFound(difference); switch (returnValue) { case RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL: return returnValue; case RETURN_ACCEPT_DIFFERENCE: break; case RETURN_IGNORE_DIFFERENCE_NODES_SIMILAR: difference.setRecoverable(true); break; case RETURN_UPGRADE_DIFFERENCE_NODES_DIFFERENT: difference.setRecoverable(false); break; default: throw new IllegalArgumentException(returnValue + " is not a defined " + " DifferenceListener" + ".RETURN_... value"); } allDifferences.add(difference); return returnValue; } /** * ComparisonController implementation. * @param afterDifference * @return false always as this class wants to see all differences */ public boolean haltComparison(Difference afterDifference) { return false; } /** * Obtain all the differences found by this instance * @return a list of {@link Difference differences} */ public List getAllDifferences() { compare(); return allDifferences; } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/NodeTestException.java0000644000000000000000000000573312451007364024573 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import org.w3c.dom.Node; /** * Thrown by a NodeTest that fails. *
      Examples and more at xmlunit.sourceforge.net * @see NodeTest */ public class NodeTestException extends Exception { private transient final Node node; /** * Constructor for specific node and message * @param message * @param node */ public NodeTestException(String message, Node node) { super(message); this.node = node; } /** * Constructor for message only * @param message */ public NodeTestException(String message) { this(message, null); } /** * @return true if a node was passed to constructor */ public boolean hasNode() { return node != null; } /** * @return the node passed to constructor, or null if no node was passed */ public Node getNode() { return node; } /** * @return the exception message and node information if available */ public String getMessage() { StringBuffer stringBuffer = new StringBuffer(super.getMessage()); if (hasNode()) { stringBuffer.append(' ') .append(getNode().toString()); } return stringBuffer.toString(); } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/Diff.java0000644000000000000000000003625212451007364022037 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2008,2014 Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import javax.xml.transform.TransformerException; import javax.xml.transform.dom.DOMSource; import org.custommonkey.xmlunit.exceptions.XMLUnitRuntimeException; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * Compares and describes any difference between XML documents. * Two documents are either: *
        *
      • identical: the content and sequence of the nodes in the documents * are exactly the same
      • *
      • similar: the content of the nodes in the documents are the same, * but minor differences exist e.g. sequencing of sibling elements, values of * namespace prefixes, use of implied attribute values
      • *
      • different: the contents of the documents are fundamentally * different
      • *
      *
      * The difference between compared documents is contained in a message buffer * held in this class, accessible either through the appendMessage * or toString methods. NB: When comparing documents, the * comparison is halted as soon as the status (identical / similar / different) * is known with certainty. For a list of all differences between the documents * an instance of {@link DetailedDiff the DetailedDiff class} can be used * instead. *
      Examples and more at xmlunit.sourceforge.net */ public class Diff implements DifferenceListener, ComparisonController { private final Document controlDoc; private final Document testDoc; private boolean similar = true; private boolean identical = true; private boolean compared = false; private boolean haltComparison = false; private StringBuffer messages; private DifferenceEngine differenceEngine; private DifferenceListener differenceListenerDelegate; private ElementQualifier elementQualifierDelegate; private MatchTracker matchTrackerDelegate; /** * Construct a Diff that compares the XML in two Strings */ public Diff(String control, String test) throws SAXException, IOException { this(new StringReader(control), new StringReader(test)); } /** * Construct a Diff that compares the XML read from two Readers */ public Diff(Reader control, Reader test) throws SAXException, IOException { this(XMLUnit.buildDocument(XMLUnit.newControlParser(), control), XMLUnit.buildDocument(XMLUnit.newTestParser(), test)); } /** * Construct a Diff that compares the XML in two Documents */ public Diff(Document controlDoc, Document testDoc) { this(controlDoc, testDoc, (DifferenceEngine) null); } /** * Construct a Diff that compares the XML in a control Document against the * result of a transformation */ public Diff(String control, Transform testTransform) throws IOException, TransformerException, SAXException { this(XMLUnit.buildControlDocument(control), testTransform.getResultDocument()); } /** * Construct a Diff that compares the XML read from two JAXP InputSources */ public Diff(InputSource control, InputSource test) throws SAXException, IOException { this(XMLUnit.buildDocument(XMLUnit.newControlParser(), control), XMLUnit.buildDocument(XMLUnit.newTestParser(), test)); } /** * Construct a Diff that compares the XML in two JAXP DOMSources */ public Diff(DOMSource control, DOMSource test) { this(control.getNode().getOwnerDocument(), test.getNode().getOwnerDocument()); } /** * Construct a Diff that compares the XML in two Documents using a specific * DifferenceEngine */ public Diff(Document controlDoc, Document testDoc, DifferenceEngine comparator) { this(controlDoc, testDoc, comparator, new ElementNameQualifier()); } /** * Construct a Diff that compares the XML in two Documents using a specific * DifferenceEngine and ElementQualifier */ public Diff(Document controlDoc, Document testDoc, DifferenceEngine comparator, ElementQualifier elementQualifier) { this.controlDoc = getManipulatedDocument(controlDoc); this.testDoc = getManipulatedDocument(testDoc); this.elementQualifierDelegate = elementQualifier; this.differenceEngine = comparator; this.messages = new StringBuffer(); } /** * Construct a Diff from a prototypical instance. * Used by extension subclasses * @param prototype a prototypical instance */ protected Diff(Diff prototype) { this(prototype.controlDoc, prototype.testDoc, prototype.differenceEngine, prototype.elementQualifierDelegate); this.differenceListenerDelegate = prototype.differenceListenerDelegate; } /** * If {@link XMLUnit#getIgnoreWhitespace whitespace is ignored} in * differences then manipulate the content to strip the redundant * whitespace * @param originalDoc a document making up one half of this difference * @return the original document with redundant whitespace removed if * differences ignore whitespace */ private Document getWhitespaceManipulatedDocument(Document originalDoc) { return XMLUnit.getIgnoreWhitespace() ? XMLUnit.getWhitespaceStrippedDocument(originalDoc) : originalDoc; } /** * Manipulates the given document according to the setting in the * XMLUnit class. * *

      This may involve:

      *
        *
      • {@link XMLUnit.setIgnoreWhitespace stripping redundant * whitespace}
      • *
      • {@link XMLUnit.setIgnoreComments stripping comments}
      • *
      • {@link XMLUnit.setNormalize normalizing Text nodes}
      • *
      * * @param orig a document making up one half of this difference * @return manipulated doc */ private Document getManipulatedDocument(Document orig) { return getNormalizedDocument(getCommentlessDocument(getWhitespaceManipulatedDocument(orig))); } /** * Removes all comment nodes if {@link XMLUnit.getIgnoreComments * comments are ignored}. * * @param originalDoc a document making up one half of this difference * @return manipulated doc */ private Document getCommentlessDocument(Document orig) { if (!XMLUnit.getIgnoreComments()) { return orig; } try { Transform commentStripper = XMLUnit.getStripCommentsTransform(orig); return commentStripper.getResultDocument(); } catch (TransformerException e) { throw new XMLUnitRuntimeException(e.getMessage(), e.getCause()); } } private Document getNormalizedDocument(Document orig) { if (!XMLUnit.getNormalize()) { return orig; } Document d = (Document) orig.cloneNode(true); d.normalize(); return d; } /** * Top of the recursive comparison execution tree */ protected final void compare() { if (compared) { return; } getDifferenceEngine().compare(controlDoc, testDoc, this, elementQualifierDelegate); compared = true; } /** * Return the result of a comparison. Two documents are considered * to be "similar" if they contain the same elements and attributes * regardless of order. */ public boolean similar(){ compare(); return similar; } /** * Return the result of a comparison. Two documents are considered * to be "identical" if they contain the same elements and attributes * in the same order. */ public boolean identical(){ compare(); return identical; } /** * Append a meaningful message to the buffer of messages * @param appendTo the messages buffer * @param expected * @param actual * @param control * @param test * @param difference */ private void appendDifference(StringBuffer appendTo, Difference difference) { appendTo.append(' ').append(difference).append('\n'); } /** * DifferenceListener implementation. * If the {@link Diff#overrideDifferenceListener overrideDifferenceListener} * method has been called then the interpretation of the difference * will be delegated. * @param difference * @return a DifferenceListener.RETURN_... constant indicating how the * difference was interpreted. * Always RETURN_ACCEPT_DIFFERENCE if the call is not delegated. */ public int differenceFound(Difference difference) { int returnValue = RETURN_ACCEPT_DIFFERENCE; if (differenceListenerDelegate != null) { returnValue = differenceListenerDelegate.differenceFound(difference); } switch (returnValue) { case RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL: return returnValue; case RETURN_IGNORE_DIFFERENCE_NODES_SIMILAR: identical = false; haltComparison = false; break; case RETURN_ACCEPT_DIFFERENCE: identical = false; if (difference.isRecoverable()) { haltComparison = false; } else { similar = false; haltComparison = true; } break; case RETURN_UPGRADE_DIFFERENCE_NODES_DIFFERENT: identical = similar = false; haltComparison = true; break; default: throw new IllegalArgumentException(returnValue + " is not a defined DifferenceListener.RETURN_... value"); } if (haltComparison) { messages.append("\n[different]"); } else { messages.append("\n[not identical]"); } appendDifference(messages, difference); return returnValue; } /** * DifferenceListener implementation. * If the {@link Diff#overrideDifferenceListener overrideDifferenceListener} * method has been called then the call will be delegated * otherwise a message is printed to System.err. * @param control * @param test */ public void skippedComparison(Node control, Node test) { if (differenceListenerDelegate != null) { differenceListenerDelegate.skippedComparison(control, test); } else { System.err.println("DifferenceListener.skippedComparison: " + "unhandled control node type=" + control + ", unhandled test node type=" + test); } } /** * ComparisonController implementation. * @param afterDifference * @return true if the difference is not recoverable and * the comparison should be halted, or false if the difference * is recoverable and the comparison can continue */ public boolean haltComparison(Difference afterDifference) { return haltComparison; } /** * Append the message from the result of this Diff instance to a specified * StringBuffer * @param toAppendTo * @return specified StringBuffer with message appended */ public StringBuffer appendMessage(StringBuffer toAppendTo) { compare(); if (messages.length()==0) { messages.append("[identical]"); } // fix for JDK1.4 backwards incompatibility return toAppendTo.append(messages.toString()); } /** * Get the result of this Diff instance as a String * *

      Note:

      This method will perform the comparison and * cache the result if it hasn't been performed already. Any * configuration changes made after calling this method will be * ignored.

      * * @return result of this Diff */ public String toString(){ StringBuffer buf = new StringBuffer(getClass().getName()); appendMessage(buf); return buf.toString(); } /** * Override the DifferenceListener used to determine how * to handle differences that are found. * @param delegate the DifferenceListener instance to delegate handling to. */ public void overrideDifferenceListener(DifferenceListener delegate) { this.differenceListenerDelegate = delegate; } /** * Override the ElementQualifier used to determine which * control and test nodes are comparable for this difference comparison. * @param delegate the ElementQualifier instance to delegate to. */ public void overrideElementQualifier(ElementQualifier delegate) { this.elementQualifierDelegate = delegate; } /** * Override the MatchTracker used to track * successfully matched nodes. * @param delegate the MatchTracker instance to delegate handling to. */ public void overrideMatchTracker(MatchTracker delegate) { this.matchTrackerDelegate = delegate; if (differenceEngine != null) { differenceEngine.setMatchTracker(delegate); } } /** * Lazily initializes the difference engine if it hasn't been set * via a constructor. */ private DifferenceEngine getDifferenceEngine() { return differenceEngine == null ? new DifferenceEngine(this, matchTrackerDelegate) : differenceEngine; } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/ElementNameAndAttributeQualifier.java0000644000000000000000000001456512451007364027535 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.util.Arrays; import org.w3c.dom.Attr; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; /** * More complex interface implementation that tests two elements for tag name * and attribute name comparability. *
      Examples and more at * xmlunit.sourceforge.net * @see DifferenceEngine#compareNodeList(NodeList, NodeList, int, DifferenceListener, ElementQualifier) * @see Diff#overrideElementQualifier(ElementQualifier) */ public class ElementNameAndAttributeQualifier extends ElementNameQualifier { private static final String[] ALL_ATTRIBUTES = {"*"}; private final String[] qualifyingAttrNames; /** * No-args constructor: use all attributes from all elements to determine * whether elements qualify for comparability */ public ElementNameAndAttributeQualifier() { this(ALL_ATTRIBUTES); } /** * Simple constructor for a single qualifying attribute name * @param attrName the value to use to qualify whether two elements can be * compared further for differences */ public ElementNameAndAttributeQualifier(String attrName) { this(new String[] {attrName}); } /** * Extended constructor for multiple qualifying attribute names * @param attrNames the array of values to use to qualify whether two * elements can be compared further for differences */ public ElementNameAndAttributeQualifier(String[] attrNames) { this.qualifyingAttrNames = new String[attrNames.length]; System.arraycopy(attrNames, 0, qualifyingAttrNames, 0, attrNames.length); } /** * Determine whether two elements qualify for further Difference comparison. * @param differenceEngine the DifferenceEngine instance wanting to * determine if the elements are comparable * @param control * @param test * @return true if the two elements qualify for further comparison based on * both the superclass qualification (namespace URI and non- namespaced tag * name), and the presence of qualifying attributes with the same values; * false otherwise */ public boolean qualifyForComparison(Element control, Element test) { if (super.qualifyForComparison(control, test)) { return areAttributesComparable(control, test); } return false; } /** * Determine whether the qualifying attributes are present in both elements * and if so whether their values are the same * @param control * @param test * @return true if all qualifying attributes are present with the same * values, false otherwise */ protected boolean areAttributesComparable(Element control, Element test) { String controlValue, testValue; Attr[] qualifyingAttributes; NamedNodeMap namedNodeMap = control.getAttributes(); if (matchesAllAttributes(qualifyingAttrNames)) { qualifyingAttributes = new Attr[namedNodeMap.getLength()]; for (int n=0; n < qualifyingAttributes.length; ++n) { qualifyingAttributes[n] = (Attr) namedNodeMap.item(n); } } else { qualifyingAttributes = new Attr[qualifyingAttrNames.length]; for (int n=0; n < qualifyingAttrNames.length; ++n) { qualifyingAttributes[n] = (Attr) namedNodeMap.getNamedItem(qualifyingAttrNames[n]); } } String nsURI, name; for (int i=0; i < qualifyingAttributes.length; ++i) { if (qualifyingAttributes[i] != null) { nsURI = qualifyingAttributes[i].getNamespaceURI(); controlValue = qualifyingAttributes[i].getNodeValue(); name = qualifyingAttributes[i].getName(); } else { // cannot be "*" case nsURI = controlValue = ""; name = qualifyingAttrNames[i]; } if (nsURI == null || nsURI.length() == 0) { testValue = test.getAttribute(name); } else { testValue = test.getAttributeNS(nsURI, qualifyingAttributes[i].getLocalName()); } if (controlValue == null) { if (testValue != null) { return false; } } else { if (!controlValue.equals(testValue)) { return false; } } } return true; } private static boolean matchesAllAttributes(String[] attributes) { return Arrays.equals(attributes, ALL_ATTRIBUTES); } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/DoctypeSupport.java0000644000000000000000000002313212451007364024164 0ustar rootroot/* ****************************************************************** Copyright (c) 2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.io.IOException; import org.custommonkey.xmlunit.exceptions.XMLUnitRuntimeException; import org.custommonkey.xmlunit.util.IntegerBuffer; /** * Contains some common code for DoctypeReader and DoctypeInputStream. * *

      When used with DoctypeInputStream it assumes that the whole * DOCTYPE declaration consists of US-ASCII characters.

      */ final class DoctypeSupport { static interface Readable { int read() throws IOException; } final static String DOCTYPE_OPEN_DECL = ""; final static String DOCTYPE = "DOCTYPE "; final static String SYSTEM = " SYSTEM \""; private final static int[] DOCTYPE_INTS = { 'D', 'O', 'C', 'T', 'Y', 'P', 'E', ' ' }; private boolean hasSplit; private final Readable original; private Readable decl; private Readable beforeDoctype; private Readable afterDoctype; /** * Encapsulates a DOCTYPE declaration for the given name and system id. */ DoctypeSupport(String name, String systemId, Readable original, boolean characters, String encoding) { this.original = original; StringBuffer sb = new StringBuffer(DOCTYPE_OPEN_DECL); sb.append(DOCTYPE).append(name).append(SYSTEM) .append(systemId).append('\"').append(DOCTYPE_CLOSE_DECL); String s = sb.toString(); IntegerBuffer buf = new IntegerBuffer(s.length() * (characters ? 1 : 2)); if (characters) { char[] c = s.toCharArray(); for (int i = 0; i < c.length; i++) { buf.append(c[i]); } } else { try { byte[] b = encoding == null ? s.getBytes() : s.getBytes(encoding); for (int i = 0; i < b.length; i++) { buf.append(b[i] & 0xFF); } } catch (java.io.UnsupportedEncodingException use) { throw new XMLUnitRuntimeException("Unsupported encoding", use); } } decl = new IntBufferReadable(buf); } /** * Reads the next character from the declaration. * @return -1 if the end of the declaration has been reached. */ int read() throws IOException { int nextInt = -1; if (!hasSplit) { split(); } if (beforeDoctype != null) { nextInt = beforeDoctype.read(); if (nextInt == -1) { beforeDoctype = null; } } if (nextInt == -1 && decl != null) { nextInt = decl.read(); if (nextInt == -1) { decl = null; } } if (nextInt == -1 && afterDoctype != null) { nextInt = afterDoctype.read(); if (nextInt == -1) { afterDoctype = null; } } if (nextInt == -1) { nextInt = original.read(); } return nextInt; } /** * Reads enough of the original Readable to know where to place * the declaration. Fills beforeDecl and afterDecl from the data * read ahead. Swallows the original DOCTYPE if there is one. */ private void split() throws IOException { hasSplit = true; IntegerBuffer before = new IntegerBuffer(); IntegerBuffer after = new IntegerBuffer(); int current; boolean ready = false; boolean stillNeedToSeeDoctype = true; while (!ready && (current = original.read()) != -1) { if (Character.isWhitespace((char) current)) { before.append(current); } else if (current == '<') { // could be XML declaration, comment, PI, DOCTYPE // or the first element int[] elementOrDeclOr = readUntilCloseCharIsReached(); if (elementOrDeclOr.length > 0) { if (elementOrDeclOr[0] == '?') { // XML declaration or PI before.append('<'); before.append(elementOrDeclOr); } else if (elementOrDeclOr[0] != '!') { // first element after.append('<'); after.append(elementOrDeclOr); stillNeedToSeeDoctype = false; ready = true; } else { // comment or doctype IntegerBuffer b = new IntegerBuffer(elementOrDeclOr.length); b.append(elementOrDeclOr); if (b.indexOf(DOCTYPE_INTS) == -1) { after.append('<'); after.append(elementOrDeclOr); } else { // swallow old declaration stillNeedToSeeDoctype = false; } ready = true; } } else { after.append('<'); stillNeedToSeeDoctype = false; ready = true; } } else { after.append(current); stillNeedToSeeDoctype = false; ready = true; } } // need to eliminate original DOCTYPE if it exists while (stillNeedToSeeDoctype && (current = original.read()) != -1) { if (Character.isWhitespace((char) current)) { after.append(current); } else if (current == '<') { int[] elementOrDeclOr = readUntilCloseCharIsReached(); if (elementOrDeclOr.length > 0) { if (elementOrDeclOr[0] == '?') { // PI after.append('<'); after.append(elementOrDeclOr); } else if (elementOrDeclOr[0] != '!') { // first element after.append('<'); after.append(elementOrDeclOr); stillNeedToSeeDoctype = false; } else { // comment or doctype IntegerBuffer b = new IntegerBuffer(elementOrDeclOr.length); b.append(elementOrDeclOr); if (b.indexOf(DOCTYPE_INTS) == -1) { after.append('<'); after.append(elementOrDeclOr); } else { // swallow old declaration stillNeedToSeeDoctype = false; } } } else { after.append('<'); stillNeedToSeeDoctype = false; } } else { after.append(current); stillNeedToSeeDoctype = false; } } beforeDoctype = before.size() > 0 ? new IntBufferReadable(before) : null; afterDoctype = after.size() > 0 ? new IntBufferReadable(after) : null; } private int[] readUntilCloseCharIsReached() throws IOException { IntegerBuffer i = new IntegerBuffer(); int intRead = -1; int openCount = 1; while (openCount > 0 && (intRead = original.read()) != -1) { i.append(intRead); if (intRead == '<') { openCount++; } if (intRead == '>') { openCount--; } } return i.toIntArray(); } private static class IntBufferReadable implements Readable { private final int[] buf; private int off; IntBufferReadable(IntegerBuffer b) { buf = b.toIntArray(); } public int read() { return off >= buf.length ? -1 : buf[off++]; } } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/NodeInputStream.java0000644000000000000000000001077712451007364024254 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.IOException; import java.util.Properties; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Node; /** * Adapter class to present the content of a DOM Node (e.g. a Document) as an * InputStream using a DOM to Stream transformation. *
      Examples and more at xmlunit.sourceforge.net */ public class NodeInputStream extends InputStream { private final Node rootNode; private final ByteArrayOutputStream nodeContentBytes; private final Properties outputProperties; private int atPos = 0; /** * Simple constructor * @param rootNode the node to be presented as an input stream */ public NodeInputStream(Node rootNode) { this(rootNode, null); } /** * Simple constructor * @param rootNode the node to be presented as an input stream */ public NodeInputStream(Node rootNode, Properties outputProperties) { this.rootNode = rootNode; nodeContentBytes = new ByteArrayOutputStream(); this.outputProperties = outputProperties; } /** * Do the actual work of serializing the node to bytes * @throws IOException if serialization goes awry */ private void ensureContentAvailable() throws IOException { if (nodeContentBytes.size() > 0) { return; } try { Transform serializeTransform = new Transform(rootNode); if (outputProperties!=null) { serializeTransform.setOutputProperties(outputProperties); } StreamResult byteResult = new StreamResult(nodeContentBytes); serializeTransform.transformTo(byteResult); } catch (Exception e) { throw new IOException("Unable to serialize document to outputstream: " + e.toString()); } } /** * InputStream method * @return byte as read * @throws IOException */ public int read() throws IOException { ensureContentAvailable(); if (reallyAvailable()==0) { return -1; } int contentByte = nodeContentBytes.toByteArray()[atPos]; atPos++; return contentByte; } /** * InputStream method * Note that calling close allows a repeated read of the content * @throws IOException */ public void close() throws IOException { atPos = 0; } /** * InputStream method * @return number of bytes available */ public int available() throws IOException { ensureContentAvailable(); return reallyAvailable(); } /** * @return really available */ private int reallyAvailable() { return nodeContentBytes.size() - atPos; } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/XMLTestCase.java0000644000000000000000000011026212451007364023255 0ustar rootroot/* ****************************************************************** Copyright (c) 2001, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import org.custommonkey.xmlunit.exceptions.ConfigurationException; import org.custommonkey.xmlunit.exceptions.XpathException; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import junit.framework.TestCase; import org.w3c.dom.Document; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * JUnit TestCase subclass: extend this to add XML assertion facilities to your * test suites. * Available assertions are provided by static methods of the {@link XMLAssert * XMLAssert} class. * NB: All underlying similarity and difference testing is done using {@link * Diff Diff} instances which can be instantiated and evaluated independently of * an XMLTestCase. * @see Diff#similar() * @see Diff#identical() *
      Examples and more at xmlunit.sourceforge.net */ public abstract class XMLTestCase extends TestCase implements XSLTConstants { /** * Construct a new XML test case. */ public XMLTestCase(){ super(); } /** * Construct a new test case. * @param name Name of test */ public XMLTestCase(String name){ super(name); } /** * Compare XML documents provided by two InputSource classes * @param control Control document * @param test Document to test * @return Diff object describing differences in documents * @throws SAXException * @throws IOException */ public Diff compareXML(InputSource control, InputSource test) throws SAXException, IOException { return XMLUnit.compareXML(control, test); } /** * Compare XML documents provided by two Reader classes * @param control Control document * @param test Document to test * @return Diff object describing differences in documents * @throws SAXException * @throws IOException */ public Diff compareXML(Reader control, Reader test) throws SAXException, IOException { return XMLUnit.compareXML(control, test); } /** * Compare XML documents provided by two Reader classes * @param control Control document * @param test Document to test * @return Diff object describing differences in documents * @throws SAXException * @throws IOException */ public Diff compareXML(String control, Reader test) throws SAXException, IOException { return XMLUnit.compareXML(new StringReader(control), test); } /** * Compare XML documents provided by two Reader classes * @param control Control document * @param test Document to test * @return Diff object describing differences in documents * @throws SAXException * @throws IOException */ public Diff compareXML(Reader control, String test) throws SAXException, IOException { return XMLUnit.compareXML(control, new StringReader(test)); } /** * Compare two XML documents provided as strings * @param control Control document * @param test Document to test * @return Diff object describing differences in documents * @throws SAXException * @throws IOException */ public Diff compareXML(String control, String test) throws SAXException, IOException { return XMLUnit.compareXML(control, test); } /** * Compare two XML documents provided as strings * @param control Control document * @param test Document to test * @return Diff object describing differences in documents */ public Diff compareXML(Document control, Document test) { return XMLUnit.compareXML(control, test); } /** * Assert that the result of an XML comparison is or is not similar. * @param diff the result of an XML comparison * @param assertion true if asserting that result is similar */ public void assertXMLEqual(Diff diff, boolean assertion) { XMLAssert.assertXMLEqual(diff, assertion); } /** * Assert that the result of an XML comparison is or is not similar. * @param msg additional message to display if assertion fails * @param diff the result of an XML comparison * @param assertion true if asserting that result is similar */ public void assertXMLEqual(String msg, Diff diff, boolean assertion) { XMLAssert.assertXMLEqual(msg, diff, assertion); } /** * Assert that the result of an XML comparison is or is not identical * @param diff the result of an XML comparison * @param assertion true if asserting that result is identical */ public void assertXMLIdentical(Diff diff, boolean assertion) { XMLAssert.assertXMLIdentical(diff.toString(), diff, assertion); } /** * Assert that the result of an XML comparison is or is not identical * @param msg Message to display if assertion fails * @param diff the result of an XML comparison * @param assertion true if asserting that result is identical */ public void assertXMLIdentical(String msg, Diff diff, boolean assertion) { XMLAssert.assertXMLIdentical(msg, diff, assertion); } /** * Assert that two XML documents are similar * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public void assertXMLEqual(InputSource control, InputSource test) throws SAXException, IOException { XMLAssert.assertXMLEqual(control, test); } /** * Assert that two XML documents are similar * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public void assertXMLEqual(String control, String test) throws SAXException, IOException { XMLAssert.assertXMLEqual(control, test); } /** * Assert that two XML documents are similar * @param control XML to be compared against * @param test XML to be tested */ public void assertXMLEqual(Document control, Document test) { XMLAssert.assertXMLEqual(control, test); } /** * Assert that two XML documents are similar * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public void assertXMLEqual(Reader control, Reader test) throws SAXException, IOException { XMLAssert.assertXMLEqual(control, test); } /** * Assert that two XML documents are similar * @param err Message to be displayed on assertion failure * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public void assertXMLEqual(String err, String control, String test) throws SAXException, IOException { XMLAssert.assertXMLEqual(err, control, test); } /** * Assert that two XML documents are similar * @param err Message to be displayed on assertion failure * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public void assertXMLEqual(String err, InputSource control, InputSource test) throws SAXException, IOException { XMLAssert.assertXMLEqual(err, control, test); } /** * Assert that two XML documents are similar * @param err Message to be displayed on assertion failure * @param control XML to be compared against * @param test XML to be tested */ public void assertXMLEqual(String err, Document control, Document test) { XMLAssert.assertXMLEqual(err, control, test); } /** * Assert that two XML documents are similar * @param err Message to be displayed on assertion failure * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public void assertXMLEqual(String err, Reader control, Reader test) throws SAXException, IOException { XMLAssert.assertXMLEqual(err, control, test); } /** * Assert that two XML documents are NOT similar * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public void assertXMLNotEqual(InputSource control, InputSource test) throws SAXException, IOException { XMLAssert.assertXMLNotEqual(control, test); } /** * Assert that two XML documents are NOT similar * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public void assertXMLNotEqual(String control, String test) throws SAXException, IOException { XMLAssert.assertXMLNotEqual(control, test); } /** * Assert that two XML documents are NOT similar * @param control XML to be compared against * @param test XML to be tested */ public void assertXMLNotEqual(Document control, Document test) { XMLAssert.assertXMLNotEqual(control, test); } /** * Assert that two XML documents are NOT similar * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public void assertXMLNotEqual(Reader control, Reader test) throws SAXException, IOException { XMLAssert.assertXMLNotEqual(control, test); } /** * Assert that two XML documents are NOT similar * @param err Message to be displayed on assertion failure * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public void assertXMLNotEqual(String err, InputSource control, InputSource test) throws SAXException, IOException { XMLAssert.assertXMLNotEqual(err, control, test); } /** * Assert that two XML documents are NOT similar * @param err Message to be displayed on assertion failure * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public void assertXMLNotEqual(String err, String control, String test) throws SAXException, IOException { XMLAssert.assertXMLNotEqual(err, control, test); } /** * Assert that two XML documents are NOT similar * @param err Message to be displayed on assertion failure * @param control XML to be compared against * @param test XML to be tested */ public void assertXMLNotEqual(String err, Document control, Document test) { XMLAssert.assertXMLNotEqual(err, control, test); } /** * Assert that two XML documents are NOT similar * @param err Message to be displayed on assertion failure * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public void assertXMLNotEqual(String err, Reader control, Reader test) throws SAXException, IOException { XMLAssert.assertXMLNotEqual(err, control, test); } /** * Assert that the node lists of two Xpaths in the same document are equal * @param xpathOne * @param xpathTwo * @param document * @see XpathEngine */ public void assertXpathsEqual(String controlXpath, String testXpath, InputSource document) throws SAXException, IOException, XpathException { XMLAssert.assertXpathsEqual(controlXpath, testXpath, document); } /** * Assert that the node lists of two Xpaths in the same document are equal * @param xpathOne * @param xpathTwo * @param document * @see XpathEngine */ public void assertXpathsEqual(String controlXpath, String testXpath, Document document) throws XpathException { XMLAssert.assertXpathsEqual(controlXpath, testXpath, document); } /** * Assert that the node lists of two Xpaths in the same XML string are * equal * @param xpathOne * @param xpathTwo * @param inXMLString * @throws SAXException * @throws IOException */ public void assertXpathsEqual(String controlXpath, String testXpath, String inXMLString) throws SAXException, IOException, XpathException { XMLAssert.assertXpathsEqual(controlXpath, testXpath, inXMLString); } /** * Assert that the node lists of two Xpaths in two XML pieces are equal * @param xpathOne * @param control * @param xpathTwo * @param test * @throws SAXException * @throws IOException */ public void assertXpathsEqual(String controlXpath, InputSource control, String testXpath, InputSource test) throws SAXException, IOException, XpathException { XMLAssert.assertXpathsEqual(controlXpath, control, testXpath, test); } /** * Assert that the node lists of two Xpaths in two XML strings are equal * @param xpathOne * @param inControlXMLString * @param xpathTwo * @param inTestXMLString * @throws SAXException * @throws IOException */ public void assertXpathsEqual(String controlXpath, String inControlXMLString, String testXpath, String inTestXMLString) throws SAXException, IOException, XpathException { XMLAssert.assertXpathsEqual(controlXpath, inControlXMLString, testXpath, inTestXMLString); } /** * Assert that the node lists of two Xpaths in two documents are equal * @param xpathOne * @param xpathTwo * @param document * @see XpathEngine */ public void assertXpathsEqual(String controlXpath, Document controlDocument, String testXpath, Document testDocument) throws XpathException { XMLAssert.assertXpathsEqual(controlXpath, controlDocument, testXpath, testDocument); } /** * Assert that the node lists of two Xpaths in the same document are NOT equal * @param xpathOne * @param xpathTwo * @param document * @see XpathEngine */ public void assertXpathsNotEqual(String controlXpath, String testXpath, Document document) throws XpathException { XMLAssert.assertXpathsNotEqual(controlXpath, testXpath, document); } /** * Assert that the node lists of two Xpaths in the same XML are NOT * equal * @param xpathOne * @param xpathTwo * @param control * @throws SAXException * @throws IOException */ public void assertXpathsNotEqual(String controlXpath, String testXpath, InputSource control) throws SAXException, IOException, XpathException { XMLAssert.assertXpathsNotEqual(controlXpath, testXpath, control); } /** * Assert that the node lists of two Xpaths in the same XML string are NOT * equal * @param xpathOne * @param xpathTwo * @param inXMLString * @throws SAXException * @throws IOException */ public void assertXpathsNotEqual(String controlXpath, String testXpath, String inXMLString) throws SAXException, IOException, XpathException { XMLAssert.assertXpathsNotEqual(controlXpath, testXpath, inXMLString); } /** * Assert that the node lists of two Xpaths in two pieces of XML * are NOT equal * @param xpathOne * @param control * @param xpathTwo * @param test * @throws SAXException * @throws IOException */ public void assertXpathsNotEqual(String controlXpath, InputSource control, String testXpath, InputSource test) throws SAXException, IOException, XpathException { XMLAssert.assertXpathsNotEqual(controlXpath, control, testXpath, test); } /** * Assert that the node lists of two Xpaths in two XML strings are NOT equal * @param xpathOne * @param inControlXMLString * @param xpathTwo * @param inTestXMLString * @throws SAXException * @throws IOException */ public void assertXpathsNotEqual(String controlXpath, String inControlXMLString, String testXpath, String inTestXMLString) throws SAXException, IOException, XpathException { XMLAssert.assertXpathsNotEqual(controlXpath, inControlXMLString, testXpath, inTestXMLString); } /** * Assert that the node lists of two Xpaths in two documents are NOT equal * @param xpathOne * @param xpathTwo * @param document * @see XpathEngine */ public void assertXpathsNotEqual(String controlXpath, Document controlDocument, String testXpath, Document testDocument) throws XpathException { XMLAssert.assertXpathsNotEqual(controlXpath, controlDocument, testXpath, testDocument); } /** * Assert that the evaluation of two Xpaths in the same document are equal * @param xpathOne * @param xpathTwo * @param document * @see XpathEngine */ public void assertXpathValuesEqual(String controlXpath, String testXpath, Document document) throws XpathException { XMLAssert.assertXpathValuesEqual(controlXpath, testXpath, document); } /** * Assert that the evaluation of two Xpaths in the same XML are * equal * @param xpathOne * @param xpathTwo * @param control * @throws SAXException * @throws IOException */ public void assertXpathValuesEqual(String controlXpath, String testXpath, InputSource control) throws SAXException, IOException, XpathException { XMLAssert.assertXpathValuesEqual(controlXpath, testXpath, control); } /** * Assert that the evaluation of two Xpaths in the same XML string are * equal * @param xpathOne * @param xpathTwo * @param inXMLString * @throws SAXException * @throws IOException */ public void assertXpathValuesEqual(String controlXpath, String testXpath, String inXMLString) throws SAXException, IOException, XpathException { XMLAssert.assertXpathValuesEqual(controlXpath, testXpath, inXMLString); } /** * Assert that the evaluation of two Xpaths in two XML strings are equal * @param xpathOne * @param control * @param xpathTwo * @param test * @throws SAXException * @throws IOException */ public void assertXpathValuesEqual(String controlXpath, InputSource control, String testXpath, InputSource test) throws SAXException, IOException, XpathException { XMLAssert.assertXpathValuesEqual(controlXpath, control, testXpath, test); } /** * Assert that the evaluation of two Xpaths in two XML strings are equal * @param xpathOne * @param inControlXMLString * @param xpathTwo * @param inTestXMLString * @throws SAXException * @throws IOException */ public void assertXpathValuesEqual(String controlXpath, String inControlXMLString, String testXpath, String inTestXMLString) throws SAXException, IOException, XpathException { XMLAssert.assertXpathValuesEqual(controlXpath, inControlXMLString, testXpath, inTestXMLString); } /** * Assert that the evaluation of two Xpaths in two documents are equal * @param xpathOne * @param xpathTwo * @param document * @see XpathEngine */ public void assertXpathValuesEqual(String controlXpath, Document controlDocument, String testXpath, Document testDocument) throws XpathException { XMLAssert.assertXpathValuesEqual(controlXpath, controlDocument, testXpath, testDocument); } /** * Assert that the evaluation of two Xpaths in the same XML string are * NOT equal * @param xpathOne * @param xpathTwo * @param control * @throws SAXException * @throws IOException */ public void assertXpathValuesNotEqual(String controlXpath, String testXpath, InputSource control) throws SAXException, IOException, XpathException { XMLAssert.assertXpathValuesNotEqual(controlXpath, testXpath, control); } /** * Assert that the evaluation of two Xpaths in the same XML string are * NOT equal * @param xpathOne * @param xpathTwo * @param inXMLString * @throws SAXException * @throws IOException */ public void assertXpathValuesNotEqual(String controlXpath, String testXpath, String inXMLString) throws SAXException, IOException, XpathException { XMLAssert.assertXpathValuesNotEqual(controlXpath, testXpath, inXMLString); } /** * Assert that the evaluation of two Xpaths in the same document are * NOT equal * @param xpathOne * @param xpathTwo * @param document */ public void assertXpathValuesNotEqual(String controlXpath, String testXpath, Document document) throws XpathException { XMLAssert.assertXpathValuesNotEqual(controlXpath, testXpath, document); } /** * Assert that the evaluation of two Xpaths in two XML strings are * NOT equal * @param xpathOne * @param control * @param xpathTwo * @param test * @throws SAXException * @throws IOException */ public void assertXpathValuesNotEqual(String controlXpath, InputSource control, String testXpath, InputSource test) throws SAXException, IOException, XpathException { XMLAssert.assertXpathValuesNotEqual(controlXpath, control, testXpath, test); } /** * Assert that the evaluation of two Xpaths in two XML strings are * NOT equal * @param xpathOne * @param inControlXMLString * @param xpathTwo * @param inTestXMLString * @throws SAXException * @throws IOException */ public void assertXpathValuesNotEqual(String controlXpath, String inControlXMLString, String testXpath, String inTestXMLString) throws SAXException, IOException, XpathException { XMLAssert.assertXpathValuesNotEqual(controlXpath, inControlXMLString, testXpath, inTestXMLString); } /** * Assert that the evaluation of two Xpaths in two documents are * NOT equal * @param xpathOne * @param xpathTwo * @param document */ public void assertXpathValuesNotEqual(String controlXpath, Document controlDocument, String testXpath, Document testDocument) throws XpathException { XMLAssert.assertXpathValuesNotEqual(controlXpath, controlDocument, testXpath, testDocument); } /** * Assert the value of an Xpath expression in an XML String * @param expectedValue * @param xpathExpression * @param control * @throws SAXException * @throws IOException * @see XpathEngine which provides the underlying evaluation mechanism */ public void assertXpathEvaluatesTo(String expectedValue, String xpathExpression, InputSource control) throws SAXException, IOException, XpathException { XMLAssert.assertXpathEvaluatesTo(expectedValue, xpathExpression, control); } /** * Assert the value of an Xpath expression in an XML String * @param expectedValue * @param xpathExpression * @param inXMLString * @throws SAXException * @throws IOException * @see XpathEngine which provides the underlying evaluation mechanism */ public void assertXpathEvaluatesTo(String expectedValue, String xpathExpression, String inXMLString) throws SAXException, IOException, XpathException { XMLAssert.assertXpathEvaluatesTo(expectedValue, xpathExpression, inXMLString); } /** * Assert the value of an Xpath expression in an DOM Document * @param expectedValue * @param xpathExpression * @param inDocument * @param ctx * @see XpathEngine which provides the underlying evaluation mechanism */ public void assertXpathEvaluatesTo(String expectedValue, String xpathExpression, Document inDocument) throws XpathException { XMLAssert.assertXpathEvaluatesTo(expectedValue, xpathExpression, inDocument); } /** * Assert that a specific XPath exists in some given XML * @param inXpathExpression * @param xml * @see XpathEngine which provides the underlying evaluation mechanism */ public void assertXpathExists(String xPathExpression, InputSource xml) throws IOException, SAXException, XpathException { XMLAssert.assertXpathExists(xPathExpression, xml); } /** * Assert that a specific XPath exists in some given XML * @param inXpathExpression * @param inXMLString * @see XpathEngine which provides the underlying evaluation mechanism */ public void assertXpathExists(String xPathExpression, String inXMLString) throws IOException, SAXException, XpathException { XMLAssert.assertXpathExists(xPathExpression, inXMLString); } /** * Assert that a specific XPath exists in some given XML * @param inXpathExpression * @param inDocument * @param ctx * @see XpathEngine which provides the underlying evaluation mechanism */ public void assertXpathExists(String xPathExpression, Document inDocument) throws XpathException { XMLAssert.assertXpathExists(xPathExpression, inDocument); } /** * Assert that a specific XPath does NOT exist in some given XML * @param inXpathExpression * @param xml * @see XpathEngine which provides the underlying evaluation mechanism */ public void assertXpathNotExists(String xPathExpression, InputSource xml) throws IOException, SAXException, XpathException { XMLAssert.assertXpathNotExists(xPathExpression, xml); } /** * Assert that a specific XPath does NOT exist in some given XML * @param inXpathExpression * @param inXMLString * @see XpathEngine which provides the underlying evaluation mechanism */ public void assertXpathNotExists(String xPathExpression, String inXMLString) throws IOException, SAXException, XpathException { XMLAssert.assertXpathNotExists(xPathExpression, inXMLString); } /** * Assert that a specific XPath does NOT exist in some given XML * @param inXpathExpression * @param inDocument * @see XpathEngine which provides the underlying evaluation mechanism */ public void assertXpathNotExists(String xPathExpression, Document inDocument) throws XpathException { XMLAssert.assertXpathNotExists(xPathExpression, inDocument); } /** * Assert that a piece of XML contains valid XML: the input must * contain a DOCTYPE declaration to be validated * @param xml * @throws SAXException * @throws ConfigurationException if validation could not be turned on * @see Validator */ public void assertXMLValid(InputSource xml) throws SAXException, ConfigurationException { XMLAssert.assertXMLValid(xml); } /** * Assert that a String containing XML contains valid XML: the String must * contain a DOCTYPE declaration to be validated * @param xmlString * @throws SAXException * @throws ConfigurationException if validation could not be turned on * @see Validator */ public void assertXMLValid(String xmlString) throws SAXException, ConfigurationException { XMLAssert.assertXMLValid(xmlString); } /** * Assert that a piece of XML contains valid XML: the document must * contain a DOCTYPE to be validated, but the validation will use the * systemId to obtain the DTD * @param xml * @param systemId * @throws SAXException * @throws ConfigurationException if validation could not be turned on * @see Validator */ public void assertXMLValid(InputSource xml, String systemId) throws SAXException, ConfigurationException { XMLAssert.assertXMLValid(xml, systemId); } /** * Assert that a String containing XML contains valid XML: the String must * contain a DOCTYPE to be validated, but the validation will use the * systemId to obtain the DTD * @param xmlString * @param systemId * @throws SAXException * @throws ConfigurationException if validation could not be turned on * @see Validator */ public void assertXMLValid(String xmlString, String systemId) throws SAXException, ConfigurationException { XMLAssert.assertXMLValid(xmlString, systemId); } /** * Assert that a piece of XML contains valid XML: the document * will be given a DOCTYPE to be validated with the name and * systemId specified regardless of whether it already contains a * doctype declaration. * @param xml * @param systemId * @param doctype * @throws SAXException * @throws ConfigurationException if validation could not be turned on * @see Validator */ public void assertXMLValid(InputSource xml, String systemId, String doctype) throws SAXException, ConfigurationException { XMLAssert.assertXMLValid(xml, systemId, doctype); } /** * Assert that a String containing XML contains valid XML: the String will * be given a DOCTYPE to be validated with the name and systemId specified * regardless of whether it already contains a doctype declaration. * @param xmlString * @param systemId * @param doctype * @throws SAXException * @throws ConfigurationException if validation could not be turned on * @see Validator */ public void assertXMLValid(String xmlString, String systemId, String doctype) throws SAXException, ConfigurationException { XMLAssert.assertXMLValid(xmlString, systemId, doctype); } /** * Assert that a Validator instance returns isValid() == true * @param validator */ public void assertXMLValid(Validator validator) { XMLAssert.assertXMLValid(validator); } /** * Execute a NodeTest for a single node type * and assert that it passes * @param xml XML to be tested * @param tester The test strategy * @param nodeType The node type to be tested: constants defined * in {@link Node org.w3c.dom.Node} e.g. Node.ELEMENT_NODE * @throws SAXException * @throws IOException * @see AbstractNodeTester * @see CountingNodeTester */ public void assertNodeTestPasses(InputSource xml, NodeTester tester, short nodeType) throws SAXException, IOException { XMLAssert.assertNodeTestPasses(xml, tester, nodeType); } /** * Execute a NodeTest for a single node type * and assert that it passes * @param xmlString XML to be tested * @param tester The test strategy * @param nodeType The node type to be tested: constants defined * in {@link Node org.w3c.dom.Node} e.g. Node.ELEMENT_NODE * @throws SAXException * @throws IOException * @see AbstractNodeTester * @see CountingNodeTester */ public void assertNodeTestPasses(String xmlString, NodeTester tester, short nodeType) throws SAXException, IOException { XMLAssert.assertNodeTestPasses(xmlString, tester, nodeType); } /** * Execute a NodeTest for multiple node types and make an * assertion about it whether it is expected to pass * @param test a NodeTest instance containing the XML source to be tested * @param tester The test strategy * @param nodeTypes The node types to be tested: constants defined * in {@link Node org.w3c.dom.Node} e.g. Node.ELEMENT_NODE * @param assertion true if the test is expected to pass, false otherwise * @see AbstractNodeTester * @see CountingNodeTester */ public void assertNodeTestPasses(NodeTest test, NodeTester tester, short[] nodeTypes, boolean assertion) { XMLAssert.assertNodeTestPasses(test, tester, nodeTypes, assertion); } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/ElementQualifier.java0000644000000000000000000000500112451007364024406 0ustar rootroot/* ****************************************************************** Copyright (c) 2001, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import org.w3c.dom.Element; /** * Interface used by the DifferenceEngine class to determine which elements can * be compared within a NodeList of child nodes. * *
      Examples and more at * xmlunit.sourceforge.net * @see DifferenceEngine#compareNodeList(NodeList, NodeList, int, DifferenceListener, ElementQualifier) * @see Diff#overrideElementQualifier(ElementQualifier) */ public interface ElementQualifier { /** * Determine whether two elements are comparable * @param control an Element from the control XML NodeList * @param test an Element from the test XML NodeList * @return true if the elements are comparable, false otherwise */ boolean qualifyForComparison(Element control, Element test); } xmlunit-1.6/src/java/org/custommonkey/xmlunit/util/0000755000000000000000000000000012451007364021271 5ustar rootrootxmlunit-1.6/src/java/org/custommonkey/xmlunit/util/IntegerBuffer.java0000644000000000000000000001070612451007364024667 0ustar rootroot/* ****************************************************************** Copyright (c) 2001, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.util; /** * Simplistic dynamically growing buffer of integers used by DoctypeSupport. * *

      No attempt has been made to make this class thread-safe at all. * The append methods and indexOf are not too efficient either, but * work for what we need.

      */ public class IntegerBuffer { // should be big enough for the DoctypeSupport use case private static final int INITIAL_SIZE = 512; private int[] buffer; private int currentSize; /** * Creates a new buffer. */ public IntegerBuffer() { this(INITIAL_SIZE); } /** * Creates a new buffer with the given initial capacity. */ public IntegerBuffer(int capacity) { buffer = new int[capacity]; } /** * Returns the current size. */ public int size() { return currentSize; } /** * Returns the current capacity (the size the buffer can use * before it needs to grow). */ public int capacity() { return buffer.length; } /** * Appends a single int. */ public void append(int i) { // could simply delegate to the other append methods, but this // (avoiding arraycopy) is more efficient. while (currentSize >= buffer.length) { grow(); } buffer[currentSize++] = i; } /** * Appends an array of ints. */ public void append(int[] i) { // could simply delegate to the other append methods, but this // (avoiding repeated comparisions) is more efficient. while (currentSize + i.length > buffer.length) { grow(); } System.arraycopy(i, 0, buffer, currentSize, i.length); currentSize += i.length; } /** * Returns an arry view of this buffer's content. */ public int[] toIntArray() { int[] i = new int[currentSize]; System.arraycopy(buffer, 0, i, 0, currentSize); return i; } /** * finds sequence in current buffer. * * @return index of sequence or -1 if not found */ public int indexOf(int[] sequence) { int index = -1; for (int i = 0; index == -1 && i <= currentSize - sequence.length; i++) { if (buffer[i] == sequence[0]) { boolean matches = true; for (int j = 1; matches && j < sequence.length; j++) { if (buffer[i + j] != sequence[j]) { matches = false; } } if (matches) { index = i; } } } return index; } private void grow() { int[] i = new int[buffer.length * 2 + 1]; System.arraycopy(buffer, 0, i, 0, buffer.length); buffer = i; } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/XpathNodeTracker.java0000644000000000000000000002435412451007364024375 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.w3c.dom.Attr; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * Tracks Nodes visited by the DifferenceEngine and * converts that information into an Xpath-String to supply * to the NodeDetail of a Difference instance * @see NodeDetail#getXpathLocation() * @see Difference#getControlNodeDetail * @see Difference#getTestNodeDetail */ public class XpathNodeTracker implements XMLConstants { private final List indentationList = new ArrayList(); private TrackingEntry currentEntry; /** * Simple constructor */ public XpathNodeTracker() { newLevel(); } /** * Clear state data. * Call if required to reuse an existing instance. */ public void reset() { indentationList.clear(); indent(); } /** * Call before examining child nodes one level of indentation into DOM */ public void indent() { if (currentEntry != null) { currentEntry.clearTrackedAttribute(); } newLevel(); } private void newLevel() { currentEntry = new TrackingEntry(); indentationList.add(currentEntry); } /** * Call after processing attributes of an element and turining to * compare the child nodes. */ public void clearTrackedAttribute() { if (currentEntry != null) { currentEntry.clearTrackedAttribute(); } } /** * Call after examining child nodes, ie before returning back one level of indentation from DOM */ public void outdent() { int last = indentationList.size() - 1; indentationList.remove(last); --last; if (last >= 0) { currentEntry = (TrackingEntry) indentationList.get(last); } } /** * Call when visiting a node whose xpath location needs tracking * @param node the Node being visited */ public void visited(Node node) { String nodeName; switch(node.getNodeType()) { case Node.ATTRIBUTE_NODE: nodeName = ((Attr)node).getLocalName(); if (nodeName == null || nodeName.length() == 0) { nodeName = node.getNodeName(); } visitedAttribute(nodeName); break; case Node.ELEMENT_NODE: nodeName = ((Element)node).getLocalName(); if (nodeName == null || nodeName.length() == 0) { nodeName = node.getNodeName(); } visitedNode(node, nodeName); break; case Node.COMMENT_NODE: visitedNode(node, XPATH_COMMENT_IDENTIFIER); break; case Node.PROCESSING_INSTRUCTION_NODE: visitedNode(node, XPATH_PROCESSING_INSTRUCTION_IDENTIFIER); break; case Node.CDATA_SECTION_NODE: case Node.TEXT_NODE: visitedNode(node, XPATH_CHARACTER_NODE_IDENTIFIER); break; default: // ignore unhandled node types break; } } protected void visitedNode(Node visited, String value) { currentEntry.trackNode(visited, value); } protected void visitedAttribute(String visited) { currentEntry.trackAttribute(visited); } /** * Preload the items in a NodeList by visiting each in turn * Required for pieces of test XML whose node children can be visited * out of sequence by a DifferenceEngine comparison * @param nodeList the items to preload */ public void preloadNodeList(NodeList nodeList) { currentEntry.trackNodesAsWellAsValues(true); int length = nodeList.getLength(); for (int i=0; i < length; ++i) { visited(nodeList.item(i)); } currentEntry.trackNodesAsWellAsValues(false); } /** * Preload the items in a List by visiting each in turn * Required for pieces of test XML whose node children can be visited * out of sequence by a DifferenceEngine comparison * @param nodeList the items to preload */ public void preloadChildList(List nodeList) { currentEntry.trackNodesAsWellAsValues(true); int length = nodeList.size(); for (int i=0; i < length; ++i) { visited((Node) nodeList.get(i)); } currentEntry.trackNodesAsWellAsValues(false); } /** * @return the last visited node as an xpath-location String */ public String toXpathString() { StringBuffer buf = new StringBuffer(); TrackingEntry nextEntry; for (Iterator iter = indentationList.iterator(); iter.hasNext(); ) { nextEntry = (TrackingEntry) iter.next(); nextEntry.appendEntryTo(buf); } return buf.toString(); } /** * Wrapper class around a mutable int value * Avoids creation of many immutable Integer objects */ private static final class Int { private int value; public Int(int startAt) { value = startAt; } public void increment() { ++value; } public int getValue() { return value; } public Integer toInteger() { return new Integer(value); } } /** * Holds node tracking details - one instance is used for each level of indentation in a DOM * Provides reference between a String-ified Node value and the xpath index of that value */ private static final class TrackingEntry { private final Map valueMap = new HashMap(); private String currentValue, currentAttribute; private Map nodeReferenceMap; private boolean trackNodeReferences = false; private Integer nodeReferenceLookup = null; private Int lookup(String value) { return (Int) valueMap.get(value); } /** * Keep a reference to the current visited (non-attribute) node * @param visited the non-attribute node visited * @param value the String-ified value of the non-attribute node visited */ public void trackNode(Node visited, String value) { if (nodeReferenceMap == null || trackNodeReferences) { Int occurrence = lookup(value); if (occurrence == null) { occurrence = new Int(1); valueMap.put(value, occurrence); } else { occurrence.increment(); } if (trackNodeReferences) { nodeReferenceMap.put(visited, occurrence.toInteger()); } } else { nodeReferenceLookup = (Integer) nodeReferenceMap.get(visited); } currentValue = value; clearTrackedAttribute(); } /** * Keep a reference to the visited attribute at the current visited node * @param value the attribute visited */ public void trackAttribute(String visited) { currentAttribute = visited; } /** * Clear any reference to the current visited attribute */ public void clearTrackedAttribute() { currentAttribute = null; } /** * Append the details of the current visited node to a StringBuffer * @param buf the StringBuffer to append to */ public void appendEntryTo(StringBuffer buf) { if (currentValue == null) { return; } buf.append(XPATH_SEPARATOR).append(currentValue); int value = nodeReferenceLookup == null ? lookup(currentValue).getValue() : nodeReferenceLookup.intValue(); buf.append(XPATH_NODE_INDEX_START).append(value).append(XPATH_NODE_INDEX_END); if (currentAttribute != null) { buf.append(XPATH_SEPARATOR).append(XPATH_ATTRIBUTE_IDENTIFIER) .append(currentAttribute); } } public void trackNodesAsWellAsValues(boolean yesNo) { this.trackNodeReferences = yesNo; if (yesNo) { nodeReferenceMap = new HashMap(); } } } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/ElementNameAndTextQualifier.java0000644000000000000000000000773612451007364026520 0ustar rootroot/* ****************************************************************** Copyright (c) 2001, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; /** * More complex interface implementation that tests two elements for tag name * and text content comparability. *
      Examples and more at * xmlunit.sourceforge.net * @see DifferenceEngine#compareNodeList(NodeList, NodeList, int, DifferenceListener, ElementQualifier) * @see Diff#overrideElementQualifier(ElementQualifier) */ public class ElementNameAndTextQualifier extends ElementNameQualifier { /** * Determine whether two elements qualify for further Difference comparison. * @param control * @param test * @return true if the two elements qualify for further comparison based on * both the superclass qualification (namespace URI and non- namespaced tag * name), and the qualification of the text nodes contained within the * elements; false otherwise */ public boolean qualifyForComparison(Element control, Element test) { if (super.qualifyForComparison(control, test)) { return similar(extractText(control), extractText(test)); } return false; } /** * Determine whether the text nodes contain similar values * @param control * @param test * @return true if text nodes are similar, false otherwise */ protected boolean similar(Text control, Text test) { if (control == null) { return test == null; } else if (test == null) { return false; } return control.getNodeValue().equals(test.getNodeValue()); } /** * Extract the normalized text from within an element * @param fromElement * @return extracted Text node (could be null) */ protected Text extractText(Element fromElement) { fromElement.normalize(); NodeList fromNodeList = fromElement.getChildNodes(); Node currentNode; for (int i=0; i < fromNodeList.getLength(); ++i) { currentNode = fromNodeList.item(i); if (currentNode.getNodeType() == Node.TEXT_NODE) { return (Text) currentNode; } } return null; } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/IgnoreTextAndAttributeValuesDifferenceListener.java0000644000000000000000000000633212451007364032423 0ustar rootroot/* ****************************************************************** Copyright (c) 2001, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import org.w3c.dom.Node; /** * Class to use when performing a Diff that only compares the * structure of 2 pieces of XML, i.e. where the values of text * and attribute nodes should be ignored. * @see Diff#overrideDifferenceListener */ public class IgnoreTextAndAttributeValuesDifferenceListener implements DifferenceListener { private static final int[] IGNORE_VALUES = new int[] { DifferenceConstants.ATTR_VALUE.getId(), DifferenceConstants.ATTR_VALUE_EXPLICITLY_SPECIFIED.getId(), DifferenceConstants.TEXT_VALUE.getId() }; private boolean isIgnoredDifference(Difference difference) { int differenceId = difference.getId(); for (int i=0; i < IGNORE_VALUES.length; ++i) { if (differenceId == IGNORE_VALUES[i]) { return true; } } return false; } /** * @return RETURN_IGNORE_DIFFERENCE_NODES_SIMILAR to ignore * differences in values of TEXT or ATTRIBUTE nodes, * and RETURN_ACCEPT_DIFFERENCE to accept all other * differences. * @see DifferenceListener#differenceFound(Difference) */ public int differenceFound(Difference difference) { if (isIgnoredDifference(difference)) { return RETURN_IGNORE_DIFFERENCE_NODES_SIMILAR; } else { return RETURN_ACCEPT_DIFFERENCE; } } /** * Do nothing * @see DifferenceListener#skippedComparison(Node, Node) */ public void skippedComparison(Node control, Node test) { } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/DoctypeInputStream.java0000644000000000000000000001040612451007364024763 0ustar rootroot/* ****************************************************************** Copyright (c) 2001, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.IOException; /** * Adapts the marked-up content in a source InputStream to specify that it * conforms to a different DTD. * Combines InputStream semantics with the ability to specify a target doctype * for a byte stream containing XML markup. * Used by Validator class to wrap an InputStrea, when performing validation of a * document against a DTD. *
      Examples and more at xmlunit.sourceforge.net */ public class DoctypeInputStream extends InputStream { private final ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); private final InputStream wrappedStream; private final DoctypeSupport support; /** * Create an InputStream whose XML content is provided by the * originalSource with the exception of the DOCTYPE which is * provided by the doctypeName and systemID. * @param originalSource * @param doctypeName * @param systemID */ public DoctypeInputStream(InputStream originalSource, String encoding, String doctypeName, String systemID) { wrappedStream = originalSource instanceof BufferedInputStream ? originalSource : new BufferedInputStream(originalSource); support = new DoctypeSupport(doctypeName, systemID, new DoctypeSupport.Readable() { public int read() throws IOException { return wrappedStream.read(); } }, false, encoding); } /** * @return the content of the original source, without amendments or * substitutions. Safe to call multiple times. * @throws IOException if thrown while reading from the original source */ protected String getContent(String encoding) throws IOException { if (baos.size() == 0) { byte[] buffer = new byte[8192]; int bytesRead = -1; while ((bytesRead = wrappedStream.read(buffer)) > -1) { baos.write(buffer, 0, bytesRead); } } return encoding == null ? baos.toString() : baos.toString(encoding); } /** * Read DOCTYPE-replaced content from the wrapped InputStream */ public int read() throws IOException { return support.read(); } public void close() throws IOException { wrappedStream.close(); } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/XpathEngine.java0000644000000000000000000000526412451007364023400 0ustar rootroot/* ****************************************************************** Copyright (c) 2006-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import org.custommonkey.xmlunit.exceptions.XpathException; import org.w3c.dom.Document; import org.w3c.dom.NodeList; /** * Abstraction of an engine evaluating XPath expressions. */ public interface XpathEngine { /** * Execute the specified xpath syntax select expression * on the specified document and return the list of nodes (could have * length zero) that match * @param select * @param document * @return list of matching nodes */ NodeList getMatchingNodes(String select, Document document) throws XpathException; /** * Evaluate the result of executing the specified xpath syntax * select expression on the specified document * @param select * @param document * @return evaluated result */ String evaluate(String select, Document document) throws XpathException; /** * Establish a namespace context. */ void setNamespaceContext(NamespaceContext ctx); } xmlunit-1.6/src/java/org/custommonkey/xmlunit/XMLAssert.java0000644000000000000000000013361212451007364023007 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007,2011,2014 Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import org.custommonkey.xmlunit.exceptions.ConfigurationException; import org.custommonkey.xmlunit.exceptions.XpathException; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.util.Iterator; import java.util.NoSuchElementException; import javax.xml.parsers.DocumentBuilder; import junit.framework.Assert; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * Collection of static methods so that XML assertion facilities are available * in any class, not just test suites. Thanks to Andrew McCormick and others for * suggesting this refactoring.
      * Available assertion methods are: *
        *
      • assertXMLEqual
        * assert that two pieces of XML markup are similar
      • *
      • assertXMLNotEqual
        * assert that two pieces of XML markup are different
      • *
      • assertXMLIdentical
        * assert that two pieces of XML markup are identical. In most cases * this assertion is too strong and assertXMLEqual is sufficient
      • *
      • assertXpathExists
        * assert that an XPath expression matches at least one node
      • *
      • assertXpathNotExists
        * assert that an XPath expression does not match any nodes
      • *
      • assertXpathsEqual
        * assert that the nodes obtained by executing two Xpaths * are similar
      • *
      • assertXpathsNotEqual
        * assert that the nodes obtained by executing two Xpaths * are different
      • *
      • assertXpathValuesEqual
        * assert that the flattened String obtained by executing two Xpaths * are similar
      • *
      • assertXpathValuesNotEqual
        * assert that the flattened String obtained by executing two Xpaths * are different
      • *
      • assertXpathEvaluatesTo
        * assert that the flattened String obtained by executing an Xpath * is a particular value
      • *
      • assertXMLValid
        * assert that a piece of XML markup is valid with respect to a DTD: either * by using the markup's own DTD or a different DTD
      • *
      • assertNodeTestPasses
        * assert that a piece of XML markup passes a {@link NodeTest NodeTest}
      • *
      * All underlying similarity and difference testing is done using * {@link Diff Diff} instances which can be instantiated and evaluated * independently of this class. * @see Diff#similar() * @see Diff#identical() *
      Examples and more at xmlunit.sourceforge.net */ public class XMLAssert extends Assert implements XSLTConstants { protected XMLAssert(){ super(); } /** * Assert that the result of an XML comparison is or is not similar. * @param diff the result of an XML comparison * @param assertion true if asserting that result is similar */ public static void assertXMLEqual(Diff diff, boolean assertion) { assertXMLEqual(null, diff, assertion); } /** * Assert that the result of an XML comparison is or is not similar. * @param msg additional message to display if assertion fails * @param diff the result of an XML comparison * @param assertion true if asserting that result is similar */ public static void assertXMLEqual(String msg, Diff diff, boolean assertion) { if (assertion != diff.similar()) { fail(getFailMessage(msg, diff)); } } private static String getFailMessage(String msg, Diff diff) { StringBuffer sb = new StringBuffer(); if (msg != null && msg.length() > 0) { sb.append(msg).append(", "); } return sb.append(diff.toString()).toString(); } /** * Assert that the result of an XML comparison is or is not identical * @param diff the result of an XML comparison * @param assertion true if asserting that result is identical */ public static void assertXMLIdentical(Diff diff, boolean assertion) { assertXMLIdentical(null, diff, assertion); } /** * Assert that the result of an XML comparison is or is not identical * @param msg Message to display if assertion fails * @param diff the result of an XML comparison * @param assertion true if asserting that result is identical */ public static void assertXMLIdentical(String msg, Diff diff, boolean assertion) { if (assertion != diff.identical()) { fail(getFailMessage(msg, diff)); } } /** * Assert that two XML documents are similar * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public static void assertXMLEqual(InputSource control, InputSource test) throws SAXException, IOException { assertXMLEqual(null, control, test); } /** * Assert that two XML documents are similar * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public static void assertXMLEqual(String control, String test) throws SAXException, IOException { assertXMLEqual(null, control, test); } /** * Assert that two XML documents are similar * @param control XML to be compared against * @param test XML to be tested */ public static void assertXMLEqual(Document control, Document test) { assertXMLEqual(null, control, test); } /** * Assert that two XML documents are similar * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public static void assertXMLEqual(Reader control, Reader test) throws SAXException, IOException { assertXMLEqual(null, control, test); } /** * Assert that two XML documents are similar * @param err Message to be displayed on assertion failure * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public static void assertXMLEqual(String err, InputSource control, InputSource test) throws SAXException, IOException { Diff diff = new Diff(control, test); assertXMLEqual(err, diff, true); } /** * Assert that two XML documents are similar * @param err Message to be displayed on assertion failure * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public static void assertXMLEqual(String err, String control, String test) throws SAXException, IOException { Diff diff = new Diff(control, test); assertXMLEqual(err, diff, true); } /** * Assert that two XML documents are similar * @param err Message to be displayed on assertion failure * @param control XML to be compared against * @param test XML to be tested */ public static void assertXMLEqual(String err, Document control, Document test) { Diff diff = new Diff(control, test); assertXMLEqual(err, diff, true); } /** * Assert that two XML documents are similar * @param err Message to be displayed on assertion failure * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public static void assertXMLEqual(String err, Reader control, Reader test) throws SAXException, IOException { Diff diff = new Diff(control, test); assertXMLEqual(err, diff, true); } /** * Assert that two XML documents are NOT similar * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public static void assertXMLNotEqual(InputSource control, InputSource test) throws SAXException, IOException { assertXMLNotEqual(null, control, test); } /** * Assert that two XML documents are NOT similar * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public static void assertXMLNotEqual(String control, String test) throws SAXException, IOException { assertXMLNotEqual(null, control, test); } /** * Assert that two XML documents are NOT similar * @param control XML to be compared against * @param test XML to be tested */ public static void assertXMLNotEqual(Document control, Document test) { assertXMLNotEqual(null, control, test); } /** * Assert that two XML documents are NOT similar * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public static void assertXMLNotEqual(Reader control, Reader test) throws SAXException, IOException { assertXMLNotEqual(null, control, test); } /** * Assert that two XML documents are NOT similar * @param err Message to be displayed on assertion failure * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public static void assertXMLNotEqual(String err, InputSource control, InputSource test) throws SAXException, IOException { Diff diff = new Diff(control, test); assertXMLEqual(err, diff, false); } /** * Assert that two XML documents are NOT similar * @param err Message to be displayed on assertion failure * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public static void assertXMLNotEqual(String err, String control, String test) throws SAXException, IOException { Diff diff = new Diff(control, test); assertXMLEqual(err, diff, false); } /** * Assert that two XML documents are NOT similar * @param err Message to be displayed on assertion failure * @param control XML to be compared against * @param test XML to be tested */ public static void assertXMLNotEqual(String err, Document control, Document test) { Diff diff = new Diff(control, test); assertXMLEqual(err, diff, false); } /** * Assert that two XML documents are NOT similar * @param err Message to be displayed on assertion failure * @param control XML to be compared against * @param test XML to be tested * @throws SAXException * @throws IOException */ public static void assertXMLNotEqual(String err, Reader control, Reader test) throws SAXException, IOException { Diff diff = new Diff(control, test); assertXMLEqual(err, diff, false); } /** * Assert that the node lists of two Xpaths in the same document are equal * @param xpathOne * @param xpathTwo * @param document * @see XpathEngine */ public static void assertXpathsEqual(String controlXpath, String testXpath, Document document) throws XpathException { assertXpathsEqual(controlXpath, document, testXpath, document); } /** * Assert that the node lists of two Xpaths in the same document are equal * @param xpathOne * @param xpathTwo * @param document * @see XpathEngine */ public static void assertXpathsEqual(String controlXpath, String testXpath, InputSource document) throws SAXException, IOException, XpathException { assertXpathsEqual(controlXpath, testXpath, XMLUnit.buildControlDocument(document)); } /** * Assert that the node lists of two Xpaths in the same XML string are * equal * @param xpathOne * @param xpathTwo * @param inXMLString * @throws SAXException * @throws IOException */ public static void assertXpathsEqual(String controlXpath, String testXpath, String inXMLString) throws SAXException, IOException, XpathException { assertXpathsEqual(controlXpath, testXpath, XMLUnit.buildControlDocument(inXMLString)); } /** * Assert that the node lists of two Xpaths in two documents are equal * @param xpathOne * @param xpathTwo * @param controlDocument * @param testDocument * @see XpathEngine */ public static void assertXpathsEqual(String controlXpath, InputSource controlDocument, String testXpath, InputSource testDocument) throws SAXException, IOException, XpathException { assertXpathsEqual(controlXpath, XMLUnit.buildControlDocument(controlDocument), testXpath, XMLUnit.buildTestDocument(testDocument)); } /** * Assert that the node lists of two Xpaths in two XML strings are equal * @param xpathOne * @param inControlXMLString * @param xpathTwo * @param inTestXMLString * @throws SAXException * @throws IOException */ public static void assertXpathsEqual(String controlXpath, String inControlXMLString, String testXpath, String inTestXMLString) throws SAXException, IOException, XpathException { assertXpathsEqual( controlXpath, XMLUnit.buildControlDocument(inControlXMLString), testXpath, XMLUnit.buildTestDocument(inTestXMLString)); } /** * Assert that the node lists of two Xpaths in two documents are equal * @param xpathOne * @param xpathTwo * @param controlDocument * @param testDocument * @see XpathEngine */ public static void assertXpathsEqual(String controlXpath, Document controlDocument, String testXpath, Document testDocument) throws XpathException { assertXpathEquality(controlXpath, controlDocument, testXpath, testDocument, true); } /** * Assert that the node lists of two Xpaths in the same document are NOT equal * @param xpathOne * @param xpathTwo * @param document * @see XpathEngine */ public static void assertXpathsNotEqual(String controlXpath, String testXpath, Document document) throws XpathException { assertXpathsNotEqual(controlXpath, document, testXpath, document); } /** * Assert that the node lists of two Xpaths in the same document are NOT equal * @param xpathOne * @param xpathTwo * @param document * @see XpathEngine */ public static void assertXpathsNotEqual(String controlXpath, String testXpath, InputSource document) throws SAXException, IOException, XpathException { assertXpathsNotEqual(controlXpath, testXpath, XMLUnit.buildControlDocument(document)); } /** * Assert that the node lists of two Xpaths in the same XML string are NOT * equal * @param xpathOne * @param xpathTwo * @param inXMLString * @throws SAXException * @throws IOException */ public static void assertXpathsNotEqual(String controlXpath, String testXpath, String inXMLString) throws SAXException, IOException, XpathException { assertXpathsNotEqual(controlXpath, testXpath, XMLUnit.buildControlDocument(inXMLString)); } /** * Assert that the node lists of two Xpaths in two XML strings are NOT equal * @param xpathOne * @param inControlXMLString * @param xpathTwo * @param inTestXMLString * @throws SAXException * @throws IOException */ public static void assertXpathsNotEqual(String controlXpath, String inControlXMLString, String testXpath, String inTestXMLString) throws SAXException, IOException, XpathException { assertXpathsNotEqual( controlXpath, XMLUnit.buildControlDocument(inControlXMLString), testXpath, XMLUnit.buildTestDocument(inTestXMLString)); } /** * Assert that the node lists of two Xpaths in two XML strings are * NOT equal * @param xpathOne * @param controlDocument * @param xpathTwo * @param testDocument * @throws SAXException * @throws IOException */ public static void assertXpathsNotEqual(String controlXpath, InputSource controlDocument, String testXpath, InputSource testDocument) throws SAXException, IOException, XpathException { assertXpathsNotEqual(controlXpath, XMLUnit.buildControlDocument(controlDocument), testXpath, XMLUnit.buildTestDocument(testDocument)); } /** * Assert that the node lists of two Xpaths in two documents are NOT equal * @param xpathOne * @param xpathTwo * @param document * @see XpathEngine */ public static void assertXpathsNotEqual(String controlXpath, Document controlDocument, String testXpath, Document testDocument) throws XpathException { assertXpathEquality(controlXpath, controlDocument, testXpath, testDocument, false); } /** * Assert that the node lists of two Xpaths in two documents are * equal or not. * @param xpathOne * @param xpathTwo * @param document * @param equality whether the values should be equal. * @see XpathEngine */ private static void assertXpathEquality(String controlXpath, Document controlDocument, String testXpath, Document testDocument, boolean equal) throws XpathException { XpathEngine xpath = XMLUnit.newXpathEngine(); Diff diff = new Diff(asXpathResultDocument(XMLUnit.newControlParser(), xpath.getMatchingNodes(controlXpath, controlDocument)), asXpathResultDocument(XMLUnit.newTestParser(), xpath.getMatchingNodes(testXpath, testDocument))); assertXMLEqual(diff, equal); } /** * Assert that the evaluation of two Xpaths in the same document are equal * @param xpathOne * @param xpathTwo * @param document * @see XpathEngine */ public static void assertXpathValuesEqual(String controlXpath, String testXpath, Document document) throws XpathException { assertXpathValuesEqual(controlXpath, document, testXpath, document); } /** * Assert that the evaluation of two Xpaths in the same XML string are * equal * @param xpathOne * @param xpathTwo * @param document * @throws SAXException * @throws IOException */ public static void assertXpathValuesEqual(String controlXpath, String testXpath, InputSource document) throws SAXException, IOException, XpathException { assertXpathValuesEqual(controlXpath, testXpath, XMLUnit.buildControlDocument(document)); } /** * Assert that the evaluation of two Xpaths in the same XML string are * equal * @param xpathOne * @param xpathTwo * @param inXMLString * @throws SAXException * @throws IOException */ public static void assertXpathValuesEqual(String controlXpath, String testXpath, String inXMLString) throws SAXException, IOException, XpathException { assertXpathValuesEqual(controlXpath, testXpath, XMLUnit.buildControlDocument(inXMLString)); } /** * Assert that the evaluation of two Xpaths in two XML strings are equal * @param xpathOne * @param control * @param xpathTwo * @param test * @throws SAXException * @throws IOException */ public static void assertXpathValuesEqual(String controlXpath, InputSource control, String testXpath, InputSource test) throws SAXException, IOException, XpathException { assertXpathValuesEqual(controlXpath, XMLUnit.buildControlDocument(control), testXpath, XMLUnit.buildTestDocument(test)); } /** * Assert that the evaluation of two Xpaths in two XML strings are equal * @param xpathOne * @param inControlXMLString * @param xpathTwo * @param inTestXMLString * @throws SAXException * @throws IOException */ public static void assertXpathValuesEqual(String controlXpath, String inControlXMLString, String testXpath, String inTestXMLString) throws SAXException, IOException, XpathException { assertXpathValuesEqual(controlXpath, XMLUnit.buildControlDocument(inControlXMLString), testXpath, XMLUnit.buildTestDocument(inTestXMLString)); } /** * Assert that the evaluation of two Xpaths in two documents are equal * @param xpathOne * @param xpathTwo * @param document * @see XpathEngine */ public static void assertXpathValuesEqual(String controlXpath, Document controlDocument, String testXpath, Document testDocument) throws XpathException { XpathEngine xpath = XMLUnit.newXpathEngine(); assertEquals(xpath.evaluate(controlXpath, controlDocument), xpath.evaluate(testXpath, testDocument)); } /** * Assert that the evaluation of two Xpaths in the same XML string are * NOT equal * @param xpathOne * @param xpathTwo * @param control * @throws SAXException * @throws IOException */ public static void assertXpathValuesNotEqual(String controlXpath, String testXpath, InputSource control) throws SAXException, IOException, XpathException { assertXpathValuesNotEqual(controlXpath, testXpath, XMLUnit.buildControlDocument(control)); } /** * Assert that the evaluation of two Xpaths in the same XML string are * NOT equal * @param xpathOne * @param xpathTwo * @param inXMLString * @throws SAXException * @throws IOException */ public static void assertXpathValuesNotEqual(String controlXpath, String testXpath, String inXMLString) throws SAXException, IOException, XpathException { assertXpathValuesNotEqual(controlXpath, testXpath, XMLUnit.buildControlDocument(inXMLString)); } /** * Assert that the evaluation of two Xpaths in the same document are * NOT equal * @param xpathOne * @param xpathTwo * @param document */ public static void assertXpathValuesNotEqual(String controlXpath, String testXpath, Document document) throws XpathException { assertXpathValuesNotEqual(controlXpath, document, testXpath, document); } /** * Assert that the evaluation of two Xpaths in two XML strings are * NOT equal * @param xpathOne * @param control * @param xpathTwo * @param test * @throws SAXException * @throws IOException */ public static void assertXpathValuesNotEqual(String controlXpath, InputSource control, String testXpath, InputSource test) throws SAXException, IOException, XpathException { assertXpathValuesNotEqual(controlXpath, XMLUnit.buildControlDocument(control), testXpath, XMLUnit.buildTestDocument(test)); } /** * Assert that the evaluation of two Xpaths in two XML strings are * NOT equal * @param xpathOne * @param inControlXMLString * @param xpathTwo * @param inTestXMLString * @throws SAXException * @throws IOException */ public static void assertXpathValuesNotEqual(String controlXpath, String inControlXMLString, String testXpath, String inTestXMLString) throws SAXException, IOException, XpathException { assertXpathValuesNotEqual(controlXpath, XMLUnit.buildControlDocument(inControlXMLString), testXpath, XMLUnit.buildTestDocument(inTestXMLString)); } /** * Assert that the evaluation of two Xpaths in two documents are * NOT equal * @param xpathOne * @param xpathTwo * @param document */ public static void assertXpathValuesNotEqual(String controlXpath, Document controlDocument, String testXpath, Document testDocument) throws XpathException { XpathEngine xpath = XMLUnit.newXpathEngine(); String control = xpath.evaluate(controlXpath, controlDocument); String test = xpath.evaluate(testXpath, testDocument); if (control!=null) { if (control.equals(test)) { fail("Expected test value NOT to be equal to control but both were " + test); } } else if (test != null) { fail("control xPath evaluated to empty node set, " + "but test xPath evaluated to " + test); } } /** * Assert the value of an Xpath expression in an XML document. * @param expectedValue * @param xpathExpression * @param control * @throws SAXException * @throws IOException * @see XpathEngine which provides the underlying evaluation mechanism */ public static void assertXpathEvaluatesTo(String expectedValue, String xpathExpression, InputSource control) throws SAXException, IOException, XpathException { Document document = XMLUnit.buildControlDocument(control); assertXpathEvaluatesTo(expectedValue, xpathExpression, document); } /** * Assert the value of an Xpath expression in an XML String * @param expectedValue * @param xpathExpression * @param inXMLString * @throws SAXException * @throws IOException * @see XpathEngine which provides the underlying evaluation mechanism */ public static void assertXpathEvaluatesTo(String expectedValue, String xpathExpression, String inXMLString) throws SAXException, IOException, XpathException { Document document = XMLUnit.buildControlDocument(inXMLString); assertXpathEvaluatesTo(expectedValue, xpathExpression, document); } /** * Assert the value of an Xpath expression in an DOM Document * @param expectedValue * @param xpathExpression * @param inDocument * @see XpathEngine which provides the underlying evaluation mechanism */ public static void assertXpathEvaluatesTo(String expectedValue, String xpathExpression, Document inDocument) throws XpathException { XpathEngine simpleXpathEngine = XMLUnit.newXpathEngine(); assertEquals(expectedValue, simpleXpathEngine.evaluate(xpathExpression, inDocument)); } /** * Assert the value of an Xpath expression in an XML document. * @param expectedValue * @param xpathExpression * @param control * @throws SAXException * @throws IOException * @see XpathEngine which provides the underlying evaluation mechanism */ public static void assertXpathEvaluatesTo(QualifiedName expectedValue, String xpathExpression, InputSource control) throws SAXException, IOException, XpathException { Document document = XMLUnit.buildControlDocument(control); assertXpathEvaluatesTo(expectedValue, xpathExpression, document); } /** * Assert the value of an Xpath expression in an XML String * @param expectedValue * @param xpathExpression * @param inXMLString * @throws SAXException * @throws IOException * @see XpathEngine which provides the underlying evaluation mechanism */ public static void assertXpathEvaluatesTo(QualifiedName expectedValue, String xpathExpression, String inXMLString) throws SAXException, IOException, XpathException { Document document = XMLUnit.buildControlDocument(inXMLString); assertXpathEvaluatesTo(expectedValue, xpathExpression, document); } /** * Assert the value of an Xpath expression in an DOM Document * @param expectedValue * @param xpathExpression * @param inDocument * @see XpathEngine which provides the underlying evaluation mechanism */ public static void assertXpathEvaluatesTo(QualifiedName expectedValue, String xpathExpression, Document inDocument) throws XpathException { XpathEngine simpleXpathEngine = XMLUnit.newXpathEngine(); NodeList nl = simpleXpathEngine.getMatchingNodes(xpathExpression, inDocument); NamespaceContext ctx = null; if (nl.getLength() > 0) { ctx = new NodeBasedNamespaceContext(nl.item(0)); } assertEquals(expectedValue, QualifiedName .valueOf(simpleXpathEngine.evaluate(xpathExpression, inDocument), ctx)); } /** * Assert that a specific XPath exists in some given XML * @param inXpathExpression * @param control * @see XpathEngine which provides the underlying evaluation mechanism */ public static void assertXpathExists(String xPathExpression, InputSource control) throws IOException, SAXException, XpathException { Document inDocument = XMLUnit.buildControlDocument(control); assertXpathExists(xPathExpression, inDocument); } /** * Assert that a specific XPath exists in some given XML * @param inXpathExpression * @param inXMLString * @see XpathEngine which provides the underlying evaluation mechanism */ public static void assertXpathExists(String xPathExpression, String inXMLString) throws IOException, SAXException, XpathException { Document inDocument = XMLUnit.buildControlDocument(inXMLString); assertXpathExists(xPathExpression, inDocument); } /** * Assert that a specific XPath exists in some given XML * @param inXpathExpression * @param inDocument * @see XpathEngine which provides the underlying evaluation mechanism */ public static void assertXpathExists(String xPathExpression, Document inDocument) throws XpathException { XpathEngine simpleXpathEngine = XMLUnit.newXpathEngine(); NodeList nodeList = simpleXpathEngine.getMatchingNodes( xPathExpression, inDocument); int matches = nodeList.getLength(); assertTrue("Expecting to find matches for Xpath " + xPathExpression, matches > 0); } /** * Assert that a specific XPath does NOT exist in some given XML * @param inXpathExpression * @param control * @see XpathEngine which provides the underlying evaluation mechanism */ public static void assertXpathNotExists(String xPathExpression, InputSource control) throws IOException, SAXException, XpathException { Document inDocument = XMLUnit.buildControlDocument(control); assertXpathNotExists(xPathExpression, inDocument); } /** * Assert that a specific XPath does NOT exist in some given XML * @param inXpathExpression * @param inXMLString * @see XpathEngine which provides the underlying evaluation mechanism */ public static void assertXpathNotExists(String xPathExpression, String inXMLString) throws IOException, SAXException, XpathException { Document inDocument = XMLUnit.buildControlDocument(inXMLString); assertXpathNotExists(xPathExpression, inDocument); } /** * Assert that a specific XPath does NOT exist in some given XML * @param inXpathExpression * @param inDocument * @see XpathEngine which provides the underlying evaluation mechanism */ public static void assertXpathNotExists(String xPathExpression, Document inDocument) throws XpathException { XpathEngine simpleXpathEngine = XMLUnit.newXpathEngine(); NodeList nodeList = simpleXpathEngine.getMatchingNodes( xPathExpression, inDocument); int matches = nodeList.getLength(); assertEquals("Should be zero matches for Xpath " + xPathExpression, 0, matches); } /** * Assert that an InputSource containing XML contains valid XML: * the document must contain a DOCTYPE declaration to be validated * @param xml * @throws SAXException * @throws ConfigurationException if validation could not be turned on * @see Validator */ public static void assertXMLValid(InputSource xml) throws SAXException, ConfigurationException { assertXMLValid(new Validator(xml)); } /** * Assert that a String containing XML contains valid XML: the String must * contain a DOCTYPE declaration to be validated * @param xmlString * @throws SAXException * @throws ConfigurationException if validation could not be turned on * @see Validator */ public static void assertXMLValid(String xmlString) throws SAXException, ConfigurationException { assertXMLValid(new Validator(xmlString)); } /** * Assert that an InputSource containing XML contains valid XML: * the document must contain a DOCTYPE to be validated, but the * validation will use the systemId to obtain the DTD * @param xml * @param systemId * @throws SAXException * @throws ConfigurationException if validation could not be turned on * @see Validator */ public static void assertXMLValid(InputSource xml, String systemId) throws SAXException, ConfigurationException { assertXMLValid(new Validator(xml, systemId)); } /** * Assert that a String containing XML contains valid XML: the String must * contain a DOCTYPE to be validated, but the validation will use the * systemId to obtain the DTD * @param xmlString * @param systemId * @throws SAXException * @throws ConfigurationException if validation could not be turned on * @see Validator */ public static void assertXMLValid(String xmlString, String systemId) throws SAXException, ConfigurationException { assertXMLValid(new Validator(xmlString, systemId)); } /** * Assert that a piece of XML contains valid XML: the document * will be given a DOCTYPE to be validated with the name and * systemId specified regardless of whether it already contains a * doctype declaration. * @param xml * @param systemId * @param doctype * @throws SAXException * @throws ConfigurationException if validation could not be turned on * @see Validator */ public static void assertXMLValid(InputSource xml, String systemId, String doctype) throws SAXException, ConfigurationException { assertXMLValid(new Validator(xml, systemId, doctype)); } /** * Assert that a String containing XML contains valid XML: the String will * be given a DOCTYPE to be validated with the name and systemId specified * regardless of whether it already contains a doctype declaration. * @param xmlString * @param systemId * @param doctype * @throws SAXException * @throws ConfigurationException if validation could not be turned on * @see Validator */ public static void assertXMLValid(String xmlString, String systemId, String doctype) throws SAXException, ConfigurationException { assertXMLValid(new Validator(new StringReader(xmlString), systemId, doctype)); } /** * Assert that a Validator instance returns isValid() == true * @param validator */ public static void assertXMLValid(Validator validator) { assertEquals(validator.toString(), true, validator.isValid()); } /** * Execute a NodeTest for a single node type * and assert that it passes * @param xml XML to be tested * @param tester The test strategy * @param nodeType The node type to be tested: constants defined * in {@link Node org.w3c.dom.Node} e.g. Node.ELEMENT_NODE * @throws SAXException * @throws IOException * @see AbstractNodeTester * @see CountingNodeTester */ public static void assertNodeTestPasses(InputSource xml, NodeTester tester, short nodeType) throws SAXException, IOException { NodeTest test = new NodeTest(xml); assertNodeTestPasses(test, tester, new short[] {nodeType}, true); } /** * Execute a NodeTest for a single node type * and assert that it passes * @param xmlString XML to be tested * @param tester The test strategy * @param nodeType The node type to be tested: constants defined * in {@link Node org.w3c.dom.Node} e.g. Node.ELEMENT_NODE * @throws SAXException * @throws IOException * @see AbstractNodeTester * @see CountingNodeTester */ public static void assertNodeTestPasses(String xmlString, NodeTester tester, short nodeType) throws SAXException, IOException { NodeTest test = new NodeTest(xmlString); assertNodeTestPasses(test, tester, new short[] {nodeType}, true); } /** * Execute a NodeTest for multiple node types and make an * assertion about it whether it is expected to pass * @param test a NodeTest instance containing the XML source to be tested * @param tester The test strategy * @param nodeTypes The node types to be tested: constants defined * in {@link Node org.w3c.dom.Node} e.g. Node.ELEMENT_NODE * @param assertion true if the test is expected to pass, false otherwise * @see AbstractNodeTester * @see CountingNodeTester */ public static void assertNodeTestPasses(NodeTest test, NodeTester tester, short[] nodeTypes, boolean assertion) { try { test.performTest(tester, nodeTypes); if (!assertion) { fail("Expected node test to fail, but it passed!"); } } catch (NodeTestException e) { if (assertion) { fail("Expected node test to pass, but it failed! " + e.getMessage()); } } } private static Document asXpathResultDocument(final DocumentBuilder builder, final NodeList nodes) { final Document d = builder.newDocument(); final Element root = d.createElement("xpathResult"); d.appendChild(root); final int length = nodes.getLength(); for (int i = 0; i < length; i++) { Node n = d.importNode(nodes.item(i), true); if (n instanceof Attr) { root.setAttributeNodeNS((Attr) n); } else { root.appendChild(n); } } return d; } private static class NodeBasedNamespaceContext implements NamespaceContext { private final Node node; NodeBasedNamespaceContext(Node n) { node = n; } public String getNamespaceURI(String prefix) { return node.lookupNamespaceURI(prefix); } // not used in context of #assertXpathEvaluatesTo public Iterator getPrefixes() { return new Iterator() { public boolean hasNext() { return false; } public Object next() { throw new NoSuchElementException(); } public void remove() { throw new UnsupportedOperationException(); } }; } } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/XMLUnit.java0000644000000000000000000007601512451007364022470 0ustar rootroot/* ***************************************************************** Copyright (c) 2001-2008, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import org.custommonkey.xmlunit.exceptions.ConfigurationException; import org.custommonkey.xmlunit.exceptions.XMLUnitRuntimeException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.URIResolver; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.text.NumberFormat; import java.text.ParseException; import java.util.Locale; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.EntityResolver; /** * Allows access to project control parameters such as which Parser to use and * provides some convenience methods for building Documents from Strings etc. *
      Examples and more at xmlunit.sourceforge.net */ public final class XMLUnit { private static DocumentBuilderFactory controlBuilderFactory; private static DocumentBuilderFactory testBuilderFactory; private static TransformerFactory transformerFactory; private static SAXParserFactory saxParserFactory; private static boolean ignoreWhitespace = false; private static URIResolver uriResolver = null; private static EntityResolver testEntityResolver = null; private static EntityResolver controlEntityResolver = null; private static NamespaceContext namespaceContext = null; private static boolean ignoreDiffBetweenTextAndCDATA = false; private static boolean ignoreComments = false; private static boolean normalize = false; private static boolean normalizeWhitespace = false; private static boolean ignoreAttributeOrder = false; private static String xsltVersion = "1.0"; private static String xpathFactoryName = null; private static boolean expandEntities = false; private static boolean compareUnmatched = true; private static final String XSLT_VERSION_START = " version=\""; private static final String XSLT_VERSION_END = "\">"; private static final String STRIP_WHITESPACE_STYLESHEET_START = new StringBuffer(XMLConstants.XML_DECLARATION) .append(XSLTConstants.XSLT_START_NO_VERSION) .append(XSLT_VERSION_START) .toString(); private static final String STRIP_WHITESPACE_STYLESHEET_END = new StringBuffer(XSLT_VERSION_END) .append(XSLTConstants.XSLT_XML_OUTPUT_NOINDENT) .append(XSLTConstants.XSLT_STRIP_WHITESPACE) .append(XSLTConstants.XSLT_IDENTITY_TEMPLATE) .append(XSLTConstants.XSLT_END) .toString(); private static final String STRIP_COMMENTS_STYLESHEET_START = new StringBuffer(XMLConstants.XML_DECLARATION) .append(XSLTConstants.XSLT_START_NO_VERSION) .append(XSLT_VERSION_START) .toString(); private static final String STRIP_COMMENTS_STYLESHEET_END = new StringBuffer(XSLT_VERSION_END) .append(XSLTConstants.XSLT_XML_OUTPUT_NOINDENT) .append(XSLTConstants.XSLT_STRIP_COMMENTS_TEMPLATE) .append(XSLTConstants.XSLT_END) .toString(); /** * Private constructor. * Makes class non-instantiable */ private XMLUnit() { // access via static methods please } /** * Overide the DocumentBuilder to use to parse control documents. * This is useful when comparing the output of two different * parsers. Note: setting the control parser before any test cases * are run will affect the test parser as well. */ public static void setControlParser(String className) { System.setProperty("javax.xml.parsers.DocumentBuilderFactory", className); controlBuilderFactory = null; controlBuilderFactory = getControlDocumentBuilderFactory(); } /** * Get the DocumentBuilder instance used to parse the control * XML in an XMLTestCase. * @return parser for control values * @throws ConfigurationException */ public static DocumentBuilder newControlParser() throws ConfigurationException { try { controlBuilderFactory = getControlDocumentBuilderFactory(); DocumentBuilder builder = controlBuilderFactory.newDocumentBuilder(); if (controlEntityResolver!=null) { builder.setEntityResolver(controlEntityResolver); } return builder; } catch (ParserConfigurationException ex) { throw new ConfigurationException(ex); } } /** * Sets an EntityResolver to be added to all new test parsers. * Setting to null will reset to the default EntityResolver */ public static void setTestEntityResolver(EntityResolver resolver) { testEntityResolver = resolver; } /** * Sets an EntityResolver to be added to all new control parsers. * Setting to null will reset to the default EntityResolver */ public static void setControlEntityResolver(EntityResolver resolver) { controlEntityResolver = resolver; } /** * Obtains the EntityResolver to be added to all new control parsers. */ public static EntityResolver getControlEntityResolver() { return controlEntityResolver; } /** * Get the DocumentBuilderFactory instance used to instantiate * parsers for the control XML in an XMLTestCase. * @return factory for control parsers */ public static DocumentBuilderFactory getControlDocumentBuilderFactory() { if (controlBuilderFactory == null) { controlBuilderFactory = DocumentBuilderFactory.newInstance(); controlBuilderFactory.setNamespaceAware(true); } return controlBuilderFactory; } /** * Override the DocumentBuilderFactory used to instantiate * parsers for the control XML in an XMLTestCase. */ public static void setControlDocumentBuilderFactory(DocumentBuilderFactory factory) { if (factory == null) { throw new IllegalArgumentException("Cannot set control DocumentBuilderFactory to null!"); } controlBuilderFactory = factory; } /** * Overide the DocumentBuilder to use to parser test documents. * This is useful when comparing the output of two different * parsers. Note: setting the test parser before any test cases * are run will affect the control parser as well. */ public static void setTestParser(String className) { System.setProperty("javax.xml.parsers.DocumentBuilderFactory", className); testBuilderFactory = null; testBuilderFactory = getTestDocumentBuilderFactory(); } /** * Get the DocumentBuilder instance used to parse the test XML * in an XMLTestCase. * @return parser for test values * @throws ConfigurationException */ public static DocumentBuilder newTestParser() throws ConfigurationException { try { testBuilderFactory = getTestDocumentBuilderFactory(); DocumentBuilder builder = testBuilderFactory.newDocumentBuilder(); if (testEntityResolver!=null) { builder.setEntityResolver(testEntityResolver); } return builder; } catch (ParserConfigurationException ex) { throw new ConfigurationException(ex); } } /** * Get the DocumentBuilder instance used to parse the test XML * in an XMLTestCase. * @return parser for test values * @throws ConfigurationException * @deprecated use newTestParser() */ public static DocumentBuilder getTestParser() throws ConfigurationException { return newTestParser(); } /** * Get the DocumentBuilder instance used to parse the test XML * in an XMLTestCase. * @return parser for control values * @deprecated use newControlParser() * @throws ConfigurationException */ public static DocumentBuilder getControlParser() throws ConfigurationException { return newControlParser(); } /** * Get the DocumentBuilderFactory instance used to instantiate * parsers for the test XML in an XMLTestCase. * @return factory for test parsers */ public static DocumentBuilderFactory getTestDocumentBuilderFactory() { if (testBuilderFactory == null) { testBuilderFactory = DocumentBuilderFactory.newInstance(); testBuilderFactory.setNamespaceAware(true); } return testBuilderFactory; } /** * Override the DocumentBuilderFactory used to instantiate * parsers for the test XML in an XMLTestCase. */ public static void setTestDocumentBuilderFactory(DocumentBuilderFactory factory) { if (factory == null) { throw new IllegalArgumentException("Cannot set test DocumentBuilderFactory to null!"); } testBuilderFactory = factory; } /** * Whether to ignore whitespace when comparing node values. * *

      This method also invokes * setIgnoringElementContentWhitespace() on the * underlying control AND test document builder factories.

      * *

      Setting this parameter has no effect on {@link * setNormalizeWhitespace whitespace inside texts}.

      */ public static void setIgnoreWhitespace(boolean ignore){ ignoreWhitespace = ignore; getControlDocumentBuilderFactory().setIgnoringElementContentWhitespace(ignore); getTestDocumentBuilderFactory().setIgnoringElementContentWhitespace(ignore); } /** * Whether to ignore whitespace when comparing node values. * @return true if whitespace should be ignored when comparing nodes, false * otherwise */ public static boolean getIgnoreWhitespace(){ return ignoreWhitespace; } /** * Utility method to build a Document using the control DocumentBuilder * to parse the specified String. * @param fromXML * @return Document representation of the String content * @throws SAXException * @throws IOException */ public static Document buildControlDocument(String fromXML) throws SAXException, IOException { return buildDocument(newControlParser(), new StringReader(fromXML)); } /** * Utility method to build a Document using the control DocumentBuilder * and the specified InputSource * @param fromSource * @return Document representation of the String content * @throws SAXException * @throws IOException */ public static Document buildControlDocument(InputSource fromSource) throws IOException, SAXException { return buildDocument(newControlParser(), fromSource); } /** * Utility method to build a Document using the test DocumentBuilder * to parse the specified String. * @param fromXML * @return Document representation of the String content * @throws SAXException * @throws IOException */ public static Document buildTestDocument(String fromXML) throws SAXException, IOException { return buildDocument(newTestParser(), new StringReader(fromXML)); } /** * Utility method to build a Document using the test DocumentBuilder * and the specified InputSource * @param fromSource * @return Document representation of the String content * @throws SAXException * @throws IOException */ public static Document buildTestDocument(InputSource fromSource) throws IOException, SAXException { return buildDocument(newTestParser(), fromSource); } /** * Utility method to build a Document using a specific DocumentBuilder * and reading characters from a specific Reader. * @param withBuilder * @param fromReader * @return Document built * @throws SAXException * @throws IOException */ public static Document buildDocument(DocumentBuilder withBuilder, Reader fromReader) throws SAXException, IOException { return buildDocument(withBuilder, new InputSource(fromReader)); } /** * Utility method to build a Document using a specific DocumentBuilder * and a specific InputSource * @param withBuilder * @param fromSource * @return Document built * @throws SAXException * @throws IOException */ public static Document buildDocument(DocumentBuilder withBuilder, InputSource fromSource) throws IOException, SAXException { return withBuilder.parse(fromSource); } /** * Overide the transformer to use for XSLT transformations (and by * implication serialization and XPaths). * This is useful when comparing transformer implementations. */ public static void setTransformerFactory(String className) { System.setProperty("javax.xml.transform.TransformerFactory", className); transformerFactory = null; getTransformerFactory(); } /** * Get the transformer to use for XSLT transformations (and by * implication serialization and XPaths). * @return the current transformer factory in use * a new instance of the default transformer factory */ public static TransformerFactory getTransformerFactory() { if (transformerFactory == null) { transformerFactory = newTransformerFactory(); } return transformerFactory; } /** * Get a fresh transformer to use for XSLT transformations (and by * implication serialization and XPaths). * @return a new instance of the default transformer factory */ static TransformerFactory newTransformerFactory() { TransformerFactory tf = TransformerFactory.newInstance(); if (uriResolver != null) { tf.setURIResolver(uriResolver); } return tf; } /** * Sets the URIResolver to use during transformations. */ public static void setURIResolver(URIResolver resolver) { if (uriResolver != resolver) { uriResolver = resolver; transformerFactory = null; getTransformerFactory(); } } /** * Gets the URIResolver used during Transformations. */ public static URIResolver getURIResolver() { return uriResolver; } /** * Override the SAX parser to use in tests. * Currently only used by {@link Validator Validator class} * @param className */ public static void setSAXParserFactory(String className) { System.setProperty("javax.xml.parsers.SAXParserFactory", className); saxParserFactory = null; getSAXParserFactory(); } /** * Override the SAX parser to use in tests. * Currently only used by {@link Validator Validator class} * @param factory */ public static void setSAXParserFactory(SAXParserFactory factory) { saxParserFactory = factory; } /** * Get the SAX parser to use in tests. * *

      Unless an instance has been given via {@link * setSAXParserFactory(SAXParserFactory) setSAXParserFactory} * explicitly, the returned factory will be namespace aware.

      * * @return the SAXParserFactory instance used by the {@link * Validator Validator} to perform DTD validation */ public static SAXParserFactory getSAXParserFactory() { if (saxParserFactory == null) { saxParserFactory = SAXParserFactory.newInstance(); saxParserFactory.setNamespaceAware(true); } return saxParserFactory; } private static String getStripWhitespaceStylesheet() { return STRIP_WHITESPACE_STYLESHEET_START + getXSLTVersion() + STRIP_WHITESPACE_STYLESHEET_END; } /** * Obtain the transformation that will strip whitespace from a DOM * containing empty Text nodes * @param forDocument * @return a Transform to do the whitespace stripping */ public static Transform getStripWhitespaceTransform(Document forDocument) { return new Transform(forDocument, getStripWhitespaceStylesheet()); } /** * Returns a new Document instance that is identical to the one * passed in with element content whitespace removed. * *

      Will use {@link #getStripWhitespaceTransform * getStripWhitespaceTransform} unless we are operating under the * severly broken XSLTC Transformer shipping with JDK 1.5.

      */ public static Document getWhitespaceStrippedDocument(Document forDoc) { String factory = getTransformerFactory().getClass().getName(); if (XSLTConstants.JAVA5_XSLTC_FACTORY_NAME.equals(factory)) { return stripWhiteSpaceWithoutXSLT(forDoc); } else { return stripWhiteSpaceUsingXSLT(forDoc); } } private static Document stripWhiteSpaceUsingXSLT(Document forDoc) { try { Transform whitespaceStripper = getStripWhitespaceTransform(forDoc); return whitespaceStripper.getResultDocument(); } catch (TransformerException e) { throw new XMLUnitRuntimeException(e.getMessage(), e.getCause()); } } private static Document stripWhiteSpaceWithoutXSLT(Document forDoc) { Document copy = (Document) forDoc.cloneNode(true); stripEmptyTextNodes(copy); return copy; } private static void stripEmptyTextNodes(Node n) { final NodeList nl = n.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node child = nl.item(i); if (child.getNodeType() == Node.ELEMENT_NODE) { stripEmptyTextNodes(child); } else if (child.getNodeType() == Node.TEXT_NODE) { String value = child.getNodeValue(); if (value == null || value.trim().length() == 0) { n.removeChild(child); --i; } } } } private static String getStripCommentsStylesheet() { return STRIP_COMMENTS_STYLESHEET_START + getXSLTVersion() + STRIP_COMMENTS_STYLESHEET_END; } /** * Obtain the transformation that will strip comments from a DOM. * @param forDocument * @return a Transform to do the whitespace stripping */ public static Transform getStripCommentsTransform(Document forDocument) { return new Transform(forDocument, getStripCommentsStylesheet()); } /** * Place holder for current version info. * @return current version */ public static String getVersion() { return "1.3alpha"; } /** * Compare XML documents provided by two InputSource classes * @param control Control document * @param test Document to test * @return Diff object describing differences in documents * @throws SAXException * @throws IOException */ public static Diff compareXML(InputSource control, InputSource test) throws SAXException, IOException { return new Diff(control, test); } /** * Compare XML documents provided by two Reader classes * @param control Control document * @param test Document to test * @return Diff object describing differences in documents * @throws SAXException * @throws IOException */ public static Diff compareXML(Reader control, Reader test) throws SAXException, IOException { return new Diff(control, test); } /** * Compare XML documents provided by two Reader classes * @param control Control document * @param test Document to test * @return Diff object describing differences in documents * @throws SAXException * @throws IOException */ public static Diff compareXML(String control, Reader test) throws SAXException, IOException { return new Diff(new StringReader(control), test); } /** * Compare XML documents provided by two Reader classes * @param control Control document * @param test Document to test * @return Diff object describing differences in documents * @throws SAXException * @throws IOException */ public static Diff compareXML(Reader control, String test) throws SAXException, IOException { return new Diff(control, new StringReader(test)); } /** * Compare two XML documents provided as strings * @param control Control document * @param test Document to test * @return Diff object describing differences in documents * @throws SAXException * @throws IOException */ public static Diff compareXML(String control, String test) throws SAXException, IOException { return new Diff(control, test); } /** * Compare two XML documents provided as strings * @param control Control document * @param test Document to test * @return Diff object describing differences in documents */ public static Diff compareXML(Document control, Document test) { return new Diff(control, test); } /** * Get the NamespaceContext to use in XPath tests. */ public static NamespaceContext getXpathNamespaceContext() { return namespaceContext; } /** * Set the NamespaceContext to use in XPath tests. */ public static void setXpathNamespaceContext(NamespaceContext ctx) { namespaceContext = ctx; } /** * Obtains an XpathEngine to use in XPath tests. */ public static XpathEngine newXpathEngine() { XpathEngine eng = null; try { Class.forName("javax.xml.xpath.XPath"); Class c = Class.forName("org.custommonkey.xmlunit.jaxp13" + ".Jaxp13XpathEngine"); eng = (XpathEngine) c.newInstance(); } catch (Throwable ex) { // should probably only catch ClassNotFoundException, but some // constellations - like Ant shipping a more recent version of // xml-apis than the JDK - may contain the JAXP 1.3 interfaces // without implementations eng = new SimpleXpathEngine(); } if (namespaceContext != null) { eng.setNamespaceContext(namespaceContext); } return eng; } /** * Whether CDATA sections and Text nodes should be considered the same. * *

      The default is false.

      * *

      This also set the DocumentBuilderFactory's {@link * javax.xml.parsers.DocumentBuilderFactory#setCoalescing * coalescing} flag on the factories for the control and test * document.

      */ public static void setIgnoreDiffBetweenTextAndCDATA(boolean b) { ignoreDiffBetweenTextAndCDATA = b; getControlDocumentBuilderFactory().setCoalescing(b); getTestDocumentBuilderFactory().setCoalescing(b); } /** * Whether CDATA sections and Text nodes should be considered the same. * *

      The default is false.

      */ public static boolean getIgnoreDiffBetweenTextAndCDATA() { return ignoreDiffBetweenTextAndCDATA; } /** * Whether comments should be ignored. * *

      The default value is false

      */ public static void setIgnoreComments(boolean b) { ignoreComments = b; } /** * Whether comments should be ignored. * *

      The default value is false

      */ public static boolean getIgnoreComments() { return ignoreComments; } /** * Whether Text nodes should be normalized. * *

      The default value is false

      * *

      Note: if you are only working with documents read * from streams (like files or network connections) or working * with strings, there is no reason to change the default since * the XML parser is required to normalize the documents. If you * are testing {@link org.w3c.Document Document} instances you've * created in code, you may want to alter the default * behavior.

      * *

      Note2: depending on the XML parser or XSLT * transformer you use, setting {@link setIgnoreWhitespace * ignoreWhitespace} or {@link setIgnoreComments ignoreComments} * to true may have already normalized your document and this * setting doesn't have any effect anymore.

      */ public static void setNormalize(boolean b) { normalize = b; } /** * Whether Text nodes should be normalized. * *

      The default value is false

      */ public static boolean getNormalize() { return normalize; } /** * Whether whitespace characters inside text nodes or attributes * should be "normalized". * *

      Normalized in this context means that all whitespace is * replaced by the space character and adjacent whitespace * characters are collapsed to a single space character. It will * also trim the resulting character content on both ends.

      * *

      The default value is false.

      * *

      Setting this parameter has no effect on {@link * setIgnoreWhitespace ignorable whitespace}.

      */ public static void setNormalizeWhitespace(boolean b) { normalizeWhitespace = b; } /** * Whether whitespace characters inside text nodes or attributes * should be "normalized". * *

      Normalized in this context means that all whitespace is * replaced by the space character and adjacent whitespace * characters are collapsed to a single space character.

      * *

      The default value is false.

      */ public static boolean getNormalizeWhitespace() { return normalizeWhitespace; } /** * Whether to ignore the order of attributes on an element. * *

      The order of attributes has never been relevant for XML * documents, still XMLUnit will consider two pieces of XML * not-identical (but similar) if they differ in order of * attributes. Set this option to false to ignore the order.

      * *

      The default value is false for backwards compatibility * reasons.

      */ public static void setIgnoreAttributeOrder(boolean b) { ignoreAttributeOrder = b; } /** * Whether to ignore the order of attributes on an element. * *

      The order of attributes has never been relevant for XML * documents, still XMLUnit will consider two pieces of XML * not-identical (but similar) if they differ in order of * attributes. Set this option to false to ignore the order.

      * *

      The default value is false for backwards compatibility * reasons.

      */ public static boolean getIgnoreAttributeOrder() { return ignoreAttributeOrder; } /** * Sets the XSLT version to set on stylesheets used internally. * *

      Defaults to "1.0".

      * * @throws ConfigurationException if the argument cannot be parsed * as a positive number. */ public static void setXSLTVersion(String s) { try { Number n = NumberFormat.getInstance(Locale.US).parse(s); if (n.doubleValue() < 0) { throw new ConfigurationException(s + " doesn't reperesent a" + " positive number."); } } catch (ParseException e) { throw new ConfigurationException(e); } xsltVersion = s; } /** * The XSLT version set on stylesheets used internally. * *

      Defaults to "1.0".

      */ public static String getXSLTVersion() { return xsltVersion; } /** * Sets the class to use as XPathFactory when using JAXP 1.3. */ public static void setXPathFactory(String className) { xpathFactoryName = className; } /** * Gets the class to use as XPathFactory when using JAXP 1.3. */ public static String getXPathFactory() { return xpathFactoryName; } /** * XSLT stylesheet element using the configured XSLT version. */ static String getXSLTStart() { return XSLTConstants.XSLT_START_NO_VERSION + XSLT_VERSION_START + getXSLTVersion() + XSLT_VERSION_END; } /** * Whether the parser shall be instructed to expand entity references. * *

      Defaults to false.

      * * @see javax.xml.parsers.DocumentBuilderFactory#setExpandEntityReferences */ public static void setExpandEntityReferences(boolean b) { expandEntities = b; getControlDocumentBuilderFactory().setExpandEntityReferences(b); getTestDocumentBuilderFactory().setExpandEntityReferences(b); } /** * Whether the parser shall be instructed to expand entity references. */ public static boolean getExpandEntityReferences() { return expandEntities; } /** * Whether to compare unmatched control nodes to unmatched test nodes. * *

      Defaults to true.

      */ public static void setCompareUnmatched(boolean b) { compareUnmatched = b; } /** * Whether unmatched control nodes should be compared to unmatched * test nodes. */ public static boolean getCompareUnmatched() { return compareUnmatched; } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/DifferenceEngine.java.rej0000644000000000000000000000123512451007364025117 0ustar rootroot--- src/java/org/custommonkey/xmlunit/DifferenceEngine.java (revision 578) +++ src/java/org/custommonkey/xmlunit/DifferenceEngine.java (working copy) @@ -660,7 +662,17 @@ return aNode.getNodeName(); } + private String getQName(Node aNode) { + return getQName(aNode, isNamespaced(aNode)); + } + private String getQName(Node aNode, boolean isNamespacedNode) { + if (isNamespacedNode) { + return "{" + aNode.getNamespaceURI() + "}" + aNode.getLocalName(); + } + return aNode.getNodeName(); + } + /** * @param attribute * @return true if the attribute represents a namespace declaration xmlunit-1.6/src/java/org/custommonkey/xmlunit/Difference.java0000644000000000000000000001400212451007364023206 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; /** * Value object that describes a difference between DOM Nodes using one of * the DifferenceConstants ID values and a NodeDetail instance. *
      Examples and more at xmlunit.sourceforge.net * @see NodeDetail */ public class Difference { /** Simple unique identifier */ private final int id; /** Description of the difference */ private final String description; /** TRUE if the difference represents a similarity, FALSE otherwise */ private boolean recoverable; private NodeDetail controlNodeDetail = null; private NodeDetail testNodeDetail = null; /** * Constructor for non-similar Difference instances * @param id * @param description */ protected Difference(int id, String description) { this(id, description, false); } /** * Constructor for similar Difference instances * @param id * @param description */ protected Difference(int id, String description, boolean recoverable) { this.id = id; this.description = description; this.recoverable = recoverable; } /** * Copy constructor using prototype Difference and * encountered NodeDetails */ protected Difference(Difference prototype, NodeDetail controlNodeDetail, NodeDetail testNodeDetail) { this(prototype.getId(), prototype.getDescription(), prototype.isRecoverable()); this.controlNodeDetail = controlNodeDetail; this.testNodeDetail = testNodeDetail; } /** * @return the id */ public int getId() { return id; } /** * @return the description */ public String getDescription() { return description; } /** * @return TRUE if the difference represents a similarity, FALSE otherwise */ public boolean isRecoverable() { return recoverable; } /** * Allow the recoverable field value to be overridden. * Used when an override DifferenceListener is used in conjunction with * a DetailedDiff. */ protected void setRecoverable(boolean overrideValue) { recoverable = overrideValue; } /** * @return the NodeDetail from the piece of XML used as the control * at the Node where this difference was encountered */ public NodeDetail getControlNodeDetail() { return controlNodeDetail; } /** * @return the NodeDetail from the piece of XML used as the test * at the Node where this difference was encountered */ public NodeDetail getTestNodeDetail() { return testNodeDetail; } /** * Now that Differences can be constructed from prototypes * we need to be able to compare them to those in DifferenceConstants */ public boolean equals(Object other) { if (other == null) { return false; } else if (other instanceof Difference) { Difference otherDifference = (Difference) other; return id == otherDifference.getId(); } else { return false; } } /** * hashcode implementation to go with equals. */ public int hashCode() { return id; } /** * @return a basic representation of the object state and identity * and if NodeDetail instances are populated append * their details also */ public String toString() { StringBuffer buf = new StringBuffer(); if (controlNodeDetail == null || testNodeDetail == null) { appendBasicRepresentation(buf); } else { appendDetailedRepresentation(buf); } return buf.toString(); } private void appendBasicRepresentation(StringBuffer buf) { buf.append("Difference (#").append(id). append(") ").append(description); } private void appendDetailedRepresentation(StringBuffer buf) { buf.append("Expected ").append(getDescription()) .append(" '").append(controlNodeDetail.getValue()) .append("' but was '").append(testNodeDetail.getValue()) .append("' - comparing "); NodeDescriptor.appendNodeDetail(buf, controlNodeDetail); buf.append(" to "); NodeDescriptor.appendNodeDetail(buf, testNodeDetail); } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/CountingNodeTester.java0000644000000000000000000000432512451007364024746 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; /** * Counts the number of nodes in a document to allow assertions to be made * using a NodeTest. *
      Examples and more at xmlunit.sourceforge.net * @see NodeTest * @deprecated Use {@link * org.custommonkey.xmlunit.examples.CountingNodeTester * CountingNodeTester} instead. */ public class CountingNodeTester extends org.custommonkey.xmlunit.examples.CountingNodeTester { public CountingNodeTester(int expectedNumNodes) { super(expectedNumNodes); } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/JAXPConstants.java0000644000000000000000000000517212451007364023623 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; /** * Utility constant class for JAXP-defined constants. */ public final class JAXPConstants { /** * Utility constant class for JAXP Properties, typically used with * {@link XMLReader#setProperty(String, Object)}. * */ public static final class Properties { /** * Property name for the Schema Language being used. As of JAXP 1.2, * only W3C Schema are supported. * * @see XMLConstants#W3C_XML_SCHEMA_NS_URI * @see http://java.sun.com/webservices/jaxp/change-requests-11.html */ public static final String SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage"; /** * Property name for the Schema Location being used. * * @see http://java.sun.com/webservices/jaxp/change-requests-11.html */ public static final String SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource"; } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/examples/0000755000000000000000000000000012451007364022132 5ustar rootrootxmlunit-1.6/src/java/org/custommonkey/xmlunit/examples/MultiLevelElementNameAndTextQualifier.java0000644000000000000000000001323512451007364032330 0ustar rootroot/* ****************************************************************** Copyright (c) 2006-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.examples; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.custommonkey.xmlunit.ElementNameAndTextQualifier; import org.custommonkey.xmlunit.ElementNameQualifier; import org.custommonkey.xmlunit.ElementQualifier; /** * Per popular request an interface implementation that uses element * names and the text node containes in the n'th child node to compare * elements. * *

      This means {@link ElementNameAndTextQualifier * ElementNameQualifier} and MultiLevelElementNameQualifier(1) should * lead to the same results.

      * *

      Any attribute values are completely ignored. Only works on * elements with exactly one child element at each level.

      * *

      This class mostly exists as an example for custom ElementQualifiers.

      */ public class MultiLevelElementNameAndTextQualifier implements ElementQualifier { private final int levels; private final boolean ignoreEmptyTexts; private static final ElementNameQualifier NAME_QUALIFIER = new ElementNameQualifier(); private static final ElementNameAndTextQualifier NAME_AND_TEXT_QUALIFIER = new ElementNameAndTextQualifier(); /** * Uses element names and the text nested levels * child elements deeper into the element to compare elements. * *

      Does not ignore empty text nodes. */ public MultiLevelElementNameAndTextQualifier(int levels) { this(levels, false); } /** * Uses element names and the text nested levels * child elements deeper into the element to compare elements. * * @param ignoreEmptyTexts whether whitespace-only textnodes * should be ignored. */ public MultiLevelElementNameAndTextQualifier(int levels, boolean ignoreEmptyTexts) { if (levels < 1) { throw new IllegalArgumentException("levels must be equal or" + " greater than one"); } this.levels = levels; this.ignoreEmptyTexts = ignoreEmptyTexts; } public boolean qualifyForComparison(Element control, Element test) { boolean stillSimilar = true; Element currentControl = control; Element currentTest = test; // match on element names only for leading levels for (int currentLevel = 0; stillSimilar && currentLevel <= levels - 2; currentLevel++) { stillSimilar = NAME_QUALIFIER.qualifyForComparison(currentControl, currentTest); if (stillSimilar) { if (currentControl.hasChildNodes() && currentTest.hasChildNodes()) { Node n1 = getFirstEligibleChild(currentControl); Node n2 = getFirstEligibleChild(currentTest); if (n1.getNodeType() == Node.ELEMENT_NODE && n2.getNodeType() == Node.ELEMENT_NODE) { currentControl = (Element) n1; currentTest = (Element) n2; } else { stillSimilar = false; } } else { stillSimilar = false; } } } // finally compare the level containing the text child node if (stillSimilar) { stillSimilar = NAME_AND_TEXT_QUALIFIER .qualifyForComparison(currentControl, currentTest); } return stillSimilar; } private Node getFirstEligibleChild(Node parent) { Node n1 = parent.getFirstChild(); if (ignoreEmptyTexts) { while (n1.getNodeType() == Node.TEXT_NODE && n1.getNodeValue().trim().length() == 0) { Node n2 = n1.getNextSibling(); if (n2 == null) break; n1 = n2; } } return n1; } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/examples/FloatingPointTolerantDifferenceListener.java0000644000000000000000000000642412451007364032752 0ustar rootroot/* ****************************************************************** Copyright (c) 2008,2013 Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.examples; import org.custommonkey.xmlunit.Difference; import org.custommonkey.xmlunit.DifferenceListener; /** * Expects texts to be floating point numbers and treats them as * identical if they only differ by a given tolerance value (or less). * *

      This implementation uses an absolute tolerance value value for * all numbers encountered, calculating a difference relative to one * of the numbers compared might be more appropriate in general.

      */ public class FloatingPointTolerantDifferenceListener extends TextDifferenceListenerBase { private final double tolerance; public FloatingPointTolerantDifferenceListener(DifferenceListener delegateTo, double tolerance) { super(delegateTo); this.tolerance = tolerance; } protected int textualDifference(Difference d) { String control = d.getControlNodeDetail().getValue(); String test = d.getTestNodeDetail().getValue(); if (control != null && test != null) { try { double controlVal = Double.parseDouble(control); double testVal = Double.parseDouble(test); return Math.abs(controlVal - testVal) <= tolerance ? DifferenceListener.RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL : DifferenceListener.RETURN_ACCEPT_DIFFERENCE; } catch (NumberFormatException nfe) { // ignore, delegate to nested DifferenceListener } } // no numbers or null, delegate return super.textualDifference(d); } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/examples/CaseInsensitiveDifferenceListener.java0000644000000000000000000000522612451007364031557 0ustar rootroot/* ****************************************************************** Copyright (c) 2008, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.examples; import java.util.Locale; import org.custommonkey.xmlunit.Difference; import org.custommonkey.xmlunit.DifferenceListener; /** * Ignores case for all textual content. */ public class CaseInsensitiveDifferenceListener extends TextDifferenceListenerBase { public CaseInsensitiveDifferenceListener(DifferenceListener delegateTo) { super(delegateTo); } protected int textualDifference(Difference d) { String control = d.getControlNodeDetail().getValue(); if (control != null) { control = control.toLowerCase(Locale.US); if (d.getTestNodeDetail().getValue() != null && control.equals(d.getTestNodeDetail().getValue() .toLowerCase(Locale.US))) { return DifferenceListener.RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL; } } // some string is null, delegate return super.textualDifference(d); } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/examples/CountingNodeTester.java0000644000000000000000000000657012451007364026570 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.examples; import org.custommonkey.xmlunit.NodeTest; import org.custommonkey.xmlunit.NodeTestException; import org.custommonkey.xmlunit.NodeTester; import org.w3c.dom.Node; /** * Counts the number of nodes in a document to allow assertions to be made * using a NodeTest. *
      Examples and more at xmlunit.sourceforge.net * @see NodeTest */ public class CountingNodeTester implements NodeTester { private final int expectedNumNodes; private int actualNumNodes; public CountingNodeTester(int expectedNumNodes) { this.expectedNumNodes = expectedNumNodes; } /** * A single Node is always valid * @param aNode * @param forTest */ public void testNode(Node aNode, NodeTest forTest) { actualNumNodes++; } /** * Called by NodeTest when all nodes have been iterated over: time to see * if all the nodes that were expected were found. * Note that this method also invokes {@link #resetCounter resetCounter} * so that the instance can be reused. * @exception true if expected num nodes == actual num nodes, * false otherwise */ public void noMoreNodes(NodeTest forTest) throws NodeTestException { int testedNodes = actualNumNodes; resetCounter(); if (testedNodes != expectedNumNodes) { throw new NodeTestException("Counted " + testedNodes + " node(s) but expected " + expectedNumNodes); } } /** * Reset the counter so that an instance can be reused for another * NodeTest */ public void resetCounter() { actualNumNodes = 0; } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/examples/RecursiveElementNameAndTextQualifier.java0000644000000000000000000001641312451007364032216 0ustar rootroot/* ****************************************************************** Copyright (c) 2008,2013 Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.examples; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.custommonkey.xmlunit.ElementNameQualifier; import org.custommonkey.xmlunit.ElementQualifier; /** * Compares all Element and Text nodes in two pieces of XML. Allows elements of * complex, deeply nested types that are returned in different orders but have * the same content to be recognized as comparable. * * @author Frank Callahan */ public class RecursiveElementNameAndTextQualifier implements ElementQualifier { private static final ElementNameQualifier NAME_QUALIFIER = new ElementNameQualifier(); /** * Uses element names and the text nested an arbitrary level of * child elements deeper into the element to compare * elements. Checks all nodes, not just first child element. * *

      Does not ignore empty text nodes. */ public RecursiveElementNameAndTextQualifier() { } /** * Returns result of recursive comparison of all the nodes of a * control and test element. */ public boolean qualifyForComparison(Element currentControl, Element currentTest) { return compareNodes(currentControl, currentTest); } private boolean compareNodes(Node currentControl, Node currentTest) { try { // if they are elements, compare names of the two nodes if (!NAME_QUALIFIER.qualifyForComparison((Element) currentControl, (Element) currentTest)) { return false; } // Compare the control and test elements' children NodeList controlNodes = null; NodeList testNodes = null; // Check that both nodes have children and, if so, get lists of them if (currentControl.hasChildNodes() && currentTest.hasChildNodes()) { controlNodes = currentControl.getChildNodes(); testNodes = currentTest.getChildNodes(); } else if (currentControl.hasChildNodes() || currentTest.hasChildNodes()) { return false; // if both nodes are empty, they are comparable } else { return true; } // check that both node lists have the same length if (countNodesWithoutConsecutiveTextNodes(controlNodes) != countNodesWithoutConsecutiveTextNodes(testNodes)) { return false; } // Do checks of test and control nodes' children final int cNodes = controlNodes.getLength(); final int tNodes = testNodes.getLength(); int i, j; for (i = j = 0; i < cNodes && j < tNodes; i++, j++) { Node controlNode = controlNodes.item(i); Node testNode = testNodes.item(j); // check if both node are same type if (controlNode.getNodeType() != testNode.getNodeType()) { return false; } // compare text nodes if (controlNode.getNodeType() == Node.TEXT_NODE) { // compare concatenated, trimmed text nodes if (!catText(controlNode).equals(catText(testNode))) { return false; } // swallow adjacent Text nodes for (; i < cNodes - 1 && controlNodes.item(i + 1).getNodeType() == Node.TEXT_NODE; i++); for (; j < tNodes - 1 && testNodes.item(j + 1).getNodeType() == Node.TEXT_NODE; j++); // recursive check of current child control and test nodes' // children } else if (!compareNodes((Element) controlNode, (Element) testNode)) { return false; } } if (i != cNodes || j != tNodes) { return false; } // All descendants of current control and test nodes are comparable return true; } catch (Exception e) { return false; } } /** * Concatenates contiguous Text nodes and removes all leading and * trailing whitespace. * @param textNode * @return */ private static String catText(Node textNode) { StringBuffer text = new StringBuffer(); Node next = textNode; do { if (next.getNodeValue() != null) { text.append(next.getNodeValue().trim()); next = next.getNextSibling(); } } while (next != null && next.getNodeType() == Node.TEXT_NODE); return text.toString(); } /** * Calculates the number of Nodes that are either not Text nodes * or are Text nodes whose previous sibling isn't a Text node as * well. I.e. consecutive Text nodes are counted as a single * node. */ private static int countNodesWithoutConsecutiveTextNodes(NodeList l) { int count = 0; boolean lastNodeWasText = false; final int length = l.getLength(); for (int i = 0; i < length; i++) { Node n = l.item(i); if (!lastNodeWasText || n.getNodeType() != Node.TEXT_NODE) { count++; } lastNodeWasText = n.getNodeType() == Node.TEXT_NODE; } return count; } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/examples/XPathRegexAssert.java0000644000000000000000000001021412451007364026174 0ustar rootroot// -*- Mode: JDE -*- /* ****************************************************************** Copyright (c) 2006-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.examples; import java.io.IOException; import java.io.Reader; import org.custommonkey.xmlunit.XpathEngine; import org.custommonkey.xmlunit.XMLUnit; import org.custommonkey.xmlunit.exceptions.XpathException; import org.w3c.dom.Document; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import junit.framework.Assert; /** * Example demonstrating how to use the XPath API of XMLUnit in * conjunction with regular expressions (as provided by the * java.util.regex package of JDK 1.4+). */ public class XPathRegexAssert { // no instances private XPathRegexAssert() {} public static void assertXPathMatches(String message, String regex, String xpath, Document doc) throws XpathException { XpathEngine engine = XMLUnit.newXpathEngine(); String value = engine.evaluate(xpath, doc); Assert.assertTrue(message, value.matches(regex)); } public static void assertXPathMatches(String message, String regex, String xpath, String xml) throws XpathException, SAXException, IOException { Document doc = XMLUnit.buildControlDocument(xml); assertXPathMatches(message, regex, xpath, doc); } public static void assertXPathMatches(String message, String regex, String xpath, Reader reader) throws XpathException, SAXException, IOException { Document doc = XMLUnit.buildControlDocument(new InputSource(reader)); assertXPathMatches(message, regex, xpath, doc); } public static void assertXPathMatches(String regex, String xpath, Document doc) throws XpathException { assertXPathMatches("expected value to match " + regex, regex, xpath, doc); } public static void assertXPathMatches(String regex, String xpath, String xml) throws XpathException, SAXException, IOException { assertXPathMatches("expected value to match " + regex, regex, xpath, xml); } public static void assertXPathMatches(String regex, String xpath, Reader reader) throws XpathException, SAXException, IOException { assertXPathMatches("expected value to match " + regex, regex, xpath, reader); } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/examples/TextDifferenceListenerBase.java0000644000000000000000000001102312451007364030172 0ustar rootroot/* ****************************************************************** Copyright (c) 2008, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit.examples; import org.custommonkey.xmlunit.Difference; import org.custommonkey.xmlunit.DifferenceConstants; import org.custommonkey.xmlunit.DifferenceListener; import org.w3c.dom.Node; /** * Base class that delegates all differences to another DifferenceListener. * *

      Subclasses get a chance to hook into special methods that will * be invoked for differences in textual values of attributes, CDATA * sections, Text or comment nodes.

      */ public abstract class TextDifferenceListenerBase implements DifferenceListener { private final DifferenceListener delegateTo; protected TextDifferenceListenerBase(DifferenceListener delegateTo) { this.delegateTo = delegateTo; } /** * Delegates to the nested DifferenceListener unless the * Difference is of type {@link DifferenceConstants#ATTR_VALUE_ID * ATTR_VALUE_ID}, {@link DifferenceConstants#CDATA_VALUE_ID * CDATA_VALUE_ID}, {@link DifferenceConstants#COMMENT_VALUE_ID * COMMENT_VALUE_ID} or {@link DifferenceConstants#TEXT_VALUE_ID * TEXT_VALUE_ID} - for those special differences {@link * #attributeDifference attributeDifference}, {@link * #cdataDifference cdataDifference}, {@link #commentDifference * commentDifference} or {@link #textDifference textDifference} * are invoked respectively. */ public int differenceFound(Difference difference) { switch (difference.getId()) { case DifferenceConstants.ATTR_VALUE_ID: return attributeDifference(difference); case DifferenceConstants.CDATA_VALUE_ID: return cdataDifference(difference); case DifferenceConstants.COMMENT_VALUE_ID: return commentDifference(difference); case DifferenceConstants.TEXT_VALUE_ID: return textDifference(difference); } return delegateTo.differenceFound(difference); } /** * Delegates to {@link #textualDifference textualDifference}. */ protected int attributeDifference(Difference d) { return textualDifference(d); } /** * Delegates to {@link #textualDifference textualDifference}. */ protected int cdataDifference(Difference d) { return textualDifference(d); } /** * Delegates to {@link #textualDifference textualDifference}. */ protected int commentDifference(Difference d) { return textualDifference(d); } /** * Delegates to {@link #textualDifference textualDifference}. */ protected int textDifference(Difference d) { return textualDifference(d); } /** * Delegates to the nested DifferenceListener. */ protected int textualDifference(Difference d) { return delegateTo.differenceFound(d); } public void skippedComparison(Node control, Node test) { delegateTo.skippedComparison(control, test); } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/SimpleNamespaceContext.java0000644000000000000000000000542312451007364025576 0ustar rootroot/* ****************************************************************** Copyright (c) 2001, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * Implementation of NamespaceContext that's backed by a map. */ public class SimpleNamespaceContext implements NamespaceContext { /* prefix -> NS URI */ private final Map/**/ prefixMap; /** * An empty context containing no prefixes at all. */ public static final SimpleNamespaceContext EMPTY_CONTEXT = new SimpleNamespaceContext(Collections.EMPTY_MAP); /** * Creates a NamespaceContext backed by the given map. * *

      Copies the map, changes made to the given map after calling * the constructor are not reflected into the * NamespaceContext.

      * * @param prefixMap maps prefix to Namespace URI */ public SimpleNamespaceContext(Map prefixMap) { this.prefixMap = new HashMap(prefixMap); } public String getNamespaceURI(String prefix) { return (String) prefixMap.get(prefix); } public Iterator getPrefixes() { return prefixMap.keySet().iterator(); } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/DoctypeReader.java0000644000000000000000000002107512451007364023716 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; /** * Adapts the marked-up content in a source Reader to specify that it * conforms to a different DTD. * Combines Reader semantics with the ability to specify a target doctype * for a character stream containing XML markup. * Used by Validator class to wrap a Reader when performing validation of a * document against a DTD. *
      Examples and more at xmlunit.sourceforge.net */ public class DoctypeReader extends Reader { private final Reader originalReader; private final StringBuffer sourceBuffer = new StringBuffer(1024); private final DoctypeSupport support; /** * Create a Reader whose XML content is provided by the originalSource with * the exception of the DOCTYPE which is provided by the doctypeName * and systemID. * @param originalSource * @param doctypeName * @param systemID */ public DoctypeReader(Reader originalSource, String doctypeName, String systemID) { originalReader = originalSource instanceof BufferedReader ? originalSource : new BufferedReader(originalSource); support = new DoctypeSupport(doctypeName, systemID, new DoctypeSupport.Readable() { public int read() throws IOException { return originalReader.read(); } }, true, null); } /** * @return the content of the original source, without amendments or * substitutions. Safe to call multiple times. * @throws IOException if thrown while reading from the original source */ protected String getContent() throws IOException { return getContent(originalReader).toString(); } /** * @param originalSource * @return the contents of the originalSource within a StringBuffer * @throws IOException if thrown while reading from the original source */ private StringBuffer getContent(Reader originalSource) throws IOException { if (sourceBuffer.length() == 0) { BufferedReader bufferedReader; if (originalSource instanceof BufferedReader) { bufferedReader = (BufferedReader) originalSource; } else { bufferedReader = new BufferedReader(originalSource); } String newline = System.getProperty("line.separator"); String source; boolean atFirstLine = true; while ((source = bufferedReader.readLine()) != null) { if (atFirstLine) { atFirstLine = false; } else { sourceBuffer.append(newline); } sourceBuffer.append(source); } bufferedReader.close(); } return sourceBuffer; } /** * Determine where to place the DOCTYPE declaration within some marked-up * content * @param withinContent * @return */ private int obsoleteFindStartDoctype(StringBuffer withinContent) { int startAt = -1; char curChar; boolean canInsert = true; for (int i = 0; startAt == -1; ++i) { curChar = withinContent.charAt(i); if (curChar == '<') { switch (withinContent.charAt(i + 1)) { case '?': case '!': case '-': canInsert = false; break; default: startAt = i; } } else if (curChar == '>') { canInsert = true; } else if (canInsert) { startAt = i; } } return startAt; } /** * Perform DOCTYPE amendment / addition within some marked-up content * @param withinContent * @param doctypeName * @param systemId * @return the content, after DOCTYPE amendment / addition * @deprecated this method is only here for BWC, it is no longer * used by this class */ public String replaceDoctype(StringBuffer withinContent, String doctypeName, String systemId) { String content = withinContent.toString(); int startDoctype = content.indexOf(DoctypeSupport.DOCTYPE); boolean noCurrentDoctype = false; if (startDoctype == -1) { startDoctype = obsoleteFindStartDoctype(withinContent); noCurrentDoctype = true; } int endDoctype = startDoctype + DoctypeSupport.DOCTYPE.length(); if (noCurrentDoctype) { withinContent.insert(startDoctype, DoctypeSupport.DOCTYPE_OPEN_DECL); withinContent.insert(startDoctype + DoctypeSupport.DOCTYPE_OPEN_DECL.length(), DoctypeSupport.DOCTYPE); endDoctype += DoctypeSupport.DOCTYPE_OPEN_DECL.length(); } else { int startInternalDecl = content.indexOf('[', endDoctype); if (startInternalDecl > 0) { int endInternalDecl = content.indexOf(']', startInternalDecl); withinContent.delete(endDoctype, endInternalDecl + 1); } else { int endDoctypeTag = content.indexOf('>', endDoctype); withinContent.delete(endDoctype, endDoctypeTag); } } int atPos = endDoctype; withinContent.insert(atPos, doctypeName); atPos += doctypeName.length(); withinContent.insert(atPos, DoctypeSupport.SYSTEM); atPos += DoctypeSupport.SYSTEM.length(); withinContent.insert(atPos, systemId); atPos += systemId.length(); withinContent.insert(atPos, '"'); if (noCurrentDoctype) { withinContent.insert(++atPos, DoctypeSupport.DOCTYPE_CLOSE_DECL); } return withinContent.toString(); } /** * Read DOCTYPE-replaced content from the wrapped Reader * @param cbuf * @param off * @param len * @return The number of characters read, or -1 if the end of the * stream has been reached * @throws IOException */ public int read(char cbuf[], int off, int len) throws IOException { int startPos = off; int currentlyRead; while (off - startPos < len && (currentlyRead = read()) != -1) { cbuf[off++] = (char) currentlyRead; } return off == startPos && len != 0 ? -1 : off - startPos; } /** * Read DOCTYPE-replaced content from the wrapped Reader */ public int read() throws IOException { return support.read(); } public void close() throws IOException { originalReader.close(); } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/NodeTest.java0000644000000000000000000001716212451007364022713 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.traversal.DocumentTraversal; import org.w3c.dom.traversal.NodeFilter; import org.w3c.dom.traversal.NodeIterator; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * Encapsulation of the Node-by-Node testing of a DOM Document * Uses a nodetype-specific NodeFilter to pass the DOM Nodes * to a NodeTester instance that performs the acual Node validation. *
      Examples and more at xmlunit. * sourceforge.net * @see NodeTester */ public class NodeTest { private final DocumentTraversal documentTraversal; private final Node rootNode; /** * Construct a NodeTest for the DOM built using the String and JAXP */ public NodeTest(String xmlString) throws SAXException, IOException { this(new StringReader(xmlString)); } /** * Construct a NodeTest for the DOM built using the Reader and JAXP */ public NodeTest(Reader reader) throws SAXException, IOException { this(XMLUnit.buildDocument(XMLUnit.newControlParser(), reader)); } /** * Construct a NodeTest for the DOM built using the InputSource. */ public NodeTest(InputSource src) throws SAXException, IOException { this(XMLUnit.buildDocument(XMLUnit.newControlParser(), src)); } /** * Construct a NodeTest for the specified Document * @exception IllegalArgumentException if the Document does not support the DOM * DocumentTraversal interface (most DOM implementations should provide this * support) */ public NodeTest(Document document) { this(getDocumentTraversal(document), document.getDocumentElement()); } /** * Try to cast a Document into a DocumentTraversal * @param document * @return DocumentTraversal interface if the DOM implementation supports it */ private static DocumentTraversal getDocumentTraversal(Document document) { try { return (DocumentTraversal) document; } catch (ClassCastException e) { throw new IllegalArgumentException("DOM Traversal not supported by " + document.getImplementation().getClass().getName() + ". To use this class you will need to switch to a DOM implementation that supports Traversal."); } } /** * Construct a NodeTest using the specified DocumentTraversal, starting at * the specified root node */ public NodeTest(DocumentTraversal documentTraversal, Node rootNode) { this.documentTraversal = documentTraversal; this.rootNode = rootNode; } /** * Does this NodeTest pass using the specified NodeTester instance? * @param tester * @param singleNodeType note Node.ATTRIBUTE_NODE is not * exposed by the DocumentTraversal node iterator unless the root node * is itself an attribute - so a NodeTester that needs to test attributes * should obtain those attributes from Node.ELEMENT_NODE * nodes * @exception NodeTestException if test fails */ public void performTest(NodeTester tester, short singleNodeType) throws NodeTestException { performTest(tester, new short[] {singleNodeType}); } /** * Does this NodeTest pass using the specified NodeTester instance? * @param tester * @param nodeTypes note Node.ATTRIBUTE_NODE is not * exposed by the DocumentTraversal node iterator unless the root node * is itself an attribute - so a NodeTester that needs to test attributes * should obtain those attributes from Node.ELEMENT_NODE * nodes instead * @exception NodeTestException if test fails */ public void performTest(NodeTester tester, short[] nodeTypes) throws NodeTestException { NodeIterator iter = documentTraversal.createNodeIterator(rootNode, NodeFilter.SHOW_ALL, new NodeTypeNodeFilter(nodeTypes), true); for (Node nextNode = iter.nextNode(); nextNode != null; nextNode = iter.nextNode()) { tester.testNode(nextNode, this); } tester.noMoreNodes(this); } /** * Node type specific Node Filter: accepts Nodes of those types specified * in constructor, rejects all others */ private static class NodeTypeNodeFilter implements NodeFilter { private final short[] nodeTypes; /** * Construct filter for specific node types * @param nodeTypes note Node.ATTRIBUTE_NODE is not * exposed by the DocumentTraversal node iterator unless the root node * is itself an attribute - so a NodeTester that needs to test attributes * should obtain those attributes from Node.ELEMENT_NODE * nodes */ public NodeTypeNodeFilter(short[] nodeTypes) { this.nodeTypes = nodeTypes; } /** * NodeFilter method. * @param aNode * @return */ public short acceptNode(Node aNode) { if (acceptNodeType(aNode.getNodeType())) { return NodeFilter.FILTER_ACCEPT; } return NodeFilter.FILTER_REJECT; } /** * Does this instance accept nodes with the node type value * @param shortVal * @return */ private boolean acceptNodeType(short shortVal) { for (int i=0; i < nodeTypes.length; ++i) { if (nodeTypes[i] == shortVal) { return true; } } return false; } } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/ComparisonController.java0000644000000000000000000000451612451007364025343 0ustar rootroot/* ****************************************************************** Copyright (c) 2001, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; /** * Callback interface used by DifferenceEngine to determine whether to halt the * node-by-node comparison of two pieces of XML */ public interface ComparisonController { /** * Determine whether a Difference that the listener has been notified of * should halt further XML comparison. Default behaviour for a Diff * instance is to halt if the Difference is not recoverable. * @see Difference#isRecoverable * @param afterDifference the last Difference passed to differenceFound * @return true to halt further comparison, false otherwise */ boolean haltComparison(Difference afterDifference); } xmlunit-1.6/src/java/org/custommonkey/xmlunit/DifferenceConstants.java0000644000000000000000000002315012451007364025107 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; /** * Constants for describing differences between DOM Nodes. *
      Examples and more at xmlunit.sourceforge.net */ public interface DifferenceConstants { /** Comparing an implied attribute value against an explicit value */ int ATTR_VALUE_EXPLICITLY_SPECIFIED_ID = 1; /** Comparing 2 elements and one has an attribute the other does not */ int ATTR_NAME_NOT_FOUND_ID = 2; /** Comparing 2 attributes with the same name but different values */ int ATTR_VALUE_ID = 3; /** Comparing 2 attribute lists with the same attributes in different sequence */ int ATTR_SEQUENCE_ID = 4; /** Comparing 2 CDATA sections with different values */ int CDATA_VALUE_ID = 5; /** Comparing 2 comments with different values */ int COMMENT_VALUE_ID = 6; /** Comparing 2 document types with different names */ int DOCTYPE_NAME_ID = 7; /** Comparing 2 document types with different public identifiers */ int DOCTYPE_PUBLIC_ID_ID = 8; /** Comparing 2 document types with different system identifiers */ int DOCTYPE_SYSTEM_ID_ID = 9; /** Comparing 2 elements with different tag names */ int ELEMENT_TAG_NAME_ID = 10; /** Comparing 2 elements with different number of attributes */ int ELEMENT_NUM_ATTRIBUTES_ID = 11; /** Comparing 2 processing instructions with different targets */ int PROCESSING_INSTRUCTION_TARGET_ID = 12; /** Comparing 2 processing instructions with different instructions */ int PROCESSING_INSTRUCTION_DATA_ID = 13; /** Comparing 2 different text values */ int TEXT_VALUE_ID = 14; /** Comparing 2 nodes with different namespace prefixes */ int NAMESPACE_PREFIX_ID = 15; /** Comparing 2 nodes with different namespace URIs */ int NAMESPACE_URI_ID = 16; /** Comparing 2 nodes with different node types */ int NODE_TYPE_ID = 17; /** Comparing 2 nodes but only one has any children*/ int HAS_CHILD_NODES_ID = 18; /** Comparing 2 nodes with different numbers of children */ int CHILD_NODELIST_LENGTH_ID = 19; /** Comparing 2 nodes with children whose nodes are in different sequence*/ int CHILD_NODELIST_SEQUENCE_ID = 20; /** Comparing 2 Documents only one of which has a doctype */ int HAS_DOCTYPE_DECLARATION_ID = 21; /** Comparing 2 nodes and one holds more childnodes than can be * matched against child nodes of the other. */ int CHILD_NODE_NOT_FOUND_ID = 22; /** Comparing 2 nodes with different xsi:schemaLocation * attributes, potentially only one of the two provides such an * attribute at all. */ int SCHEMA_LOCATION_ID = 23; /** Comparing 2 nodes with different xsi:noNamespaceSchemaLocation * attributes, potentially only one of the two provides such an * attribute at all. */ int NO_NAMESPACE_SCHEMA_LOCATION_ID = 24; /** Comparing an implied attribute value against an explicit value */ public static final Difference ATTR_VALUE_EXPLICITLY_SPECIFIED = new Difference(ATTR_VALUE_EXPLICITLY_SPECIFIED_ID, "attribute value explicitly specified", true); /** Comparing 2 elements and one has an attribute the other does not */ public static final Difference ATTR_NAME_NOT_FOUND = new Difference(ATTR_NAME_NOT_FOUND_ID, "attribute name"); /** Comparing 2 attributes with the same name but different values */ public static final Difference ATTR_VALUE = new Difference(ATTR_VALUE_ID, "attribute value"); /** Comparing 2 attribute lists with the same attributes in different sequence */ public static final Difference ATTR_SEQUENCE = new Difference(ATTR_SEQUENCE_ID, "sequence of attributes", true); /** Comparing 2 CDATA sections with different values */ public static final Difference CDATA_VALUE = new Difference(CDATA_VALUE_ID, "CDATA section value"); /** Comparing 2 comments with different values */ public static final Difference COMMENT_VALUE = new Difference(COMMENT_VALUE_ID, "comment value"); /** Comparing 2 document types with different names */ public static final Difference DOCTYPE_NAME = new Difference(DOCTYPE_NAME_ID, "doctype name"); /** Comparing 2 document types with different public identifiers */ public static final Difference DOCTYPE_PUBLIC_ID = new Difference(DOCTYPE_PUBLIC_ID_ID, "doctype public identifier"); /** Comparing 2 document types with different system identifiers */ public static final Difference DOCTYPE_SYSTEM_ID = new Difference(DOCTYPE_SYSTEM_ID_ID, "doctype system identifier", true); /** Comparing 2 elements with different tag names */ public static final Difference ELEMENT_TAG_NAME = new Difference(ELEMENT_TAG_NAME_ID, "element tag name"); /** Comparing 2 elements with different number of attributes */ public static final Difference ELEMENT_NUM_ATTRIBUTES = new Difference(ELEMENT_NUM_ATTRIBUTES_ID, "number of element attributes"); /** Comparing 2 processing instructions with different targets */ public static final Difference PROCESSING_INSTRUCTION_TARGET = new Difference(PROCESSING_INSTRUCTION_TARGET_ID, "processing instruction target"); /** Comparing 2 processing instructions with different instructions */ public static final Difference PROCESSING_INSTRUCTION_DATA = new Difference(PROCESSING_INSTRUCTION_DATA_ID, "processing instruction data"); /** Comparing 2 different text values */ public static final Difference TEXT_VALUE = new Difference(TEXT_VALUE_ID, "text value"); /** Comparing 2 nodes with different namespace prefixes */ public static final Difference NAMESPACE_PREFIX = new Difference(NAMESPACE_PREFIX_ID, "namespace prefix", true); /** Comparing 2 nodes with different namespace URIs */ public static final Difference NAMESPACE_URI = new Difference(NAMESPACE_URI_ID, "namespace URI"); /** Comparing 2 nodes with different node types */ public static final Difference NODE_TYPE = new Difference(NODE_TYPE_ID, "node type"); /** Comparing 2 nodes but only one has any children*/ public static final Difference HAS_CHILD_NODES = new Difference(HAS_CHILD_NODES_ID, "presence of child nodes to be"); /** Comparing 2 nodes with different numbers of children */ public static final Difference CHILD_NODELIST_LENGTH = new Difference(CHILD_NODELIST_LENGTH_ID, "number of child nodes"); /** Comparing 2 nodes with children whose nodes are in different sequence*/ public static final Difference CHILD_NODELIST_SEQUENCE = new Difference(CHILD_NODELIST_SEQUENCE_ID, "sequence of child nodes", true); /** Comparing 2 Documents only one of which has a doctype */ public static final Difference HAS_DOCTYPE_DECLARATION = new Difference(HAS_DOCTYPE_DECLARATION_ID, "presence of doctype declaration", true); /** Comparing 2 nodes and one holds more childnodes than can be * matched against child nodes of the other. */ public static final Difference CHILD_NODE_NOT_FOUND = new Difference(CHILD_NODE_NOT_FOUND_ID, "presence of child node"); /** Comparing 2 nodes with different xsi:schemaLocation * attributes, potentially only one of the two provides such an * attribute at all. */ public static final Difference SCHEMA_LOCATION = new Difference(SCHEMA_LOCATION_ID, "xsi:schemaLocation attribute", true); /** Comparing 2 nodes with different xsi:noNamespaceSchemaLocation * attributes, potentially only one of the two provides such an * attribute at all. */ public static final Difference NO_NAMESPACE_SCHEMA_LOCATION = new Difference(NO_NAMESPACE_SCHEMA_LOCATION_ID, "xsi:noNamespaceSchemaLocation attribute", true); } xmlunit-1.6/src/java/org/custommonkey/xmlunit/Transform.java0000644000000000000000000002350312451007364023135 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import org.custommonkey.xmlunit.exceptions.ConfigurationException; import org.custommonkey.xmlunit.exceptions.XMLUnitRuntimeException; import java.io.File; import java.io.StringReader; import java.io.StringWriter; import java.net.MalformedURLException; import java.util.Properties; import javax.xml.transform.ErrorListener; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.URIResolver; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.xml.sax.InputSource; import org.w3c.dom.Document; import org.w3c.dom.Node; /** * Handy wrapper for an XSLT transformation performed using JAXP/Trax. * Note that transformation is not actually performed until a call to * getResultXXX method, and Templates are not used. *
      Examples and more at xmlunit.sourceforge.net */ public class Transform { private static final File PWD = new File("."); private final Source inputSource; private final Transformer transformer; /** * Create a transformation using String input XML and String stylesheet * @param input * @param stylesheet */ public Transform(String input, String stylesheet) { this(new StreamSource(new StringReader(input)), new StreamSource(new StringReader(stylesheet))); } /** * Create a transformation using String input XML and stylesheet in a File * @param input * @param stylesheet */ public Transform(String input, File stylesheet) { this(new StreamSource(new StringReader(input)), new StreamSource(stylesheet)); } /** * Create a transformation using InputSource input XML and * InputSource stylesheet * @param input * @param stylesheet */ public Transform(InputSource input, InputSource stylesheet) { this(new SAXSource(input), new SAXSource(stylesheet)); } /** * Create a transformation using InputSource input XML and * stylesheet in a File * @param input * @param stylesheet */ public Transform(InputSource input, File stylesheet) { this(new SAXSource(input), new StreamSource(stylesheet)); } /** * Create a transformation that allows us to serialize a DOM Node * @param source */ public Transform(Node sourceNode) { this(sourceNode, (Source)null); } /** * Create a transformation from an input Node and stylesheet in a String * @param sourceNode * @param stylesheet */ public Transform(Node sourceNode, String stylesheet) { this(sourceNode, new StreamSource(new StringReader(stylesheet))); } /** * Create a transformation from an input Node and stylesheet in a File * @param sourceNode * @param stylesheet */ public Transform(Node sourceNode, File stylesheet) { this(sourceNode, new StreamSource(stylesheet)); } /** * Create a transformation from an input Node and stylesheet in a Source * @param sourceNode * @param stylesheetSource */ private Transform(Node sourceNode, Source stylesheetSource) { this(new DOMSource(sourceNode), stylesheetSource); } /** * Create a transformation using Source input XML and Source stylesheet * @param inputReader * @param stylesheetReader */ public Transform(Source inputSource, Source stylesheetSource) { this.inputSource = inputSource; provideSystemIdIfRequired(inputSource); provideSystemIdIfRequired(stylesheetSource); this.transformer = getTransformer(stylesheetSource); } /** * Ensure that the source has a systemId * @param source */ private void provideSystemIdIfRequired(Source source) { if (source!=null && (source.getSystemId() == null || source.getSystemId().length() == 0)) { source.setSystemId(getDefaultSystemId()); } } /** * @return the current working directory as an URL-form string */ private String getDefaultSystemId() { try { return PWD.toURL().toExternalForm(); } catch (MalformedURLException e) { throw new XMLUnitRuntimeException("Unable to determine current " + "working directory!", e); } } /** * Factory method * @param stylesheetSource * @throws ConfigurationException * @return */ private Transformer getTransformer(Source stylesheetSource) throws ConfigurationException { try { TransformerFactory factory = XMLUnit.getTransformerFactory(); Transformer t = stylesheetSource == null ? factory.newTransformer() : factory.newTransformer(stylesheetSource); return t; } catch (javax.xml.transform.TransformerConfigurationException ex) { throw new ConfigurationException(ex); } } /** * Perform the actual transformation * @param result * @throws TransformerException */ protected void transformTo(Result result) throws TransformerException { transformer.transform(inputSource, result); } /** * Perform the XSLT transformation specified in the constructor * @return the result as a String * @throws TransformerException */ public String getResultString() throws TransformerException { StringWriter outputWriter = new StringWriter(); transformTo(new StreamResult(outputWriter)); return outputWriter.toString(); } /** * Perform the XSLT transformation specified in the constructor * @return the result as a DOM Document * @throws TransformerException */ public Document getResultDocument() throws TransformerException { DOMResult result = new DOMResult(); transformTo(result); return (Document) result.getNode(); } /** * Override an output property specified in the transformation stylesheet * @param name * @param value */ public void setOutputProperty(String name, String value) { Properties properties = new Properties(); properties.setProperty(name, value); setOutputProperties(properties); } /** * Override output properties specified in the transformation stylesheet * @param outputProperties * @see Transformer#setOutputProperties(java.util.Properties) */ public void setOutputProperties(Properties outputProperties) { transformer.setOutputProperties(outputProperties); } /** * Add a parameter for the transformation * @param name * @param value * @see Transformer#setParameter(java.lang.String, java.lang.Object) */ public void setParameter(String name, Object value) { transformer.setParameter(name, value); } /** * See a parameter used for the transformation * @param name * @return the parameter value * @see Transformer#getParameter(java.lang.String) */ public Object getParameter(String name) { return transformer.getParameter(name); } /** * Clear parameters used for the transformation * @see Transformer#clearParameters() */ public void clearParameters() { transformer.clearParameters(); } /** * Set the URIResolver for the transformation * @see Transformer#setURIResolver(javax.xml.transform.URIResolver) */ public void setURIResolver(URIResolver uriResolver) { transformer.setURIResolver(uriResolver); } /** * Set the ErrorListener for the transformation * @see Transformer#setErrorListener(javax.xml.transform.ErrorListener) */ public void setErrorListener(ErrorListener errorListener) { transformer.setErrorListener(errorListener); } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/NodeDescriptor.java0000644000000000000000000001506612451007364024113 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import org.w3c.dom.Attr; import org.w3c.dom.DocumentType; import org.w3c.dom.Node; import org.w3c.dom.ProcessingInstruction; /** * Class for describing Nodes */ public class NodeDescriptor implements XMLConstants { protected static final String DOCUMENT_NODE_DESCRIPTION = "Document Node "; /** * Convert a Node into a simple String representation * and append to StringBuffer * @param buf * @param aNode */ public static void appendNodeDetail(StringBuffer buf, NodeDetail nodeDetail) { appendNodeDetail(buf, nodeDetail.getNode(), true); buf.append(" at ").append(nodeDetail.getXpathLocation()); } private static void appendNodeDetail(StringBuffer buf, Node aNode, boolean notRecursing) { if (aNode==null) { return; } if (notRecursing) { buf.append(XMLConstants.OPEN_START_NODE); } switch (aNode.getNodeType()) { case Node.ATTRIBUTE_NODE: appendAttributeDetail(buf, aNode); break; case Node.ELEMENT_NODE: appendElementDetail(buf, aNode, notRecursing); break; case Node.TEXT_NODE: appendTextDetail(buf, aNode); break; case Node.CDATA_SECTION_NODE: appendCdataSectionDetail(buf, aNode); break; case Node.COMMENT_NODE: appendCommentDetail(buf, aNode); break; case Node.PROCESSING_INSTRUCTION_NODE: appendProcessingInstructionDetail(buf, aNode); break; case Node.DOCUMENT_TYPE_NODE: appendDocumentTypeDetail(buf, aNode); break; case Node.DOCUMENT_NODE: appendDocumentDetail(buf); break; default: buf.append("!--NodeType ").append(aNode.getNodeType()) .append(' ').append(aNode.getNodeName()) .append('/').append(aNode.getNodeValue()) .append("--"); } if (notRecursing) { buf.append(XMLConstants.CLOSE_NODE); } } protected static void appendDocumentDetail(StringBuffer buf) { buf.append(DOCUMENT_NODE_DESCRIPTION) .append(XMLConstants.OPEN_START_NODE) .append("...") .append(XMLConstants.CLOSE_NODE); } protected static void appendDocumentTypeDetail(StringBuffer buf, Node aNode) { DocumentType type = (DocumentType) aNode; buf.append(XMLConstants.START_DOCTYPE).append(type.getName()); boolean hasNoPublicId = true; if (type.getPublicId()!=null && type.getPublicId().length() > 0) { buf.append(" PUBLIC \"").append(type.getPublicId()) .append('"'); hasNoPublicId = false; } if (type.getSystemId()!=null && type.getSystemId().length() > 0) { if (hasNoPublicId) { buf.append(" SYSTEM"); } buf.append(" \"").append(type.getSystemId()) .append('"'); } } protected static void appendProcessingInstructionDetail( StringBuffer buf, Node aNode) { ProcessingInstruction instr = (ProcessingInstruction) aNode; buf.append(XMLConstants.START_PROCESSING_INSTRUCTION) .append(instr.getTarget()) .append(' ').append(instr.getData()) .append(XMLConstants.END_PROCESSING_INSTRUCTION); } protected static void appendCommentDetail(StringBuffer buf, Node aNode) { buf.append(XMLConstants.START_COMMENT) .append(aNode.getNodeValue()) .append(XMLConstants.END_COMMENT); } protected static void appendCdataSectionDetail(StringBuffer buf, Node aNode) { buf.append(XMLConstants.START_CDATA) .append(aNode.getNodeValue()) .append(XMLConstants.END_CDATA); } protected static void appendTextDetail(StringBuffer buf, Node aNode) { appendNodeDetail(buf, aNode.getParentNode(), false); buf.append(" ...").append(XMLConstants.CLOSE_NODE) .append(aNode.getNodeValue()) .append(XMLConstants.OPEN_END_NODE); appendNodeDetail(buf, aNode.getParentNode(), false); } protected static void appendElementDetail(StringBuffer buf, Node aNode, boolean notRecursing) { buf.append(aNode.getNodeName()); if (notRecursing) { buf.append("..."); } } protected static void appendAttributeDetail(StringBuffer buf, Node aNode) { appendNodeDetail(buf, ((Attr)aNode).getOwnerElement(), false); buf.append(' ') .append(aNode.getNodeName()).append("=\"") .append(aNode.getNodeValue()).append("\"..."); } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/XSLTConstants.java0000644000000000000000000000646512451007364023661 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; /** * A convenient place to hang constants relating to XSL transformations */ public interface XSLTConstants extends XMLConstants { /** * <xsl:stylesheet */ String XSLT_START_NO_VERSION = ""; /** * <xsl:output> for XML with no indentation */ String XSLT_XML_OUTPUT_NOINDENT = ""; /** * <xsl:strip-space> for all elements */ String XSLT_STRIP_WHITESPACE = ""; /** * <xsl:template> to copy the current nodeset into the output tree */ String XSLT_IDENTITY_TEMPLATE = ""; /** * <xsl:template> to copy the current nodeset into the * output tree while stripping comments. */ String XSLT_STRIP_COMMENTS_TEMPLATE = "" + "" + ""; /** * </xsl:stylesheet> */ String XSLT_END = ""; /** * Factory class of the XSLTC version shipping with JDK 1.5 which * is pretty broken. */ String JAVA5_XSLTC_FACTORY_NAME = "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"; } xmlunit-1.6/src/java/org/custommonkey/xmlunit/NodeTester.java0000644000000000000000000000517212451007364023240 0ustar rootroot/* ****************************************************************** Copyright (c) 2001, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import org.w3c.dom.Node; /** * Perform Node-by-Node validation of a DOM Document. * Nodes are supplied to testNode method by a NodeTest instance, * and after all the nodes in the NodeTest have been supplied the * noMoreNodes method is called. *
      Examples and more at xmlunit.sourceforge.net * @see NodeTest */ public interface NodeTester { /** * Validate a single Node * @param aNode * @param forTest * @exception NodeTestException if the node fails the test */ void testNode(Node aNode, NodeTest forTest) throws NodeTestException ; /** * Validate that the Nodes passed one-by-one to the testNode * method were all the Nodes expected. * @param forTest * @exception NodeTestException if this instance was expecting more nodes */ void noMoreNodes(NodeTest forTest) throws NodeTestException ; } xmlunit-1.6/src/java/org/custommonkey/xmlunit/Validator.java0000644000000000000000000004607412451007364023117 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import org.custommonkey.xmlunit.exceptions.ConfigurationException; import org.custommonkey.xmlunit.exceptions.XMLUnitRuntimeException; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; import org.w3c.dom.Document; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotSupportedException; import org.xml.sax.SAXParseException; import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; /** * Validates XML against its internal or external DOCTYPE, or a completely * different DOCTYPE. * Usage: *
        *
      • new Validator(readerForXML);
        * to validate some XML that contains or references an accessible DTD or * schema *
      • *
      • new Validator(readerForXML, systemIdForValidation);
        * to validate some XML that references a DTD but using a local systemId * to perform the validation *
      • *
      • new Validator(readerForXML, systemIdForValidation, doctypeName);
        * to validate some XML against a completely different DTD *
      • *
      *
      Examples and more at xmlunit.sourceforge.net */ public class Validator extends DefaultHandler implements ErrorHandler { private final InputSource validationInputSource; private final SAXParser parser; private final StringBuffer messages; private final boolean usingDoctypeReader; private Boolean isValid; /** * Kept for backwards compatibility. * @deprecated Use the protected three arg constructor instead. */ protected Validator(InputSource inputSource, boolean usingDoctypeReader) throws SAXException, ConfigurationException { this(inputSource, null, usingDoctypeReader); } /** * Baseline constructor: called by all others * * @param inputSource * @param systemId * @param usingDoctypeReader * @throws SAXException * @throws ConfigurationException if validation could not be turned on */ protected Validator(InputSource inputSource, String systemId, boolean usingDoctypeReader) throws SAXException, ConfigurationException { isValid = null; messages = new StringBuffer(); SAXParserFactory factory = XMLUnit.getSAXParserFactory(); factory.setValidating(true); try { parser = factory.newSAXParser(); } catch (ParserConfigurationException ex) { throw new ConfigurationException(ex); } this.validationInputSource = inputSource; if (systemId != null) { validationInputSource.setSystemId(systemId); } this.usingDoctypeReader = usingDoctypeReader; } /** * DOM-style constructor: allows Document validation post-manipulation * of the DOM tree's contents. * This takes a fairly tortuous route to validation as DOM level 2 does * not allow creation of Doctype nodes. * The supplied systemId and doctype name will replace any Doctype * settings in the Document. * * @param document * @param systemID * @param doctype * @throws SAXException if unable to obtain new Sax parser via JAXP factory * @throws ConfigurationException if validation could not be turned on */ public Validator(Document document, String systemID, String doctype) throws SAXException, ConfigurationException { this(new InputStreamReader(new NodeInputStream(document)), systemID, doctype); } /** * Basic constructor. * Validates the contents of the Reader using the DTD or schema referenced * by those contents. * * @param readerForValidation * @throws SAXException if unable to obtain new Sax parser via JAXP factory * @throws ConfigurationException if validation could not be turned on */ public Validator(Reader readerForValidation) throws SAXException, ConfigurationException { this(readerForValidation, null); } /** * Basic constructor. * Validates the contents of the String using the DTD or schema referenced * by those contents. * * @param stringForValidation * @throws SAXException if unable to obtain new Sax parser via JAXP factory * @throws ConfigurationException if validation could not be turned on */ public Validator(String stringForValidation) throws SAXException, ConfigurationException { this(new StringReader(stringForValidation)); } /** * Basic constructor. * Validates the contents of the InputSource using the DTD or * schema referenced by those contents. * * @param readerForValidation * @throws SAXException if unable to obtain new Sax parser via JAXP factory * @throws ConfigurationException if validation could not be turned on */ public Validator(InputSource sourceForValidation) throws SAXException, ConfigurationException { this(sourceForValidation, null); } /** * Extended constructor. * Validates the contents of the Reader using the DTD specified with the * systemID. There must be DOCTYPE instruction in the markup that * references the DTD or else the markup will be considered invalid: if * there is no DOCTYPE in the markup use the 3-argument constructor * * @param readerForValidation * @param systemID * @throws SAXException if unable to obtain new Sax parser via JAXP factory * @throws ConfigurationException if validation could not be turned on */ public Validator(Reader readerForValidation, String systemID) throws SAXException, ConfigurationException { this(new InputSource(readerForValidation), systemID, (readerForValidation instanceof DoctypeReader)); } /** * Extended constructor. * Validates the contents of the String using the DTD specified with the * systemID. There must be DOCTYPE instruction in the markup that * references the DTD or else the markup will be considered invalid: if * there is no DOCTYPE in the markup use the 3-argument constructor * * @param stringForValidation * @param systemID * @throws SAXException if unable to obtain new Sax parser via JAXP factory * @throws ConfigurationException if validation could not be turned on */ public Validator(String stringForValidation, String systemID) throws SAXException, ConfigurationException { this(new StringReader(stringForValidation), systemID); } /** * Extended constructor. * Validates the contents of the InputSource using the DTD * specified with the systemID. There must be DOCTYPE instruction * in the markup that references the DTD or else the markup will * be considered invalid: if there is no DOCTYPE in the markup use * the 3-argument constructor * * @param sourceForValidation * @param systemID * @throws SAXException if unable to obtain new Sax parser via JAXP factory * @throws ConfigurationException if validation could not be turned on */ public Validator(InputSource sourceForValidation, String systemID) throws SAXException, ConfigurationException { this(sourceForValidation, systemID, false); } /** * Full constructor. * Validates the contents of the InputSource using the DTD * specified with the systemID and named with the doctype name. * * @param sourceForValidation * @param systemID * @param doctype * @throws SAXException * @throws ConfigurationException if validation could not be turned on */ public Validator(InputSource sourceForValidation, String systemID, String doctype) throws SAXException, ConfigurationException { this(sourceForValidation.getCharacterStream() != null ? new InputSource(new DoctypeReader(sourceForValidation .getCharacterStream(), doctype, systemID)) : new InputSource(new DoctypeInputStream(sourceForValidation .getByteStream(), sourceForValidation .getEncoding(), doctype, systemID)), systemID, true); } /** * Full constructor. * Validates the contents of the Reader using the DTD specified with the * systemID and named with the doctype name. * * @param readerForValidation * @param systemID * @param doctype * @throws SAXException * @throws ConfigurationException if validation could not be turned on */ public Validator(Reader readerForValidation, String systemID, String doctype) throws SAXException, ConfigurationException { this(readerForValidation instanceof DoctypeReader ? readerForValidation : new DoctypeReader(readerForValidation, doctype, systemID), systemID); } /** * Turn on XML Schema validation. * *

      This feature should work with any XML parser that is JAXP * 1.2 compliant and supports XML Schema validation.

      * *

      For a fully JAXP 1.2 compliant parser the property {@link * JAXPConstants.Properties.SCHEMA_LANGUAGE * http://java.sun.com/xml/jaxp/properties/schemaLanguage} is set, * if this fails the method falls back to the features * http://apache.org/xml/features/validation/schema & * http://apache.org/xml/features/validation/dynamic which should * cover early versions of Xerces 2 as well.

      * * @param use indicate that XML Schema should be used to validate * documents. * @throws SAXException * @see #setJAXP12SchemaSource(Object) */ public void useXMLSchema(boolean use) throws SAXException { boolean tryXercesProperties = false; try { if (use) { parser.setProperty(JAXPConstants.Properties.SCHEMA_LANGUAGE, XMLConstants.W3C_XML_SCHEMA_NS_URI); } } catch (SAXNotRecognizedException e) { tryXercesProperties = true; } catch (SAXNotSupportedException e) { tryXercesProperties = true; } if (tryXercesProperties) { parser.getXMLReader().setFeature("http://apache.org/xml/features/validation/schema", use); parser.getXMLReader().setFeature("http://apache.org/xml/features/validation/dynamic", use); } } /** * Perform the validation of the source against DTD / Schema. * * @return true if the input supplied to the constructor passes validation, * false otherwise */ public boolean isValid() { validate(); return isValid.booleanValue(); } /** * Assert that a document is valid. */ public void assertIsValid(){ if(!isValid()){ junit.framework.Assert.fail(messages.toString()); } } /** * Append any validation message(s) to the specified StringBuffer. * * @param toAppendTo * @return specified StringBuffer with message(s) appended */ private StringBuffer appendMessage(StringBuffer toAppendTo) { if (isValid()) { return toAppendTo.append("[valid]"); } return toAppendTo.append(messages); } /** * @return class name appended with validation messages */ public String toString() { StringBuffer buf = new StringBuffer(super.toString()).append(':'); return appendMessage(buf).toString(); } /** * Actually perform validation. */ private void validate() { if (isValid != null) { return; } try { parser.parse(validationInputSource, this); } catch (SAXException e) { parserException(e); } catch (IOException e) { parserException(e); } if (isValid == null) { isValid = Boolean.TRUE; } else if (usingDoctypeReader) { try { messages.append("\nContent was: ") .append(getOriginalContent(validationInputSource)); } catch (IOException e) { // silent but deadly? } } } /** * Deal with any parser exceptions not handled by the ErrorHandler interface. * * @param e */ private void parserException(Exception e) { invalidate(e.getMessage()); } /** * ErrorHandler interface method. * * @param exception * @throws SAXException */ public void warning(SAXParseException exception) throws SAXException { errorHandlerException(exception); } /** * ErrorHandler interface method. * * @param exception * @throws SAXException */ public void error(SAXParseException exception) throws SAXException { errorHandlerException(exception); } /** * ErrorHandler interface method. * * @param exception * @throws SAXException */ public void fatalError(SAXParseException exception) throws SAXException { errorHandlerException(exception); } /** * Entity Resolver method: allows us to override an existing systemID * referenced in the markup DOCTYPE instruction. * * @param publicId * @param systemId * @return the sax InputSource that points to the overridden systemID */ public InputSource resolveEntity(String publicId, String systemId) { if (validationInputSource.getSystemId() != null) { return new InputSource(validationInputSource.getSystemId()); } else { InputSource s = null; try { if (XMLUnit.getControlEntityResolver() != null) { s = XMLUnit.getControlEntityResolver() .resolveEntity(publicId, systemId); } } catch (SAXException e) { throw new XMLUnitRuntimeException("failed to resolve entity: " + publicId, e); } catch (IOException e) { // even if we wanted to re-throw IOException (which we // can't because of backwards compatibility) the mere // fact that DefaultHandler has stripped IOException // from EntityResolver's method's signature wouldn't // let us. throw new XMLUnitRuntimeException("failed to resolve entity: " + publicId, e); } if (s != null) { return s; } else if (systemId != null) { return new InputSource(systemId); } } return null; } /** * Deal with exceptions passed to the ErrorHandler interface by the parser. */ private void errorHandlerException(SAXParseException e) { String msg = "At line " + e.getLineNumber() + ", column: " + e.getColumnNumber() + " ==> " + e.getMessage(); if (!msg.endsWith("\n")) msg += "\n"; invalidate(msg); } /** * Set the validation status flag to false and capture the message for use * later. * * @param message */ private void invalidate(String message) { isValid = Boolean.FALSE; messages.append(message).append(' '); } /** * As per JAXP 1.2 changes, which introduced a standard way for parsers to * support schema validation. Since only W3C Schema support was included in * JAXP 1.2, this is the only mechanism currently supported by this method. * * @param schemaSource * This can be one of the following: *
        *
      • String that points to the URI of the schema
      • *
      • InputStream with the contents of the schema
      • *
      • SAX InputSource
      • *
      • File
      • *
      • an array of Objects with the contents being one of the * types defined above. An array of Objects can be used only when * the schema language has the ability to assemble a schema at * runtime. When an array of Objects is passed it is illegal to * have two schemas that share the same namespace.
      • *
      * @throws SAXException if this method of validating isn't supported. * @see http://java.sun.com/webservices/jaxp/change-requests-11.html */ public void setJAXP12SchemaSource(Object schemaSource) throws SAXException { parser.setProperty(JAXPConstants.Properties.SCHEMA_SOURCE, schemaSource); } private static String getOriginalContent(InputSource s) throws IOException { return s.getCharacterStream() instanceof DoctypeReader ? ((DoctypeReader) s.getCharacterStream()).getContent() : ((DoctypeInputStream) s.getByteStream()) .getContent(s.getEncoding()); } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/NamespaceContext.java0000644000000000000000000000452412451007364024425 0ustar rootroot/* ****************************************************************** Copyright (c) 2001, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.util.Iterator; /** * Interface used by XpathEngine in order to map prefixes to namespace URIs. * *

      This is modelled after javax.xml.namespace.NamespaceContext but * reduced to our needs.

      */ public interface NamespaceContext { /** * Obtain the URI for a given prefix. * *

      Unlike the method in javax.xml.namespace.NamespaceContext * doesn't have to implement any special handling for predefined * prefix values.

      * * @return null if the prefix is unknown. */ String getNamespaceURI(String prefix); /** * Get all prefixes of this context. */ Iterator getPrefixes(); } xmlunit-1.6/src/java/org/custommonkey/xmlunit/MatchTracker.java0000644000000000000000000000431712451007364023534 0ustar rootroot/* ****************************************************************** Copyright (c) 2008, Stefan Bodewig All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; /** * Listener for callbacks from a {@link DifferenceEngine#compare * DifferenceEngine comparison} that is notified on each and every * comparision that resulted in a match. */ public interface MatchTracker { /** * Receive notification that 2 match. * @param match a Difference instance as defined in {@link * DifferenceConstants DifferenceConstants} describing the test * that matched and containing the detail of the nodes that have * been compared */ void matchFound(Difference difference); } xmlunit-1.6/src/java/org/custommonkey/xmlunit/package.html0000644000000000000000000000020012451007364022565 0ustar rootroot XMLUnit package documentation Contains XMLUnit classes and interfaces. xmlunit-1.6/src/java/org/custommonkey/xmlunit/AbstractNodeTester.java0000644000000000000000000001730012451007364024720 0ustar rootroot/* ****************************************************************** Copyright (c) 2001, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import org.w3c.dom.Attr; import org.w3c.dom.CDATASection; import org.w3c.dom.Comment; import org.w3c.dom.DocumentType; import org.w3c.dom.Element; import org.w3c.dom.Entity; import org.w3c.dom.EntityReference; import org.w3c.dom.Node; import org.w3c.dom.Notation; import org.w3c.dom.ProcessingInstruction; import org.w3c.dom.Text; /** * Helper class. * Abstract interface implementation that performs Node-type checks and * delegates testNode() processing to subclass. *
      Examples and more at xmlunit.sourceforge.net * @see NodeTest */ public abstract class AbstractNodeTester implements NodeTester { /** * Validate a single Node by delegating to node type specific methods. * @see #testAttribute(Attr) * @see #testCDATASection(CDATASection) * @see #testComment(Comment) * @see #testDocumentType(DocumentType) * @see #testElement(Element) * @see #testEntity(Entity) * @see #testEntityReference(EntityReference) * @see #testNotation(Notation) * @see #testProcessingInstruction(ProcessingInstruction) * @see #testText(Text) */ public void testNode(Node aNode, NodeTest forTest) throws NodeTestException { switch (aNode.getNodeType()) { case Node.ATTRIBUTE_NODE: // should not happen as attributes are not exposed by DOM traversal testAttribute((Attr)aNode); break; case Node.CDATA_SECTION_NODE: testCDATASection((CDATASection)aNode); break; case Node.COMMENT_NODE: testComment((Comment)aNode); break; case Node.DOCUMENT_TYPE_NODE: testDocumentType((DocumentType)aNode); break; case Node.ELEMENT_NODE: testElement((Element)aNode); break; case Node.ENTITY_NODE: testEntity((Entity)aNode); break; case Node.ENTITY_REFERENCE_NODE: testEntityReference((EntityReference)aNode); break; case Node.NOTATION_NODE: testNotation((Notation)aNode); break; case Node.PROCESSING_INSTRUCTION_NODE: testProcessingInstruction( (ProcessingInstruction) aNode); break; case Node.TEXT_NODE: testText((Text)aNode); break; default: throw new NodeTestException("No delegate method for Node type", aNode); } } /** * Template delegator for testNode() method. OVERRIDE to add custom logic * @param attribute * @exception NodeTestException always: override if required in subclass */ public void testAttribute(Attr attribute) throws NodeTestException { unhandled(attribute); } /** * Template delegator for testNode() method. OVERRIDE to add custom logic * @param cdata * @exception NodeTestException always: override if required in subclass */ public void testCDATASection(CDATASection cdata) throws NodeTestException { unhandled(cdata); } /** * Template delegator for testNode() method. OVERRIDE to add custom logic * @param comment * @exception NodeTestException always: override if required in subclass */ public void testComment(Comment comment) throws NodeTestException { unhandled(comment); } /** * Template delegator for testNode() method. OVERRIDE to add custom logic * @param doctype * @exception NodeTestException always: override if required in subclass */ public void testDocumentType(DocumentType doctype) throws NodeTestException { unhandled(doctype); } /** * Template delegator for testNode() method. OVERRIDE to add custom logic * @param element * @exception NodeTestException always: override if required in subclass */ public void testElement(Element element) throws NodeTestException { unhandled(element); } /** * Template delegator for testNode() method. OVERRIDE to add custom logic * @param entity * @exception NodeTestException always: override if required in subclass */ public void testEntity(Entity entity) throws NodeTestException { unhandled(entity); } /** * Template delegator for testNode() method. OVERRIDE to add custom logic * @param reference * @exception NodeTestException always: override if required in subclass */ public void testEntityReference(EntityReference reference) throws NodeTestException { unhandled(reference); } /** * Template delegator for testNode() method. OVERRIDE to add custom logic * @param notation * @exception NodeTestException always: override if required in subclass */ public void testNotation(Notation notation) throws NodeTestException { unhandled(notation); } /** * Template delegator for testNode() method. OVERRIDE to add custom logic * @param instr * @exception NodeTestException always: override if required in subclass */ public void testProcessingInstruction(ProcessingInstruction instr) throws NodeTestException { unhandled(instr); } /** * Template delegator for testNode() method. OVERRIDE to add custom logic * @param text * @exception NodeTestException always: override if required in subclass */ public void testText(Text text) throws NodeTestException { unhandled(text); } private void unhandled(Node aNode) throws NodeTestException { throw new NodeTestException("Test fails by default in AbstractNodeTester", aNode); } /** * Validate that the Nodes validated one-by-one in the isValid * method were all the Nodes expected. By default do nothing: * can override to add custom logic * @exception NodeTestException if mode Nodes were expected */ public void noMoreNodes(NodeTest forTest) throws NodeTestException { //by default do nothing } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/HTMLDocumentBuilder.java0000644000000000000000000002574012451007364024741 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2007, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.util.Enumeration; import javax.swing.text.*; import javax.swing.text.html.*; import javax.swing.text.html.parser.*; import org.w3c.dom.Document; import org.xml.sax.ContentHandler; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.ext.LexicalHandler; import org.xml.sax.helpers.AttributesImpl; /** * Build a DOM document from HTML content converting from 'plain' HTML into * 'XHTML' along the way with the help of a TolerantSaxDocumentBuilder and * the Swing html parser classes. * This allows XML assertions to be made against badly formed HTML. *
      Examples and more at xmlunit.sourceforge.net * @see TolerantSaxDocumentBuilder */ public class HTMLDocumentBuilder { protected final TolerantSaxDocumentBuilder tolerantSaxDocumentBuilder; protected final SwingEvent2SaxAdapter swingEvent2SaxAdapter; private final StringBuffer traceBuffer; /** * Constructor * @param tolerantSaxDocumentBuilder the instance that will receive SAX * calls generated as the HTML is parsed and build up a DOM Document */ public HTMLDocumentBuilder( TolerantSaxDocumentBuilder tolerantSaxDocumentBuilder) { this.tolerantSaxDocumentBuilder = tolerantSaxDocumentBuilder; this.swingEvent2SaxAdapter = new SwingEvent2SaxAdapter(); this.traceBuffer = new StringBuffer(); } /** * @return a DOM document parsed from the Reader via an SwingEvent2SaxAdapter * and TolerantSaxBuilder. * Not thread-safe! * @see TolerantSaxDocumentBuilder */ public Document parse(Reader reader) throws SAXException, IOException { traceBuffer.delete(0, traceBuffer.length()); swingEvent2SaxAdapter.parse(reader, tolerantSaxDocumentBuilder); traceBuffer.append(tolerantSaxDocumentBuilder.getTrace()); return tolerantSaxDocumentBuilder.getDocument(); } /** * @return a DOM document parsed from the String via an SwingEvent2SaxAdapter * and TolerantSaxBuilder. * Not thread-safe! * @see TolerantSaxDocumentBuilder */ public Document parse(String htmlString) throws SAXException, IOException { return parse(new StringReader(htmlString)); } /** * @return the trace of events and / or warnings encountered during parsing */ public String getTrace() { return traceBuffer.toString(); } /** * Append to the log built up during parsing * @param msg what to append */ private void trace(String msg) { traceBuffer.append(msg).append('\n'); } /** * Adapts Swing HTML callback messages to Sax equivalents, passing them * to a Sax-aware ContentHandler. */ public class SwingEvent2SaxAdapter extends HTMLEditorKit.ParserCallback { private static final boolean IGNORE_HTML_CHAR_SET = true; private final AttributesImpl attributes; private final ParserDelegator delegator; private boolean lastTagWasSimpleTag; private ContentHandler saxContentHandler; private SAXException firstUnhandledException; /** * Default constructor */ public SwingEvent2SaxAdapter() { this.attributes = new AttributesImpl(); this.delegator = new ParserDelegator(); } /** * Perform Swing-HTML-parse-event-to-Sax-event conversion */ public void parse(Reader reader, ContentHandler saxContentHandler) throws SAXException, IOException { this.saxContentHandler = saxContentHandler; preParse(); delegator.parse(reader, this, IGNORE_HTML_CHAR_SET); postParse(); } /** * Equivalent to Sax startDocument * @throws SAXException */ private void preParse() throws SAXException { firstUnhandledException = null; saxContentHandler.startDocument(); } /** * Equivalent to Sax endDocument * @throws SAXException if any SAXExceptions have occurred during * parsing */ private void postParse() throws SAXException { try { saxContentHandler.endDocument(); } catch (SAXException e) { handleSAXException(e); } if (firstUnhandledException != null) { throw firstUnhandledException; } } /** * Swing-HTML-parser template method, no ContentHandler equivalent */ public void flush() throws javax.swing.text.BadLocationException { } /** * Equivalent to Sax characters */ public void handleText(char[] data, int pos) { try { int startPos; if (lastTagWasSimpleTag) { startPos = getStartIgnoringClosingSimpleTag(data); } else { startPos = 0; } if (startPos < data.length) { saxContentHandler.characters(data, startPos, data.length - startPos); } } catch (SAXException e) { handleSAXException(e); } } /** * Adjusts the start offset into the character array for the fact that * the Swing HTML parser doesn't handle simple tags with explicit * closing angle brackets e.g. <hr/> * @param data * @return offset of actual character data into the array */ private int getStartIgnoringClosingSimpleTag(char[] data) { if (data[0] == '>') { return 1; } return 0; } /** * Equivalent to Sax LexicalHandler comment method. * If the supplied ContentHandler is also an LexicalHandler then the * cast will be made and the sax event passed on. */ public void handleComment(char[] data, int pos) { if (saxContentHandler instanceof LexicalHandler) { try { ((LexicalHandler)saxContentHandler).comment(data, 0, data.length); } catch (SAXException e) { handleSAXException(e); } } else { trace("Unhandled comment " + new String(data)); } } /** * Equivalent to Sax startElement */ public void handleStartTag(javax.swing.text.html.HTML.Tag tag, javax.swing.text.MutableAttributeSet attributeSet, int pos) { try { saxContentHandler.startElement("", "", tag.toString(), convertToSaxAttributes(attributeSet)); } catch (SAXException e) { handleSAXException(e); } lastTagWasSimpleTag = false; } /** * Equivalent to Sax endElement */ public void handleEndTag(javax.swing.text.html.HTML.Tag tag, int pos) { try { saxContentHandler.endElement("", "", tag.toString()); } catch (SAXException e) { handleSAXException(e); } } /** * Equivalent to Sax startElement plus * endElement */ public void handleSimpleTag(javax.swing.text.html.HTML.Tag tag, javax.swing.text.MutableAttributeSet attributeSet, int pos) { handleStartTag(tag, attributeSet, pos); handleEndTag(tag, pos); lastTagWasSimpleTag = true; } /** * Swing-HTML-parser template method, no ContentHandler equivalent. * These errors are generally recoverable, so they are logged. */ public void handleError(String errorMsg, int pos){ trace("HTML ERROR: " + errorMsg); } /** * Simple conversion method. * @param attributeSet * @return Sax CDATA Attributes from the Swing MutableAttributeSet */ private Attributes convertToSaxAttributes( MutableAttributeSet attributeSet) { Object attrName, attrValue; attributes.clear(); for(Enumeration en = attributeSet.getAttributeNames(); en.hasMoreElements(); ) { attrName = en.nextElement(); attrValue = attributeSet.getAttribute(attrName); attributes.addAttribute("", "", attrName.toString(), "CDATA", attrValue.toString()); } return attributes; } /** * Log an error from the ContentHandler for raising post-parse */ private void handleSAXException(SAXException e) { trace("SAX Error: " + e.getMessage()); if (firstUnhandledException==null) { firstUnhandledException = e; } } } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/DifferenceListener.java0000644000000000000000000000737112451007364024727 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2008, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import org.w3c.dom.Node; /** * Listener for callbacks from a * {@link DifferenceEngine#compare DifferenceEngine comparison}. *
      Examples and more at xmlunit.sourceforge.net */ public interface DifferenceListener { /** * Standard return value for the differenceFound method. * Indicates that the Difference is interpreted as defined * in {@link DifferenceConstants DifferenceConstants}. */ int RETURN_ACCEPT_DIFFERENCE = 0; /** * Override return value for the differenceFound method. * Indicates that the nodes identified as being different should be * interpreted as being identical. */ int RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL = 1; /** * Override return value for the differenceFound method. * Indicates that the nodes identified as being different should be * interpreted as being similar. */ int RETURN_IGNORE_DIFFERENCE_NODES_SIMILAR = 2; /** * Override return value for the differenceFound method. * Indicates that the nodes identified as being similar should be * interpreted as being different. */ int RETURN_UPGRADE_DIFFERENCE_NODES_DIFFERENT = 3; /** * Receive notification that 2 nodes are different. * @param difference a Difference instance as defined in {@link * DifferenceConstants DifferenceConstants} describing the cause * of the difference and containing the detail of the nodes that * differ * @return int one of the RETURN_... constants describing how this * difference was interpreted */ int differenceFound(Difference difference); /** * Receive notification that a comparison between 2 nodes has been skipped * because the node types are not comparable by the DifferenceEngine * @param control the control node being compared * @param test the test node being compared * @see DifferenceEngine */ void skippedComparison(Node control, Node test); } xmlunit-1.6/src/java/org/custommonkey/xmlunit/NodeDetail.java0000644000000000000000000000522612451007364023174 0ustar rootroot/* ****************************************************************** Copyright (c) 2001, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import org.w3c.dom.Node; /** * Parameter class for holding information about a Node within * a Difference instance * @see Difference#getControlNodeDetail * @see Difference#getTestNodeDetail */ public class NodeDetail { private final String value; private final Node node; private final String xpathLocation; /** * Constructor for NodeDetail. */ public NodeDetail(String value, Node node, String xpathLocation) { this.value = value; this.node = node; this.xpathLocation = xpathLocation; } /** * Returns the node. * @return Node */ public Node getNode() { return node; } /** * Returns the value. * @return String */ public String getValue() { return value; } /** * Returns the xpathLocation. * @return String */ public String getXpathLocation() { return xpathLocation; } } xmlunit-1.6/src/java/org/custommonkey/xmlunit/DifferenceEngine.java0000644000000000000000000012262712451007364024351 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2014, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import org.w3c.dom.Attr; import org.w3c.dom.CharacterData; import org.w3c.dom.CDATASection; import org.w3c.dom.Comment; import org.w3c.dom.Document; import org.w3c.dom.DocumentType; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.ProcessingInstruction; import org.w3c.dom.Text; /** * Class that has responsibility for comparing Nodes and notifying a * DifferenceListener of any differences or dissimilarities that are found. * Knows how to compare namespaces and nested child nodes, but currently * only compares nodes of type ELEMENT_NODE, CDATA_SECTION_NODE, * COMMENT_NODE, DOCUMENT_TYPE_NODE, PROCESSING_INSTRUCTION_NODE and TEXT_NODE. * Nodes of other types (eg ENTITY_NODE) will be skipped. *
      Examples and more at xmlunit. * sourceforge.net * @see DifferenceListener#differenceFound(Difference) */ public class DifferenceEngine implements DifferenceConstants { private static final String NULL_NODE = "null"; private static final String NOT_NULL_NODE = "not null"; private static final String ATTRIBUTE_ABSENT = "[attribute absent]"; private final ComparisonController controller; private MatchTracker matchTracker; private final XpathNodeTracker controlTracker; private final XpathNodeTracker testTracker; /** * Simple constructor that uses no MatchTracker at all. * @param controller the instance used to determine whether a Difference * detected by this class should halt further comparison or not * @see ComparisonController#haltComparison(Difference) */ public DifferenceEngine(ComparisonController controller) { this(controller, null); } /** * Simple constructor * @param controller the instance used to determine whether a Difference * detected by this class should halt further comparison or not * @param matchTracker the instance that is notified on each * successful match. May be null. * @see ComparisonController#haltComparison(Difference) * @see MatchTracker#matchFound(Difference) */ public DifferenceEngine(ComparisonController controller, MatchTracker matchTracker) { this.controller = controller; this.matchTracker = matchTracker; this.controlTracker = new XpathNodeTracker(); this.testTracker = new XpathNodeTracker(); } /** * @param matchTracker the instance that is notified on each * successful match. May be null. */ public void setMatchTracker(MatchTracker matchTracker) { this.matchTracker = matchTracker; } /** * Entry point for Node comparison testing. * @param control Control XML to compare * @param test Test XML to compare * @param listener Notified of any {@link Difference differences} detected * during node comparison testing * @param elementQualifier Used to determine which elements qualify for * comparison e.g. when a node has repeated child elements that may occur * in any sequence and that sequence is not considered important. */ public void compare(Node control, Node test, DifferenceListener listener, ElementQualifier elementQualifier) { controlTracker.reset(); testTracker.reset(); try { compare(getNullOrNotNull(control), getNullOrNotNull(test), control, test, listener, NODE_TYPE); if (control!=null) { compareNode(control, test, listener, elementQualifier); } } catch (DifferenceFoundException e) { // thrown by the protected compare() method to terminate the // comparison and unwind the call stack back to here } } private String getNullOrNotNull(Node aNode) { return aNode==null ? NULL_NODE : NOT_NULL_NODE; } /** * First point of call: if nodes are comparable it compares node values then * recurses to compare node children. * @param control * @param test * @param listener * @param elementQualifier * @throws DifferenceFoundException */ protected void compareNode(Node control, Node test, DifferenceListener listener, ElementQualifier elementQualifier) throws DifferenceFoundException { boolean comparable = compareNodeBasics(control, test, listener); boolean isDocumentNode = false; if (comparable) { switch (control.getNodeType()) { case Node.ELEMENT_NODE: compareElement((Element)control, (Element)test, listener); break; case Node.CDATA_SECTION_NODE: case Node.TEXT_NODE: compareText((CharacterData) control, (CharacterData) test, listener); break; case Node.COMMENT_NODE: compareComment((Comment)control, (Comment)test, listener); break; case Node.DOCUMENT_TYPE_NODE: compareDocumentType((DocumentType)control, (DocumentType)test, listener); break; case Node.PROCESSING_INSTRUCTION_NODE: compareProcessingInstruction((ProcessingInstruction)control, (ProcessingInstruction)test, listener); break; case Node.DOCUMENT_NODE: isDocumentNode = true; compareDocument((Document)control, (Document) test, listener, elementQualifier); break; default: listener.skippedComparison(control, test); } } compareHasChildNodes(control, test, listener); if (isDocumentNode) { Element controlElement = ((Document)control).getDocumentElement(); Element testElement = ((Document)test).getDocumentElement(); if (controlElement!=null && testElement!=null) { compareNode(controlElement, testElement, listener, elementQualifier); } } else { controlTracker.indent(); testTracker.indent(); compareNodeChildren(control, test, listener, elementQualifier); controlTracker.outdent(); testTracker.outdent(); } } /** * Compare two Documents for doctype and then element differences * @param control * @param test * @param listener * @param elementQualifier * @throws DifferenceFoundException */ protected void compareDocument(Document control, Document test, DifferenceListener listener, ElementQualifier elementQualifier) throws DifferenceFoundException { DocumentType controlDoctype = control.getDoctype(); DocumentType testDoctype = test.getDoctype(); compare(getNullOrNotNull(controlDoctype), getNullOrNotNull(testDoctype), controlDoctype, testDoctype, listener, HAS_DOCTYPE_DECLARATION); if (controlDoctype!=null && testDoctype!=null) { compareNode(controlDoctype, testDoctype, listener, elementQualifier); } } /** * Compares node type and node namespace characteristics: basically * determines if nodes are comparable further * @param control * @param test * @param listener * @return true if the nodes are comparable further, false otherwise * @throws DifferenceFoundException */ protected boolean compareNodeBasics(Node control, Node test, DifferenceListener listener) throws DifferenceFoundException { controlTracker.visited(control); testTracker.visited(test); Short controlType = new Short(control.getNodeType()); Short testType = new Short(test.getNodeType()); boolean textAndCDATA = comparingTextAndCDATA(control.getNodeType(), test.getNodeType()); if (!textAndCDATA) { compare(controlType, testType, control, test, listener, NODE_TYPE); } compare(control.getNamespaceURI(), test.getNamespaceURI(), control, test, listener, NAMESPACE_URI); compare(control.getPrefix(), test.getPrefix(), control, test, listener, NAMESPACE_PREFIX); return textAndCDATA || controlType.equals(testType); } private boolean comparingTextAndCDATA(short controlType, short testType) { return XMLUnit.getIgnoreDiffBetweenTextAndCDATA() && (controlType == Node.TEXT_NODE && testType == Node.CDATA_SECTION_NODE || testType == Node.TEXT_NODE && controlType == Node.CDATA_SECTION_NODE); } /** * Compare the number of children, and if the same, compare the actual * children via their NodeLists. * @param control * @param test * @param listener * @throws DifferenceFoundException */ protected void compareHasChildNodes(Node control, Node test, DifferenceListener listener) throws DifferenceFoundException { Boolean controlHasChildren = hasChildNodes(control); Boolean testHasChildren = hasChildNodes(test); compare(controlHasChildren, testHasChildren, control, test, listener, HAS_CHILD_NODES); } /** * Tests whether a Node has children, taking ignoreComments * setting into account. */ private Boolean hasChildNodes(Node n) { boolean flag = n.hasChildNodes(); if (flag && XMLUnit.getIgnoreComments()) { List nl = nodeList2List(n.getChildNodes()); flag = !nl.isEmpty(); } return flag ? Boolean.TRUE : Boolean.FALSE; } /** * Returns the NodeList's Nodes as List, taking ignoreComments * into account. */ static List nodeList2List(NodeList nl) { int len = nl.getLength(); ArrayList l = new ArrayList(len); for (int i = 0; i < len; i++) { Node n = nl.item(i); if (!XMLUnit.getIgnoreComments() || !(n instanceof Comment)) { l.add(n); } } return l; } /** * Compare the number of children, and if the same, compare the actual * children via their NodeLists. * @param control * @param test * @param listener * @param elementQualifier * @throws DifferenceFoundException */ protected void compareNodeChildren(Node control, Node test, DifferenceListener listener, ElementQualifier elementQualifier) throws DifferenceFoundException { List controlChildren = nodeList2List(control.getChildNodes()); List testChildren = nodeList2List(test.getChildNodes()); Integer controlLength = new Integer(controlChildren.size()); Integer testLength = new Integer(testChildren.size()); compare(controlLength, testLength, control, test, listener, CHILD_NODELIST_LENGTH); if (control.hasChildNodes() || test.hasChildNodes()) { if (!control.hasChildNodes()) { for (Iterator iter = testChildren.iterator(); iter.hasNext();) { missingNode(null, (Node) iter.next(), listener); } } else if (!test.hasChildNodes()) { for (Iterator iter = controlChildren.iterator(); iter.hasNext();) { missingNode((Node) iter.next(), null, listener); } } else { compareNodeList(controlChildren, testChildren, controlLength.intValue(), listener, elementQualifier); } } } /** * Compare the contents of two node list one by one, assuming that order * of children is NOT important: matching begins at same position in test * list as control list. * @param control * @param test * @param numNodes convenience parameter because the calling method should * know the value already * @param listener * @param elementQualifier used to determine which of the child elements in * the test NodeList should be compared to the current child element in the * control NodeList. * @throws DifferenceFoundException * @deprecated Use the version with List arguments instead */ protected void compareNodeList(final NodeList control, final NodeList test, final int numNodes, final DifferenceListener listener, final ElementQualifier elementQualifier) throws DifferenceFoundException { compareNodeList(nodeList2List(control), nodeList2List(test), numNodes, listener, elementQualifier); } /** * Compare the contents of two node list one by one, assuming that order * of children is NOT important: matching begins at same position in test * list as control list. * @param control * @param test * @param numNodes convenience parameter because the calling method should * know the value already * @param listener * @param elementQualifier used to determine which of the child elements in * the test NodeList should be compared to the current child element in the * control NodeList. * @throws DifferenceFoundException */ protected void compareNodeList(final List controlChildren, final List testChildren, final int numNodes, final DifferenceListener listener, final ElementQualifier elementQualifier) throws DifferenceFoundException { int j = 0; final int lastTestNode = testChildren.size() - 1; testTracker.preloadChildList(testChildren); HashMap/**/ matchingNodes = new HashMap(); HashMap/**/ matchingNodeIndexes = new HashMap(); List/**/ unmatchedTestNodes = new ArrayList(testChildren); // first pass to find the matching nodes in control and test docs for (int i=0; i < numNodes; ++i) { Node nextControl = (Node) controlChildren.get(i); boolean matchOnElement = nextControl instanceof Element; short findNodeType = nextControl.getNodeType(); int startAt = ( i > lastTestNode ? lastTestNode : i); j = startAt; boolean matchFound = false; /* * XMLUnit 1.2 and earlier don't check whether the * "matched" test node has already been matched to a * different control node and will happily match the same * test node to each and every control node, if necessary. * * I (Stefan) feel this is wrong but can't change it * without breaking backwards compatibility * (testXpathLocation12 in test_DifferenceEngine which * predates XMLUnit 1.0 fails, so at one point it has been * the expected and intended behaviour). * * As a side effect it may leave test nodes inside the * unmatched list, see * https://sourceforge.net/tracker/?func=detail&aid=2807167&group_id=23187&atid=377768 * * To overcome the later problem the code will now prefer * test nodes that haven't already been matched to any * other node and falls back to the first * (multiply-)matched node if none could be found. Yes, * this is strange. */ int fallbackMatch = -1; while (!matchFound) { Node t = (Node) testChildren.get(j); if (findNodeType == t.getNodeType() || comparingTextAndCDATA(findNodeType, t.getNodeType())) { matchFound = !matchOnElement || elementQualifier == null || elementQualifier .qualifyForComparison((Element) nextControl, (Element) t); } if (matchFound && !unmatchedTestNodes.contains(t)) { /* * test node already matched to a different * control node, try the other test nodes first * but keep this as "fallback" (unless there * already is a fallback) */ if (fallbackMatch < 0) { fallbackMatch = j; } matchFound = false; } if (!matchFound) { ++j; if (j > lastTestNode) { j = 0; } if (j == startAt) { // been through all children break; } } } if (!matchFound && XMLUnit.getCompareUnmatched() && fallbackMatch >= 0) { matchFound = true; j = fallbackMatch; } if (matchFound) { matchingNodes.put(nextControl, testChildren.get(j)); matchingNodeIndexes.put(nextControl, new Integer(j)); unmatchedTestNodes.remove(testChildren.get(j)); } } // next, do the actual comparision on those that matched - or // match them against the first test nodes that didn't match // any other control nodes for (int i=0; i < numNodes; ++i) { Node nextControl = (Node) controlChildren.get(i); Node nextTest = (Node) matchingNodes.get(nextControl); Integer testIndex = (Integer) matchingNodeIndexes.get(nextControl); if (nextTest == null && XMLUnit.getCompareUnmatched() && !unmatchedTestNodes.isEmpty()) { nextTest = (Node) unmatchedTestNodes.get(0); testIndex = new Integer(testChildren.indexOf(nextTest)); unmatchedTestNodes.remove(0); } if (nextTest != null) { compareNode(nextControl, nextTest, listener, elementQualifier); compare(new Integer(i), testIndex, nextControl, nextTest, listener, CHILD_NODELIST_SEQUENCE); } else { missingNode(nextControl, null, listener); } } // now handle remaining unmatched test nodes for (Iterator iter = unmatchedTestNodes.iterator(); iter.hasNext();) { missingNode(null, (Node) iter.next(), listener); } } private void missingNode(Node control, Node test, DifferenceListener listener) throws DifferenceFoundException { if (control != null) { controlTracker.visited(control); compare(getQName(control), null, control, null, listener, CHILD_NODE_NOT_FOUND, controlTracker, null); } else { testTracker.visited(test); compare(null, getQName(test), null, test, listener, CHILD_NODE_NOT_FOUND, null, testTracker); } } /** * @param aNode * @return true if the node has a namespace */ private boolean isNamespaced(Node aNode) { String namespace = aNode.getNamespaceURI(); return namespace != null && namespace.length() > 0; } /** * Compare 2 elements and their attributes * @param control * @param test * @param listener * @throws DifferenceFoundException */ protected void compareElement(Element control, Element test, DifferenceListener listener) throws DifferenceFoundException { compare(getUnNamespacedNodeName(control), getUnNamespacedNodeName(test), control, test, listener, ELEMENT_TAG_NAME); NamedNodeMap controlAttr = control.getAttributes(); Integer controlNonXmlnsAttrLength = getNonSpecialAttrLength(controlAttr); NamedNodeMap testAttr = test.getAttributes(); Integer testNonXmlnsAttrLength = getNonSpecialAttrLength(testAttr); compare(controlNonXmlnsAttrLength, testNonXmlnsAttrLength, control, test, listener, ELEMENT_NUM_ATTRIBUTES); compareElementAttributes(control, test, controlAttr, testAttr, listener); } /** * The number of attributes not related to namespace declarations * and/or Schema location. */ private Integer getNonSpecialAttrLength(NamedNodeMap attributes) { int length = 0, maxLength = attributes.getLength(); for (int i = 0; i < maxLength; ++i) { Attr a = (Attr) attributes.item(i); if (!isXMLNSAttribute(a) && !isRecognizedXMLSchemaInstanceAttribute(a)) { ++length; } } return new Integer(length); } void compareElementAttributes(Element control, Element test, NamedNodeMap controlAttr, NamedNodeMap testAttr, DifferenceListener listener) throws DifferenceFoundException { ArrayList unmatchedTestAttrs = new ArrayList(); for (int i=0; i < testAttr.getLength(); ++i) { Attr nextAttr = (Attr) testAttr.item(i); if (!isXMLNSAttribute(nextAttr)) { unmatchedTestAttrs.add(nextAttr); } } for (int i=0; i < controlAttr.getLength(); ++i) { Attr nextAttr = (Attr) controlAttr.item(i); if (isXMLNSAttribute(nextAttr)) { // xml namespacing is handled in compareNodeBasics } else { boolean isNamespacedAttr = isNamespaced(nextAttr); String attrName = getUnNamespacedNodeName(nextAttr, isNamespacedAttr); Attr compareTo = null; if (isNamespacedAttr) { compareTo = (Attr) testAttr.getNamedItemNS( nextAttr.getNamespaceURI(), attrName); } else { compareTo = (Attr) testAttr.getNamedItem(attrName); } if (compareTo != null) { unmatchedTestAttrs.remove(compareTo); } if (isRecognizedXMLSchemaInstanceAttribute(nextAttr)) { compareRecognizedXMLSchemaInstanceAttribute(nextAttr, compareTo, listener); } else if (compareTo != null) { compareAttribute(nextAttr, compareTo, listener); if (!XMLUnit.getIgnoreAttributeOrder()) { Attr attributeItem = (Attr) testAttr.item(i); String testAttrName = ATTRIBUTE_ABSENT; if (attributeItem != null) { testAttrName = getUnNamespacedNodeName(attributeItem); } compare(attrName, testAttrName, nextAttr, compareTo, listener, ATTR_SEQUENCE); } } else { controlTracker.clearTrackedAttribute(); controlTracker.visited(nextAttr); testTracker.clearTrackedAttribute(); compare(getQName(nextAttr, isNamespacedAttr), null, control, test, listener, ATTR_NAME_NOT_FOUND); } } } for (Iterator iter = unmatchedTestAttrs.iterator(); iter.hasNext(); ) { Attr nextAttr = (Attr) iter.next(); if (isRecognizedXMLSchemaInstanceAttribute(nextAttr)) { compareRecognizedXMLSchemaInstanceAttribute(null, nextAttr, listener); } else { controlTracker.clearTrackedAttribute(); testTracker.clearTrackedAttribute(); testTracker.visited(nextAttr); compare(null, getQName(nextAttr), control, test, listener, ATTR_NAME_NOT_FOUND); } } controlTracker.clearTrackedAttribute(); testTracker.clearTrackedAttribute(); } private String getUnNamespacedNodeName(Node aNode) { return getUnNamespacedNodeName(aNode, isNamespaced(aNode)); } private String getUnNamespacedNodeName(Node aNode, boolean isNamespacedNode) { if (isNamespacedNode) { return aNode.getLocalName(); } return aNode.getNodeName(); } private String getQName(Node aNode) { return getQName(aNode, isNamespaced(aNode)); } private String getQName(Node aNode, boolean isNamespacedNode) { if (isNamespacedNode) { return "{" + aNode.getNamespaceURI() + "}" + aNode.getLocalName(); } return aNode.getNodeName(); } /** * @param attribute * @return true if the attribute represents a namespace declaration */ private boolean isXMLNSAttribute(Attr attribute) { return XMLConstants.XMLNS_PREFIX.equals(attribute.getPrefix()) || XMLConstants.XMLNS_PREFIX.equals(attribute.getName()); } /** * @param attr * @return true if the attribute is an XML Schema Instance * namespace attribute XMLUnit treats in a special way. */ private boolean isRecognizedXMLSchemaInstanceAttribute(Attr attr) { return XMLConstants .W3C_XML_SCHEMA_INSTANCE_NS_URI.equals(attr.getNamespaceURI()) && (XMLConstants .W3C_XML_SCHEMA_INSTANCE_SCHEMA_LOCATION_ATTR .equals(attr.getLocalName()) || XMLConstants .W3C_XML_SCHEMA_INSTANCE_NO_NAMESPACE_SCHEMA_LOCATION_ATTR .equals(attr.getLocalName())); } /** * Compare two attributes * @param control * @param test * @param listener * @throws DifferenceFoundException */ protected void compareRecognizedXMLSchemaInstanceAttribute(Attr control, Attr test, DifferenceListener listener) throws DifferenceFoundException { Attr nonNullNode = control != null ? control : test; Difference d = XMLConstants.W3C_XML_SCHEMA_INSTANCE_SCHEMA_LOCATION_ATTR .equals(nonNullNode.getLocalName()) ? SCHEMA_LOCATION : NO_NAMESPACE_SCHEMA_LOCATION; if (control != null) { controlTracker.visited(control); } if (test != null) { testTracker.visited(test); } compare(control != null ? control.getValue() : ATTRIBUTE_ABSENT, test != null ? test.getValue() : ATTRIBUTE_ABSENT, control, test, listener, d); } /** * @param attr * @return true if the attribute is an XML Schema Instance * namespace attribute XMLUnit treats in a special way. */ private boolean isXMLSchemaTypeAttribute(Attr attr) { return XMLConstants .W3C_XML_SCHEMA_INSTANCE_NS_URI.equals(attr.getNamespaceURI()) && XMLConstants.W3C_XML_SCHEMA_INSTANCE_TYPE_ATTR .equals(attr.getLocalName()); } /** * Compare two attributes * @param control * @param test * @param listener * @throws DifferenceFoundException */ protected void compareAttribute(Attr control, Attr test, DifferenceListener listener) throws DifferenceFoundException { controlTracker.visited(control); testTracker.visited(test); compare(control.getPrefix(), test.getPrefix(), control, test, listener, NAMESPACE_PREFIX); if (isXMLSchemaTypeAttribute(control) && isXMLSchemaTypeAttribute(test)) { compareXMLSchemaTypeAttributeValues(control, test, listener); } else { compare(control.getValue(), test.getValue(), control, test, listener, ATTR_VALUE); } compare(control.getSpecified() ? Boolean.TRUE : Boolean.FALSE, test.getSpecified() ? Boolean.TRUE : Boolean.FALSE, control, test, listener, ATTR_VALUE_EXPLICITLY_SPECIFIED); } private void compareXMLSchemaTypeAttributeValues(Attr control, Attr test, DifferenceListener listener) throws DifferenceFoundException { String controlValue = control.getValue(); String testValue = test.getValue(); String controlLocal = controlValue; String controlPrefix = ""; String testLocal = testValue; String testPrefix = ""; int controlColon = controlValue.indexOf(":"); if (controlColon >= 0 && controlColon < controlValue.length() - 1) { controlLocal = controlValue.substring(controlColon + 1); controlPrefix = controlValue.substring(0, controlColon); } int testColon = testValue.indexOf(":"); if (testColon >= 0 && testColon < testValue.length() - 1) { testLocal = testValue.substring(testColon + 1); testPrefix = testValue.substring(0, testColon); } compare("{" + findNamespaceURIForPrefix(control, controlPrefix) + "}" + controlLocal, "{" + findNamespaceURIForPrefix(test, testPrefix) + "}" + testLocal, control, test, listener, ATTR_VALUE); } /** * Compare two CDATA sections - unused, kept for backwards compatibility * @param control * @param test * @param listener * @throws DifferenceFoundException */ protected void compareCDataSection(CDATASection control, CDATASection test, DifferenceListener listener) throws DifferenceFoundException { compareText(control, test, listener); } /** * Compare two comments * @param control * @param test * @param listener * @throws DifferenceFoundException */ protected void compareComment(Comment control, Comment test, DifferenceListener listener) throws DifferenceFoundException { if (!XMLUnit.getIgnoreComments()) { compareCharacterData(control, test, listener, COMMENT_VALUE); } } /** * Compare two DocumentType nodes * @param control * @param test * @param listener * @throws DifferenceFoundException */ protected void compareDocumentType(DocumentType control, DocumentType test, DifferenceListener listener) throws DifferenceFoundException { compare(control.getName(), test.getName(), control, test, listener, DOCTYPE_NAME); compare(control.getPublicId(), test.getPublicId(), control, test, listener, DOCTYPE_PUBLIC_ID); compare(control.getSystemId(), test.getSystemId(), control, test, listener, DOCTYPE_SYSTEM_ID); } /** * Compare two processing instructions * @param control * @param test * @param listener * @throws DifferenceFoundException */ protected void compareProcessingInstruction(ProcessingInstruction control, ProcessingInstruction test, DifferenceListener listener) throws DifferenceFoundException { compare(control.getTarget(), test.getTarget(), control, test, listener, PROCESSING_INSTRUCTION_TARGET); compare(control.getData(), test.getData(), control, test, listener, PROCESSING_INSTRUCTION_DATA); } /** * Compare text - unused, kept for backwards compatibility * @param control * @param test * @param listener * @throws DifferenceFoundException */ protected void compareText(Text control, Text test, DifferenceListener listener) throws DifferenceFoundException { compareText((CharacterData) control, (CharacterData) test, listener); } /** * Compare text * @param control * @param test * @param listener * @throws DifferenceFoundException */ protected void compareText(CharacterData control, CharacterData test, DifferenceListener listener) throws DifferenceFoundException { compareCharacterData(control, test, listener, control instanceof CDATASection ? CDATA_VALUE : TEXT_VALUE); } /** * Character comparison method used by comments, text and CDATA sections * @param control * @param test * @param listener * @param differenceType * @throws DifferenceFoundException */ private void compareCharacterData(CharacterData control, CharacterData test, DifferenceListener listener, Difference difference) throws DifferenceFoundException { compare(control.getData(), test.getData(), control, test, listener, difference); } /** * If the expected and actual values are unequal then inform the listener of * a difference and throw a DifferenceFoundException. * @param expected * @param actual * @param control * @param test * @param listener * @param differenceType * @throws DifferenceFoundException */ protected void compare(Object expected, Object actual, Node control, Node test, DifferenceListener listener, Difference difference) throws DifferenceFoundException { compare(expected, actual, control, test, listener, difference, controlTracker, testTracker); } /** * If the expected and actual values are unequal then inform the listener of * a difference and throw a DifferenceFoundException. * @param expected * @param actual * @param control * @param test * @param listener * @param differenceType * @throws DifferenceFoundException */ protected void compare(Object expected, Object actual, Node control, Node test, DifferenceListener listener, Difference difference, XpathNodeTracker controlLoc, XpathNodeTracker testLoc) throws DifferenceFoundException { NodeDetail controlDetail = new NodeDetail(String.valueOf(expected), control, controlLoc == null ? null : controlLoc.toXpathString()); NodeDetail testDetail = new NodeDetail(String.valueOf(actual), test, testLoc == null ? null : testLoc.toXpathString()); Difference differenceInstance = new Difference(difference, controlDetail, testDetail); if (unequal(expected, actual)) { listener.differenceFound(differenceInstance); if (controller.haltComparison(differenceInstance)) { throw flowControlException; } } else if (matchTracker != null) { matchTracker.matchFound(differenceInstance); } } /** * Test two possibly null values for inequality * @param expected * @param actual * @return TRUE if the values are neither both null, nor equals() equal */ private boolean unequal(Object expected, Object actual) { return (expected==null ? actual!=null : unequalNotNull(expected, actual)); } /** * Test two non-null values for inequality * @param expected * @param actual * @return TRUE if the values are not equals() equal (taking whitespace * into account if necessary) */ private boolean unequalNotNull(Object expected, Object actual) { if ((XMLUnit.getIgnoreWhitespace() || XMLUnit.getNormalizeWhitespace()) && expected instanceof String && actual instanceof String) { String expectedString = ((String) expected).trim(); String actualString = ((String) actual).trim(); if (XMLUnit.getNormalizeWhitespace()) { expectedString = normalizeWhitespace(expectedString); actualString = normalizeWhitespace(actualString); } return !expectedString.equals(actualString); } return !(expected.equals(actual)); } /** * Replace all whitespace characters with SPACE and collapse * consecutive whitespace chars to a single SPACE. */ final static String normalizeWhitespace(String orig) { StringBuffer sb = new StringBuffer(); boolean lastCharWasWhitespace = false; boolean changed = false; char[] characters = orig.toCharArray(); for (int i = 0; i < characters.length; i++) { if (Character.isWhitespace(characters[i])) { if (lastCharWasWhitespace) { // suppress character changed = true; } else { sb.append(' '); changed |= characters[i] != ' '; lastCharWasWhitespace = true; } } else { sb.append(characters[i]); lastCharWasWhitespace = false; } } return changed ? sb.toString() : orig; } /** * Try to find the namespace URI that is mapped to the given * prefix at the given node. */ private String findNamespaceURIForPrefix(Node onNode, String prefix) { if (onNode != null && onNode instanceof Attr) { onNode = ((Attr) onNode).getOwnerElement(); } while (onNode != null && onNode.getNodeType() != Node.ELEMENT_NODE) { onNode = onNode.getParentNode(); } if (onNode == null) { return null; } NamedNodeMap attrs = onNode.getAttributes(); Attr attr = null; if (prefix == null || "".equals(prefix)) { attr = (Attr) attrs.getNamedItem(XMLConstants.XMLNS_PREFIX); } else { attr = (Attr) attrs.getNamedItemNS(XMLConstants.XMLNS_ATTRIBUTE_URI, prefix); } if (attr != null) { return attr.getValue(); } return findNamespaceURIForPrefix(onNode.getParentNode(), prefix); } /** * Marker exception thrown by the protected compare() method and passed * upwards through the call stack to the public compare() method. */ protected static final class DifferenceFoundException extends Exception { private DifferenceFoundException() { super("This exception is used to control flow"); } } /** * Exception instance used internally to control flow * when a difference is found */ private static final DifferenceFoundException flowControlException = new DifferenceFoundException(); } xmlunit-1.6/src/java/org/custommonkey/xmlunit/SimpleXpathEngine.java0000644000000000000000000002471512451007364024554 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2008, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ package org.custommonkey.xmlunit; import org.custommonkey.xmlunit.exceptions.ConfigurationException; import org.custommonkey.xmlunit.exceptions.XpathException; import java.io.StringReader; import java.io.StringWriter; import java.util.Iterator; import javax.xml.transform.ErrorListener; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.Result; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * Simple class for accessing the Nodes matched by an Xpath expression, or * evaluating the String value of an Xpath expression. * Uses a copy-of or value-of XSL template (as * appropriate) to execute the Xpath. * This is not an efficient method for accessing XPaths but it is portable * across underlying transform implementations. (Yes I know Jaxen is too, but * this approach seemed to be the simplest thing that could possibly work...) *
      Examples and more at xmlunit.sourceforge.net */ public class SimpleXpathEngine implements XpathEngine, XSLTConstants { private NamespaceContext ctx = SimpleNamespaceContext.EMPTY_CONTEXT; /** * What every XSL transform needs * @return */ private StringBuffer getXSLTBase() { StringBuffer result = new StringBuffer(XML_DECLARATION) .append(XMLUnit.getXSLTStart()); String tmp = result.toString(); int close = tmp.lastIndexOf('>'); if (close == -1) { close = tmp.length(); } result.insert(close, getNamespaceDeclarations()); return result; } /** * @param select an xpath syntax select expression * @return the copy-of transformation */ private String getCopyTransformation(String select) { return getXSLTBase() .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append("") .append(" ") .append("") .append("") .toString(); } /** * @param select an xpath syntax select expression * @return the value-of transformation */ private String getValueTransformation(String select) { return getXSLTBase() .append("") .append("") .append(" ") .append("") .append("") .toString(); } /** * Perform the actual transformation work required * @param xslt * @param document * @param result * @throws XpathException * @throws TransformerException * @throws ConfigurationException */ private void performTransform(String xslt, Document document, Result result) throws TransformerException, ConfigurationException, XpathException { try { StreamSource source = new StreamSource(new StringReader(xslt)); TransformerFactory tf = XMLUnit.newTransformerFactory(); ErrorListener el = new ErrorListener() { public void error(TransformerException ex) throws TransformerException { // any error in our simple stylesheet must be fatal throw ex; } public void fatalError(TransformerException ex) throws TransformerException { throw ex; } public void warning(TransformerException ex) { // there shouldn't be any warning ex.printStackTrace(); } }; tf.setErrorListener(el); Transformer transformer = tf.newTransformer(source); // Issue 1985229 says Xalan-J 2.7.0 may return null for // illegal input if (transformer == null) { throw new XpathException("failed to obtain an XSLT transformer" + " for XPath expression."); } transformer.setErrorListener(el); transformer.transform(new DOMSource(document), result); } catch (javax.xml.transform.TransformerConfigurationException ex) { throw new ConfigurationException(ex); } } /** * Testable method to execute the copy-of transform and return the root * node of the resulting Document. * @param select * @param document * @throws ConfigurationException * @throws TransformerException * @return the root node of the Document created by the copy-of transform. */ protected Node getXPathResultNode(String select, Document document) throws ConfigurationException, TransformerException, XpathException { return getXPathResultAsDocument(select, document).getDocumentElement(); } /** * Execute the copy-of transform and return the resulting Document. * Used for XMLTestCase comparison * @param select * @param document * @throws ConfigurationException * @throws TransformerException * @return the Document created by the copy-of transform. */ protected Document getXPathResultAsDocument(String select, Document document) throws ConfigurationException, TransformerException, XpathException { DOMResult result = new DOMResult(); performTransform(getCopyTransformation(select), document, result); return (Document) result.getNode(); } /** * Execute the specified xpath syntax select expression * on the specified document and return the list of nodes (could have * length zero) that match * @param select * @param document * @return list of matching nodes */ public NodeList getMatchingNodes(String select, Document document) throws ConfigurationException, XpathException { try { return getXPathResultNode(select, document).getChildNodes(); } catch (TransformerException ex) { throw new XpathException("Failed to apply stylesheet", ex); } } /** * Evaluate the result of executing the specified xpath syntax * select expression on the specified document * @param select * @param document * @return evaluated result */ public String evaluate(String select, Document document) throws ConfigurationException, XpathException { try { StringWriter writer = new StringWriter(); StreamResult result = new StreamResult(writer); performTransform(getValueTransformation(select), document, result); return writer.toString(); } catch (TransformerException ex) { throw new XpathException("Failed to apply stylesheet", ex); } } public void setNamespaceContext(NamespaceContext ctx) { this.ctx = ctx; } /** * returns namespace declarations for all namespaces known to the * current context. */ private String getNamespaceDeclarations() { StringBuffer nsDecls = new StringBuffer(); String quoteStyle = "'"; for (Iterator keys = ctx.getPrefixes(); keys.hasNext(); ) { String prefix = (String) keys.next(); String uri = ctx.getNamespaceURI(prefix); if (uri == null) { continue; } // this shouldn't have happened, but better safe than sorry if (prefix == null) { prefix = ""; } if (uri.indexOf('\'') != -1) { quoteStyle = "\""; } nsDecls.append(' ').append(XMLNS_PREFIX); if (prefix.length() > 0) { nsDecls.append(':'); } nsDecls.append(prefix).append('=') .append(quoteStyle).append(uri).append(quoteStyle) .append(' '); } return nsDecls.toString(); } } xmlunit-1.6/src/java/overview.html0000644000000000000000000001035712451007364016032 0ustar rootroot XMLUnit XMLUnit provides extensions to the JUnit framework to allow assertions to be made about XML content.

      Using XMLUnit

      1. Create a subclass of {@link org.custommonkey.xmlunit.XMLTestCase XMLTestCase} something like this:
        public class TestSomething extends XMLTestcase {
            // standard JUnit style constructor
            public TestSomething(String name) {
                super(name);
            }
            // standard JUnit style method
            public static TestSuite suite() {
                return new TestSuite(TestSomething.class);
            }
        }
        
      2. Set the global JAXP settings in {@link org.custommonkey.xmlunit.XMLUnit XMLUnit} so that your chosen parser and transformer are used for the tests.
        Note:You can skip this bit if you use the default JAXP settings or you have an Ant task that uses -D JVM options to specify the JAXP settings.
            // set the JAXP factories to use the Xerces parser
            // - declare to throw Exception as if this fails then all the tests will
            // fail, and JUnit copes with these Exceptions for us
            public void setUp() throws Exception {
                XMLUnit.setControlParser(
                    "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
                // this next line is strictly not required - if no test parser is
                // explicitly specified then the same factory class will be used for
                // both test and control
                XMLUnit.setTestParser(
                    "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
        
                XMLUnit.setSAXParserFactory(
                    "org.apache.xerces.jaxp.SAXParserFactoryImpl");
                XMLUnit.setTransformerFactory(
                    "org.apache.xalan.processor.TransformerFactoryImpl");
            }
        
      3. Add test methods to make your assertions: the {@link org.custommonkey.xmlunit.XMLTestCase XMLTestCase} javadoc lists the available assertion methods and their usage, but here are some examples...
            public void testObjectAsXML() throws Exception {
                String expectedXML = "....";
                String objectAsXML = null;
                //...set up some object here and serialize its state into
                //our test String...
                assertXMLEqual(expectedXML, objectAsXML);
            }
        
            public void testTransformToFormatB() throws Exception {
                String expectedFormatB = "....";
                String formatA = "....";
                String transformXSLT = "....";
                Transform formatAToFormatB = new Transform(formatA, transformXSLT);
                assertXMLEqual(new Diff(expectedFormatB, formatAToFormatB), true);
            }
        
            public void testIsValidAfterTransform() throws Exception {
                String incomingMessage = "....";
                String toSourceSystemXSLT = "....";
                Transform transform = new Transform(incomingMessage, toSourceSystemXSLT);
                assertXMLValid(transform.getResultString());
            }
        
            public void testXpaths() throws Exception {
                String ukCustomerContactPhoneNos = "//customer[@country='UK']/contact/phone";
                String customerExtract1 = "....";
                String customerExtract2 = "....";
                assertXpathsNotEqual(ukCustomerContactPhoneNos, customerExtract1,
                    ukCustomerContactPhoneNos, customerExtract2);
            }
        
            public void testXpathValues() throws Exception {
                String firstListItem = "/html/body/div[@id='myList']/h1/ol/li[1]";
                String secondListItem = "/html/body/div[@id='myList']/h1/ol/li[2]";
                String myHtmlPage = "....";
                assertXpathValuesNotEqual(firstListItem, secondListItem, myHtmlPage);
            }
        
            public void testSpecificXpath() throws Exception {
                String todaysTop10 = "count(//single[@topTen='true'])";
                String playlist = "....";
                assertXpathEvaluatesTo("10", todaysTop10, playlist);
        
            }
        

      A little bit of history

      XMLUnit is the result of the efforts of Tim Bacon and Jeff Martin. We needed a tool to test the serialization and de-serialiation of objects into XML for an application that sent and received XML messages over HTTP, and this project grew out of that. We hope you find it useful, and welcome any feedback you can give us.

      xmlunit-1.6/src/etc/0000755000000000000000000000000012451007364013122 5ustar rootrootxmlunit-1.6/src/etc/xmlunit.pom0000644000000000000000000000537012451007364015344 0ustar rootroot 4.0.0 @GROUP@ @ARTIFACT@ @TYPE@ XMLUnit for Java @VERSION@ http://www.xmlunit.org/ @DESCRIPTION@ @LICENSE@ @LICENSE_URL@ https://sourceforge.net/p/xmlunit/code/HEAD/tree/trunk/ scm:svn:https://svn.code.sf.net/p/xmlunit/code/trunk junit junit 3.8.2 true bodewig Stefan Bodewig stefan.bodewig@freenet dot de xmlunit-1.6/src/etc/xmlunit.spec0000644000000000000000000000324112451007364015476 0ustar rootroot%define name xmlunit %define version 1.0 %define release 1 %define javadir %{_datadir}/java %define javadocdir %{_datadir}/javadoc %define section free Name: %{name} Version: %{version} Release: %{release} Summary: XML testing package License: BSD Style Software License URL: http://xmlunit.sf.net/ Group: Development/Testing Vendor: XMLUnit Project Distribution: XMLUnit Source0: http://xmlunit.sf.net/dist/%{name}-%{version}-src.tgz Provides: xmlunit Requires: junit Requires: /usr/sbin/update-alternatives BuildRequires: ant >= 1.5 BuildArch: noarch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot %description XMLUnit extends JUnit to simplify unit testing of XML. It compares a control XML document to a test document or the result of a transformation, validates documents against a DTD, and (from v0.5) compares the results of XPath expressions. %prep %setup -q -n xmlunit-%{version} #%patch0 -p0 #%patch1 -p2 %build # build with empty CLASSPATH ant -buildfile build.xml -Dbuild.compiler=modern jar docs %install rm -rf $RPM_BUILD_ROOT # jars mkdir -p $RPM_BUILD_ROOT%{javadir} cp -p lib/%{name}-%{version}.jar $RPM_BUILD_ROOT%{javadir}/%{name}-%{version}.jar (cd $RPM_BUILD_ROOT%{javadir} && for jar in *-%{version}.jar; do ln -sf ${jar} `echo $jar| sed "s|-%{version}||g"`; done) # docs #mkdir -p $RPM_BUILD_ROOT%{javadocdir}/%{name}-%{version} #cp -pr doc/* \ #$RPM_BUILD_ROOT%{javadocdir}/%{name}-%{version} %clean rm -rf $RPM_BUILD_ROOT %post %postun %files %defattr(0644,root,root,0755) %doc LICENSE.txt README.txt ISSUES %{javadir}/* #%{javadocdir}/* %changelog * Wed Mar 05 2003 Jeff Martin 1.0.0-1jpp - first release xmlunit-1.6/src/etc/xmlunit-ivy.xml0000644000000000000000000000365212451007364016157 0ustar rootroot @DESCRIPTION@ xmlunit-1.6/LICENSE.txt0000644000000000000000000000323712451007364013410 0ustar rootroot/* ****************************************************************** Copyright (c) 2001-2014, Jeff Martin, Tim Bacon All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the xmlunit.sourceforge.net nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************** */ xmlunit-1.6/build.xml0000644000000000000000000002641112451007502013377 0ustar rootroot Some tests failed