blob: b4c48b975de7fada84193fd404a6c0bdd86bd919 [file] [log] [blame]
package deduplicator
import (
"fmt"
"testing"
"github.com/stretchr/testify/mock"
"go.skia.org/infra/fuzzer/go/data"
"go.skia.org/infra/fuzzer/go/tests"
"go.skia.org/infra/go/testutils"
)
func TestLocalSimpleDeduplication(t *testing.T) {
testutils.SmallTest(t)
d := NewLocalDeduplicator()
r1 := data.MockReport("skpicture", "aaaa")
r2 := data.MockReport("skpicture", "bbbb")
// mock report ffff and aaaa are the same, except for the name.
r3 := data.MockReport("skpicture", "ffff")
// mock report jjjj and aaaa are the same, except for the name and architecture.
r4 := data.MockReport("skpicture", "jjjj")
if !d.IsUnique(r1) {
t.Errorf("The deduplicator has not seen %#v, but said it has", r1)
}
if !d.IsUnique(r2) {
t.Errorf("The deduplicator has not seen %#v, but said it has", r2)
}
if d.IsUnique(r1) {
t.Errorf("Should not have said %#v was unique, it just saw it.", r1)
}
if d.IsUnique(r3) {
t.Errorf("Should not have said %#v was unique, it just saw something like it.", r3)
}
if !d.IsUnique(r4) {
t.Errorf("The deduplicator has not seen %#v, but said it has", r4)
}
}
func TestLocalUnknownStacktraces(t *testing.T) {
testutils.SmallTest(t)
d := NewLocalDeduplicator()
// mock report ee has no stacktrace for either. It should not be considered a duplicate, ever.
r1 := data.MockReport("skpicture", "eeee")
if !d.IsUnique(r1) {
t.Errorf("The deduplicator has not seen %#v, but said it has", r1)
}
if !d.IsUnique(r1) {
t.Errorf("Should not have said %#v was not unique, unknown stacktraces don't count.", r1)
}
}
func TestKey(t *testing.T) {
testutils.SmallTest(t)
// r1 is a report with 6 and 7 stacktrace frames for Debug/Release
r1 := makeReport()
r1.DebugStackTrace.Frames = append(r1.DebugStackTrace.Frames, data.StackTraceFrame{})
r1.ReleaseStackTrace.Frames = append(r1.DebugStackTrace.Frames, data.StackTraceFrame{})
k1 := key(r1)
k2 := key(r1)
if k1 != k2 {
t.Errorf("Keys should be deterministic\n%s != %s", k1, k2)
}
if n := len(r1.DebugStackTrace.Frames); n != _MAX_STACKTRACE_LINES+1 {
t.Errorf("key() should not have changed the report - it now has %d frames instead of 6", n)
t.Errorf(r1.DebugStackTrace.String())
}
if n := len(r1.ReleaseStackTrace.Frames); n != _MAX_STACKTRACE_LINES+2 {
t.Errorf("key() should not have changed the report - it now has %d frames instead of 7", n)
t.Errorf(r1.DebugStackTrace.String())
}
if frame := r1.DebugStackTrace.Frames[0]; frame.LineNumber == 0 {
t.Errorf("key() should not have changed the report - it now has the wrong line number at index 0: %s", r1.DebugStackTrace.String())
}
if frame := r1.ReleaseStackTrace.Frames[0]; frame.LineNumber == 0 {
t.Errorf("key() should not have changed the report - it now has the wrong line number at index 0: %s", r1.ReleaseStackTrace.String())
}
}
func TestLocalLinesOfStacktrace(t *testing.T) {
testutils.SmallTest(t)
d := NewLocalDeduplicator()
r1 := makeReport()
r2 := makeReport()
r2.DebugStackTrace.Frames = append(r2.DebugStackTrace.Frames, data.StackTraceFrame{})
r3 := makeReport()
r3.ReleaseStackTrace.Frames = append(r3.ReleaseStackTrace.Frames, data.StackTraceFrame{})
if !d.IsUnique(r1) {
t.Errorf("The deduplicator has not seen %#v, but said it has", r1)
}
if d.IsUnique(r2) {
t.Errorf("Should not have said %#v was unique, it just saw something with the same top %d stacktraces.", r2, _MAX_STACKTRACE_LINES)
t.Errorf("Debug stacktraces: \n%s\n\n%s", r1.DebugStackTrace.String(), r2.DebugStackTrace.String())
t.Errorf("Release stacktraces: \n%s\n\n%s", r1.ReleaseStackTrace.String(), r2.ReleaseStackTrace.String())
}
if d.IsUnique(r3) {
t.Errorf("Should not have said %#v was unique, it just saw something with the same top %d stacktraces.", r3, _MAX_STACKTRACE_LINES)
t.Errorf("Debug stacktraces: \n%s\n\n%s", r1.DebugStackTrace.String(), r3.DebugStackTrace.String())
t.Errorf("Release stacktraces: \n%s\n\n%s", r1.ReleaseStackTrace.String(), r3.ReleaseStackTrace.String())
}
}
func TestLocalLineNumbers(t *testing.T) {
testutils.SmallTest(t)
d := NewLocalDeduplicator()
r1 := makeReport()
r2 := makeReport()
r2.DebugStackTrace.Frames[0].LineNumber = 9999
r3 := makeReport()
r3.ReleaseStackTrace.Frames[0].LineNumber = 9999
if !d.IsUnique(r1) {
t.Errorf("The deduplicator has not seen %#v, but said it has", r1)
}
if d.IsUnique(r2) {
t.Errorf("Should not have said %#v was unique, it just saw something with the same top %d stacktraces, not counting line numbers.", r2, _MAX_STACKTRACE_LINES)
t.Errorf("Debug stacktraces: \n%s\n\n%s", r1.DebugStackTrace.String(), r2.DebugStackTrace.String())
t.Errorf("Release stacktraces: \n%s\n\n%s", r1.ReleaseStackTrace.String(), r2.ReleaseStackTrace.String())
}
if d.IsUnique(r3) {
t.Errorf("Should not have said %#v was unique, it just saw something with the same top %d stacktraces, not counting line numbers.", r3, _MAX_STACKTRACE_LINES)
t.Errorf("Debug stacktraces: \n%s\n\n%s", r1.DebugStackTrace.String(), r3.DebugStackTrace.String())
t.Errorf("Release stacktraces: \n%s\n\n%s", r1.ReleaseStackTrace.String(), r3.ReleaseStackTrace.String())
}
}
func TestLocalFlags(t *testing.T) {
testutils.SmallTest(t)
d := NewLocalDeduplicator()
r1 := makeReport()
r2 := makeReport()
r2.ReleaseFlags = makeFlags(4, 2)
r3 := makeReport()
r3.DebugFlags = makeFlags(4, 2)
if !d.IsUnique(r1) {
t.Errorf("The deduplicator has not seen %#v, but said it has", r1)
}
if !d.IsUnique(r2) {
t.Errorf("The deduplicator has not seen %#v, but said it has", r2)
t.Errorf("Release flags: \n%s\n\n%s", r1.ReleaseFlags, r2.ReleaseFlags)
}
if !d.IsUnique(r3) {
t.Errorf("The deduplicator has not seen %#v, but said it has", r3)
t.Errorf("Release flags: \n%s\n\n%s", r1.ReleaseFlags, r3.ReleaseFlags)
}
}
func TestLocalCategory(t *testing.T) {
testutils.SmallTest(t)
d := NewLocalDeduplicator()
r1 := makeReport()
r2 := makeReport()
r2.FuzzCategory = "something else"
if !d.IsUnique(r1) {
t.Errorf("The deduplicator has not seen %#v, but said it has", r1)
}
if !d.IsUnique(r2) {
t.Errorf("The deduplicator has not seen %#v, but said it has", r2)
}
}
func TestLocalArchitecture(t *testing.T) {
testutils.SmallTest(t)
d := NewLocalDeduplicator()
r1 := makeReport()
r2 := makeReport()
r2.FuzzArchitecture = "something else"
if !d.IsUnique(r1) {
t.Errorf("The deduplicator has not seen %#v, but said it has", r1)
}
if !d.IsUnique(r2) {
t.Errorf("The deduplicator has not seen %#v, but said it has", r2)
}
}
func TestLocalOther(t *testing.T) {
testutils.SmallTest(t)
d := NewLocalDeduplicator()
r1 := makeReport()
r1.DebugFlags = append(r1.DebugFlags, "Other")
r2 := makeReport()
r2.ReleaseFlags = append(r2.ReleaseFlags, "Other")
if !d.IsUnique(r1) {
t.Errorf("The deduplicator has not seen %#v, but said it has", r1)
}
if !d.IsUnique(r1) {
t.Errorf("The deduplicator should have said %#v was unique. The flag 'Other' should not be filtered.", r1)
}
if !d.IsUnique(r2) {
t.Errorf("The deduplicator has not seen %#v, but said it has", r2)
}
if !d.IsUnique(r2) {
t.Errorf("The deduplicator should have said %#v was unique. The flag 'Other' should not be filtered.", r2)
}
}
var ctx = mock.AnythingOfType("*context.emptyCtx")
func TestRemoteLookup(t *testing.T) {
testutils.SmallTest(t)
m := tests.NewMockGCSClient()
defer m.AssertExpectations(t)
d := NewRemoteDeduplicator(m)
d.SetRevision("COMMIT_HASH")
r1 := data.MockReport("skpicture", "aaaa")
r2 := data.MockReport("skpicture", "bbbb")
// hash for r1
m.On("GetFileContents", ctx, "skpicture/COMMIT_HASH/mock_arm8/traces/5a190a404735d7fd70340da671c9c8008dc597ba").Return([]byte("5a190a404735d7fd70340da671c9c8008dc597ba"), nil)
if d.IsUnique(r1) {
t.Errorf("The deduplicator should have found %#v remotely, but said it didn't", r1)
}
m.On("GetFileContents", ctx, "skpicture/COMMIT_HASH/mock_arm8/traces/0f743583793a3156803021669b4521b747c5b3c4").Return([]byte(nil), fmt.Errorf("Not found"))
m.On("SetFileContents", ctx, "skpicture/COMMIT_HASH/mock_arm8/traces/0f743583793a3156803021669b4521b747c5b3c4", FILE_WRITE_OPTS, []byte("0f743583793a3156803021669b4521b747c5b3c4")).Return(nil)
if !d.IsUnique(r2) {
t.Errorf("The deduplicator has not seen %#v, but said it has", r2)
}
m.AssertNumberOfCalls(t, "GetFileContents", 2)
m.AssertNumberOfCalls(t, "SetFileContents", 1)
}
func TestRemoteLookupWithLocalCache(t *testing.T) {
testutils.SmallTest(t)
m := tests.NewMockGCSClient()
defer m.AssertExpectations(t)
d := NewRemoteDeduplicator(m)
d.SetRevision("COMMIT_HASH")
r1 := data.MockReport("skpicture", "aaaa")
m.On("GetFileContents", ctx, "skpicture/COMMIT_HASH/mock_arm8/traces/5a190a404735d7fd70340da671c9c8008dc597ba").Return([]byte("5a190a404735d7fd70340da671c9c8008dc597ba"), nil)
if d.IsUnique(r1) {
t.Errorf("The deduplicator has seen %#v, but said it has not", r1)
}
if d.IsUnique(r1) {
t.Errorf("The deduplicator has seen %#v, but said it has not", r1)
}
if d.IsUnique(r1) {
t.Errorf("The deduplicator has seen %#v, but said it has not", r1)
}
// The Remote lookup should keep a local copy too.
m.AssertNumberOfCalls(t, "GetFileContents", 1)
}
func TestRemoteLookupReset(t *testing.T) {
testutils.SmallTest(t)
m := tests.NewMockGCSClient()
defer m.AssertExpectations(t)
d := NewRemoteDeduplicator(m)
d.SetRevision("COMMIT_HASH")
r1 := data.MockReport("skpicture", "aaaa")
// AnythingofType("[]byte") doesn't work because https://github.com/stretchr/testify/issues/387
m.On("SetFileContents", ctx, mock.AnythingOfType("string"), FILE_WRITE_OPTS, mock.AnythingOfType("[]uint8")).Return(nil)
m.On("GetFileContents", ctx, "skpicture/COMMIT_HASH/mock_arm8/traces/5a190a404735d7fd70340da671c9c8008dc597ba").Return([]byte(nil), fmt.Errorf("Not found"))
if !d.IsUnique(r1) {
t.Errorf("The deduplicator has not seen %#v, but said it has", r1)
}
m.On("GetFileContents", ctx, "skpicture/THE_SECOND_COMMIT_HASH/mock_arm8/traces/5a190a404735d7fd70340da671c9c8008dc597ba").Return([]byte(nil), fmt.Errorf("Not found")).Once()
d.SetRevision("THE_SECOND_COMMIT_HASH")
if !d.IsUnique(r1) {
t.Errorf("The deduplicator has not seen %#v, but said it has", r1)
}
// The Remote lookup should have to relookup the file after commit changed.
m.AssertNumberOfCalls(t, "GetFileContents", 2)
m.AssertNumberOfCalls(t, "SetFileContents", 2)
}
// Makes a report with the smallest stacktraces distinguishable by the deduplicator, 3 debug
// flags, 3 release flags and a standard name and category
func makeReport() data.FuzzReport {
ds := makeStacktrace(0)
rs := makeStacktrace(3)
df := makeFlags(0, 3)
rf := makeFlags(1, 2)
return data.FuzzReport{
DebugStackTrace: ds,
ReleaseStackTrace: rs,
DebugFlags: df,
ReleaseFlags: rf,
FuzzName: "doesn't matter",
FuzzCategory: "api",
FuzzArchitecture: "mock_x64",
}
}
var names = []string{"alpha", "beta", "gamma", "delta", "epsilon", "zeta", "theta", "iota", "kappa", "lambda", "mu"}
func makeStacktrace(start int) data.StackTrace {
st := data.StackTrace{}
r := start
n := len(names)
for i := 0; i < _MAX_STACKTRACE_LINES; i++ {
a, b, c, d := r%n, (r+1)%n, (r+2)%n, (r+3)%n
st.Frames = append(st.Frames, data.FullStackFrame(names[a], names[b], names[c], d))
r = (r + 4) % n
}
return st
}
func makeFlags(start, count int) []string {
return names[start:(start + count)]
}