Skip to content

Commit 8931743

Browse files
committed
[testutil] Introduce test util to aid with testing
1 parent 56c0ee6 commit 8931743

File tree

6 files changed

+277
-18
lines changed

6 files changed

+277
-18
lines changed

pkg/leeway/package.go

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func FindUnresolvedArguments(pkg *Package) ([]string, error) {
4747
// re-marshal packageInternal without the variants. Their unresolved arguments do not matter.
4848
// Only when a variant becomes selected do its argumnents play a role, at which point they'll
4949
// show up during the config/env re-marshalling.
50-
pi := pkg.packageInternal
50+
pi := pkg.PackageInternal
5151
meta, err := yaml.Marshal(pi)
5252
if err != nil {
5353
return nil, err
@@ -135,27 +135,28 @@ func (n PackageNotFoundErr) Error() string {
135135
return fmt.Sprintf("package \"%s\" is unknown", n.Package)
136136
}
137137

138-
type packageInternal struct {
138+
// PackageInternal is the YAML serialised content of a package
139+
type PackageInternal struct {
139140
Name string `yaml:"name"`
140141
Type PackageType `yaml:"type"`
141-
Sources []string `yaml:"srcs"`
142-
Dependencies []string `yaml:"deps"`
143-
Layout map[string]string `yaml:"layout"`
144-
ArgumentDependencies []string `yaml:"argdeps"`
145-
Environment []string `yaml:"env"`
146-
Ephemeral bool `yaml:"ephemeral"`
147-
PreparationCommands [][]string `yaml:"prep"`
142+
Sources []string `yaml:"srcs,omitempty"`
143+
Dependencies []string `yaml:"deps,omitempty"`
144+
Layout map[string]string `yaml:"layout,omitempty"`
145+
ArgumentDependencies []string `yaml:"argdeps,omitempty"`
146+
Environment []string `yaml:"env,omitempty"`
147+
Ephemeral bool `yaml:"ephemeral,omitempty"`
148+
PreparationCommands [][]string `yaml:"prep,omitempty"`
148149
}
149150

150151
// Package is a single buildable artifact within a component
151152
type Package struct {
152-
C *Component
153+
C *Component `yaml:"-"`
153154

154155
// computing the version is expensive - let's cache that
155156
versionCache string
156157

157-
packageInternal
158-
Config PackageConfig `yaml:"config"`
158+
PackageInternal `yaml:"_,inline"`
159+
Config PackageConfig `yaml:"config,omitempty"`
159160
// Definition is the raw package definition YAML
160161
Definition []byte `yaml:"-"`
161162

@@ -328,12 +329,12 @@ func (p *Package) BuildLayoutLocation(dependency *Package) (loc string) {
328329

329330
// UnmarshalYAML unmarshals the package definition
330331
func (p *Package) UnmarshalYAML(unmarshal func(interface{}) error) error {
331-
var tpe packageInternal
332+
var tpe PackageInternal
332333
err := unmarshal(&tpe)
333334
if err != nil {
334335
return err
335336
}
336-
*p = Package{packageInternal: tpe}
337+
*p = Package{PackageInternal: tpe}
337338

338339
var buf yaml.Node
339340
err = unmarshal(&buf)

pkg/leeway/package_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ func NewTestPackage(name string) *Package {
301301
Name: "testcomp",
302302
},
303303

304-
packageInternal: packageInternal{
304+
PackageInternal: PackageInternal{
305305
Name: name,
306306
Type: GenericPackage,
307307
},

pkg/leeway/scripts.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ func (p *Script) Run(opts ...BuildOption) error {
109109
err = Build(&Package{
110110
C: p.C,
111111
dependencies: p.dependencies,
112-
packageInternal: packageInternal{
112+
PackageInternal: PackageInternal{
113113
Name: fmt.Sprintf("%s-deps", p.Name),
114114
Environment: p.Environment,
115115
Ephemeral: true,

pkg/leeway/workspace.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ type WorkspaceProvenance struct {
5454
Enabled bool `yaml:"enabled"`
5555
SLSA bool `yaml:"slsa"`
5656

57-
KeyPath string `yaml:"key"`
58-
key *in_toto.Key
57+
KeyPath string `yaml:"key"`
58+
key *in_toto.Key `yaml:"-"`
5959
}
6060

6161
// EnvironmentManifest is a collection of environment manifest entries

pkg/testutil/testutil.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package testutil
2+
3+
import (
4+
"io"
5+
"os"
6+
"path/filepath"
7+
8+
"github.com/gitpod-io/leeway/pkg/leeway"
9+
"gopkg.in/yaml.v3"
10+
)
11+
12+
type Setup struct {
13+
Workspace leeway.Workspace `yaml:"workspace"`
14+
Components []Component `yaml:"components"`
15+
}
16+
17+
type Component struct {
18+
Location string `yaml:"location"`
19+
Files map[string]string `yaml:"files"`
20+
Comp leeway.Component `yaml:"comp"`
21+
Packages []leeway.Package `yaml:"packages"`
22+
}
23+
24+
// LoadFromYAML loads a workspace setup from a YAML file
25+
func LoadFromYAML(in io.Reader) (*Setup, error) {
26+
fc, err := io.ReadAll(in)
27+
if err != nil {
28+
return nil, err
29+
}
30+
31+
var res Setup
32+
err = yaml.Unmarshal(fc, &res)
33+
if err != nil {
34+
return nil, err
35+
}
36+
37+
return &res, nil
38+
}
39+
40+
// Materialize produces a leeway workspace according to the setup spec
41+
func (s Setup) Materialize() (workspaceRoot string, err error) {
42+
workspaceRoot, err = os.MkdirTemp("", "leeway-test-*")
43+
if err != nil {
44+
return
45+
}
46+
47+
fc, err := yaml.Marshal(s.Workspace)
48+
if err != nil {
49+
return
50+
}
51+
err = os.WriteFile(filepath.Join(workspaceRoot, "WORKSPACE.yaml"), fc, 0644)
52+
if err != nil {
53+
return
54+
}
55+
56+
for _, comp := range s.Components {
57+
err = os.MkdirAll(filepath.Join(workspaceRoot, comp.Location), 0755)
58+
if err != nil {
59+
return
60+
}
61+
62+
cmp := struct {
63+
Constants leeway.Arguments `yaml:"const,omitempty"`
64+
Packages []leeway.Package `yaml:"packages,omitempty"`
65+
Scripts []*leeway.Script `yaml:"scripts,omitempty"`
66+
}{
67+
Constants: comp.Comp.Constants,
68+
Packages: comp.Packages,
69+
Scripts: comp.Comp.Scripts,
70+
}
71+
72+
fc, err = yaml.Marshal(cmp)
73+
if err != nil {
74+
return
75+
}
76+
77+
err = os.WriteFile(filepath.Join(workspaceRoot, comp.Location, "BUILD.yaml"), fc, 0644)
78+
if err != nil {
79+
return
80+
}
81+
}
82+
83+
return
84+
}

pkg/testutil/testutil_test.go

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
package testutil
2+
3+
import (
4+
"bytes"
5+
"crypto/sha256"
6+
"fmt"
7+
"io"
8+
"os"
9+
"path/filepath"
10+
"testing"
11+
12+
"github.com/gitpod-io/leeway/pkg/leeway"
13+
"github.com/google/go-cmp/cmp"
14+
"github.com/google/go-cmp/cmp/cmpopts"
15+
)
16+
17+
func TestLoadFromYAML(t *testing.T) {
18+
ignores := cmpopts.IgnoreUnexported(
19+
leeway.Workspace{},
20+
leeway.Component{},
21+
leeway.Package{},
22+
leeway.WorkspaceProvenance{},
23+
leeway.GitInfo{},
24+
)
25+
26+
tests := []struct {
27+
Name string
28+
Content string
29+
Expectation *Setup
30+
}{
31+
{
32+
Name: "empty",
33+
Expectation: &Setup{},
34+
},
35+
{
36+
Name: "single",
37+
Expectation: &Setup{
38+
Components: []Component{
39+
{
40+
Location: "comp1",
41+
Packages: []leeway.Package{
42+
{
43+
PackageInternal: leeway.PackageInternal{
44+
Name: "pkg1",
45+
Type: leeway.GenericPackage,
46+
Sources: []string{"**"},
47+
},
48+
Config: leeway.GenericPkgConfig{
49+
Commands: [][]string{{"foo"}},
50+
},
51+
},
52+
},
53+
},
54+
},
55+
},
56+
Content: `components:
57+
- location: comp1
58+
packages:
59+
- name: pkg1
60+
type: generic
61+
srcs:
62+
- "**"
63+
config:
64+
commands: [["foo"]]`,
65+
},
66+
}
67+
68+
for _, test := range tests {
69+
t.Run(test.Name, func(t *testing.T) {
70+
act, err := LoadFromYAML(bytes.NewBufferString(test.Content))
71+
if err != nil {
72+
t.Fatal(err)
73+
}
74+
75+
if diff := cmp.Diff(test.Expectation, act, ignores); diff != "" {
76+
t.Errorf("LoadFromYAML() mismatch (-want +got):\n%s", diff)
77+
}
78+
})
79+
}
80+
}
81+
82+
func TestMaterialise(t *testing.T) {
83+
type File struct {
84+
Path string
85+
Size int64
86+
SHA256 string
87+
}
88+
tests := []struct {
89+
Name string
90+
Setup Setup
91+
Expectation []File
92+
}{
93+
{
94+
Name: "simple",
95+
Setup: Setup{
96+
Components: []Component{
97+
{
98+
Location: "comp1",
99+
Files: map[string]string{
100+
"someFile": "content",
101+
"some/other/file": "more content",
102+
},
103+
Comp: leeway.Component{
104+
Constants: leeway.Arguments{
105+
"hello": "world",
106+
},
107+
},
108+
Packages: []leeway.Package{
109+
{
110+
PackageInternal: leeway.PackageInternal{
111+
Name: "pkg1",
112+
Type: leeway.GenericPackage,
113+
Sources: []string{"**"},
114+
},
115+
Config: leeway.GenericPkgConfig{
116+
Commands: [][]string{{"bla"}},
117+
},
118+
},
119+
},
120+
},
121+
},
122+
},
123+
Expectation: []File{
124+
{Path: "WORKSPACE.yaml"},
125+
{Path: "comp1/BUILD.yaml", SHA256: "6cc73a81aa8f00851c4738947d0d22ae296d8c626af4d1f4fe60fbabb09f906f"},
126+
},
127+
},
128+
}
129+
130+
for _, test := range tests {
131+
t.Run(test.Name, func(t *testing.T) {
132+
loc, err := test.Setup.Materialize()
133+
if err != nil {
134+
t.Fatal(err)
135+
}
136+
t.Cleanup(func() { os.RemoveAll(loc) })
137+
t.Logf("materialized at %s", loc)
138+
139+
for _, f := range test.Expectation {
140+
fn := filepath.Join(loc, f.Path)
141+
stat, err := os.Stat(fn)
142+
if err != nil {
143+
t.Errorf("expected file mismatch: %s: %v", f.Path, err)
144+
continue
145+
}
146+
147+
if f.Size > 0 && stat.Size() != f.Size {
148+
t.Errorf("expected file size mismatch: %s: expected %d, got %d", f.Path, f.Size, stat.Size())
149+
}
150+
151+
if f.SHA256 == "" {
152+
continue
153+
}
154+
hash := sha256.New()
155+
fp, err := os.Open(fn)
156+
if err != nil {
157+
t.Errorf("cannot hash %s: %v", fn, err)
158+
continue
159+
}
160+
_, err = io.Copy(hash, fp)
161+
fp.Close()
162+
if err != nil {
163+
t.Errorf("cannot hash %s: %v", fn, err)
164+
continue
165+
}
166+
167+
sum := fmt.Sprintf("%x", hash.Sum(nil))
168+
if f.SHA256 != sum {
169+
t.Errorf("file hash mismatch: %s: expected %s, got %s", f.Path, f.SHA256, sum)
170+
}
171+
}
172+
})
173+
}
174+
}

0 commit comments

Comments
 (0)