Commit d1e27cb5 authored by protolambda's avatar protolambda

op-e2e: cleanup test actors, run all action tests in parallel

parent 225e1643
...@@ -50,9 +50,10 @@ type StatefulTesting interface { ...@@ -50,9 +50,10 @@ type StatefulTesting interface {
State() ActionStatus State() ActionStatus
} }
// NewDefaultTesting returns a new testing obj. // NewDefaultTesting returns a new testing obj, and enables parallel test execution.
// Returns an interface, we're likely changing the behavior here as we build more action tests. // Returns an interface, we're likely changing the behavior here as we build more action tests.
func NewDefaultTesting(tb e2eutils.TestingBase) StatefulTesting { func NewDefaultTesting(tb e2eutils.TestingBase) StatefulTesting {
tb.Parallel()
return &defaultTesting{ return &defaultTesting{
TestingBase: tb, TestingBase: tb,
ctx: context.Background(), ctx: context.Background(),
......
...@@ -32,8 +32,8 @@ type L1Miner struct { ...@@ -32,8 +32,8 @@ type L1Miner struct {
} }
// NewL1Miner creates a new L1Replica that can also build blocks. // NewL1Miner creates a new L1Replica that can also build blocks.
func NewL1Miner(log log.Logger, genesis *core.Genesis) *L1Miner { func NewL1Miner(t Testing, log log.Logger, genesis *core.Genesis) *L1Miner {
rep := NewL1Replica(log, genesis) rep := NewL1Replica(t, log, genesis)
return &L1Miner{ return &L1Miner{
L1Replica: *rep, L1Replica: *rep,
} }
......
...@@ -18,7 +18,7 @@ func TestL1Miner_BuildBlock(gt *testing.T) { ...@@ -18,7 +18,7 @@ func TestL1Miner_BuildBlock(gt *testing.T) {
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug) log := testlog.Logger(t, log.LvlDebug)
miner := NewL1Miner(log, sd.L1Cfg) miner := NewL1Miner(t, log, sd.L1Cfg)
t.Cleanup(func() { t.Cleanup(func() {
_ = miner.Close() _ = miner.Close()
}) })
...@@ -55,7 +55,7 @@ func TestL1Miner_BuildBlock(gt *testing.T) { ...@@ -55,7 +55,7 @@ func TestL1Miner_BuildBlock(gt *testing.T) {
require.Equal(t, tx.Hash(), bl.Transactions()[0].Hash()) require.Equal(t, tx.Hash(), bl.Transactions()[0].Hash())
// now make a replica that syncs these two blocks from the miner // now make a replica that syncs these two blocks from the miner
replica := NewL1Replica(log, sd.L1Cfg) replica := NewL1Replica(t, log, sd.L1Cfg)
t.Cleanup(func() { t.Cleanup(func() {
_ = replica.Close() _ = replica.Close()
}) })
......
...@@ -2,12 +2,7 @@ package actions ...@@ -2,12 +2,7 @@ package actions
import ( import (
"errors" "errors"
"fmt"
"github.com/ethereum-optimism/optimism/op-node/client"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-node/testutils"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
...@@ -19,6 +14,11 @@ import ( ...@@ -19,6 +14,11 @@ import (
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/op-node/client"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-node/testutils"
) )
// L1CanonSrc is used to sync L1 from another node. // L1CanonSrc is used to sync L1 from another node.
...@@ -46,7 +46,7 @@ type L1Replica struct { ...@@ -46,7 +46,7 @@ type L1Replica struct {
} }
// NewL1Replica constructs a L1Replica starting at the given genesis. // NewL1Replica constructs a L1Replica starting at the given genesis.
func NewL1Replica(log log.Logger, genesis *core.Genesis) *L1Replica { func NewL1Replica(t Testing, log log.Logger, genesis *core.Genesis) *L1Replica {
ethCfg := &ethconfig.Config{ ethCfg := &ethconfig.Config{
NetworkId: genesis.Config.ChainID.Uint64(), NetworkId: genesis.Config.ChainID.Uint64(),
Genesis: genesis, Genesis: genesis,
...@@ -65,20 +65,17 @@ func NewL1Replica(log log.Logger, genesis *core.Genesis) *L1Replica { ...@@ -65,20 +65,17 @@ func NewL1Replica(log log.Logger, genesis *core.Genesis) *L1Replica {
}, },
} }
n, err := node.New(nodeCfg) n, err := node.New(nodeCfg)
if err != nil { require.NoError(t, err)
panic(err) t.Cleanup(func() {
} _ = n.Close()
})
backend, err := eth.New(n, ethCfg) backend, err := eth.New(n, ethCfg)
if err != nil { require.NoError(t, err)
panic(err)
}
n.RegisterAPIs(tracers.APIs(backend.APIBackend)) n.RegisterAPIs(tracers.APIs(backend.APIBackend))
if err := n.Start(); err != nil { require.NoError(t, n.Start(), "failed to start L1 geth node")
panic(fmt.Errorf("failed to start L1 geth node: %w", err))
}
return &L1Replica{ return &L1Replica{
log: log, log: log,
node: n, node: n,
......
...@@ -32,7 +32,7 @@ func TestL1Replica_ActL1RPCFail(gt *testing.T) { ...@@ -32,7 +32,7 @@ func TestL1Replica_ActL1RPCFail(gt *testing.T) {
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug) log := testlog.Logger(t, log.LvlDebug)
replica := NewL1Replica(log, sd.L1Cfg) replica := NewL1Replica(t, log, sd.L1Cfg)
t.Cleanup(func() { t.Cleanup(func() {
_ = replica.Close() _ = replica.Close()
}) })
...@@ -78,7 +78,7 @@ func TestL1Replica_ActL1Sync(gt *testing.T) { ...@@ -78,7 +78,7 @@ func TestL1Replica_ActL1Sync(gt *testing.T) {
} }
// Enough setup, create the test actor and run the actual actions // Enough setup, create the test actor and run the actual actions
replica1 := NewL1Replica(log, sd.L1Cfg) replica1 := NewL1Replica(t, log, sd.L1Cfg)
t.Cleanup(func() { t.Cleanup(func() {
_ = replica1.Close() _ = replica1.Close()
}) })
...@@ -99,7 +99,7 @@ func TestL1Replica_ActL1Sync(gt *testing.T) { ...@@ -99,7 +99,7 @@ func TestL1Replica_ActL1Sync(gt *testing.T) {
require.Equal(t, replica1.l1Chain.CurrentBlock().Hash(), chainB[len(chainB)-1].Hash(), "sync replica1 to head of chain B") require.Equal(t, replica1.l1Chain.CurrentBlock().Hash(), chainB[len(chainB)-1].Hash(), "sync replica1 to head of chain B")
// Adding and syncing a new replica // Adding and syncing a new replica
replica2 := NewL1Replica(log, sd.L1Cfg) replica2 := NewL1Replica(t, log, sd.L1Cfg)
t.Cleanup(func() { t.Cleanup(func() {
_ = replica2.Close() _ = replica2.Close()
}) })
......
...@@ -4,12 +4,13 @@ import ( ...@@ -4,12 +4,13 @@ import (
"math/big" "math/big"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-node/testlog"
) )
func TestBatcher(gt *testing.T) { func TestBatcher(gt *testing.T) {
......
...@@ -2,7 +2,6 @@ package actions ...@@ -2,7 +2,6 @@ package actions
import ( import (
"errors" "errors"
"fmt"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -58,7 +57,7 @@ type L2Engine struct { ...@@ -58,7 +57,7 @@ type L2Engine struct {
failL2RPC error // mock error failL2RPC error // mock error
} }
func NewL2Engine(log log.Logger, genesis *core.Genesis, rollupGenesisL1 eth.BlockID, jwtPath string) *L2Engine { func NewL2Engine(t Testing, log log.Logger, genesis *core.Genesis, rollupGenesisL1 eth.BlockID, jwtPath string) *L2Engine {
ethCfg := &ethconfig.Config{ ethCfg := &ethconfig.Config{
NetworkId: genesis.Config.ChainID.Uint64(), NetworkId: genesis.Config.ChainID.Uint64(),
Genesis: genesis, Genesis: genesis,
...@@ -74,13 +73,12 @@ func NewL2Engine(log log.Logger, genesis *core.Genesis, rollupGenesisL1 eth.Bloc ...@@ -74,13 +73,12 @@ func NewL2Engine(log log.Logger, genesis *core.Genesis, rollupGenesisL1 eth.Bloc
JWTSecret: jwtPath, JWTSecret: jwtPath,
} }
n, err := node.New(nodeCfg) n, err := node.New(nodeCfg)
if err != nil { require.NoError(t, err)
panic(err) t.Cleanup(func() {
} _ = n.Close()
})
backend, err := geth.New(n, ethCfg) backend, err := geth.New(n, ethCfg)
if err != nil { require.NoError(t, err)
panic(err)
}
n.RegisterAPIs(tracers.APIs(backend.APIBackend)) n.RegisterAPIs(tracers.APIs(backend.APIBackend))
chain := backend.BlockChain() chain := backend.BlockChain()
...@@ -109,9 +107,7 @@ func NewL2Engine(log log.Logger, genesis *core.Genesis, rollupGenesisL1 eth.Bloc ...@@ -109,9 +107,7 @@ func NewL2Engine(log log.Logger, genesis *core.Genesis, rollupGenesisL1 eth.Bloc
Authenticated: true, Authenticated: true,
}, },
}) })
if err := n.Start(); err != nil { require.NoError(t, n.Start(), "failed to start L2 op-geth node")
panic(fmt.Errorf("failed to start L2 op-geth node: %w", err))
}
return eng return eng
} }
......
...@@ -32,7 +32,7 @@ func TestL2EngineAPI(gt *testing.T) { ...@@ -32,7 +32,7 @@ func TestL2EngineAPI(gt *testing.T) {
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
sd.L2Cfg.MustCommit(db) sd.L2Cfg.MustCommit(db)
engine := NewL2Engine(log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath) engine := NewL2Engine(t, log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath)
l2Cl, err := sources.NewEngineClient(engine.RPCClient(), log, nil, sources.EngineClientDefaultConfig(sd.RollupCfg)) l2Cl, err := sources.NewEngineClient(engine.RPCClient(), log, nil, sources.EngineClientDefaultConfig(sd.RollupCfg))
require.NoError(t, err) require.NoError(t, err)
...@@ -94,7 +94,7 @@ func TestL2EngineAPIBlockBuilding(gt *testing.T) { ...@@ -94,7 +94,7 @@ func TestL2EngineAPIBlockBuilding(gt *testing.T) {
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
sd.L2Cfg.MustCommit(db) sd.L2Cfg.MustCommit(db)
engine := NewL2Engine(log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath) engine := NewL2Engine(t, log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath)
t.Cleanup(func() { t.Cleanup(func() {
_ = engine.Close() _ = engine.Close()
}) })
...@@ -174,7 +174,7 @@ func TestL2EngineAPIFail(gt *testing.T) { ...@@ -174,7 +174,7 @@ func TestL2EngineAPIFail(gt *testing.T) {
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug) log := testlog.Logger(t, log.LvlDebug)
engine := NewL2Engine(log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath) engine := NewL2Engine(t, log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath)
// mock an RPC failure // mock an RPC failure
engine.ActL2RPCFail(t) engine.ActL2RPCFail(t)
// check RPC failure // check RPC failure
......
package actions package actions
import ( import (
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/rollup/driver" "github.com/ethereum-optimism/optimism/op-node/rollup/driver"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
) )
// L2Sequencer is an actor that functions like a rollup node, // L2Sequencer is an actor that functions like a rollup node,
...@@ -22,8 +23,8 @@ type L2Sequencer struct { ...@@ -22,8 +23,8 @@ type L2Sequencer struct {
failL2GossipUnsafeBlock error // mock error failL2GossipUnsafeBlock error // mock error
} }
func NewL2Sequencer(log log.Logger, l1 derive.L1Fetcher, eng L2API, cfg *rollup.Config, seqConfDepth uint64) *L2Sequencer { func NewL2Sequencer(t Testing, log log.Logger, l1 derive.L1Fetcher, eng L2API, cfg *rollup.Config, seqConfDepth uint64) *L2Sequencer {
ver := NewL2Verifier(log, l1, eng, cfg) ver := NewL2Verifier(t, log, l1, eng, cfg)
return &L2Sequencer{ return &L2Sequencer{
L2Verifier: *ver, L2Verifier: *ver,
sequencer: driver.NewSequencer(log, cfg, l1, eng), sequencer: driver.NewSequencer(log, cfg, l1, eng),
......
...@@ -19,15 +19,15 @@ import ( ...@@ -19,15 +19,15 @@ import (
func setupSequencerTest(t Testing, sd *e2eutils.SetupData, log log.Logger) (*L1Miner, *L2Engine, *L2Sequencer) { func setupSequencerTest(t Testing, sd *e2eutils.SetupData, log log.Logger) (*L1Miner, *L2Engine, *L2Sequencer) {
jwtPath := e2eutils.WriteDefaultJWT(t) jwtPath := e2eutils.WriteDefaultJWT(t)
miner := NewL1Miner(log, sd.L1Cfg) miner := NewL1Miner(t, log, sd.L1Cfg)
l1F, err := sources.NewL1Client(miner.RPCClient(), log, nil, sources.L1ClientDefaultConfig(sd.RollupCfg, false)) l1F, err := sources.NewL1Client(miner.RPCClient(), log, nil, sources.L1ClientDefaultConfig(sd.RollupCfg, false))
require.NoError(t, err) require.NoError(t, err)
engine := NewL2Engine(log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath) engine := NewL2Engine(t, log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath)
l2Cl, err := sources.NewEngineClient(engine.RPCClient(), log, nil, sources.EngineClientDefaultConfig(sd.RollupCfg)) l2Cl, err := sources.NewEngineClient(engine.RPCClient(), log, nil, sources.EngineClientDefaultConfig(sd.RollupCfg))
require.NoError(t, err) require.NoError(t, err)
sequencer := NewL2Sequencer(log, l1F, l2Cl, sd.RollupCfg, 0) sequencer := NewL2Sequencer(t, log, l1F, l2Cl, sd.RollupCfg, 0)
return miner, engine, sequencer return miner, engine, sequencer
} }
......
...@@ -5,6 +5,12 @@ import ( ...@@ -5,6 +5,12 @@ import (
"errors" "errors"
"io" "io"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
gnode "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/op-node/client" "github.com/ethereum-optimism/optimism/op-node/client"
"github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/node" "github.com/ethereum-optimism/optimism/op-node/node"
...@@ -13,11 +19,6 @@ import ( ...@@ -13,11 +19,6 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup/driver" "github.com/ethereum-optimism/optimism/op-node/rollup/driver"
"github.com/ethereum-optimism/optimism/op-node/sources" "github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-node/testutils" "github.com/ethereum-optimism/optimism/op-node/testutils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
gnode "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/require"
) )
// L2Verifier is an actor that functions like a rollup node, // L2Verifier is an actor that functions like a rollup node,
...@@ -50,7 +51,7 @@ type L2API interface { ...@@ -50,7 +51,7 @@ type L2API interface {
GetProof(ctx context.Context, address common.Address, blockTag string) (*eth.AccountResult, error) GetProof(ctx context.Context, address common.Address, blockTag string) (*eth.AccountResult, error)
} }
func NewL2Verifier(log log.Logger, l1 derive.L1Fetcher, eng L2API, cfg *rollup.Config) *L2Verifier { func NewL2Verifier(t Testing, log log.Logger, l1 derive.L1Fetcher, eng L2API, cfg *rollup.Config) *L2Verifier {
metrics := &testutils.TestDerivationMetrics{} metrics := &testutils.TestDerivationMetrics{}
pipeline := derive.NewDerivationPipeline(log, cfg, l1, eng, metrics) pipeline := derive.NewDerivationPipeline(log, cfg, l1, eng, metrics)
pipeline.Reset() pipeline.Reset()
...@@ -66,6 +67,7 @@ func NewL2Verifier(log log.Logger, l1 derive.L1Fetcher, eng L2API, cfg *rollup.C ...@@ -66,6 +67,7 @@ func NewL2Verifier(log log.Logger, l1 derive.L1Fetcher, eng L2API, cfg *rollup.C
rollupCfg: cfg, rollupCfg: cfg,
rpc: rpc.NewServer(), rpc: rpc.NewServer(),
} }
t.Cleanup(rollupNode.rpc.Stop)
// setup RPC server for rollup node, hooked to the actor as backend // setup RPC server for rollup node, hooked to the actor as backend
m := &testutils.TestRPCMetrics{} m := &testutils.TestRPCMetrics{}
...@@ -85,9 +87,7 @@ func NewL2Verifier(log log.Logger, l1 derive.L1Fetcher, eng L2API, cfg *rollup.C ...@@ -85,9 +87,7 @@ func NewL2Verifier(log log.Logger, l1 derive.L1Fetcher, eng L2API, cfg *rollup.C
Authenticated: false, Authenticated: false,
}, },
} }
if err := gnode.RegisterApis(apis, nil, rollupNode.rpc); err != nil { require.NoError(t, gnode.RegisterApis(apis, nil, rollupNode.rpc), "failed to set up APIs")
panic(err)
}
return rollupNode return rollupNode
} }
......
...@@ -3,24 +3,25 @@ package actions ...@@ -3,24 +3,25 @@ package actions
import ( import (
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/testlog"
) )
func setupVerifier(t Testing, sd *e2eutils.SetupData, log log.Logger, l1F derive.L1Fetcher) (*L2Engine, *L2Verifier) { func setupVerifier(t Testing, sd *e2eutils.SetupData, log log.Logger, l1F derive.L1Fetcher) (*L2Engine, *L2Verifier) {
jwtPath := e2eutils.WriteDefaultJWT(t) jwtPath := e2eutils.WriteDefaultJWT(t)
engine := NewL2Engine(log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath) engine := NewL2Engine(t, log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath)
engCl := engine.EngineClient(t, sd.RollupCfg) engCl := engine.EngineClient(t, sd.RollupCfg)
verifier := NewL2Verifier(log, l1F, engCl, sd.RollupCfg) verifier := NewL2Verifier(t, log, l1F, engCl, sd.RollupCfg)
return engine, verifier return engine, verifier
} }
func setupVerifierOnlyTest(t Testing, sd *e2eutils.SetupData, log log.Logger) (*L1Miner, *L2Engine, *L2Verifier) { func setupVerifierOnlyTest(t Testing, sd *e2eutils.SetupData, log log.Logger) (*L1Miner, *L2Engine, *L2Verifier) {
miner := NewL1Miner(log, sd.L1Cfg) miner := NewL1Miner(t, log, sd.L1Cfg)
l1Cl := miner.L1Client(t, sd.RollupCfg) l1Cl := miner.L1Client(t, sd.RollupCfg)
engine, verifier := setupVerifier(t, sd, log, l1Cl) engine, verifier := setupVerifier(t, sd, log, l1Cl)
return miner, engine, verifier return miner, engine, verifier
......
...@@ -29,6 +29,7 @@ type TestingBase interface { ...@@ -29,6 +29,7 @@ type TestingBase interface {
Skipf(format string, args ...any) Skipf(format string, args ...any)
Skipped() bool Skipped() bool
TempDir() string TempDir() string
Parallel()
} }
func TimeoutCtx(t *testing.T, timeout time.Duration) context.Context { func TimeoutCtx(t *testing.T, timeout time.Duration) context.Context {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment