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