| // Copyright 2017 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| //go:build cgo | |
| package app | |
| import ( | |
| "encoding/json" | |
| "fmt" | |
| "io" | |
| "mime/multipart" | |
| "net/http" | |
| "net/url" | |
| "reflect" | |
| "testing" | |
| "golang.org/x/perf/storage" | |
| "golang.org/x/perf/storage/benchfmt" | |
| ) | |
| func TestQuery(t *testing.T) { | |
| app := createTestApp(t) | |
| defer app.Close() | |
| // Write 1024 test results to the database. These results | |
| // have labels named label0, label1, etc. Each label's value | |
| // is an integer whose value is (record number) / (1 << label | |
| // number). So 1 record has each value of label0, 2 records | |
| // have each value of label1, 4 records have each value of | |
| // label2, etc. This allows writing queries that match 2^n records. | |
| status := app.uploadFiles(t, func(mpw *multipart.Writer) { | |
| w, err := mpw.CreateFormFile("file", "path/1.txt") | |
| if err != nil { | |
| t.Errorf("CreateFormFile: %v", err) | |
| } | |
| bp := benchfmt.NewPrinter(w) | |
| for i := 0; i < 1024; i++ { | |
| r := &benchfmt.Result{Labels: make(map[string]string), NameLabels: make(map[string]string), Content: "BenchmarkName 1 ns/op"} | |
| for j := uint(0); j < 10; j++ { | |
| r.Labels[fmt.Sprintf("label%d", j)] = fmt.Sprintf("%d", i/(1<<j)) | |
| } | |
| r.NameLabels["name"] = "Name" | |
| if err := bp.Print(r); err != nil { | |
| t.Fatalf("Print: %v", err) | |
| } | |
| } | |
| }) | |
| tests := []struct { | |
| q string | |
| want []int | |
| }{ | |
| {"label0:0", []int{0}}, | |
| {"label1:0", []int{0, 1}}, | |
| {"label0:5 name:Name", []int{5}}, | |
| {"label0:0 label0:5", nil}, | |
| } | |
| for _, test := range tests { | |
| t.Run("query="+test.q, func(t *testing.T) { | |
| u := app.srv.URL + "/search?" + url.Values{"q": []string{test.q}}.Encode() | |
| resp, err := http.Get(u) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| defer resp.Body.Close() | |
| if resp.StatusCode != 200 { | |
| t.Fatalf("get /search: %v", resp.Status) | |
| } | |
| br := benchfmt.NewReader(resp.Body) | |
| for i, num := range test.want { | |
| if !br.Next() { | |
| t.Fatalf("#%d: Next() = false, want true (Err() = %v)", i, br.Err()) | |
| } | |
| r := br.Result() | |
| if r.Labels["upload"] != status.UploadID { | |
| t.Errorf("#%d: upload = %q, want %q", i, r.Labels["upload"], status.UploadID) | |
| } | |
| if r.Labels["upload-part"] != status.FileIDs[0] { | |
| t.Errorf("#%d: upload-part = %q, want %q", i, r.Labels["upload-part"], status.FileIDs[0]) | |
| } | |
| if r.Labels["upload-file"] != "1.txt" { | |
| t.Errorf("#%d: upload-file = %q, want %q", i, r.Labels["upload-file"], "1.txt") | |
| } | |
| if r.Labels["label0"] != fmt.Sprintf("%d", num) { | |
| t.Errorf("#%d: label0 = %q, want %d", i, r.Labels["label0"], num) | |
| } | |
| if r.NameLabels["name"] != "Name" { | |
| t.Errorf("#%d: name = %q, want %q", i, r.NameLabels["name"], "Name") | |
| } | |
| if r.Labels["by"] != "user" { | |
| t.Errorf("#%d: by = %q, want %q", i, r.Labels["uploader"], "user") | |
| } | |
| } | |
| if br.Next() { | |
| t.Fatalf("Next() = true, want false") | |
| } | |
| if err := br.Err(); err != nil { | |
| t.Errorf("Err() = %v, want nil", err) | |
| } | |
| }) | |
| } | |
| } | |
| func TestUploads(t *testing.T) { | |
| app := createTestApp(t) | |
| defer app.Close() | |
| // Write 9 uploads to the database. These uploads have 1-9 | |
| // results each, a common label "i" set to the upload number, | |
| // and a label "j" set to the record number within the upload. | |
| var uploadIDs []string | |
| for i := 0; i < 9; i++ { | |
| status := app.uploadFiles(t, func(mpw *multipart.Writer) { | |
| w, err := mpw.CreateFormFile("file", "path/1.txt") | |
| if err != nil { | |
| t.Errorf("CreateFormFile: %v", err) | |
| } | |
| bp := benchfmt.NewPrinter(w) | |
| for j := 0; j <= i; j++ { | |
| r := &benchfmt.Result{Labels: map[string]string{"i": fmt.Sprintf("%d", i)}, NameLabels: make(map[string]string), Content: "BenchmarkName 1 ns/op"} | |
| r.Labels["j"] = fmt.Sprintf("%d", j) | |
| if err := bp.Print(r); err != nil { | |
| t.Fatalf("Print: %v", err) | |
| } | |
| } | |
| }) | |
| uploadIDs = append(uploadIDs, status.UploadID) | |
| } | |
| tests := []struct { | |
| q string | |
| extraLabels []string | |
| want []storage.UploadInfo | |
| }{ | |
| {"", nil, []storage.UploadInfo{ | |
| {Count: 9, UploadID: uploadIDs[8], LabelValues: nil}, {Count: 8, UploadID: uploadIDs[7], LabelValues: nil}, {Count: 7, UploadID: uploadIDs[6], LabelValues: nil}, {Count: 6, UploadID: uploadIDs[5], LabelValues: nil}, {Count: 5, UploadID: uploadIDs[4], LabelValues: nil}, {Count: 4, UploadID: uploadIDs[3], LabelValues: nil}, {Count: 3, UploadID: uploadIDs[2], LabelValues: nil}, {Count: 2, UploadID: uploadIDs[1], LabelValues: nil}, {Count: 1, UploadID: uploadIDs[0], LabelValues: nil}, | |
| }}, | |
| {"j:5", nil, []storage.UploadInfo{{Count: 1, UploadID: uploadIDs[8], LabelValues: nil}, {Count: 1, UploadID: uploadIDs[7], LabelValues: nil}, {Count: 1, UploadID: uploadIDs[6], LabelValues: nil}, {Count: 1, UploadID: uploadIDs[5], LabelValues: nil}}}, | |
| {"i:5", []string{"i"}, []storage.UploadInfo{{Count: 6, UploadID: uploadIDs[5], LabelValues: benchfmt.Labels{"i": "5"}}}}, | |
| {"not:found", nil, nil}, | |
| } | |
| for _, test := range tests { | |
| t.Run("query="+test.q, func(t *testing.T) { | |
| u := app.srv.URL + "/uploads" | |
| uv := url.Values{} | |
| if test.q != "" { | |
| uv["q"] = []string{test.q} | |
| } | |
| if test.extraLabels != nil { | |
| uv["extra_label"] = test.extraLabels | |
| } | |
| if len(uv) > 0 { | |
| u += "?" + uv.Encode() | |
| } | |
| resp, err := http.Get(u) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| defer resp.Body.Close() | |
| if resp.StatusCode != 200 { | |
| t.Fatalf("get /uploads: %v", resp.Status) | |
| } | |
| dec := json.NewDecoder(resp.Body) | |
| i := 0 | |
| for { | |
| var ui storage.UploadInfo | |
| if err := dec.Decode(&ui); err == io.EOF { | |
| break | |
| } else if err != nil { | |
| t.Fatalf("failed to parse UploadInfo: %v", err) | |
| } | |
| if i > len(test.want) { | |
| t.Fatalf("too many responses: have %d+ want %d", i, len(test.want)) | |
| } | |
| if !reflect.DeepEqual(ui, test.want[i]) { | |
| t.Errorf("uploadinfo = %#v, want %#v", ui, test.want[i]) | |
| } | |
| i++ | |
| } | |
| if i < len(test.want) { | |
| t.Fatalf("missing responses: have %d want %d", i, len(test.want)) | |
| } | |
| }) | |
| } | |
| } |