// 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) } }