11package itest
22
33import (
4+ "flag"
5+ "fmt"
6+ "math/rand"
47 "os"
58 "strings"
69 "testing"
@@ -12,6 +15,43 @@ import (
1215 "github.com/stretchr/testify/require"
1316)
1417
18+ const (
19+ // defaultSplitTranches is the default number of tranches to divide the
20+ // test suite into when no override is provided.
21+ defaultSplitTranches uint = 1
22+
23+ // defaultRunTranche is the default tranche index to execute when no
24+ // explicit tranche is selected.
25+ defaultRunTranche uint = 0
26+ )
27+
28+ var (
29+ // testCasesSplitTranches is the number of tranches the test cases should
30+ // be split into. By default this is set to 1, so no splitting happens.
31+ // If this value is increased, then the -runtranche flag must be
32+ // specified as well to indicate which part should be run in the current
33+ // invocation.
34+ testCasesSplitTranches = flag .Uint (
35+ "splittranches" , defaultSplitTranches ,
36+ "split the test cases in this many tranches and run the tranche " +
37+ "at 0-based index specified by the -runtranche flag" ,
38+ )
39+
40+ // shuffleSeedFlag enables deterministic shuffling of test cases to
41+ // balance workload across tranches.
42+ shuffleSeedFlag = flag .Uint64 (
43+ "shuffleseed" , 0 , "if set, shuffles the test cases using this " +
44+ "as the source of randomness" ,
45+ )
46+
47+ // testCasesRunTranche selects which tranche (0-based) to execute.
48+ testCasesRunTranche = flag .Uint (
49+ "runtranche" , defaultRunTranche ,
50+ "run the tranche of the split test cases with the given (0-based) " +
51+ "index" ,
52+ )
53+ )
54+
1555// TestLightningTerminal performs a series of integration tests amongst a
1656// programmatically driven network of lnd nodes.
1757func TestLightningTerminal (t * testing.T ) {
@@ -39,9 +79,18 @@ func TestLightningTerminal(t *testing.T) {
3979 "--rpcmiddleware.enable" ,
4080 }
4181
82+ testCases , trancheIndex , trancheOffset := selectTestTranche ()
83+ totalTestCases := len (allTestCases )
84+
4285 // Run the subset of the test cases selected in this tranche.
43- for _ , testCase := range allTestCases {
44- success := t .Run (testCase .name , func (t1 * testing.T ) {
86+ for idx , testCase := range testCases {
87+ testOrdinal := int (trancheOffset ) + idx + 1
88+ testName := fmt .Sprintf (
89+ "tranche%02d/%02d-of-%d/%s" , int (trancheIndex ),
90+ testOrdinal , totalTestCases , testCase .name ,
91+ )
92+
93+ success := t .Run (testName , func (t1 * testing.T ) {
4594 cleanTestCaseName := strings .ReplaceAll (
4695 testCase .name , " " , "_" ,
4796 )
@@ -107,6 +156,79 @@ func TestLightningTerminal(t *testing.T) {
107156 }
108157}
109158
159+ // maybeShuffleTestCases shuffles the test cases if the flag `shuffleseed` is
160+ // set and not 0. This is used by parallel test runs to even out the work
161+ // across tranches.
162+ func maybeShuffleTestCases () {
163+ // Exit if not set or set to 0.
164+ if shuffleSeedFlag == nil || * shuffleSeedFlag == 0 {
165+ return
166+ }
167+
168+ // Init the seed and shuffle the test cases.
169+ // #nosec G404 -- This is not for cryptographic purposes.
170+ r := rand .New (rand .NewSource (int64 (* shuffleSeedFlag )))
171+ r .Shuffle (len (allTestCases ), func (i , j int ) {
172+ allTestCases [i ], allTestCases [j ] =
173+ allTestCases [j ], allTestCases [i ]
174+ })
175+ }
176+
177+ // createIndices divides the number of test cases into pairs of indices that
178+ // specify the start and end of a tranche.
179+ func createIndices (numCases , numTranches uint ) [][2 ]uint {
180+ base := numCases / numTranches
181+ remainder := numCases % numTranches
182+
183+ indices := make ([][2 ]uint , numTranches )
184+ start := uint (0 )
185+
186+ for i := uint (0 ); i < numTranches ; i ++ {
187+ end := start + base
188+ if i < remainder {
189+ end ++
190+ }
191+ indices [i ] = [2 ]uint {start , end }
192+ start = end
193+ }
194+
195+ return indices
196+ }
197+
198+ // selectTestTranche returns the sub slice of the test cases that should be run
199+ // as the current split tranche as well as the index and slice offset of the
200+ // tranche.
201+ func selectTestTranche () ([]* testCase , uint , uint ) {
202+ numTranches := defaultSplitTranches
203+ if testCasesSplitTranches != nil {
204+ numTranches = * testCasesSplitTranches
205+ }
206+ runTranche := defaultRunTranche
207+ if testCasesRunTranche != nil {
208+ runTranche = * testCasesRunTranche
209+ }
210+
211+ // There's a special flake-hunt mode where we run the same test multiple
212+ // times in parallel. In that case the tranche index is equal to the
213+ // thread ID, but we need to actually run all tests for the regex
214+ // selection to work.
215+ threadID := runTranche
216+ if numTranches == 1 {
217+ runTranche = 0
218+ }
219+
220+ // Shuffle the test cases if the `shuffleseed` flag is set.
221+ maybeShuffleTestCases ()
222+
223+ numCases := uint (len (allTestCases ))
224+ indices := createIndices (numCases , numTranches )
225+ index := indices [runTranche ]
226+ trancheOffset , trancheEnd := index [0 ], index [1 ]
227+
228+ return allTestCases [trancheOffset :trancheEnd ], threadID ,
229+ trancheOffset
230+ }
231+
110232func init () {
111233 logger := btclog .NewSLogger (btclog .NewDefaultHandler (os .Stdout ))
112234 UseLogger (logger .SubSystem (Subsystem ))
0 commit comments