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