github.com/anchore/syft@v1.38.2/cmd/syft/internal/test/integration/package_deduplication_test.go (about)

     1  package integration
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/assert"
     8  
     9  	"github.com/anchore/syft/syft/pkg"
    10  	"github.com/anchore/syft/syft/source"
    11  )
    12  
    13  func TestPackageDeduplication(t *testing.T) {
    14  	tests := []struct {
    15  		scope         source.Scope
    16  		packageCount  int
    17  		instanceCount map[string]int
    18  		locationCount map[string]int
    19  	}{
    20  		{
    21  			scope:        source.AllLayersScope,
    22  			packageCount: 175, // without deduplication this would be ~600
    23  			instanceCount: map[string]int{
    24  				"basesystem":   1,
    25  				"wget":         1,
    26  				"curl-minimal": 2, // upgraded in the image
    27  				"vsftpd":       1,
    28  				"httpd":        1, // rpm, - we exclude binary
    29  			},
    30  			locationCount: map[string]int{
    31  				"basesystem-11-13.el9":               5, // in all layers
    32  				"curl-minimal-7.76.1-26.el9_3.2.0.1": 2, // base + wget layer
    33  				"curl-minimal-7.76.1-31.el9_6.1":     3, // curl upgrade layer + all above layers
    34  				"wget-1.21.1-8.el9_4":                4, // wget + all above layers
    35  				"vsftpd-3.0.5-6.el9":                 2, // vsftpd + all above layers
    36  				"httpd-2.4.62-4.el9_6.4":             1, // last layer
    37  			},
    38  		},
    39  		{
    40  			scope:        source.SquashedScope,
    41  			packageCount: 169,
    42  			instanceCount: map[string]int{
    43  				"basesystem":   1,
    44  				"wget":         1,
    45  				"curl-minimal": 1, // upgraded, but the most recent
    46  				"vsftpd":       1,
    47  				"httpd":        1, // rpm, binary is now excluded by overlap
    48  			},
    49  			locationCount: map[string]int{
    50  				"basesystem-11-13.el9":           1,
    51  				"curl-minimal-7.76.1-31.el9_6.1": 1, // upgrade
    52  				"wget-1.21.1-8.el9_4":            1,
    53  				"vsftpd-3.0.5-6.el9":             1,
    54  				"httpd-2.4.62-4.el9_6.4":         1,
    55  			},
    56  		},
    57  	}
    58  
    59  	for _, tt := range tests {
    60  		t.Run(string(tt.scope), func(t *testing.T) {
    61  			sbom, _ := catalogFixtureImage(t, "image-vertical-package-dups", tt.scope)
    62  			for _, p := range sbom.Artifacts.Packages.Sorted() {
    63  				if p.Type == pkg.BinaryPkg {
    64  					assert.NotEmpty(t, p.Name)
    65  				}
    66  			}
    67  
    68  			assert.Equal(t, tt.packageCount, sbom.Artifacts.Packages.PackageCount())
    69  			for name, expectedInstanceCount := range tt.instanceCount {
    70  				pkgs := sbom.Artifacts.Packages.PackagesByName(name)
    71  
    72  				// with multiple packages with the same name, something is wrong (or this is the wrong fixture)
    73  				if assert.Len(t, pkgs, expectedInstanceCount, "unexpected package count for %s", name) {
    74  					for _, p := range pkgs {
    75  						nameVersion := fmt.Sprintf("%s-%s", name, p.Version)
    76  						expectedLocationCount, ok := tt.locationCount[nameVersion]
    77  						if !ok {
    78  							t.Errorf("missing name-version: %s", nameVersion)
    79  							continue
    80  						}
    81  
    82  						// we should see merged locations (assumption, there was 1 location for each package)
    83  						assert.Len(t, p.Locations.ToSlice(), expectedLocationCount, "unexpected location count for %s", nameVersion)
    84  
    85  						// all paths should match
    86  						assert.Len(t, p.Locations.CoordinateSet().Paths(), 1, "unexpected location count for %s", nameVersion)
    87  					}
    88  				}
    89  			}
    90  
    91  		})
    92  	}
    93  }