| library markdown.tool.common_mark_stats; |
| |
| import 'dart:convert'; |
| import 'dart:io'; |
| 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; |
| |
| const _commonMarkTests = 'common_mark_tests.json'; |
| |
| // Locate the "test" directory. Use mirrors so that this works with the test |
| // package, which loads this suite into an isolate. |
| String get _currentDir => p.dirname(currentMirrorSystem() |
| .findLibrary(#markdown.tool.common_mark_stats) |
| .uri |
| .path); |
| |
| void main() { |
| var sections = loadCommonMarkSections(); |
| |
| var scores = <String, int>{}; |
| |
| int maxSectionLength = 0; |
| int totalExamples = 0; |
| int totalValid = 0; |
| |
| sections.forEach((section, examples) { |
| int validCount = 0; |
| for (var e in examples) { |
| var output; |
| |
| try { |
| output = markdownToHtml(e.markdown); |
| } catch (exc) { |
| continue; |
| } |
| |
| var expected = parseFragment(e.html); |
| var actual = parseFragment(output); |
| |
| if (compareHtml(expected.children, actual.children)) { |
| validCount++; |
| } |
| } |
| |
| maxSectionLength = math.max(maxSectionLength, section.length); |
| |
| scores[section] = validCount; |
| |
| totalValid += validCount; |
| totalExamples += examples.length; |
| }); |
| |
| scores.forEach((section, count) { |
| var total = sections[section].length; |
| var pct = (100 * count / total).toStringAsFixed(1).padLeft(5); |
| |
| print('${section.padLeft(maxSectionLength)} ' |
| '${count.toString().padLeft(3)} ' |
| 'of ${total.toString().padLeft(3)} ' |
| '– ${pct}%'); |
| }); |
| |
| var pct = (100 * totalValid / totalExamples).toStringAsFixed(1).padLeft(5); |
| |
| print('${"TOTAL".padLeft(maxSectionLength)} ' |
| '${totalValid.toString().padLeft(3)} ' |
| 'of ${totalExamples.toString().padLeft(3)} ' |
| '– ${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(); |
| |
| var testArray = JSON.decode(testsJson) as List<Map>; |
| |
| var sections = new Map<String, List<CommonMarkTestCase>>(); |
| |
| for (var exampleMap in testArray) { |
| var exampleTest = new CommonMarkTestCase.fromJson(exampleMap); |
| |
| var sectionList = |
| sections.putIfAbsent(exampleTest.section, () => <CommonMarkTestCase>[]); |
| |
| sectionList.add(exampleTest); |
| } |
| |
| return sections; |
| } |
| |
| class CommonMarkTestCase { |
| final String markdown; |
| final String section; |
| final int example; |
| final String html; |
| final int startLine; |
| final int endLine; |
| |
| CommonMarkTestCase(this.example, this.section, this.startLine, this.endLine, |
| this.markdown, this.html); |
| |
| factory CommonMarkTestCase.fromJson(Map<String, dynamic> json) { |
| return new CommonMarkTestCase(json['example'], json['section'], |
| json['start_line'], json['end_line'], json['markdown'], json['html']); |
| } |
| } |