golang.org/x/build@v0.0.0-20240506185731-218518f32b70/perfdata/app/query_test.go (about) 1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build cgo 6 7 package app 8 9 import ( 10 "encoding/json" 11 "fmt" 12 "io" 13 "mime/multipart" 14 "net/http" 15 "net/url" 16 "reflect" 17 "testing" 18 19 "golang.org/x/build/perfdata" 20 "golang.org/x/perf/storage/benchfmt" 21 ) 22 23 func TestQuery(t *testing.T) { 24 app := createTestApp(t) 25 defer app.Close() 26 27 // Write 1024 test results to the database. These results 28 // have labels named label0, label1, etc. Each label's value 29 // is an integer whose value is (record number) / (1 << label 30 // number). So 1 record has each value of label0, 2 records 31 // have each value of label1, 4 records have each value of 32 // label2, etc. This allows writing queries that match 2^n records. 33 status := app.uploadFiles(t, func(mpw *multipart.Writer) { 34 w, err := mpw.CreateFormFile("file", "path/1.txt") 35 if err != nil { 36 t.Errorf("CreateFormFile: %v", err) 37 } 38 bp := benchfmt.NewPrinter(w) 39 for i := 0; i < 1024; i++ { 40 r := &benchfmt.Result{Labels: make(map[string]string), NameLabels: make(map[string]string), Content: "BenchmarkName 1 ns/op"} 41 for j := uint(0); j < 10; j++ { 42 r.Labels[fmt.Sprintf("label%d", j)] = fmt.Sprintf("%d", i/(1<<j)) 43 } 44 r.NameLabels["name"] = "Name" 45 if err := bp.Print(r); err != nil { 46 t.Fatalf("Print: %v", err) 47 } 48 } 49 }) 50 51 tests := []struct { 52 q string 53 want []int 54 }{ 55 {"label0:0", []int{0}}, 56 {"label1:0", []int{0, 1}}, 57 {"label0:5 name:Name", []int{5}}, 58 {"label0:0 label0:5", nil}, 59 } 60 for _, test := range tests { 61 t.Run("query="+test.q, func(t *testing.T) { 62 u := app.srv.URL + "/search?" + url.Values{"q": []string{test.q}}.Encode() 63 resp, err := http.Get(u) 64 if err != nil { 65 t.Fatal(err) 66 } 67 defer resp.Body.Close() 68 if resp.StatusCode != 200 { 69 t.Fatalf("get /search: %v", resp.Status) 70 } 71 br := benchfmt.NewReader(resp.Body) 72 for i, num := range test.want { 73 if !br.Next() { 74 t.Fatalf("#%d: Next() = false, want true (Err() = %v)", i, br.Err()) 75 } 76 r := br.Result() 77 if r.Labels["upload"] != status.UploadID { 78 t.Errorf("#%d: upload = %q, want %q", i, r.Labels["upload"], status.UploadID) 79 } 80 if r.Labels["upload-part"] != status.FileIDs[0] { 81 t.Errorf("#%d: upload-part = %q, want %q", i, r.Labels["upload-part"], status.FileIDs[0]) 82 } 83 if r.Labels["upload-file"] != "1.txt" { 84 t.Errorf("#%d: upload-file = %q, want %q", i, r.Labels["upload-file"], "1.txt") 85 } 86 if r.Labels["label0"] != fmt.Sprintf("%d", num) { 87 t.Errorf("#%d: label0 = %q, want %d", i, r.Labels["label0"], num) 88 } 89 if r.NameLabels["name"] != "Name" { 90 t.Errorf("#%d: name = %q, want %q", i, r.NameLabels["name"], "Name") 91 } 92 if r.Labels["by"] != "user" { 93 t.Errorf("#%d: by = %q, want %q", i, r.Labels["uploader"], "user") 94 } 95 } 96 if br.Next() { 97 t.Fatalf("Next() = true, want false") 98 } 99 if err := br.Err(); err != nil { 100 t.Errorf("Err() = %v, want nil", err) 101 } 102 }) 103 } 104 } 105 106 func TestUploads(t *testing.T) { 107 app := createTestApp(t) 108 defer app.Close() 109 110 // Write 9 uploads to the database. These uploads have 1-9 111 // results each, a common label "i" set to the upload number, 112 // and a label "j" set to the record number within the upload. 113 var uploadIDs []string 114 for i := 0; i < 9; i++ { 115 status := app.uploadFiles(t, func(mpw *multipart.Writer) { 116 w, err := mpw.CreateFormFile("file", "path/1.txt") 117 if err != nil { 118 t.Errorf("CreateFormFile: %v", err) 119 } 120 bp := benchfmt.NewPrinter(w) 121 for j := 0; j <= i; j++ { 122 r := &benchfmt.Result{Labels: map[string]string{"i": fmt.Sprintf("%d", i)}, NameLabels: make(map[string]string), Content: "BenchmarkName 1 ns/op"} 123 r.Labels["j"] = fmt.Sprintf("%d", j) 124 if err := bp.Print(r); err != nil { 125 t.Fatalf("Print: %v", err) 126 } 127 } 128 }) 129 uploadIDs = append(uploadIDs, status.UploadID) 130 } 131 132 tests := []struct { 133 q string 134 extraLabels []string 135 want []perfdata.UploadInfo 136 }{ 137 {"", nil, []perfdata.UploadInfo{ 138 {9, uploadIDs[8], nil}, {8, uploadIDs[7], nil}, {7, uploadIDs[6], nil}, {6, uploadIDs[5], nil}, {5, uploadIDs[4], nil}, {4, uploadIDs[3], nil}, {3, uploadIDs[2], nil}, {2, uploadIDs[1], nil}, {1, uploadIDs[0], nil}, 139 }}, 140 {"j:5", nil, []perfdata.UploadInfo{{1, uploadIDs[8], nil}, {1, uploadIDs[7], nil}, {1, uploadIDs[6], nil}, {1, uploadIDs[5], nil}}}, 141 {"i:5", []string{"i"}, []perfdata.UploadInfo{{6, uploadIDs[5], benchfmt.Labels{"i": "5"}}}}, 142 {"not:found", nil, nil}, 143 } 144 for _, test := range tests { 145 t.Run("query="+test.q, func(t *testing.T) { 146 u := app.srv.URL + "/uploads" 147 uv := url.Values{} 148 if test.q != "" { 149 uv["q"] = []string{test.q} 150 } 151 if test.extraLabels != nil { 152 uv["extra_label"] = test.extraLabels 153 } 154 if len(uv) > 0 { 155 u += "?" + uv.Encode() 156 } 157 158 resp, err := http.Get(u) 159 if err != nil { 160 t.Fatal(err) 161 } 162 defer resp.Body.Close() 163 if resp.StatusCode != 200 { 164 t.Fatalf("get /uploads: %v", resp.Status) 165 } 166 dec := json.NewDecoder(resp.Body) 167 i := 0 168 for { 169 var ui perfdata.UploadInfo 170 if err := dec.Decode(&ui); err == io.EOF { 171 break 172 } else if err != nil { 173 t.Fatalf("failed to parse UploadInfo: %v", err) 174 } 175 if i > len(test.want) { 176 t.Fatalf("too many responses: have %d+ want %d", i, len(test.want)) 177 } 178 if !reflect.DeepEqual(ui, test.want[i]) { 179 t.Errorf("uploadinfo = %#v, want %#v", ui, test.want[i]) 180 } 181 i++ 182 } 183 if i < len(test.want) { 184 t.Fatalf("missing responses: have %d want %d", i, len(test.want)) 185 } 186 }) 187 } 188 }