blob: 2fa8bcd3c455ef87a489e6e500d1dc75d365459e [file] [log] [blame]
package databuilder
import (
"crypto/md5"
"encoding/hex"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.skia.org/infra/go/paramtools"
"go.skia.org/infra/go/testutils"
"go.skia.org/infra/go/testutils/unittest"
"go.skia.org/infra/golden/go/sql/schema"
"go.skia.org/infra/golden/go/types"
)
const (
digestA = types.Digest("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
digestB = types.Digest("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
digestC = types.Digest("cccccccccccccccccccccccccccccccc")
digestD = types.Digest("dddddddddddddddddddddddddddddddd")
)
func TestBuild_CalledWithValidInput_ProducesCorrectData(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.CommitsWithData().
Append("author_one", "subject_one", "2020-12-05T16:00:00Z").
Append("author_two", "subject_two", "2020-12-06T17:00:00Z").
Append("author_three", "subject_three", "2020-12-07T18:00:00Z").
Append("author_four", "subject_four", "2020-12-08T19:00:00Z")
b.CommitsWithNoData().Insert(5, "author_five", "no data yet", "2020-12-08T20:00:00Z")
b.SetDigests(map[rune]types.Digest{
// by convention, upper case are positively triaged, lowercase
// are untriaged, numbers are negative, symbols are special.
'A': digestA,
'b': digestB,
'1': digestC,
'D': digestD,
})
b.SetGroupingKeys(types.CorpusField, types.PrimaryKeyField)
b.AddTracesWithCommonKeys(paramtools.Params{
"os": "Android",
"device": "Crosshatch",
"color_mode": "rgb",
types.CorpusField: "corpus_one",
}).History(
"AAbb",
"D--D",
).Keys([]paramtools.Params{{
types.PrimaryKeyField: "test_one",
}, {
types.PrimaryKeyField: "test_two",
}}).OptionsAll(paramtools.Params{"ext": "png"}).
IngestedFrom([]string{"crosshatch_file1", "crosshatch_file2", "crosshatch_file3", "crosshatch_file4"},
[]string{"2020-12-11T10:09:00Z", "2020-12-11T10:10:00Z", "2020-12-11T10:11:00Z", "2020-12-11T10:12:13Z"})
b.AddTracesWithCommonKeys(paramtools.Params{
"os": "Windows10.7",
"device": "NUC1234",
"color_mode": "rgb",
types.CorpusField: "corpus_one",
types.PrimaryKeyField: "test_two",
}).History("11D-").
Keys([]paramtools.Params{{types.PrimaryKeyField: "test_one"}}).
OptionsPerTrace([]paramtools.Params{{"ext": "png"}}).
IngestedFrom([]string{"windows_file1", "windows_file2", "windows_file3", ""},
[]string{"2020-12-11T14:15:00Z", "2020-12-11T15:16:00Z", "2020-12-11T16:17:00Z", ""})
b.AddTriageEvent("user_one", "2020-12-12T12:12:12Z").
ExpectationsForGrouping(map[string]string{
types.CorpusField: "corpus_one",
types.PrimaryKeyField: "test_one"}).
Positive(digestA)
b.AddTriageEvent("user_two", "2020-12-13T13:13:13Z").
ExpectationsForGrouping(map[string]string{
types.CorpusField: "corpus_one",
types.PrimaryKeyField: "test_two"}).
Positive(digestD).
Negative(digestC)
firstIgnoreRuleID := b.AddIgnoreRule("ignore_author_one", "ignore_author_two", "2021-03-14T15:09:27Z", "note 1",
paramtools.ParamSet{
"does not": []string{"apply", "to any trace"},
})
secondIgnoreRuleID := b.AddIgnoreRule("ignore_author_two", "ignore_author_one", "2021-06-28T03:18:53Z", "note 2",
paramtools.ParamSet{
"os": []string{"Windows10.7", "Windows10.8"},
"device": []string{"NUC1234"},
})
dir := testutils.TestDataDir(t)
b.ComputeDiffMetricsFromImages(dir, "2020-12-14T14:14:14Z")
tables := b.Build()
assert.Equal(t, []schema.OptionsRow{{
OptionsID: h(`{"ext":"png"}`),
Keys: paramtools.Params{"ext": "png"},
}}, tables.Options)
assert.Equal(t, []schema.GroupingRow{{
GroupingID: h(`{"name":"test_one","source_type":"corpus_one"}`),
Keys: paramtools.Params{"name": "test_one", "source_type": "corpus_one"},
}, {
GroupingID: h(`{"name":"test_two","source_type":"corpus_one"}`),
Keys: paramtools.Params{"name": "test_two", "source_type": "corpus_one"},
}}, tables.Groupings)
assert.Equal(t, []schema.SourceFileRow{{
SourceFileID: h("crosshatch_file1"),
SourceFile: "crosshatch_file1",
LastIngested: time.Date(2020, time.December, 11, 10, 9, 0, 0, time.UTC),
}, {
SourceFileID: h("crosshatch_file2"),
SourceFile: "crosshatch_file2",
LastIngested: time.Date(2020, time.December, 11, 10, 10, 0, 0, time.UTC),
}, {
SourceFileID: h("crosshatch_file3"),
SourceFile: "crosshatch_file3",
LastIngested: time.Date(2020, time.December, 11, 10, 11, 0, 0, time.UTC),
}, {
SourceFileID: h("crosshatch_file4"),
SourceFile: "crosshatch_file4",
LastIngested: time.Date(2020, time.December, 11, 10, 12, 13, 0, time.UTC),
}, {
SourceFileID: h("windows_file1"),
SourceFile: "windows_file1",
LastIngested: time.Date(2020, time.December, 11, 14, 15, 0, 0, time.UTC),
}, {
SourceFileID: h("windows_file2"),
SourceFile: "windows_file2",
LastIngested: time.Date(2020, time.December, 11, 15, 16, 0, 0, time.UTC),
}, {
SourceFileID: h("windows_file3"),
SourceFile: "windows_file3",
LastIngested: time.Date(2020, time.December, 11, 16, 17, 0, 0, time.UTC),
}}, tables.SourceFiles)
assert.Equal(t, []schema.TraceRow{{
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_one","os":"Android","source_type":"corpus_one"}`),
Corpus: "corpus_one",
GroupingID: h(`{"name":"test_one","source_type":"corpus_one"}`),
Keys: paramtools.Params{"color_mode": "rgb", "device": "Crosshatch", "name": "test_one", "os": "Android", "source_type": "corpus_one"},
MatchesAnyIgnoreRule: schema.NBFalse,
}, {
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_two","os":"Android","source_type":"corpus_one"}`),
Corpus: "corpus_one",
GroupingID: h(`{"name":"test_two","source_type":"corpus_one"}`),
Keys: paramtools.Params{"color_mode": "rgb", "device": "Crosshatch", "name": "test_two", "os": "Android", "source_type": "corpus_one"},
MatchesAnyIgnoreRule: schema.NBFalse,
}, {
TraceID: h(`{"color_mode":"rgb","device":"NUC1234","name":"test_two","os":"Windows10.7","source_type":"corpus_one"}`),
Corpus: "corpus_one",
GroupingID: h(`{"name":"test_two","source_type":"corpus_one"}`),
Keys: paramtools.Params{"color_mode": "rgb", "device": "NUC1234", "name": "test_two", "os": "Windows10.7", "source_type": "corpus_one"},
MatchesAnyIgnoreRule: schema.NBTrue,
}}, tables.Traces)
assert.Equal(t, []schema.CommitRow{{
CommitID: 1,
GitHash: "0001000100010001000100010001000100010001",
CommitTime: time.Date(2020, time.December, 5, 16, 0, 0, 0, time.UTC),
AuthorEmail: "author_one",
Subject: "subject_one",
HasData: true,
}, {
CommitID: 2,
GitHash: "0002000200020002000200020002000200020002",
CommitTime: time.Date(2020, time.December, 6, 17, 0, 0, 0, time.UTC),
AuthorEmail: "author_two",
Subject: "subject_two",
HasData: true,
}, {
CommitID: 3,
GitHash: "0003000300030003000300030003000300030003",
CommitTime: time.Date(2020, time.December, 7, 18, 0, 0, 0, time.UTC),
AuthorEmail: "author_three",
Subject: "subject_three",
HasData: true,
}, {
CommitID: 4,
GitHash: "0004000400040004000400040004000400040004",
CommitTime: time.Date(2020, time.December, 8, 19, 0, 0, 0, time.UTC),
AuthorEmail: "author_four",
Subject: "subject_four",
HasData: true,
}, {
CommitID: 5,
GitHash: "0005000500050005000500050005000500050005",
CommitTime: time.Date(2020, time.December, 8, 20, 0, 0, 0, time.UTC),
AuthorEmail: "author_five",
Subject: "no data yet",
HasData: false,
}}, tables.Commits)
pngOptionsID := h(`{"ext":"png"}`)
testOneGroupingID := h(`{"name":"test_one","source_type":"corpus_one"}`)
testTwoGroupingID := h(`{"name":"test_two","source_type":"corpus_one"}`)
assert.Equal(t, []schema.TraceValueRow{{
Shard: 0x3,
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_one","os":"Android","source_type":"corpus_one"}`),
CommitID: 1,
Digest: d(t, digestA),
GroupingID: testOneGroupingID,
OptionsID: pngOptionsID,
SourceFileID: h("crosshatch_file1"),
}, {
Shard: 0x3,
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_one","os":"Android","source_type":"corpus_one"}`),
CommitID: 2,
Digest: d(t, digestA),
GroupingID: testOneGroupingID,
OptionsID: pngOptionsID,
SourceFileID: h("crosshatch_file2"),
}, {
Shard: 0x3,
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_one","os":"Android","source_type":"corpus_one"}`),
CommitID: 3,
Digest: d(t, digestB),
GroupingID: testOneGroupingID,
OptionsID: pngOptionsID,
SourceFileID: h("crosshatch_file3"),
}, {
Shard: 0x3,
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_one","os":"Android","source_type":"corpus_one"}`),
CommitID: 4,
Digest: d(t, digestB),
GroupingID: testOneGroupingID,
OptionsID: pngOptionsID,
SourceFileID: h("crosshatch_file4"),
}, {
Shard: 0x4,
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_two","os":"Android","source_type":"corpus_one"}`),
CommitID: 1,
Digest: d(t, digestD),
GroupingID: testTwoGroupingID,
OptionsID: pngOptionsID,
SourceFileID: h("crosshatch_file1"),
}, {
Shard: 0x4,
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_two","os":"Android","source_type":"corpus_one"}`),
CommitID: 4,
Digest: d(t, digestD),
GroupingID: testTwoGroupingID,
OptionsID: pngOptionsID,
SourceFileID: h("crosshatch_file4"),
}, {
Shard: 0x6,
TraceID: h(`{"color_mode":"rgb","device":"NUC1234","name":"test_two","os":"Windows10.7","source_type":"corpus_one"}`),
CommitID: 1,
Digest: d(t, digestC),
GroupingID: testTwoGroupingID,
OptionsID: pngOptionsID,
SourceFileID: h("windows_file1"),
}, {
Shard: 0x6,
TraceID: h(`{"color_mode":"rgb","device":"NUC1234","name":"test_two","os":"Windows10.7","source_type":"corpus_one"}`),
CommitID: 2,
Digest: d(t, digestC),
GroupingID: testTwoGroupingID,
OptionsID: pngOptionsID,
SourceFileID: h("windows_file2"),
}, {
Shard: 0x6,
TraceID: h(`{"color_mode":"rgb","device":"NUC1234","name":"test_two","os":"Windows10.7","source_type":"corpus_one"}`),
CommitID: 3,
Digest: d(t, digestD),
GroupingID: testTwoGroupingID,
OptionsID: pngOptionsID,
SourceFileID: h("windows_file3"),
}}, tables.TraceValues)
require.Len(t, tables.ExpectationRecords, 2)
recordIDOne := tables.ExpectationRecords[0].ExpectationRecordID
recordIDTwo := tables.ExpectationRecords[1].ExpectationRecordID
assert.Equal(t, []schema.ExpectationRecordRow{{
ExpectationRecordID: recordIDOne,
BranchName: nil, // primary branch
UserName: "user_one",
TriageTime: time.Date(2020, time.December, 12, 12, 12, 12, 0, time.UTC),
NumChanges: 1,
}, {
ExpectationRecordID: recordIDTwo,
BranchName: nil, // primary branch
UserName: "user_two",
TriageTime: time.Date(2020, time.December, 13, 13, 13, 13, 0, time.UTC),
NumChanges: 2,
}}, tables.ExpectationRecords)
assert.Equal(t, []schema.ExpectationDeltaRow{{
ExpectationRecordID: recordIDOne,
GroupingID: testOneGroupingID,
Digest: d(t, digestA),
LabelBefore: schema.LabelUntriaged,
LabelAfter: schema.LabelPositive,
}, {
ExpectationRecordID: recordIDTwo,
GroupingID: testTwoGroupingID,
Digest: d(t, digestD),
LabelBefore: schema.LabelUntriaged,
LabelAfter: schema.LabelPositive,
}, {
ExpectationRecordID: recordIDTwo,
GroupingID: testTwoGroupingID,
Digest: d(t, digestC),
LabelBefore: schema.LabelUntriaged,
LabelAfter: schema.LabelNegative,
}}, tables.ExpectationDeltas)
assert.Equal(t, []schema.ExpectationRow{{
GroupingID: testOneGroupingID,
Digest: d(t, digestA),
Label: schema.LabelPositive,
ExpectationRecordID: &recordIDOne,
}, {
GroupingID: testTwoGroupingID,
Digest: d(t, digestD),
Label: schema.LabelPositive,
ExpectationRecordID: &recordIDTwo,
}, {
GroupingID: testTwoGroupingID,
Digest: d(t, digestC),
Label: schema.LabelNegative,
ExpectationRecordID: &recordIDTwo,
}, {
GroupingID: testOneGroupingID,
Digest: d(t, digestB),
Label: schema.LabelUntriaged,
ExpectationRecordID: nil,
}}, tables.Expectations)
ts := time.Date(2020, time.December, 14, 14, 14, 14, 0, time.UTC)
assert.ElementsMatch(t, []schema.DiffMetricRow{{
LeftDigest: d(t, digestA),
RightDigest: d(t, digestB),
NumPixelsDiff: 7,
PercentPixelsDiff: 10.9375,
MaxRGBADiffs: [4]int{250, 244, 197, 51},
MaxChannelDiff: 250,
CombinedMetric: 2.9445405,
DimensionsDiffer: false,
Timestamp: ts,
}, {
LeftDigest: d(t, digestB),
RightDigest: d(t, digestA),
NumPixelsDiff: 7,
PercentPixelsDiff: 10.9375,
MaxRGBADiffs: [4]int{250, 244, 197, 51},
MaxChannelDiff: 250,
CombinedMetric: 2.9445405,
DimensionsDiffer: false,
Timestamp: ts,
}, {
LeftDigest: d(t, digestC),
RightDigest: d(t, digestD),
NumPixelsDiff: 36,
PercentPixelsDiff: 56.25,
MaxRGBADiffs: [4]int{106, 21, 21, 0},
MaxChannelDiff: 106,
CombinedMetric: 3.4844475,
DimensionsDiffer: false,
Timestamp: ts,
}, {
LeftDigest: d(t, digestD),
RightDigest: d(t, digestC),
NumPixelsDiff: 36,
PercentPixelsDiff: 56.25,
MaxRGBADiffs: [4]int{106, 21, 21, 0},
MaxChannelDiff: 106,
CombinedMetric: 3.4844475,
DimensionsDiffer: false,
Timestamp: ts,
}}, tables.DiffMetrics)
assert.ElementsMatch(t, []schema.TiledTraceDigestRow{{
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_one","os":"Android","source_type":"corpus_one"}`),
StartCommitID: 0,
Digest: d(t, digestA),
}, {
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_one","os":"Android","source_type":"corpus_one"}`),
StartCommitID: 0,
Digest: d(t, digestB),
}, {
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_two","os":"Android","source_type":"corpus_one"}`),
StartCommitID: 0,
Digest: d(t, digestD),
}, {
TraceID: h(`{"color_mode":"rgb","device":"NUC1234","name":"test_two","os":"Windows10.7","source_type":"corpus_one"}`),
StartCommitID: 0,
Digest: d(t, digestC),
}, {
TraceID: h(`{"color_mode":"rgb","device":"NUC1234","name":"test_two","os":"Windows10.7","source_type":"corpus_one"}`),
StartCommitID: 0,
Digest: d(t, digestD),
}}, tables.TiledTraceDigests)
assert.ElementsMatch(t, []schema.PrimaryBranchParamRow{
{Key: "name", Value: "test_one", StartCommitID: 0},
{Key: "name", Value: "test_two", StartCommitID: 0},
{Key: "device", Value: "Crosshatch", StartCommitID: 0},
{Key: "device", Value: "NUC1234", StartCommitID: 0},
{Key: "os", Value: "Android", StartCommitID: 0},
{Key: "os", Value: "Windows10.7", StartCommitID: 0},
{Key: "color_mode", Value: "rgb", StartCommitID: 0},
{Key: "source_type", Value: "corpus_one", StartCommitID: 0},
{Key: "ext", Value: "png", StartCommitID: 0},
}, tables.PrimaryBranchParams)
assert.ElementsMatch(t, []schema.ValueAtHeadRow{{
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_one","os":"Android","source_type":"corpus_one"}`),
MostRecentCommitID: 4,
Digest: d(t, digestB),
OptionsID: pngOptionsID,
GroupingID: testOneGroupingID,
Corpus: "corpus_one",
Keys: paramtools.Params{"color_mode": "rgb", "device": "Crosshatch", "name": "test_one", "os": "Android", "source_type": "corpus_one"},
Label: schema.LabelUntriaged,
ExpectationRecordID: nil,
MatchesAnyIgnoreRule: schema.NBFalse,
}, {
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_two","os":"Android","source_type":"corpus_one"}`),
MostRecentCommitID: 4,
Digest: d(t, digestD),
OptionsID: pngOptionsID,
GroupingID: testTwoGroupingID,
Corpus: "corpus_one",
Keys: paramtools.Params{"color_mode": "rgb", "device": "Crosshatch", "name": "test_two", "os": "Android", "source_type": "corpus_one"},
Label: schema.LabelPositive,
ExpectationRecordID: &recordIDTwo,
MatchesAnyIgnoreRule: schema.NBFalse,
}, {
TraceID: h(`{"color_mode":"rgb","device":"NUC1234","name":"test_two","os":"Windows10.7","source_type":"corpus_one"}`),
MostRecentCommitID: 3,
Digest: d(t, digestD),
OptionsID: pngOptionsID,
GroupingID: testTwoGroupingID,
Corpus: "corpus_one",
Keys: paramtools.Params{"color_mode": "rgb", "device": "NUC1234", "name": "test_two", "os": "Windows10.7", "source_type": "corpus_one"},
Label: schema.LabelPositive,
ExpectationRecordID: &recordIDTwo,
MatchesAnyIgnoreRule: schema.NBTrue,
}}, tables.ValuesAtHead)
assert.Equal(t, []schema.IgnoreRuleRow{{
IgnoreRuleID: firstIgnoreRuleID,
CreatorEmail: "ignore_author_one",
UpdatedEmail: "ignore_author_two",
Expires: time.Date(2021, time.March, 14, 15, 9, 27, 0, time.UTC),
Note: "note 1",
Query: paramtools.ReadOnlyParamSet{"does not": []string{"apply", "to any trace"}},
}, {
IgnoreRuleID: secondIgnoreRuleID,
CreatorEmail: "ignore_author_two",
UpdatedEmail: "ignore_author_one",
Expires: time.Date(2021, time.June, 28, 03, 18, 53, 0, time.UTC),
Note: "note 2",
Query: paramtools.ReadOnlyParamSet{"device": []string{"NUC1234"}, "os": []string{"Windows10.7", "Windows10.8"}},
}}, tables.IgnoreRules)
}
func TestBuild_CalledWithChangelistData_ProducesCorrectData(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.CommitsWithData().
Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
b.SetDigests(map[rune]types.Digest{
// by convention, upper case are positively triaged, lowercase
// are untriaged, numbers are negative, symbols are special.
'A': digestA,
'b': digestB,
'1': digestC,
'D': digestD,
})
b.SetGroupingKeys(types.CorpusField, types.PrimaryKeyField)
b.AddTracesWithCommonKeys(paramtools.Params{
"os": "Android",
"device": "Crosshatch",
"color_mode": "rgb",
types.CorpusField: "corpus_one",
}).History(
"A",
"D",
).Keys([]paramtools.Params{{
types.PrimaryKeyField: "test_one",
}, {
types.PrimaryKeyField: "test_two",
}}).OptionsAll(paramtools.Params{"ext": "png"}).
IngestedFrom([]string{"crosshatch_file1"}, []string{"2020-12-11T10:09:00Z"})
cl := b.AddChangelist("changelist_one", "gerrit", "owner_one", "First CL", schema.StatusAbandoned)
cl.AddPatchset("ps_2", "ps_hash_2", 2).
DataWithCommonKeys(paramtools.Params{
"os": "Android",
"device": "Crosshatch",
"color_mode": "rgb",
types.CorpusField: "corpus_one",
}).Digests(digestB, digestC, digestD).
Keys([]paramtools.Params{{
types.PrimaryKeyField: "test_one",
}, {
types.PrimaryKeyField: "test_two",
}, {
types.PrimaryKeyField: "test_three",
}}).OptionsAll(paramtools.Params{"ext": "png"}).
FromTryjob("tryjob_001", "bb", "Test-Crosshatch", "tryjob_file1", "2020-12-11T10:11:00Z")
cl.AddPatchset("ps_3", "ps_hash_3", 3).
DataWithCommonKeys(paramtools.Params{
"os": "Android",
"device": "Crosshatch",
"color_mode": "rgb",
types.CorpusField: "corpus_one",
}).Digests(digestB, digestC, digestA).
Keys([]paramtools.Params{{
types.PrimaryKeyField: "test_one",
}, {
types.PrimaryKeyField: "test_two",
}, {
types.PrimaryKeyField: "test_three",
}}).OptionsPerPoint([]paramtools.Params{
{"ext": "png"},
{"ext": "png"},
{"ext": "png", "matcher": "fuzzy", "threshold": "2"},
}).
FromTryjob("tryjob_002", "bb", "Test-Crosshatch", "tryjob_file2", "2020-12-11T11:12:13Z")
cl.AddTriageEvent("cl_user", "2020-12-11T11:40:00Z").
ExpectationsForGrouping(map[string]string{
types.CorpusField: "corpus_one",
types.PrimaryKeyField: "test_three"}).
Negative(digestD)
b.AddTriageEvent("user_one", "2020-12-12T12:12:12Z").
ExpectationsForGrouping(map[string]string{
types.CorpusField: "corpus_one",
types.PrimaryKeyField: "test_one"}).
Positive(digestA).
ExpectationsForGrouping(map[string]string{
types.CorpusField: "corpus_one",
types.PrimaryKeyField: "test_two"}).
Positive(digestD)
b.AddIgnoreRule("ignore_author", "ignore_author", "2021-03-14T15:09:27Z", "note 1",
paramtools.ParamSet{
types.PrimaryKeyField: []string{"test_two"},
})
dir := testutils.TestDataDir(t)
b.ComputeDiffMetricsFromImages(dir, "2020-12-14T14:14:14Z")
tables := b.Build()
assert.Equal(t, []schema.OptionsRow{{
OptionsID: h(`{"ext":"png"}`),
Keys: paramtools.Params{"ext": "png"},
}, {
OptionsID: h(`{"ext":"png","matcher":"fuzzy","threshold":"2"}`),
Keys: paramtools.Params{"ext": "png", "matcher": "fuzzy", "threshold": "2"},
}}, tables.Options)
assert.Equal(t, []schema.GroupingRow{{
GroupingID: h(`{"name":"test_one","source_type":"corpus_one"}`),
Keys: paramtools.Params{"name": "test_one", "source_type": "corpus_one"},
}, {
GroupingID: h(`{"name":"test_two","source_type":"corpus_one"}`),
Keys: paramtools.Params{"name": "test_two", "source_type": "corpus_one"},
}, {
GroupingID: h(`{"name":"test_three","source_type":"corpus_one"}`),
Keys: paramtools.Params{"name": "test_three", "source_type": "corpus_one"},
}}, tables.Groupings)
assert.Equal(t, []schema.SourceFileRow{{
SourceFileID: h("crosshatch_file1"),
SourceFile: "crosshatch_file1",
LastIngested: time.Date(2020, time.December, 11, 10, 9, 0, 0, time.UTC),
}, {
SourceFileID: h("tryjob_file1"),
SourceFile: "tryjob_file1",
LastIngested: time.Date(2020, time.December, 11, 10, 11, 0, 0, time.UTC),
}, {
SourceFileID: h("tryjob_file2"),
SourceFile: "tryjob_file2",
LastIngested: time.Date(2020, time.December, 11, 11, 12, 13, 0, time.UTC),
}}, tables.SourceFiles)
assert.Equal(t, []schema.TraceRow{{
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_one","os":"Android","source_type":"corpus_one"}`),
Corpus: "corpus_one",
GroupingID: h(`{"name":"test_one","source_type":"corpus_one"}`),
Keys: paramtools.Params{"color_mode": "rgb", "device": "Crosshatch", "name": "test_one", "os": "Android", "source_type": "corpus_one"},
MatchesAnyIgnoreRule: schema.NBFalse,
}, {
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_two","os":"Android","source_type":"corpus_one"}`),
Corpus: "corpus_one",
GroupingID: h(`{"name":"test_two","source_type":"corpus_one"}`),
Keys: paramtools.Params{"color_mode": "rgb", "device": "Crosshatch", "name": "test_two", "os": "Android", "source_type": "corpus_one"},
MatchesAnyIgnoreRule: schema.NBTrue,
}, {
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_three","os":"Android","source_type":"corpus_one"}`),
Corpus: "corpus_one",
GroupingID: h(`{"name":"test_three","source_type":"corpus_one"}`),
Keys: paramtools.Params{"color_mode": "rgb", "device": "Crosshatch", "name": "test_three", "os": "Android", "source_type": "corpus_one"},
MatchesAnyIgnoreRule: schema.NBFalse,
}}, tables.Traces)
assert.Equal(t, []schema.TraceValueRow{{
Shard: 0x3,
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_one","os":"Android","source_type":"corpus_one"}`),
CommitID: 1,
Digest: d(t, digestA),
GroupingID: h(`{"name":"test_one","source_type":"corpus_one"}`),
OptionsID: h(`{"ext":"png"}`),
SourceFileID: h("crosshatch_file1"),
}, {
Shard: 0x4,
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_two","os":"Android","source_type":"corpus_one"}`),
CommitID: 1,
Digest: d(t, digestD),
GroupingID: h(`{"name":"test_two","source_type":"corpus_one"}`),
OptionsID: h(`{"ext":"png"}`),
SourceFileID: h("crosshatch_file1"),
}}, tables.TraceValues)
assert.ElementsMatch(t, []schema.PrimaryBranchParamRow{
{Key: "name", Value: "test_one", StartCommitID: 0},
{Key: "name", Value: "test_two", StartCommitID: 0},
{Key: "device", Value: "Crosshatch", StartCommitID: 0},
{Key: "os", Value: "Android", StartCommitID: 0},
{Key: "color_mode", Value: "rgb", StartCommitID: 0},
{Key: "source_type", Value: "corpus_one", StartCommitID: 0},
{Key: "ext", Value: "png", StartCommitID: 0},
}, tables.PrimaryBranchParams)
qualifiedCLID := "gerrit_changelist_one"
assert.Equal(t, []schema.ChangelistRow{{
ChangelistID: qualifiedCLID,
System: "gerrit",
Status: schema.StatusAbandoned,
OwnerEmail: "owner_one",
Subject: "First CL",
LastIngestedData: time.Date(2020, time.December, 11, 11, 12, 13, 0, time.UTC),
}}, tables.Changelists)
assert.Equal(t, []schema.PatchsetRow{{
PatchsetID: "gerrit_ps_2",
System: "gerrit",
ChangelistID: qualifiedCLID,
Order: 2,
GitHash: "ps_hash_2",
}, {
PatchsetID: "gerrit_ps_3",
System: "gerrit",
ChangelistID: qualifiedCLID,
Order: 3,
GitHash: "ps_hash_3",
}}, tables.Patchsets)
assert.Equal(t, []schema.TryjobRow{{
TryjobID: "bb_tryjob_001",
System: "bb",
ChangelistID: qualifiedCLID,
PatchsetID: "gerrit_ps_2",
DisplayName: "Test-Crosshatch",
LastIngestedData: time.Date(2020, time.December, 11, 10, 11, 0, 0, time.UTC),
}, {
TryjobID: "bb_tryjob_002",
System: "bb",
ChangelistID: qualifiedCLID,
PatchsetID: "gerrit_ps_3",
DisplayName: "Test-Crosshatch",
LastIngestedData: time.Date(2020, time.December, 11, 11, 12, 13, 0, time.UTC),
}}, tables.Tryjobs)
assert.ElementsMatch(t, []schema.SecondaryBranchParamRow{
{Key: "name", Value: "test_one", BranchName: qualifiedCLID, VersionName: "gerrit_ps_2"},
{Key: "name", Value: "test_two", BranchName: qualifiedCLID, VersionName: "gerrit_ps_2"},
{Key: "name", Value: "test_three", BranchName: qualifiedCLID, VersionName: "gerrit_ps_2"},
{Key: "device", Value: "Crosshatch", BranchName: qualifiedCLID, VersionName: "gerrit_ps_2"},
{Key: "os", Value: "Android", BranchName: qualifiedCLID, VersionName: "gerrit_ps_2"},
{Key: "color_mode", Value: "rgb", BranchName: qualifiedCLID, VersionName: "gerrit_ps_2"},
{Key: "source_type", Value: "corpus_one", BranchName: qualifiedCLID, VersionName: "gerrit_ps_2"},
{Key: "ext", Value: "png", BranchName: qualifiedCLID, VersionName: "gerrit_ps_2"},
{Key: "name", Value: "test_one", BranchName: qualifiedCLID, VersionName: "gerrit_ps_3"},
{Key: "name", Value: "test_two", BranchName: qualifiedCLID, VersionName: "gerrit_ps_3"},
{Key: "name", Value: "test_three", BranchName: qualifiedCLID, VersionName: "gerrit_ps_3"},
{Key: "device", Value: "Crosshatch", BranchName: qualifiedCLID, VersionName: "gerrit_ps_3"},
{Key: "os", Value: "Android", BranchName: qualifiedCLID, VersionName: "gerrit_ps_3"},
{Key: "color_mode", Value: "rgb", BranchName: qualifiedCLID, VersionName: "gerrit_ps_3"},
{Key: "source_type", Value: "corpus_one", BranchName: qualifiedCLID, VersionName: "gerrit_ps_3"},
{Key: "ext", Value: "png", BranchName: qualifiedCLID, VersionName: "gerrit_ps_3"},
{Key: "matcher", Value: "fuzzy", BranchName: qualifiedCLID, VersionName: "gerrit_ps_3"},
{Key: "threshold", Value: "2", BranchName: qualifiedCLID, VersionName: "gerrit_ps_3"},
}, tables.SecondaryBranchParams)
assert.Equal(t, []schema.SecondaryBranchValueRow{{
BranchName: qualifiedCLID,
VersionName: "gerrit_ps_2",
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_one","os":"Android","source_type":"corpus_one"}`),
Digest: d(t, digestB),
GroupingID: h(`{"name":"test_one","source_type":"corpus_one"}`),
OptionsID: h(`{"ext":"png"}`),
SourceFileID: h("tryjob_file1"),
TryjobID: "bb_tryjob_001",
}, {
BranchName: qualifiedCLID,
VersionName: "gerrit_ps_2",
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_two","os":"Android","source_type":"corpus_one"}`),
Digest: d(t, digestC),
GroupingID: h(`{"name":"test_two","source_type":"corpus_one"}`),
OptionsID: h(`{"ext":"png"}`),
SourceFileID: h("tryjob_file1"),
TryjobID: "bb_tryjob_001",
}, {
BranchName: qualifiedCLID,
VersionName: "gerrit_ps_2",
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_three","os":"Android","source_type":"corpus_one"}`),
Digest: d(t, digestD),
GroupingID: h(`{"name":"test_three","source_type":"corpus_one"}`),
OptionsID: h(`{"ext":"png"}`),
SourceFileID: h("tryjob_file1"),
TryjobID: "bb_tryjob_001",
}, {
BranchName: qualifiedCLID,
VersionName: "gerrit_ps_3",
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_one","os":"Android","source_type":"corpus_one"}`),
Digest: d(t, digestB),
GroupingID: h(`{"name":"test_one","source_type":"corpus_one"}`),
OptionsID: h(`{"ext":"png"}`),
SourceFileID: h("tryjob_file2"),
TryjobID: "bb_tryjob_002",
}, {
BranchName: qualifiedCLID,
VersionName: "gerrit_ps_3",
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_two","os":"Android","source_type":"corpus_one"}`),
Digest: d(t, digestC),
GroupingID: h(`{"name":"test_two","source_type":"corpus_one"}`),
OptionsID: h(`{"ext":"png"}`),
SourceFileID: h("tryjob_file2"),
TryjobID: "bb_tryjob_002",
}, {
BranchName: qualifiedCLID,
VersionName: "gerrit_ps_3",
TraceID: h(`{"color_mode":"rgb","device":"Crosshatch","name":"test_three","os":"Android","source_type":"corpus_one"}`),
Digest: d(t, digestA),
GroupingID: h(`{"name":"test_three","source_type":"corpus_one"}`),
OptionsID: h(`{"ext":"png","matcher":"fuzzy","threshold":"2"}`),
SourceFileID: h("tryjob_file2"),
TryjobID: "bb_tryjob_002",
}}, tables.SecondaryBranchValues)
require.Len(t, tables.ExpectationRecords, 2)
primaryBranchRecordID := tables.ExpectationRecords[0].ExpectationRecordID
clRecordID := tables.ExpectationRecords[1].ExpectationRecordID
assert.Equal(t, []schema.ExpectationRecordRow{{
ExpectationRecordID: primaryBranchRecordID,
BranchName: nil, // primary branch
UserName: "user_one",
TriageTime: time.Date(2020, time.December, 12, 12, 12, 12, 0, time.UTC),
NumChanges: 2,
}, {
ExpectationRecordID: clRecordID,
BranchName: &qualifiedCLID,
UserName: "cl_user",
TriageTime: time.Date(2020, time.December, 11, 11, 40, 0, 0, time.UTC),
NumChanges: 1,
}}, tables.ExpectationRecords)
assert.Equal(t, []schema.ExpectationDeltaRow{{
ExpectationRecordID: primaryBranchRecordID,
GroupingID: h(`{"name":"test_one","source_type":"corpus_one"}`),
Digest: d(t, digestA),
LabelBefore: schema.LabelUntriaged,
LabelAfter: schema.LabelPositive,
}, {
ExpectationRecordID: primaryBranchRecordID,
GroupingID: h(`{"name":"test_two","source_type":"corpus_one"}`),
Digest: d(t, digestD),
LabelBefore: schema.LabelUntriaged,
LabelAfter: schema.LabelPositive,
}, {
ExpectationRecordID: clRecordID,
GroupingID: h(`{"name":"test_three","source_type":"corpus_one"}`),
Digest: d(t, digestD),
LabelBefore: schema.LabelUntriaged,
LabelAfter: schema.LabelNegative,
}}, tables.ExpectationDeltas)
assert.Equal(t, []schema.SecondaryBranchExpectationRow{{
BranchName: qualifiedCLID,
GroupingID: h(`{"name":"test_three","source_type":"corpus_one"}`),
Digest: d(t, digestD),
Label: schema.LabelNegative,
ExpectationRecordID: clRecordID,
}}, tables.SecondaryBranchExpectations)
assert.Equal(t, []schema.ExpectationRow{{
GroupingID: h(`{"name":"test_one","source_type":"corpus_one"}`),
Digest: d(t, digestA),
Label: schema.LabelPositive,
ExpectationRecordID: &primaryBranchRecordID,
}, {
GroupingID: h(`{"name":"test_two","source_type":"corpus_one"}`),
Digest: d(t, digestD),
Label: schema.LabelPositive,
ExpectationRecordID: &primaryBranchRecordID,
}}, tables.Expectations)
ts := time.Date(2020, time.December, 14, 14, 14, 14, 0, time.UTC)
assert.ElementsMatch(t, []schema.DiffMetricRow{{
// These first 4 are the same as the first test.
LeftDigest: d(t, digestA),
RightDigest: d(t, digestB),
NumPixelsDiff: 7,
PercentPixelsDiff: 10.9375,
MaxRGBADiffs: [4]int{250, 244, 197, 51},
MaxChannelDiff: 250,
CombinedMetric: 2.9445405,
DimensionsDiffer: false,
Timestamp: ts,
}, {
LeftDigest: d(t, digestB),
RightDigest: d(t, digestA),
NumPixelsDiff: 7,
PercentPixelsDiff: 10.9375,
MaxRGBADiffs: [4]int{250, 244, 197, 51},
MaxChannelDiff: 250,
CombinedMetric: 2.9445405,
DimensionsDiffer: false,
Timestamp: ts,
}, {
LeftDigest: d(t, digestC),
RightDigest: d(t, digestD),
NumPixelsDiff: 36,
PercentPixelsDiff: 56.25,
MaxRGBADiffs: [4]int{106, 21, 21, 0},
MaxChannelDiff: 106,
CombinedMetric: 3.4844475,
DimensionsDiffer: false,
Timestamp: ts,
}, {
LeftDigest: d(t, digestD),
RightDigest: d(t, digestC),
NumPixelsDiff: 36,
PercentPixelsDiff: 56.25,
MaxRGBADiffs: [4]int{106, 21, 21, 0},
MaxChannelDiff: 106,
CombinedMetric: 3.4844475,
DimensionsDiffer: false,
Timestamp: ts,
}, { // The following 2 were calculated on the new test introduced by this CL
LeftDigest: d(t, digestA),
RightDigest: d(t, digestD),
NumPixelsDiff: 64,
PercentPixelsDiff: 100,
MaxRGBADiffs: [4]int{250, 244, 197, 255},
MaxChannelDiff: 255,
CombinedMetric: 9.653383,
DimensionsDiffer: false,
Timestamp: ts,
}, {
LeftDigest: d(t, digestD),
RightDigest: d(t, digestA),
NumPixelsDiff: 64,
PercentPixelsDiff: 100,
MaxRGBADiffs: [4]int{250, 244, 197, 255},
MaxChannelDiff: 255,
CombinedMetric: 9.653383,
DimensionsDiffer: false,
Timestamp: ts,
}}, tables.DiffMetrics)
}
func TestCommits_CalledMultipleTimes_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.CommitsWithData()
assert.Panics(t, func() {
b.CommitsWithData()
})
}
func TestCommits_InvalidTime_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
assert.Panics(t, func() {
b.CommitsWithData().Append("fine", "dandy", "no good")
})
}
func TestCommits_InsertInMonotonicOrder_Success(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.CommitsWithData().
Insert(98, "author_one", "subject_98", "2020-12-05T15:00:00Z").
Append("author_one", "subject_99", "2020-12-05T16:00:00Z").
Insert(2000, "author_2k", "subject_2k", "2022-02-02T02:02:00Z")
tables := b.Build()
assert.Equal(t, []schema.CommitRow{{
CommitID: 98,
GitHash: "0098009800980098009800980098009800980098",
CommitTime: time.Date(2020, time.December, 5, 15, 0, 0, 0, time.UTC),
AuthorEmail: "author_one",
Subject: "subject_98",
HasData: false,
}, {
CommitID: 99,
GitHash: "0099009900990099009900990099009900990099",
CommitTime: time.Date(2020, time.December, 5, 16, 0, 0, 0, time.UTC),
AuthorEmail: "author_one",
Subject: "subject_99",
HasData: false,
}, {
CommitID: 2000,
GitHash: "2000200020002000200020002000200020002000",
CommitTime: time.Date(2022, time.February, 2, 2, 2, 0, 0, time.UTC),
AuthorEmail: "author_2k",
Subject: "subject_2k",
HasData: false,
}}, tables.Commits)
}
func TestCommits_InsertOutOfOrder_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
assert.Panics(t, func() {
b.CommitsWithData().
Insert(2000, "author_2k", "subject_2k", "2022-02-02T02:02:00Z").
Insert(98, "author_one", "subject_98", "2020-12-05T15:00:00Z").
Append("author_one", "subject_99", "2020-12-05T16:00:00Z")
})
}
func TestSetDigests_CalledMultipleTimes_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetDigests(map[rune]types.Digest{'A': digestA})
assert.Panics(t, func() {
b.SetDigests(map[rune]types.Digest{'A': digestA})
})
}
func TestSetDigests_InvalidData_Panics(t *testing.T) {
unittest.SmallTest(t)
assert.Panics(t, func() {
b := TablesBuilder{}
b.SetDigests(map[rune]types.Digest{'-': digestA})
})
assert.Panics(t, func() {
b := TablesBuilder{}
b.SetDigests(map[rune]types.Digest{'a': "Invalid digest!"})
})
}
func TestSetGroupingKeys_CalledMultipleTimes_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys("foo")
assert.Panics(t, func() {
b.SetGroupingKeys("bar")
})
}
func TestAddTracesWithCommonKeys_MissingSetupCalls_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
assert.Panics(t, func() {
b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
assert.Panics(t, func() {
b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
})
b.SetGroupingKeys(types.CorpusField)
assert.Panics(t, func() {
b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
})
b.SetDigests(map[rune]types.Digest{'A': digestA})
// Everything should be setup now
assert.NotPanics(t, func() {
b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
})
}
func TestAddTracesWithCommonKeys_ZeroCommitsSpecified_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData() // oops, no commits
assert.Panics(t, func() {
b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
})
}
func TestHistory_CalledMultipleTimes_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
tb := b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
tb.History("A")
assert.Panics(t, func() {
tb.History("A")
})
}
func TestHistory_WrongSizeTraces_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
tb := b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
// Expected length is 1
assert.Panics(t, func() {
tb.History("AA")
})
tb = b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
assert.Panics(t, func() {
tb.History("A", "-A")
})
tb = b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
assert.Panics(t, func() {
tb.History("A", "")
})
}
func TestHistory_UnknownSymbol_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
tb := b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
assert.Panics(t, func() {
tb.History("?")
})
}
func TestKeys_CalledWithoutHistory_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
tb := b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
assert.Panics(t, func() {
tb.Keys([]paramtools.Params{{types.CorpusField: "whatever"}})
})
}
func TestKeys_CalledMultipleTimes_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
tb := b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
tb.History("A")
tb.Keys([]paramtools.Params{{types.CorpusField: "whatever"}})
assert.Panics(t, func() {
tb.Keys([]paramtools.Params{{types.CorpusField: "whatever"}})
})
}
func TestKeys_IncorrectLength_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
tb := b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
tb.History("A")
assert.Panics(t, func() {
tb.Keys(nil)
})
tb = b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
tb.History("A")
assert.Panics(t, func() {
tb.Keys([]paramtools.Params{})
})
tb = b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
tb.History("A")
assert.Panics(t, func() {
tb.Keys([]paramtools.Params{{types.CorpusField: "too"}, {types.CorpusField: "many"}})
})
}
func TestKeys_MissingGrouping_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys("group1", "group2")
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
tb := b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
tb.History("A")
assert.Panics(t, func() {
// missing group2
tb.Keys([]paramtools.Params{{"group1": "whatever"}})
})
}
func TestKeys_IdenticalTraces_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys("group1")
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
tb := b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
tb.History("A", "-")
assert.Panics(t, func() {
tb.Keys([]paramtools.Params{{"group1": "identical"}, {"group1": "identical"}})
})
}
func TestOptionsPerTrace_CalledWithoutHistory_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
tb := b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
assert.Panics(t, func() {
tb.OptionsPerTrace([]paramtools.Params{{"opt": "whatever"}})
})
}
func TestOptionsPerTrace_CalledMultipleTimes_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
tb := b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
tb.History("A")
tb.OptionsPerTrace([]paramtools.Params{{"opt": "whatever"}})
assert.Panics(t, func() {
tb.OptionsPerTrace([]paramtools.Params{{"opt": "whatever"}})
})
}
func TestOptionsPerTrace_IncorrectLength_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
tb := b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
tb.History("A")
assert.Panics(t, func() {
tb.OptionsPerTrace(nil)
})
tb = b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
tb.History("A")
assert.Panics(t, func() {
tb.OptionsPerTrace([]paramtools.Params{})
})
tb = b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
tb.History("A")
assert.Panics(t, func() {
tb.OptionsPerTrace([]paramtools.Params{{"opt": "too"}, {"opt": "many"}})
})
}
func TestOptionsPerPoint_CorrectDataLinedUp(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys("test")
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData().
Append("author_one", "subject_one", "2020-12-05T16:00:00Z").
Append("author_one", "subject_two", "2020-12-05T17:00:00Z")
b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"}).
History("AA", "AA").
Keys([]paramtools.Params{{"test": "one"}, {"test": "two"}}).
OptionsPerPoint([][]paramtools.Params{
{{"option": "cell 1"}, {"option": "cell 2"}},
{{"option": "cell 3"}, {"option": "cell 4"}},
}).IngestedFrom([]string{"first", "second"}, []string{"2020-12-05T16:30:00Z", "2020-12-05T17:30:00Z"})
data := b.Build()
assert.Equal(t, []schema.OptionsRow{{
OptionsID: h(`{"option":"cell 1"}`),
Keys: paramtools.Params{"option": "cell 1"},
}, {
OptionsID: h(`{"option":"cell 2"}`),
Keys: paramtools.Params{"option": "cell 2"},
}, {
OptionsID: h(`{"option":"cell 3"}`),
Keys: paramtools.Params{"option": "cell 3"},
}, {
OptionsID: h(`{"option":"cell 4"}`),
Keys: paramtools.Params{"option": "cell 4"},
}}, data.Options)
assert.Equal(t, schema.OptionsID(h(`{"option":"cell 1"}`)), data.TraceValues[0].OptionsID)
assert.Equal(t, schema.OptionsID(h(`{"option":"cell 2"}`)), data.TraceValues[1].OptionsID)
assert.Equal(t, schema.OptionsID(h(`{"option":"cell 3"}`)), data.TraceValues[2].OptionsID)
assert.Equal(t, schema.OptionsID(h(`{"option":"cell 4"}`)), data.TraceValues[3].OptionsID)
}
func TestIngestedFrom_CalledWithoutHistory_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
tb := b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
assert.Panics(t, func() {
tb.IngestedFrom([]string{"file1"}, []string{"2020-12-05T16:00:00Z"})
})
}
func TestIngestedFrom_CalledMultipleTimes_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
tb := b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
tb.History("A")
tb.IngestedFrom([]string{"file1"}, []string{"2020-12-05T16:00:00Z"})
assert.Panics(t, func() {
tb.IngestedFrom([]string{"file1"}, []string{"2020-12-05T16:00:00Z"})
})
}
func TestIngestedFrom_IncorrectLength_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
tb := b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
tb.History("A")
assert.Panics(t, func() {
tb.IngestedFrom([]string{"file1"}, []string{""})
})
tb = b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
tb.History("A")
assert.Panics(t, func() {
tb.IngestedFrom([]string{""}, []string{"2020-12-05T16:00:00Z"})
})
tb = b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
tb.History("A")
assert.Panics(t, func() {
tb.IngestedFrom([]string{"file1"}, []string{})
})
tb = b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
tb.History("A")
assert.Panics(t, func() {
tb.IngestedFrom([]string{}, []string{"2020-12-05T16:00:00Z"})
})
}
func TestIngestedFrom_InvalidDateFormat_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
tb := b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
tb.History("A")
assert.Panics(t, func() {
tb.IngestedFrom([]string{"file1"}, []string{"not valid date"})
})
}
func TestBuild_IncompleteData_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
tb := b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"})
tb.History("A")
assert.Panics(t, func() {
b.Build()
})
tb.Keys([]paramtools.Params{{types.CorpusField: "a corpus"}})
assert.Panics(t, func() {
b.Build()
})
tb.OptionsAll(paramtools.Params{"opts": "something"})
assert.Panics(t, func() {
b.Build()
})
tb.IngestedFrom([]string{"file1"}, []string{"2020-12-05T16:00:00Z"})
assert.NotPanics(t, func() {
// should be good now
b.Build()
})
}
func TestBuild_IdenticalTracesFromTwoSets_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"}).
History("A").
Keys([]paramtools.Params{{types.CorpusField: "identical"}}).
OptionsAll(paramtools.Params{"opts": "something"}).
IngestedFrom([]string{"file1"}, []string{"2020-12-05T16:00:00Z"})
b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"}).
History("A").
Keys([]paramtools.Params{{types.CorpusField: "identical"}}).
OptionsAll(paramtools.Params{"opts": "does not impact trace identity"}).
IngestedFrom([]string{"file1"}, []string{"2020-12-05T16:00:00Z"})
assert.Panics(t, func() {
b.Build()
})
}
func TestAddTriageEvent_NoGroupingKeys_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
assert.Panics(t, func() {
b.AddTriageEvent("user", "2020-12-05T16:00:00Z")
})
}
func TestAddTriageEvent_InvalidTime_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
assert.Panics(t, func() {
b.AddTriageEvent("user", "invalid time")
})
}
func TestTriage_ReplacingPreviousExpectations_LabelAndRecordOverwritten(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys("test")
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"}).
History("A").Keys([]paramtools.Params{{"test": "one"}}).
OptionsAll(paramtools.Params{"opt": "opt"}).
IngestedFrom([]string{"first"}, []string{"2020-12-05T16:30:00Z"})
b.AddTriageEvent("mistake_user", "2020-12-05T16:00:00Z").
ExpectationsForGrouping(paramtools.Params{"test": "one"}).
Positive(digestA)
b.AddTriageEvent("mistake_user", "2020-12-05T16:00:05Z").
ExpectationsForGrouping(paramtools.Params{"test": "one"}).
Triage(digestA, schema.LabelPositive, schema.LabelNegative)
data := b.Build()
require.Len(t, data.ExpectationRecords, 2)
firstID := data.ExpectationRecords[0].ExpectationRecordID
secondID := data.ExpectationRecords[1].ExpectationRecordID
assert.Equal(t, []schema.ExpectationRow{{
GroupingID: h(`{"test":"one"}`),
Digest: d(t, digestA),
Label: schema.LabelNegative,
ExpectationRecordID: &secondID,
}}, data.Expectations)
assert.Equal(t, []schema.ExpectationDeltaRow{{
ExpectationRecordID: firstID,
GroupingID: h(`{"test":"one"}`),
Digest: d(t, digestA),
LabelBefore: schema.LabelUntriaged,
LabelAfter: schema.LabelPositive,
}, {
ExpectationRecordID: secondID,
GroupingID: h(`{"test":"one"}`),
Digest: d(t, digestA),
LabelBefore: schema.LabelPositive,
LabelAfter: schema.LabelNegative,
}}, data.ExpectationDeltas)
}
func TestExpectationsForGrouping_KeyMissingFromGrouping_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
eb := b.AddTriageEvent("user", "2020-12-05T16:00:00Z")
assert.Panics(t, func() {
eb.ExpectationsForGrouping(paramtools.Params{"oops": "missing"})
})
}
func TestExpectationsBuilderPositive_InvalidDigest_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
eb := b.AddTriageEvent("user", "2020-12-05T16:00:00Z").
ExpectationsForGrouping(paramtools.Params{types.CorpusField: "whatever"})
assert.Panics(t, func() {
eb.Positive("invalid")
})
}
func TestExpectationsBuilderNegative_InvalidDigest_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
eb := b.AddTriageEvent("user", "2020-12-05T16:00:00Z").
ExpectationsForGrouping(paramtools.Params{types.CorpusField: "whatever"})
assert.Panics(t, func() {
eb.Negative("invalid")
})
}
func TestComputeDiffMetricsFromImages_IncompleteData_Panics(t *testing.T) {
unittest.SmallTest(t)
testDir := testutils.TestDataDir(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
assert.Panics(t, func() {
b.ComputeDiffMetricsFromImages(testDir, "2020-12-05T16:00:00Z")
})
b.SetDigests(map[rune]types.Digest{'A': digestA})
assert.Panics(t, func() {
b.ComputeDiffMetricsFromImages(testDir, "2020-12-05T16:00:00Z")
})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"}).
History("A")
// We should have the right data now.
assert.NotPanics(t, func() {
b.ComputeDiffMetricsFromImages(testDir, "2020-12-05T16:00:00Z")
})
}
func TestComputeDiffMetricsFromImages_InvalidTime_Panics(t *testing.T) {
unittest.SmallTest(t)
testDir := testutils.TestDataDir(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"}).
History("A")
assert.Panics(t, func() {
b.ComputeDiffMetricsFromImages(testDir, "not a valid time")
})
}
func TestComputeDiffMetricsFromImages_InvalidDirectory_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys(types.CorpusField)
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"}).
History("A")
assert.Panics(t, func() {
b.ComputeDiffMetricsFromImages("Not a valid directory", "2020-12-05T16:00:00Z")
})
}
func TestAddIgnoreRule_InvalidDate_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
assert.Panics(t, func() {
b.AddIgnoreRule("fine", "fine", "Invalid date", "whatever",
paramtools.ParamSet{"what": []string{"ever"}})
})
}
func TestAddIgnoreRule_InvalidQuery_Panics(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
assert.Panics(t, func() {
b.AddIgnoreRule("fine", "fine", "2020-12-05T16:00:00Z", "whatever", nil)
})
assert.Panics(t, func() {
b.AddIgnoreRule("fine", "fine", "2020-12-05T16:00:00Z", "whatever", paramtools.ParamSet{})
})
}
func TestPatchsetBuilder_DataWithCommonKeysChained_Success(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys("test")
b.SetDigests(map[rune]types.Digest{'A': digestA})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"}).
History("A").Keys([]paramtools.Params{{"test": "one"}}).
OptionsAll(paramtools.Params{"opt": "opt"}).
IngestedFrom([]string{"first"}, []string{"2020-12-05T16:30:00Z"})
cl := b.AddChangelist("cl1", "gerrit", "user1", "whatever", schema.StatusOpen)
ps := cl.AddPatchset("ps1", "ffff111111111111111111111111111111111111", 1)
ps.DataWithCommonKeys(paramtools.Params{"os": "Android"}).
Digests(digestB).Keys([]paramtools.Params{{"test": "one"}}).
OptionsAll(paramtools.Params{"opt": "opt"}).
FromTryjob("tryjob1", "bb", "TRYJOB1", "tryjob1.txt", "2020-12-05T16:30:00Z")
ps.DataWithCommonKeys(paramtools.Params{"os": "Mac"}).
Digests(digestC).Keys([]paramtools.Params{{"test": "one"}}).
OptionsAll(paramtools.Params{"opt": "opt"}).
FromTryjob("tryjob2", "bb", "TRYJOB2", "tryjob2.txt", "2020-12-05T16:30:00Z")
data := b.Build()
assert.Equal(t, []schema.TraceRow{{
TraceID: h(`{"os":"Android","test":"one"}`),
Corpus: "",
GroupingID: h(`{"test":"one"}`),
Keys: paramtools.Params{"os": "Android", "test": "one"},
MatchesAnyIgnoreRule: schema.NBNull,
}, {
TraceID: h(`{"os":"Mac","test":"one"}`),
Corpus: "",
GroupingID: h(`{"test":"one"}`),
Keys: paramtools.Params{"os": "Mac", "test": "one"},
MatchesAnyIgnoreRule: schema.NBNull,
}}, data.Traces)
assert.Equal(t, []schema.SecondaryBranchValueRow{{
BranchName: "gerrit_cl1",
VersionName: "gerrit_ps1",
TraceID: h(`{"os":"Android","test":"one"}`),
Digest: d(t, digestB),
GroupingID: h(`{"test":"one"}`),
OptionsID: h(`{"opt":"opt"}`),
SourceFileID: h("tryjob1.txt"),
TryjobID: "bb_tryjob1",
}, {
BranchName: "gerrit_cl1",
VersionName: "gerrit_ps1",
TraceID: h(`{"os":"Mac","test":"one"}`),
Digest: d(t, digestC),
GroupingID: h(`{"test":"one"}`),
OptionsID: h(`{"opt":"opt"}`),
SourceFileID: h("tryjob2.txt"),
TryjobID: "bb_tryjob2",
}}, data.SecondaryBranchValues)
}
func TestPatchsetBuilder_TriageSameDigest_FinalLabelCorrect(t *testing.T) {
unittest.SmallTest(t)
b := TablesBuilder{}
b.SetGroupingKeys("test")
b.SetDigests(map[rune]types.Digest{'B': digestB})
b.CommitsWithData().Append("author_one", "subject_one", "2020-12-05T16:00:00Z")
b.AddTracesWithCommonKeys(paramtools.Params{"os": "Android"}).
History("B").Keys([]paramtools.Params{{"test": "one"}}).
OptionsAll(paramtools.Params{"opt": "opt"}).
IngestedFrom([]string{"first"}, []string{"2020-12-05T16:30:00Z"})
cl := b.AddChangelist("cl1", "gerrit", "user1", "whatever", schema.StatusOpen)
ps := cl.AddPatchset("ps1", "ffff111111111111111111111111111111111111", 1)
ps.DataWithCommonKeys(paramtools.Params{"os": "Android"}).
Digests(digestB).Keys([]paramtools.Params{{"test": "one"}}).
OptionsAll(paramtools.Params{"opt": "opt"}).
FromTryjob("tryjob1", "bb", "TRYJOB1", "tryjob1.txt", "2020-12-05T16:30:00Z")
cl.AddTriageEvent("user1", "2020-12-12T09:31:19Z").
ExpectationsForGrouping(paramtools.Params{"test": "one"}).
Negative(digestB)
cl.AddTriageEvent("user1", "2020-12-12T09:31:32Z").
ExpectationsForGrouping(paramtools.Params{"test": "one"}).
Triage(digestB, schema.LabelNegative, schema.LabelUntriaged)
data := b.Build()
assert.Len(t, data.ExpectationRecords, 2)
firstTriageRecord := data.ExpectationRecords[0].ExpectationRecordID
secondTriageRecord := data.ExpectationRecords[1].ExpectationRecordID
assert.Equal(t, []schema.SecondaryBranchExpectationRow{{
BranchName: "gerrit_cl1",
GroupingID: h(`{"test":"one"}`),
Digest: d(t, digestB),
Label: schema.LabelUntriaged, // second triage should be in effect
ExpectationRecordID: secondTriageRecord,
}}, data.SecondaryBranchExpectations)
assert.Equal(t, []schema.ExpectationDeltaRow{{
ExpectationRecordID: firstTriageRecord,
GroupingID: h(`{"test":"one"}`),
Digest: d(t, digestB),
LabelBefore: schema.LabelUntriaged,
LabelAfter: schema.LabelNegative,
}, {
ExpectationRecordID: secondTriageRecord,
GroupingID: h(`{"test":"one"}`),
Digest: d(t, digestB),
LabelBefore: schema.LabelNegative,
LabelAfter: schema.LabelUntriaged,
}}, data.ExpectationDeltas)
assert.Equal(t, []schema.ExpectationRow{{
GroupingID: h(`{"test":"one"}`),
Digest: d(t, digestB),
Label: schema.LabelUntriaged,
ExpectationRecordID: nil,
}}, data.Expectations)
}
// h returns the MD5 hash of the provided string.
func h(s string) []byte {
hash := md5.Sum([]byte(s))
return hash[:]
}
// d returns the bytes associated with the hex-encoded digest string.
func d(t *testing.T, digest types.Digest) []byte {
require.Len(t, digest, 2*md5.Size)
b, err := hex.DecodeString(string(digest))
require.NoError(t, err)
return b
}