/
packagescanner.go
144 lines (130 loc) · 3.25 KB
/
packagescanner.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package test
import (
"context"
"fmt"
"net/http"
"sort"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/quay/zlog"
"github.com/quay/claircore"
"github.com/quay/claircore/indexer"
"github.com/quay/claircore/test/fetch"
)
// ScannerTestcase can be used for testing layers found in the wild against a
// scanner.
//
// Tests that use this struct should not be marked as integration tests, as the
// Run method does that internally if it needs to talk to the network.
type ScannerTestcase struct {
Domain string
Name string
Hash string
Want []*claircore.Package
Scanner indexer.PackageScanner
}
// Digest reports the digest in the Hash member.
//
// Panics if an error is returned from ParseDigest.
func (tc ScannerTestcase) Digest() claircore.Digest {
d, err := claircore.ParseDigest(tc.Hash)
if err != nil {
panic(err)
}
return d
}
// Run returns a function suitable for using with (*testing.T).Run.
//
// This function assumes the returned values must exactly match tc.Want.
// If tc.Want only covers a subset of potential returned values, then
// use RunSubset.
func (tc ScannerTestcase) Run(ctx context.Context) func(*testing.T) {
sort.Slice(tc.Want, pkgSort(tc.Want))
return func(t *testing.T) {
ctx := zlog.Test(ctx, t)
d := tc.Digest()
n, err := fetch.Layer(ctx, t, http.DefaultClient, tc.Domain, tc.Name, d)
if err != nil {
t.Fatal(err)
}
defer n.Close()
l := &claircore.Layer{
Hash: d,
}
l.SetLocal(n.Name())
got, err := tc.Scanner.Scan(ctx, l)
if err != nil {
t.Fatal(err)
}
sort.Slice(got, pkgSort(got))
t.Logf("found %d packages", len(got))
if !cmp.Equal(tc.Want, got) {
t.Error(cmp.Diff(tc.Want, got))
}
}
}
// RunSubset returns a function suitable for using with (*testing.T).Run.
//
// This function is similar to except it assumes tc.Want is a subset of
// all potential values.
// n is the total number of expected packages, ie len(got).
func (tc ScannerTestcase) RunSubset(ctx context.Context, n int) func(*testing.T) {
sort.Slice(tc.Want, pkgSort(tc.Want))
return func(t *testing.T) {
ctx := zlog.Test(ctx, t)
d := tc.Digest()
f, err := fetch.Layer(ctx, t, http.DefaultClient, tc.Domain, tc.Name, d)
if err != nil {
t.Fatal(err)
}
defer f.Close()
l := &claircore.Layer{
Hash: d,
}
l.SetLocal(f.Name())
got, err := tc.Scanner.Scan(ctx, l)
if err != nil {
t.Fatal(err)
}
t.Logf("found %d packages", len(got))
if !cmp.Equal(n, len(got)) {
t.Error(cmp.Diff(n, len(got)))
}
type key struct {
name, hint string
}
gotMap := make(map[key]*claircore.Package, len(got))
for _, p := range got {
gotMap[key{
name: p.Name,
hint: p.RepositoryHint,
}] = p
}
for _, p := range tc.Want {
g, exists := gotMap[key{
name: p.Name,
hint: p.RepositoryHint,
}]
if !exists {
t.Error(fmt.Sprintf("got is missing package %s with hint %s", p.Name, p.RepositoryHint))
continue
}
if !cmp.Equal(p, g) {
t.Error(cmp.Diff(p, g))
}
}
}
}
func pkgSort(s []*claircore.Package) func(i, j int) bool {
return func(i, j int) bool {
switch strings.Compare(s[i].Name, s[j].Name) {
case -1:
return true
case 0:
return strings.Compare(s[i].RepositoryHint, s[j].RepositoryHint) == -1
default:
}
return false
}
}