1
1
package md_test
2
2
3
3
import (
4
+ "fmt"
4
5
"html"
5
6
"regexp"
6
7
"strings"
@@ -10,6 +11,7 @@ import (
10
11
"github.com/google/go-cmp/cmp"
11
12
. "src.elv.sh/pkg/md"
12
13
"src.elv.sh/pkg/testutil"
14
+ "src.elv.sh/pkg/wcwidth"
13
15
)
14
16
15
17
var supplementalFmtCases = []testCase {
@@ -91,43 +93,38 @@ func TestFmtPreservesHTMLRender(t *testing.T) {
91
93
}
92
94
}
93
95
94
- func TestReflowFmtPreservesHTMLRenderModuleWhitespaces (t * testing.T ) {
95
- testutil .Set (t , & UnescapeHTML , html .UnescapeString )
96
- for _ , tc := range fmtTestCases {
97
- t .Run (tc .testName (), func (t * testing.T ) {
98
- testReflowFmtPreservesHTMLRenderModuloWhitespaces (t , tc .Markdown , 80 )
99
- })
100
- }
101
- }
102
-
103
96
func FuzzFmtPreservesHTMLRender (f * testing.F ) {
104
97
for _ , tc := range fmtTestCases {
105
98
f .Add (tc .Markdown )
106
99
}
107
100
f .Fuzz (testFmtPreservesHTMLRender )
108
101
}
109
102
110
- func FuzzReflowFmtPreservesHTMLRenderModuleWhitespaces (f * testing.F ) {
111
- for _ , tc := range fmtTestCases {
112
- f .Add (tc .Markdown , 20 )
113
- f .Add (tc .Markdown , 80 )
114
- }
115
- f .Fuzz (testReflowFmtPreservesHTMLRenderModuloWhitespaces )
116
- }
117
-
118
103
func testFmtPreservesHTMLRender (t * testing.T , original string ) {
119
- t .Helper ()
120
104
testFmtPreservesHTMLRenderModulo (t , original , 0 , nil )
121
105
}
122
106
107
+ func TestReflowFmtPreservesHTMLRenderModuleWhitespaces (t * testing.T ) {
108
+ testReflowFmt (t , testReflowFmtPreservesHTMLRenderModuloWhitespaces )
109
+ }
110
+
111
+ func FuzzReflowFmtPreservesHTMLRenderModuleWhitespaces (f * testing.F ) {
112
+ fuzzReflowFmt (f , testReflowFmtPreservesHTMLRenderModuloWhitespaces )
113
+ }
114
+
123
115
var (
124
116
paragraph = regexp .MustCompile (`(?s)<p>.*?</p>` )
125
117
whitespaceRun = regexp .MustCompile (`[ \t\n]+` )
126
118
brWithWhitespaces = regexp .MustCompile (`[ \t\n]*<br />[ \t\n]*` )
127
119
)
128
120
129
121
func testReflowFmtPreservesHTMLRenderModuloWhitespaces (t * testing.T , original string , w int ) {
130
- t .Helper ()
122
+ if strings .Contains (original , "<p>" ) {
123
+ t .Skip ("markdown contains <p>" )
124
+ }
125
+ if strings .Contains (original , "</p>" ) {
126
+ t .Skip ("markdown contains </p>" )
127
+ }
131
128
testFmtPreservesHTMLRenderModulo (t , original , w , func (html string ) string {
132
129
// Coalesce whitespaces in each paragraph.
133
130
return paragraph .ReplaceAllStringFunc (html , func (p string ) string {
@@ -141,8 +138,98 @@ func testReflowFmtPreservesHTMLRenderModuloWhitespaces(t *testing.T, original st
141
138
})
142
139
}
143
140
141
+ func TestReflowFmtResultIsUnchangedUnderFmt (t * testing.T ) {
142
+ testReflowFmt (t , testReflowFmtResultIsUnchangedUnderFmt )
143
+ }
144
+
145
+ func FuzzReflowFmtResultIsUnchangedUnderFmt (f * testing.F ) {
146
+ fuzzReflowFmt (f , testReflowFmtResultIsUnchangedUnderFmt )
147
+ }
148
+
149
+ func testReflowFmtResultIsUnchangedUnderFmt (t * testing.T , original string , w int ) {
150
+ reflowed := formatAndSkipIfUnsupported (t , original , w )
151
+ formatted := RenderString (reflowed , & FmtCodec {})
152
+ if reflowed != formatted {
153
+ t .Errorf ("original:\n %s\n reflowed:\n %s\n formatted:\n %s" +
154
+ "markdown diff (-reflowed +formatted):\n %s" ,
155
+ hr + "\n " + original + hr , hr + "\n " + reflowed + hr , hr + "\n " + formatted + hr ,
156
+ cmp .Diff (reflowed , formatted ))
157
+ }
158
+ }
159
+
160
+ func TestReflowFmtResultFitsInWidth (t * testing.T ) {
161
+ testReflowFmt (t , testReflowFmtResultFitsInWidth )
162
+ }
163
+
164
+ func FuzzReflowFmtResultFitsInWidth (f * testing.F ) {
165
+ fuzzReflowFmt (f , testReflowFmtResultFitsInWidth )
166
+ }
167
+
168
+ var (
169
+ // Match all markers that can be written by FmtCodec.
170
+ markersRegexp = regexp .MustCompile (`^ *(?:(?:[-*>]|[0-9]{1,9}[.)]) *)*` )
171
+ linkRegexp = regexp .MustCompile (`\[.*\]\(.*\)` )
172
+ codeSpanRegexp = regexp .MustCompile ("`.*`" )
173
+ )
174
+
175
+ func testReflowFmtResultFitsInWidth (t * testing.T , original string , w int ) {
176
+ if w <= 0 {
177
+ t .Skip ("width <= 0" )
178
+ }
179
+
180
+ var trace TraceCodec
181
+ Render (original , & trace )
182
+ for _ , op := range trace .Ops () {
183
+ switch op .Type {
184
+ case OpHeading , OpCodeBlock , OpHTMLBlock :
185
+ t .Skipf ("input contains unsupported block type %s" , op .Type )
186
+ }
187
+ }
188
+
189
+ reflowed := formatAndSkipIfUnsupported (t , original , w )
190
+
191
+ for _ , line := range strings .Split (reflowed , "\n " ) {
192
+ lineWidth := wcwidth .Of (line )
193
+ if lineWidth <= w {
194
+ continue
195
+ }
196
+ // Strip all markers
197
+ content := line [len (markersRegexp .FindString (line )):]
198
+ // Analyze whether the content is allowed to exceed width
199
+ switch {
200
+ case ! strings .Contains (content , " " ):
201
+ case strings .Contains (content , "<" ):
202
+ case linkRegexp .MatchString (content ):
203
+ case codeSpanRegexp .MatchString (content ):
204
+ default :
205
+ t .Errorf ("line length > %d: %q\n full reflowed:\n %s" ,
206
+ w , line , hr + "\n " + reflowed + hr )
207
+ }
208
+ }
209
+ }
210
+
211
+ var widths = []int {20 , 51 , 80 }
212
+
213
+ func testReflowFmt (t * testing.T , test func (* testing.T , string , int )) {
214
+ for _ , tc := range fmtTestCases {
215
+ for _ , w := range widths {
216
+ t .Run (fmt .Sprintf ("%s/Width %d" , tc .testName (), w ), func (t * testing.T ) {
217
+ test (t , tc .Markdown , w )
218
+ })
219
+ }
220
+ }
221
+ }
222
+
223
+ func fuzzReflowFmt (f * testing.F , test func (* testing.T , string , int )) {
224
+ for _ , tc := range fmtTestCases {
225
+ for _ , w := range widths {
226
+ f .Add (tc .Markdown , w )
227
+ }
228
+ }
229
+ f .Fuzz (test )
230
+ }
231
+
144
232
func testFmtPreservesHTMLRenderModulo (t * testing.T , original string , w int , processHTML func (string ) string ) {
145
- t .Helper ()
146
233
formatted := formatAndSkipIfUnsupported (t , original , w )
147
234
originalRender := RenderString (original , & HTMLCodec {})
148
235
formattedRender := RenderString (formatted , & HTMLCodec {})
@@ -163,7 +250,6 @@ func testFmtPreservesHTMLRenderModulo(t *testing.T, original string, w int, proc
163
250
}
164
251
165
252
func formatAndSkipIfUnsupported (t * testing.T , original string , w int ) string {
166
- t .Helper ()
167
253
if ! utf8 .ValidString (original ) {
168
254
t .Skipf ("input is not valid UTF-8" )
169
255
}
0 commit comments