Skip to content

Commit efbdc30

Browse files
committed
Add Data assets
Many tests require temporary resources (Dockerfiles, compose.yml, etc), and routinely redo the same thing over and over (create separate temp dir, write file, remove temp dir). At best this adds a lot of boilerplate / helper functions to the test - at worst, the resources are not properly cleaned-up, or not well isolated from other test. This changeset introduces Data helpers to take care of these (AssetSave, AssetLoad, AssetPath). Signed-off-by: apostasie <[email protected]>
1 parent a3d981b commit efbdc30

File tree

8 files changed

+132
-15
lines changed

8 files changed

+132
-15
lines changed

mod/tigron/expect/comparators.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func JSON[T any](obj T, verifier func(T, string, tig.T)) test.Comparator {
7979
t.Helper()
8080

8181
err := json.Unmarshal([]byte(stdout), &obj)
82-
assertive.ErrorIsNil(assertive.WithFailLater(t), err, "Unmarshalling JSON from stdout must succeed")
82+
assertive.ErrorIsNil(assertive.WithSilentSuccess(t), err, "Unmarshalling JSON from stdout must succeed")
8383

8484
if verifier != nil && err == nil {
8585
verifier(obj, "Inspecting output (JSON)", t)

mod/tigron/internal/assertive/assertive.go

+1
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ func True(testing tig.T, comp bool, msg ...string) bool {
165165
// Failing later is necessary when asserting inside go routines, and also if you want many
166166
// successive asserts to all
167167
// evaluate instead of stopping at the first failing one.
168+
// FIXME: it should be possible to have both WithFailLater and WithSilentSuccess at the same time.
168169
func WithFailLater(t tig.T) tig.T {
169170
return &failLater{
170171
t,

mod/tigron/test/command.go

+8-6
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ func (gc *GenericCommand) Run(expect *Expected) {
191191
debug = append(debug, []any{"Signal", result.Signal.String()})
192192
}
193193

194+
// FIXME: wire this
194195
if gc.fullDebug {
195196
debug = append(debug,
196197
[]any{"Environ", strings.Join(result.Environ, "\n")},
@@ -199,12 +200,13 @@ func (gc *GenericCommand) Run(expect *Expected) {
199200
}
200201

201202
// FIXME: wire this so that we can get the environ display back in
202-
if gc.fullDebug {
203-
debug = append(debug,
204-
[]any{"Dir", gc.cmd.WorkingDir},
205-
[]any{"Limit", gc.cmd.Timeout},
206-
)
207-
}
203+
// FIXME: we should also be able to inspect `test.Config` and `test.Data` (especially with assets)
204+
// if gc.fullDebug {
205+
debug = append(debug,
206+
[]any{"Dir", gc.cmd.WorkingDir},
207+
[]any{"Limit", gc.cmd.Timeout},
208+
)
209+
// }
208210

209211
gc.t.Log("\n\n" + formatter.Table(debug, "-") + "\n")
210212

mod/tigron/test/consts.go

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package test
18+
19+
const (
20+
// FilePermissionsDefault specifies the default creation mode for temporary files.
21+
// Note that umask will affect these.
22+
FilePermissionsDefault = 0o644
23+
// DirPermissionsDefault specifies the default creation mode for temporary directories.
24+
// Note that umask will affect these.
25+
DirPermissionsDefault = 0o755
26+
)

mod/tigron/test/data.go

+59-6
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,13 @@ package test
1919
import (
2020
"crypto/sha256"
2121
"fmt"
22+
"os"
23+
"path/filepath"
2224
"regexp"
2325
"strings"
24-
"testing"
26+
27+
"github.com/containerd/nerdctl/mod/tigron/internal/assertive"
28+
"github.com/containerd/nerdctl/mod/tigron/tig"
2529
)
2630

2731
const (
@@ -38,26 +42,49 @@ func WithData(key, value string) Data {
3842
return dat
3943
}
4044

41-
// Contains the implementation of the Data interface.
42-
func configureData(t *testing.T, seedData, parent Data) Data {
45+
// Contains the implementation of the Data interface
46+
//
47+
//nolint:varnamelen
48+
func configureData(t tig.T, seedData, parent Data) Data {
4349
t.Helper()
4450

51+
silentT := assertive.WithSilentSuccess(t)
52+
4553
if seedData == nil {
4654
seedData = &data{}
4755
}
4856

49-
//nolint:forcetypeassert
57+
var labels map[string]string
58+
if castData, ok := seedData.(*data); ok {
59+
labels = castData.labels
60+
}
61+
5062
dat := &data{
5163
// Note: implementation dependent
52-
labels: seedData.(*data).labels,
53-
tempDir: t.TempDir(),
64+
labels: labels,
5465
testID: func(suffix ...string) string {
5566
suffix = append([]string{t.Name()}, suffix...)
5667

5768
return defaultIdentifierHashing(suffix...)
5869
},
70+
t: silentT,
5971
}
6072

73+
// NOTE: certain systems will use the path dirname to decide how they name resources.
74+
// t.TempDir() will always return /tmp/TestTempDir2153252249/001, meaning these systems will all
75+
// use the identical 001 part. This is true for compose specifically.
76+
// Appending the base test identifier here would guarantee better unicity.
77+
// Note though that Windows will barf if >256 characters, so, hashing...
78+
// Small caveat: identically named tests in different modules WILL still end-up with the same last segment.
79+
tempDir := filepath.Join(
80+
t.TempDir(),
81+
fmt.Sprintf("%x", sha256.Sum256([]byte(t.Name())))[0:identifierSignatureLength],
82+
)
83+
84+
assertive.ErrorIsNil(silentT, os.MkdirAll(tempDir, DirPermissionsDefault))
85+
86+
dat.tempDir = tempDir
87+
6188
if parent != nil {
6289
dat.adopt(parent)
6390
}
@@ -69,6 +96,7 @@ type data struct {
6996
labels map[string]string
7097
testID func(suffix ...string) string
7198
tempDir string
99+
t tig.T
72100
}
73101

74102
func (dt *data) Get(key string) string {
@@ -85,6 +113,31 @@ func (dt *data) Set(key, value string) Data {
85113
return dt
86114
}
87115

116+
func (dt *data) AssetLoad(key string) string {
117+
//nolint:gosec
118+
content, err := os.ReadFile(filepath.Join(dt.tempDir, key))
119+
120+
assertive.ErrorIsNil(dt.t, err)
121+
122+
return string(content)
123+
}
124+
125+
func (dt *data) AssetSave(key, value string) Data {
126+
err := os.WriteFile(
127+
filepath.Join(dt.tempDir, key),
128+
[]byte(value),
129+
FilePermissionsDefault,
130+
)
131+
132+
assertive.ErrorIsNil(dt.t, err)
133+
134+
return dt
135+
}
136+
137+
func (dt *data) AssetPath(key string) string {
138+
return filepath.Join(dt.tempDir, key)
139+
}
140+
88141
func (dt *data) Identifier(suffix ...string) string {
89142
return dt.testID(suffix...)
90143
}

mod/tigron/test/data_test.go

+25
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,23 @@ func TestDataBasic(t *testing.T) {
3434

3535
dataObj.Set("test", "set")
3636
assertive.IsEqual(t, dataObj.Get("test"), "set")
37+
38+
t.Run("verify that (parallel) subtest can access parent data", func(t *testing.T) {
39+
t.Parallel()
40+
41+
assertive.IsEqual(t, dataObj.Get("doesnotexist"), "")
42+
// NOTE: this is really tricky. Test being parallel means it will execute once the parent is done.
43+
assertive.IsEqual(t, dataObj.Get("test"), "setagain")
44+
})
45+
46+
//nolint:paralleltest
47+
t.Run("verify that (non-parallel) subtest can access parent data", func(t *testing.T) {
48+
assertive.IsEqual(t, dataObj.Get("doesnotexist"), "")
49+
assertive.IsEqual(t, dataObj.Get("test"), "set")
50+
})
51+
52+
dataObj.Set("test", "setagain")
53+
assertive.IsEqual(t, dataObj.Get("test"), "setagain")
3754
}
3855

3956
func TestDataTempDir(t *testing.T) {
@@ -46,6 +63,14 @@ func TestDataTempDir(t *testing.T) {
4663

4764
assertive.IsEqual(t, one, two)
4865
assertive.IsNotEqual(t, one, "")
66+
67+
t.Run("verify that subtest has an independent TempDir", func(t *testing.T) {
68+
t.Parallel()
69+
70+
dataObj = configureData(t, nil, nil)
71+
three := dataObj.TempDir()
72+
assertive.IsNotEqual(t, one, three)
73+
})
4974
}
5075

5176
func TestDataIdentifier(t *testing.T) {

mod/tigron/test/funct.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@ type Evaluator func(data Data, helpers Helpers) (bool, string)
2525
// or Requirement.
2626
type Butler func(data Data, helpers Helpers)
2727

28+
// TODO: when we will break API:
29+
// - remove the info parameter
30+
// - move to tig.T
31+
2832
// A Comparator is the function signature to implement for the Output property of an Expected.
29-
// TODO: when we will break API, remove the info parameter.
3033
type Comparator func(stdout, info string, t *testing.T)
3134

3235
// A Manager is the function signature meant to produce expectations for a command.

mod/tigron/test/interfaces.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,20 @@ import (
2828
// test data - second, some commonly useful immutable test properties (a way to generate unique
2929
// identifiers for that test, temporary directory, etc.)
3030
// Note that Data is inherited, from parent test to subtest (except for Identifier and TempDir of
31-
// course).
31+
// course - along with Assets, which are private as well).
3232
type Data interface {
3333
// Get returns the value of a certain key for custom data
3434
Get(key string) string
3535
// Set will save `value` for `key`
3636
Set(key, value string) Data
3737

38+
// AssetSave will store data on the filesystem, inside the test private temp directory
39+
AssetSave(key, value string) Data
40+
// AssetLoad will retrieve previously stored data from the filesystem
41+
AssetLoad(key string) string
42+
// AssetPath will return the absolute path for the asset
43+
AssetPath(key string) string
44+
3845
// Identifier returns the test identifier that can be used to name resources
3946
Identifier(suffix ...string) string
4047
// TempDir returns the test temporary directory

0 commit comments

Comments
 (0)