| package indexer |
| |
| import ( |
| "context" |
| "flag" |
| "math/rand" |
| "sync" |
| "testing" |
| "time" |
| |
| assert "github.com/stretchr/testify/require" |
| "go.skia.org/infra/go/ds" |
| ds_testutil "go.skia.org/infra/go/ds/testutil" |
| "go.skia.org/infra/go/eventbus" |
| "go.skia.org/infra/go/gcs" |
| "go.skia.org/infra/go/git/gitinfo" |
| "go.skia.org/infra/go/testutils" |
| "go.skia.org/infra/go/tiling" |
| tracedb "go.skia.org/infra/go/trace/db" |
| "go.skia.org/infra/golden/go/expstorage" |
| "go.skia.org/infra/golden/go/ignore" |
| "go.skia.org/infra/golden/go/mocks" |
| "go.skia.org/infra/golden/go/storage" |
| "go.skia.org/infra/golden/go/types" |
| ) |
| |
| const ( |
| // Directory with testdata. |
| TEST_DATA_DIR = "./testdata" |
| |
| // Local file location of the test data. |
| TEST_DATA_PATH = TEST_DATA_DIR + "/goldentile.json.zip" |
| |
| // Folder in the testdata bucket. See go/testutils for details. |
| TEST_DATA_STORAGE_PATH = "gold-testdata/goldentile.json.gz" |
| |
| // REPO_URL is the url of the repo to check out. |
| REPO_URL = "https://skia.googlesource.com/skia" |
| |
| // REPO_DIR contains the location of where to check out Skia for benchmarks. |
| REPO_DIR = "./skia" |
| |
| // N_COMMITS is the number of commits used in benchmarks. |
| N_COMMITS = 50 |
| |
| // Database user used by benchmarks. |
| DB_USER = "readwrite" |
| |
| // TEST_HASHES_PATH is the GCS path where the file will be written. |
| TEST_HASHES_PATH = "skia-infra-testdata/hash_files/testing-known-hashes.txt" |
| ) |
| |
| // Flags used by benchmarks. Everything else uses reasonable assumptions based |
| // on a local setup of tracedb and skia_ingestion. |
| var ( |
| traceService = flag.String("trace_service", "localhost:9001", "The address of the traceservice endpoint.") |
| dbName = flag.String("db_name", "gold_skiacorrectness", "The name of the databased to use. User 'readwrite' and local test settings are assumed.") |
| ) |
| |
| func TestIndexer(t *testing.T) { |
| testutils.LargeTest(t) |
| |
| err := gcs.DownloadTestDataFile(t, gcs.TEST_DATA_BUCKET, TEST_DATA_STORAGE_PATH, TEST_DATA_PATH) |
| assert.NoError(t, err, "Unable to download testdata.") |
| defer testutils.RemoveAll(t, TEST_DATA_DIR) |
| |
| tileBuilder := mocks.NewMockTileBuilderFromJson(t, TEST_DATA_PATH) |
| eventBus := eventbus.New() |
| expStore := expstorage.NewMemExpectationsStore(eventBus) |
| |
| opts := &storage.GSClientOptions{ |
| HashesGSPath: TEST_HASHES_PATH, |
| } |
| gsClient, err := storage.NewGStorageClient(mocks.GetHTTPClient(t), opts) |
| assert.NoError(t, err) |
| assert.NotNil(t, gsClient) |
| |
| storages := &storage.Storage{ |
| ExpectationsStore: expStore, |
| MasterTileBuilder: tileBuilder, |
| DigestStore: &mocks.MockDigestStore{ |
| FirstSeen: time.Now().Unix(), |
| OkValue: true, |
| }, |
| DiffStore: mocks.NewMockDiffStore(), |
| EventBus: eventBus, |
| GStorageClient: gsClient, |
| } |
| |
| assert.NoError(t, storages.InitBaseliner()) |
| |
| ixr, err := New(storages, time.Minute) |
| assert.NoError(t, err) |
| |
| idxOne := ixr.GetIndex() |
| |
| // Set up a waitgroup so we can block until the index is updated. |
| var wg sync.WaitGroup |
| eventBus.SubscribeAsync(EV_INDEX_UPDATED, func(ignore interface{}) { |
| wg.Done() |
| }) |
| wg.Add(1) |
| |
| // Change the classifications and wait for the indexing to propagate. |
| changes := getChanges(t, idxOne.tilePair.Tile) |
| assert.NoError(t, storages.ExpectationsStore.AddChange(changes, "")) |
| wg.Wait() |
| |
| // Make sure the new index is different from the previous one. |
| idxTwo := ixr.GetIndex() |
| assert.NotEqual(t, idxOne, idxTwo) |
| } |
| |
| func getChanges(t *testing.T, tile *tiling.Tile) types.TestExp { |
| ret := types.TestExp{} |
| labelVals := []types.Label{types.POSITIVE, types.NEGATIVE} |
| for _, trace := range tile.Traces { |
| if rand.Float32() > 0.5 { |
| gTrace := trace.(*types.GoldenTrace) |
| for _, digest := range gTrace.Values { |
| if digest != types.MISSING_DIGEST { |
| testName := gTrace.Params_[types.PRIMARY_KEY_FIELD] |
| if found, ok := ret[testName]; ok { |
| found[digest] = labelVals[rand.Int()%2] |
| } else { |
| ret[testName] = types.TestClassification{digest: labelVals[rand.Int()%2]} |
| } |
| } |
| } |
| } |
| } |
| |
| assert.True(t, len(ret) > 0) |
| return ret |
| } |
| |
| func BenchmarkIndexer(b *testing.B) { |
| ctx := context.Background() |
| storages, expStore := setupStorages(b, ctx) |
| defer testutils.RemoveAll(b, REPO_DIR) |
| |
| // Build the initial index. |
| b.ResetTimer() |
| _, err := New(storages, time.Minute*15) |
| assert.NoError(b, err) |
| |
| // Update the expectations. |
| changes, err := expStore.Get() |
| assert.NoError(b, err) |
| |
| changesTestExp := changes.TestExp() |
| |
| // Wait for the indexTests to complete when we change the expectations. |
| var wg sync.WaitGroup |
| wg.Add(1) |
| storages.EventBus.SubscribeAsync(EV_INDEX_UPDATED, func(state interface{}) { |
| wg.Done() |
| }) |
| assert.NoError(b, storages.ExpectationsStore.AddChange(changesTestExp, "")) |
| wg.Wait() |
| } |
| |
| func setupStorages(t testutils.TestingT, ctx context.Context) (*storage.Storage, expstorage.ExpectationsStore) { |
| flag.Parse() |
| |
| // Set up the diff store, the event bus and the DB connection. |
| diffStore := mocks.NewMockDiffStore() |
| evt := eventbus.New() |
| |
| // Set up the cloud datasstore and initialize the expectations store. |
| cleanup := ds_testutil.InitDatastore(t, ds.KindsToBackup[ds.GOLD_SKIA_PROD_NS]...) |
| defer cleanup() |
| |
| cloudExpStore, _, err := expstorage.NewCloudExpectationsStore(ds.DS, evt) |
| assert.NoError(t, err) |
| |
| expStore := expstorage.NewCachingExpectationStore(cloudExpStore, evt) |
| |
| git, err := gitinfo.CloneOrUpdate(context.Background(), REPO_URL, REPO_DIR, false) |
| assert.NoError(t, err) |
| |
| traceDB, err := tracedb.NewTraceServiceDBFromAddress(*traceService, types.GoldenTraceBuilder) |
| assert.NoError(t, err) |
| |
| masterTileBuilder, err := tracedb.NewMasterTileBuilder(ctx, traceDB, git, N_COMMITS, evt, "") |
| assert.NoError(t, err) |
| |
| ret := &storage.Storage{ |
| DiffStore: diffStore, |
| ExpectationsStore: expstorage.NewMemExpectationsStore(evt), |
| MasterTileBuilder: masterTileBuilder, |
| DigestStore: &mocks.MockDigestStore{IssueIDs: []int{}, OkValue: true}, |
| NCommits: N_COMMITS, |
| EventBus: evt, |
| } |
| |
| ret.IgnoreStore, err = ignore.NewCloudIgnoreStore(ds.DS, expStore, ret.GetTileStreamNow(time.Minute)) |
| assert.NoError(t, err) |
| |
| tilePair, err := ret.GetLastTileTrimmed() |
| assert.NoError(t, err) |
| |
| assert.True(t, len(tilePair.IgnoreRules) > 0) |
| return ret, expStore |
| } |