Source file
src/mime/type_test.go
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 func TestExtensionsByType(t *testing.T) {
106 cleanup := setMimeInit(func() {
107 clearMimeTypes()
108 setType(".gif", "image/gif")
109 setType(".a", "foo/letter")
110 setType(".b", "foo/letter")
111 setType(".B", "foo/letter")
112 setType(".PNG", "image/png")
113 })
114 defer cleanup()
115
116 tests := []struct {
117 typ string
118 want []string
119 wantErr string
120 }{
121 {typ: "image/gif", want: []string{".gif"}},
122 {typ: "image/png", want: []string{".png"}},
123 {typ: "foo/letter", want: []string{".a", ".b"}},
124 {typ: "x/unknown", want: nil},
125 }
126
127 for _, tt := range tests {
128 got, err := ExtensionsByType(tt.typ)
129 if err != nil && tt.wantErr != "" && strings.Contains(err.Error(), tt.wantErr) {
130 continue
131 }
132 if err != nil {
133 t.Errorf("ExtensionsByType(%q) error: %v", tt.typ, err)
134 continue
135 }
136 if tt.wantErr != "" {
137 t.Errorf("ExtensionsByType(%q) = %q, %v; want error substring %q", tt.typ, got, err, tt.wantErr)
138 continue
139 }
140 if !slices.Equal(got, tt.want) {
141 t.Errorf("ExtensionsByType(%q) = %q; want %q", tt.typ, got, tt.want)
142 }
143 }
144 }
145
146 func TestLookupMallocs(t *testing.T) {
147 if asan.Enabled {
148 t.Skip("test allocates more with -asan; see #70079")
149 }
150 n := testing.AllocsPerRun(10000, func() {
151 TypeByExtension(".html")
152 TypeByExtension(".HtML")
153 })
154 if n > 0 {
155 t.Errorf("allocs = %v; want 0", n)
156 }
157 }
158
159 func BenchmarkTypeByExtension(b *testing.B) {
160 initMime()
161 b.ResetTimer()
162
163 for _, ext := range []string{
164 ".html",
165 ".HTML",
166 ".unused",
167 } {
168 b.Run(ext, func(b *testing.B) {
169 b.RunParallel(func(pb *testing.PB) {
170 for pb.Next() {
171 TypeByExtension(ext)
172 }
173 })
174 })
175 }
176 }
177
178 func BenchmarkExtensionsByType(b *testing.B) {
179 initMime()
180 b.ResetTimer()
181
182 for _, typ := range []string{
183 "text/html",
184 "text/html; charset=utf-8",
185 "application/octet-stream",
186 } {
187 b.Run(typ, func(b *testing.B) {
188 b.RunParallel(func(pb *testing.PB) {
189 for pb.Next() {
190 if _, err := ExtensionsByType(typ); err != nil {
191 b.Fatal(err)
192 }
193 }
194 })
195 })
196 }
197 }
198
199 func TestExtensionsByType2(t *testing.T) {
200 cleanup := setMimeInit(func() {
201 clearMimeTypes()
202
203 setMimeTypes(builtinTypesLower, builtinTypesLower)
204 })
205 defer cleanup()
206
207 tests := []struct {
208 typ string
209 want []string
210 }{
211 {typ: "application/postscript", want: []string{".ai", ".eps", ".ps"}},
212 {typ: "application/vnd.android.package-archive", want: []string{".apk"}},
213 {typ: "image/apng", want: []string{".apng"}},
214 {typ: "image/avif", want: []string{".avif"}},
215 {typ: "application/octet-stream", want: []string{".bin", ".com", ".exe"}},
216 {typ: "image/bmp", want: []string{".bmp"}},
217 {typ: "text/css; charset=utf-8", want: []string{".css"}},
218 {typ: "text/csv; charset=utf-8", want: []string{".csv"}},
219 {typ: "application/msword", want: []string{".doc"}},
220 {typ: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", want: []string{".docx"}},
221 {typ: "text/html; charset=utf-8", want: []string{".ehtml", ".htm", ".html", ".shtml"}},
222 {typ: "message/rfc822", want: []string{".eml"}},
223 {typ: "audio/flac", want: []string{".flac"}},
224 {typ: "image/gif", want: []string{".gif"}},
225 {typ: "application/gzip", want: []string{".gz"}},
226 {typ: "image/vnd.microsoft.icon", want: []string{".ico"}},
227 {typ: "text/calendar; charset=utf-8", want: []string{".ics"}},
228 {typ: "image/jpeg", want: []string{".jfif", ".jpeg", ".jpg", ".pjp", ".pjpeg"}},
229 {typ: "text/javascript; charset=utf-8", want: []string{".js", ".mjs"}},
230 {typ: "application/json", want: []string{".json"}},
231 {typ: "audio/mp4", want: []string{".m4a"}},
232 {typ: "audio/mpeg", want: []string{".mp3"}},
233 {typ: "video/mp4", want: []string{".mp4"}},
234 {typ: "audio/ogg", want: []string{".oga", ".ogg", ".opus"}},
235 {typ: "video/ogg", want: []string{".ogv"}},
236 {typ: "application/pdf", want: []string{".pdf"}},
237 {typ: "image/png", want: []string{".png"}},
238 {typ: "application/vnd.ms-powerpoint", want: []string{".ppt"}},
239 {typ: "application/vnd.openxmlformats-officedocument.presentationml.presentation", want: []string{".pptx"}},
240 {typ: "application/rdf+xml", want: []string{".rdf"}},
241 {typ: "application/rtf", want: []string{".rtf"}},
242 {typ: "image/svg+xml", want: []string{".svg"}},
243 {typ: "text/plain; charset=utf-8", want: []string{".text", ".txt"}},
244 {typ: "image/tiff", want: []string{".tif", ".tiff"}},
245 {typ: "text/vtt; charset=utf-8", want: []string{".vtt"}},
246 {typ: "application/wasm", want: []string{".wasm"}},
247 {typ: "audio/wav", want: []string{".wav"}},
248 {typ: "audio/webm", want: []string{".webm"}},
249 {typ: "image/webp", want: []string{".webp"}},
250 {typ: "text/xml; charset=utf-8", want: []string{".xbl", ".xml", ".xsl"}},
251 {typ: "image/x-xbitmap", want: []string{".xbm"}},
252 {typ: "application/xhtml+xml", want: []string{".xht", ".xhtml"}},
253 {typ: "application/vnd.ms-excel", want: []string{".xls"}},
254 {typ: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", want: []string{".xlsx"}},
255 {typ: "application/zip", want: []string{".zip"}},
256 }
257
258 for _, tt := range tests {
259 got, err := ExtensionsByType(tt.typ)
260 if err != nil {
261 t.Errorf("ExtensionsByType(%q): %v", tt.typ, err)
262 continue
263 }
264 if !slices.Equal(got, tt.want) {
265 t.Errorf("ExtensionsByType(%q) = %q; want %q", tt.typ, got, tt.want)
266 }
267 }
268 }
269
View as plain text