1 package goldmark_test
2
3 import (
4 "bytes"
5 "os"
6 "strconv"
7 "strings"
8 "testing"
9 "time"
10
11 . "github.com/yuin/goldmark"
12 "github.com/yuin/goldmark/ast"
13 "github.com/yuin/goldmark/parser"
14 "github.com/yuin/goldmark/renderer/html"
15 "github.com/yuin/goldmark/testutil"
16 )
17
18 var testTimeoutMultiplier = 1.0
19
20 func init() {
21 m, err := strconv.ParseFloat(os.Getenv("GOLDMARK_TEST_TIMEOUT_MULTIPLIER"), 64)
22 if err == nil {
23 testTimeoutMultiplier = m
24 }
25 }
26
27 func TestExtras(t *testing.T) {
28 markdown := New(WithRendererOptions(
29 html.WithXHTML(),
30 html.WithUnsafe(),
31 ))
32 testutil.DoTestCaseFile(markdown, "_test/extra.txt", t, testutil.ParseCliCaseArg()...)
33 }
34
35 func TestEndsWithNonSpaceCharacters(t *testing.T) {
36 markdown := New(WithRendererOptions(
37 html.WithXHTML(),
38 html.WithUnsafe(),
39 ))
40 source := []byte("```\na\n```")
41 var b bytes.Buffer
42 err := markdown.Convert(source, &b)
43 if err != nil {
44 t.Error(err.Error())
45 }
46 if b.String() != "<pre><code>a\n</code></pre>\n" {
47 t.Errorf("%s \n---------\n %s", source, b.String())
48 }
49 }
50
51 func TestWindowsNewLine(t *testing.T) {
52 markdown := New(WithRendererOptions(
53 html.WithXHTML(),
54 ))
55 source := []byte("a \r\nb\n")
56 var b bytes.Buffer
57 err := markdown.Convert(source, &b)
58 if err != nil {
59 t.Error(err.Error())
60 }
61 if b.String() != "<p>a<br />\nb</p>\n" {
62 t.Errorf("%s\n---------\n%s", source, b.String())
63 }
64
65 source = []byte("a\\\r\nb\r\n")
66 var b2 bytes.Buffer
67 err = markdown.Convert(source, &b2)
68 if err != nil {
69 t.Error(err.Error())
70 }
71 if b2.String() != "<p>a<br />\nb</p>\n" {
72 t.Errorf("\n%s\n---------\n%s", source, b2.String())
73 }
74 }
75
76 type myIDs struct {
77 }
78
79 func (s *myIDs) Generate(value []byte, kind ast.NodeKind) []byte {
80 return []byte("my-id")
81 }
82
83 func (s *myIDs) Put(value []byte) {
84 }
85
86 func TestAutogeneratedIDs(t *testing.T) {
87 ctx := parser.NewContext(parser.WithIDs(&myIDs{}))
88 markdown := New(WithParserOptions(parser.WithAutoHeadingID()))
89 source := []byte("# Title1\n## Title2")
90 var b bytes.Buffer
91 err := markdown.Convert(source, &b, parser.WithContext(ctx))
92 if err != nil {
93 t.Error(err.Error())
94 }
95 if b.String() != `<h1 id="my-id">Title1</h1>
96 <h2 id="my-id">Title2</h2>
97 ` {
98 t.Errorf("%s\n---------\n%s", source, b.String())
99 }
100 }
101
102 func nowMillis() int64 {
103
104 return time.Now().UnixNano() / 1000000
105 }
106
107 func TestDeepNestedLabelPerformance(t *testing.T) {
108 if testing.Short() {
109 t.Skip("skipping performance test in short mode")
110 }
111 markdown := New(WithRendererOptions(
112 html.WithXHTML(),
113 html.WithUnsafe(),
114 ))
115
116 started := nowMillis()
117 n := 50000
118 source := []byte(strings.Repeat("[", n) + strings.Repeat("]", n))
119 var b bytes.Buffer
120 _ = markdown.Convert(source, &b)
121 finished := nowMillis()
122 if (finished - started) > int64(5000*testTimeoutMultiplier) {
123 t.Error("Parsing deep nested labels took too long")
124 }
125 }
126
127 func TestManyProcessingInstructionPerformance(t *testing.T) {
128 if testing.Short() {
129 t.Skip("skipping performance test in short mode")
130 }
131 markdown := New(WithRendererOptions(
132 html.WithXHTML(),
133 html.WithUnsafe(),
134 ))
135
136 started := nowMillis()
137 n := 50000
138 source := []byte("a " + strings.Repeat("<?", n))
139 var b bytes.Buffer
140 _ = markdown.Convert(source, &b)
141 finished := nowMillis()
142 if (finished - started) > int64(5000*testTimeoutMultiplier) {
143 t.Error("Parsing processing instructions took too long")
144 }
145 }
146
147 func TestManyCDATAPerformance(t *testing.T) {
148 if testing.Short() {
149 t.Skip("skipping performance test in short mode")
150 }
151 markdown := New(WithRendererOptions(
152 html.WithXHTML(),
153 html.WithUnsafe(),
154 ))
155
156 started := nowMillis()
157 n := 50000
158 source := []byte(strings.Repeat("a <![CDATA[", n))
159 var b bytes.Buffer
160 _ = markdown.Convert(source, &b)
161 finished := nowMillis()
162 if (finished - started) > int64(5000*testTimeoutMultiplier) {
163 t.Error("Parsing processing instructions took too long")
164 }
165 }
166
167 func TestManyDeclPerformance(t *testing.T) {
168 if testing.Short() {
169 t.Skip("skipping performance test in short mode")
170 }
171 markdown := New(WithRendererOptions(
172 html.WithXHTML(),
173 html.WithUnsafe(),
174 ))
175
176 started := nowMillis()
177 n := 50000
178 source := []byte(strings.Repeat("a <!A ", n))
179 var b bytes.Buffer
180 _ = markdown.Convert(source, &b)
181 finished := nowMillis()
182 if (finished - started) > int64(5000*testTimeoutMultiplier) {
183 t.Error("Parsing processing instructions took too long")
184 }
185 }
186
187 func TestManyCommentPerformance(t *testing.T) {
188 if testing.Short() {
189 t.Skip("skipping performance test in short mode")
190 }
191 markdown := New(WithRendererOptions(
192 html.WithXHTML(),
193 html.WithUnsafe(),
194 ))
195
196 started := nowMillis()
197 n := 50000
198 source := []byte(strings.Repeat("a <!-- ", n))
199 var b bytes.Buffer
200 _ = markdown.Convert(source, &b)
201 finished := nowMillis()
202 if (finished - started) > int64(5000*testTimeoutMultiplier) {
203 t.Error("Parsing processing instructions took too long")
204 }
205 }
206
207 func TestDangerousURLStringCase(t *testing.T) {
208 markdown := New()
209
210 source := []byte(`[Basic](javascript:alert('Basic'))
211 [CaseInsensitive](JaVaScRiPt:alert('CaseInsensitive'))
212 `)
213 expected := []byte(`<p><a href="">Basic</a>
214 <a href="">CaseInsensitive</a></p>
215 `)
216 var b bytes.Buffer
217 _ = markdown.Convert(source, &b)
218 if !bytes.Equal(expected, b.Bytes()) {
219 t.Error("Dangerous URL should ignore cases:\n" + string(testutil.DiffPretty(expected, b.Bytes())))
220 }
221 }
222
View as plain text