@@ -18,7 +18,6 @@ import (
1818 "context"
1919 "fmt"
2020 "strings"
21- "testing"
2221
2322 monitoring "cloud.google.com/go/monitoring/apiv3"
2423 gax "github.com/googleapis/gax-go"
5049)
5150
5251func init() {
53- // For testing convenience, we reduce maximum time series that metric client accepts.
54- MaxTimeSeriesPerUpload = 3
55-
5652 // Mock functions.
5753 newMetricClient = mockNewMetricClient
5854 createTimeSeries = mockCreateTimeSeries
@@ -126,22 +122,29 @@ func mockAddToBundler(bndler *bundler.Bundler, item interface{}, _ int) error {
126122// One of these functions once and only once, and never call NewExporter() directly.
127123
128124// newTestExp creates an exporter which saves error to errStorage. Caller should not set
129- // opts.OnError.
130- func newTestExp(t *testing.T, opts Options) *Exporter {
125+ // opts.OnError and opts.BundleCountThreshold .
126+ func newTestExp(opts Options) ( *Exporter, error) {
131127 opts.OnError = testOnError
128+ // For testing convenience, we reduce the number of timeseris in one upload monitoring API
129+ // call.
130+ opts.BundleCountThreshold = 3
132131 exp, err := NewExporter(ctx, opts)
133132 if err != nil {
134- t.Fatalf ("creating exporter failed: %v", err)
133+ return nil, fmt.Errorf ("creating exporter failed: %v", err)
135134 }
136135 // Expose projDataMap so that mockAddToBundler() can use it.
137136 projDataMap = exp.projDataMap
138- return exp
137+ return exp, nil
139138}
140139
141140// newTestProjData creates a projectData object to test behavior of projectData.uploadRowData. Other
142141// uses are not recommended. As newTestExp, all errors are saved to errStorage.
143- func newTestProjData(t *testing.T, opts Options) *projectData {
144- return newTestExp(t, opts).newProjectData(project1)
142+ func newTestProjData(opts Options) (*projectData, error) {
143+ exp, err := newTestExp(opts)
144+ if err != nil {
145+ return nil, err
146+ }
147+ return exp.newProjectData(project1), nil
145148}
146149
147150// We define a storage for all errors happened in export operation.
@@ -157,33 +160,54 @@ func testOnError(err error, rds ...*RowData) {
157160 errStorage = append(errStorage, errRowData{err, rds})
158161}
159162
163+ // multiError stores a sequence of errors. To convert it to an actual error, call toError().
164+ type multiError struct {
165+ errs []error
166+ }
167+
168+ func (me *multiError) addf(format string, args ...interface{}) {
169+ me.errs = append(me.errs, fmt.Errorf(format, args...))
170+ }
171+
172+ func (me *multiError) toError() error {
173+ switch len(me.errs) {
174+ case 0:
175+ return nil
176+ case 1:
177+ return me.errs[0]
178+ default:
179+ return fmt.Errorf("multiple errors: %q", me.errs)
180+ }
181+ }
182+
160183// checkMetricClient checks all recorded requests to the metric client. We only compare int64
161184// values of the time series. To make this work, we assigned different int64 values for all valid
162185// rows in the test.
163- func checkMetricClient(t *testing.T, wantReqsValues [][]int64) {
186+ func checkMetricClient(wantReqsValues [][]int64) error {
164187 reqsLen, wantReqsLen := len(timeSeriesReqs), len(wantReqsValues)
165188 if reqsLen != wantReqsLen {
166- t.Errorf("number of requests got: %d, want %d", reqsLen, wantReqsLen)
167- return
189+ return fmt.Errorf("number of requests got: %d, want %d", reqsLen, wantReqsLen)
168190 }
191+ var errs multiError
169192 for i := 0; i < reqsLen; i++ {
170193 prefix := fmt.Sprintf("%d-th request mismatch", i+1)
171194 tsArr := timeSeriesReqs[i].TimeSeries
172195 wantTsValues := wantReqsValues[i]
173196 tsArrLen, wantTsArrLen := len(tsArr), len(wantTsValues)
174197 if tsArrLen != wantTsArrLen {
175- t.Errorf ("%s: number of time series got: %d, want: %d", prefix, tsArrLen, wantTsArrLen)
198+ errs.addf ("%s: number of time series got: %d, want: %d", prefix, tsArrLen, wantTsArrLen)
176199 continue
177200 }
178201 for j := 0; j < tsArrLen; j++ {
179202 // This is how monitoring API stores the int64 value.
180203 tsVal := tsArr[j].Points[0].Value.Value.(*mpb.TypedValue_Int64Value).Int64Value
181204 wantTsVal := wantTsValues[j]
182205 if tsVal != wantTsVal {
183- t.Errorf ("%s: Value got: %d, want: %d", prefix, tsVal, wantTsVal)
206+ errs.addf ("%s: Value got: %d, want: %d", prefix, tsVal, wantTsVal)
184207 }
185208 }
186209 }
210+ return errs.toError()
187211}
188212
189213// errRowDataCheck contains data for checking content of error storage.
@@ -193,96 +217,99 @@ type errRowDataCheck struct {
193217}
194218
195219// checkErrStorage checks content of error storage. For returned errors, we check prefix and suffix.
196- func checkErrStorage(t *testing.T, wantErrRdCheck []errRowDataCheck) {
220+ func checkErrStorage(wantErrRdCheck []errRowDataCheck) error {
197221 gotLen, wantLen := len(errStorage), len(wantErrRdCheck)
198222 if gotLen != wantLen {
199- t.Errorf("number of reported errors: %d, want: %d", gotLen, wantLen)
200- return
223+ return fmt.Errorf("number of reported errors: %d, want: %d", gotLen, wantLen)
201224 }
225+ var errs multiError
202226 for i := 0; i < gotLen; i++ {
203227 prefix := fmt.Sprintf("%d-th reported error mismatch", i+1)
204228 errRd, wantErrRd := errStorage[i], wantErrRdCheck[i]
205229 errStr := errRd.err.Error()
206230 if errPrefix := wantErrRd.errPrefix; !strings.HasPrefix(errStr, errPrefix) {
207- t.Errorf ("%s: error got: %q, want: prefixed by %q", prefix, errStr, errPrefix)
231+ errs.addf ("%s: error got: %q, want: prefixed by %q", prefix, errStr, errPrefix)
208232 }
209233 if errSuffix := wantErrRd.errSuffix; !strings.HasSuffix(errStr, errSuffix) {
210- t.Errorf ("%s: error got: %q, want: suffiexd by %q", prefix, errStr, errSuffix)
234+ errs.addf ("%s: error got: %q, want: suffiexd by %q", prefix, errStr, errSuffix)
211235 }
212236 if err := checkRowDataArr(errRd.rds, wantErrRd.rds); err != nil {
213- t.Errorf ("%s: RowData array mismatch: %v", prefix, err)
237+ errs.addf ("%s: RowData array mismatch: %v", prefix, err)
214238 }
215239 }
240+ return errs.toError()
216241}
217242
218243func checkRowDataArr(rds, wantRds []*RowData) error {
219244 rdLen, wantRdLen := len(rds), len(wantRds)
220245 if rdLen != wantRdLen {
221246 return fmt.Errorf("number row data got: %d, want: %d", rdLen, wantRdLen)
222247 }
248+ var errs multiError
223249 for i := 0; i < rdLen; i++ {
224250 if err := checkRowData(rds[i], wantRds[i]); err != nil {
225- return fmt.Errorf ("%d-th row data mismatch: %v", i+1, err)
251+ errs.addf ("%d-th row data mismatch: %v", i+1, err)
226252 }
227253 }
228- return nil
254+ return errs.toError()
229255}
230256
231257func checkRowData(rd, wantRd *RowData) error {
258+ var errs multiError
232259 if rd.View != wantRd.View {
233- return fmt.Errorf ("View got: %s, want: %s", rd.View.Name, wantRd.View.Name)
260+ errs.addf ("View got: %s, want: %s", rd.View.Name, wantRd.View.Name)
234261 }
235262 if rd.Start != wantRd.Start {
236- return fmt.Errorf ("Start got: %v, want: %v", rd.Start, wantRd.Start)
263+ errs.addf ("Start got: %v, want: %v", rd.Start, wantRd.Start)
237264 }
238265 if rd.End != wantRd.End {
239- return fmt.Errorf ("End got: %v, want: %v", rd.End, wantRd.End)
266+ errs.addf ("End got: %v, want: %v", rd.End, wantRd.End)
240267 }
241268 if rd.Row != wantRd.Row {
242- return fmt.Errorf ("Row got: %v, want: %v", rd.Row, wantRd.Row)
269+ errs.addf ("Row got: %v, want: %v", rd.Row, wantRd.Row)
243270 }
244- return nil
271+ return errs.toError()
245272}
246273
247274// checkProjData checks all data passed to the bundler by bundler.Add().
248- func checkProjData(t *testing.T, wantProjData map[string][]*RowData) {
249- wantProj := map[string]bool{}
250- for proj := range wantProjData {
251- wantProj[proj] = true
252- }
275+ func checkProjData(wantProjData map[string][]*RowData) error {
276+ var errs multiError
253277 for proj := range projRds {
254- if !wantProj [proj] {
255- t.Errorf ("project in exporter's project data not wanted: %s", proj)
278+ if _, ok := wantProjData [proj]; !ok {
279+ errs.addf ("project in exporter's project data not wanted: %s", proj)
256280 }
257281 }
258282
259283 for proj, wantRds := range wantProjData {
260284 rds, ok := projRds[proj]
261285 if !ok {
262- t.Errorf ("wanted project not found in exporter's project data: %v", proj)
286+ errs.addf ("wanted project not found in exporter's project data: %v", proj)
263287 continue
264288 }
265289 if err := checkRowDataArr(*rds, wantRds); err != nil {
266- t.Errorf ("RowData array mismatch for project %s: %v", proj, err)
290+ errs.addf ("RowData array mismatch for project %s: %v", proj, err)
267291 }
268292 }
293+ return errs.toError()
269294}
270295
271296// checkLabels checks data in labels.
272- func checkLabels(t *testing.T, prefix string, labels, wantLabels map[string]string) {
297+ func checkLabels(prefix string, labels, wantLabels map[string]string) error {
298+ var errs multiError
273299 for labelName, value := range labels {
274300 wantValue, ok := wantLabels[labelName]
275301 if !ok {
276- t.Errorf ("%s: label name in time series not wanted: %s", prefix, labelName)
302+ errs.addf ("%s: label name in time series not wanted: %s", prefix, labelName)
277303 continue
278304 }
279305 if value != wantValue {
280- t.Errorf ("%s: value for label name %s got: %s, want: %s", prefix, labelName, value, wantValue)
306+ errs.addf ("%s: value for label name %s got: %s, want: %s", prefix, labelName, value, wantValue)
281307 }
282308 }
283309 for wantLabelName := range wantLabels {
284310 if _, ok := labels[wantLabelName]; !ok {
285- t.Errorf ("%s: wanted label name not found in time series: %s", prefix, wantLabelName)
311+ errs.addf ("%s: wanted label name not found in time series: %s", prefix, wantLabelName)
286312 }
287313 }
314+ return errs.toError()
288315}
0 commit comments