blob: 885589455dac0ad4b3946f6301a90a87b94d786e [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package covargs
import (
"fmt"
"reflect"
"strconv"
"testing"
"go.fuchsia.dev/fuchsia/tools/debug/covargs/api/llvm"
"go.fuchsia.dev/fuchsia/tools/debug/covargs/api/third_party/codecoverage"
)
// The data below were collected from the following program which is the
// canonical examples given on the Clang Source-based Code Coverage page [1]:
//
// #define BAR(x) ((x) || (x))
// template <typename T> void foo(T x) {
// for (unsigned I = 0; I < 10; ++I) { BAR(I); }
// if (false)
// for (unsigned J = 0; J < 10; ++J) { BAR(J); }
// }
// int main() {
// foo<int>(0);
// foo<float>(0);
// return 0;
// }
//
// [1] https://clang.llvm.org/docs/SourceBasedCodeCoverage.html
func TestConversion(t *testing.T) {
var testExport = &llvm.Export{
Data: []llvm.Data{
{
Files: []llvm.File{
{
Filename: "/path/to/fuchsia/src/test.cc",
Segments: []llvm.Segment{
{1, 16, 20, true, true, false},
{1, 17, 20, true, true, false},
{1, 20, 20, true, false, false},
{1, 24, 2, true, true, false},
{1, 27, 20, true, false, false},
{1, 28, 0, false, false, false},
{2, 37, 2, true, true, false},
{3, 24, 22, true, true, false},
{3, 30, 2, true, false, false},
{3, 32, 20, true, true, false},
{3, 35, 2, true, false, false},
{3, 36, 20, true, false, false},
{3, 37, 20, true, true, false},
{3, 39, 20, true, true, false},
{3, 42, 20, true, false, false},
{3, 48, 2, true, false, false},
{4, 7, 2, true, true, false},
{4, 12, 2, true, false, false},
{4, 13, 0, true, false, true},
{5, 5, 0, true, true, false},
{5, 26, 0, true, true, false},
{5, 32, 0, true, false, false},
{5, 34, 0, true, true, false},
{5, 37, 0, true, false, false},
{5, 39, 0, true, true, false},
{5, 41, 0, true, true, false},
{5, 44, 0, true, false, false},
{5, 50, 2, true, false, false},
{6, 2, 0, false, false, false},
{7, 12, 1, true, true, false},
{11, 2, 0, false, false, false},
},
Summary: llvm.Summary{
Functions: llvm.Counts{
Count: 2,
Covered: 2,
Percent: 100,
},
Instantiations: llvm.Counts{
Count: 3,
Covered: 3,
Percent: 100,
},
Lines: llvm.Counts{
Count: 10,
Covered: 9,
Percent: 90,
},
Regions: llvm.Counts{
Count: 16,
Covered: 9,
NotCovered: 7,
Percent: 56.25,
},
},
},
},
Totals: llvm.Summary{
Functions: llvm.Counts{
Count: 2,
Covered: 2,
Percent: 100,
},
Instantiations: llvm.Counts{
Count: 3,
Covered: 3,
Percent: 100,
},
Lines: llvm.Counts{
Count: 10,
Covered: 9,
Percent: 90,
},
Regions: llvm.Counts{
Count: 16,
Covered: 9,
NotCovered: 7,
Percent: 56.25,
},
},
},
},
Type: "llvm.coverage.json.export",
Version: "2.0.0",
}
var testFiles = []*codecoverage.File{
{
Path: "//src/test.cc",
Lines: []*codecoverage.LineRange{
{
First: int32(1),
Last: int32(1),
Count: int64(20),
},
{
First: int32(2),
Last: int32(2),
Count: int64(2),
},
{
First: int32(3),
Last: int32(3),
Count: int64(22),
},
{
First: int32(4),
Last: int32(4),
Count: int64(2),
},
{
First: int32(5),
Last: int32(5),
},
{
First: int32(6),
Last: int32(6),
Count: int64(2),
},
{
First: int32(7),
Last: int32(11),
Count: int64(1),
},
},
UncoveredBlocks: []*codecoverage.ColumnRanges{
{
Line: int32(4),
Ranges: []*codecoverage.ColumnRange{
{
First: int32(13),
Last: int32(-1),
},
},
},
},
Summaries: []*codecoverage.Metric{
{
Name: "function",
Covered: int32(2),
Total: int32(2),
},
{
Name: "region",
Covered: int32(9),
Total: int32(16),
},
{
Name: "line",
Covered: int32(9),
Total: int32(10),
},
},
},
}
// We pass an empty diff mapping to avoid invoking Git.
files, err := ConvertFiles(testExport, "/path/to/fuchsia", &DiffMapping{})
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(files, testFiles) {
t.Error("expected", testFiles, "but got", files)
}
}
func TestSummary(t *testing.T) {
var testFiles = []*codecoverage.File{
{
Path: "//src/test1.cc",
Lines: []*codecoverage.LineRange{
{
First: int32(1),
Last: int32(1),
Count: int64(20),
},
{
First: int32(2),
Last: int32(2),
Count: int64(2),
},
{
First: int32(3),
Last: int32(3),
Count: int64(22),
},
{
First: int32(4),
Last: int32(4),
Count: int64(2),
},
{
First: int32(5),
Last: int32(5),
},
{
First: int32(6),
Last: int32(6),
Count: int64(2),
},
{
First: int32(7),
Last: int32(11),
Count: int64(1),
},
},
UncoveredBlocks: []*codecoverage.ColumnRanges{
{
Line: int32(4),
Ranges: []*codecoverage.ColumnRange{
{
First: int32(13),
Last: int32(-1),
},
},
},
},
Summaries: []*codecoverage.Metric{
{
Name: "function",
Covered: int32(2),
Total: int32(2),
},
{
Name: "region",
Covered: int32(9),
Total: int32(16),
},
{
Name: "line",
Covered: int32(9),
Total: int32(10),
},
},
},
{
Path: "//src/test2.cc",
Lines: []*codecoverage.LineRange{
{
First: int32(1),
Last: int32(1),
Count: int64(20),
},
{
First: int32(2),
Last: int32(2),
Count: int64(2),
},
{
First: int32(3),
Last: int32(3),
Count: int64(22),
},
{
First: int32(4),
Last: int32(4),
Count: int64(2),
},
{
First: int32(5),
Last: int32(5),
},
{
First: int32(6),
Last: int32(6),
Count: int64(2),
},
{
First: int32(7),
Last: int32(11),
Count: int64(1),
},
},
UncoveredBlocks: []*codecoverage.ColumnRanges{
{
Line: int32(4),
Ranges: []*codecoverage.ColumnRange{
{
First: int32(13),
Last: int32(-1),
},
},
},
},
Summaries: []*codecoverage.Metric{
{
Name: "function",
Covered: int32(2),
Total: int32(2),
},
{
Name: "region",
Covered: int32(9),
Total: int32(16),
},
{
Name: "line",
Covered: int32(9),
Total: int32(10),
},
},
},
}
var testDirs = []*codecoverage.GroupCoverageSummary{
{
Path: "//",
Dirs: []*codecoverage.CoverageSummary{
{
Name: "src/",
Path: "//src/",
Summaries: []*codecoverage.Metric{
{
Name: "function",
Covered: int32(4),
Total: int32(4),
},
{
Name: "region",
Covered: int32(18),
Total: int32(32),
},
{
Name: "line",
Covered: int32(18),
Total: int32(20),
},
},
},
},
Summaries: []*codecoverage.Metric{
{
Name: "function",
Covered: int32(4),
Total: int32(4),
},
{
Name: "region",
Covered: int32(18),
Total: int32(32),
},
{
Name: "line",
Covered: int32(18),
Total: int32(20),
},
},
},
{
Path: "//src/",
Files: []*codecoverage.CoverageSummary{
{
Name: "test1.cc",
Path: "//src/test1.cc",
Summaries: []*codecoverage.Metric{
{
Name: "function",
Covered: int32(2),
Total: int32(2),
},
{
Name: "region",
Covered: int32(9),
Total: int32(16),
},
{
Name: "line",
Covered: int32(9),
Total: int32(10),
},
},
},
{
Name: "test2.cc",
Path: "//src/test2.cc",
Summaries: []*codecoverage.Metric{
{
Name: "function",
Covered: int32(2),
Total: int32(2),
},
{
Name: "region",
Covered: int32(9),
Total: int32(16),
},
{
Name: "line",
Covered: int32(9),
Total: int32(10),
},
},
},
},
Summaries: []*codecoverage.Metric{
{
Name: "function",
Covered: int32(4),
Total: int32(4),
},
{
Name: "region",
Covered: int32(18),
Total: int32(32),
},
{
Name: "line",
Covered: int32(18),
Total: int32(20),
},
},
},
}
var testSummaries = []*codecoverage.Metric{
{
Name: "function",
Covered: int32(4),
Total: int32(4),
},
{
Name: "region",
Covered: int32(18),
Total: int32(32),
},
{
Name: "line",
Covered: int32(18),
Total: int32(20),
},
}
dirs, summaries := ComputeSummaries(testFiles)
if !reflect.DeepEqual(dirs, testDirs) {
t.Error("expected", testDirs, "but got", dirs)
}
if !reflect.DeepEqual(summaries, testSummaries) {
t.Error("expected", testSummaries, "but got", summaries)
}
}
func TestSave(t *testing.T) {
tests := []struct {
numFiles int
shardSize int
numShards int
fileShards []string
}{
{1, 1, 0, nil},
{3, 3, 0, nil},
{6, 3, 2, []string{"files1.json.gz", "files2.json.gz"}},
{8, 3, 3, []string{"files1.json.gz", "files2.json.gz", "files3.json.gz"}},
}
for i, tt := range tests {
t.Run(strconv.Itoa(i), func(t *testing.T) {
testDir := t.TempDir()
var files []*codecoverage.File
for i := 0; i < tt.numFiles; i++ {
files = append(files, &codecoverage.File{
Path: fmt.Sprintf("//test%d.cc", i+1),
Lines: []*codecoverage.LineRange{},
UncoveredBlocks: []*codecoverage.ColumnRanges{},
Summaries: []*codecoverage.Metric{
{
Name: "function",
Covered: 0,
Total: 0,
},
{
Name: "region",
Covered: 0,
Total: 0,
},
{
Name: "line",
Covered: 0,
Total: 0,
},
},
})
}
report, err := SaveReport(files, tt.shardSize, testDir)
if err != nil {
t.Error("unexpected error", err)
}
if tt.numShards > 0 {
if numShards := len(report.FileShards); numShards != tt.numShards {
t.Error("expected", tt.numShards, "but got", numShards)
}
if !reflect.DeepEqual(report.FileShards, tt.fileShards) {
t.Error("expected", tt.fileShards, "but got", report.FileShards)
}
} else {
if numFiles := len(report.Files); numFiles != tt.numFiles {
t.Error("expected", tt.numFiles, "but got", numFiles)
}
}
})
}
}