func Init() { rand.Seed(time.Now().UnixNano()) if *resourcesDir == "" { _, filename, _, _ := runtime.Caller(0) *resourcesDir = filepath.Join(filepath.Dir(filename), "../..") } loadTemplates() var err error git, err = gitinfo.CloneOrUpdate(*gitRepoURL, *gitRepoDir, false) if err != nil { glog.Fatal(err) } evt := eventbus.New(nil) tileStats = tilestats.New(evt) // Connect to traceDB and create the builders. db, err := tracedb.NewTraceServiceDBFromAddress(*traceservice, types.PerfTraceBuilder) if err != nil { glog.Fatalf("Failed to connect to tracedb: %s", err) } masterTileBuilder, err = tracedb.NewMasterTileBuilder(db, git, *tileSize, evt) if err != nil { glog.Fatalf("Failed to build trace/db.DB: %s", err) } branchTileBuilder = tracedb.NewBranchTileBuilder(db, git, rietveld.RIETVELD_SKIA_URL, evt) }
func main() { defer common.LogPanic() common.Init() args := flag.Args() if len(args) != 2 { flag.Usage() os.Exit(1) } bucket, prefix := args[0], args[1] v, err := skiaversion.GetVersion() if err != nil { glog.Fatal(err) } glog.Infof("Version %s, built at %s", v.Commit, v.Date) if *nsqdAddress == "" { glog.Fatal("Missing address of nsqd server.") } globalEventBus, err := geventbus.NewNSQEventBus(*nsqdAddress) if err != nil { glog.Fatalf("Unable to connect to NSQ server: %s", err) } eventBus := eventbus.New(globalEventBus) eventBus.SubscribeAsync(event.StorageEvent(bucket, prefix), func(evData interface{}) { data := evData.(*event.GoogleStorageEventData) glog.Infof("Google Storage notification from bucket\n %s: %s : %s", data.Updated, data.Bucket, data.Name) }) select {} }
func main() { common.Init() v, err := skiaversion.GetVersion() if err != nil { glog.Fatal(err) } glog.Infof("Version %s, built at %s", v.Commit, v.Date) if *nsqdAddress == "" { glog.Fatal("Missing address of nsqd server.") } globalEventBus, err := geventbus.NewNSQEventBus(*nsqdAddress) if err != nil { glog.Fatalf("Unable to connect to NSQ server: %s", err) } eventBus := eventbus.New(globalEventBus) eventBus.SubscribeAsync(event.GLOBAL_GOOGLE_STORAGE, func(evData interface{}) { data := evData.(*event.GoogleStorageEventData) glog.Infof("Google Storage notification from bucket\n %s: %s : %s", data.Updated, data.Bucket, data.Name) }) select {} }
func main() { common.Init() v, err := skiaversion.GetVersion() if err != nil { glog.Fatal(err) } glog.Infof("Version %s, built at %s", v.Commit, v.Date) if *nsqdAddress == "" { glog.Fatal("Missing address of nsqd server.") } globalEventBus, err := geventbus.NewNSQEventBus(*nsqdAddress) if err != nil { glog.Fatalf("Unable to connect to NSQ server: %s", err) } eventBus := eventbus.New(globalEventBus) // Send events every so often. for _ = range time.Tick(2 * time.Second) { evData := &event.GoogleStorageEventData{ Bucket: "test-bucket", Name: "test-name", Updated: time.Now().String(), } eventBus.Publish(event.GLOBAL_GOOGLE_STORAGE, evData) glog.Infof("Sent Event: %#v ", evData) } }
func main() { defer common.LogPanic() common.InitWithMetrics("grandcentral", graphiteServer) v, err := skiaversion.GetVersion() if err != nil { glog.Fatal(err) } glog.Infof("Version %s, built at %s", v.Commit, v.Date) if *nsqdAddress == "" { glog.Fatal("Missing address of nsqd server.") } globalEventBus, err := geventbus.NewNSQEventBus(*nsqdAddress) if err != nil { glog.Fatalf("Unable to connect to NSQ server: %s", err) } eventBus = eventbus.New(globalEventBus) if *testing { *useMetadata = false } serverURL := "https://" + *host if *testing { serverURL = "http://" + *host + *port } runServer(serverURL) }
func main() { defer common.LogPanic() common.InitWithMetrics("grandcentral", graphiteServer) v, err := skiaversion.GetVersion() if err != nil { glog.Fatal(err) } glog.Infof("Version %s, built at %s", v.Commit, v.Date) if *nsqdAddress == "" { glog.Fatal("Missing address of nsqd server.") } globalEventBus, err := geventbus.NewNSQEventBus(*nsqdAddress) if err != nil { glog.Fatalf("Unable to connect to NSQ server: %s", err) } eventBus = eventbus.New(globalEventBus) // Add a subscription for the each event type. This prevents the messages // to queue up if there are no other clients connected. eventBus.SubscribeAsync(event.GLOBAL_GOOGLE_STORAGE, func(evData interface{}) {}) eventBus.SubscribeAsync(event.GLOBAL_BUILDBOT, func(evData interface{}) {}) if *testing { *useMetadata = false } serverURL := "https://" + *host if *testing { serverURL = "http://" + *host + *port } runServer(serverURL) }
func BenchmarkHistory(b *testing.B) { assert.Nil(b, os.MkdirAll(TEST_DATA_DIR, 0755)) defer testutils.RemoveAll(b, TEST_DATA_DIR) digestStore, err := digeststore.New(TEST_DATA_DIR) assert.Nil(b, err) tileBuilder := mocks.GetTileBuilderFromEnv(b) storages := &storage.Storage{ MasterTileBuilder: tileBuilder, DigestStore: digestStore, EventBus: eventbus.New(nil), } tile := tileBuilder.GetTile() assert.Nil(b, Init(storages, 0)) // Gather the runtimes of the testname/digest lookup. runtimes := make([]int64, 0, 1000000) timeIt := func(testName, digest string) (bool, error) { startTime := time.Now().UnixNano() _, found, err := digestStore.Get(testName, digest) runtimes = append(runtimes, (time.Now().UnixNano()-startTime)/1000) return found, err } b.ResetTimer() tileLen := tile.LastCommitIndex() + 1 for _, trace := range tile.Traces { testName := trace.Params()[types.PRIMARY_KEY_FIELD] gTrace := trace.(*types.GoldenTrace) for _, digest := range gTrace.Values[:tileLen] { if digest != types.MISSING_DIGEST { found, err := timeIt(testName, digest) assert.Nil(b, err) assert.True(b, found) } } } var avg int64 = 0 for _, val := range runtimes { avg += val } glog.Infof("Average lookup time: %.3fus", float64(avg)/float64(len(runtimes))) glog.Infof("Number of lookups : %d", len(runtimes)) }
func TestMySQLExpectationsStore(t *testing.T) { // Set up the test database. testDb := testutil.SetupMySQLTestDatabase(t, db.MigrationSteps()) defer testDb.Close(t) conf := testutil.LocalTestDatabaseConfig(db.MigrationSteps()) vdb, err := conf.NewVersionedDB() assert.Nil(t, err) // Test the MySQL backed store sqlStore := NewSQLExpectationStore(vdb) testExpectationStore(t, sqlStore, nil) // Test the caching version of the MySQL store. eventBus := eventbus.New(nil) cachingStore := NewCachingExpectationStore(sqlStore, eventBus) testExpectationStore(t, cachingStore, eventBus) }
// GetTileBuilderFromEnv looks at the TEST_TRACEDB_ADDRESS environement variable for the // name of directory that contains tiles. If it's defined it will return a // TileStore instance. If the not the calling test will fail. func GetTileBuilderFromEnv(t assert.TestingT) tracedb.MasterTileBuilder { traceDBAddress := os.Getenv("TEST_TRACEDB_ADDRESS") assert.NotEqual(t, "", traceDBAddress, "Please define the TEST_TRACEDB_ADDRESS environment variable to point to the traceDB.") gitURL := os.Getenv("TEST_GIT_URL") assert.NotEqual(t, "", traceDBAddress, "Please define the TEST_TRACEDB_ADDRESS environment variable to point to the Git URL.") gitRepoDir, err := ioutil.TempDir("", "gitrepo") assert.Nil(t, err) git, err := gitinfo.CloneOrUpdate(gitURL, gitRepoDir, false) if err != nil { glog.Fatal(err) } eventBus := eventbus.New(nil) db, err := tracedb.NewTraceServiceDBFromAddress(traceDBAddress, types.GoldenTraceBuilder) assert.Nil(t, err) tileBuilder, err := tracedb.NewMasterTileBuilder(db, git, 50, eventBus) assert.Nil(t, err) return tileBuilder }
func TestNewTraceDBBuilder(t *testing.T) { defer cleanup() // First spin up a traceservice server that we wil talk to. server, err := traceservice.NewTraceServiceServer(FILENAME) if err != nil { t.Fatalf("Failed to initialize the tracestore server: %s", err) } // Start the server on an open port. lis, err := net.Listen("tcp", "localhost:0") if err != nil { t.Fatalf("failed to listen: %v", err) } port := lis.Addr().String() s := grpc.NewServer() traceservice.RegisterTraceServiceServer(s, server) go func() { t.Fatalf("Failed while serving: %s", s.Serve(lis)) }() // Get a Git repo. tr := util.NewTempRepo() defer tr.Cleanup() // Set up a connection to the server. conn, err := grpc.Dial(port, grpc.WithInsecure()) if err != nil { t.Fatalf("did not connect: %v", err) } ts, err := db.NewTraceServiceDB(conn, types.PerfTraceBuilder) if err != nil { t.Fatalf("Failed to create tracedb.DB: %s", err) } defer util.Close(ts) git, err := gitinfo.NewGitInfo(filepath.Join(tr.Dir, "testrepo"), false, false) if err != nil { t.Fatal(err) } hashes := git.LastN(50) assert.Equal(t, 50, len(hashes)) // Populate the tracedb with some data. commitID := &db.CommitID{ Timestamp: git.Timestamp(hashes[1]).Unix(), ID: hashes[1], Source: "master", } entries := map[string]*db.Entry{ "key:8888:android": &db.Entry{ Params: map[string]string{ "config": "8888", "platform": "android", "type": "skp", }, Value: types.BytesFromFloat64(0.01), }, "key:gpu:win8": &db.Entry{ Params: map[string]string{ "config": "gpu", "platform": "win8", "type": "skp", }, Value: types.BytesFromFloat64(1.234), }, } err = ts.Add(commitID, entries) if err != nil { t.Fatalf("Failed to add data to traceservice: %s", err) } evt := eventbus.New(nil) traceDB, err := db.NewTraceServiceDBFromAddress(port, types.PerfTraceBuilder) assert.Nil(t, err) builder, err := db.NewMasterTileBuilder(traceDB, git, 50, evt) if err != nil { t.Fatalf("Failed to construct TraceStore: %s", err) } tile := builder.GetTile() assert.Equal(t, 50, len(tile.Commits)) assert.Equal(t, 2, len(tile.Traces)) assert.Equal(t, commitID.ID, tile.Commits[1].Hash) assert.Equal(t, "Joe Gregorio ([email protected])", tile.Commits[1].Author) ptrace := tile.Traces["key:8888:android"].(*types.PerfTrace) assert.Equal(t, 0.01, ptrace.Values[1]) assert.Equal(t, config.MISSING_DATA_SENTINEL, ptrace.Values[0]) ptrace = tile.Traces["key:gpu:win8"].(*types.PerfTrace) assert.Equal(t, 1.234, ptrace.Values[1]) assert.Equal(t, config.MISSING_DATA_SENTINEL, ptrace.Values[0]) // Now add more data to the tracestore, trigger LoadTile() and check the results. commitID = &db.CommitID{ Timestamp: git.Timestamp(hashes[2]).Unix(), ID: hashes[2], Source: "master", } entries = map[string]*db.Entry{ "key:8888:android": &db.Entry{ Params: map[string]string{ "config": "8888", "platform": "android", "type": "skp", }, Value: types.BytesFromFloat64(0.02), }, "key:565:ubuntu": &db.Entry{ Params: map[string]string{ "config": "565", "platform": "ubuntu", "type": "skp", }, Value: types.BytesFromFloat64(2.345), }, } err = ts.Add(commitID, entries) if err != nil { t.Fatalf("Failed to add data to traceservice: %s", err) } // Access the LoadTile method without exposing it in the MasterBuilder interface. type hasLoadTile interface { LoadTile() error } masterBuilder := builder.(hasLoadTile) if err := masterBuilder.LoadTile(); err != nil { t.Fatalf("Failed to force load Tile: %s", err) } tile = builder.GetTile() assert.Equal(t, 50, len(tile.Commits)) assert.Equal(t, 3, len(tile.Traces)) assert.Equal(t, commitID.ID, tile.Commits[2].Hash) ptrace = tile.Traces["key:8888:android"].(*types.PerfTrace) assert.Equal(t, 0.01, ptrace.Values[1]) assert.Equal(t, 0.02, ptrace.Values[2]) assert.Equal(t, config.MISSING_DATA_SENTINEL, ptrace.Values[0]) assert.Equal(t, config.MISSING_DATA_SENTINEL, ptrace.Values[3]) ptrace = tile.Traces["key:gpu:win8"].(*types.PerfTrace) assert.Equal(t, 1.234, ptrace.Values[1]) assert.Equal(t, config.MISSING_DATA_SENTINEL, ptrace.Values[0]) ptrace = tile.Traces["key:565:ubuntu"].(*types.PerfTrace) assert.Equal(t, 2.345, ptrace.Values[2]) assert.Equal(t, config.MISSING_DATA_SENTINEL, ptrace.Values[3]) }
/** Conditions to test. Traces ------ id | config | test name | corpus(source_type) | digests a 8888 foo gm aaa+, bbb- b 565 foo gm ccc?, ddd? c gpu foo gm eee+ d 8888 bar gm fff-, ggg? e 8888 quux image jjj? Expectations ------------ foo aaa pos foo bbb neg foo ccc unt foo ddd unt foo eee pos bar fff neg Ignores ------- config=565 Note no entry for quux or ggg, meaning untriaged. Test the following conditions and make sure you get the expected test summaries. source_type=gm foo - pos(aaa, eee):2 neg(bbb):1 bar - neg(fff):1 unt(ggg):1 source_type=gm includeIgnores=true foo - pos(aaa, eee):2 neg(bbb):1 unt(ccc, ddd):2 bar - neg(fff):1 unt(ggg):1 source_type=gm includeIgnores=true testName=foo foo - pos(aaa, eee):2 neg(bbb):1 unt(ccc, ddd):2 testname = foo foo - pos(aaa, eee):2 neg(bbb):1 testname = quux quux - unt(jjj):1 config=565&config=8888 foo - pos(aaa):1 neg(bbb):1 bar - neg(fff):1 unt(ggg):1 quux - unt(jjj):1 config=565&config=8888 head=true foo - neg(bbb):1 bar - unt(ggg):1 quux - unt(jjj):1 config=gpu foo - pos(eee):1 config=unknown <empty> */ func TestCalcSummaries(t *testing.T) { tile := &tiling.Tile{ Traces: map[string]tiling.Trace{ "a": &types.GoldenTrace{ Values: []string{"aaa", "bbb"}, Params_: map[string]string{ "name": "foo", "config": "8888", "source_type": "gm"}, }, "b": &types.GoldenTrace{ Values: []string{"ccc", "ddd"}, Params_: map[string]string{ "name": "foo", "config": "565", "source_type": "gm"}, }, "c": &types.GoldenTrace{ Values: []string{"eee", types.MISSING_DIGEST}, Params_: map[string]string{ "name": "foo", "config": "gpu", "source_type": "gm"}, }, "d": &types.GoldenTrace{ Values: []string{"fff", "ggg"}, Params_: map[string]string{ "name": "bar", "config": "8888", "source_type": "gm"}, }, "e": &types.GoldenTrace{ Values: []string{"jjj", types.MISSING_DIGEST}, Params_: map[string]string{ "name": "quux", "config": "8888", "source_type": "image"}, }, }, Commits: []*tiling.Commit{ &tiling.Commit{ CommitTime: 42, Hash: "ffffffffffffffffffffffffffffffffffffffff", Author: "*****@*****.**", }, &tiling.Commit{ CommitTime: 45, Hash: "gggggggggggggggggggggggggggggggggggggggg", Author: "*****@*****.**", }, }, Scale: 0, TileIndex: 0, } eventBus := eventbus.New(nil) storages := &storage.Storage{ DiffStore: mocks.MockDiffStore{}, ExpectationsStore: expstorage.NewMemExpectationsStore(eventBus), IgnoreStore: ignore.NewMemIgnoreStore(), MasterTileBuilder: mocks.NewMockTileBuilderFromTile(t, tile), NCommits: 50, EventBus: eventBus, DigestStore: &mocks.MockDigestStore{FirstSeen: time.Now().Unix() + 1000, OkValue: true}, } assert.Nil(t, storages.ExpectationsStore.AddChange(map[string]types.TestClassification{ "foo": map[string]types.Label{ "aaa": types.POSITIVE, "bbb": types.NEGATIVE, "ccc": types.UNTRIAGED, "ddd": types.UNTRIAGED, "eee": types.POSITIVE, }, "bar": map[string]types.Label{ "fff": types.NEGATIVE, }, }, "*****@*****.**")) ta, _ := tally.New(storages) assert.Nil(t, storages.IgnoreStore.Create(&ignore.IgnoreRule{ ID: 1, Name: "foo", Expires: time.Now().Add(time.Hour), Query: "config=565", })) blamer, err := blame.New(storages) assert.Nil(t, err) summaries, err := New(storages, ta, blamer) assert.Nil(t, err) sum, err := summaries.CalcSummaries(nil, "source_type=gm", false, false) if err != nil { t.Fatalf("Failed to calc: %s", err) } assert.Equal(t, 2, len(sum)) triageCountsCorrect(t, sum, "foo", 2, 1, 0) triageCountsCorrect(t, sum, "bar", 0, 1, 1) assert.Equal(t, []string{}, sum["foo"].UntHashes) assert.Equal(t, []string{"ggg"}, sum["bar"].UntHashes) if sum, err = summaries.CalcSummaries(nil, "source_type=gm", true, false); err != nil { t.Fatalf("Failed to calc: %s", err) } assert.Equal(t, 2, len(sum)) triageCountsCorrect(t, sum, "foo", 2, 1, 2) triageCountsCorrect(t, sum, "bar", 0, 1, 1) assert.Equal(t, sum["foo"].UntHashes, []string{"ccc", "ddd"}) assert.Equal(t, sum["bar"].UntHashes, []string{"ggg"}) if sum, err = summaries.CalcSummaries([]string{"foo"}, "source_type=gm", true, false); err != nil { t.Fatalf("Failed to calc: %s", err) } assert.Equal(t, 1, len(sum)) triageCountsCorrect(t, sum, "foo", 2, 1, 2) assert.Equal(t, sum["foo"].UntHashes, []string{"ccc", "ddd"}) if sum, err = summaries.CalcSummaries([]string{"foo"}, "", false, false); err != nil { t.Fatalf("Failed to calc: %s", err) } assert.Equal(t, 1, len(sum)) triageCountsCorrect(t, sum, "foo", 2, 1, 0) assert.Equal(t, sum["foo"].UntHashes, []string{}) if sum, err = summaries.CalcSummaries(nil, "config=8888&config=565", false, false); err != nil { t.Fatalf("Failed to calc: %s", err) } assert.Equal(t, 3, len(sum)) triageCountsCorrect(t, sum, "foo", 1, 1, 0) triageCountsCorrect(t, sum, "bar", 0, 1, 1) triageCountsCorrect(t, sum, "quux", 0, 0, 1) assert.Equal(t, sum["foo"].UntHashes, []string{}) assert.Equal(t, sum["bar"].UntHashes, []string{"ggg"}) assert.Equal(t, sum["quux"].UntHashes, []string{"jjj"}) if sum, err = summaries.CalcSummaries(nil, "config=8888&config=565", false, true); err != nil { t.Fatalf("Failed to calc: %s", err) } assert.Equal(t, 3, len(sum)) triageCountsCorrect(t, sum, "foo", 0, 1, 0) triageCountsCorrect(t, sum, "bar", 0, 0, 1) triageCountsCorrect(t, sum, "quux", 0, 0, 1) assert.Equal(t, sum["foo"].UntHashes, []string{}) assert.Equal(t, sum["bar"].UntHashes, []string{"ggg"}) assert.Equal(t, sum["quux"].UntHashes, []string{"jjj"}) if sum, err = summaries.CalcSummaries(nil, "config=gpu", false, false); err != nil { t.Fatalf("Failed to calc: %s", err) } assert.Equal(t, 1, len(sum)) triageCountsCorrect(t, sum, "foo", 1, 0, 0) assert.Equal(t, sum["foo"].UntHashes, []string{}) if sum, err = summaries.CalcSummaries(nil, "config=unknown", false, false); err != nil { t.Fatalf("Failed to calc: %s", err) } assert.Equal(t, 0, len(sum)) }
func main() { defer common.LogPanic() var err error mainTimer := timer.New("main init") // Setup DB flags. dbConf := database.ConfigFromFlags(db.PROD_DB_HOST, db.PROD_DB_PORT, database.USER_RW, db.PROD_DB_NAME, db.MigrationSteps()) // Global init to initialize common.InitWithMetrics("skiacorrectness", graphiteServer) v, err := skiaversion.GetVersion() if err != nil { glog.Fatalf("Unable to retrieve version: %s", err) } glog.Infof("Version %s, built at %s", v.Commit, v.Date) // Enable the memory profiler if memProfile was set. // TODO(stephana): This should be moved to a HTTP endpoint that // only responds to internal IP addresses/ports. if *memProfile > 0 { time.AfterFunc(*memProfile, func() { glog.Infof("Writing Memory Profile") f, err := ioutil.TempFile("./", "memory-profile") if err != nil { glog.Fatalf("Unable to create memory profile file: %s", err) } if err := pprof.WriteHeapProfile(f); err != nil { glog.Fatalf("Unable to write memory profile file: %v", err) } util.Close(f) glog.Infof("Memory profile written to %s", f.Name()) os.Exit(0) }) } if *cpuProfile > 0 { glog.Infof("Writing CPU Profile") f, err := ioutil.TempFile("./", "cpu-profile") if err != nil { glog.Fatalf("Unable to create cpu profile file: %s", err) } if err := pprof.StartCPUProfile(f); err != nil { glog.Fatalf("Unable to write cpu profile file: %v", err) } time.AfterFunc(*cpuProfile, func() { pprof.StopCPUProfile() util.Close(f) glog.Infof("CPU profile written to %s", f.Name()) os.Exit(0) }) } // Init this module. Init() // Initialize submodules. filediffstore.Init() // Set up login // TODO (stephana): Factor out to go/login/login.go and removed hard coded // values. var cookieSalt = "notverysecret" var clientID = "31977622648-ubjke2f3staq6ouas64r31h8f8tcbiqp.apps.googleusercontent.com" var clientSecret = "rK-kRY71CXmcg0v9I9KIgWci" var useRedirectURL = fmt.Sprintf("http://localhost%s/oauth2callback/", *port) if !*local { cookieSalt = metadata.Must(metadata.ProjectGet(metadata.COOKIESALT)) clientID = metadata.Must(metadata.ProjectGet(metadata.CLIENT_ID)) clientSecret = metadata.Must(metadata.ProjectGet(metadata.CLIENT_SECRET)) useRedirectURL = *redirectURL } login.Init(clientID, clientSecret, useRedirectURL, cookieSalt, login.DEFAULT_SCOPE, *authWhiteList, *local) // get the Oauthclient if necessary. client := getOAuthClient(*oauthCacheFile) // Set up the cache implementation to use. cacheFactory := filediffstore.MemCacheFactory if *redisHost != "" { cacheFactory = func(uniqueId string, codec util.LRUCodec) util.LRUCache { return redisutil.NewRedisLRUCache(*redisHost, *redisDB, uniqueId, codec) } } // Get the expecations storage, the filediff storage and the tilestore. diffStore, err := filediffstore.NewFileDiffStore(client, *imageDir, *gsBucketName, filediffstore.DEFAULT_GS_IMG_DIR_NAME, cacheFactory, filediffstore.RECOMMENDED_WORKER_POOL_SIZE) if err != nil { glog.Fatalf("Allocating DiffStore failed: %s", err) } if !*local { if err := dbConf.GetPasswordFromMetadata(); err != nil { glog.Fatal(err) } } vdb, err := dbConf.NewVersionedDB() if err != nil { glog.Fatal(err) } if !vdb.IsLatestVersion() { glog.Fatal("Wrong DB version. Please updated to latest version.") } digestStore, err := digeststore.New(*storageDir) if err != nil { glog.Fatal(err) } eventBus := eventbus.New(nil) storages = &storage.Storage{ DiffStore: diffStore, ExpectationsStore: expstorage.NewCachingExpectationStore(expstorage.NewSQLExpectationStore(vdb), eventBus), IgnoreStore: ignore.NewSQLIgnoreStore(vdb), TileStore: filetilestore.NewFileTileStore(*tileStoreDir, config.DATASET_GOLD, 2*time.Minute), DigestStore: digestStore, NCommits: *nCommits, EventBus: eventBus, TrybotResults: trybot.NewTrybotResultStorage(vdb), RietveldAPI: rietveld.New(*rietveldURL, nil), } if err := history.Init(storages, *nTilesToBackfill); err != nil { glog.Fatalf("Unable to initialize history package: %s", err) } if blamer, err = blame.New(storages); err != nil { glog.Fatalf("Unable to create blamer: %s", err) } if err := ignore.Init(storages.IgnoreStore); err != nil { glog.Fatalf("Failed to start monitoring for expired ignore rules: %s", err) } // Enable the experimental features. tallies, err = tally.New(storages) if err != nil { glog.Fatalf("Failed to build tallies: %s", err) } paramsetSum = paramsets.New(tallies, storages) if !*local { *issueTrackerKey = metadata.Must(metadata.ProjectGet(metadata.APIKEY)) } issueTracker = issues.NewIssueTracker(*issueTrackerKey) summaries, err = summary.New(storages, tallies, blamer) if err != nil { glog.Fatalf("Failed to build summary: %s", err) } statusWatcher, err = status.New(storages) if err != nil { glog.Fatalf("Failed to initialize status watcher: %s", err) } imgFS := NewURLAwareFileServer(*imageDir, IMAGE_URL_PREFIX) pathToURLConverter = imgFS.GetURL if err := warmer.Init(storages, summaries, tallies); err != nil { glog.Fatalf("Failed to initialize the warmer: %s", err) } mainTimer.Stop() // Everything is wired up at this point. Run the self tests if requested. if *selfTest { search.SelfTest(storages, tallies, blamer, paramsetSum) } router := mux.NewRouter() // Set up the resource to serve the image files. router.PathPrefix(IMAGE_URL_PREFIX).Handler(imgFS.Handler) // New Polymer based UI endpoints. router.PathPrefix("/res/").HandlerFunc(makeResourceHandler()) // TODO(stephana): Remove the 'poly' prefix from all the handlers and clean // up main2.go by either merging it it into main.go or making it clearer that // it contains all the handlers. Make it clearer what variables are shared // between the different file. // All the handlers will be prefixed with poly to differentiate it from the // angular code until the angular code is removed. router.HandleFunc(OAUTH2_CALLBACK_PATH, login.OAuth2CallbackHandler) router.HandleFunc("/", byBlameHandler).Methods("GET") router.HandleFunc("/list", templateHandler("list.html")).Methods("GET") router.HandleFunc("/_/details", polyDetailsHandler).Methods("GET") router.HandleFunc("/_/diff", polyDiffJSONDigestHandler).Methods("GET") router.HandleFunc("/_/hashes", polyAllHashesHandler).Methods("GET") router.HandleFunc("/_/ignores", polyIgnoresJSONHandler).Methods("GET") router.HandleFunc("/_/ignores/add/", polyIgnoresAddHandler).Methods("POST") router.HandleFunc("/_/ignores/del/{id}", polyIgnoresDeleteHandler).Methods("POST") router.HandleFunc("/_/ignores/save/{id}", polyIgnoresUpdateHandler).Methods("POST") router.HandleFunc("/_/list", polyListTestsHandler).Methods("GET") router.HandleFunc("/_/paramset", polyParamsHandler).Methods("GET") router.HandleFunc("/_/nxn", nxnJSONHandler).Methods("GET") // TODO(stephana): Once /_/search3 is stable it will replace /_/search and the // /search*.html pages will be consolidated into one. router.HandleFunc("/_/search", polySearchJSONHandler).Methods("GET") router.HandleFunc("/_/search3", search3JSONHandler).Methods("GET") router.HandleFunc("/_/status/{test}", polyTestStatusHandler).Methods("GET") router.HandleFunc("/_/test", polyTestHandler).Methods("POST") router.HandleFunc("/_/triage", polyTriageHandler).Methods("POST") router.HandleFunc("/_/triagelog", polyTriageLogHandler).Methods("GET") router.HandleFunc("/_/triagelog/undo", triageUndoHandler).Methods("POST") router.HandleFunc("/_/failure", failureListJSONHandler).Methods("GET") router.HandleFunc("/_/failure/clear", failureClearJSONHandler).Methods("POST") router.HandleFunc("/_/trybot", listTrybotsJSONHandler).Methods("GET") router.HandleFunc("/byblame", byBlameHandler).Methods("GET") router.HandleFunc("/cluster", templateHandler("cluster.html")).Methods("GET") router.HandleFunc("/search2", search2Handler).Methods("GET") router.HandleFunc("/cmp/{test}", templateHandler("compare.html")).Methods("GET") router.HandleFunc("/detail", templateHandler("single.html")).Methods("GET") router.HandleFunc("/diff", templateHandler("diff.html")).Methods("GET") router.HandleFunc("/help", templateHandler("help.html")).Methods("GET") router.HandleFunc("/ignores", templateHandler("ignores.html")).Methods("GET") router.HandleFunc("/loginstatus/", login.StatusHandler) router.HandleFunc("/logout/", login.LogoutHandler) router.HandleFunc("/search", templateHandler("search.html")).Methods("GET") router.HandleFunc("/triagelog", templateHandler("triagelog.html")).Methods("GET") router.HandleFunc("/trybot", templateHandler("trybot.html")).Methods("GET") router.HandleFunc("/failures", templateHandler("failures.html")).Methods("GET") // Add the necessary middleware and have the router handle all requests. // By structuring the middleware this way we only log requests that are // authenticated. rootHandler := util.LoggingGzipRequestResponse(router) if *forceLogin { rootHandler = login.ForceAuth(rootHandler, OAUTH2_CALLBACK_PATH) } // The polyStatusHandler is being polled, so we exclude it from logging. http.HandleFunc("/_/trstatus", polyStatusHandler) http.Handle("/", rootHandler) // Start the server glog.Infoln("Serving on http://127.0.0.1" + *port) glog.Fatal(http.ListenAndServe(*port, nil)) }
func testStatusWatcher(t assert.TestingT, tileBuilder tracedb.MasterTileBuilder) { eventBus := eventbus.New(nil) storages := &storage.Storage{ ExpectationsStore: expstorage.NewMemExpectationsStore(eventBus), MasterTileBuilder: tileBuilder, DigestStore: &MockDigestStore{}, EventBus: eventBus, } watcher, err := New(storages) assert.Nil(t, err) // Go through all corpora and change all the Items to positive. status := watcher.GetStatus() assert.NotNil(t, status) for idx, corpStatus := range status.CorpStatus { // Make sure no digests has any issues attached. storages.DigestStore.(*MockDigestStore).issueIDs = nil assert.False(t, corpStatus.OK) tile, err := storages.GetLastTileTrimmed(true) assert.Nil(t, err) changes := map[string]types.TestClassification{} posOrNeg := []types.Label{types.POSITIVE, types.NEGATIVE} for _, trace := range tile.Traces { if trace.Params()[types.CORPUS_FIELD] == corpStatus.Name { gTrace := trace.(*types.GoldenTrace) testName := gTrace.Params()[types.PRIMARY_KEY_FIELD] for _, digest := range gTrace.Values { if _, ok := changes[testName]; !ok { changes[testName] = map[string]types.Label{} } changes[testName][digest] = posOrNeg[rand.Int()%2] } } } // Update the expecations and wait for the status to change. assert.Nil(t, storages.ExpectationsStore.AddChange(changes, "")) time.Sleep(1 * time.Second) newStatus := watcher.GetStatus() assert.False(t, newStatus.CorpStatus[idx].OK) assert.False(t, newStatus.OK) // Make sure all tests have an issue attached to each DigestInfo and // trigger another expectations update. storages.DigestStore.(*MockDigestStore).issueIDs = []int{1} assert.Nil(t, storages.ExpectationsStore.AddChange(changes, "")) time.Sleep(1 * time.Second) // Make sure the current corpus is now ok. newStatus = watcher.GetStatus() assert.True(t, newStatus.CorpStatus[idx].OK) } // All corpora are ok therefore the overall status should be ok. newStatus := watcher.GetStatus() assert.True(t, newStatus.OK) }
func TestBlamerWithSyntheticData(t *testing.T) { start := time.Now().Unix() commits := []*tiling.Commit{ &tiling.Commit{CommitTime: start + 10, Hash: "h1", Author: "John Doe 1"}, &tiling.Commit{CommitTime: start + 20, Hash: "h2", Author: "John Doe 2"}, &tiling.Commit{CommitTime: start + 30, Hash: "h3", Author: "John Doe 3"}, &tiling.Commit{CommitTime: start + 40, Hash: "h4", Author: "John Doe 4"}, &tiling.Commit{CommitTime: start + 50, Hash: "h5", Author: "John Doe 5"}, } params := []map[string]string{ map[string]string{"name": "foo", "config": "8888", "source_type": "gm"}, map[string]string{"name": "foo", "config": "565", "source_type": "gm"}, map[string]string{"name": "foo", "config": "gpu", "source_type": "gm"}, map[string]string{"name": "bar", "config": "8888", "source_type": "gm"}, map[string]string{"name": "bar", "config": "565", "source_type": "gm"}, map[string]string{"name": "bar", "config": "gpu", "source_type": "gm"}, map[string]string{"name": "baz", "config": "565", "source_type": "gm"}, map[string]string{"name": "baz", "config": "gpu", "source_type": "gm"}, } DI_1, DI_2, DI_3 := "digest1", "digest2", "digest3" DI_4, DI_5, DI_6 := "digest4", "digest5", "digest6" DI_7, DI_8, DI_9 := "digest7", "digest8", "digest9" MISS := types.MISSING_DIGEST digests := [][]string{ []string{MISS, MISS, DI_1, MISS, MISS}, []string{MISS, DI_1, DI_1, DI_2, MISS}, []string{DI_3, MISS, MISS, MISS, MISS}, []string{DI_5, DI_4, DI_5, DI_5, DI_5}, []string{DI_6, MISS, DI_4, MISS, MISS}, []string{MISS, MISS, MISS, MISS, MISS}, []string{DI_7, DI_7, MISS, DI_8, MISS}, []string{DI_7, MISS, DI_7, DI_8, MISS}, } // Make sure the data are consistent and create a mock TileStore. assert.Equal(t, len(commits), len(digests[0])) assert.Equal(t, len(digests), len(params)) eventBus := eventbus.New(nil) storages := &storage.Storage{ ExpectationsStore: expstorage.NewMemExpectationsStore(eventBus), MasterTileBuilder: mocks.NewMockTileBuilder(t, digests, params, commits), DigestStore: &mocks.MockDigestStore{FirstSeen: start + 1000, OkValue: true}, EventBus: eventBus, } blamer, err := New(storages) assert.Nil(t, err) // Check when completely untriaged blameLists, _ := blamer.GetAllBlameLists() assert.NotNil(t, blameLists) assert.Equal(t, 3, len(blameLists)) assert.Equal(t, 3, len(blameLists["foo"])) assert.Equal(t, []int{1, 0, 0, 0}, blameLists["foo"][DI_1].Freq) assert.Equal(t, []int{1, 0}, blameLists["foo"][DI_2].Freq) assert.Equal(t, []int{1, 0, 0, 0, 0}, blameLists["foo"][DI_3].Freq) assert.Equal(t, 3, len(blameLists["bar"])) assert.Equal(t, []int{2, 0, 0, 0}, blameLists["bar"][DI_4].Freq) assert.Equal(t, []int{1, 0, 0, 0, 0}, blameLists["bar"][DI_5].Freq) assert.Equal(t, []int{1, 0, 0, 0, 0}, blameLists["bar"][DI_6].Freq) assert.Equal(t, &BlameDistribution{Freq: []int{1}}, blamer.GetBlame("foo", DI_1, commits)) assert.Equal(t, &BlameDistribution{Freq: []int{3}}, blamer.GetBlame("foo", DI_2, commits)) assert.Equal(t, &BlameDistribution{Freq: []int{0}}, blamer.GetBlame("foo", DI_3, commits)) assert.Equal(t, &BlameDistribution{Freq: []int{1}}, blamer.GetBlame("bar", DI_4, commits)) assert.Equal(t, &BlameDistribution{Freq: []int{0}}, blamer.GetBlame("bar", DI_5, commits)) assert.Equal(t, &BlameDistribution{Freq: []int{0}}, blamer.GetBlame("bar", DI_6, commits)) // Classify some digests and re-calculate. changes := map[string]types.TestClassification{ "foo": map[string]types.Label{DI_1: types.POSITIVE, DI_2: types.NEGATIVE}, "bar": map[string]types.Label{DI_4: types.POSITIVE, DI_6: types.NEGATIVE}, } assert.Nil(t, storages.ExpectationsStore.AddChange(changes, "")) // Wait for the change to propagate. waitForChange(blamer, blameLists) blameLists, _ = blamer.GetAllBlameLists() assert.Equal(t, 3, len(blameLists)) assert.Equal(t, 1, len(blameLists["foo"])) assert.Equal(t, []int{1, 0, 0, 0, 0}, blameLists["foo"][DI_3].Freq) assert.Equal(t, 1, len(blameLists["bar"])) assert.Equal(t, []int{1, 0, 0, 0, 0}, blameLists["bar"][DI_5].Freq) assert.Equal(t, []int{1, 2, 0}, blameLists["baz"][DI_8].Freq) assert.Equal(t, &BlameDistribution{Freq: []int{0}}, blamer.GetBlame("foo", DI_3, commits)) assert.Equal(t, &BlameDistribution{Freq: []int{0}}, blamer.GetBlame("bar", DI_5, commits)) assert.Equal(t, &BlameDistribution{Freq: []int{3}}, blamer.GetBlame("baz", DI_8, commits)) // Change the underlying tile and trigger with another change. tile := storages.MasterTileBuilder.GetTile() // Get the trace for the last parameters and set a value. gTrace := tile.Traces[mocks.TraceKey(params[5])].(*types.GoldenTrace) gTrace.Values[2] = DI_9 assert.Nil(t, storages.ExpectationsStore.AddChange(changes, "")) // Wait for the change to propagate. waitForChange(blamer, blameLists) blameLists, _ = blamer.GetAllBlameLists() assert.Equal(t, 3, len(blameLists)) assert.Equal(t, 1, len(blameLists["foo"])) assert.Equal(t, 2, len(blameLists["bar"])) assert.Equal(t, []int{1, 0, 0}, blameLists["bar"][DI_9].Freq) assert.Equal(t, &BlameDistribution{Freq: []int{2}}, blamer.GetBlame("bar", DI_9, commits)) // Simulate the case where the digest is not found in digest store. storages.DigestStore.(*mocks.MockDigestStore).OkValue = false assert.Nil(t, storages.ExpectationsStore.AddChange(changes, "")) time.Sleep(10 * time.Millisecond) blameLists, _ = blamer.GetAllBlameLists() assert.Equal(t, 3, len(blameLists)) assert.Equal(t, 1, len(blameLists["foo"])) assert.Equal(t, 2, len(blameLists["bar"])) assert.Equal(t, []int{1, 0, 0}, blameLists["bar"][DI_9].Freq) assert.Equal(t, &BlameDistribution{Freq: []int{2}}, blamer.GetBlame("bar", DI_9, commits)) assert.Equal(t, &BlameDistribution{Freq: []int{1}}, blamer.GetBlame("bar", DI_9, commits[1:4])) assert.Equal(t, &BlameDistribution{Freq: []int{}}, blamer.GetBlame("bar", DI_9, commits[0:2])) }
func testBlamerWithLiveData(t assert.TestingT, tileBuilder tracedb.MasterTileBuilder) { eventBus := eventbus.New(nil) storage := &storage.Storage{ ExpectationsStore: expstorage.NewMemExpectationsStore(eventBus), MasterTileBuilder: tileBuilder, DigestStore: &mocks.MockDigestStore{ FirstSeen: time.Now().Unix(), OkValue: true, }, EventBus: eventBus, } blamer, err := New(storage) assert.Nil(t, err) // Wait until we have a blamelist. var blameLists map[string]map[string]*BlameDistribution for { blameLists, _ = blamer.GetAllBlameLists() if blameLists != nil { break } } tile := storage.MasterTileBuilder.GetTile() // Since we set the 'First' timestamp of all digest info entries // to Now. We should get a non-empty blamelist of all digests. oneTestName := "" oneDigest := "" forEachTestDigestDo(tile, func(testName, digest string) { assert.NotNil(t, blameLists[testName][digest]) assert.True(t, len(blameLists[testName][digest].Freq) > 0) // Remember the last one for later. oneTestName, oneDigest = testName, digest }) // Change the classification of one test and trigger the recalculation. changes := map[string]types.TestClassification{ oneTestName: map[string]types.Label{oneDigest: types.POSITIVE}, } assert.Nil(t, storage.ExpectationsStore.AddChange(changes, "")) // Wait for change to propagate. waitForChange(blamer, blameLists) blameLists, _ = blamer.GetAllBlameLists() // Assert the correctness of the blamelists. forEachTestDigestDo(tile, func(testName, digest string) { if (testName == oneTestName) && (digest == oneDigest) { assert.Nil(t, blameLists[testName][digest]) } else { assert.NotNil(t, blameLists[testName][digest]) assert.True(t, len(blameLists[testName][digest].Freq) > 0) } }) // Set 'First' for all digests in the past and trigger another // calculation. storage.DigestStore.(*mocks.MockDigestStore).FirstSeen = 0 assert.Nil(t, storage.ExpectationsStore.AddChange(changes, "")) waitForChange(blamer, blameLists) blameLists, _ = blamer.GetAllBlameLists() // Randomly assign labels to the different digests and make sure // that the blamelists are correct. storage.DigestStore.(*mocks.MockDigestStore).FirstSeen = time.Now().Unix() changes = map[string]types.TestClassification{} choices := []types.Label{types.POSITIVE, types.NEGATIVE, types.UNTRIAGED} forEachTestDigestDo(tile, func(testName, digest string) { targetTest := changes[testName] if targetTest == nil { targetTest = map[string]types.Label{} } // Randomly skip some digests. label := choices[rand.Int()%len(choices)] if label != types.UNTRIAGED { targetTest[digest] = label } }) // Add the labels and wait for the recalculation. assert.Nil(t, storage.ExpectationsStore.AddChange(changes, "")) waitForChange(blamer, blameLists) blameLists, commits := blamer.GetAllBlameLists() expecations, err := storage.ExpectationsStore.Get() assert.Nil(t, err) // Verify that the results are plausible. forEachTestTraceDo(tile, func(testName string, values []string) { for idx, digest := range values { if digest != types.MISSING_DIGEST { label := expecations.Classification(testName, digest) if label == types.UNTRIAGED { bl := blameLists[testName][digest] assert.NotNil(t, bl) freq := bl.Freq assert.True(t, len(freq) > 0) startIdx := len(commits) - len(freq) assert.True(t, (startIdx >= 0) && (startIdx <= idx), fmt.Sprintf("Expected (%s): Smaller than %d but got %d.", digest, startIdx, idx)) } } } }) }