diff --git a/test/e2e/generator/generate.go b/test/e2e/generator/generate.go index 4db1914d3..6db1c2645 100644 --- a/test/e2e/generator/generate.go +++ b/test/e2e/generator/generate.go @@ -46,9 +46,13 @@ var ( "unix": 10, } // FIXME: v2 disabled due to flake - nodeBlockSyncs = uniformChoice{"v0"} // "v2" - nodeMempools = uniformChoice{"v0", "v1"} - nodeStateSyncs = uniformChoice{e2e.StateSyncDisabled, e2e.StateSyncP2P, e2e.StateSyncRPC} + nodeBlockSyncs = uniformChoice{"v0"} // "v2" + nodeMempools = uniformChoice{"v0", "v1"} + nodeStateSyncs = weightedChoice{ + e2e.StateSyncDisabled: 20, + e2e.StateSyncP2P: 40, + e2e.StateSyncRPC: 40, + } nodePersistIntervals = uniformChoice{0, 1, 5} nodeSnapshotIntervals = uniformChoice{0, 3} nodeRetainBlocks = uniformChoice{0, 2 * int(e2e.EvidenceAgeHeight), 4 * int(e2e.EvidenceAgeHeight)} @@ -176,9 +180,11 @@ func generateTestnet(r *rand.Rand, opt map[string]interface{}) (e2e.Manifest, er // the initial validator set, and validator set updates for delayed nodes. nextStartAt := manifest.InitialHeight + 5 quorum := numValidators*2/3 + 1 + numValdatorStateSyncing := 0 for i := 1; i <= numValidators; i++ { startAt := int64(0) - if i > quorum { + if i > quorum && numValdatorStateSyncing < 2 && r.Float64() >= 0.5 { + numValdatorStateSyncing++ startAt = nextStartAt nextStartAt += 5 } @@ -273,7 +279,12 @@ func generateTestnet(r *rand.Rand, opt map[string]interface{}) (e2e.Manifest, er if len(seedNames) > 0 && (i == 0 || r.Float64() >= 0.5) { manifest.Nodes[name].Seeds = uniformSetChoice(seedNames).Choose(r) } else if i > 0 { - manifest.Nodes[name].PersistentPeers = uniformSetChoice(peerNames[:i]).Choose(r) + peers := uniformSetChoice(peerNames[:i]) + if manifest.Nodes[name].StateSync == e2e.StateSyncP2P { + manifest.Nodes[name].PersistentPeers = peers.ChooseAtLeast(r, 2) + } else { + manifest.Nodes[name].PersistentPeers = peers.Choose(r) + } } } @@ -311,7 +322,7 @@ func generateNode( } if startAt > 0 { - node.StateSync = nodeStateSyncs.Choose(r).(string) + node.StateSync = nodeStateSyncs.Choose(r) } // If this node is forced to be an archive node, retain all blocks and diff --git a/test/e2e/generator/random.go b/test/e2e/generator/random.go index d6c84d46c..c00d56964 100644 --- a/test/e2e/generator/random.go +++ b/test/e2e/generator/random.go @@ -74,18 +74,24 @@ func (pc probSetChoice) Choose(r *rand.Rand) []string { // uniformSetChoice picks a set of strings with uniform probability, picking at least one. type uniformSetChoice []string -func (usc uniformSetChoice) Choose(r *rand.Rand) []string { +func (usc uniformSetChoice) Choose(r *rand.Rand) []string { return usc.ChooseAtLeast(r, 1) } + +func (usc uniformSetChoice) ChooseAtLeast(r *rand.Rand, num int) []string { choices := []string{} indexes := r.Perm(len(usc)) - if len(indexes) > 1 { - indexes = indexes[:1+r.Intn(len(indexes)-1)] + if num < len(indexes) { + indexes = indexes[:1+randomInRange(r, num, len(indexes)-1)] } + for _, i := range indexes { choices = append(choices, usc[i]) } + return choices } +func randomInRange(r *rand.Rand, min, max int) int { return r.Intn(max-min+1) + min } + type weightedChoice map[string]uint func (wc weightedChoice) Choose(r *rand.Rand) string { diff --git a/test/e2e/generator/random_test.go b/test/e2e/generator/random_test.go index 3fbb19ab5..48b04f2d1 100644 --- a/test/e2e/generator/random_test.go +++ b/test/e2e/generator/random_test.go @@ -1,9 +1,12 @@ package main import ( + "fmt" + "math/rand" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestCombinations(t *testing.T) { @@ -29,3 +32,28 @@ func TestCombinations(t *testing.T) { {"bool": true, "int": 3, "string": "bar"}, }, c) } + +func TestUniformSetChoice(t *testing.T) { + set := uniformSetChoice([]string{"a", "b", "c"}) + r := rand.New(rand.NewSource(2384)) + + for i := 0; i < 100; i++ { + t.Run(fmt.Sprintf("Iteration%03d", i), func(t *testing.T) { + set = append(set, t.Name()) + + t.Run("ChooseAtLeastSubset", func(t *testing.T) { + require.True(t, len(set.ChooseAtLeast(r, 1)) >= 1) + require.True(t, len(set.ChooseAtLeast(r, 2)) >= 2) + require.True(t, len(set.ChooseAtLeast(r, len(set)/2)) >= len(set)/2) + }) + t.Run("ChooseAtLeastEqualOrGreaterToLength", func(t *testing.T) { + require.Len(t, set.ChooseAtLeast(r, len(set)), len(set)) + require.Len(t, set.ChooseAtLeast(r, len(set)+1), len(set)) + require.Len(t, set.ChooseAtLeast(r, len(set)*10), len(set)) + }) + t.Run("ChooseSingle", func(t *testing.T) { + require.True(t, len(set.Choose(r)) >= 1) + }) + }) + } +}