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();