Regres: Fix uncovered calculations after optimizations

The uncovered span calculations were ignoring span groups and inverted spans.
This is now correctly handled by Tree.allSpans().

Bug: b/152192800
Change-Id: I2bd8afa3c956b03b598a3d5297cb775fd19da35d
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/43573
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
diff --git a/tests/regres/cov/serialization.go b/tests/regres/cov/serialization.go
index b7d29c9..ebff706 100644
--- a/tests/regres/cov/serialization.go
+++ b/tests/regres/cov/serialization.go
@@ -148,16 +148,8 @@
 		file := t.files[path]
 
 		uncovered := append(SpanList{}, file.allSpans...)
-		file.tcm.traverse(func(tc *TestCoverage) {
-			for id := range tc.Spans {
-				uncovered.Remove(spansByID[id])
-			}
-		})
-
-		percentage := 0.0
-		if totalLines := file.allSpans.NumLines(); totalLines > 0 {
-			uncoveredLines := uncovered.NumLines()
-			percentage = 1.0 - (float64(uncoveredLines) / float64(totalLines))
+		for id := range t.allSpans(file, file.tcm) {
+			uncovered.Remove(spansByID[id])
 		}
 
 		if i > 0 {
@@ -167,9 +159,14 @@
 		sb.WriteString(path)
 		sb.WriteString(`":`)
 		sb.WriteString(`{`)
-		sb.WriteString(`"p":`)
-		sb.WriteString(fmt.Sprintf("%v", percentage))
-		sb.WriteString(`,"g":`)
+		if totalLines := file.allSpans.NumLines(); totalLines > 0 {
+			uncoveredLines := uncovered.NumLines()
+			percentage := 1.0 - (float64(uncoveredLines) / float64(totalLines))
+			sb.WriteString(`"p":`)
+			sb.WriteString(fmt.Sprintf("%v", percentage))
+			sb.WriteString(`,`)
+		}
+		sb.WriteString(`"g":`)
 		t.writeSpanGroupsJSON(file.spangroups, sb)
 		sb.WriteString(`,"u":`)
 		t.writeUncoveredJSON(file, uncovered, sb)
@@ -324,20 +321,11 @@
 	for span, id := range tree.spans {
 		spansByID[id] = span
 	}
-	for _, tf := range tree.files {
-		tf.tcm.traverse(func(tc *TestCoverage) {
-			for spanID := range tc.Spans {
-				span := spansByID[spanID]
-				tf.allSpans.Add(span)
-			}
-			if groupID := tc.Group; groupID != nil {
-				group := tf.spangroups[*groupID]
-				for spanID := range group.Spans {
-					span := spansByID[spanID]
-					tf.allSpans.Add(span)
-				}
-			}
-		})
+	for _, file := range tree.files {
+		for spanID := range tree.allSpans(file, file.tcm) {
+			span := spansByID[spanID]
+			file.allSpans.Add(span)
+		}
 	}
 }
 
diff --git a/tests/regres/cov/tree.go b/tests/regres/cov/tree.go
index b94de68..a6ff720 100644
--- a/tests/regres/cov/tree.go
+++ b/tests/regres/cov/tree.go
@@ -170,6 +170,21 @@
 	}
 }
 
+// allSpans returns all the spans in use by the TestCoverageMap and its children.
+func (t *Tree) allSpans(tf *treeFile, tcm TestCoverageMap) SpanSet {
+	spans := SpanSet{}
+	for _, tc := range tcm {
+		for id := tc.Group; id != nil; id = tf.spangroups[*id].Extend {
+			group := tf.spangroups[*id]
+			spans = spans.addAll(group.Spans)
+		}
+		spans = spans.addAll(tc.Spans)
+
+		spans = spans.addAll(spans.invertAll(t.allSpans(tf, tc.Children)))
+	}
+	return spans
+}
+
 // StringID is an identifier of a string
 type StringID int
 
@@ -437,6 +452,21 @@
 	return s.add(rhs)
 }
 
+func (s SpanSet) invertAll(rhs SpanSet) SpanSet {
+	out := make(SpanSet, len(s)+len(rhs))
+	for span := range s {
+		if !rhs.contains(span) {
+			out[span] = struct{}{}
+		}
+	}
+	for span := range rhs {
+		if !s.contains(span) {
+			out[span] = struct{}{}
+		}
+	}
+	return out
+}
+
 // SpanGroupID is an identifier of a SpanGroup.
 type SpanGroupID int