github.com/avfs/avfs@v0.33.1-0.20240303173310-c6ba67c33eb7/test/test_perm.go (about) 1 // 2 // Copyright 2021 The AVFS authors 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 // 16 17 package test 18 19 import ( 20 "encoding/json" 21 "errors" 22 "fmt" 23 "io/fs" 24 "os" 25 "path/filepath" 26 "strings" 27 "testing" 28 29 "github.com/avfs/avfs" 30 ) 31 32 // NewPermTests creates and returns a new environment for permissions test. 33 func (ts *Suite) NewPermTests(t *testing.T, testDir, funcName string) *PermTests { 34 return ts.NewPermTestsWithOptions(t, testDir, funcName, &PermOptions{}) 35 } 36 37 // NewPermTestsWithOptions creates and returns a new environment for permissions test with options. 38 func (ts *Suite) NewPermTestsWithOptions(t *testing.T, testDir, funcName string, options *PermOptions) *PermTests { 39 osName := avfs.CurrentOSType().String() 40 errFileName := filepath.Join(ts.testDataDir, fmt.Sprintf("perm%s%s.golden", funcName, osName)) 41 permDir := filepath.Join(testDir, funcName) 42 43 pts := &PermTests{ 44 ts: ts, 45 errors: make(map[string]*permError), 46 errFileName: errFileName, 47 errFileExists: true, 48 permDir: permDir, 49 options: *options, 50 } 51 52 vfs := ts.vfsSetup 53 ts.setInitUser(t) 54 ts.createDir(t, pts.permDir, avfs.DefaultDirPerm) 55 56 for _, ui := range UserInfos() { 57 ts.setUser(t, ui.Name) 58 59 usrDir := vfs.Join(pts.permDir, ui.Name) 60 ts.createDir(t, usrDir, avfs.DefaultDirPerm) 61 62 for m := fs.FileMode(0); m <= 0o777; m++ { 63 path := vfs.Join(usrDir, m.String()) 64 if pts.options.CreateFiles { 65 ts.createFile(t, path, m) 66 } else { 67 ts.createDir(t, path, m) 68 } 69 } 70 71 // Allow updates from user and group. 72 err := vfs.Chmod(usrDir, 0o775) 73 RequireNoError(t, err, "Chmod %s", usrDir) 74 } 75 76 ts.setUser(t, UsrTest) 77 78 return pts 79 } 80 81 // PermFunc returns an error depending on the permissions of the user and the file mode on the path. 82 type PermFunc func(path string) error 83 84 // load loads a permissions test file. 85 func (pts *PermTests) load(t *testing.T) { 86 ts := pts.ts 87 ts.setInitUser(t) 88 89 b, err := os.ReadFile(pts.errFileName) 90 if err != nil { 91 if errors.Is(err, fs.ErrNotExist) { 92 pts.errFileExists = false 93 94 return 95 } 96 97 t.Fatalf("ReadFile %s : %v", pts.errFileName, err) 98 } 99 100 err = json.Unmarshal(b, &pts.errors) 101 RequireNoError(t, err, "Unmarshal %", pts.errFileName) 102 } 103 104 // save saves a permissions test file. 105 func (pts *PermTests) save(t *testing.T) { 106 if pts.errFileExists { 107 return 108 } 109 110 b, err := json.MarshalIndent(pts.errors, "", "\t") 111 RequireNoError(t, err, "MarshalIndent %s", pts.errFileName) 112 113 ts := pts.ts 114 ts.setInitUser(t) 115 116 err = os.WriteFile(pts.errFileName, b, avfs.DefaultFilePerm) 117 RequireNoError(t, err, "WriteFile %s", pts.errFileName) 118 } 119 120 // newPermError creates and returns a normalized permError where all paths are relative to permDir. 121 func (pts *PermTests) newPermError(err error) *permError { 122 prefix := pts.permDir + string(os.PathSeparator) 123 124 switch e := err.(type) { 125 case *fs.PathError: 126 return &permError{ 127 ErrType: PathError, 128 ErrOp: e.Op, 129 ErrPath: strings.TrimPrefix(e.Path, prefix), 130 ErrErr: e.Err.Error(), 131 } 132 133 case *os.LinkError: 134 return &permError{ 135 ErrType: LinkError, 136 ErrOp: e.Op, 137 ErrOld: strings.TrimPrefix(e.Old, prefix), 138 ErrNew: strings.TrimPrefix(e.New, prefix), 139 ErrErr: e.Err.Error(), 140 } 141 case nil: 142 return &permError{} 143 default: 144 return &permError{ 145 ErrType: StringError, 146 ErrErr: e.Error(), 147 } 148 } 149 } 150 151 // Test generates or tests the golden file of the permissions for a specific function. 152 func (pts *PermTests) Test(t *testing.T, permFunc PermFunc) { 153 ts := pts.ts 154 vfs := ts.vfsSetup 155 156 pts.load(t) 157 158 if !pts.errFileExists && !vfs.HasFeature(avfs.FeatRealFS) { 159 t.Errorf("Can't test emulated file system %s before a real file system.", vfs.Type()) 160 161 return 162 } 163 164 ts.setUser(t, UsrTest) 165 166 for _, ui := range UserInfos() { 167 for m := fs.FileMode(0); m <= 0o777; m++ { 168 relPath := vfs.Join(ui.Name, m.String()) 169 170 path := vfs.Join(pts.permDir, relPath) 171 err := permFunc(path) 172 pe := pts.newPermError(err) 173 174 if pts.errFileExists { 175 wantErr, ok := pts.errors[relPath] 176 if !ok { 177 t.Fatalf("Compare %s : no test recorded", path) 178 } 179 180 errStr := pts.compare(wantErr, pe) 181 if errStr != "" { 182 t.Errorf("Compare %s : %s", relPath, errStr) 183 } 184 } else { 185 pts.errors[relPath] = pe 186 } 187 } 188 } 189 190 pts.save(t) 191 } 192 193 // compare compares wanted error to error and returns a non-empty string if there is an error. 194 func (pts *PermTests) compare(wantErr, err *permError) string { 195 po := pts.options 196 errStr := "" 197 198 if err.ErrType != wantErr.ErrType { 199 errStr += fmt.Sprintf("\n\twant error type to be %s, got %s", wantErr.ErrType, err.ErrType) 200 } 201 202 if !po.IgnoreOp && (wantErr.ErrType == PathError || wantErr.ErrType == LinkError) && err.ErrOp != wantErr.ErrOp { 203 errStr += fmt.Sprintf("\n\twant Op to be %s, got %s", wantErr.ErrOp, err.ErrOp) 204 } 205 206 if !po.IgnorePath { 207 if wantErr.ErrType == PathError && err.ErrPath != wantErr.ErrPath { 208 errStr += fmt.Sprintf("\n\twant path to be %s, got %s", wantErr.ErrPath, err.ErrPath) 209 } 210 211 if wantErr.ErrType == LinkError { 212 if err.ErrOld != wantErr.ErrOld { 213 errStr += fmt.Sprintf("\n\twant Old to be %s, got %s", wantErr.ErrOld, err.ErrOld) 214 } 215 216 if err.ErrNew != wantErr.ErrNew { 217 errStr += fmt.Sprintf("\n\twant New to be %s, got %s", wantErr.ErrNew, err.ErrNew) 218 } 219 } 220 } 221 222 if err.ErrErr != wantErr.ErrErr { 223 errStr += fmt.Sprintf("\n\twant error to be %s, got %s", wantErr.ErrErr, err.ErrErr) 224 } 225 226 return errStr 227 }