1
2
3
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
19
20 var global embed.FS
21
22
23 var concurrency string
24
25
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
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
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
103 testHiddenDir embed.FS
104
105
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/")
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
146 helloT []T
147
148 helloUint8 []uint8
149
150 helloEUint8 []EmbedUint8
151
152 helloBytes EmbedBytes
153
154 helloString EmbedString
155 )
156
157 type T byte
158 type EmbedUint8 uint8
159 type EmbedBytes []byte
160 type EmbedString string
161
162
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
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
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
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
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
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
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
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
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