You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
378 lines
14 KiB
378 lines
14 KiB
// Copyright 2020 The SwiftShader Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package cov_test
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
cov "."
|
|
)
|
|
|
|
var (
|
|
fileA = "coverage/file/a"
|
|
fileB = "coverage/file/b"
|
|
fileC = "coverage/file/c"
|
|
fileD = "coverage/file/c"
|
|
|
|
span0 = cov.Span{cov.Location{3, 2}, cov.Location{3, 9}}
|
|
span1 = cov.Span{cov.Location{4, 1}, cov.Location{5, 1}}
|
|
span2 = cov.Span{cov.Location{5, 5}, cov.Location{5, 7}}
|
|
span3 = cov.Span{cov.Location{7, 2}, cov.Location{7, 7}}
|
|
)
|
|
|
|
// a
|
|
// ╭───────┴───────╮
|
|
// b c
|
|
// ╭───┴───╮ ╭───┴───╮
|
|
// d e f g
|
|
// ╭─┴─╮ ╭─┴─╮ ╭─┴─╮ ╭─┴─╮
|
|
// h i j k l m n o
|
|
// ╭┴╮ ╭┴╮ ╭┴╮ ╭┴╮ ╭┴╮ ╭╯
|
|
// p q r s t u v w x y z
|
|
//
|
|
|
|
func TestTree(t *testing.T) {
|
|
tree := &cov.Tree{}
|
|
|
|
t.Log("Add 'b' with the coverage [0,1]")
|
|
tree.Add(cov.Path{"a", "b"}, coverage(fileA, span0, span1))
|
|
|
|
// [0,1]
|
|
// (a)
|
|
// ╭─────╯
|
|
// b
|
|
|
|
checkSpans(t, tree.Spans(), span0, span1)
|
|
checkTests(t, tree, `{a:{b}}`)
|
|
checkCoverage(t, tree, fileA, `a:{[0,1]}`)
|
|
|
|
t.Log("Add 'i' with the coverage [0,1]")
|
|
tree.Add(cov.Path{"a", "b", "d", "i"}, coverage(fileA, span0, span1))
|
|
|
|
// [0,1]
|
|
// (a)
|
|
// ╭─────╯
|
|
// b
|
|
// ╭──╯
|
|
// d
|
|
// ╰─╮
|
|
// i
|
|
checkSpans(t, tree.Spans(), span0, span1)
|
|
checkTests(t, tree, `{a:{b:{d:{i}}}}`)
|
|
checkCoverage(t, tree, fileA, `a:{[0,1]}`)
|
|
|
|
t.Log("Add 'e' with the coverage [0,1,2]")
|
|
tree.Add(cov.Path{"a", "b", "e"}, coverage(fileA, span0, span1, span2))
|
|
|
|
// [0,1]
|
|
// (a)
|
|
// ┏━━━━━┛
|
|
// (b)
|
|
// ╭──┺━━┓
|
|
// d (e)[2]
|
|
// ╰─╮
|
|
// i
|
|
checkSpans(t, tree.Spans(), span0, span1, span2)
|
|
checkTests(t, tree, `{a:{b:{d:{i} e}}}`)
|
|
checkCoverage(t, tree, fileA, `a:{[0,1] b:{e:{[2]}}}`)
|
|
|
|
t.Log("Add 'n' with the coverage [0,3]")
|
|
tree.Add(cov.Path{"a", "c", "g", "n"}, coverage(fileA, span0, span3))
|
|
|
|
// [0]
|
|
// (a)
|
|
// ┏━━━━━┻━━━━━┓
|
|
// [1](b) (c)[3]
|
|
// ╭──┺━━┓ ╰──╮
|
|
// d (e)[2] g
|
|
// ╰─╮ ╭─╯
|
|
// i n
|
|
checkSpans(t, tree.Spans(), span0, span1, span2, span3)
|
|
checkTests(t, tree, `{a:{b:{d:{i}e}c:{g:{n}}}}`)
|
|
checkCoverage(t, tree, fileA, `a:{[0] b:{[1] e:{[2]}} c:{[3]}}`)
|
|
|
|
t.Log("Add 'o' with the coverage [0, 3]")
|
|
tree.Add(cov.Path{"a", "c", "g", "o"}, coverage(fileA, span0, span3))
|
|
|
|
// [0]
|
|
// (a)
|
|
// ┏━━━━━━━┻━━━━━━━┓
|
|
// [1](b) (c)[3]
|
|
// ╭──┺━━┓ ╰──╮
|
|
// d (e)[2] g
|
|
// ╰─╮ ╭─┴─╮
|
|
// i n o
|
|
checkSpans(t, tree.Spans(), span0, span1, span2, span3)
|
|
checkTests(t, tree, `{a:{b:{d:{i}e}c:{g:{n o}}}}`)
|
|
checkCoverage(t, tree, fileA, `a:{[0] b:{[1] e:{[2]}} c:{[3]}}`)
|
|
|
|
t.Log("Add 'f' with the coverage [1]")
|
|
tree.Add(cov.Path{"a", "c", "f"}, coverage(fileA, span1))
|
|
|
|
// (a)
|
|
// ┏━━━━━━━━┻━━━━━━━━┓
|
|
// [0,1](b) (c)
|
|
// ╭──┺━━┓ ┏━━┻━━┓
|
|
// d (e)[2] [1](f) (g)[0,3]
|
|
// ╰─╮ ╭─┴─╮
|
|
// i n o
|
|
checkSpans(t, tree.Spans(), span0, span1, span2, span3)
|
|
checkTests(t, tree, `{a:{b:{d:{i} e} c:{f g:{n o}}}}`)
|
|
checkCoverage(t, tree, fileA, `a:{b:{[0,1] e:{[2]}} c:{f:{[1]} g:{[0,3]}}}`)
|
|
|
|
t.Log("Add 'j' with the coverage [3]")
|
|
tree.Add(cov.Path{"a", "b", "e", "j"}, coverage(fileA, span3))
|
|
|
|
// (a)
|
|
// ┏━━━━━━━━┻━━━━━━━━┓
|
|
// (b) (c)
|
|
// ┏━━━┻━━━┓ ┏━━┻━━┓
|
|
// [0,1](d) (e)[3] [1](f) (g)[0,3]
|
|
// ╰─╮ ╭─╯ ╭─┴─╮
|
|
// i j n o
|
|
checkSpans(t, tree.Spans(), span0, span1, span2, span3)
|
|
checkTests(t, tree, `{a:{b:{d:{i} e:{j}} c:{f g:{n o}}}}`)
|
|
checkCoverage(t, tree, fileA, `a:{b:{d:{[0,1]} e:{[3]}} c:{f:{[1]} g:{[0,3]}}}`)
|
|
|
|
t.Log("Add 'k' with the coverage [3]")
|
|
tree.Add(cov.Path{"a", "b", "e", "k"}, coverage(fileA, span3))
|
|
|
|
// (a)
|
|
// ┏━━━━━━━━┻━━━━━━━━┓
|
|
// (b) (c)
|
|
// ┏━━━┻━━━┓ ┏━━┻━━┓
|
|
// [0,1](d) (e)[3] [1](f) (g)[0,3]
|
|
// ╰─╮ ╭─┴─╮ ╭─┴─╮
|
|
// i j k n o
|
|
checkSpans(t, tree.Spans(), span0, span1, span2, span3)
|
|
checkTests(t, tree, `{a:{b:{d:{i} e:{j k}} c:{f g:{n o}}}}`)
|
|
checkCoverage(t, tree, fileA, `a:{b:{d:{[0,1]} e:{[3]}} c:{f:{[1]} g:{[0,3]}}}`)
|
|
|
|
t.Log("Add 'v' with the coverage [1,2]")
|
|
tree.Add(cov.Path{"a", "c", "f", "l", "v"}, coverage(fileA, span1, span2))
|
|
|
|
// (a)
|
|
// ┏━━━━━━━━┻━━━━━━━━━━┓
|
|
// (b) (c)
|
|
// ┏━━━┻━━━┓ ┏━━┻━━┓
|
|
// [0,1](d) (e)[3] [1,2](f) (g)[0,3]
|
|
// ╰─╮ ╭─┴─╮ ╭─╯ ╭─┴─╮
|
|
// i j k l n o
|
|
// ╭╯
|
|
// v
|
|
checkSpans(t, tree.Spans(), span0, span1, span2, span3)
|
|
checkTests(t, tree, `{a:{b:{d:{i} e:{j k}} c:{f:{l:{v}} g:{n o}}}}`)
|
|
checkCoverage(t, tree, fileA, `a:{b:{d:{[0,1]} e:{[3]}} c:{f:{[1,2]} g:{[0,3]}}}`)
|
|
|
|
t.Log("Add 'x' with the coverage [1,2]")
|
|
tree.Add(cov.Path{"a", "c", "f", "l", "x"}, coverage(fileA, span1, span2))
|
|
|
|
// (a)
|
|
// ┏━━━━━━━━┻━━━━━━━━━━┓
|
|
// (b) (c)
|
|
// ┏━━━┻━━━┓ ┏━━┻━━┓
|
|
// [0,1](d) (e)[3] [1,2](f) (g)[0,3]
|
|
// ╰─╮ ╭─┴─╮ ╭─╯ ╭─┴─╮
|
|
// i j k l n o
|
|
// ╭┴╮
|
|
// v x
|
|
checkSpans(t, tree.Spans(), span0, span1, span2, span3)
|
|
checkTests(t, tree, `{a:{b:{d:{i} e:{j k}} c:{f:{l:{v x}} g:{n o}}}}`)
|
|
checkCoverage(t, tree, fileA, `a:{b:{d:{[0,1]} e:{[3]}} c:{f:{[1,2]} g:{[0,3]}}}`)
|
|
|
|
t.Log("Add 'z' with the coverage [2]")
|
|
tree.Add(cov.Path{"a", "c", "g", "n", "z"}, coverage(fileA, span2))
|
|
|
|
// (a)
|
|
// ┏━━━━━━━━┻━━━━━━━━━━━━┓
|
|
// (b) (c)
|
|
// ┏━━━┻━━━┓ ┏━━━━┻━━━━┓
|
|
// [0,1](d) (e)[3] [1,2](f) (g)
|
|
// ╰─╮ ╭─┴─╮ ╭─╯ ┏━┻━┓
|
|
// i j k l [2](n) (o)[0,3]
|
|
// ╭┴╮ ╭╯
|
|
// v x z
|
|
checkSpans(t, tree.Spans(), span0, span1, span2, span3)
|
|
checkTests(t, tree, `{a:{b:{d:{i} e:{j k}} c:{f:{l:{v x}} g:{n: {z} o}}}}`)
|
|
checkCoverage(t, tree, fileA, `a:{b:{d:{[0,1]} e:{[3]}} c:{f:{[1,2]} g:{n:{[2]} o:{[0,3]}}}}`)
|
|
|
|
tree.Optimize()
|
|
|
|
// (a)
|
|
// ┏━━━━━━━━┻━━━━━━━━━━━━┓
|
|
// (b) (c)
|
|
// ┏━━━┻━━━┓ ┏━━━━┻━━━━┓
|
|
// <0>(d) (e)[3] <2>(f) (g)
|
|
// ╰─╮ ╭─┴─╮ ╭─╯ ┏━┻━┓
|
|
// i j k l [2](n) (o)<1>
|
|
// ╭┴╮ ╭╯
|
|
// v x z
|
|
checkSpans(t, tree.Spans(), span0, span1, span2, span3)
|
|
checkGroups(t, tree.FileSpanGroups(fileA), map[cov.SpanGroupID]cov.SpanGroup{
|
|
0: cov.SpanGroup{Spans: spans(0, 1)},
|
|
1: cov.SpanGroup{Spans: spans(0, 3)},
|
|
2: cov.SpanGroup{Spans: spans(1, 2)},
|
|
})
|
|
checkTests(t, tree, `{a:{b:{d:{i} e:{j k}} c:{f:{l:{v x}} g:{n: {z} o}}}}`)
|
|
checkCoverage(t, tree, fileA, `a:{b:{d:{<0>} e:{[3]}} c:{f:{<2>} g:{n:{[2]} o:{<1>}}}}`)
|
|
}
|
|
|
|
func TestTreeOptInvertForCommon(t *testing.T) {
|
|
tree := &cov.Tree{}
|
|
|
|
tree.Add(cov.Path{"a", "b"}, coverage(fileA, span0))
|
|
tree.Add(cov.Path{"a", "c"}, coverage(fileA, span0))
|
|
tree.Add(cov.Path{"a", "d"}, coverage(fileA, span0))
|
|
tree.Add(cov.Path{"a", "e"}, coverage(fileA, span1))
|
|
tree.Add(cov.Path{"a", "f"}, coverage(fileA, span1))
|
|
tree.Add(cov.Path{"a", "g"}, coverage(fileA, span0))
|
|
tree.Add(cov.Path{"a", "h"}, coverage(fileA, span0))
|
|
tree.Add(cov.Path{"a", "i"}, coverage(fileA, span0))
|
|
|
|
// (a)
|
|
// ┏━━━┳━━━┳━━━┳━┻━┳━━━┳━━━┳━━━┓
|
|
// (b) (c) (d) (e) (f) (g) (h) (i)
|
|
// [0] [0] [0] [1] [1] [0] [0] [0]
|
|
checkSpans(t, tree.Spans(), span0, span1)
|
|
checkTests(t, tree, `{a:{b c d e f g h i}}`)
|
|
checkCoverage(t, tree, fileA, `a:{b:{[0]} c:{[0]} d:{[0]} e:{[1]} f:{[1]} g:{[0]} h:{[0]} i:{[0]}}`)
|
|
|
|
tree.Optimize()
|
|
|
|
// [0]
|
|
// (a)
|
|
// ╭───┬───┬───┲━┻━┱───┬───┬───╮
|
|
// b c d ┏┛ ┗┓ g h i
|
|
// (e) (f)
|
|
// <0> <0>
|
|
checkSpans(t, tree.Spans(), span0, span1)
|
|
checkGroups(t, tree.FileSpanGroups(fileA), map[cov.SpanGroupID]cov.SpanGroup{
|
|
0: cov.SpanGroup{Spans: spans(0, 1)},
|
|
})
|
|
checkTests(t, tree, `{a:{b c d e f g h i}}`)
|
|
checkCoverage(t, tree, fileA, `a:{[0] e:{<0>} f:{<0>}}`)
|
|
}
|
|
|
|
func TestTreeOptDontInvertForCommon(t *testing.T) {
|
|
tree := &cov.Tree{}
|
|
|
|
tree.Add(cov.Path{"a", "b"}, coverage(fileA, span0))
|
|
tree.Add(cov.Path{"a", "c"}, coverage(fileA, span0))
|
|
tree.Add(cov.Path{"a", "d"}, coverage(fileA, span0))
|
|
tree.Add(cov.Path{"a", "e"}, coverage(fileA, span1))
|
|
tree.Add(cov.Path{"a", "f"}, coverage(fileA, span1))
|
|
tree.Add(cov.Path{"a", "g"}, coverage(fileA, span2))
|
|
tree.Add(cov.Path{"a", "h"}, coverage(fileA, span2))
|
|
tree.Add(cov.Path{"a", "i"}, coverage(fileA, span2))
|
|
|
|
// (a)
|
|
// ┏━━━┳━━━┳━━━┳━┻━┳━━━┳━━━┳━━━┓
|
|
// (b) (c) (d) (e) (f) (g) (h) (i)
|
|
// [0] [0] [0] [1] [1] [2] [2] [2]
|
|
checkSpans(t, tree.Spans(), span0, span1, span2)
|
|
checkTests(t, tree, `{a:{b c d e f g h i}}`)
|
|
checkCoverage(t, tree, fileA, `a:{b:{[0]} c:{[0]} d:{[0]} e:{[1]} f:{[1]} g:{[2]} h:{[2]} i:{[2]}}`)
|
|
|
|
tree.Optimize()
|
|
|
|
// (a)
|
|
// ┏━━━┳━━━┳━━━┳━┻━┳━━━┳━━━┳━━━┓
|
|
// (b) (c) (d) (e) (f) (g) (h) (i)
|
|
// [0] [0] [0] [1] [1] [2] [2] [2]
|
|
checkSpans(t, tree.Spans(), span0, span1, span2)
|
|
checkTests(t, tree, `{a:{b c d e f g h i}}`)
|
|
checkCoverage(t, tree, fileA, `a:{b:{[0]} c:{[0]} d:{[0]} e:{[1]} f:{[1]} g:{[2]} h:{[2]} i:{[2]}}`)
|
|
}
|
|
|
|
func checkSpans(t *testing.T, got []cov.Span, expect ...cov.Span) {
|
|
if !reflect.DeepEqual(got, expect) {
|
|
t.Errorf("Spans not as expected.\nGot: %+v\nExpect: %+v", got, expect)
|
|
}
|
|
}
|
|
|
|
func checkGroups(t *testing.T, got, expect map[cov.SpanGroupID]cov.SpanGroup) {
|
|
if !reflect.DeepEqual(got, expect) {
|
|
t.Errorf("SpanGroupss not as expected.\nGot: %+v\nExpect: %+v", got, expect)
|
|
}
|
|
}
|
|
|
|
func checkTests(t *testing.T, tree *cov.Tree, expect string) {
|
|
g, e := tree.Tests().String(tree.Strings()), expect
|
|
if tg, te := trimWS(g), trimWS(e); tg != te {
|
|
t.Errorf("Tests not as expected.\nGot:\n%v\nExpect:\n%v\n------\nGot: %v\nExpect: %v", g, e, tg, te)
|
|
}
|
|
}
|
|
|
|
func checkCoverage(t *testing.T, tree *cov.Tree, file string, expect string) {
|
|
g, e := tree.FileCoverage(file).String(tree.Tests(), tree.Strings()), expect
|
|
if tg, te := trimWS(g), trimWS(e); tg != te {
|
|
t.Errorf("Coverage not as expected.\nGot:\n%v\nExpect:\n%v\n------\nGot: %v\nExpect: %v", g, e, tg, te)
|
|
}
|
|
}
|
|
|
|
func trimWS(s string) string {
|
|
s = strings.ReplaceAll(s, " ", "")
|
|
s = strings.ReplaceAll(s, "\n", "")
|
|
return s
|
|
}
|
|
|
|
func coverage(file string, spans ...cov.Span) *cov.Coverage {
|
|
return &cov.Coverage{
|
|
[]cov.File{
|
|
cov.File{
|
|
Path: file,
|
|
Covered: spans,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func spans(ids ...cov.SpanID) cov.SpanSet {
|
|
out := make(cov.SpanSet, len(ids))
|
|
for _, id := range ids {
|
|
out[id] = struct{}{}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func TestTreeEncodeDecode(t *testing.T) {
|
|
orig := &cov.Tree{}
|
|
orig.Add(cov.Path{"a", "b"}, coverage(fileA, span0, span1))
|
|
orig.Add(cov.Path{"a", "b", "d", "i"}, coverage(fileA, span0, span1))
|
|
orig.Add(cov.Path{"a", "b", "e"}, coverage(fileA, span0, span1, span2))
|
|
orig.Add(cov.Path{"a", "c", "g", "n"}, coverage(fileB, span0, span3))
|
|
orig.Add(cov.Path{"a", "c", "g", "o"}, coverage(fileB, span0, span3))
|
|
orig.Add(cov.Path{"a", "c", "f"}, coverage(fileA, span1))
|
|
orig.Add(cov.Path{"a", "b", "e", "j"}, coverage(fileC, span3))
|
|
orig.Add(cov.Path{"a", "b", "e", "k"}, coverage(fileA, span3))
|
|
orig.Add(cov.Path{"a", "c", "f", "l", "v"}, coverage(fileA, span1, span2))
|
|
orig.Add(cov.Path{"a", "c", "f", "l", "x"}, coverage(fileA, span1, span2))
|
|
orig.Add(cov.Path{"a", "c", "g", "n", "z"}, coverage(fileC, span2))
|
|
orig.Add(cov.Path{"a", "b"}, coverage(fileA, span0, span1))
|
|
orig.Add(cov.Path{"a", "b"}, coverage(fileA, span0, span1))
|
|
|
|
origJSON := orig.JSON("revision goes here")
|
|
read, revision, err := cov.ReadJSON(strings.NewReader(origJSON))
|
|
if err != nil {
|
|
t.Fatalf("cov.ReadJSON() failed with: %v", err)
|
|
}
|
|
readJSON := read.JSON(revision)
|
|
if origJSON != readJSON {
|
|
t.Fatalf("Encode -> Decode -> Encode produced different results:\nOriginal:\n\n%v\n\nRead:\n\n%v", origJSON, readJSON)
|
|
}
|
|
}
|