Source file src/mime/type_test.go

     1  // Copyright 2010 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package mime
     6  
     7  import (
     8  	"slices"
     9  	"strings"
    10  	"sync"
    11  	"testing"
    12  )
    13  
    14  func setMimeInit(fn func()) (cleanup func()) {
    15  	once = sync.Once{}
    16  	testInitMime = fn
    17  	return func() {
    18  		testInitMime = nil
    19  		once = sync.Once{}
    20  	}
    21  }
    22  
    23  func clearMimeTypes() {
    24  	setMimeTypes(map[string]string{}, map[string]string{})
    25  }
    26  
    27  func setType(ext, typ string) {
    28  	if !strings.HasPrefix(ext, ".") {
    29  		panic("missing leading dot")
    30  	}
    31  	if err := setExtensionType(ext, typ); err != nil {
    32  		panic("bad test data: " + err.Error())
    33  	}
    34  }
    35  
    36  func TestTypeByExtension(t *testing.T) {
    37  	once = sync.Once{}
    38  	// initMimeForTests returns the platform-specific extension =>
    39  	// type tests. On Unix and Plan 9, this also tests the parsing
    40  	// of MIME text files (in testdata/*). On Windows, we test the
    41  	// real registry on the machine and assume that ".png" exists
    42  	// there, which empirically it always has, for all versions of
    43  	// Windows.
    44  	typeTests := initMimeForTests()
    45  
    46  	for ext, want := range typeTests {
    47  		val := TypeByExtension(ext)
    48  		if val != want {
    49  			t.Errorf("TypeByExtension(%q) = %q, want %q", ext, val, want)
    50  		}
    51  	}
    52  }
    53  
    54  func TestTypeByExtension_LocalData(t *testing.T) {
    55  	cleanup := setMimeInit(func() {
    56  		clearMimeTypes()
    57  		setType(".foo", "x/foo")
    58  		setType(".bar", "x/bar")
    59  		setType(".Bar", "x/bar; capital=1")
    60  	})
    61  	defer cleanup()
    62  
    63  	tests := map[string]string{
    64  		".foo":          "x/foo",
    65  		".bar":          "x/bar",
    66  		".Bar":          "x/bar; capital=1",
    67  		".sdlkfjskdlfj": "",
    68  		".t1":           "", // testdata shouldn't be used
    69  	}
    70  
    71  	for ext, want := range tests {
    72  		val := TypeByExtension(ext)
    73  		if val != want {
    74  			t.Errorf("TypeByExtension(%q) = %q, want %q", ext, val, want)
    75  		}
    76  	}
    77  }
    78  
    79  func TestTypeByExtensionCase(t *testing.T) {
    80  	const custom = "test/test; charset=iso-8859-1"
    81  	const caps = "test/test; WAS=ALLCAPS"
    82  
    83  	cleanup := setMimeInit(func() {
    84  		clearMimeTypes()
    85  		setType(".TEST", caps)
    86  		setType(".tesT", custom)
    87  	})
    88  	defer cleanup()
    89  
    90  	// case-sensitive lookup
    91  	if got := TypeByExtension(".tesT"); got != custom {
    92  		t.Fatalf("for .tesT, got %q; want %q", got, custom)
    93  	}
    94  	if got := TypeByExtension(".TEST"); got != caps {
    95  		t.Fatalf("for .TEST, got %q; want %s", got, caps)
    96  	}
    97  
    98  	// case-insensitive
    99  	if got := TypeByExtension(".TesT"); got != custom {
   100  		t.Fatalf("for .TesT, got %q; want %q", got, custom)
   101  	}
   102  }
   103  
   104  func TestExtensionsByType(t *testing.T) {
   105  	cleanup := setMimeInit(func() {
   106  		clearMimeTypes()
   107  		setType(".gif", "image/gif")
   108  		setType(".a", "foo/letter")
   109  		setType(".b", "foo/letter")
   110  		setType(".B", "foo/letter")
   111  		setType(".PNG", "image/png")
   112  	})
   113  	defer cleanup()
   114  
   115  	tests := []struct {
   116  		typ     string
   117  		want    []string
   118  		wantErr string
   119  	}{
   120  		{typ: "image/gif", want: []string{".gif"}},
   121  		{typ: "image/png", want: []string{".png"}}, // lowercase
   122  		{typ: "foo/letter", want: []string{".a", ".b"}},
   123  		{typ: "x/unknown", want: nil},
   124  	}
   125  
   126  	for _, tt := range tests {
   127  		got, err := ExtensionsByType(tt.typ)
   128  		if err != nil && tt.wantErr != "" && strings.Contains(err.Error(), tt.wantErr) {
   129  			continue
   130  		}
   131  		if err != nil {
   132  			t.Errorf("ExtensionsByType(%q) error: %v", tt.typ, err)
   133  			continue
   134  		}
   135  		if tt.wantErr != "" {
   136  			t.Errorf("ExtensionsByType(%q) = %q, %v; want error substring %q", tt.typ, got, err, tt.wantErr)
   137  			continue
   138  		}
   139  		if !slices.Equal(got, tt.want) {
   140  			t.Errorf("ExtensionsByType(%q) = %q; want %q", tt.typ, got, tt.want)
   141  		}
   142  	}
   143  }
   144  
   145  func TestLookupMallocs(t *testing.T) {
   146  	n := testing.AllocsPerRun(10000, func() {
   147  		TypeByExtension(".html")
   148  		TypeByExtension(".HtML")
   149  	})
   150  	if n > 0 {
   151  		t.Errorf("allocs = %v; want 0", n)
   152  	}
   153  }
   154  
   155  func BenchmarkTypeByExtension(b *testing.B) {
   156  	initMime()
   157  	b.ResetTimer()
   158  
   159  	for _, ext := range []string{
   160  		".html",
   161  		".HTML",
   162  		".unused",
   163  	} {
   164  		b.Run(ext, func(b *testing.B) {
   165  			b.RunParallel(func(pb *testing.PB) {
   166  				for pb.Next() {
   167  					TypeByExtension(ext)
   168  				}
   169  			})
   170  		})
   171  	}
   172  }
   173  
   174  func BenchmarkExtensionsByType(b *testing.B) {
   175  	initMime()
   176  	b.ResetTimer()
   177  
   178  	for _, typ := range []string{
   179  		"text/html",
   180  		"text/html; charset=utf-8",
   181  		"application/octet-stream",
   182  	} {
   183  		b.Run(typ, func(b *testing.B) {
   184  			b.RunParallel(func(pb *testing.PB) {
   185  				for pb.Next() {
   186  					if _, err := ExtensionsByType(typ); err != nil {
   187  						b.Fatal(err)
   188  					}
   189  				}
   190  			})
   191  		})
   192  	}
   193  }
   194  
   195  func TestExtensionsByType2(t *testing.T) {
   196  	cleanup := setMimeInit(func() {
   197  		clearMimeTypes()
   198  		// Initialize built-in types like in type.go before osInitMime.
   199  		setMimeTypes(builtinTypesLower, builtinTypesLower)
   200  	})
   201  	defer cleanup()
   202  
   203  	tests := []struct {
   204  		typ  string
   205  		want []string
   206  	}{
   207  		{typ: "image/jpeg", want: []string{".jpeg", ".jpg"}},
   208  	}
   209  
   210  	for _, tt := range tests {
   211  		got, err := ExtensionsByType(tt.typ)
   212  		if err != nil {
   213  			t.Errorf("ExtensionsByType(%q): %v", tt.typ, err)
   214  			continue
   215  		}
   216  		if !slices.Equal(got, tt.want) {
   217  			t.Errorf("ExtensionsByType(%q) = %q; want %q", tt.typ, got, tt.want)
   218  		}
   219  	}
   220  }
   221  

View as plain text