blob: 86c69417cc0d990ec6604cfc3abe8237de62ae47 [file] [log] [blame]
package issuestore
import (
"fmt"
"math/rand"
"sort"
"strings"
"testing"
assert "github.com/stretchr/testify/require"
"go.skia.org/infra/go/testutils"
"go.skia.org/infra/go/util"
)
const (
TEST_DATA_DIR = "./testdata"
// Prefixes for different generated item types.
ISSUE_PREFIX = "ISSUES_"
DIGEST_PREFIX = "DIGEST_"
IGNORES_PREFIX = "IGNORES_"
TRACE_PREFIX = "TRACE_"
TEST_PREFIX = "TEST_"
)
func TestIssueStore(t *testing.T) {
testutils.MediumTest(t)
const N_ISSUES = 20
// Add a number of issues
issueStore, err := New(TEST_DATA_DIR)
assert.NoError(t, err)
defer testutils.RemoveAll(t, TEST_DATA_DIR)
lookup := map[string][]string{}
initIssues := genIssues(t, lookup, N_ISSUES, N_ISSUES/4+1, N_ISSUES/2+1, N_ISSUES/3+1, N_ISSUES/3+1)
issueIDs := make([]string, 0, len(initIssues))
for _, issue := range initIssues {
assert.NoError(t, issueStore.Add(issue))
issueIDs = append(issueIDs, issue.IssueID)
}
for _, issue := range initIssues {
found, err := issueStore.Get([]string{issue.IssueID})
assert.NoError(t, err)
assert.Equal(t, 1, len(found))
assert.Equal(t, issue, found[0])
}
found, err := issueStore.Get(issueIDs)
assert.NoError(t, err)
assert.Equal(t, initIssues, found)
testAgainstLookup(t, issueStore, lookup)
// // Assert them I can read them by issue id, digest, traceid and testNames
updateIssues := genIssues(t, lookup, N_ISSUES/2+1, N_ISSUES/4+1, N_ISSUES/4+1, N_ISSUES/4+1, N_ISSUES/4+1)
updatedIssuesIDs := []string{}
for idx, issue := range updateIssues {
assert.NoError(t, issueStore.Add(issue))
initIssues[idx].Add(issue)
updatedIssuesIDs = append(updatedIssuesIDs, issue.IssueID)
}
testAgainstLookup(t, issueStore, lookup)
// Test the list function.
for i := 0; i < N_ISSUES; i += 2 {
foundList, total, err := issueStore.List(i, 2)
assert.NoError(t, err)
assert.Equal(t, 2, len(foundList))
assert.Equal(t, N_ISSUES, total)
compareEntries(t, initIssues[i:i+1], foundList[0:1])
compareEntries(t, initIssues[i+1:i+2], foundList[1:2])
}
foundList, total, err := issueStore.List(0, N_ISSUES+5)
assert.NoError(t, err)
assert.Equal(t, N_ISSUES, len(foundList))
assert.Equal(t, N_ISSUES, total)
compareEntries(t, initIssues, foundList)
// Remove the previously added entries.
for idx, issue := range updateIssues {
assert.NoError(t, issueStore.Subtract(issue))
removeLookup(lookup, issue)
initIssues[idx].Subtract(issue)
found, err := issueStore.Get([]string{issue.IssueID})
assert.NoError(t, err)
compareEntries(t, initIssues[idx:idx+1], found[0:1])
}
testAgainstLookup(t, issueStore, lookup)
foundList, total, err = issueStore.List(0, -1)
assert.NoError(t, err)
compareEntries(t, foundList, initIssues)
// Remove all entries to check at the bottom whether indices have been removed.
for _, issue := range initIssues {
removeLookup(lookup, issue)
}
// Subtract all annotations of a subset of issues.
for _, issue := range initIssues[:len(updateIssues)] {
assert.NoError(t, issueStore.Subtract(issue))
}
initIssues = initIssues[len(updateIssues):]
foundList, total, err = issueStore.List(0, -1)
assert.NoError(t, err)
compareEntries(t, foundList, initIssues)
// Delete all issues and make sure they are gone.
assert.NoError(t, issueStore.Delete(issueIDs))
foundList, total, err = issueStore.List(0, -1)
assert.NoError(t, err)
assert.Equal(t, []*Annotation{}, foundList)
assert.Equal(t, 0, total)
testAgainstLookup(t, issueStore, lookup)
}
func compareEntries(t assert.TestingT, exps []*Annotation, actual []*Annotation) {
assert.Equal(t, len(exps), len(actual))
for i, exp := range exps {
assert.Equal(t, exp.IssueID, actual[i].IssueID)
compareList(t, exp.Digests, actual[i].Digests)
compareList(t, exp.Traces, actual[i].Traces)
compareList(t, exp.Ignores, actual[i].Ignores)
compareList(t, exp.TestNames, actual[i].TestNames)
}
}
func compareList(t assert.TestingT, exp, actual []string) {
sort.Strings(exp)
sort.Strings(actual)
assert.Equal(t, exp, actual)
}
func testAgainstLookup(t assert.TestingT, issueStore IssueStore, lookup map[string][]string) {
for itemID, exp := range lookup {
var found []string
var err error
if strings.HasPrefix(itemID, DIGEST_PREFIX) {
found, err = issueStore.ByDigest(itemID)
} else if strings.HasPrefix(itemID, IGNORES_PREFIX) {
found, err = issueStore.ByIgnore(itemID)
} else if strings.HasPrefix(itemID, TRACE_PREFIX) {
found, err = issueStore.ByTrace(itemID)
} else if strings.HasPrefix(itemID, TEST_PREFIX) {
found, err = issueStore.ByTest(itemID)
} else {
t.FailNow()
}
assert.NoError(t, err)
assert.Equal(t, exp, found)
}
}
func genIssues(t *testing.T, lookup map[string][]string, nIssues int, nDigests int, nTraces int, nIgnores int, nTestNames int) []*Annotation {
// generate a list of issues and the given number of digests/traces and tests.
issues := fmtStrings(ISSUE_PREFIX+"%04d", nIssues)
digests := fmtStrings(DIGEST_PREFIX+"%04d", 5*nDigests)
ignores := fmtStrings(IGNORES_PREFIX+"%04d", 3*nIgnores)
traces := fmtStrings(TRACE_PREFIX+"%04d", 5*nTraces)
testNames := fmtStrings(TEST_PREFIX+"%04d", 5*nTestNames)
ret := make([]*Annotation, nIssues)
for idx, issueID := range issues {
r := &Annotation{
IssueID: issueID,
Digests: drawN(digests, nDigests, lookup, issueID),
Traces: drawN(traces, nTraces, lookup, issueID),
Ignores: drawN(ignores, nIgnores, lookup, issueID),
TestNames: drawN(testNames, nTestNames, lookup, issueID),
}
assert.Equal(t, []int{nDigests, nTraces, nIgnores, nTestNames}, []int{len(r.Digests), len(r.Traces), len(r.Ignores), len(r.TestNames)})
addLookup(lookup, r)
ret[idx] = r
}
return ret
}
func addLookup(lookup map[string][]string, rec *Annotation) {
addLookupItem(lookup, rec.Digests, rec.IssueID)
addLookupItem(lookup, rec.Traces, rec.IssueID)
addLookupItem(lookup, rec.Ignores, rec.IssueID)
addLookupItem(lookup, rec.TestNames, rec.IssueID)
}
func removeLookup(lookup map[string][]string, delta *Annotation) {
removeLookupItem(lookup, delta.Digests, delta.IssueID)
removeLookupItem(lookup, delta.Traces, delta.IssueID)
removeLookupItem(lookup, delta.Ignores, delta.IssueID)
removeLookupItem(lookup, delta.TestNames, delta.IssueID)
}
func addLookupItem(lookup map[string][]string, ids []string, parentID string) {
for _, id := range ids {
lookup[id] = util.NewStringSet(lookup[id], []string{parentID}).Keys()
sort.Strings(lookup[id])
}
}
func removeLookupItem(lookup map[string][]string, ids []string, parentID string) {
for _, id := range ids {
s := util.NewStringSet(lookup[id])
delete(s, parentID)
lookup[id] = s.Keys()
sort.Strings(lookup[id])
}
}
func fmtStrings(template string, n int) []string {
ret := make([]string, n)
for i := 0; i < n; i++ {
ret[i] = fmt.Sprintf(template, i)
}
return ret
}
func drawN(strs []string, n int, lookup map[string][]string, ignoreParent string) []string {
indices := rand.Perm(len(strs))
ret := make([]string, 0, n)
for i := 0; (i < len(indices)) && (len(ret) < n); i++ {
str := strs[indices[i]]
if ignoreParent == "" || !util.In(ignoreParent, lookup[str]) {
ret = append(ret, strs[indices[i]])
}
}
return ret
}