Bug 1046500 - Make FeedParser tests into xpcshell tests. r=mak, a=test-only
authorWes Johnston <wjohnston@mozilla.com>
Mon, 11 Aug 2014 12:35:07 -0700
changeset 217457 c086369709c627d3ce9fd713eec024b7776a967c
parent 217456 81d788a4bf74c3705e54bba090d95d60c3963cfe
child 217458 f170b174bc252509c99f597cac9baa0f535ab36c
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak, test-only
bugs1046500
milestone33.0a2
Bug 1046500 - Make FeedParser tests into xpcshell tests. r=mak, a=test-only
toolkit/components/feeds/Makefile.in
toolkit/components/feeds/moz.build
toolkit/components/feeds/test/head.js
toolkit/components/feeds/test/shell.js
toolkit/components/feeds/test/test.js
toolkit/components/feeds/test/test_xml.js
toolkit/components/feeds/test/xml/rfc4287/entry_xhtml_baseURI_with_amp.xml
toolkit/components/feeds/test/xml/rss2/mrss_content.xml
toolkit/components/feeds/test/xml/rss2/mrss_content2.xml
toolkit/components/feeds/test/xml/rss2/mrss_group_content.xml
toolkit/components/feeds/test/xpcshell.ini
--- a/toolkit/components/feeds/Makefile.in
+++ b/toolkit/components/feeds/Makefile.in
@@ -1,19 +1,8 @@
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 include $(topsrcdir)/config/rules.mk
 
 ABS_SRCDIR := $(abspath $(srcdir))
-ifeq ($(OS_ARCH),WINNT)
-
-check::
-	cd $(srcdir)/test; $(LIBXUL_DIST)/bin/xpcshell$(BIN_SUFFIX) shell.js 
-
-else
-
-check::
-	$(RUN_TEST_PROGRAM) $(LIBXUL_DIST)/bin/xpcshell$(BIN_SUFFIX) $(srcdir)/test/shell.js $(ABS_SRCDIR)/test
-
-endif # WINNT
--- a/toolkit/components/feeds/moz.build
+++ b/toolkit/components/feeds/moz.build
@@ -20,8 +20,12 @@ XPIDL_SOURCES += [
 ]
 
 XPIDL_MODULE = 'feeds'
 
 EXTRA_COMPONENTS += [
     'FeedProcessor.js',
     'FeedProcessor.manifest',
 ]
+
+XPCSHELL_TESTS_MANIFESTS += [
+    'test/xpcshell.ini'
+]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/feeds/test/head.js
@@ -0,0 +1,80 @@
+"use strict";
+
+let Cc = Components.classes;
+let Ci = Components.interfaces;
+let Cu = Components.utils;
+let Cr = Components.results;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+
+function readTestData(testFile) {
+  var testcase = {};
+
+  // Got a feed file, now we need to parse out the Description and Expect headers.
+  var istream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
+  try {
+    istream.init(testFile, 0x01, parseInt("0444", 8), 0);
+    istream.QueryInterface(Ci.nsILineInputStream);
+
+    var hasmore = false;
+    do {
+      var line = {};
+      hasmore = istream.readLine(line);
+
+      if (line.value.indexOf('Description:') > -1) {
+        testcase.desc = line.value.substring(line.value.indexOf(':')+1).trim();
+      }
+
+      if (line.value.indexOf('Expect:') > -1) {
+        testcase.expect = line.value.substring(line.value.indexOf(':')+1).trim();
+      }
+
+      if (line.value.indexOf('Base:') > -1) {
+        testcase.base = NetUtil.newURI(line.value.substring(line.value.indexOf(':')+1).trim());
+      }
+
+      if (testcase.expect && testcase.desc) {
+        testcase.path = 'xml/' + testFile.parent.leafName + '/' + testFile.leafName;
+        testcase.file = testFile;
+        break;
+      }
+
+    } while (hasmore);
+
+  } catch(e) {
+    Assert.ok(false, "FAILED! Error reading testFile case in file " + testFile.leafName  + " ---- " + e);
+  } finally {
+    istream.close();
+  }
+
+  return testcase;
+}
+
+function iterateDir(dir, recurse, callback) {
+  do_print("Iterate " + dir.leafName);
+  let entries = dir.directoryEntries;
+
+  // Loop over everything in this dir. If its a dir
+  while (entries.hasMoreElements()) {
+    let entry = entries.getNext();
+    entry.QueryInterface(Ci.nsILocalFile);
+
+    if (entry.isDirectory()) {
+      if (recurse) {
+        iterateDir(entry, recurse, callback);
+      }
+    } else {
+      callback(entry);
+    }
+  }
+}
+
+function isIID(a, iid){
+  try {
+    a.QueryInterface(iid);
+    return true;
+  } catch(e) { }
+
+  return false;
+}
deleted file mode 100644
--- a/toolkit/components/feeds/test/test.js
+++ /dev/null
@@ -1,88 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var passed = 0;
-var ran = 0;
-if(tests.length < 1){
-  dump("FAILED! tests.length was " + tests.length  + "\n");
-}
-
-function logResult(didPass, testcase, extra) {
-  var start = didPass ? "PASS | " : "FAIL | ";
-  print(start + testcase.path + " | Test was: \"" +
-        testcase.desc + "\" | " + testcase.expect + " |");
-  if (extra) {
-    print(extra);
-  }
-}
-
-
-function isIID(a,iid){
-  var rv = false;
-  try{
-    a.QueryInterface(iid);
-    rv = true;
-  }catch(e){}
-  return rv;
-}
-function TestListener(){}
-
-TestListener.prototype = {
-  handleResult: function(result){
-    var feed = result.doc;    
-    // QI to something
-    (isIID(feed, Components.interfaces.nsIFeed));
-    try {
-      if(!eval(testcase.expect)){
-        logResult(false, testcase);
-  
-      }else{
-        logResult(true, testcase);
-        passed += 1;
-      }
-    }
-    catch(e) {
-      logResult(false, testcase, "ex: " + e.message);
-    }
-
-    ran += 1;
-    result = null;
-  }
-}
-
-var startDate = new Date();
-
-for(var i=0; i<tests.length; i++){
-  var testcase = tests[i];
-  var uri;
-  if (testcase.base == null)
-    uri = ioService.newURI('http://example.org/'+testcase.path, null,null);
-  else
-    uri = testcase.base
-
-  var parser = Components.classes["@mozilla.org/feed-processor;1"]
-                         .createInstance(Components.interfaces.nsIFeedProcessor);
-  var stream = Components.classes["@mozilla.org/network/file-input-stream;1"]
-                         .createInstance(Components.interfaces.nsIFileInputStream);
-  var listener = new TestListener();
-  try{
-    stream.init(testcase.file, 0x01, 0444, 0);
-    parser.listener = listener;
-    parser.parseFromStream(stream, uri);
-  }catch(e){
-    dump("FAILED! Error parsing file " + testcase.file.leafName  + 
-         "Error: " + e.message + "\n");
-  }finally{
-    stream.close();
-  }
-
-}
-var endDate = new Date();
-
-print("Start: " + startDate);
-print("End  : " + endDate);
-print("tests:  " + tests.length);
-print("ran:    " + ran);
-print("passed: " + passed);
rename from toolkit/components/feeds/test/shell.js
rename to toolkit/components/feeds/test/test_xml.js
--- a/toolkit/components/feeds/test/shell.js
+++ b/toolkit/components/feeds/test/test_xml.js
@@ -1,129 +1,97 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-// shell.js
-// This file sets up the unit test environment by building an array of files
-// to be tested. It assumes it lives in a folder adjacent to the a folder
-// called 'xml', where the testcases live.
-//
-// The directory layout looks something like this:
-//
-// tests/ *shell.js*
-//       |                  
-//       - test.js
-//       |
-//       - xml/ -- rss1/... 
-//              |
-//              -- rss2/...
-//              |
-//              -- atom/testcase.xml
-
-function trimString(s)
-{
-  return(s.replace(/^\s+/,'').replace(/\s+$/,''));
-}
-
-var tests = new Array();
-const ioService = Components.classes['@mozilla.org/network/io-service;1'].getService(Components.interfaces.nsIIOService);
+/* test_xml.js
+ * This file sets up the unit test environment by building an array of files
+ * to be tested. It assumes it lives in a folder adjacent to the a folder
+ * called 'xml', where the testcases live.
+ *
+ * The directory layout looks something like this:
+ *
+ * tests/test_xml.js*
+ *      |
+ *      - head.js
+ *      |
+ *      - xml/ -- rss1/... 
+ *             |
+ *             -- rss2/...
+ *             |
+ *             -- atom/testcase.xml
+ *
+ * To add more tests, just include the file in the xml subfolder and add its name to xpcshell.ini
+ */
 
-// find the directory containing our test XML
-
-if (0 < arguments.length) {
+"use strict";
 
-  // dir is specified on the command line
-  
-  var topDir = Components.classes["@mozilla.org/file/local;1"]
-    .createInstance(Components.interfaces.nsILocalFile);
-  
-  topDir.initWithPath(arguments[0]);
-}
-else {
-  const nsIDirectoryServiceProvider
-    = Components.interfaces.nsIDirectoryServiceProvider;
-  const nsIDirectoryServiceProvider_CONTRACTID
-    = "@mozilla.org/file/directory_service;1";
-
-  var dirServiceProvider 
-    = Components.classes[nsIDirectoryServiceProvider_CONTRACTID]
-    .getService(nsIDirectoryServiceProvider);
-
-  var persistent = new Object();
-
-  var topDir = dirServiceProvider.getFile("CurWorkD", persistent);
+// Listens to feeds being loaded. Runs the tests built into the feed afterwards to veryify they
+// were parsed correctly.
+function FeedListener(testcase) {
+  this.testcase = testcase;
 }
 
-var entries = topDir.directoryEntries;
-var xmlDir;
-while(entries.hasMoreElements()){
-  xmlDir = entries.getNext();
-  xmlDir.QueryInterface(Components.interfaces.nsILocalFile);
-  if(xmlDir.leafName == "xml") 
-    break;
-  else
-    xmlDir = null;
+FeedListener.prototype = {
+  handleResult: function(result) {
+    var feed = result.doc;
+    try {
+      do_print(true, "Testing feed " + this.testcase.file.path);
+      Assert.ok(isIID(feed, Ci.nsIFeed), "Has feed interface");
+
+      if (!eval(this.testcase.expect)) {
+        Assert.ok(false, "expect failed for " + this.testcase.desc);
+      } else {
+        Assert.ok(true, "expect passed for " + this.testcase.desc);
+      }
+    } catch(e) {
+      Assert.ok(false, "expect failed for " + this.testcase.desc + " ---- " + e.message);
+    }
+
+    run_next_test();
+  }
 }
 
-// if we got our xmldir, find our testcases
-var testDir;
-if(xmlDir){
-  entries = xmlDir.directoryEntries;
-  while(entries.hasMoreElements()){
-    testDir = entries.getNext();
-    testDir.QueryInterface(Components.interfaces.nsILocalFile);
-    if(testDir.exists() && testDir.isDirectory()){
-      var testfiles = testDir.directoryEntries;
-      while(testfiles.hasMoreElements()){ // inside an individual testdir, like "rss2"
-        var test = testfiles.getNext();
-        test.QueryInterface(Components.interfaces.nsILocalFile);
-        if(test.exists() && test.isFile()){
-          
-          // Got a feed file, now we need to add it to our tests
-          // and parse out the Description and Expect headers.
-          // That lets us write one test.js file and keep the
-          // tests with the XML. Convenient method learned from
-          // Mark Pilgrim.
+function createTest(data) {
+  return function() {
+    var uri;
 
-          var istream = Components.classes["@mozilla.org/network/file-input-stream;1"]
-                                  .createInstance(Components.interfaces.nsIFileInputStream);
-          
-          try{
-            // open an input stream from file
-            istream.init(test, 0x01, 0444, 0);
-            istream.QueryInterface(Components.interfaces.nsILineInputStream);
-            var line = {}, hasmore, testcase = {}, expectIndex, descIndex;
+    if (data.base == null) {
+      uri = NetUtil.newURI('http://example.org/' + data.path);
+    } else {
+      uri = data.base;
+    }
+
+    do_print("Testing " + data.file.leafName);
 
-            do {
-              hasmore = istream.readLine(line);
-              expectIndex = line.value.indexOf('Expect:');
-              descIndex = line.value.indexOf('Description:');
-              baseIndex = line.value.indexOf('Base:');
-              if(descIndex > -1)
-                testcase.desc = trimString(line.value.substring(line.value.indexOf(':')+1));
-              if(expectIndex > -1)
-                testcase.expect = trimString(line.value.substring(line.value.indexOf(':')+1));
-              if(baseIndex > -1)
-                testcase.base = ioService.newURI(trimString(line.value.substring(line.value.indexOf(':')+1)),null,null);
-              if(testcase.expect && testcase.desc){
-                // got our fields, let's add it to our array and break
-                testcase.path = xmlDir.leafName + '/' + testDir.leafName + '/' + test.leafName;
-                testcase.file = test;
-                tests.push(testcase);
-                break; // out of do-while
-              }
-              
-            } while(hasmore);
+    var parser = Cc["@mozilla.org/feed-processor;1"].createInstance(Ci.nsIFeedProcessor);
+    var stream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
+    stream.init(data.file, 0x01, parseInt("0444", 8), 0);
+    parser.listener = new FeedListener(data);
 
-          }catch(e){
-            dump("FAILED! Error reading test case in file " + test.leafName  + " " + e + "\n"); 
-          }finally{
-            istream.close();
-          }
-        }
-      }
+    try {
+      parser.parseFromStream(stream, uri);
+    } catch(e) {
+      Assert.ok(false, "parse failed for " + data.file.leafName + " ---- " + e.message);
+      // If the parser failed, the listener won't be notified, run the next test here.
+      run_next_test();
+    } finally {
+      stream.close();
     }
   }
 }
 
-load(topDir.path+'/test.js');
+function run_test() {
+  // Get the 'xml' directory in here
+  var topDir = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+  topDir.append("xml");
+
+  // Every file in the test dir contains an encapulated RSS "test". Iterate through
+  // them all and add them to the test runner.
+  iterateDir(topDir, true, file => {
+    var data = readTestData(file);
+    add_test(createTest(data));
+  });
+
+  // Now run!
+  run_next_test();
+}
--- a/toolkit/components/feeds/test/xml/rfc4287/entry_xhtml_baseURI_with_amp.xml
+++ b/toolkit/components/feeds/test/xml/rfc4287/entry_xhtml_baseURI_with_amp.xml
@@ -1,14 +1,14 @@
 <?xml version="1.0" encoding="iso-8859-1"?>
 <!--
 
 Description: atom entry content xhtml works with a base URI that contains an ampersand.
 Base: http://www.travellerspoint.com/photo_gallery_feed.cfm?tags=Canada&onlyShowFeatured=true
-Expect: var frag = null; var content = feed.items.queryElementAt(0, Components.interfaces.nsIFeedEntry).content; var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"].createInstance(Components.interfaces.nsIDOMParser); doc = parser.parseFromString("<div/>", "text/xml"); frag = content.createDocumentFragment(doc.documentElement); (frag != null);
+Expect: var frag = null; var content = feed.items.queryElementAt(0, Components.interfaces.nsIFeedEntry).content; var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"].createInstance(Components.interfaces.nsIDOMParser); var doc = parser.parseFromString("<div/>", "text/xml"); frag = content.createDocumentFragment(doc.documentElement); notEqual(frag, null, "frag is not null"); true;
 
 -->
 <feed xmlns="http://www.w3.org/2005/Atom"
       xmlns:foo="http://www.example.org"
       foo:quux="quuux">
 
   <title>hmm</title>
 
--- a/toolkit/components/feeds/test/xml/rss2/mrss_content.xml
+++ b/toolkit/components/feeds/test/xml/rss2/mrss_content.xml
@@ -1,21 +1,20 @@
 <?xml version="1.0" encoding="iso-8859-1"?>
 <!--
 
 Description: mrss content works
-Expect: var enc = feed.items.queryElementAt(0, Components.interfaces.nsIFeedEntry).fields.getPropertyAsInterface('mediacontent', Components.interfaces.nsIArray).queryElementAt(0, Components.interfaces.nsIPropertyBag); ((enc.getProperty('fileSize') == '24986239') && (enc.getProperty('type') == 'video/mpeg') && (enc.getProperty('url') == 'http://dallas.example.com/joebob_050689.mpeg') && (feed.type == 4) && (feed.enclosureCount == 1));
+Expect: var enc = feed.items.queryElementAt(0, Components.interfaces.nsIFeedEntry).fields.getPropertyAsInterface('mediacontent', Components.interfaces.nsIArray).queryElementAt(0, Components.interfaces.nsIPropertyBag); Assert.equal(enc.getProperty('fileSize'), '24986239', 'file size is correct'); Assert.equal(enc.getProperty('type'), 'video/mpeg', 'type is correct'); Assert.equal(enc.getProperty('url'), 'http://dallas.example.com/joebob_050689.mpeg', 'url is correct'); Assert.equal(feed.type, 4, 'Feed type is correct'); Assert.equal(feed.enclosureCount,1, 'Enclosure count is correct'); true;
 
 -->
 <rss xmlns:media="http://search.yahoo.com/mrss" version="2.0" >
 <channel>
 <item>
 
 <media:content fileSize="24986239" type="video/mpeg" url="http://dallas.example.com/joebob_050689.mpeg" />
-<media:thumbnail url="http://dallas.example.com/joebob_050689.jpg"  width="75" height="50"/>
 <author>jbb@dallas.example.com (Joe Bob Briggs)</author>
 <comments>http://example.org</comments>
 <title>test</title>
 
 <category domain="foo">bar</category>
 
 <description>I'm headed for France. I wasn't gonna go this year, but then last week &lt;a href="http://www.imdb.com/title/tt0086525/"&gt;Valley Girl&lt;/a&gt; came out and I said to myself, Joe Bob, you gotta get out of the country for a while.</description></item>
 </channel>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/feeds/test/xml/rss2/mrss_content2.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!--
+
+Description: mrss content with a thumbnail
+Expect: var enc = feed.items.queryElementAt(0, Components.interfaces.nsIFeedEntry).fields.getPropertyAsInterface('mediacontent', Components.interfaces.nsIArray).queryElementAt(0, Components.interfaces.nsIPropertyBag); Assert.equal(enc.getProperty('fileSize'), '24986239', 'file size is correct'); Assert.equal(enc.getProperty('type'), 'video/mpeg', 'type is correct'); Assert.equal(enc.getProperty('url'), 'http://dallas.example.com/joebob_050689.mpeg', 'url is correct'); Assert.equal(feed.type, 0, 'Feed type is correct'); Assert.equal(feed.enclosureCount,2, 'Enclosure count is correct'); true;
+
+-->
+<rss xmlns:media="http://search.yahoo.com/mrss" version="2.0" >
+<channel>
+<item>
+
+<media:content fileSize="24986239" type="video/mpeg" url="http://dallas.example.com/joebob_050689.mpeg" />
+<media:thumbnail url="http://dallas.example.com/joebob_050689.jpg"  width="75" height="50"/>
+<author>jbb@dallas.example.com (Joe Bob Briggs)</author>
+<comments>http://example.org</comments>
+<title>test</title>
+
+<category domain="foo">bar</category>
+
+<description>I'm headed for France. I wasn't gonna go this year, but then last week &lt;a href="http://www.imdb.com/title/tt0086525/"&gt;Valley Girl&lt;/a&gt; came out and I said to myself, Joe Bob, you gotta get out of the country for a while.</description></item>
+</channel>
+</rss>
--- a/toolkit/components/feeds/test/xml/rss2/mrss_group_content.xml
+++ b/toolkit/components/feeds/test/xml/rss2/mrss_group_content.xml
@@ -1,15 +1,14 @@
 <?xml version="1.0" encoding="iso-8859-1"?>
 <!--
 
 Description: mrss group content works
 Expect: var mg = feed.items.queryElementAt(0, Components.interfaces.nsIFeedEntry).fields.getProperty('mediagroup'); mg.QueryInterface(Components.interfaces.nsIPropertyBag); var mcs = mg.getProperty("mediacontent"); mcs.QueryInterface(Components.interfaces.nsIArray); var mc1 = mcs.queryElementAt(0, Components.interfaces.nsIPropertyBag); var mc2 = mcs.queryElementAt(1, Components.interfaces.nsIPropertyBag); ((mc1.getProperty('fileSize') == '400') && (mc1.getProperty('type') == 'audio/mpeg') && (mc1.getProperty('url') == 'http://dallas.example.com/joebob_050689_2.mp3') && (mc2.getProperty('fileSize') == '200') && (mc2.getProperty('type') == 'audio/mpeg') && (mc2.getProperty('url') == 'http://dallas.example.com/joebob_050689_1.mp3'));
 
-
 -->
 <rss xmlns:media="http://search.yahoo.com/mrss" version="2.0" >
 <channel>
 <item>
 
 <media:group>
   <media:content fileSize="400" type="audio/mpeg" url="http://dallas.example.com/joebob_050689_2.mp3" /> 
   <media:content fileSize="200" type="audio/mpeg" url="http://dallas.example.com/joebob_050689_1.mp3" /> 
@@ -17,10 +16,11 @@ Expect: var mg = feed.items.queryElement
 
 <author>jbb@dallas.example.com (Joe Bob Briggs)</author>
 <comments>http://example.org</comments>
 <title>test</title>
 
 <category domain="foo">bar</category>
 
 <description>I'm headed for France. I wasn't gonna go this year, but then last week &lt;a href="http://www.imdb.com/title/tt0086525/"&gt;Valley Girl&lt;/a&gt; came out and I said to myself, Joe Bob, you gotta get out of the country for a while.</description></item>
+
 </channel>
 </rss>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/feeds/test/xpcshell.ini
@@ -0,0 +1,209 @@
+[DEFAULT]
+head = head.js
+tail =
+firefox-appdir = browser
+support-files =
+    xml/rfc4287/author_namespaces.xml
+    xml/rfc4287/entry_link_IANA.xml
+    xml/rfc4287/feed_author_email_2.xml
+    xml/rfc4287/feed_logo.xml
+    xml/rfc4287/entry_author.xml
+    xml/rfc4287/entry_link_otherURI_alt.xml
+    xml/rfc4287/feed_author_email.xml
+    xml/rfc4287/feed_random_attributes_on_feed_and_entry.xml
+    xml/rfc4287/entry_content_encoded.xml
+    xml/rfc4287/entry_link_payment_alt.xml
+    xml/rfc4287/feed_author_name.xml
+    xml/rfc4287/feed_rights_normalized.xml
+    xml/rfc4287/entry_content_html.xml
+    xml/rfc4287/entry_link_random.xml
+    xml/rfc4287/feed_author_surrounded.xml
+    xml/rfc4287/feed_rights_xhtml_nested_divs.xml
+    xml/rfc4287/entry_content_xhtml_with_markup.xml
+    xml/rfc4287/entry_published.xml
+    xml/rfc4287/feed_author_uri.xml
+    xml/rfc4287/feed_rights_xhtml.xml
+    xml/rfc4287/entry_content_xhtml.xml
+    xml/rfc4287/entry_rights_normalized.xml
+    xml/rfc4287/feed_author.xml
+    xml/rfc4287/feed_subtitle.xml
+    xml/rfc4287/entry_content.xml
+    xml/rfc4287/entry_summary.xml
+    xml/rfc4287/feed_comment_rss_extra_att.xml
+    xml/rfc4287/feed_tantek_title.xml
+    xml/rfc4287/entry_contributor.xml
+    xml/rfc4287/entry_title_normalized.xml
+    xml/rfc4287/feed_contributor.xml
+    xml/rfc4287/feed_title_full_feed.xml
+    xml/rfc4287/entry_html_cdata.xml
+    xml/rfc4287/entry_title.xml
+    xml/rfc4287/feed_entry_count.xml
+    xml/rfc4287/feed_title_xhtml_entities.xml
+    xml/rfc4287/entry_id.xml
+    xml/rfc4287/entry_updated.xml
+    xml/rfc4287/feed_generator_uri.xml
+    xml/rfc4287/feed_title_xhtml.xml
+    xml/rfc4287/entry_link_2alts_allcore2.xml
+    xml/rfc4287/entry_w_content_encoded.xml
+    xml/rfc4287/feed_generator_uri_xmlbase.xml
+    xml/rfc4287/feed_title.xml
+    xml/rfc4287/entry_link_2alts_allcore.xml
+    xml/rfc4287/entry_xhtml_baseURI_with_amp.xml
+    xml/rfc4287/feed_generator_version.xml
+    xml/rfc4287/feed_updated_invalid.xml
+    xml/rfc4287/entry_link_2alts.xml
+    xml/rfc4287/entry_xmlBase_on_link.xml
+    xml/rfc4287/feed_generator.xml
+    xml/rfc4287/feed_updated_normalized.xml
+    xml/rfc4287/entry_link_alt_extension.xml
+    xml/rfc4287/entry_xmlBase.xml
+    xml/rfc4287/feed_icon.xml
+    xml/rfc4287/feed_updated.xml
+    xml/rfc4287/entry_link_enclosure_populate_enclosures.xml
+    xml/rfc4287/feed_atom_rights_xhtml.xml
+    xml/rfc4287/feed_id_extra_att.xml
+    xml/rfc4287/feed_version.xml
+    xml/rfc4287/entry_link_enclosure.xml
+    xml/rfc4287/feed_author2.xml
+    xml/rfc4287/feed_id.xml
+    xml/rfc4287/feed_xmlBase.xml
+    xml/rss09x/rss090.xml
+    xml/rss09x/rss091_withNS.xml
+    xml/rss09x/rss091.xml
+    xml/rss09x/rss092.xml
+    xml/rss09x/rss093.xml
+    xml/rss09x/rss094.xml
+    xml/rss09x/rssUnknown.xml
+    xml/rss1/feed_bogus_title.xml
+    xml/rss1/feed_description_normalized.xml
+    xml/rss1/feed_description_with_dc_only.xml
+    xml/rss1/feed_description_with_dc.xml
+    xml/rss1/feed_description.xml
+    xml/rss1/feed_generator.xml
+    xml/rss1/feed_id.xml
+    xml/rss1/feed_image.xml
+    xml/rss1/feed_items_length_zero.xml
+    xml/rss1/feed_link_normalized.xml
+    xml/rss1/feed_link.xml
+    xml/rss1/feed_textInput.xml
+    xml/rss1/feed_title_extra_att.xml
+    xml/rss1/feed_title_normalized.xml
+    xml/rss1/feed_title.xml
+    xml/rss1/feed_updated_dctermsmodified.xml
+    xml/rss1/feed_updated.xml
+    xml/rss1/feed_version.xml
+    xml/rss1/full_feed_not_bozo.xml
+    xml/rss1/full_feed_unknown_extension.xml
+    xml/rss1/full_feed.xml
+    xml/rss1/item_2_dc_description.xml
+    xml/rss1/item_2_dc_publisher_extra_att_invalid_rdf.xml
+    xml/rss1/item_2_dc_publisher.xml
+    xml/rss1/item_count.xml
+    xml/rss1/item_dc_creator.xml
+    xml/rss1/item_dc_description_normalized.xml
+    xml/rss1/item_dc_description.xml
+    xml/rss1/item_description.xml
+    xml/rss1/item_id.xml
+    xml/rss1/item_link_normalized.xml
+    xml/rss1/item_link.xml
+    xml/rss1/item_title_normalized.xml
+    xml/rss1/item_title.xml
+    xml/rss1/item_updated_dcterms.xml
+    xml/rss1/item_wiki_importance_extra_att.xml
+    xml/rss2/feed_category_count.xml
+    xml/rss2/feed_category.xml
+    xml/rss2/feed_cloud.xml
+    xml/rss2/feed_copyright_linebreak.xml
+    xml/rss2/feed_copyright.xml
+    xml/rss2/feed_data_outside_channel.xml
+    xml/rss2/feed_dc_contributor.xml
+    xml/rss2/feed_dc_creator.xml
+    xml/rss2/feed_description_html_cdata.xml
+    xml/rss2/feed_description_html.xml
+    xml/rss2/feed_description.xml
+    xml/rss2/feed_docs.xml
+    xml/rss2/feed_generator.xml
+    xml/rss2/feed_image_desc_width_height.xml
+    xml/rss2/feed_image_desc.xml
+    xml/rss2/feed_image_required.xml
+    xml/rss2/feed_language.xml
+    xml/rss2/feed_lastBuildDate.xml
+    xml/rss2/feed_linebreak_link.xml
+    xml/rss2/feed_link_cdata.xml
+    xml/rss2/feed_link.xml
+    xml/rss2/feed_managingEditor_extra_att.xml
+    xml/rss2/feed_managingEditor.xml
+    xml/rss2/feed_multiple_categories.xml
+    xml/rss2/feed_pubDate_invalid.xml
+    xml/rss2/feed_pubDate_nonRFC822_1.xml
+    xml/rss2/feed_pubDate_nonRFC822_2.xml
+    xml/rss2/feed_pubDate_timezoneZ.xml
+    xml/rss2/feed_pubDate_utc.xml
+    xml/rss2/feed_pubDate.xml
+    xml/rss2/feed_rating.xml
+    xml/rss2/feed_single_quote_stylesheet_pi.xml
+    xml/rss2/feed_skipDays.xml
+    xml/rss2/feed_skipHours.xml
+    xml/rss2/feed_subtitle_html.xml
+    xml/rss2/feed_subtitle_markup_stripped.xml
+    xml/rss2/feed_subtitle.xml
+    xml/rss2/feed_textinput.xml
+    xml/rss2/feed_title_cdata_mixed.xml
+    xml/rss2/feed_title_nesting.xml
+    xml/rss2/feed_title.xml
+    xml/rss2/feed_ttl.xml
+    xml/rss2/feed_updated_dcdate.xml
+    xml/rss2/feed_updated_lastBuildDate_priority.xml
+    xml/rss2/feed_updated_lastBuildDate.xml
+    xml/rss2/feed_updated.xml
+    xml/rss2/feed_webMaster.xml
+    xml/rss2/feed_wfw_commentapi.xml
+    xml/rss2/feed_wfw_commentrss.xml
+    xml/rss2/feed_wiki_unusual_prefix.xml
+    xml/rss2/feed_wiki.xml
+    xml/rss2/item_author.xml
+    xml/rss2/item_category.xml
+    xml/rss2/item_comments.xml
+    xml/rss2/item_content_encoded.xml
+    xml/rss2/item_count2.xml
+    xml/rss2/item_count.xml
+    xml/rss2/item_description_2.xml
+    xml/rss2/item_description_cdata.xml
+    xml/rss2/item_description_decode_entities.xml
+    xml/rss2/item_description_normalized_nohtml.xml
+    xml/rss2/item_description_normalized.xml
+    xml/rss2/item_description.xml
+    xml/rss2/item_enclosure_duplicates2.xml
+    xml/rss2/item_enclosure_duplicates.xml
+    xml/rss2/item_enclosure_mixed2.xml
+    xml/rss2/item_enclosure_mixed.xml
+    xml/rss2/item_enclosure.xml
+    xml/rss2/item_guid_bogus_url.xml
+    xml/rss2/item_guid_isPermaLink_default.xml
+    xml/rss2/item_guid_isPermaLink_false_uppercase.xml
+    xml/rss2/item_guid_isPermaLink_false.xml
+    xml/rss2/item_guid_isPermaLink_true_uppercase.xml
+    xml/rss2/item_guid_isPermaLink_unknown_value.xml
+    xml/rss2/item_guid_isPermaLink.xml
+    xml/rss2/item_guid_normalized.xml
+    xml/rss2/item_guid_with_link.xml
+    xml/rss2/item_guid.xml
+    xml/rss2/item_link_normalized.xml
+    xml/rss2/item_link.xml
+    xml/rss2/item_plain_desc.xml
+    xml/rss2/item_populated_enclosures.xml
+    xml/rss2/item_pubDate.xml
+    xml/rss2/item_published.xml
+    xml/rss2/items_2_titles.xml
+    xml/rss2/item_title_normalized.xml
+    xml/rss2/item_title.xml
+    xml/rss2/item_updated_dcdate.xml
+    xml/rss2/mrss_content_429049.xml
+    xml/rss2/mrss_content_multiple.xml
+    xml/rss2/mrss_content_populate_enclosure.xml
+    xml/rss2/mrss_content.xml
+    xml/rss2/mrss_content2.xml
+    xml/rss2/mrss_group_content_populate_enclosure.xml
+    xml/rss2/mrss_group_content.xml
+
+[test_xml.js]