Skip to content

Commit c82d683

Browse files
marunmaru-avaCopilot
authored
[antithesis] Enable reuse of banff e2e test for antithesis testing (#3554)
Signed-off-by: maru <maru.newby@avalabs.org> Co-authored-by: Maru Newby <maru.newby@avalabs.org> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 28d5491 commit c82d683

File tree

12 files changed

+396
-172
lines changed

12 files changed

+396
-172
lines changed

tests/antithesis/avalanchego/main.go

Lines changed: 68 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,12 @@ import (
4444

4545
const NumKeys = 5
4646

47+
// TODO(marun) Extract the common elements of test execution for reuse across test setups
48+
4749
func main() {
4850
// TODO(marun) Support choosing the log format
49-
tc := tests.NewTestContext(tests.NewDefaultLogger(""))
50-
defer tc.Cleanup()
51+
tc := antithesis.NewInstrumentedTestContext(tests.NewDefaultLogger(""))
52+
defer tc.RecoverAndExit()
5153
require := require.New(tc)
5254

5355
c := antithesis.NewConfig(
@@ -57,17 +59,12 @@ func main() {
5759
},
5860
)
5961
ctx := tests.DefaultNotifyContext(c.Duration, tc.DeferCleanup)
62+
// Ensure contexts sourced from the test context use the notify context as their parent
63+
tc.SetDefaultContextParent(ctx)
6064

6165
kc := secp256k1fx.NewKeychain(genesis.EWOQKey)
6266
walletSyncStartTime := time.Now()
63-
wallet, err := primary.MakeWallet(
64-
ctx,
65-
c.URIs[0],
66-
kc,
67-
kc,
68-
primary.WalletConfig{},
69-
)
70-
require.NoError(err, "failed to initialize wallet")
67+
wallet := e2e.NewWallet(tc, kc, tmpnet.NodeURI{URI: c.URIs[0]})
7168
tc.Log().Info("synced wallet",
7269
zap.Duration("duration", time.Since(walletSyncStartTime)),
7370
)
@@ -122,14 +119,7 @@ func main() {
122119
uri := c.URIs[i%len(c.URIs)]
123120
kc := secp256k1fx.NewKeychain(key)
124121
walletSyncStartTime := time.Now()
125-
wallet, err := primary.MakeWallet(
126-
ctx,
127-
uri,
128-
kc,
129-
kc,
130-
primary.WalletConfig{},
131-
)
132-
require.NoError(err, "failed to initialize wallet")
122+
wallet := e2e.NewWallet(tc, kc, tmpnet.NodeURI{URI: uri})
133123
tc.Log().Info("synced wallet",
134124
zap.Duration("duration", time.Since(walletSyncStartTime)),
135125
)
@@ -162,11 +152,25 @@ type workload struct {
162152
uris []string
163153
}
164154

155+
// newTestContext returns a test context that ensures that log output and assertions are
156+
// associated with this worker.
157+
func (w *workload) newTestContext(ctx context.Context) *tests.SimpleTestContext {
158+
return antithesis.NewInstrumentedTestContextWithArgs(
159+
ctx,
160+
w.log,
161+
map[string]any{
162+
"worker": w.id,
163+
},
164+
)
165+
}
166+
165167
func (w *workload) run(ctx context.Context) {
166168
timer := timerpkg.StoppedTimer()
167169

168-
tc := tests.NewTestContext(w.log)
169-
defer tc.Cleanup()
170+
tc := w.newTestContext(ctx)
171+
// Any assertion failure from this test context will result in process exit due to the
172+
// panic being rethrown. This ensures that failures in test setup are fatal.
173+
defer tc.RecoverAndRethrow()
170174
require := require.New(tc)
171175

172176
xAVAX, pAVAX := e2e.GetWalletBalances(tc, w.wallet)
@@ -177,28 +181,9 @@ func (w *workload) run(ctx context.Context) {
177181
})
178182

179183
for {
180-
val, err := rand.Int(rand.Reader, big.NewInt(5))
181-
require.NoError(err, "failed to read randomness")
182-
183-
flowID := val.Int64()
184-
w.log.Info("executing test",
185-
zap.Int("workerID", w.id),
186-
zap.Int64("flowID", flowID),
187-
)
188-
switch flowID {
189-
case 0:
190-
w.issueXChainBaseTx(ctx)
191-
case 1:
192-
w.issueXChainCreateAssetTx(ctx)
193-
case 2:
194-
w.issueXChainOperationTx(ctx)
195-
case 3:
196-
w.issueXToPTransfer(ctx)
197-
case 4:
198-
w.issuePToXTransfer(ctx)
199-
}
184+
w.executeTest(ctx)
200185

201-
val, err = rand.Int(rand.Reader, big.NewInt(int64(time.Second)))
186+
val, err := rand.Int(rand.Reader, big.NewInt(int64(time.Second)))
202187
require.NoError(err, "failed to read randomness")
203188

204189
timer.Reset(time.Duration(val.Int64()))
@@ -210,6 +195,48 @@ func (w *workload) run(ctx context.Context) {
210195
}
211196
}
212197

198+
// executeTest executes a test at random.
199+
func (w *workload) executeTest(ctx context.Context) {
200+
tc := w.newTestContext(ctx)
201+
// Panics will be recovered without being rethrown, ensuring that test failures are not fatal.
202+
defer tc.Recover()
203+
require := require.New(tc)
204+
205+
// Ensure this value matches the number of tests + 1 to offset
206+
// 0-based + 1 for sleep case in the switch statement for flowID
207+
testCount := int64(6)
208+
209+
val, err := rand.Int(rand.Reader, big.NewInt(testCount))
210+
require.NoError(err, "failed to read randomness")
211+
212+
flowID := val.Int64()
213+
switch flowID {
214+
case 0:
215+
// TODO(marun) Create abstraction for a test that supports a name e.g. `aTest{name: "foo", mytestfunc}`
216+
w.log.Info("executing issueXChainBaseTx")
217+
w.issueXChainBaseTx(ctx)
218+
case 1:
219+
w.log.Info("executing issueXChainCreateAssetTx")
220+
w.issueXChainCreateAssetTx(ctx)
221+
case 2:
222+
w.log.Info("executing issueXChainOperationTx")
223+
w.issueXChainOperationTx(ctx)
224+
case 3:
225+
w.log.Info("executing issueXToPTransfer")
226+
w.issueXToPTransfer(ctx)
227+
case 4:
228+
w.log.Info("executing issuePToXTransfer")
229+
w.issuePToXTransfer(ctx)
230+
case 5:
231+
w.log.Info("sleeping")
232+
}
233+
234+
// TODO(marun) Enable execution of the banff e2e test as part of https://github.com/ava-labs/avalanchego/issues/4049
235+
// w.log.Info("executing banff.TestCustomAssetTransfer")
236+
// addr, _ := w.addrs.Peek()
237+
// banff.TestCustomAssetTransfer(tc, *w.wallet, addr)
238+
}
239+
213240
func (w *workload) issueXChainBaseTx(ctx context.Context) {
214241
var (
215242
xWallet = w.wallet.X()

tests/antithesis/context.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package antithesis
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"maps"
10+
11+
"github.com/antithesishq/antithesis-sdk-go/assert"
12+
13+
"github.com/ava-labs/avalanchego/tests"
14+
"github.com/ava-labs/avalanchego/utils/logging"
15+
)
16+
17+
// NewInstrumentedTestContext returns a test context that makes antithesis SDK assertions.
18+
func NewInstrumentedTestContext(log logging.Logger) *tests.SimpleTestContext {
19+
return NewInstrumentedTestContextWithArgs(context.Background(), log, nil)
20+
}
21+
22+
// NewInstrumentedTestContextWithArgs returns a test context that makes antithesis SDK assertions.
23+
func NewInstrumentedTestContextWithArgs(
24+
ctx context.Context,
25+
log logging.Logger,
26+
details map[string]any,
27+
) *tests.SimpleTestContext {
28+
return tests.NewTestContextWithArgs(
29+
ctx,
30+
log,
31+
func(format string, args ...any) {
32+
assert.Unreachable(fmt.Sprintf("Assertion failure: "+format, args...), details)
33+
},
34+
func(r any) {
35+
detailsClone := maps.Clone(details)
36+
detailsClone["panic"] = r
37+
assert.Unreachable("unexpected panic", detailsClone)
38+
},
39+
)
40+
}

tests/antithesis/xsvm/main.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ const (
3939

4040
func main() {
4141
// TODO(marun) Support choosing the log format
42-
tc := tests.NewTestContext(tests.NewDefaultLogger(""))
43-
defer tc.Cleanup()
42+
tc := antithesis.NewInstrumentedTestContext(tests.NewDefaultLogger(""))
43+
defer tc.RecoverAndExit()
4444
require := require.New(tc)
4545

4646
c := antithesis.NewConfigWithSubnets(
@@ -55,6 +55,8 @@ func main() {
5555
},
5656
)
5757
ctx := tests.DefaultNotifyContext(c.Duration, tc.DeferCleanup)
58+
// Ensure contexts sourced from the test context use the notify context as their parent
59+
tc.SetDefaultContextParent(ctx)
5860

5961
require.Len(c.ChainIDs, 1)
6062
tc.Log().Debug("raw chain ID",
@@ -140,8 +142,16 @@ type workload struct {
140142
func (w *workload) run(ctx context.Context) {
141143
timer := timerpkg.StoppedTimer()
142144

143-
tc := tests.NewTestContext(w.log)
144-
defer tc.Cleanup()
145+
tc := antithesis.NewInstrumentedTestContextWithArgs(
146+
ctx,
147+
w.log,
148+
map[string]any{
149+
"worker": w.id,
150+
},
151+
)
152+
// Any assertion failure from this test context will result in process exit due to the
153+
// panic being rethrown. This ensures that failures in test setup are fatal.
154+
defer tc.RecoverAndRethrow()
145155
require := require.New(tc)
146156

147157
uri := w.uris[w.id%len(w.uris)]

tests/context_helpers.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ const DefaultTimeout = 2 * time.Minute
1616

1717
// Helper simplifying use of a timed context by canceling the context with the test context.
1818
func ContextWithTimeout(tc TestContext, duration time.Duration) context.Context {
19-
ctx, cancel := context.WithTimeout(context.Background(), duration)
19+
parent := tc.GetDefaultContextParent()
20+
ctx, cancel := context.WithTimeout(parent, duration)
2021
tc.DeferCleanup(cancel)
2122
return ctx
2223
}

0 commit comments

Comments
 (0)