github.com/web-platform-tests/wpt.fyi@v0.0.0-20240530210107-70cf978996f1/util/populate_dev_data.go (about) 1 // Copyright 2017 The WPT Dashboard Project. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package main 6 7 import ( 8 "context" 9 "flag" 10 "fmt" 11 "log" 12 "os" 13 "strings" 14 "time" 15 16 mapset "github.com/deckarep/golang-set" 17 18 "github.com/web-platform-tests/wpt.fyi/shared" 19 "github.com/web-platform-tests/wpt.fyi/shared/metrics" 20 ) 21 22 var ( 23 project = flag.String("project", "", "project ID used to connect to Datastore") 24 datastoreHost = flag.String("datastore_host", "", "Cloud Datastore emulator host") 25 localHost = flag.String("local_host", "localhost:8080", "local dev_appserver.py webapp host") 26 remoteHost = flag.String("remote_host", "wpt.fyi", "wpt.fyi host to fetch prod runs from") 27 numRemoteRuns = flag.Int("num_remote_runs", 10, "number of remote runs to copy from host to local environment") 28 staticRuns = flag.Bool("static_runs", false, "Include runs in the /static dir") 29 remoteRuns = flag.Bool("remote_runs", true, "Include copies of remote runs") 30 seenTestRunIDs = mapset.NewSet() 31 labels = flag.String("labels", "", "Labels for which to fetch runs") 32 ) 33 34 // populate_dev_data.go populates a local running webapp instance with some 35 // of the latest production entities, so that there's data to view. 36 // 37 // Usage (from util/): 38 // go run populate_dev_data.go 39 func main() { 40 log.SetFlags(log.LstdFlags | log.Lshortfile) 41 flag.Parse() 42 43 if *project != "" { 44 os.Setenv("DATASTORE_PROJECT_ID", *project) 45 } 46 if *datastoreHost != "" { 47 os.Setenv("DATASTORE_EMULATOR_HOST", *datastoreHost) 48 } 49 50 ctx := context.Background() 51 shared.Clients.Init(ctx) 52 defer shared.Clients.Close() 53 54 log.Printf("Adding dev data to local emulator...") 55 56 emptySecretToken := &shared.Token{} 57 enabledFlag := &shared.Flag{Enabled: true} 58 staticDataTime := time.Now() 59 60 // Follow pattern established in run/*.py data collection code. 61 const staticRunSHA = "24278ab61781de72ed363b866ae6b50b86822b27" 62 summaryURLFmtString := "http://%s/static/%s/%s" 63 chrome := shared.TestRun{ 64 ProductAtRevision: shared.ProductAtRevision{ 65 Product: shared.Product{ 66 BrowserName: "chrome", 67 BrowserVersion: "74.0", 68 OSName: "linux", 69 OSVersion: "3.16", 70 }, 71 FullRevisionHash: staticRunSHA, 72 Revision: staticRunSHA[:10], 73 }, 74 ResultsURL: fmt.Sprintf(summaryURLFmtString, *localHost, staticRunSHA[:10], "chrome[stable]-summary_v2.json.gz"), 75 CreatedAt: staticDataTime, 76 TimeStart: staticDataTime, 77 Labels: []string{"chrome", shared.StableLabel}, 78 } 79 chromeExp := chrome 80 chromeExp.BrowserVersion = "76.0" 81 chromeExp.Labels = []string{"chrome", shared.ExperimentalLabel} 82 chromeExp.ResultsURL = strings.Replace(chrome.ResultsURL, "[stable]", "[experimental]", -1) 83 84 edge := chrome 85 edge.BrowserName = "edge" 86 edge.BrowserVersion = "18" 87 edge.OSName = "windows" 88 edge.OSVersion = "10" 89 edge.ResultsURL = fmt.Sprintf(summaryURLFmtString, *localHost, staticRunSHA[:10], "edge[stable]-summary_v2.json.gz") 90 edge.Labels = []string{"edge", shared.StableLabel} 91 92 edgeExp := edge 93 edgeExp.BrowserVersion = "20" 94 edgeExp.ResultsURL = strings.Replace(edge.ResultsURL, "[stable]", "[experimental]", -1) 95 edgeExp.Labels = []string{"edge", shared.ExperimentalLabel} 96 97 firefox := chrome 98 firefox.BrowserName = "firefox" 99 firefox.BrowserVersion = "66" 100 firefox.ResultsURL = fmt.Sprintf(summaryURLFmtString, *localHost, staticRunSHA[:10], "firefox[stable]-summary_v2.json.gz") 101 firefox.Labels = []string{"firefox", shared.StableLabel} 102 firefoxExp := firefox 103 firefoxExp.BrowserVersion = "68.0" 104 firefoxExp.Labels = []string{"firefox", shared.ExperimentalLabel} 105 firefoxExp.ResultsURL = strings.Replace(firefox.ResultsURL, "[stable]", "[experimental]", -1) 106 107 safari := chrome 108 safari.BrowserName = "safari" 109 safari.BrowserVersion = "12.1" 110 safari.OSName = "mac" 111 safari.OSName = "10.13" 112 safari.ResultsURL = fmt.Sprintf(summaryURLFmtString, *localHost, staticRunSHA[:10], "safari[stable]-summary_v2.json.gz") 113 safari.Labels = []string{"safari", shared.StableLabel} 114 safariExp := safari 115 safariExp.BrowserVersion = "81 preview" 116 safariExp.Labels = []string{"safari", shared.ExperimentalLabel} 117 safariExp.ResultsURL = strings.Replace(safari.ResultsURL, "[stable]", "[experimental]", -1) 118 119 staticTestRuns := shared.TestRuns{ 120 chrome, 121 chromeExp, 122 edge, 123 edgeExp, 124 firefox, 125 firefoxExp, 126 safari, 127 safariExp, 128 } 129 labelRuns(staticTestRuns, "test", "static", shared.MasterLabel) 130 131 timeZero := time.Unix(0, 0) 132 // Follow pattern established in metrics/run/*.go data collection code. 133 // Use unzipped JSON for local dev. 134 const metricsURLFmtString = "/static/wptd-metrics/0-0/%s.json" 135 staticTestRunMetadata := make([]interface{}, len(staticTestRuns)) 136 for i := range staticTestRuns { 137 staticTestRunMetadata[i] = &staticTestRuns[i] 138 } 139 passRateMetadata := metrics.PassRateMetadata{ 140 TestRunsMetadata: metrics.TestRunsMetadata{ 141 StartTime: timeZero, 142 EndTime: timeZero, 143 DataURL: fmt.Sprintf(metricsURLFmtString, "pass-rates"), 144 }, 145 } 146 147 testRunKindName := "TestRun" 148 passRateMetadataKindName := metrics.GetDatastoreKindName( 149 metrics.PassRateMetadata{}) 150 151 log.Print("Adding local (empty) secrets...") 152 store := shared.NewAppEngineDatastore(ctx, false) 153 addSecretToken(store, "upload-token", emptySecretToken) 154 addSecretToken(store, "github-wpt-fyi-bot-token", emptySecretToken) 155 addSecretToken(store, "github-oauth-client-id", emptySecretToken) 156 addSecretToken(store, "github-oauth-client-secret", emptySecretToken) 157 addSecretToken(store, "secure-cookie-hashkey", &shared.Token{ 158 Secret: "a-very-secret-sixty-four-bytes!!a-very-secret-sixty-four-bytes!!", 159 }) 160 addSecretToken(store, "secure-cookie-blockkey", &shared.Token{ 161 Secret: "a-very-secret-thirty-two-bytes!!", 162 }) 163 164 log.Print("Adding flag defaults...") 165 addFlag(store, "queryBuilder", enabledFlag) 166 addFlag(store, "diffFilter", enabledFlag) 167 addFlag(store, "diffFromAPI", enabledFlag) 168 addFlag(store, "structuredQueries", enabledFlag) 169 addFlag(store, "diffRenames", enabledFlag) 170 addFlag(store, "paginationTokens", enabledFlag) 171 172 log.Print("Adding uploader \"test\"...") 173 addData(store, "Uploader", []interface{}{ 174 &shared.Uploader{Username: "test", Password: "123"}, 175 }) 176 177 if *staticRuns { 178 log.Print("Adding local mock data (static/)...") 179 for i, key := range addData(store, testRunKindName, staticTestRunMetadata) { 180 staticTestRuns[i].ID = key.IntID() 181 } 182 stableRuns := shared.TestRuns{} 183 defaultRuns := shared.TestRuns{} 184 for _, run := range staticTestRuns { 185 labels := run.LabelsSet() 186 if labels.Contains(shared.StableLabel) { 187 stableRuns = append(stableRuns, run) 188 } else if labels.Contains("edge") || labels.Contains(shared.ExperimentalLabel) { 189 defaultRuns = append(defaultRuns, run) 190 } 191 } 192 stableInterop := passRateMetadata 193 stableInterop.TestRunIDs = stableRuns.GetTestRunIDs() 194 defaultInterop := passRateMetadata 195 defaultInterop.TestRunIDs = defaultRuns.GetTestRunIDs() 196 addData(store, passRateMetadataKindName, []interface{}{ 197 &stableInterop, 198 &defaultInterop, 199 }) 200 } 201 202 if *remoteRuns { 203 log.Print("Adding latest production TestRun data...") 204 extraLabels := mapset.NewSet() 205 if labels != nil { 206 for _, s := range strings.Split(*labels, ",") { 207 if s != "" { 208 extraLabels.Add(s) 209 } 210 } 211 } 212 filters := shared.TestRunFilter{ 213 Labels: extraLabels.Union(mapset.NewSetWith(shared.StableLabel)), 214 MaxCount: numRemoteRuns, 215 } 216 copyProdRuns(store, filters) 217 218 log.Print("Adding latest master TestRun data...") 219 filters.Labels = extraLabels.Union(mapset.NewSetWith(shared.MasterLabel)) 220 copyProdRuns(store, filters) 221 222 log.Print("Adding latest experimental TestRun data...") 223 filters.Labels = extraLabels.Union(mapset.NewSetWith(shared.ExperimentalLabel)) 224 copyProdRuns(store, filters) 225 226 log.Print("Adding latest beta TestRun data...") 227 filters.Labels = extraLabels.Union(mapset.NewSetWith(shared.BetaLabel)) 228 copyProdRuns(store, filters) 229 230 log.Print("Adding latest aligned Edge stable and Chrome/Firefox/Safari experimental data...") 231 filters.Labels = extraLabels.Union(mapset.NewSet(shared.MasterLabel)) 232 filters.Products, _ = shared.ParseProductSpecs("chrome[experimental]", "edge[stable]", "firefox[experimental]", "safari[experimental]") 233 copyProdRuns(store, filters) 234 235 log.Printf("Successfully copied a total of %v distinct TestRuns", seenTestRunIDs.Cardinality()) 236 237 log.Print("Adding latest production PendingTestRun...") 238 copyProdPendingRuns(store, *numRemoteRuns) 239 } 240 241 log.Print("Adding test history data...") 242 addFakeHistoryData(store) 243 } 244 245 func copyProdRuns(store shared.Datastore, filters shared.TestRunFilter) { 246 for _, aligned := range []bool{false, true} { 247 if aligned { 248 filters.Aligned = &aligned 249 } 250 prodTestRuns, err := shared.FetchRuns(*remoteHost, filters) 251 if err != nil { 252 log.Print(err) 253 continue 254 } 255 labelRuns(prodTestRuns, "prod") 256 257 latestProductionTestRunMetadata := make([]interface{}, 0, len(prodTestRuns)) 258 for i := range prodTestRuns { 259 if !seenTestRunIDs.Contains(prodTestRuns[i].ID) { 260 seenTestRunIDs.Add(prodTestRuns[i].ID) 261 latestProductionTestRunMetadata = append(latestProductionTestRunMetadata, &prodTestRuns[i]) 262 } 263 } 264 addData(store, "TestRun", latestProductionTestRunMetadata) 265 } 266 } 267 268 func copyProdPendingRuns(store shared.Datastore, numRuns int) { 269 pendingRuns, err := FetchPendingRuns(*remoteHost) 270 if err != nil { 271 log.Fatalf("Failed to fetch pending runs: %s", err.Error()) 272 } 273 var castRuns []interface{} 274 for i := range pendingRuns { 275 castRuns = append(castRuns, &pendingRuns[i]) 276 } 277 addData(store, "PendingTestRun", castRuns) 278 } 279 280 func labelRuns(runs []shared.TestRun, labels ...string) { 281 for i := range runs { 282 for _, label := range labels { 283 runs[i].Labels = append(runs[i].Labels, label) 284 } 285 } 286 } 287 288 func addSecretToken(store shared.Datastore, id string, data interface{}) { 289 key := store.NewNameKey("Token", id) 290 if _, err := store.Put(key, data); err != nil { 291 log.Fatalf("Failed to add %s secret: %s", id, err.Error()) 292 } 293 log.Printf("Added %s secret", id) 294 } 295 296 func addFlag(store shared.Datastore, id string, data interface{}) { 297 key := store.NewNameKey("Flag", id) 298 if _, err := store.Put(key, data); err != nil { 299 log.Fatalf("Failed to add %s flag: %s", id, err.Error()) 300 } 301 log.Printf("Added %s flag", id) 302 } 303 304 func addData(store shared.Datastore, kindName string, data []interface{}) (keys []shared.Key) { 305 keys = make([]shared.Key, len(data)) 306 for i := range data { 307 keys[i] = store.NewIncompleteKey(kindName) 308 } 309 var err error 310 if keys, err = store.PutMulti(keys, data); err != nil { 311 log.Fatalf("Failed to add %s entities: %s", kindName, err.Error()) 312 } 313 log.Printf("Added %v %s entities", len(data), kindName) 314 return keys 315 } 316 317 // FetchPendingRuns fetches recent PendingTestRuns. 318 func FetchPendingRuns(wptdHost string) ([]shared.PendingTestRun, error) { 319 url := "https://" + wptdHost + "/api/status" 320 var pendingRuns []shared.PendingTestRun 321 err := shared.FetchJSON(url, &pendingRuns) 322 return pendingRuns, err 323 } 324 325 // TODO: Import real data here when staging is populated with real data 326 func addFakeHistoryData(store shared.Datastore) { 327 // browser_name,browser_version,date,test_name,subtest_name,status 328 devData := []map[string]string{ 329 { 330 "run_id": "5074677897101312", 331 "date": "2022-06-02T06:02:55.000Z", 332 "test_name": "example test name", 333 "subtest_name": "", 334 "status": "OK", 335 }, 336 { 337 "run_id": "5074677897101312", 338 "date": "2023-02-21T03:08:15.000Z", 339 "test_name": "example test name", 340 "subtest_name": "", 341 "status": "TIMEOUT", 342 }, 343 { 344 "run_id": "5074677897101312", 345 "date": "2023-03-30T20:27:32.000Z", 346 "test_name": "example test name", 347 "subtest_name": "", 348 "status": "OK", 349 }, 350 { 351 "run_id": "5074677897101312", 352 "date": "2022-06-02T06:02:55.000Z", 353 "test_name": "example test name", 354 "subtest_name": "subtest_name_1", 355 "status": "PASS", 356 }, 357 { 358 "run_id": "5074677897101312", 359 "date": "2022-08-14T06:02:55.000Z", 360 "test_name": "example test name", 361 "subtest_name": "subtest_name_1", 362 "status": "FAIL", 363 }, 364 { 365 "run_id": "5074677897101312", 366 "date": "2023-02-21T03:08:15.000Z", 367 "test_name": "example test name", 368 "subtest_name": "subtest_name_1", 369 "status": "NOTRUN", 370 }, 371 { 372 "run_id": "5074677897101312", 373 "date": "2023-03-30T20:27:32.611Z", 374 "test_name": "example test name", 375 "subtest_name": "subtest_name_1", 376 "status": "FAIL", 377 }, 378 { 379 "run_id": "5074677897101312", 380 "date": "2023-06-19T20:54:12.611Z", 381 "test_name": "example test name", 382 "subtest_name": "subtest_name_1", 383 "status": "PASS", 384 }, 385 { 386 "run_id": "5074677897101312", 387 "date": "2022-06-02T06:02:55.000Z", 388 "test_name": "example test name", 389 "subtest_name": "subtest_name_2", 390 "status": "TIMEOUT", 391 }, 392 { 393 "run_id": "5074677897101312", 394 "date": "2022-09-25T23:49:35.000Z", 395 "test_name": "example test name", 396 "subtest_name": "subtest_name_2", 397 "status": "PASS", 398 }, 399 { 400 "run_id": "5074677897101312", 401 "date": "2023-02-21T03:08:15.000Z", 402 "test_name": "example test name", 403 "subtest_name": "subtest_name_2", 404 "status": "NOTRUN", 405 }, 406 { 407 "run_id": "5074677897101312", 408 "date": "2023-03-30T20:27:32.611Z", 409 "test_name": "example test name", 410 "subtest_name": "subtest_name_2", 411 "status": "PASS", 412 }, 413 { 414 "run_id": "5074677897101312", 415 "date": "2022-06-02T06:02:55.000Z", 416 "test_name": "example test name", 417 "subtest_name": "subtest_name_3", 418 "status": "PASS", 419 }, 420 { 421 "run_id": "5074677897101312", 422 "date": "2023-02-21T03:08:15.000Z", 423 "test_name": "example test name", 424 "subtest_name": "subtest_name_3", 425 "status": "NOTRUN", 426 }, 427 { 428 "run_id": "5074677897101312", 429 "date": "2023-03-30T20:27:32.611Z", 430 "test_name": "example test name", 431 "subtest_name": "subtest_name_3", 432 "status": "PASS", 433 }, 434 } 435 436 browserMetadata := []map[string]string{ 437 { 438 "browser": "chrome", 439 }, 440 { 441 "browser": "edge", 442 }, 443 444 { 445 "browser": "firefox", 446 }, 447 448 { 449 "browser": "safari", 450 }, 451 } 452 453 browserEntries := make([]interface{}, 0, len(devData)) 454 for _, metadata := range browserMetadata { 455 for _, entry := range devData { 456 testHistoryEntry := shared.TestHistoryEntry{ 457 BrowserName: metadata["browser"], 458 RunID: entry["run_id"], 459 Date: entry["date"], 460 TestName: entry["test_name"], 461 SubtestName: entry["subtest_name"], 462 Status: entry["status"], 463 } 464 browserEntries = append(browserEntries, &testHistoryEntry) 465 } 466 } 467 addData(store, "TestHistoryEntry", browserEntries) 468 }