github.com/google/osv-scalibr@v0.4.1/artifact/image/whiteout/whiteout_test.go (about) 1 // Copyright 2025 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 15 package whiteout_test 16 17 import ( 18 "os" 19 "path/filepath" 20 "testing" 21 22 "github.com/google/go-cmp/cmp" 23 "github.com/google/osv-scalibr/artifact/image/whiteout" 24 scalibrfs "github.com/google/osv-scalibr/fs" 25 ) 26 27 func TestWhiteout(t *testing.T) { 28 testCases := []struct { 29 desc string 30 paths []string 31 dirs []string 32 want map[string]struct{} 33 }{ 34 { 35 desc: "Empty filesystem", 36 paths: []string{}, 37 dirs: []string{}, 38 want: map[string]struct{}{}, 39 }, 40 { 41 desc: "Single_regular_file", 42 paths: []string{ 43 "hello_world.txt", 44 }, 45 dirs: []string{}, 46 want: map[string]struct{}{}, 47 }, 48 { 49 desc: "Single_whiteout_file", 50 paths: []string{ 51 ".wh.hello_world.txt", 52 }, 53 dirs: []string{}, 54 want: map[string]struct{}{ 55 ".wh.hello_world.txt": {}, 56 }, 57 }, 58 { 59 desc: "Mix_of_regular_and_whiteout_files", 60 paths: []string{ 61 "hello_world.txt", 62 ".wh.foo.txt", 63 ".wh.bar.txt", 64 }, 65 dirs: []string{}, 66 want: map[string]struct{}{ 67 ".wh.foo.txt": {}, 68 ".wh.bar.txt": {}, 69 }, 70 }, 71 { 72 desc: "Mix_of_regular_and_whiteout_files_in_different_directories", 73 paths: []string{ 74 "hello_world.txt", 75 "/dir1/.wh.foo.txt", 76 "/dir2/.wh.bar.txt", 77 }, 78 dirs: []string{ 79 "dir1", 80 "dir2", 81 }, 82 want: map[string]struct{}{ 83 "dir1/.wh.foo.txt": {}, 84 "dir2/.wh.bar.txt": {}, 85 }, 86 }, 87 { 88 desc: "Single_whiteout_directory", 89 paths: []string{ 90 ".wh..wh..opa.dir1", 91 }, 92 dirs: []string{ 93 "dir1", 94 }, 95 want: map[string]struct{}{ 96 ".wh..wh..opa.dir1": {}, 97 }, 98 }, 99 { 100 desc: "Mix_of_regular_and_whiteout_files_/_directory", 101 paths: []string{ 102 ".wh..wh..opa.dir1", 103 ".wh..wh..opa.dir2", 104 "/dir3/foo.txt", 105 "/dir3/dir4/.wh.bar.txt", 106 }, 107 dirs: []string{ 108 "dir1", 109 "dir2", 110 "dir3/dir4", 111 }, 112 want: map[string]struct{}{ 113 ".wh..wh..opa.dir1": {}, 114 ".wh..wh..opa.dir2": {}, 115 "dir3/dir4/.wh.bar.txt": {}, 116 }, 117 }, 118 } 119 120 for _, tc := range testCases { 121 t.Run(tc.desc, func(t *testing.T) { 122 tmp := t.TempDir() 123 fs := scalibrfs.DirFS(tmp) 124 125 // Create directories first, then write files to fs. 126 for _, dir := range tc.dirs { 127 err := os.MkdirAll(filepath.Join(tmp, dir), 0777) 128 if err != nil { 129 t.Fatalf("os.MkdirAll(%q): unexpected error: %v", filepath.Join(tmp, dir), err) 130 } 131 } 132 133 for _, path := range tc.paths { 134 err := os.WriteFile(filepath.Join(tmp, path), []byte("Content"), 0644) 135 if err != nil { 136 t.Fatalf("os.WriteFile(%q): unexpected error: %v", filepath.Join(tmp, path), err) 137 } 138 } 139 140 got, err := whiteout.Files(fs) 141 if err != nil { 142 t.Fatalf("whiteout.Files(%v): unexpected error: %v", fs, err) 143 } 144 if diff := cmp.Diff(tc.want, got); diff != "" { 145 t.Errorf("whiteout.Files(%v): unexpected diff (-want +got):\n%s", fs, diff) 146 } 147 }) 148 } 149 } 150 151 func TestIsWhiteout(t *testing.T) { 152 testCases := []struct { 153 desc string 154 path string 155 want bool 156 }{ 157 { 158 desc: "Empty_path", 159 path: "", 160 want: false, 161 }, 162 { 163 desc: "Simple_file_path", 164 path: "file.txt", 165 want: false, 166 }, 167 { 168 desc: "Path_with_directories", 169 path: "dir1/dir2/foo.txt", 170 want: false, 171 }, 172 { 173 desc: "Simple_whiteout_path", 174 path: ".wh.file.txt", 175 want: true, 176 }, 177 { 178 desc: "Whiteout_path_with_directories", 179 path: "dir1/dir2/.wh.foo.txt", 180 want: true, 181 }, 182 } 183 for _, tc := range testCases { 184 t.Run(tc.desc, func(t *testing.T) { 185 got := whiteout.IsWhiteout(tc.path) 186 if got != tc.want { 187 t.Errorf("IsWhiteout(%q) = %v, want: %v", tc.path, got, tc.want) 188 } 189 }) 190 } 191 } 192 193 func TestToWhiteout(t *testing.T) { 194 testCases := []struct { 195 desc string 196 path string 197 want string 198 }{ 199 { 200 desc: "Empty_path", 201 path: "", 202 want: ".wh.", 203 }, 204 { 205 desc: "Simple_file_path", 206 path: "file.txt", 207 want: ".wh.file.txt", 208 }, 209 { 210 desc: "Path_with_directories", 211 path: "dir1/dir2/foo.txt", 212 want: "dir1/dir2/.wh.foo.txt", 213 }, 214 } 215 for _, tc := range testCases { 216 t.Run(tc.desc, func(t *testing.T) { 217 got := whiteout.ToWhiteout(tc.path) 218 if got != tc.want { 219 t.Errorf("ToWhiteout(%q) = %q, want: %q", tc.path, got, tc.want) 220 } 221 }) 222 } 223 } 224 225 func TestToPath(t *testing.T) { 226 testCases := []struct { 227 desc string 228 path string 229 want string 230 }{ 231 { 232 desc: "Empty_path", 233 path: "", 234 want: "", 235 }, 236 { 237 desc: "Simple_file_path", 238 path: "file.txt", 239 want: "file.txt", 240 }, 241 { 242 desc: "Path_with_directories", 243 path: "dir1/dir2/foo.txt", 244 want: "dir1/dir2/foo.txt", 245 }, 246 { 247 desc: "Simple_whiteout_path", 248 path: ".wh.file.txt", 249 want: "file.txt", 250 }, 251 { 252 desc: "Whiteout_path_with_directories", 253 path: "dir1/dir2/.wh.foo.txt", 254 want: "dir1/dir2/foo.txt", 255 }, 256 } 257 for _, tc := range testCases { 258 t.Run(tc.desc, func(t *testing.T) { 259 got := whiteout.ToPath(tc.path) 260 if got != tc.want { 261 t.Errorf("ToPath(%q) = %q, want: %q", tc.path, got, tc.want) 262 } 263 }) 264 } 265 }