1package md2man
2
3import (
4	"testing"
5
6	"github.com/russross/blackfriday/v2"
7)
8
9type TestParams struct {
10	extensions blackfriday.Extensions
11}
12
13func TestEmphasis(t *testing.T) {
14	var tests = []string{
15		"nothing inline\n",
16		".nh\n\n.PP\nnothing inline\n",
17
18		"simple *inline* test\n",
19		".nh\n\n.PP\nsimple \\fIinline\\fP test\n",
20
21		"*at the* beginning\n",
22		".nh\n\n.PP\n\\fIat the\\fP beginning\n",
23
24		"at the *end*\n",
25		".nh\n\n.PP\nat the \\fIend\\fP\n",
26
27		"*try two* in *one line*\n",
28		".nh\n\n.PP\n\\fItry two\\fP in \\fIone line\\fP\n",
29
30		"over *two\nlines* test\n",
31		".nh\n\n.PP\nover \\fItwo\nlines\\fP test\n",
32
33		"odd *number of* markers* here\n",
34		".nh\n\n.PP\nodd \\fInumber of\\fP markers* here\n",
35
36		"odd *number\nof* markers* here\n",
37		".nh\n\n.PP\nodd \\fInumber\nof\\fP markers* here\n",
38
39		"simple _inline_ test\n",
40		".nh\n\n.PP\nsimple \\fIinline\\fP test\n",
41
42		"_at the_ beginning\n",
43		".nh\n\n.PP\n\\fIat the\\fP beginning\n",
44
45		"at the _end_\n",
46		".nh\n\n.PP\nat the \\fIend\\fP\n",
47
48		"_try two_ in _one line_\n",
49		".nh\n\n.PP\n\\fItry two\\fP in \\fIone line\\fP\n",
50
51		"over _two\nlines_ test\n",
52		".nh\n\n.PP\nover \\fItwo\nlines\\fP test\n",
53
54		"odd _number of_ markers_ here\n",
55		".nh\n\n.PP\nodd \\fInumber of\\fP markers\\_ here\n",
56
57		"odd _number\nof_ markers_ here\n",
58		".nh\n\n.PP\nodd \\fInumber\nof\\fP markers\\_ here\n",
59
60		"mix of *markers_\n",
61		".nh\n\n.PP\nmix of *markers\\_\n",
62
63		"*What is A\\* algorithm?*\n",
64		".nh\n\n.PP\n\\fIWhat is A* algorithm?\\fP\n",
65	}
66	doTestsInline(t, tests)
67}
68
69func TestStrong(t *testing.T) {
70	var tests = []string{
71		"nothing inline\n",
72		".nh\n\n.PP\nnothing inline\n",
73
74		"simple **inline** test\n",
75		".nh\n\n.PP\nsimple \\fBinline\\fP test\n",
76
77		"**at the** beginning\n",
78		".nh\n\n.PP\n\\fBat the\\fP beginning\n",
79
80		"at the **end**\n",
81		".nh\n\n.PP\nat the \\fBend\\fP\n",
82
83		"**try two** in **one line**\n",
84		".nh\n\n.PP\n\\fBtry two\\fP in \\fBone line\\fP\n",
85
86		"over **two\nlines** test\n",
87		".nh\n\n.PP\nover \\fBtwo\nlines\\fP test\n",
88
89		"odd **number of** markers** here\n",
90		".nh\n\n.PP\nodd \\fBnumber of\\fP markers** here\n",
91
92		"odd **number\nof** markers** here\n",
93		".nh\n\n.PP\nodd \\fBnumber\nof\\fP markers** here\n",
94
95		"simple __inline__ test\n",
96		".nh\n\n.PP\nsimple \\fBinline\\fP test\n",
97
98		"__at the__ beginning\n",
99		".nh\n\n.PP\n\\fBat the\\fP beginning\n",
100
101		"at the __end__\n",
102		".nh\n\n.PP\nat the \\fBend\\fP\n",
103
104		"__try two__ in __one line__\n",
105		".nh\n\n.PP\n\\fBtry two\\fP in \\fBone line\\fP\n",
106
107		"over __two\nlines__ test\n",
108		".nh\n\n.PP\nover \\fBtwo\nlines\\fP test\n",
109
110		"odd __number of__ markers__ here\n",
111		".nh\n\n.PP\nodd \\fBnumber of\\fP markers\\_\\_ here\n",
112
113		"odd __number\nof__ markers__ here\n",
114		".nh\n\n.PP\nodd \\fBnumber\nof\\fP markers\\_\\_ here\n",
115
116		"mix of **markers__\n",
117		".nh\n\n.PP\nmix of **markers\\_\\_\n",
118
119		"**`/usr`** : this folder is named `usr`\n",
120		".nh\n\n.PP\n\\fB\\fB\\fC/usr\\fR\\fP : this folder is named \\fB\\fCusr\\fR\n",
121
122		"**`/usr`** :\n\n this folder is named `usr`\n",
123		".nh\n\n.PP\n\\fB\\fB\\fC/usr\\fR\\fP :\n\n.PP\nthis folder is named \\fB\\fCusr\\fR\n",
124	}
125	doTestsInline(t, tests)
126}
127
128func TestEmphasisMix(t *testing.T) {
129	var tests = []string{
130		"***triple emphasis***\n",
131		".nh\n\n.PP\n\\fB\\fItriple emphasis\\fP\\fP\n",
132
133		"***triple\nemphasis***\n",
134		".nh\n\n.PP\n\\fB\\fItriple\nemphasis\\fP\\fP\n",
135
136		"___triple emphasis___\n",
137		".nh\n\n.PP\n\\fB\\fItriple emphasis\\fP\\fP\n",
138
139		"***triple emphasis___\n",
140		".nh\n\n.PP\n***triple emphasis\\_\\_\\_\n",
141
142		"*__triple emphasis__*\n",
143		".nh\n\n.PP\n\\fI\\fBtriple emphasis\\fP\\fP\n",
144
145		"__*triple emphasis*__\n",
146		".nh\n\n.PP\n\\fB\\fItriple emphasis\\fP\\fP\n",
147
148		"**improper *nesting** is* bad\n",
149		".nh\n\n.PP\n\\fBimproper *nesting\\fP is* bad\n",
150
151		"*improper **nesting* is** bad\n",
152		".nh\n\n.PP\n*improper \\fBnesting* is\\fP bad\n",
153	}
154	doTestsInline(t, tests)
155}
156
157func TestCodeSpan(t *testing.T) {
158	var tests = []string{
159		"`source code`\n",
160		".nh\n\n.PP\n\\fB\\fCsource code\\fR\n",
161
162		"` source code with spaces `\n",
163		".nh\n\n.PP\n\\fB\\fCsource code with spaces\\fR\n",
164
165		"` source code with spaces `not here\n",
166		".nh\n\n.PP\n\\fB\\fCsource code with spaces\\fRnot here\n",
167
168		"a `single marker\n",
169		".nh\n\n.PP\na `single marker\n",
170
171		"a single multi-tick marker with ``` no text\n",
172		".nh\n\n.PP\na single multi\\-tick marker with ``` no text\n",
173
174		"markers with ` ` a space\n",
175		".nh\n\n.PP\nmarkers with  a space\n",
176
177		"`source code` and a `stray\n",
178		".nh\n\n.PP\n\\fB\\fCsource code\\fR and a `stray\n",
179
180		"`source *with* _awkward characters_ in it`\n",
181		".nh\n\n.PP\n\\fB\\fCsource *with* \\_awkward characters\\_ in it\\fR\n",
182
183		"`split over\ntwo lines`\n",
184		".nh\n\n.PP\n\\fB\\fCsplit over\ntwo lines\\fR\n",
185
186		"```multiple ticks``` for the marker\n",
187		".nh\n\n.PP\n\\fB\\fCmultiple ticks\\fR for the marker\n",
188
189		"```multiple ticks `with` ticks inside```\n",
190		".nh\n\n.PP\n\\fB\\fCmultiple ticks `with` ticks inside\\fR\n",
191	}
192	doTestsInline(t, tests)
193}
194
195func TestListLists(t *testing.T) {
196	var tests = []string{
197		"\n\n**[grpc]**\n: Section for gRPC socket listener settings. Contains three properties:\n - **address** (Default: \"/run/containerd/containerd.sock\")\n - **uid** (Default: 0)\n - **gid** (Default: 0)",
198		".nh\n\n.TP\n\\fB[grpc]\\fP\nSection for gRPC socket listener settings. Contains three properties:\n.RS\n.IP \\(bu 2\n\\fBaddress\\fP (Default: \"/run/containerd/containerd.sock\")\n.IP \\(bu 2\n\\fBuid\\fP (Default: 0)\n.IP \\(bu 2\n\\fBgid\\fP (Default: 0)\n\n.RE\n\n",
199	}
200	doTestsParam(t, tests, TestParams{blackfriday.DefinitionLists})
201}
202
203func TestLineBreak(t *testing.T) {
204	var tests = []string{
205		"this line  \nhas a break\n",
206		".nh\n\n.PP\nthis line\n.br\nhas a break\n",
207
208		"this line \ndoes not\n",
209		".nh\n\n.PP\nthis line\ndoes not\n",
210
211		"this line\\\ndoes not\n",
212		".nh\n\n.PP\nthis line\\\\\ndoes not\n",
213
214		"this line\\ \ndoes not\n",
215		".nh\n\n.PP\nthis line\\\\\ndoes not\n",
216
217		"this has an   \nextra space\n",
218		".nh\n\n.PP\nthis has an\n.br\nextra space\n",
219	}
220	doTestsInline(t, tests)
221
222	tests = []string{
223		"this line  \nhas a break\n",
224		".nh\n\n.PP\nthis line\n.br\nhas a break\n",
225
226		"this line \ndoes not\n",
227		".nh\n\n.PP\nthis line\ndoes not\n",
228
229		"this line\\\nhas a break\n",
230		".nh\n\n.PP\nthis line\n.br\nhas a break\n",
231
232		"this line\\ \ndoes not\n",
233		".nh\n\n.PP\nthis line\\\\\ndoes not\n",
234
235		"this has an   \nextra space\n",
236		".nh\n\n.PP\nthis has an\n.br\nextra space\n",
237	}
238	doTestsInlineParam(t, tests, TestParams{
239		extensions: blackfriday.BackslashLineBreak})
240}
241
242func TestTable(t *testing.T) {
243	var tests = []string{
244		`
245| Animal               | Color         |
246| --------------| --- |
247| elephant        | Gray. The elephant is very gray.  |
248| wombat     | No idea.      |
249| zebra        | Sometimes black and sometimes white, depending on the stripe.     |
250| robin | red. |
251`,
252		`.nh
253
254.TS
255allbox;
256l l
257l l .
258\fB\fCAnimal\fR	\fB\fCColor\fR
259elephant	T{
260Gray. The elephant is very gray.
261T}
262wombat	No idea.
263zebra	T{
264Sometimes black and sometimes white, depending on the stripe.
265T}
266robin	red.
267.TE
268`,
269	}
270	doTestsInlineParam(t, tests, TestParams{blackfriday.Tables})
271}
272
273func TestLinks(t *testing.T) {
274	var tests = []string{
275		"See [docs](https://docs.docker.com/) for\nmore",
276		".nh\n\n.PP\nSee docs\n\\[la]https://docs.docker.com/\\[ra] for\nmore\n",
277	}
278	doTestsInline(t, tests)
279}
280
281func execRecoverableTestSuite(t *testing.T, tests []string, params TestParams, suite func(candidate *string)) {
282	// Catch and report panics. This is useful when running 'go test -v' on
283	// the integration server. When developing, though, crash dump is often
284	// preferable, so recovery can be easily turned off with doRecover = false.
285	var candidate string
286	const doRecover = true
287	if doRecover {
288		defer func() {
289			if err := recover(); err != nil {
290				t.Errorf("\npanic while processing [%#v]: %s\n", candidate, err)
291			}
292		}()
293	}
294	suite(&candidate)
295}
296
297func runMarkdown(input string, params TestParams) string {
298	renderer := NewRoffRenderer()
299	return string(blackfriday.Run([]byte(input), blackfriday.WithRenderer(renderer),
300		blackfriday.WithExtensions(params.extensions)))
301}
302
303func doTestsParam(t *testing.T, tests []string, params TestParams) {
304	execRecoverableTestSuite(t, tests, params, func(candidate *string) {
305		for i := 0; i+1 < len(tests); i += 2 {
306			input := tests[i]
307			*candidate = input
308			expected := tests[i+1]
309			actual := runMarkdown(*candidate, params)
310			if actual != expected {
311				t.Errorf("\nInput   [%#v]\nExpected[%#v]\nActual  [%#v]",
312					*candidate, expected, actual)
313			}
314
315			// now test every substring to stress test bounds checking
316			if !testing.Short() {
317				for start := 0; start < len(input); start++ {
318					for end := start + 1; end <= len(input); end++ {
319						*candidate = input[start:end]
320						runMarkdown(*candidate, params)
321					}
322				}
323			}
324		}
325	})
326}
327
328func doTestsInline(t *testing.T, tests []string) {
329	doTestsInlineParam(t, tests, TestParams{})
330}
331
332func doTestsInlineParam(t *testing.T, tests []string, params TestParams) {
333	params.extensions |= blackfriday.Strikethrough
334	doTestsParam(t, tests, params)
335}
336