Skip to content

Commit 9abc853

Browse files
denikclaude
andauthored
acc: Select subset of tests that cover all output (#4800)
## Changes If -subset (or -update) option is passed, only a few tests selected from EnvMatrix-generated test cases: by default a single test is selected from the whole matrix. The configurations with DATABRICKS_BUNDLE_ENGINE=direct setting get 10x weight, so they are preferred over terraform (but we still run a few tests on terraform). We can do this, because all subtests are supposed to generate the same output. The exception is tests that do `> out.$DATABRICKS_BUNDLE_ENGINE.txt` redirects. Those need both terraform and direct variants to run. For such tests, we select two variants, one with "terraform" and another with "direct" engine. The exact rule for triggering this: either script in current directory or _script in any parent directories contains "$DATABRICKS_BUNDLE_ENGINE" substring. ## Why - Speed up "make test-update". - Potentially more correct update, since parallel runs do not write the same files at the same time. ## Tests Just like in #4795 remove all output files and check that "make test-update" regenerates them all. This branch: ``` % git grep -l 'Local = true' '**/out.test.toml' | find_out_files.py | xargs rm % time make test-update # This branch … make test-update 561.48s user 266.50s system 659% cpu 2:05.49 total ``` main: ``` % git grep -l 'Local = true' '**/out.test.toml' | find_out_files.py | xargs rm … % time make test-update # main make test-update 1063.22s user 442.75s system 709% cpu 3:32.27 total ``` --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 410853a commit 9abc853

File tree

10 files changed

+145
-8
lines changed

10 files changed

+145
-8
lines changed

acceptance/acceptance_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ var (
4747
UseVersion string
4848
WorkspaceTmpDir bool
4949
OnlyOutTestToml bool
50+
Subset bool
5051
)
5152

5253
// In order to debug CLI running under acceptance test, search for TestInprocessMode and update
@@ -78,6 +79,7 @@ func init() {
7879
// to simulate an identical environment.
7980
flag.BoolVar(&WorkspaceTmpDir, "workspace-tmp-dir", false, "Run tests on the workspace file system (For DBR testing).")
8081
flag.BoolVar(&OnlyOutTestToml, "only-out-test-toml", false, "Only regenerate out.test.toml files without running tests")
82+
flag.BoolVar(&Subset, "subset", false, "Select a subset of EnvMatrix variants that cover all output files. Auto-enabled on -update.")
8183
}
8284

8385
const (
@@ -156,7 +158,32 @@ func setReplsForTestEnvVars(t *testing.T, repls *testdiff.ReplacementsContext) {
156158
}
157159
}
158160

161+
// helperScriptUsesEngineCache caches whether a _script helper in a given directory
162+
// (or any of its ancestors) references $DATABRICKS_BUNDLE_ENGINE.
163+
// Since _script helpers are shared across many tests, caching avoids redundant reads.
164+
var helperScriptUsesEngineCache sync.Map
165+
166+
// anyHelperScriptUsesEngine returns true if any _script helper in dir or its ancestors
167+
// contains $DATABRICKS_BUNDLE_ENGINE.
168+
func anyHelperScriptUsesEngine(dir string) bool {
169+
if dir == "" || dir == "." {
170+
return false
171+
}
172+
if v, ok := helperScriptUsesEngineCache.Load(dir); ok {
173+
return v.(bool)
174+
}
175+
content, err := os.ReadFile(filepath.Join(dir, "_script"))
176+
result := (err == nil && strings.Contains(string(content), "$DATABRICKS_BUNDLE_ENGINE")) ||
177+
anyHelperScriptUsesEngine(filepath.Dir(dir))
178+
helperScriptUsesEngineCache.Store(dir, result)
179+
return result
180+
}
181+
159182
func testAccept(t *testing.T, inprocessMode bool, singleTest string) int {
183+
if testdiff.OverwriteMode {
184+
Subset = true
185+
}
186+
160187
repls := testdiff.ReplacementsContext{}
161188
cwd, err := os.Getwd()
162189
require.NoError(t, err)
@@ -366,6 +393,12 @@ func testAccept(t *testing.T, inprocessMode bool, singleTest string) int {
366393
}
367394

368395
expanded := internal.ExpandEnvMatrix(config.EnvMatrix, config.EnvMatrixExclude, extraVars)
396+
if Subset {
397+
scriptContent, _ := os.ReadFile(filepath.Join(dir, EntryPointScript))
398+
scriptUsesEngine := strings.Contains(string(scriptContent), "$DATABRICKS_BUNDLE_ENGINE") ||
399+
anyHelperScriptUsesEngine(dir)
400+
expanded = internal.SubsetExpanded(expanded, dir, scriptUsesEngine)
401+
}
369402

370403
for ind, envset := range expanded {
371404
envname := strings.Join(envset, "/")

acceptance/internal/config.go

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package internal
22

33
import (
4+
"hash/fnv"
45
"os"
56
"path/filepath"
67
"reflect"
@@ -427,17 +428,66 @@ func filterExcludedEnvSets(envSets [][]string, exclude map[string][]string) [][]
427428
return filtered
428429
}
429430

431+
// SubsetExpanded selects one variant per DATABRICKS_BUNDLE_ENGINE value (if scriptUsesEngine)
432+
// or one variant total from an already-expanded and exclusion-filtered list.
433+
// DATABRICKS_BUNDLE_ENGINE=direct has weight 10; all other variants have weight 1.
434+
func SubsetExpanded(expanded [][]string, testDir string, scriptUsesEngine bool) [][]string {
435+
if len(expanded) <= 1 {
436+
return expanded
437+
}
438+
if scriptUsesEngine {
439+
// Collect candidates per engine key, preserving first-seen order.
440+
// keyToIdx maps engine value -> index in result/groups slices.
441+
var result [][]string
442+
var groups [][][]string
443+
keyToIdx := make(map[string]int)
444+
for _, envset := range expanded {
445+
engine := ""
446+
for _, kv := range envset {
447+
if v, ok := strings.CutPrefix(kv, "DATABRICKS_BUNDLE_ENGINE="); ok {
448+
engine = v
449+
break
450+
}
451+
}
452+
idx, ok := keyToIdx[engine]
453+
if !ok {
454+
idx = len(result)
455+
keyToIdx[engine] = idx
456+
result = append(result, nil)
457+
groups = append(groups, nil)
458+
}
459+
groups[idx] = append(groups[idx], envset)
460+
}
461+
for i, group := range groups {
462+
result[i] = weightedSelect(group, testDir)
463+
}
464+
return result
465+
}
466+
return [][]string{weightedSelect(expanded, testDir)}
467+
}
468+
469+
// weightedSelect picks one envset using weighted consistent hashing.
470+
// DATABRICKS_BUNDLE_ENGINE=direct has weight 10; all other envsets have weight 1.
471+
func weightedSelect(envsets [][]string, testDir string) []string {
472+
var weighted [][]string
473+
for _, envset := range envsets {
474+
weight := 1
475+
if slices.Contains(envset, "DATABRICKS_BUNDLE_ENGINE=direct") {
476+
weight = 10
477+
}
478+
for range weight {
479+
weighted = append(weighted, envset)
480+
}
481+
}
482+
h := fnv.New64a()
483+
h.Write([]byte(testDir))
484+
return weighted[h.Sum64()%uint64(len(weighted))]
485+
}
486+
430487
// matchesExclusionRule returns true if envSet contains all KEY=value pairs from excludeRule.
431488
func matchesExclusionRule(envSet, excludeRule []string) bool {
432489
for _, excludePair := range excludeRule {
433-
found := false
434-
for _, envPair := range envSet {
435-
if envPair == excludePair {
436-
found = true
437-
break
438-
}
439-
}
440-
if !found {
490+
if !slices.Contains(envSet, excludePair) {
441491
return false
442492
}
443493
}

acceptance/internal/config_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package internal
22

33
import (
4+
"fmt"
45
"os"
56
"path/filepath"
7+
"strings"
68
"testing"
79

810
"github.com/stretchr/testify/assert"
@@ -201,6 +203,45 @@ func TestExpandEnvMatrix(t *testing.T) {
201203
}
202204
}
203205

206+
func TestSubsetExpanded_DirectBias(t *testing.T) {
207+
// Across many test dirs, DATABRICKS_BUNDLE_ENGINE=direct should be selected ~10/11 of the time.
208+
expanded := [][]string{
209+
{"DATABRICKS_BUNDLE_ENGINE=terraform"},
210+
{"DATABRICKS_BUNDLE_ENGINE=direct"},
211+
}
212+
directCount := 0
213+
total := 1000
214+
for i := range total {
215+
r := SubsetExpanded(expanded, fmt.Sprintf("test/dir%d", i), false)
216+
if r[0][0] == "DATABRICKS_BUNDLE_ENGINE=direct" {
217+
directCount++
218+
}
219+
}
220+
ratio := float64(directCount) / float64(total)
221+
assert.InDelta(t, float64(10)/11, ratio, 0.05, "expected ~10/11 direct, got %.1f%%", ratio*100)
222+
}
223+
224+
func TestSubsetExpanded_ScriptUsesEngine(t *testing.T) {
225+
// When script uses $DATABRICKS_BUNDLE_ENGINE, one combo per engine value is returned.
226+
expanded := [][]string{
227+
{"DATABRICKS_BUNDLE_ENGINE=terraform", "READPLAN="},
228+
{"DATABRICKS_BUNDLE_ENGINE=direct", "READPLAN="},
229+
{"DATABRICKS_BUNDLE_ENGINE=direct", "READPLAN=1"},
230+
}
231+
result := SubsetExpanded(expanded, "test/dir", true)
232+
require.Len(t, result, 2)
233+
engines := make(map[string]bool)
234+
for _, envset := range result {
235+
for _, kv := range envset {
236+
if strings.HasPrefix(kv, "DATABRICKS_BUNDLE_ENGINE=") {
237+
engines[kv] = true
238+
}
239+
}
240+
}
241+
assert.True(t, engines["DATABRICKS_BUNDLE_ENGINE=terraform"])
242+
assert.True(t, engines["DATABRICKS_BUNDLE_ENGINE=direct"])
243+
}
244+
204245
func TestLoadConfigPhaseIsNotInherited(t *testing.T) {
205246
tests := []struct {
206247
name string
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
echo "engine=$DATABRICKS_BUNDLE_ENGINE" > "out.$DATABRICKS_BUNDLE_ENGINE.txt"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
engine=direct
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
engine=terraform

acceptance/selftest/subset_ancestor_engine/child/out.test.toml

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

acceptance/selftest/subset_ancestor_engine/child/output.txt

Whitespace-only changes.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# This script does not directly reference $DATABRICKS_BUNDLE_ENGINE.
2+
# Engine detection happens via the ancestor _script helper,
3+
# exercising the anyHelperScriptUsesEngine() path in subset selection.
4+
source "$TESTDIR/../_script"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
EnvVaryOutput = "DATABRICKS_BUNDLE_ENGINE"

0 commit comments

Comments
 (0)