github.com/google/osv-scalibr@v0.4.1/common/linux/dpkg/dpkg_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 dpkg
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"io"
    21  	"sort"
    22  	"testing"
    23  
    24  	"github.com/google/go-cmp/cmp"
    25  	"github.com/google/osv-scalibr/common/linux/dpkg/testing/dpkgutil"
    26  	scalibrfs "github.com/google/osv-scalibr/fs"
    27  )
    28  
    29  func TestConffilePathIterator(t *testing.T) {
    30  	tests := []struct {
    31  		name      string
    32  		conffiles map[string]string
    33  		want      []string
    34  		wantOpen  []string
    35  	}{
    36  		{
    37  			name:      "empty info dir",
    38  			conffiles: map[string]string{},
    39  			want:      nil,
    40  		},
    41  		{
    42  			name: "No_conffiles_files",
    43  			conffiles: map[string]string{
    44  				"foo.txt": "foocontents",
    45  			},
    46  			want: nil,
    47  		},
    48  		{
    49  			name: "one_empty_conffiles_file",
    50  			conffiles: map[string]string{
    51  				"foo.conffiles": "",
    52  			},
    53  			want: []string{""},
    54  		},
    55  		{
    56  			name: "one_conffiles_file_with_lines",
    57  			conffiles: map[string]string{
    58  				"foo.conffiles": "/foo\n/bar",
    59  			},
    60  			want: []string{"/foo", "/bar"},
    61  		},
    62  		{
    63  			name: "two_conffiles_files",
    64  			conffiles: map[string]string{
    65  				"foo.conffiles": "/foo\n/bar",
    66  				"baz.conffiles": "/baz",
    67  			},
    68  			want: []string{"/foo", "/bar", "/baz"},
    69  		},
    70  		{
    71  			name: "conffiles_file_with_irrelevant_files",
    72  			conffiles: map[string]string{
    73  				"foo.conffiles": "/foo\n/bar",
    74  				"baz.txt":       "bazcontents",
    75  			},
    76  			want: []string{"/foo", "/bar"},
    77  		},
    78  	}
    79  
    80  	for _, tt := range tests {
    81  		t.Run(tt.name, func(t *testing.T) {
    82  			root := dpkgutil.SetupDPKGInfo(t, tt.conffiles, false)
    83  			fs := scalibrfs.RealFSScanRoot(root).FS
    84  
    85  			it, err := NewConffilePathIterator(fs)
    86  			if err != nil {
    87  				t.Fatalf("NewConffilePathIterator() returned err: %v", err)
    88  			}
    89  			defer it.Close()
    90  
    91  			var got []string
    92  			for {
    93  				path, err := it.Next(t.Context())
    94  				if errors.Is(err, io.EOF) {
    95  					break
    96  				}
    97  				if err != nil {
    98  					t.Fatalf("it.Next() returned err: %v", err)
    99  				}
   100  				got = append(got, path)
   101  			}
   102  
   103  			sort.Strings(got)
   104  			sort.Strings(tt.want)
   105  
   106  			if diff := cmp.Diff(tt.want, got); diff != "" {
   107  				t.Errorf("ConffilePathIterator returned diff (-want +got):\n%s", diff)
   108  			}
   109  		})
   110  	}
   111  }
   112  
   113  func TestConffilePathIteratorContextCancel(t *testing.T) {
   114  	root := dpkgutil.SetupDPKGInfo(t, map[string]string{
   115  		"foo.conffiles": "/foo\n/bar",
   116  	}, false)
   117  	fs := scalibrfs.RealFSScanRoot(root).FS
   118  
   119  	ctx, cancel := context.WithCancel(t.Context())
   120  	it, err := NewConffilePathIterator(fs)
   121  	if err != nil {
   122  		t.Fatalf("NewConffilePathIterator() returned err: %v", err)
   123  	}
   124  	defer it.Close()
   125  	cancel()
   126  	_, err = it.Next(ctx)
   127  	if !errors.Is(err, context.Canceled) {
   128  		t.Errorf("it.Next() after context cancel: got err %v, want context.Canceled", err)
   129  	}
   130  }
   131  
   132  func TestConffilePathIteratorMissingInfoDir(t *testing.T) {
   133  	sfs := scalibrfs.RealFSScanRoot(t.TempDir()).FS
   134  	it, err := NewConffilePathIterator(sfs)
   135  	if err != nil {
   136  		t.Fatalf("NewConffilePathIterator() with missing info dir: got err %v, want nil", err)
   137  	}
   138  	defer it.Close()
   139  	_, err = it.Next(t.Context())
   140  	if !errors.Is(err, io.EOF) {
   141  		t.Errorf("it.Next() with missing info dir: got err %v, want io.EOF", err)
   142  	}
   143  }
   144  
   145  func TestListFilePathIterator(t *testing.T) {
   146  	tests := []struct {
   147  		name     string
   148  		list     map[string]string
   149  		want     []string
   150  		wantOpen []string
   151  	}{
   152  		{
   153  			name: "empty_info_dir",
   154  			list: map[string]string{},
   155  			want: nil,
   156  		},
   157  		{
   158  			name: "No_list_files",
   159  			list: map[string]string{
   160  				"foo.txt": "foocontents",
   161  			},
   162  			want: nil,
   163  		},
   164  		{
   165  			name: "one_empty_list_file",
   166  			list: map[string]string{
   167  				"foo.list": "",
   168  			},
   169  			want: []string{""},
   170  		},
   171  		{
   172  			name: "one_list_file_with_lines",
   173  			list: map[string]string{
   174  				"foo.list": "/foo\n/bar",
   175  			},
   176  			want: []string{"/foo", "/bar"},
   177  		},
   178  		{
   179  			name: "two_list_files",
   180  			list: map[string]string{
   181  				"foo.list": "/foo\n/bar",
   182  				"baz.list": "/baz",
   183  			},
   184  			want: []string{"/foo", "/bar", "/baz"},
   185  		},
   186  		{
   187  			name: "list_file_with_irrelevant_files",
   188  			list: map[string]string{
   189  				"foo.list": "/foo\n/bar",
   190  				"baz.txt":  "bazcontents",
   191  			},
   192  			want: []string{"/foo", "/bar"},
   193  		},
   194  	}
   195  
   196  	for _, tt := range tests {
   197  		t.Run(tt.name, func(t *testing.T) {
   198  			root := dpkgutil.SetupDPKGInfo(t, tt.list, false)
   199  			fs := scalibrfs.RealFSScanRoot(root).FS
   200  
   201  			it, err := NewListFilePathIterator(fs)
   202  			if err != nil {
   203  				t.Fatalf("NewFileIterator() returned err: %v", err)
   204  			}
   205  			defer it.Close()
   206  
   207  			var got []string
   208  			for {
   209  				path, err := it.Next(t.Context())
   210  				if errors.Is(err, io.EOF) {
   211  					break
   212  				}
   213  				if err != nil {
   214  					t.Fatalf("it.Next() returned err: %v", err)
   215  				}
   216  				got = append(got, path)
   217  			}
   218  
   219  			sort.Strings(got)
   220  			sort.Strings(tt.want)
   221  
   222  			if diff := cmp.Diff(tt.want, got); diff != "" {
   223  				t.Errorf("FileIterator returned diff (-want +got):\n%s", diff)
   224  			}
   225  		})
   226  	}
   227  }
   228  
   229  func TestListFilePathIteratorContextCancel(t *testing.T) {
   230  	root := dpkgutil.SetupDPKGInfo(t, map[string]string{
   231  		"foo.list": "/foo\n/bar",
   232  	}, false)
   233  	fs := scalibrfs.RealFSScanRoot(root).FS
   234  
   235  	ctx, cancel := context.WithCancel(t.Context())
   236  	it, err := NewListFilePathIterator(fs)
   237  	if err != nil {
   238  		t.Fatalf("NewFileIterator() returned err: %v", err)
   239  	}
   240  	defer it.Close()
   241  	cancel()
   242  	_, err = it.Next(ctx)
   243  	if !errors.Is(err, context.Canceled) {
   244  		t.Errorf("it.Next() after context cancel: got err %v, want context.Canceled", err)
   245  	}
   246  }
   247  
   248  func TestListFilePathIteratorMissingInfoDir(t *testing.T) {
   249  	sfs := scalibrfs.RealFSScanRoot(t.TempDir()).FS
   250  	it, err := NewListFilePathIterator(sfs)
   251  	if err != nil {
   252  		t.Fatalf("NewFileIterator() with missing info dir: got err %v, want nil", err)
   253  	}
   254  	defer it.Close()
   255  	_, err = it.Next(t.Context())
   256  	if !errors.Is(err, io.EOF) {
   257  		t.Errorf("it.Next() with missing info dir: got err %v, want io.EOF", err)
   258  	}
   259  }