99 _ "embed"
1010 "fmt"
1111 "html/template"
12+ "slices"
1213 "sort"
1314 "strings"
1415
@@ -21,7 +22,8 @@ import (
2122type templateHeatmapRow struct {
2223 Items []* templateHeatmapRow
2324 Name string
24- Coverage []int64
25+ Coverage []int64 // in percents
26+ Covered []int64 // in lines count
2527 IsDir bool
2628 Depth int
2729 LastDayInstrumented int64
@@ -41,6 +43,33 @@ type templateHeatmap struct {
4143 Managers []string
4244}
4345
46+ // Filter removes the records where predicate is true.
47+ func (th * templateHeatmap ) Filter (pred func (* templateHeatmapRow ) bool ) {
48+ th .Root .filter (pred )
49+ }
50+
51+ func (th * templateHeatmap ) Sort (pred func (* templateHeatmapRow , * templateHeatmapRow ) int ) {
52+ th .Root .sort (pred )
53+ }
54+
55+ func (thm * templateHeatmapRow ) filter (pred func (* templateHeatmapRow ) bool ) {
56+ var filteredItems []* templateHeatmapRow
57+ for _ , item := range thm .Items {
58+ item .filter (pred )
59+ if pred (item ) {
60+ filteredItems = append (filteredItems , item )
61+ }
62+ }
63+ thm .Items = filteredItems
64+ }
65+
66+ func (thm * templateHeatmapRow ) sort (pred func (* templateHeatmapRow , * templateHeatmapRow ) int ) {
67+ for _ , item := range thm .Items {
68+ item .sort (pred )
69+ }
70+ slices .SortFunc (thm .Items , pred )
71+ }
72+
4473func (thm * templateHeatmapRow ) addParts (depth int , pathLeft []string , filePath string , instrumented , covered int64 ,
4574 timePeriod coveragedb.TimePeriod ) {
4675 thm .instrumented [timePeriod ] += instrumented
@@ -94,6 +123,7 @@ func (thm *templateHeatmapRow) prepareDataFor(pageColumns []pageColumnTarget, sk
94123 dateCoverage = percent (thm .covered [tp ], thm .instrumented [tp ])
95124 }
96125 thm .Coverage = append (thm .Coverage , dateCoverage )
126+ thm .Covered = append (thm .Covered , thm .covered [tp ])
97127 thm .Tooltips = append (thm .Tooltips , fmt .Sprintf ("Instrumented:\t %d blocks\n Covered:\t %d blocks" ,
98128 thm .instrumented [tp ], thm .covered [tp ]))
99129 if ! thm .IsDir {
@@ -179,22 +209,30 @@ func stylesBodyJSTemplate(templData *templateHeatmap,
179209 template .HTML (js .Bytes ()), nil
180210}
181211
212+ type Format struct {
213+ FilterMinSpread int
214+ OrderByInstrumentedDrop bool
215+ // TODO(tarasmadan): move onlyUnique here as a filter
216+ }
217+
182218func DoHeatMapStyleBodyJS (
183219 ctx context.Context , client spannerclient.SpannerClient , scope * coveragedb.SelectScope , onlyUnique bool ,
184- sss , managers []string ) (template.CSS , template.HTML , template.HTML , error ) {
220+ sss , managers []string , dataFilters Format ) (template.CSS , template.HTML , template.HTML , error ) {
185221 covAndDates , err := coveragedb .FilesCoverageWithDetails (ctx , client , scope , onlyUnique )
186222 if err != nil {
187223 return "" , "" , "" , fmt .Errorf ("failed to FilesCoverageWithDetails: %w" , err )
188224 }
189225 templData := filesCoverageToTemplateData (covAndDates , onlyUnique )
190226 templData .Subsystems = sss
191227 templData .Managers = managers
228+ FormatResult (templData , dataFilters )
229+
192230 return stylesBodyJSTemplate (templData )
193231}
194232
195233func DoSubsystemsHeatMapStyleBodyJS (
196234 ctx context.Context , client spannerclient.SpannerClient , scope * coveragedb.SelectScope , onlyUnique bool ,
197- sss , managers []string ) (template.CSS , template.HTML , template.HTML , error ) {
235+ sss , managers []string , format Format ) (template.CSS , template.HTML , template.HTML , error ) {
198236 covWithDetails , err := coveragedb .FilesCoverageWithDetails (ctx , client , scope , onlyUnique )
199237 if err != nil {
200238 panic (err )
@@ -215,9 +253,28 @@ func DoSubsystemsHeatMapStyleBodyJS(
215253 }
216254 templData := filesCoverageToTemplateData (ssCovAndDates , onlyUnique )
217255 templData .Managers = managers
256+ FormatResult (templData , format )
218257 return stylesBodyJSTemplate (templData )
219258}
220259
260+ func FormatResult (thm * templateHeatmap , format Format ) {
261+ if format .FilterMinSpread > 0 {
262+ thm .Filter (func (row * templateHeatmapRow ) bool {
263+ if row .IsDir && len (row .Items ) > 0 {
264+ return true
265+ }
266+ return row .Coverage [len (row .Coverage )- 1 ]- slices .Min (row .Coverage ) >= int64 (format .FilterMinSpread )
267+ })
268+ }
269+ if format .OrderByInstrumentedDrop {
270+ thm .Sort (func (row1 * templateHeatmapRow , row2 * templateHeatmapRow ) int {
271+ row1InstrDrop := slices .Max (row1 .Covered ) - row1 .Covered [len (row1 .Covered )- 1 ]
272+ row2InstrDrop := slices .Max (row2 .Covered ) - row2 .Covered [len (row2 .Covered )- 1 ]
273+ return int (row2InstrDrop - row1InstrDrop )
274+ })
275+ }
276+ }
277+
221278func approximateInstrumented (points int64 ) string {
222279 dim := "_"
223280 if points > 10000 {
0 commit comments