Source file
src/mime/type_test.go
Documentation: mime
1
2
3
4
5 package mime
6
7 import (
8 "internal/asan"
9 "slices"
10 "strings"
11 "sync"
12 "testing"
13 )
14
15 func setMimeInit(fn func()) (cleanup func()) {
16 once = sync.Once{}
17 testInitMime = fn
18 return func() {
19 testInitMime = nil
20 once = sync.Once{}
21 }
22 }
23
24 func clearMimeTypes() {
25 setMimeTypes(map[string]string{}, map[string]string{})
26 }
27
28 func setType(ext, typ string) {
29 if !strings.HasPrefix(ext, ".") {
30 panic("missing leading dot")
31 }
32 if err := setExtensionType(ext, typ); err != nil {
33 panic("bad test data: " + err.Error())
34 }
35 }
36
37 func TestTypeByExtension(t *testing.T) {
38 once = sync.Once{}
39
40
41
42
43
44
45 typeTests := initMimeForTests()
46
47 for ext, want := range typeTests {
48 val := TypeByExtension(ext)
49 if val != want {
50 t.Errorf("TypeByExtension(%q) = %q, want %q", ext, val, want)
51 }
52 }
53 }
54
55 func TestTypeByExtension_LocalData(t *testing.T) {
56 cleanup := setMimeInit(func() {
57 clearMimeTypes()
58 setType(".foo", "x/foo")
59 setType(".bar", "x/bar")
60 setType(".Bar", "x/bar; capital=1")
61 })
62 defer cleanup()
63
64 tests := map[string]string{
65 ".foo": "x/foo",
66 ".bar": "x/bar",
67 ".Bar": "x/bar; capital=1",
68 ".sdlkfjskdlfj": "",
69 ".t1": "",
70 }
71
72 for ext, want := range tests {
73 val := TypeByExtension(ext)
74 if val != want {
75 t.Errorf("TypeByExtension(%q) = %q, want %q", ext, val, want)
76 }
77 }
78 }
79
80 func TestTypeByExtensionCase(t *testing.T) {
81 const custom = "test/test; charset=iso-8859-1"
82 const caps = "test/test; WAS=ALLCAPS"
83
84 cleanup := setMimeInit(func() {
85 clearMimeTypes()
86 setType(".TEST", caps)
87 setType(".tesT", custom)
88 })
89 defer cleanup()
90
91
92 if got := TypeByExtension(".tesT"); got != custom {
93 t.Fatalf("for .tesT, got %q; want %q", got, custom)
94 }
95 if got := TypeByExtension(".TEST"); got != caps {
96 t.Fatalf("for .TEST, got %q; want %s", got, caps)
97 }
98
99
100 if got := TypeByExtension(".TesT"); got != custom {
101 t.Fatalf("for .TesT, got %q; want %q", got, custom)
102 }
103 }
104
105
106
107
108
109 func TestAddExtensionType_TextMIMEWithParamsDefaultCharset(t *testing.T) {
110 cleanup := setMimeInit(func() {
111 clearMimeTypes()
112 })
113 defer cleanup()
114
115 const ext = ".regtxt"
116 const in = "text/x-reg; myparam=myvalue"
117 if err := AddExtensionType(ext, in); err != nil {
118 t.Fatal(err)
119 }
120 got := TypeByExtension(ext)
121 if got == "" {
122 t.Fatal("TypeByExtension: got empty string; want non-empty formatted text/* with default charset")
123 }
124 const wantSubstr = "charset=utf-8"
125 if !strings.Contains(got, wantSubstr) {
126 t.Fatalf("TypeByExtension(%q) = %q; want substring %q", ext, got, wantSubstr)
127 }
128 if !strings.HasPrefix(got, "text/x-reg") {
129 t.Fatalf("TypeByExtension(%q) = %q; want prefix text/x-reg", ext, got)
130 }
131 }
132
133 func TestExtensionsByType(t *testing.T) {
134 cleanup := setMimeInit(func() {
135 clearMimeTypes()
136 setType(".gif", "image/gif")
137 setType(".a", "foo/letter")
138 setType(".b", "foo/letter")
139 setType(".B", "foo/letter")
140 setType(".PNG", "image/png")
141 })
142 defer cleanup()
143
144 tests := []struct {
145 typ string
146 want []string
147 wantErr string
148 }{
149 {typ: "image/gif", want: []string{".gif"}},
150 {typ: "image/png", want: []string{".png"}},
151 {typ: "foo/letter", want: []string{".a", ".b"}},
152 {typ: "x/unknown", want: nil},
153 }
154
155 for _, tt := range tests {
156 got, err := ExtensionsByType(tt.typ)
157 if err != nil && tt.wantErr != "" && strings.Contains(err.Error(), tt.wantErr) {
158 continue
159 }
160 if err != nil {
161 t.Errorf("ExtensionsByType(%q) error: %v", tt.typ, err)
162 continue
163 }
164 if tt.wantErr != "" {
165 t.Errorf("ExtensionsByType(%q) = %q, %v; want error substring %q", tt.typ, got, err, tt.wantErr)
166 continue
167 }
168 if !slices.Equal(got, tt.want) {
169 t.Errorf("ExtensionsByType(%q) = %q; want %q", tt.typ, got, tt.want)
170 }
171 }
172 }
173
174 func TestLookupMallocs(t *testing.T) {
175 if asan.Enabled {
176 t.Skip("test allocates more with -asan; see #70079")
177 }
178 n := testing.AllocsPerRun(10000, func() {
179 TypeByExtension(".html")
180 TypeByExtension(".HtML")
181 })
182 if n > 0 {
183 t.Errorf("allocs = %v; want 0", n)
184 }
185 }
186
187 func BenchmarkTypeByExtension(b *testing.B) {
188 initMime()
189 b.ResetTimer()
190
191 for _, ext := range []string{
192 ".html",
193 ".HTML",
194 ".unused",
195 } {
196 b.Run(ext, func(b *testing.B) {
197 b.RunParallel(func(pb *testing.PB) {
198 for pb.Next() {
199 TypeByExtension(ext)
200 }
201 })
202 })
203 }
204 }
205
206 func BenchmarkExtensionsByType(b *testing.B) {
207 initMime()
208 b.ResetTimer()
209
210 for _, typ := range []string{
211 "text/html",
212 "text/html; charset=utf-8",
213 "application/octet-stream",
214 } {
215 b.Run(typ, func(b *testing.B) {
216 b.RunParallel(func(pb *testing.PB) {
217 for pb.Next() {
218 if _, err := ExtensionsByType(typ); err != nil {
219 b.Fatal(err)
220 }
221 }
222 })
223 })
224 }
225 }
226
227 func TestExtensionsByType2(t *testing.T) {
228 cleanup := setMimeInit(func() {
229 clearMimeTypes()
230
231 setMimeTypes(builtinTypesLower, builtinTypesLower)
232 })
233 defer cleanup()
234
235 tests := []struct {
236 typ string
237 want []string
238 }{
239 {typ: "application/postscript", want: []string{".ai", ".eps", ".ps"}},
240 {typ: "application/vnd.android.package-archive", want: []string{".apk"}},
241 {typ: "image/apng", want: []string{".apng"}},
242 {typ: "image/avif", want: []string{".avif"}},
243 {typ: "application/octet-stream", want: []string{".bin", ".com", ".exe"}},
244 {typ: "image/bmp", want: []string{".bmp"}},
245 {typ: "text/css; charset=utf-8", want: []string{".css"}},
246 {typ: "text/csv; charset=utf-8", want: []string{".csv"}},
247 {typ: "application/msword", want: []string{".doc"}},
248 {typ: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", want: []string{".docx"}},
249 {typ: "text/html; charset=utf-8", want: []string{".ehtml", ".htm", ".html", ".shtml"}},
250 {typ: "message/rfc822", want: []string{".eml"}},
251 {typ: "audio/flac", want: []string{".flac"}},
252 {typ: "image/gif", want: []string{".gif"}},
253 {typ: "application/gzip", want: []string{".gz"}},
254 {typ: "image/vnd.microsoft.icon", want: []string{".ico"}},
255 {typ: "text/calendar; charset=utf-8", want: []string{".ics"}},
256 {typ: "image/jpeg", want: []string{".jfif", ".jpeg", ".jpg", ".pjp", ".pjpeg"}},
257 {typ: "text/javascript; charset=utf-8", want: []string{".js", ".mjs"}},
258 {typ: "application/json", want: []string{".json"}},
259 {typ: "audio/mp4", want: []string{".m4a"}},
260 {typ: "audio/mpeg", want: []string{".mp3"}},
261 {typ: "video/mp4", want: []string{".mp4"}},
262 {typ: "audio/ogg", want: []string{".oga", ".ogg", ".opus"}},
263 {typ: "video/ogg", want: []string{".ogv"}},
264 {typ: "application/pdf", want: []string{".pdf"}},
265 {typ: "image/png", want: []string{".png"}},
266 {typ: "application/vnd.ms-powerpoint", want: []string{".ppt"}},
267 {typ: "application/vnd.openxmlformats-officedocument.presentationml.presentation", want: []string{".pptx"}},
268 {typ: "application/rdf+xml", want: []string{".rdf"}},
269 {typ: "application/rtf", want: []string{".rtf"}},
270 {typ: "image/svg+xml", want: []string{".svg"}},
271 {typ: "text/plain; charset=utf-8", want: []string{".text", ".txt"}},
272 {typ: "image/tiff", want: []string{".tif", ".tiff"}},
273 {typ: "text/vtt; charset=utf-8", want: []string{".vtt"}},
274 {typ: "application/wasm", want: []string{".wasm"}},
275 {typ: "audio/wav", want: []string{".wav"}},
276 {typ: "audio/webm", want: []string{".weba"}},
277 {typ: "video/webm", want: []string{".webm"}},
278 {typ: "image/webp", want: []string{".webp"}},
279 {typ: "text/xml; charset=utf-8", want: []string{".xbl", ".xml", ".xsl"}},
280 {typ: "image/x-xbitmap", want: []string{".xbm"}},
281 {typ: "application/xhtml+xml", want: []string{".xht", ".xhtml"}},
282 {typ: "application/vnd.ms-excel", want: []string{".xls"}},
283 {typ: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", want: []string{".xlsx"}},
284 {typ: "application/zip", want: []string{".zip"}},
285 }
286
287 for _, tt := range tests {
288 got, err := ExtensionsByType(tt.typ)
289 if err != nil {
290 t.Errorf("ExtensionsByType(%q): %v", tt.typ, err)
291 continue
292 }
293 if !slices.Equal(got, tt.want) {
294 t.Errorf("ExtensionsByType(%q) = %q; want %q", tt.typ, got, tt.want)
295 }
296 }
297 }
298
View as plain text