diff --git a/cmd/promtool/main.go b/cmd/promtool/main.go
index 26618855c7..49676ee5c4 100644
--- a/cmd/promtool/main.go
+++ b/cmd/promtool/main.go
@@ -217,6 +217,7 @@ func main() {
"test-rule-file",
"The unit test file.",
).Required().ExistingFiles()
+ testRulesDebug := testRulesCmd.Flag("debug", "Enable unit test debugging.").Default("false").Bool()
testRulesDiff := testRulesCmd.Flag("diff", "[Experimental] Print colored differential output between expected & received output.").Default("false").Bool()
defaultDBPath := "data/"
@@ -392,6 +393,7 @@ func main() {
},
*testRulesRun,
*testRulesDiff,
+ *testRulesDebug,
*testRulesFiles...),
)
diff --git a/cmd/promtool/unittest.go b/cmd/promtool/unittest.go
index 667e748061..78dacdc569 100644
--- a/cmd/promtool/unittest.go
+++ b/cmd/promtool/unittest.go
@@ -46,11 +46,11 @@ import (
// RulesUnitTest does unit testing of rules based on the unit testing files provided.
// More info about the file format can be found in the docs.
-func RulesUnitTest(queryOpts promqltest.LazyLoaderOpts, runStrings []string, diffFlag bool, files ...string) int {
- return RulesUnitTestResult(io.Discard, queryOpts, runStrings, diffFlag, files...)
+func RulesUnitTest(queryOpts promqltest.LazyLoaderOpts, runStrings []string, diffFlag, debug bool, files ...string) int {
+ return RulesUnitTestResult(io.Discard, queryOpts, runStrings, diffFlag, debug, files...)
}
-func RulesUnitTestResult(results io.Writer, queryOpts promqltest.LazyLoaderOpts, runStrings []string, diffFlag bool, files ...string) int {
+func RulesUnitTestResult(results io.Writer, queryOpts promqltest.LazyLoaderOpts, runStrings []string, diffFlag, debug bool, files ...string) int {
failed := false
junit := &junitxml.JUnitXML{}
@@ -60,7 +60,7 @@ func RulesUnitTestResult(results io.Writer, queryOpts promqltest.LazyLoaderOpts,
}
for _, f := range files {
- if errs := ruleUnitTest(f, queryOpts, run, diffFlag, junit.Suite(f)); errs != nil {
+ if errs := ruleUnitTest(f, queryOpts, run, diffFlag, debug, junit.Suite(f)); errs != nil {
fmt.Fprintln(os.Stderr, " FAILED:")
for _, e := range errs {
fmt.Fprintln(os.Stderr, e.Error())
@@ -82,7 +82,7 @@ func RulesUnitTestResult(results io.Writer, queryOpts promqltest.LazyLoaderOpts,
return successExitCode
}
-func ruleUnitTest(filename string, queryOpts promqltest.LazyLoaderOpts, run *regexp.Regexp, diffFlag bool, ts *junitxml.TestSuite) []error {
+func ruleUnitTest(filename string, queryOpts promqltest.LazyLoaderOpts, run *regexp.Regexp, diffFlag, debug bool, ts *junitxml.TestSuite) []error {
b, err := os.ReadFile(filename)
if err != nil {
ts.Abort(err)
@@ -131,7 +131,7 @@ func ruleUnitTest(filename string, queryOpts promqltest.LazyLoaderOpts, run *reg
if t.Interval == 0 {
t.Interval = unitTestInp.EvaluationInterval
}
- ers := t.test(evalInterval, groupOrderMap, queryOpts, diffFlag, unitTestInp.RuleFiles...)
+ ers := t.test(testname, evalInterval, groupOrderMap, queryOpts, diffFlag, debug, unitTestInp.RuleFiles...)
if ers != nil {
for _, e := range ers {
tc.Fail(e.Error())
@@ -198,7 +198,14 @@ type testGroup struct {
}
// test performs the unit tests.
-func (tg *testGroup) test(evalInterval time.Duration, groupOrderMap map[string]int, queryOpts promqltest.LazyLoaderOpts, diffFlag bool, ruleFiles ...string) (outErr []error) {
+func (tg *testGroup) test(testname string, evalInterval time.Duration, groupOrderMap map[string]int, queryOpts promqltest.LazyLoaderOpts, diffFlag, debug bool, ruleFiles ...string) (outErr []error) {
+ if debug {
+ testStart := time.Now()
+ fmt.Printf("DEBUG: Starting test %s\n", testname)
+ defer func() {
+ fmt.Printf("DEBUG: Test %s finished, took %v\n", testname, time.Since(testStart))
+ }()
+ }
// Setup testing suite.
suite, err := promqltest.NewLazyLoader(tg.seriesLoadingString(), queryOpts)
if err != nil {
@@ -482,6 +489,32 @@ Outer:
}
}
+ if debug {
+ ts := tg.maxEvalTime()
+ // Potentially a test can be specified at a time with fractional seconds,
+ // which PromQL cannot represent, so round up to the next whole second.
+ ts = (ts + time.Second).Truncate(time.Second)
+ expr := fmt.Sprintf(`{__name__=~".+"}[%v]`, ts)
+ q, err := suite.QueryEngine().NewInstantQuery(context.Background(), suite.Queryable(), nil, expr, mint.Add(ts))
+ if err != nil {
+ fmt.Printf("DEBUG: Failed querying, expr: %q, err: %v\n", expr, err)
+ return errs
+ }
+ res := q.Exec(suite.Context())
+ if res.Err != nil {
+ fmt.Printf("DEBUG: Failed query exec, expr: %q, err: %v\n", expr, res.Err)
+ return errs
+ }
+ switch v := res.Value.(type) {
+ case promql.Matrix:
+ fmt.Printf("DEBUG: Dump of all data (input_series and rules) at %v:\n", ts)
+ fmt.Println(v.String())
+ default:
+ fmt.Printf("DEBUG: Got unexpected type %T\n", v)
+ return errs
+ }
+ }
+
if len(errs) > 0 {
return errs
}
diff --git a/cmd/promtool/unittest_test.go b/cmd/promtool/unittest_test.go
index 9bbac28e9f..9b73dcdc1c 100644
--- a/cmd/promtool/unittest_test.go
+++ b/cmd/promtool/unittest_test.go
@@ -141,14 +141,14 @@ func TestRulesUnitTest(t *testing.T) {
reuseCount[tt.want] += len(tt.args.files)
}
t.Run(tt.name, func(t *testing.T) {
- if got := RulesUnitTest(tt.queryOpts, nil, false, tt.args.files...); got != tt.want {
+ if got := RulesUnitTest(tt.queryOpts, nil, false, false, tt.args.files...); got != tt.want {
t.Errorf("RulesUnitTest() = %v, want %v", got, tt.want)
}
})
}
t.Run("Junit xml output ", func(t *testing.T) {
var buf bytes.Buffer
- if got := RulesUnitTestResult(&buf, promqltest.LazyLoaderOpts{}, nil, false, reuseFiles...); got != 1 {
+ if got := RulesUnitTestResult(&buf, promqltest.LazyLoaderOpts{}, nil, false, false, reuseFiles...); got != 1 {
t.Errorf("RulesUnitTestResults() = %v, want 1", got)
}
var test junitxml.JUnitXML
@@ -230,7 +230,7 @@ func TestRulesUnitTestRun(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- got := RulesUnitTest(tt.queryOpts, tt.args.run, false, tt.args.files...)
+ got := RulesUnitTest(tt.queryOpts, tt.args.run, false, false, tt.args.files...)
require.Equal(t, tt.want, got)
})
}
diff --git a/docs/command-line/promtool.md b/docs/command-line/promtool.md
index 996a996555..5e2a8f6bb1 100644
--- a/docs/command-line/promtool.md
+++ b/docs/command-line/promtool.md
@@ -462,6 +462,7 @@ Unit tests for rules.
| Flag | Description | Default |
| --- | --- | --- |
| --run
... | If set, will only run test groups whose names match the regular expression. Can be specified multiple times. | |
+| --debug
| Enable unit test debugging. | `false` |
| --diff
| [Experimental] Print colored differential output between expected & received output. | `false` |