11package itest
22
33import (
4+ "flag"
5+ "fmt"
6+ "math/rand"
47 "os"
58 "strings"
69 "testing"
@@ -12,6 +15,40 @@ 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 controls how many tranches the full itest list
30+ // is divided into for parallel execution.
31+ testCasesSplitTranches = flag .Uint (
32+ "splittranches" , defaultSplitTranches ,
33+ "split the test cases in this many tranches and run the tranche " +
34+ "at 0-based index specified by the -runtranche flag" ,
35+ )
36+
37+ // shuffleSeedFlag enables deterministic shuffling of test cases to
38+ // balance workload across tranches.
39+ shuffleSeedFlag = flag .Uint64 (
40+ "shuffleseed" , 0 , "if set, shuffles the test cases using this " +
41+ "as the source of randomness" ,
42+ )
43+
44+ // testCasesRunTranche selects which tranche (0-based) to execute.
45+ testCasesRunTranche = flag .Uint (
46+ "runtranche" , defaultRunTranche ,
47+ "run the tranche of the split test cases with the given (0-based) " +
48+ "index" ,
49+ )
50+ )
51+
1552// TestLightningTerminal performs a series of integration tests amongst a
1653// programmatically driven network of lnd nodes.
1754func TestLightningTerminal (t * testing.T ) {
@@ -39,9 +76,18 @@ func TestLightningTerminal(t *testing.T) {
3976 "--rpcmiddleware.enable" ,
4077 }
4178
79+ testCases , trancheIndex , trancheOffset := selectTestTranche ()
80+ totalTestCases := len (allTestCases )
81+
4282 // 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 ) {
83+ for idx , testCase := range testCases {
84+ testOrdinal := int (trancheOffset ) + idx + 1
85+ testName := fmt .Sprintf (
86+ "tranche%02d/%02d-of-%d/%s" , int (trancheIndex ),
87+ testOrdinal , totalTestCases , testCase .name ,
88+ )
89+
90+ success := t .Run (testName , func (t1 * testing.T ) {
4591 cleanTestCaseName := strings .ReplaceAll (
4692 testCase .name , " " , "_" ,
4793 )
@@ -107,6 +153,79 @@ func TestLightningTerminal(t *testing.T) {
107153 }
108154}
109155
156+ // maybeShuffleTestCases shuffles the test cases if the flag `shuffleseed` is
157+ // set and not 0. This is used by parallel test runs to even out the work
158+ // across tranches.
159+ func maybeShuffleTestCases () {
160+ // Exit if not set or set to 0.
161+ if shuffleSeedFlag == nil || * shuffleSeedFlag == 0 {
162+ return
163+ }
164+
165+ // Init the seed and shuffle the test cases.
166+ // #nosec G404 -- This is not for cryptographic purposes.
167+ r := rand .New (rand .NewSource (int64 (* shuffleSeedFlag )))
168+ r .Shuffle (len (allTestCases ), func (i , j int ) {
169+ allTestCases [i ], allTestCases [j ] =
170+ allTestCases [j ], allTestCases [i ]
171+ })
172+ }
173+
174+ // createIndices divides the number of test cases into pairs of indices that
175+ // specify the start and end of a tranche.
176+ func createIndices (numCases , numTranches uint ) [][2 ]uint {
177+ base := numCases / numTranches
178+ remainder := numCases % numTranches
179+
180+ indices := make ([][2 ]uint , numTranches )
181+ start := uint (0 )
182+
183+ for i := uint (0 ); i < numTranches ; i ++ {
184+ end := start + base
185+ if i < remainder {
186+ end ++
187+ }
188+ indices [i ] = [2 ]uint {start , end }
189+ start = end
190+ }
191+
192+ return indices
193+ }
194+
195+ // selectTestTranche returns the sub slice of the test cases that should be run
196+ // as the current split tranche as well as the index and slice offset of the
197+ // tranche.
198+ func selectTestTranche () ([]* testCase , uint , uint ) {
199+ numTranches := defaultSplitTranches
200+ if testCasesSplitTranches != nil {
201+ numTranches = * testCasesSplitTranches
202+ }
203+ runTranche := defaultRunTranche
204+ if testCasesRunTranche != nil {
205+ runTranche = * testCasesRunTranche
206+ }
207+
208+ // There's a special flake-hunt mode where we run the same test multiple
209+ // times in parallel. In that case the tranche index is equal to the
210+ // thread ID, but we need to actually run all tests for the regex
211+ // selection to work.
212+ threadID := runTranche
213+ if numTranches == 1 {
214+ runTranche = 0
215+ }
216+
217+ // Shuffle the test cases if the `shuffleseed` flag is set.
218+ maybeShuffleTestCases ()
219+
220+ numCases := uint (len (allTestCases ))
221+ indices := createIndices (numCases , numTranches )
222+ index := indices [runTranche ]
223+ trancheOffset , trancheEnd := index [0 ], index [1 ]
224+
225+ return allTestCases [trancheOffset :trancheEnd ], threadID ,
226+ trancheOffset
227+ }
228+
110229func init () {
111230 logger := btclog .NewSLogger (btclog .NewDefaultHandler (os .Stdout ))
112231 UseLogger (logger .SubSystem (Subsystem ))
0 commit comments