helpers_test.go (view raw)
1//
2// Blackfriday Markdown Processor
3// Available at http://github.com/russross/blackfriday
4//
5// Copyright © 2011 Russ Ross <russ@russross.com>.
6// Distributed under the Simplified BSD License.
7// See README.md for details.
8//
9
10//
11// Helper functions for unit testing
12//
13
14package blackfriday
15
16import (
17 "io/ioutil"
18 "path/filepath"
19 "regexp"
20 "testing"
21
22 "github.com/pmezard/go-difflib/difflib"
23)
24
25type TestParams struct {
26 extensions Extensions
27 referenceOverride ReferenceOverrideFunc
28 HTMLFlags
29 HTMLRendererParameters
30}
31
32func execRecoverableTestSuite(t *testing.T, tests []string, params TestParams, suite func(candidate *string)) {
33 // Catch and report panics. This is useful when running 'go test -v' on
34 // the integration server. When developing, though, crash dump is often
35 // preferable, so recovery can be easily turned off with doRecover = false.
36 var candidate string
37 const doRecover = true
38 if doRecover {
39 defer func() {
40 if err := recover(); err != nil {
41 t.Errorf("\npanic while processing [%#v]: %s\n", candidate, err)
42 }
43 }()
44 }
45 suite(&candidate)
46}
47
48func runMarkdown(input string, params TestParams) string {
49 params.HTMLRendererParameters.Flags = params.HTMLFlags
50 renderer := NewHTMLRenderer(params.HTMLRendererParameters)
51 return string(Run([]byte(input), WithRenderer(renderer),
52 WithExtensions(params.extensions),
53 WithRefOverride(params.referenceOverride)))
54}
55
56// doTests runs full document tests using MarkdownCommon configuration.
57func doTests(t *testing.T, tests []string) {
58 doTestsParam(t, tests, TestParams{
59 extensions: CommonExtensions,
60 HTMLRendererParameters: HTMLRendererParameters{
61 Flags: CommonHTMLFlags,
62 },
63 })
64}
65
66func doTestsBlock(t *testing.T, tests []string, extensions Extensions) {
67 doTestsParam(t, tests, TestParams{
68 extensions: extensions,
69 HTMLFlags: UseXHTML,
70 })
71}
72
73func doTestsParam(t *testing.T, tests []string, params TestParams) {
74 execRecoverableTestSuite(t, tests, params, func(candidate *string) {
75 for i := 0; i+1 < len(tests); i += 2 {
76 input := tests[i]
77 *candidate = input
78 expected := tests[i+1]
79 actual := runMarkdown(*candidate, params)
80 if actual != expected {
81 t.Errorf("\nInput [%#v]\nExpected[%#v]\nActual [%#v]",
82 *candidate, expected, actual)
83 }
84
85 // now test every substring to stress test bounds checking
86 if !testing.Short() {
87 for start := 0; start < len(input); start++ {
88 for end := start + 1; end <= len(input); end++ {
89 *candidate = input[start:end]
90 runMarkdown(*candidate, params)
91 }
92 }
93 }
94 }
95 })
96}
97
98func doTestsInline(t *testing.T, tests []string) {
99 doTestsInlineParam(t, tests, TestParams{})
100}
101
102func doLinkTestsInline(t *testing.T, tests []string) {
103 doTestsInline(t, tests)
104
105 prefix := "http://localhost"
106 params := HTMLRendererParameters{AbsolutePrefix: prefix}
107 transformTests := transformLinks(tests, prefix)
108 doTestsInlineParam(t, transformTests, TestParams{
109 HTMLRendererParameters: params,
110 })
111 doTestsInlineParam(t, transformTests, TestParams{
112 HTMLFlags: UseXHTML,
113 HTMLRendererParameters: params,
114 })
115}
116
117func doSafeTestsInline(t *testing.T, tests []string) {
118 doTestsInlineParam(t, tests, TestParams{HTMLFlags: Safelink})
119
120 // All the links in this test should not have the prefix appended, so
121 // just rerun it with different parameters and the same expectations.
122 prefix := "http://localhost"
123 params := HTMLRendererParameters{AbsolutePrefix: prefix}
124 transformTests := transformLinks(tests, prefix)
125 doTestsInlineParam(t, transformTests, TestParams{
126 HTMLFlags: Safelink,
127 HTMLRendererParameters: params,
128 })
129}
130
131func doTestsInlineParam(t *testing.T, tests []string, params TestParams) {
132 params.extensions |= Autolink | Strikethrough
133 params.HTMLFlags |= UseXHTML
134 doTestsParam(t, tests, params)
135}
136
137func transformLinks(tests []string, prefix string) []string {
138 newTests := make([]string, len(tests))
139 anchorRe := regexp.MustCompile(`<a href="/(.*?)"`)
140 imgRe := regexp.MustCompile(`<img src="/(.*?)"`)
141 for i, test := range tests {
142 if i%2 == 1 {
143 test = anchorRe.ReplaceAllString(test, `<a href="`+prefix+`/$1"`)
144 test = imgRe.ReplaceAllString(test, `<img src="`+prefix+`/$1"`)
145 }
146 newTests[i] = test
147 }
148 return newTests
149}
150
151func doTestsReference(t *testing.T, files []string, flag Extensions) {
152 params := TestParams{extensions: flag}
153 execRecoverableTestSuite(t, files, params, func(candidate *string) {
154 for _, basename := range files {
155 filename := filepath.Join("testdata", basename+".text")
156 inputBytes, err := ioutil.ReadFile(filename)
157 if err != nil {
158 t.Errorf("Couldn't open '%s', error: %v\n", filename, err)
159 continue
160 }
161 input := string(inputBytes)
162
163 filename = filepath.Join("testdata", basename+".html")
164 expectedBytes, err := ioutil.ReadFile(filename)
165 if err != nil {
166 t.Errorf("Couldn't open '%s', error: %v\n", filename, err)
167 continue
168 }
169 expected := string(expectedBytes)
170
171 actual := string(runMarkdown(input, params))
172 if actual != expected {
173 t.Errorf("\n" + doTestDiff(basename, expected, actual))
174 }
175
176 // now test every prefix of every input to check for
177 // bounds checking
178 if !testing.Short() {
179 start, max := 0, len(input)
180 for end := start + 1; end <= max; end++ {
181 *candidate = input[start:end]
182 runMarkdown(*candidate, params)
183 }
184 }
185 }
186 })
187}
188
189func doTestDiff(name, expected, actual string) string {
190 d, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
191 A: difflib.SplitLines(expected),
192 B: difflib.SplitLines(actual),
193 FromFile: "expect: " + name,
194 ToFile: "actual: " + name,
195 Context: 1,
196 })
197 return d
198}