1// Copyright 2015 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.package zipfs
4package zipfs
5
6import (
7	"archive/zip"
8	"bytes"
9	"fmt"
10	"io"
11	"io/ioutil"
12	"os"
13	"reflect"
14	"testing"
15
16	"golang.org/x/tools/godoc/vfs"
17)
18
19var (
20
21	// files to use to build zip used by zipfs in testing; maps path : contents
22	files = map[string]string{"foo": "foo", "bar/baz": "baz", "a/b/c": "c"}
23
24	// expected info for each entry in a file system described by files
25	tests = []struct {
26		Path      string
27		IsDir     bool
28		IsRegular bool
29		Name      string
30		Contents  string
31		Files     map[string]bool
32	}{
33		{"/", true, false, "", "", map[string]bool{"foo": true, "bar": true, "a": true}},
34		{"//", true, false, "", "", map[string]bool{"foo": true, "bar": true, "a": true}},
35		{"/foo", false, true, "foo", "foo", nil},
36		{"/foo/", false, true, "foo", "foo", nil},
37		{"/foo//", false, true, "foo", "foo", nil},
38		{"/bar", true, false, "bar", "", map[string]bool{"baz": true}},
39		{"/bar/", true, false, "bar", "", map[string]bool{"baz": true}},
40		{"/bar/baz", false, true, "baz", "baz", nil},
41		{"//bar//baz", false, true, "baz", "baz", nil},
42		{"/a/b", true, false, "b", "", map[string]bool{"c": true}},
43	}
44
45	// to be initialized in setup()
46	fs        vfs.FileSystem
47	statFuncs []statFunc
48)
49
50type statFunc struct {
51	Name string
52	Func func(string) (os.FileInfo, error)
53}
54
55func TestMain(t *testing.M) {
56	if err := setup(); err != nil {
57		fmt.Fprintf(os.Stderr, "Error setting up zipfs testing state: %v.\n", err)
58		os.Exit(1)
59	}
60	os.Exit(t.Run())
61}
62
63// setups state each of the tests uses
64func setup() error {
65	// create zipfs
66	b := new(bytes.Buffer)
67	zw := zip.NewWriter(b)
68	for file, contents := range files {
69		w, err := zw.Create(file)
70		if err != nil {
71			return err
72		}
73		_, err = io.WriteString(w, contents)
74		if err != nil {
75			return err
76		}
77	}
78	zw.Close()
79	zr, err := zip.NewReader(bytes.NewReader(b.Bytes()), int64(b.Len()))
80	if err != nil {
81		return err
82	}
83	rc := &zip.ReadCloser{
84		Reader: *zr,
85	}
86	fs = New(rc, "foo")
87
88	// pull out different stat functions
89	statFuncs = []statFunc{
90		{"Stat", fs.Stat},
91		{"Lstat", fs.Lstat},
92	}
93
94	return nil
95}
96
97func TestZipFSReadDir(t *testing.T) {
98	for _, test := range tests {
99		if test.IsDir {
100			infos, err := fs.ReadDir(test.Path)
101			if err != nil {
102				t.Errorf("Failed to read directory %v\n", test.Path)
103				continue
104			}
105			got := make(map[string]bool)
106			for _, info := range infos {
107				got[info.Name()] = true
108			}
109			if want := test.Files; !reflect.DeepEqual(got, want) {
110				t.Errorf("ReadDir %v got %v\nwanted %v\n", test.Path, got, want)
111			}
112		}
113	}
114}
115
116func TestZipFSStatFuncs(t *testing.T) {
117	for _, test := range tests {
118		for _, statFunc := range statFuncs {
119
120			// test can stat
121			info, err := statFunc.Func(test.Path)
122			if err != nil {
123				t.Errorf("Unexpected error using %v for %v: %v\n", statFunc.Name, test.Path, err)
124				continue
125			}
126
127			// test info.Name()
128			if got, want := info.Name(), test.Name; got != want {
129				t.Errorf("Using %v for %v info.Name() got %v wanted %v\n", statFunc.Name, test.Path, got, want)
130			}
131			// test info.IsDir()
132			if got, want := info.IsDir(), test.IsDir; got != want {
133				t.Errorf("Using %v for %v info.IsDir() got %v wanted %v\n", statFunc.Name, test.Path, got, want)
134			}
135			// test info.Mode().IsDir()
136			if got, want := info.Mode().IsDir(), test.IsDir; got != want {
137				t.Errorf("Using %v for %v info.Mode().IsDir() got %v wanted %v\n", statFunc.Name, test.Path, got, want)
138			}
139			// test info.Mode().IsRegular()
140			if got, want := info.Mode().IsRegular(), test.IsRegular; got != want {
141				t.Errorf("Using %v for %v info.Mode().IsRegular() got %v wanted %v\n", statFunc.Name, test.Path, got, want)
142			}
143			// test info.Size()
144			if test.IsRegular {
145				if got, want := info.Size(), int64(len(test.Contents)); got != want {
146					t.Errorf("Using %v for %v inf.Size() got %v wanted %v", statFunc.Name, test.Path, got, want)
147				}
148			}
149		}
150	}
151}
152
153func TestZipFSNotExist(t *testing.T) {
154	_, err := fs.Open("/does-not-exist")
155	if err == nil {
156		t.Fatalf("Expected an error.\n")
157	}
158	if !os.IsNotExist(err) {
159		t.Errorf("Expected an error satisfying os.IsNotExist: %v\n", err)
160	}
161}
162
163func TestZipFSOpenSeek(t *testing.T) {
164	for _, test := range tests {
165		if test.IsRegular {
166
167			// test Open()
168			f, err := fs.Open(test.Path)
169			if err != nil {
170				t.Error(err)
171				return
172			}
173			defer f.Close()
174
175			// test Seek() multiple times
176			for i := 0; i < 3; i++ {
177				all, err := ioutil.ReadAll(f)
178				if err != nil {
179					t.Error(err)
180					return
181				}
182				if got, want := string(all), test.Contents; got != want {
183					t.Errorf("File contents for %v got %v wanted %v\n", test.Path, got, want)
184				}
185				f.Seek(0, 0)
186			}
187		}
188	}
189}
190
191func TestRootType(t *testing.T) {
192	tests := []struct {
193		path   string
194		fsType vfs.RootType
195	}{
196		{"/src/net/http", vfs.RootTypeGoRoot},
197		{"/src/badpath", ""},
198		{"/", vfs.RootTypeGoRoot},
199	}
200
201	for _, item := range tests {
202		if fs.RootType(item.path) != item.fsType {
203			t.Errorf("unexpected fsType. Expected- %v, Got- %v", item.fsType, fs.RootType(item.path))
204		}
205	}
206}
207