Loading the parity record
Fetching the measured run — the pinned upstream oracle, every symbol and every case.
Fetching the measured run — the pinned upstream oracle, every symbol and every case.
Parity / Markdown
Every number on this page was produced by running both implementations over the same cases: the real commonmark package pinned at 0.31.2 answers first, and its answer is the expectation the Go port is held to. Nothing is a hand-written expectation, so a new upstream release re-scores the port on its own. See Markdown for the port's own documentation. Source: github.com/malcolmston/markdown.
Not a generic diagram: every node below names an artefact of this harness — the pinned package it installed, the runner files it started, the case files it streamed, and the counts it wrote out.
| Case group | Cases | Match | Mismatch | Group parity |
|---|---|---|---|---|
| Emphasis and strong emphasis | 132 | 132 | 0 | 100.0% |
| Links | 90 | 90 | 0 | 100.0% |
| List items | 48 | 48 | 0 | 100.0% |
| HTML blocks | 44 | 44 | 0 | 100.0% |
| Fenced code blocks | 29 | 29 | 0 | 100.0% |
| Link reference definitions | 27 | 27 | 0 | 100.0% |
| Setext headings | 27 | 27 | 0 | 100.0% |
| Lists | 26 | 26 | 0 | 100.0% |
| Block quotes | 25 | 25 | 0 | 100.0% |
| Code spans | 22 | 22 | 0 | 100.0% |
| Images | 22 | 22 | 0 | 100.0% |
| Raw HTML | 20 | 20 | 0 | 100.0% |
| Autolinks | 19 | 19 | 0 | 100.0% |
| security | 19 | 11 | 8 | 57.9% |
| Thematic breaks | 19 | 19 | 0 | 100.0% |
| ATX headings | 18 | 18 | 0 | 100.0% |
| Entity and numeric character references | 17 | 17 | 0 | 100.0% |
| api | 15 | 14 | 1 | 93.3% |
| Hard line breaks | 15 | 15 | 0 | 100.0% |
| options | 15 | 6 | 9 | 40.0% |
| Backslash escapes | 13 | 13 | 0 | 100.0% |
| Indented code blocks | 12 | 12 | 0 | 100.0% |
| Tabs | 11 | 11 | 0 | 100.0% |
| Paragraphs | 8 | 8 | 0 | 100.0% |
| Textual content | 3 | 3 | 0 | 100.0% |
| Soft line breaks | 2 | 2 | 0 | 100.0% |
| Blank lines | 1 | 1 | 0 | 100.0% |
| Inlines | 1 | 1 | 0 | 100.0% |
| Precedence | 1 | 1 | 0 | 100.0% |
Every exported symbol of the upstream package, and what the port offers for it. The upstream list is derived mechanically, never from a README: Derived from `GOWORK=off go doc -all github.com/malcolmston/markdown`.. A symbol with no case is untested, never a match.
| Upstream symbol | Go symbol | Status | Cases | Note |
|---|---|---|---|---|
| Parser (constructor) | markdown.New / markdown.Parse | match | api-ast-*, api-node-* | |
| new Parser({smart}) | Options.Typographer | match | opt-smart-quotes, opt-smart-dashes-ellipsis, opt-smart-apostrophe, opt-smart-nested-quotes, opt-smart-in-code-span | all 5 agree byte for byte |
| Parser#parse | markdown.Parse, (*Markdown).Parse | match | api-ast-headings-and-text, api-ast-lists, api-ast-code-blocks, api-ast-links-and-images, api-ast-blockquote-and-breaks, api-ast-raw-html, api-ast-code-span, api-ast-empty | AST field subset, see below |
| HtmlRenderer (constructor) | markdown.New | match | all 652 spec cases | |
| HtmlRenderer#render | markdown.Render, (*Markdown).Render | match | all 652 spec cases + 12 sec-* | |
| new HtmlRenderer({safe}) | — | missing | sec-safe-* (7) | no sanitising mode in the port at all |
| new HtmlRenderer({softbreak}) | Options.LineBreaks | differs | opt-softbreak-br, opt-softbreak-multi-line, opt-softbreak-in-heading | port emits <br />\n, upstream emits <br /> (markdown-it vs cmark style). Not listed in the module's API-DEVIATIONS.md, so scored as a mismatch |
| new HtmlRenderer({softbreak}) on a hard break | Options.LineBreaks | match | opt-softbreak-hardbreak-still-br | |
| new HtmlRenderer({sourcepos}) | — | missing | — | untested: the port records no source positions, so there is nothing to compare |
| new HtmlRenderer({esc}) custom escaper | — | missing | — | untested: no renderer hooks in the port |
| XmlRenderer (+ #render, #cr, #esc, #out, #tag) | — | missing | — | port renders HTML only |
| Renderer (base class: #render, #cr, #esc, #lit, #out) | — | missing | — | the port's renderer is unexported and not subclassable |
| HtmlRenderer# per-node methods (text, softbreak, linebreak, link, image, emph, strong, paragraph, heading, code, code_block, thematic_break, block_quote, list, item, html_block, html_inline, custom_block, custom_inline, attrs, esc, out, tag) | — | missing | — | 23 overridable render hooks; the port exposes none. Their *effects* are covered by the 652 spec cases |
| Node (constructor) | markdown.Node struct literal | match | api-append-child | |
| Node#type | Node.Type + NodeType.String | match | api-ast-*, api-node-types-all-constructs | all 18 type names are spelled identically, including softbreak/linebreak |
| Node#literal | Node.Literal | match | api-ast-code-blocks, api-ast-raw-html, api-ast-code-span | compared for text, code, code_block, html_block, html_inline only |
| Node#level | Node.Level | match | api-ast-headings-and-text | compared on heading only |
| Node#destination | Node.Destination | match | api-ast-links-and-images | |
| Node#title | Node.Title | match | api-ast-links-and-images | |
| Node#info | Node.Info | match | api-ast-code-blocks | compared on code_block only |
| Node#listType | Node.Info on a list | match | api-ast-lists | "bullet" / "ordered" in both |
| Node#listTight | Node.Tight | match | api-ast-lists | |
| Node#listStart | Node.Level on a list | match | api-ast-lists | |
| Node#listDelimiter | Node.Literal on a list | differs | — | untested by design: upstream exposes only . / ), the port also stores the bullet character -/*/+ and copies both onto every item. Masked out of the AST comparison |
| Node#firstChild | (*Node).FirstChild | match | api-node-nav-document, api-node-nav-empty | |
| Node#lastChild | (*Node).LastChild | match | api-node-nav-document, api-node-nav-empty | |
| Node#parent | (*Node).Parent | match | api-node-nav-document, api-node-nav-empty | both report null/nil for the document root |
| Node#appendChild | (*Node).AppendChild | match | api-append-child | |
| Node#walker | (*Node).Walk | match | api-node-types-all-constructs, api-node-nav-document | node counts and type sets agree |
| Node#next | Node.Children slice | match | every api-ast-* case | sibling links vs a slice; the traversal order is what is compared |
| Node#prev | — | missing | — | untested: no backward sibling link |
| Node#insertAfter | — | missing | — | port has no sibling insertion |
| Node#insertBefore | — | missing | — | port has no sibling insertion |
| Node#prependChild | — | missing | — | |
| Node#unlink | — | missing | — | no way to detach a node |
| Node#isContainer | NodeType.IsBlock | differs | api-block-types | different questions: isContainer is per-node ("may have children", true for link/emph), IsBlock is per-type ("block-level"). No oracle, so this is scored as a mismatch and is really *extra* |
| Node#sourcepos | — | missing | — | the port records no source positions |
| Node#onEnter / #onExit | — | missing | — | custom-block hooks; the port has no custom node types |
| — | markdown.RenderTo | extra | api-render-to-matches-render, api-render-to-write-error | output identical to upstream render(); the error path wraps ErrWrite, ErrMarkdown and the writer's own error, all three verified with errors.Is |
| — | (*Markdown).RenderTo | extra | — | untested: same code path as markdown.RenderTo |
| — | markdown.DefaultOptions | extra | opt-default-options | no upstream accessor; the case exists to pin the shipped default {HTML:true} |
| — | Options.HTML | extra | opt-nohtml-block, opt-nohtml-inline, opt-nohtml-does-not-touch-links | no upstream counterpart (safe:true also rewrites links); escapes rather than omits raw HTML |
| — | Options.LinkTarget | extra | opt-linktarget-blank, opt-linktarget-autolink | no upstream counterpart |
| — | markdown.ErrMarkdown | extra | api-render-to-write-error | |
| — | markdown.ErrWrite | extra | api-render-to-write-error | |
| — | NodeType + its 18 constants | extra | api-node-types-all-constructs | upstream uses bare strings |
| — | NodeType.String | extra | api-node-types-all-constructs, every api-ast-* | names match upstream's strings exactly |
| — | NodeType.IsBlock | extra | api-block-types | no upstream equivalent |
| — | Node.Tight, Node.Info, Node.Level reuse for list metadata | extra | api-ast-lists | documented in the module's API-DEVIATIONS.md §1 |
Every case the harness streamed to both runners, with the exact upstream symbol and Go symbol it exercised. A deliberate, documented difference is a deviation and is counted apart from a mismatch.
| Case | Group | Upstream symbol | Go symbol | Status | Note |
|---|---|---|---|---|---|
| api-ast-headings-and-text | api | new Parser().parse | (*markdown.Markdown).Parse | match | |
| api-ast-lists | api | new Parser().parse | (*markdown.Markdown).Parse | match | |
| api-ast-code-blocks | api | new Parser().parse | (*markdown.Markdown).Parse | match | |
| api-ast-links-and-images | api | new Parser().parse | (*markdown.Markdown).Parse | match | |
| api-ast-blockquote-and-breaks | api | new Parser().parse | (*markdown.Markdown).Parse | match | |
| api-ast-raw-html | api | new Parser().parse | (*markdown.Markdown).Parse | match | |
| api-ast-code-span | api | new Parser().parse | (*markdown.Markdown).Parse | match | |
| api-ast-empty | api | new Parser().parse | (*markdown.Markdown).Parse | match | |
| api-node-types-all-constructs | api | Node#walker | markdown.Parse + (*Node).Walk + NodeType.String | match | the distinct node-type names a document containing every construct produces |
| api-node-nav-document | api | Node#firstChild / #parent / #walker | (*Node).FirstChild / .LastChild / .Parent / .Walk | match | |
| api-node-nav-empty | api | Node#firstChild / #parent / #walker | (*Node).FirstChild / .LastChild / .Parent / .Walk | match | |
| api-append-child | api | new Node(type) + Node#appendChild | &markdown.Node{...} + (*Node).AppendChild | match | the port has no exported renderer for a *Node, so the resulting tree is compared, not its HTML |
| api-render-to-matches-render | api | new HtmlRenderer().render(...) | markdown.RenderTo | match | the io.Writer entry point must produce exactly what upstream's render() returns |
| api-render-to-write-error | api | — | markdown.RenderTo + ErrWrite / ErrMarkdown | match | both sides fail, which is parity: upstream has no writer to fail, and the port's error wraps ErrWrite, ErrMarkdown and the writer's own error |
| api-block-types | api | — | markdown.NodeType.IsBlock | mismatch | no upstream counterpart; commonmark.js only has Node#isContainer, a different question |
| opt-smart-quotes | options | new Parser({smart: true}) | markdown.New(Options{HTML: true, Typographer: true}) | match | |
| opt-smart-dashes-ellipsis | options | new Parser({smart: true}) | markdown.New(Options{HTML: true, Typographer: true}) | match | |
| opt-smart-apostrophe | options | new Parser({smart: true}) | markdown.New(Options{HTML: true, Typographer: true}) | match | |
| opt-smart-nested-quotes | options | new Parser({smart: true}) | markdown.New(Options{HTML: true, Typographer: true}) | match | |
| opt-smart-in-code-span | options | new Parser({smart: true}) | markdown.New(Options{HTML: true, Typographer: true}) | match | code spans must be left alone |
| opt-softbreak-br | options | new HtmlRenderer({softbreak: '<br />'}) | markdown.New(Options{HTML: true, LineBreaks: true}) | mismatch | |
| opt-softbreak-multi-line | options | new HtmlRenderer({softbreak: '<br />'}) | markdown.New(Options{HTML: true, LineBreaks: true}) | mismatch | |
| opt-softbreak-hardbreak-still-br | options | new HtmlRenderer({softbreak: '<br />'}) | markdown.New(Options{HTML: true, LineBreaks: true}) | match | |
| opt-softbreak-in-heading | options | new HtmlRenderer({softbreak: '<br />'}) | markdown.New(Options{HTML: true, LineBreaks: true}) | mismatch | |
| opt-nohtml-block | options | — | markdown.New(Options{HTML: false}) | mismatch | no upstream counterpart: commonmark.js only has safe:true, which also rewrites links |
| opt-nohtml-inline | options | — | markdown.New(Options{HTML: false}) | mismatch | |
| opt-nohtml-does-not-touch-links | options | — | markdown.New(Options{HTML: false}) | mismatch | records that HTML:false is NOT a sanitiser: the javascript: href survives |
| opt-linktarget-blank | options | — | markdown.New(Options{HTML: true, LinkTarget: "_blank"}) | mismatch | no upstream counterpart |
| opt-linktarget-autolink | options | — | markdown.New(Options{HTML: true, LinkTarget: "_blank"}) | mismatch | |
| opt-default-options | options | — | markdown.DefaultOptions | mismatch | records the shipped default: {HTML:true, Typographer:false, LineBreaks:false, LinkTarget:""} |
| sec-js-scheme-link | security | new HtmlRenderer().render(...) | markdown.Render | match | default: both emit a live javascript: href |
| sec-js-scheme-link-mixed-case | security | new HtmlRenderer().render(...) | markdown.Render | match | |
| sec-js-scheme-entity-obfuscated | security | new HtmlRenderer().render(...) | markdown.Render | match | character reference decoded before the href is written |
| sec-js-scheme-tab-obfuscated | security | new HtmlRenderer().render(...) | markdown.Render | mismatch | |
| sec-vbscript-scheme-link | security | new HtmlRenderer().render(...) | markdown.Render | match | |
| sec-data-scheme-link | security | new HtmlRenderer().render(...) | markdown.Render | match | |
| sec-js-scheme-image | security | new HtmlRenderer().render(...) | markdown.Render | match | |
| sec-js-scheme-reference-def | security | new HtmlRenderer().render(...) | markdown.Render | match | |
| sec-raw-script-block | security | new HtmlRenderer().render(...) | markdown.Render | match | default HTML:true passes raw HTML straight through |
| sec-raw-img-onerror | security | new HtmlRenderer().render(...) | markdown.Render | match | |
| sec-raw-iframe-js-src | security | new HtmlRenderer().render(...) | markdown.Render | match | |
| sec-raw-inline-event-handler | security | new HtmlRenderer().render(...) | markdown.Render | match | |
| sec-safe-js-scheme-link | security | new HtmlRenderer({safe: true}).render(...) | — | mismatch | upstream drops the href; the port has no equivalent option |
| sec-safe-vbscript-scheme-link | security | new HtmlRenderer({safe: true}).render(...) | — | mismatch | |
| sec-safe-data-scheme-link | security | new HtmlRenderer({safe: true}).render(...) | — | mismatch | |
| sec-safe-js-scheme-image | security | new HtmlRenderer({safe: true}).render(...) | — | mismatch | |
| sec-safe-raw-script-block | security | new HtmlRenderer({safe: true}).render(...) | — | mismatch | upstream replaces the block with <!-- raw HTML omitted --> |
| sec-safe-raw-inline-event-handler | security | new HtmlRenderer({safe: true}).render(...) | — | mismatch | |
| sec-safe-http-link-untouched | security | new HtmlRenderer({safe: true}).render(...) | — | mismatch | safe mode leaves benign links alone, so it is a usable default |
| ex-001 | Tabs | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-002 | Tabs | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-003 | Tabs | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-004 | Tabs | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-005 | Tabs | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-006 | Tabs | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-007 | Tabs | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-008 | Tabs | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-009 | Tabs | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-010 | Tabs | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-011 | Tabs | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-012 | Backslash escapes | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-013 | Backslash escapes | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-014 | Backslash escapes | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-015 | Backslash escapes | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-016 | Backslash escapes | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-017 | Backslash escapes | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-018 | Backslash escapes | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-019 | Backslash escapes | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-020 | Backslash escapes | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-021 | Backslash escapes | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-022 | Backslash escapes | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-023 | Backslash escapes | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-024 | Backslash escapes | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-025 | Entity and numeric character references | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-026 | Entity and numeric character references | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-027 | Entity and numeric character references | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-028 | Entity and numeric character references | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-029 | Entity and numeric character references | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-030 | Entity and numeric character references | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-031 | Entity and numeric character references | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-032 | Entity and numeric character references | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-033 | Entity and numeric character references | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-034 | Entity and numeric character references | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-035 | Entity and numeric character references | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-036 | Entity and numeric character references | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-037 | Entity and numeric character references | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-038 | Entity and numeric character references | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-039 | Entity and numeric character references | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-040 | Entity and numeric character references | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-041 | Entity and numeric character references | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-042 | Precedence | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-043 | Thematic breaks | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-044 | Thematic breaks | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-045 | Thematic breaks | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-046 | Thematic breaks | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-047 | Thematic breaks | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-048 | Thematic breaks | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-049 | Thematic breaks | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-050 | Thematic breaks | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match | |
| ex-051 | Thematic breaks | new commonmark.HtmlRenderer().render(new commonmark.Parser().parse(src)) | markdown.Render | match |