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} 199