blob: 73d9eb84595447a2ccd33201dffc22433304c20d [file] [log] [blame]
package dataframe
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.skia.org/infra/go/deepequal/assertdeep"
"go.skia.org/infra/go/paramtools"
"go.skia.org/infra/go/vec32"
perfgit "go.skia.org/infra/perf/go/git"
"go.skia.org/infra/perf/go/git/gittest"
"go.skia.org/infra/perf/go/types"
)
const (
e = vec32.MissingDataSentinel
)
func TestBuildParamSet(t *testing.T) {
// Test the empty case first.
df := &DataFrame{
TraceSet: types.TraceSet{},
ParamSet: paramtools.NewReadOnlyParamSet(),
}
df.BuildParamSet()
assert.Equal(t, 0, len(df.ParamSet))
df = &DataFrame{
TraceSet: types.TraceSet{
",arch=x86,config=565,": types.Trace([]float32{1.2, 2.1}),
",arch=x86,config=8888,": types.Trace([]float32{1.3, 3.1}),
",arch=x86,config=gpu,": types.Trace([]float32{1.4, 4.1}),
},
ParamSet: paramtools.NewReadOnlyParamSet(),
}
df.BuildParamSet()
assert.Equal(t, 2, len(df.ParamSet))
values, ok := df.ParamSet["arch"]
assert.True(t, ok)
assert.Equal(t, []string{"x86"}, values)
values, ok = df.ParamSet["config"]
assert.True(t, ok)
assert.Equal(t, []string{"565", "8888", "gpu"}, values)
}
func TestFilter(t *testing.T) {
df := &DataFrame{
TraceSet: types.TraceSet{
",arch=x86,config=565,": types.Trace([]float32{1.2, 2.1}),
",arch=x86,config=8888,": types.Trace([]float32{1.3, 3.1}),
",arch=x86,config=gpu,": types.Trace([]float32{1.4, 4.1}),
},
ParamSet: paramtools.NewReadOnlyParamSet(),
}
f := func(tr types.Trace) bool {
return tr[0] > 1.25
}
df.FilterOut(f)
assert.Equal(t, 1, len(df.TraceSet))
assert.Equal(t, []string{"565"}, df.ParamSet["config"])
df = &DataFrame{
TraceSet: types.TraceSet{
",arch=x86,config=565,": types.Trace([]float32{1.2, 2.1}),
",arch=x86,config=8888,": types.Trace([]float32{1.3, 3.1}),
",arch=x86,config=gpu,": types.Trace([]float32{1.4, 4.1}),
},
ParamSet: paramtools.NewReadOnlyParamSet(),
}
f = func(tr types.Trace) bool {
return true
}
df.FilterOut(f)
assert.Equal(t, 0, len(df.TraceSet))
}
func TestSlice(t *testing.T) {
df := &DataFrame{
Header: []*ColumnHeader{
{Offset: 10},
{Offset: 12},
{Offset: 14},
{Offset: 15},
{Offset: 16},
{Offset: 17},
},
TraceSet: types.TraceSet{
",arch=x86,config=565,": types.Trace([]float32{0.1, 0.2, 0.3, 0.4, 0.5, 0.6}),
",arch=x86,config=8888,": types.Trace([]float32{1.1, 1.2, 1.3, 1.4, 1.5, 1.6}),
",arch=x86,config=gpu,": types.Trace([]float32{2.1, 2.2, 2.3, 2.4, 2.5, 2.6}),
},
ParamSet: paramtools.NewReadOnlyParamSet(),
}
// Test error conditions.
_, err := df.Slice(0, 10)
assert.Error(t, err)
_, err = df.Slice(4, 3)
assert.Error(t, err)
// Test boundary conditions.
sub, err := df.Slice(1, 0)
assert.NoError(t, err)
assert.Equal(t, []*ColumnHeader{}, sub.Header)
assert.Len(t, sub.TraceSet, 3)
assert.Len(t, sub.TraceSet[",arch=x86,config=gpu,"], 0)
// Test the happy path.
sub, err = df.Slice(0, 3)
assert.NoError(t, err)
assert.Equal(t, []*ColumnHeader{
{Offset: 10},
{Offset: 12},
{Offset: 14},
}, sub.Header)
assert.Len(t, sub.TraceSet, 3)
assert.Equal(t, sub.TraceSet[",arch=x86,config=gpu,"], types.Trace([]float32{2.1, 2.2, 2.3}))
assert.Equal(t, sub.ParamSet, paramtools.ReadOnlyParamSet{"arch": []string{"x86"}, "config": []string{"565", "8888", "gpu"}})
sub, err = df.Slice(1, 3)
assert.NoError(t, err)
assert.Equal(t, []*ColumnHeader{
{Offset: 12},
{Offset: 14},
{Offset: 15},
}, sub.Header)
assert.Len(t, sub.TraceSet, 3)
assert.Equal(t, sub.TraceSet[",arch=x86,config=gpu,"], types.Trace([]float32{2.2, 2.3, 2.4}))
assert.Equal(t, sub.ParamSet, paramtools.ReadOnlyParamSet{"arch": []string{"x86"}, "config": []string{"565", "8888", "gpu"}})
}
func TestFromTimeRange_Success(t *testing.T) {
ctx, db, _, _, _, instanceConfig := gittest.NewForTest(t)
g, err := perfgit.New(ctx, true, db, instanceConfig)
require.NoError(t, err)
columnHeaders, commitNumbers, _, err := FromTimeRange(ctx, g, gittest.StartTime, gittest.StartTime.Add(2*time.Minute), false)
require.NoError(t, err)
assert.Equal(t, []*ColumnHeader{
{
Offset: 0,
Timestamp: gittest.StartTime.Unix(),
},
{
Offset: 1,
Timestamp: gittest.StartTime.Add(time.Minute).Unix(),
},
}, columnHeaders)
assert.Equal(t, []types.CommitNumber{0, 1}, commitNumbers)
}
func TestFromTimeRange_EmptySlicesIfNothingInTimeRange(t *testing.T) {
ctx, db, _, _, _, instanceConfig := gittest.NewForTest(t)
g, err := perfgit.New(ctx, true, db, instanceConfig)
require.NoError(t, err)
// Query outside the time of any commit.
columnHeaders, commitNumbers, _, err := FromTimeRange(ctx, g, gittest.StartTime.Add(-time.Hour), gittest.StartTime.Add(-time.Hour+2*time.Minute), false)
require.NoError(t, err)
assert.Empty(t, columnHeaders)
assert.Empty(t, commitNumbers)
}
func TestMerge(t *testing.T) {
// Simple
a := []*ColumnHeader{
{Offset: 1},
{Offset: 2},
{Offset: 4},
}
b := []*ColumnHeader{
{Offset: 3},
{Offset: 4},
}
m, aMap, bMap := MergeColumnHeaders(a, b)
expected := []*ColumnHeader{
{Offset: 1},
{Offset: 2},
{Offset: 3},
{Offset: 4},
}
assert.Equal(t, m, expected)
assert.Equal(t, map[int]int{0: 0, 1: 1, 2: 3}, aMap)
assert.Equal(t, map[int]int{0: 2, 1: 3}, bMap)
// Skips
a = []*ColumnHeader{
{Offset: 1},
{Offset: 2},
{Offset: 4},
}
b = []*ColumnHeader{
{Offset: 5},
{Offset: 7},
}
m, aMap, bMap = MergeColumnHeaders(a, b)
expected = []*ColumnHeader{
{Offset: 1},
{Offset: 2},
{Offset: 4},
{Offset: 5},
{Offset: 7},
}
assert.Equal(t, m, expected)
assert.Equal(t, map[int]int{0: 0, 1: 1, 2: 2}, aMap)
assert.Equal(t, map[int]int{0: 3, 1: 4}, bMap)
// Empty b
a = []*ColumnHeader{
{Offset: 1},
{Offset: 2},
{Offset: 4},
}
b = []*ColumnHeader{}
m, aMap, bMap = MergeColumnHeaders(a, b)
expected = []*ColumnHeader{
{Offset: 1},
{Offset: 2},
{Offset: 4},
}
assert.Equal(t, m, expected)
assert.Equal(t, map[int]int{0: 0, 1: 1, 2: 2}, aMap)
assert.Equal(t, map[int]int{}, bMap)
// Empty a
a = []*ColumnHeader{}
b = []*ColumnHeader{
{Offset: 1},
{Offset: 2},
{Offset: 4},
}
m, aMap, bMap = MergeColumnHeaders(a, b)
expected = []*ColumnHeader{
{Offset: 1},
{Offset: 2},
{Offset: 4},
}
assert.Equal(t, m, expected)
assert.Equal(t, map[int]int{}, aMap)
assert.Equal(t, map[int]int{0: 0, 1: 1, 2: 2}, bMap)
// Empty a and b.
a = []*ColumnHeader{}
b = []*ColumnHeader{}
m, aMap, bMap = MergeColumnHeaders(a, b)
expected = []*ColumnHeader{}
assert.Equal(t, m, expected)
assert.Equal(t, map[int]int{}, aMap)
assert.Equal(t, map[int]int{}, bMap)
}
func TestJoin(t *testing.T) {
a := DataFrame{
Header: []*ColumnHeader{
{Offset: 1},
{Offset: 2},
{Offset: 4},
},
TraceSet: types.TraceSet{
",config=8888,arch=x86,": []float32{0.1, 0.2, 0.4},
",config=8888,arch=arm,": []float32{1.1, 1.2, 1.4},
},
}
b := DataFrame{
Header: []*ColumnHeader{
{Offset: 3},
{Offset: 4},
},
TraceSet: types.TraceSet{
",config=565,arch=x86,": []float32{3.3, 3.4},
",config=565,arch=arm,": []float32{4.3, 4.4},
},
}
a.BuildParamSet()
b.BuildParamSet()
r := Join(&a, &b)
expectedHeader := []*ColumnHeader{
{Offset: 1},
{Offset: 2},
{Offset: 3},
{Offset: 4},
}
assert.Equal(t, expectedHeader, r.Header)
assert.Len(t, r.TraceSet, 4)
assert.Equal(t, types.Trace{0.1, 0.2, e, 0.4}, r.TraceSet[",config=8888,arch=x86,"])
assert.Equal(t, types.Trace{1.1, 1.2, e, 1.4}, r.TraceSet[",config=8888,arch=arm,"])
assert.Equal(t, types.Trace{e, e, 4.3, 4.4}, r.TraceSet[",config=565,arch=arm,"])
assert.Equal(t, types.Trace{e, e, 3.3, 3.4}, r.TraceSet[",config=565,arch=x86,"])
}
func TestCompress(t *testing.T) {
tests := []struct {
name string
source *DataFrame
want *DataFrame
}{
{
name: "DropLastColumn",
source: &DataFrame{
Header: []*ColumnHeader{
{Offset: 1},
{Offset: 2},
{Offset: 3},
},
TraceSet: types.TraceSet{
",arch=x86,": []float32{1, e, e},
",arch=arm,": []float32{e, 2, e},
},
},
want: &DataFrame{
Header: []*ColumnHeader{
{Offset: 1},
{Offset: 2},
},
TraceSet: types.TraceSet{
",arch=x86,": []float32{1, e},
",arch=arm,": []float32{e, 2},
},
},
},
{
name: "DropSecondColumn",
source: &DataFrame{
Header: []*ColumnHeader{
{Offset: 1},
{Offset: 2},
{Offset: 3},
},
TraceSet: types.TraceSet{
",arch=x86,": []float32{1, e, 3},
",arch=arm,": []float32{e, e, 3.1},
},
},
want: &DataFrame{
Header: []*ColumnHeader{
{Offset: 1},
{Offset: 3},
},
TraceSet: types.TraceSet{
",arch=x86,": []float32{1, 3},
",arch=arm,": []float32{e, 3.1},
},
},
},
{
name: "DoNotDropAnyColumns",
source: &DataFrame{
Header: []*ColumnHeader{
{Offset: 1},
{Offset: 2},
{Offset: 3},
},
TraceSet: types.TraceSet{
",arch=x86,": []float32{1, 2, 3},
},
},
want: &DataFrame{
Header: []*ColumnHeader{
{Offset: 1},
{Offset: 2},
{Offset: 3},
},
TraceSet: types.TraceSet{
",arch=x86,": []float32{1, 2, 3},
},
},
},
{
name: "HandlesEmptyDataFrames",
source: &DataFrame{
Header: []*ColumnHeader{},
TraceSet: types.TraceSet{},
},
want: &DataFrame{
Header: []*ColumnHeader{},
TraceSet: types.TraceSet{},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assertdeep.Equal(t, tt.want, tt.source.Compress())
})
}
}