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 }