Source file src/embed/internal/embedtest/embed_test.go

     1  // Copyright 2020 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 embedtest
     6  
     7  import (
     8  	"embed"
     9  	"errors"
    10  	"io"
    11  	"io/fs"
    12  	"reflect"
    13  	"slices"
    14  	"testing"
    15  	"testing/fstest"
    16  )
    17  
    18  //go:embed testdata/h*.txt
    19  //go:embed c*.txt testdata/g*.txt
    20  var global embed.FS
    21  
    22  //go:embed c*txt
    23  var concurrency string
    24  
    25  //go:embed testdata/g*.txt
    26  var glass []byte
    27  
    28  func testFiles(t *testing.T, f embed.FS, name, data string) {
    29  	t.Helper()
    30  	d, err := f.ReadFile(name)
    31  	if err != nil {
    32  		t.Error(err)
    33  		return
    34  	}
    35  	if string(d) != data {
    36  		t.Errorf("read %v = %q, want %q", name, d, data)
    37  	}
    38  }
    39  
    40  func testString(t *testing.T, s, name, data string) {
    41  	t.Helper()
    42  	if s != data {
    43  		t.Errorf("%v = %q, want %q", name, s, data)
    44  	}
    45  }
    46  
    47  func testDir(t *testing.T, f embed.FS, name string, expect ...string) {
    48  	t.Helper()
    49  	dirs, err := f.ReadDir(name)
    50  	if err != nil {
    51  		t.Error(err)
    52  		return
    53  	}
    54  	var names []string
    55  	for _, d := range dirs {
    56  		name := d.Name()
    57  		if d.IsDir() {
    58  			name += "/"
    59  		}
    60  		names = append(names, name)
    61  	}
    62  	if !slices.Equal(names, expect) {
    63  		t.Errorf("readdir %v = %v, want %v", name, names, expect)
    64  	}
    65  }
    66  
    67  // Tests for issue 49514.
    68  var _ = '"'
    69  var _ = '\''
    70  var _ = '🦆'
    71  
    72  func TestGlobal(t *testing.T) {
    73  	testFiles(t, global, "concurrency.txt", "Concurrency is not parallelism.\n")
    74  	testFiles(t, global, "testdata/hello.txt", "hello, world\n")
    75  	testFiles(t, global, "testdata/glass.txt", "I can eat glass and it doesn't hurt me.\n")
    76  
    77  	if err := fstest.TestFS(global, "concurrency.txt", "testdata/hello.txt"); err != nil {
    78  		t.Fatal(err)
    79  	}
    80  
    81  	testString(t, concurrency, "concurrency", "Concurrency is not parallelism.\n")
    82  	testString(t, string(glass), "glass", "I can eat glass and it doesn't hurt me.\n")
    83  }
    84  
    85  //go:embed testdata
    86  var testDirAll embed.FS
    87  
    88  func TestDir(t *testing.T) {
    89  	all := testDirAll
    90  	testFiles(t, all, "testdata/hello.txt", "hello, world\n")
    91  	testFiles(t, all, "testdata/i/i18n.txt", "internationalization\n")
    92  	testFiles(t, all, "testdata/i/j/k/k8s.txt", "kubernetes\n")
    93  	testFiles(t, all, "testdata/ken.txt", "If a program is too slow, it must have a loop.\n")
    94  
    95  	testDir(t, all, ".", "testdata/")
    96  	testDir(t, all, "testdata/i", "i18n.txt", "j/")
    97  	testDir(t, all, "testdata/i/j", "k/")
    98  	testDir(t, all, "testdata/i/j/k", "k8s.txt")
    99  }
   100  
   101  var (
   102  	//go:embed testdata
   103  	testHiddenDir embed.FS
   104  
   105  	//go:embed testdata/*
   106  	testHiddenStar embed.FS
   107  )
   108  
   109  func TestHidden(t *testing.T) {
   110  	dir := testHiddenDir
   111  	star := testHiddenStar
   112  
   113  	t.Logf("//go:embed testdata")
   114  
   115  	testDir(t, dir, "testdata",
   116  		"-not-hidden/", "ascii.txt", "glass.txt", "hello.txt", "i/", "ken.txt")
   117  
   118  	t.Logf("//go:embed testdata/*")
   119  
   120  	testDir(t, star, "testdata",
   121  		"-not-hidden/", ".hidden/", "_hidden/", "ascii.txt", "glass.txt", "hello.txt", "i/", "ken.txt")
   122  
   123  	testDir(t, star, "testdata/.hidden",
   124  		"fortune.txt", "more/") // but not .more or _more
   125  }
   126  
   127  func TestUninitialized(t *testing.T) {
   128  	var uninitialized embed.FS
   129  	testDir(t, uninitialized, ".")
   130  	f, err := uninitialized.Open(".")
   131  	if err != nil {
   132  		t.Fatal(err)
   133  	}
   134  	defer f.Close()
   135  	fi, err := f.Stat()
   136  	if err != nil {
   137  		t.Fatal(err)
   138  	}
   139  	if !fi.IsDir() {
   140  		t.Errorf("in uninitialized embed.FS, . is not a directory")
   141  	}
   142  }
   143  
   144  var (
   145  	//go:embed "testdata/hello.txt"
   146  	helloT []T
   147  	//go:embed "testdata/hello.txt"
   148  	helloUint8 []uint8
   149  	//go:embed "testdata/hello.txt"
   150  	helloEUint8 []EmbedUint8
   151  	//go:embed "testdata/hello.txt"
   152  	helloBytes EmbedBytes
   153  	//go:embed "testdata/hello.txt"
   154  	helloString EmbedString
   155  )
   156  
   157  type T byte
   158  type EmbedUint8 uint8
   159  type EmbedBytes []byte
   160  type EmbedString string
   161  
   162  // golang.org/issue/47735
   163  func TestAliases(t *testing.T) {
   164  	all := testDirAll
   165  	want, e := all.ReadFile("testdata/hello.txt")
   166  	if e != nil {
   167  		t.Fatal("ReadFile:", e)
   168  	}
   169  	check := func(g any) {
   170  		got := reflect.ValueOf(g)
   171  		for i := 0; i < got.Len(); i++ {
   172  			if byte(got.Index(i).Uint()) != want[i] {
   173  				t.Fatalf("got %v want %v", got.Bytes(), want)
   174  			}
   175  		}
   176  	}
   177  	check(helloT)
   178  	check(helloUint8)
   179  	check(helloEUint8)
   180  	check(helloBytes)
   181  	check(helloString)
   182  }
   183  
   184  func TestOffset(t *testing.T) {
   185  	file, err := testDirAll.Open("testdata/hello.txt")
   186  	if err != nil {
   187  		t.Fatal("Open:", err)
   188  	}
   189  
   190  	want := "hello, world\n"
   191  
   192  	// Read the entire file.
   193  	got := make([]byte, len(want))
   194  	n, err := file.Read(got)
   195  	if err != nil {
   196  		t.Fatal("Read:", err)
   197  	}
   198  	if n != len(want) {
   199  		t.Fatal("Read:", n)
   200  	}
   201  	if string(got) != want {
   202  		t.Fatalf("Read: %q", got)
   203  	}
   204  
   205  	// Try to read one byte; confirm we're at the EOF.
   206  	var buf [1]byte
   207  	n, err = file.Read(buf[:])
   208  	if err != io.EOF {
   209  		t.Fatal("Read:", err)
   210  	}
   211  	if n != 0 {
   212  		t.Fatal("Read:", n)
   213  	}
   214  
   215  	// Use seek to get the offset at the EOF.
   216  	seeker := file.(io.Seeker)
   217  	off, err := seeker.Seek(0, io.SeekCurrent)
   218  	if err != nil {
   219  		t.Fatal("Seek:", err)
   220  	}
   221  	if off != int64(len(want)) {
   222  		t.Fatal("Seek:", off)
   223  	}
   224  
   225  	// Use Seek with an invalid whence.
   226  	_, err = seeker.Seek(0, io.SeekEnd+5)
   227  	if err == nil {
   228  		t.Fatal("Seek: expected error for invalid whence")
   229  	}
   230  	if !errors.Is(err, fs.ErrInvalid) {
   231  		t.Fatalf("Seek: expected fs.ErrInvalid, got %v", err)
   232  	}
   233  
   234  	// Use ReadAt to read the entire file, ignoring the offset.
   235  	at := file.(io.ReaderAt)
   236  	got = make([]byte, len(want))
   237  	n, err = at.ReadAt(got, 0)
   238  	if err != nil {
   239  		t.Fatal("ReadAt:", err)
   240  	}
   241  	if n != len(want) {
   242  		t.Fatalf("ReadAt: got %d bytes, want %d bytes", n, len(want))
   243  	}
   244  	if string(got) != want {
   245  		t.Fatalf("ReadAt: got %q, want %q", got, want)
   246  	}
   247  
   248  	// Use ReadAt with non-zero offset.
   249  	off = int64(7)
   250  	part := want[off:]
   251  	got = make([]byte, len(part))
   252  	n, err = at.ReadAt(got, off)
   253  	if err != nil {
   254  		t.Fatal("ReadAt:", err)
   255  	}
   256  	if n != len(part) {
   257  		t.Fatalf("ReadAt: got %d bytes, want %d bytes", n, len(part))
   258  	}
   259  	if string(got) != part {
   260  		t.Fatalf("ReadAt: got %q, want %q", got, part)
   261  	}
   262  
   263  	// Use ReadAt with an offset at the end of the file.
   264  	off = int64(len(want))
   265  	got = make([]byte, 1)
   266  	n, err = at.ReadAt(got, off)
   267  	if err != io.EOF {
   268  		t.Fatalf("ReadAt: expected io.EOF for offset at end of file")
   269  	}
   270  	if n != 0 {
   271  		t.Fatalf("ReadAt: got %d bytes, want 0 bytes", n)
   272  	}
   273  
   274  	// Use ReadAt with an offset beyond the end of the file.
   275  	off = int64(len(want) + 1)
   276  	got = make([]byte, 1)
   277  	n, err = at.ReadAt(got, off)
   278  	if err != io.EOF {
   279  		t.Fatalf("ReadAt: expected io.EOF for offset beyond end of file")
   280  	}
   281  	if n != 0 {
   282  		t.Fatalf("ReadAt: got %d bytes, want 0 bytes", n)
   283  	}
   284  }
   285  

View as plain text