| // Copyright 2024 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package txtar_test |
| |
| import ( |
| "io/fs" |
| "strings" |
| "testing" |
| "testing/fstest" |
| |
| "golang.org/x/tools/txtar" |
| ) |
| |
| func TestFS(t *testing.T) { |
| var fstestcases = []struct { |
| name, input, files string |
| }{ |
| { |
| name: "empty", |
| input: ``, |
| files: "", |
| }, |
| { |
| name: "one", |
| input: ` |
| -- one.txt -- |
| one |
| `, |
| files: "one.txt", |
| }, |
| { |
| name: "two", |
| input: ` |
| -- one.txt -- |
| one |
| -- two.txt -- |
| two |
| `, |
| files: "one.txt two.txt", |
| }, |
| { |
| name: "subdirectories", |
| input: ` |
| -- one.txt -- |
| one |
| -- 2/two.txt -- |
| two |
| -- 2/3/three.txt -- |
| three |
| -- 4/four.txt -- |
| four |
| `, |
| files: "one.txt 2/two.txt 2/3/three.txt 4/four.txt", |
| }, |
| } |
| |
| for _, tc := range fstestcases { |
| t.Run(tc.name, func(t *testing.T) { |
| a := txtar.Parse([]byte(tc.input)) |
| fsys, err := txtar.FS(a) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| files := strings.Fields(tc.files) |
| if err := fstest.TestFS(fsys, files...); err != nil { |
| t.Fatal(err) |
| } |
| |
| for _, f := range a.Files { |
| b, err := fs.ReadFile(fsys, f.Name) |
| if err != nil { |
| t.Errorf("ReadFile(%q) failed with error: %v", f.Name, err) |
| } |
| if got, want := string(b), string(f.Data); got != want { |
| t.Errorf("ReadFile(%q) = %q; want %q", f.Name, got, want) |
| } |
| } |
| }) |
| } |
| } |
| |
| func TestInvalid(t *testing.T) { |
| invalidtestcases := []struct { |
| name, want string |
| input string |
| }{ |
| {"unclean file names", "invalid path", ` |
| -- 1/../one.txt -- |
| one |
| -- 2/sub/../two.txt -- |
| two |
| `}, |
| {"duplicate name", `cannot create fs.FS from txtar.Archive: duplicate path "1/2/one.txt"`, ` |
| -- 1/2/one.txt -- |
| one |
| -- 1/2/one.txt -- |
| two |
| `}, |
| {"file conflicts with directory", `duplicate path "1/2"`, ` |
| -- 1/2 -- |
| one |
| -- 1/2/one.txt -- |
| two |
| `}, |
| } |
| |
| for _, tc := range invalidtestcases { |
| t.Run(tc.name, func(t *testing.T) { |
| a := txtar.Parse([]byte(tc.input)) |
| _, err := txtar.FS(a) |
| if err == nil { |
| t.Fatal("txtar.FS(...) succeeded; expected an error") |
| } |
| if got := err.Error(); !strings.Contains(got, tc.want) || tc.want == "" { |
| t.Errorf("txtar.FS(...) got error %q; want %q", got, tc.want) |
| } |
| }) |
| } |
| } |
| |
| func TestModified(t *testing.T) { |
| const input = ` |
| -- one.txt -- |
| one |
| ` |
| for _, mod := range []func(a *txtar.Archive){ |
| func(a *txtar.Archive) { a.Files[0].Data = []byte("other") }, |
| func(a *txtar.Archive) { a.Files[0].Name = "other" }, |
| func(a *txtar.Archive) { a.Files = nil }, |
| } { |
| a := txtar.Parse([]byte(input)) |
| if n := len(a.Files); n != 1 { |
| t.Fatalf("txtar.Parse(%q) got %d files; expected 1", input, n) |
| } |
| |
| fsys, err := txtar.FS(a) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| // Confirm we can open "one.txt". |
| _, err = fsys.Open("one.txt") |
| if err != nil { |
| t.Fatal(err) |
| } |
| // Modify a to get ErrModified when opening "one.txt". |
| mod(a) |
| |
| _, err = fsys.Open("one.txt") |
| if err != txtar.ErrModified { |
| t.Errorf("Open(%q) got error %s; want ErrModified", "one.txt", err) |
| } |
| } |
| } |
| |
| func TestReadFile(t *testing.T) { |
| const input = ` |
| -- 1/one.txt -- |
| one |
| ` |
| a := txtar.Parse([]byte(input)) |
| fsys, err := txtar.FS(a) |
| if err != nil { |
| t.Fatal(err) |
| } |
| readfs := fsys.(fs.ReadFileFS) |
| _, err = readfs.ReadFile("1") |
| if err == nil { |
| t.Errorf("ReadFile(%q) succeeded; expected an error when reading a directory", "1") |
| } |
| |
| content, err := readfs.ReadFile("1/one.txt") |
| if err != nil { |
| t.Fatal(err) |
| } |
| want := "one\n" |
| if got := string(content); want != got { |
| t.Errorf("ReadFile(%q) = %q; want %q", "1/one.txt", got, want) |
| } |
| } |