From cb01fba01562665fe197192ff204deed1ce2be57 Mon Sep 17 00:00:00 2001 From: Anders Eknert Date: Fri, 4 Mar 2022 15:27:32 +0100 Subject: [PATCH] coverage: remove duplicates from coverage report (#4410) Fixes #4393 Signed-off-by: Anders Eknert --- cover/cover.go | 8 ++-- cover/cover_test.go | 93 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 3 deletions(-) diff --git a/cover/cover.go b/cover/cover.go index 27a98afb4d..c5da6d6ce1 100644 --- a/cover/cover.go +++ b/cover/cover.go @@ -232,7 +232,7 @@ func (r Report) IsCovered(file string, row int) bool { } // CoverageThresholdError represents an error raised when the global -// code coverage percenta is lower than the specified threshold. +// code coverage percentage is lower than the specified threshold. type CoverageThresholdError struct { Coverage float64 Threshold float64 @@ -252,9 +252,11 @@ func sortedPositionSliceToRangeSlice(sorted []Position) (result []Range) { start, end := sorted[0], sorted[0] for i := 1; i < len(sorted); i++ { curr := sorted[i] - if curr.Row == end.Row+1 { + switch { + case curr.Row == end.Row: // skip + case curr.Row == end.Row+1: end = curr - } else { + default: result = append(result, Range{start, end}) start, end = curr, curr } diff --git a/cover/cover_test.go b/cover/cover_test.go index a30e337df8..8fb00b628d 100644 --- a/cover/cover_test.go +++ b/cover/cover_test.go @@ -139,6 +139,99 @@ p { } } +func TestCoverNoDuplicates(t *testing.T) { + + cover := New() + + module := `package test + +# Both a rule and an expression, but should not be counted twice +foo := 1 + +allow { true } +` + + parsedModule, err := ast.ParseModule("test.rego", module) + if err != nil { + t.Fatal(err) + } + + eval := rego.New( + rego.Module("test.rego", module), + rego.Query("data.test.allow"), + rego.QueryTracer(cover), + ) + + ctx := context.Background() + _, err = eval.Eval(ctx) + + if err != nil { + t.Fatal(err) + } + + report := cover.Report(map[string]*ast.Module{ + "test.rego": parsedModule, + }) + + fr, ok := report.Files["test.rego"] + if !ok { + t.Fatal("Expected file report for test.rego") + } + + expectedCovered := []Position{ + {6}, // allow + } + + expectedNotCovered := []Position{ + {4}, // foo + } + + for _, exp := range expectedCovered { + if !fr.IsCovered(exp.Row) { + t.Errorf("Expected %v to be covered", exp) + } + } + + for _, exp := range expectedNotCovered { + if !fr.IsNotCovered(exp.Row) { + t.Errorf("Expected %v to NOT be covered", exp) + } + } + + if len(expectedCovered) != fr.locCovered() { + t.Errorf( + "Expected %d loc to be covered, got %d instead", + len(expectedCovered), + fr.locCovered()) + } + + if len(expectedNotCovered) != fr.locNotCovered() { + t.Errorf( + "Expected %d loc to not be covered, got %d instead", + len(expectedNotCovered), + fr.locNotCovered()) + } + + expectedCoveragePercentage := round(100.0*float64(len(expectedCovered))/float64(len(expectedCovered)+len(expectedNotCovered)), 2) + if expectedCoveragePercentage != fr.Coverage { + t.Errorf("Expected coverage %f != %f", expectedCoveragePercentage, fr.Coverage) + } + + if expectedCoveragePercentage != report.Coverage { + t.Errorf("Expected report coverage %f != %f", + expectedCoveragePercentage, + report.Coverage) + } + + if t.Failed() { + bs, err := json.MarshalIndent(fr, "", " ") + if err != nil { + t.Fatal(err) + } + fmt.Println(string(bs)) + } +} + func TestCoverTraceConfig(t *testing.T) { ct := topdown.QueryTracer(New()) conf := ct.Config()