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