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  }