Skip to content

Unit testing for rules#4350

Merged
brian-brazil merged 8 commits intoprometheus:masterfrom
codesome:unit-testing-for-rules
Sep 25, 2018
Merged

Unit testing for rules#4350
brian-brazil merged 8 commits intoprometheus:masterfrom
codesome:unit-testing-for-rules

Conversation

@codesome
Copy link
Member

@codesome codesome commented Jul 4, 2018

Issue: #1695

Design Doc: Prometheus - Unit testing for Rules

Usage: ./promtool test rules test1.yml test2.yml test3.yml ...

Example for valid test file: Prometheus - Unit Testing for Rules - Example test files

Signed-off-by: Ganesh Vernekar <cs15btech11018@iith.ac.in>
@codesome codesome force-pushed the unit-testing-for-rules branch 2 times, most recently from 4156e66 to 6500dc6 Compare July 23, 2018 16:21
Signed-off-by: Ganesh Vernekar <cs15btech11018@iith.ac.in>
@codesome codesome force-pushed the unit-testing-for-rules branch from 6500dc6 to 5d35364 Compare July 24, 2018 12:14
@codesome codesome changed the title [WIP] Unit testing for rules Unit testing for rules Jul 24, 2018
…rules

Signed-off-by: Ganesh Vernekar <cs15btech11018@iith.ac.in>
@codesome codesome force-pushed the unit-testing-for-rules branch from 2bbd264 to afd433e Compare July 25, 2018 09:30
Copy link
Member

@gouthamve gouthamve left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good for the first version. But ideally, I'd like to remove the custom parsing done by the promql tests altogether and replace it with this YAML format.

But that should be the next PR.


// test performs the unit tests.
func (tg *testGroup) test(mint, maxt time.Time, evalInterval time.Duration, groupOrderMap map[string]int, ruleFiles ...string) []error {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra whitespace

presentInTest := make(map[time.Duration]map[string]struct{})
// Map of all the unit tests for given eval_time.
alertTests := make(map[time.Duration][]alertTestCase)
for _, art := range tg.AlertRuleTest {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alert would be better than art

type testGroup struct {
Interval time.Duration `yaml:"interval"`
InputSeries []series `yaml:"input_series"`
AlertRuleTest []alertTestCase `yaml:"alert_rule_test,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plural AlertRuleTests and PromqlExprTests :)

}
return len(alertEvalTimes)
}()
alertEvalTimes = append(alertEvalTimes[:pos], append([]time.Duration{art.EvalTime}, alertEvalTimes[pos:]...)...)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not sort the alertEvalTimes via: sort.Slice() after getting an unsorted slice? That looks more readable.

curr := 0

var errs []error
for ts := mint; maxt.Sub(ts) > 0; ts = ts.Add(evalInterval) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ts.Before(maxt) is better

}
}

if _, ok := got[ar.Name()]; ok {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could simply do: got[ar.Name()] = append(got[ar.Name()], alerts...)

}
presentInTest[art.EvalTime][art.Alertname] = struct{}{}

if _, ok := alertTests[art.EvalTime]; ok {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could just be: alertTests[art.EvalTime] = append(alertTests[art.EvalTime], art)


for _, testCase := range alertTests[t] {
// Checking alerts.
gotAlerts, ok := got[testCase.Alertname]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just gotAlerts := got[testCase.Alertname] should be fine.

}
}

for _, testCase := range alertTests[t] {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

testcase should be fine. No need to camelcase that :)

func (la labelsAndAnnotations) Len() int { return len(la) }
func (la labelsAndAnnotations) Swap(i, j int) { la[i], la[j] = la[j], la[i] }
func (la labelsAndAnnotations) Less(i, j int) bool {
l1h, l2h := la[i].Labels.Hash(), la[j].Labels.Hash()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

labels.Compare is better here.

Ganesh Vernekar added 2 commits August 23, 2018 22:23
Signed-off-by: Ganesh Vernekar <cs15btech11018@iith.ac.in>
…rules

Signed-off-by: Ganesh Vernekar <cs15btech11018@iith.ac.in>
@codesome
Copy link
Member Author

@gouthamve All the reviews fixed. Learn few stuff about Golang :)

"The rule files to check.",
).Required().ExistingFiles()

ruleTestCmd := testCmd.Command("rules", "Unit tests for rules.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you move this to the end? (let all check* commands be next to each other). Further it should be named testRuleCmd for consistency.

// mentioned by `groupOrderMap`. The returned slice starts with the groups that need to be in order,
// followed by rest of the groups in undetermined order.
func orderedGroups(groupsMap map[string]*rules.Group, groupOrderMap map[string]int) []*rules.Group {
var groups []*rules.Group
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, this is too complex. Can we do this instead:

groups := make([]*rules.Group, 0, len(groupsMaps))
for _, g := range groupsMap {
  groups = append(groups, g)
}

sort.Slice(groups, func(i, j int) bool {
  return groupOrderMap[groups[i].Name()] < groupOrderMap[groups[j].Name()]
})

return groups

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't think of that, thanks! I have updated.

@codesome codesome force-pushed the unit-testing-for-rules branch from f85ea9d to 5576d8e Compare August 27, 2018 10:36
@codesome
Copy link
Member Author

@brian-brazil PTAL when you get free. Also, where would the docs for this go in?

@brian-brazil
Copy link
Contributor

I won't get to look until next week. A new sibling page to Recording/Alerting rules is probably the right place.

@codesome
Copy link
Member Author

Sure, thanks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/codesome/prometheus/blob/5576d8e6312d07431d2db5e8d76ac78908510968/cmd/promtool/unittest.go#L192

If we don't add some extra time, the above loop can miss the last eval if maxt happens to be exactly the last eval. I think adding one evalInterval would make more sense than rounding off.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All this preparation is so that we can test alerts as we evaluate the rules. This avoids storing them in memory, as the number of evals might be high.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

asked for

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit long of a for expression, I'd suggest moving it into an if inside the for

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not break out here, rather than adding a new bool?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So that I can skip checking for got == exp here in case it was an error. So I am using a bool to keep track of that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you break out of the outer for, you don't need that

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have made the changes to continue to the outer for.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have rights to use this code as apache2?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am unaware of licenses, but what I see is, if I remove that comment, the code is general enough that it can be used. Maybe change it a bit you say?

@codesome codesome force-pushed the unit-testing-for-rules branch 2 times, most recently from 556e729 to 348284d Compare September 6, 2018 11:50
@codesome
Copy link
Member Author

@brian-brazil PTAL.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add proper docs for this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I will add docs for this. So I will remove that comment from here as docs will be added for this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any more changes/additions do you feel is required?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not seeing the docs in the PR

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I was trying to add docs in https://github.com/prometheus/docs. Just found that there is a docs/ directory in this repo. I will add them.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added the docs.

Signed-off-by: Ganesh Vernekar <cs15btech11018@iith.ac.in>
@codesome codesome force-pushed the unit-testing-for-rules branch from 348284d to 9bf78e7 Compare September 21, 2018 12:51
Signed-off-by: Ganesh Vernekar <cs15btech11018@iith.ac.in>
@codesome codesome force-pushed the unit-testing-for-rules branch from 3c1b74c to f9701f7 Compare September 25, 2018 10:47

# This uses expanding notation.
# Expanding notation:
# a+bxc ~ a a+b a+(2*b) a+(3*b) … a+(c*b)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The meaning of the ~ here is not clear, I'd suggest using becomes instead


### `<alert>`

Remember, this alert shoud be firing.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comes across weirdly, I'd remove it.


```yaml
# Labels of the sample in series notation.
labels: <series_notation>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's series notation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated it with examples. I meant to say <metric name>{<label name>=<label value>, ...}.

@codesome codesome force-pushed the unit-testing-for-rules branch from e56d1b9 to 5944b28 Compare September 25, 2018 13:01
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

usual

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Signed-off-by: Ganesh Vernekar <cs15btech11018@iith.ac.in>
@codesome codesome force-pushed the unit-testing-for-rules branch from 5944b28 to 5215e79 Compare September 25, 2018 14:15
@brian-brazil brian-brazil merged commit 5790d23 into prometheus:master Sep 25, 2018
@brian-brazil
Copy link
Contributor

Thanks!

@codesome codesome deleted the unit-testing-for-rules branch September 25, 2018 16:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants