1*9e94795aSAndroid Build Coastguard Worker// Copyright 2022 Google LLC 2*9e94795aSAndroid Build Coastguard Worker// 3*9e94795aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*9e94795aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*9e94795aSAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*9e94795aSAndroid Build Coastguard Worker// 7*9e94795aSAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*9e94795aSAndroid Build Coastguard Worker// 9*9e94795aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*9e94795aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*9e94795aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*9e94795aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*9e94795aSAndroid Build Coastguard Worker// limitations under the License. 14*9e94795aSAndroid Build Coastguard Worker 15*9e94795aSAndroid Build Coastguard Workerpackage testfs 16*9e94795aSAndroid Build Coastguard Worker 17*9e94795aSAndroid Build Coastguard Workerimport ( 18*9e94795aSAndroid Build Coastguard Worker "fmt" 19*9e94795aSAndroid Build Coastguard Worker "io" 20*9e94795aSAndroid Build Coastguard Worker "io/fs" 21*9e94795aSAndroid Build Coastguard Worker "strings" 22*9e94795aSAndroid Build Coastguard Worker "time" 23*9e94795aSAndroid Build Coastguard Worker) 24*9e94795aSAndroid Build Coastguard Worker 25*9e94795aSAndroid Build Coastguard Worker// TestFS implements a test file system (fs.FS) simulated by a map from filename to []byte content. 26*9e94795aSAndroid Build Coastguard Workertype TestFS map[string][]byte 27*9e94795aSAndroid Build Coastguard Worker 28*9e94795aSAndroid Build Coastguard Workervar _ fs.FS = (*TestFS)(nil) 29*9e94795aSAndroid Build Coastguard Workervar _ fs.StatFS = (*TestFS)(nil) 30*9e94795aSAndroid Build Coastguard Worker 31*9e94795aSAndroid Build Coastguard Worker// Open implements fs.FS.Open() to open a file based on the filename. 32*9e94795aSAndroid Build Coastguard Workerfunc (tfs *TestFS) Open(name string) (fs.File, error) { 33*9e94795aSAndroid Build Coastguard Worker if _, ok := (*tfs)[name]; !ok { 34*9e94795aSAndroid Build Coastguard Worker return nil, fmt.Errorf("unknown file %q", name) 35*9e94795aSAndroid Build Coastguard Worker } 36*9e94795aSAndroid Build Coastguard Worker return &TestFile{tfs, name, 0}, nil 37*9e94795aSAndroid Build Coastguard Worker} 38*9e94795aSAndroid Build Coastguard Worker 39*9e94795aSAndroid Build Coastguard Worker// Stat implements fs.StatFS.Stat() to examine a file based on the filename. 40*9e94795aSAndroid Build Coastguard Workerfunc (tfs *TestFS) Stat(name string) (fs.FileInfo, error) { 41*9e94795aSAndroid Build Coastguard Worker if content, ok := (*tfs)[name]; ok { 42*9e94795aSAndroid Build Coastguard Worker return &TestFileInfo{name, len(content), 0666}, nil 43*9e94795aSAndroid Build Coastguard Worker } 44*9e94795aSAndroid Build Coastguard Worker dirname := name 45*9e94795aSAndroid Build Coastguard Worker if !strings.HasSuffix(dirname, "/") { 46*9e94795aSAndroid Build Coastguard Worker dirname = dirname + "/" 47*9e94795aSAndroid Build Coastguard Worker } 48*9e94795aSAndroid Build Coastguard Worker for name := range (*tfs) { 49*9e94795aSAndroid Build Coastguard Worker if strings.HasPrefix(name, dirname) { 50*9e94795aSAndroid Build Coastguard Worker return &TestFileInfo{name, 8, fs.ModeDir | fs.ModePerm}, nil 51*9e94795aSAndroid Build Coastguard Worker } 52*9e94795aSAndroid Build Coastguard Worker } 53*9e94795aSAndroid Build Coastguard Worker return nil, fmt.Errorf("file not found: %q", name) 54*9e94795aSAndroid Build Coastguard Worker} 55*9e94795aSAndroid Build Coastguard Worker 56*9e94795aSAndroid Build Coastguard Worker// TestFileInfo implements a file info (fs.FileInfo) based on TestFS above. 57*9e94795aSAndroid Build Coastguard Workertype TestFileInfo struct { 58*9e94795aSAndroid Build Coastguard Worker name string 59*9e94795aSAndroid Build Coastguard Worker size int 60*9e94795aSAndroid Build Coastguard Worker mode fs.FileMode 61*9e94795aSAndroid Build Coastguard Worker} 62*9e94795aSAndroid Build Coastguard Worker 63*9e94795aSAndroid Build Coastguard Workervar _ fs.FileInfo = (*TestFileInfo)(nil) 64*9e94795aSAndroid Build Coastguard Worker 65*9e94795aSAndroid Build Coastguard Worker// Name returns the name of the file 66*9e94795aSAndroid Build Coastguard Workerfunc (fi *TestFileInfo) Name() string { 67*9e94795aSAndroid Build Coastguard Worker return fi.name 68*9e94795aSAndroid Build Coastguard Worker} 69*9e94795aSAndroid Build Coastguard Worker 70*9e94795aSAndroid Build Coastguard Worker// Size returns the size of the file in bytes. 71*9e94795aSAndroid Build Coastguard Workerfunc (fi *TestFileInfo) Size() int64 { 72*9e94795aSAndroid Build Coastguard Worker return int64(fi.size) 73*9e94795aSAndroid Build Coastguard Worker} 74*9e94795aSAndroid Build Coastguard Worker 75*9e94795aSAndroid Build Coastguard Worker// Mode returns the fs.FileMode bits. 76*9e94795aSAndroid Build Coastguard Workerfunc (fi *TestFileInfo) Mode() fs.FileMode { 77*9e94795aSAndroid Build Coastguard Worker return fi.mode 78*9e94795aSAndroid Build Coastguard Worker} 79*9e94795aSAndroid Build Coastguard Worker 80*9e94795aSAndroid Build Coastguard Worker// ModTime fakes a modification time. 81*9e94795aSAndroid Build Coastguard Workerfunc (fi *TestFileInfo) ModTime() time.Time { 82*9e94795aSAndroid Build Coastguard Worker return time.UnixMicro(0xb0bb) 83*9e94795aSAndroid Build Coastguard Worker} 84*9e94795aSAndroid Build Coastguard Worker 85*9e94795aSAndroid Build Coastguard Worker// IsDir is a synonym for Mode().IsDir() 86*9e94795aSAndroid Build Coastguard Workerfunc (fi *TestFileInfo) IsDir() bool { 87*9e94795aSAndroid Build Coastguard Worker return fi.mode.IsDir() 88*9e94795aSAndroid Build Coastguard Worker} 89*9e94795aSAndroid Build Coastguard Worker 90*9e94795aSAndroid Build Coastguard Worker// Sys is unused and returns nil. 91*9e94795aSAndroid Build Coastguard Workerfunc (fi *TestFileInfo) Sys() any { 92*9e94795aSAndroid Build Coastguard Worker return nil 93*9e94795aSAndroid Build Coastguard Worker} 94*9e94795aSAndroid Build Coastguard Worker 95*9e94795aSAndroid Build Coastguard Worker// TestFile implements a test file (fs.File) based on TestFS above. 96*9e94795aSAndroid Build Coastguard Workertype TestFile struct { 97*9e94795aSAndroid Build Coastguard Worker fs *TestFS 98*9e94795aSAndroid Build Coastguard Worker name string 99*9e94795aSAndroid Build Coastguard Worker posn int 100*9e94795aSAndroid Build Coastguard Worker} 101*9e94795aSAndroid Build Coastguard Worker 102*9e94795aSAndroid Build Coastguard Workervar _ fs.File = (*TestFile)(nil) 103*9e94795aSAndroid Build Coastguard Worker 104*9e94795aSAndroid Build Coastguard Worker// Stat not implemented to obviate implementing fs.FileInfo. 105*9e94795aSAndroid Build Coastguard Workerfunc (f *TestFile) Stat() (fs.FileInfo, error) { 106*9e94795aSAndroid Build Coastguard Worker return f.fs.Stat(f.name) 107*9e94795aSAndroid Build Coastguard Worker} 108*9e94795aSAndroid Build Coastguard Worker 109*9e94795aSAndroid Build Coastguard Worker// Read copies bytes from the TestFS map. 110*9e94795aSAndroid Build Coastguard Workerfunc (f *TestFile) Read(b []byte) (int, error) { 111*9e94795aSAndroid Build Coastguard Worker if f.posn < 0 { 112*9e94795aSAndroid Build Coastguard Worker return 0, fmt.Errorf("file not open: %q", f.name) 113*9e94795aSAndroid Build Coastguard Worker } 114*9e94795aSAndroid Build Coastguard Worker if f.posn >= len((*f.fs)[f.name]) { 115*9e94795aSAndroid Build Coastguard Worker return 0, io.EOF 116*9e94795aSAndroid Build Coastguard Worker } 117*9e94795aSAndroid Build Coastguard Worker n := copy(b, (*f.fs)[f.name][f.posn:]) 118*9e94795aSAndroid Build Coastguard Worker f.posn += n 119*9e94795aSAndroid Build Coastguard Worker return n, nil 120*9e94795aSAndroid Build Coastguard Worker} 121*9e94795aSAndroid Build Coastguard Worker 122*9e94795aSAndroid Build Coastguard Worker// Close marks the TestFile as no longer in use. 123*9e94795aSAndroid Build Coastguard Workerfunc (f *TestFile) Close() error { 124*9e94795aSAndroid Build Coastguard Worker if f.posn < 0 { 125*9e94795aSAndroid Build Coastguard Worker return fmt.Errorf("file already closed: %q", f.name) 126*9e94795aSAndroid Build Coastguard Worker } 127*9e94795aSAndroid Build Coastguard Worker f.posn = -1 128*9e94795aSAndroid Build Coastguard Worker return nil 129*9e94795aSAndroid Build Coastguard Worker} 130