Improve dart-lang/markdowndart-lang/markdown#51.

First, put a try/catch around attempting to parse Markdown.
Failure to parse should count as a negative test case, but
should not abort testing altogether.

Second, implement a primitive HTML comparison function that
is not sensitive to attribute order, meaningless whitespace,
etc.

The second change alone boosts CommonMark score from 232/599
to 383/599.
diff --git a/pkgs/markdown/pubspec.yaml b/pkgs/markdown/pubspec.yaml
index 2fdfc0d..f7092bd 100644
--- a/pkgs/markdown/pubspec.yaml
+++ b/pkgs/markdown/pubspec.yaml
@@ -6,5 +6,6 @@
 environment:
   sdk: '>=1.12.0 <2.0.0'
 dev_dependencies:
+  html: '^0.12.2'
   path: '^1.3.1'
   test: '^0.12.4+1'
diff --git a/pkgs/markdown/tool/common_mark_stats.dart b/pkgs/markdown/tool/common_mark_stats.dart
index d1c0d10..e3d1c32 100644
--- a/pkgs/markdown/tool/common_mark_stats.dart
+++ b/pkgs/markdown/tool/common_mark_stats.dart
@@ -5,6 +5,7 @@
 import 'dart:math' as math;
 import 'dart:mirrors';
 
+import 'package:html/parser.dart' show parseFragment;
 import 'package:markdown/markdown.dart';
 import 'package:path/path.dart' as p;
 
@@ -29,9 +30,18 @@
   sections.forEach((section, examples) {
     int validCount = 0;
     for (var e in examples) {
-      var output = markdownToHtml(e.markdown);
+      var output;
 
-      if (output == e.html) {
+      try {
+        output = markdownToHtml(e.markdown);
+      } catch (exc) {
+        continue;
+      }
+
+      var expected = parseFragment(e.html);
+      var actual = parseFragment(output);
+
+      if (compareHtml(expected.children, actual.children)) {
         validCount++;
       }
     }
@@ -62,6 +72,60 @@
       '– ${pct}%');
 }
 
+/// Compare two DOM trees for equality.
+bool compareHtml(List<Element> expectedEls, List<Element> actualEls) {
+  if (expectedEls.length != actualEls.length) {
+    return false;
+  }
+
+  for (int childNum in new List.generate(expectedEls.length, (i) => i)) {
+    var expected = expectedEls[childNum];
+    var actual = actualEls[childNum];
+
+    if (expected.runtimeType != actual.runtimeType) {
+      return false;
+    }
+
+    if (expected is Element) {
+      if (expected.localName != actual.localName) {
+        return false;
+      }
+
+      if (expected.attributes.length != actual.attributes.length) {
+        return false;
+      }
+
+      List expectedAttrKeys = new List.from(expected.attributes.keys);
+      expectedAttrKeys.sort();
+
+      List actualAttrKeys = new List.from(actual.attributes.keys);
+      actualAttrKeys.sort();
+
+      for (int attrNum in new List.generate(actualAttrKeys.length, (i) => i)) {
+        var expectedAttrKey = expectedAttrKeys[attrNum];
+        var actualAttrKey = actualAttrKeys[attrNum];
+
+        if (expectedAttrKey != actualAttrKey) {
+          return false;
+        }
+
+        if (expected.attributes[expectedAttrKey] !=
+            actual.attributes.keys[actualAttrKey]) {
+          return false;
+        }
+      }
+    }
+
+    bool childrenEqual = compareHtml(expected.children, actual.children);
+
+    if (!childrenEqual) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 Map<String, List<CommonMarkTestCase>> loadCommonMarkSections() {
   var testFile = new File(p.join(_currentDir, _commonMarkTests));
   var testsJson = testFile.readAsStringSync();