|
| 1 | +// Copyright 2018, OpenCensus Authors |
| 2 | +// |
| 3 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +// you may not use this file except in compliance with the License. |
| 5 | +// You may obtain a copy of the License at |
| 6 | +// |
| 7 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +// |
| 9 | +// Unless required by applicable law or agreed to in writing, software |
| 10 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +// See the License for the specific language governing permissions and |
| 13 | +// limitations under the License. |
| 14 | + |
| 15 | +package mongowrapper |
| 16 | + |
| 17 | +import ( |
| 18 | + "context" |
| 19 | + "errors" |
| 20 | + "reflect" |
| 21 | + "strings" |
| 22 | + "testing" |
| 23 | + "time" |
| 24 | + |
| 25 | + "go.opencensus.io/stats/view" |
| 26 | + "go.opencensus.io/tag" |
| 27 | + "go.opencensus.io/trace" |
| 28 | +) |
| 29 | + |
| 30 | +func TestUnitRoundtripTrackingOperation(t *testing.T) { |
| 31 | + if err := RegisterAllViews(); err != nil { |
| 32 | + t.Fatalf("Failed to register all the views: %v", err) |
| 33 | + } |
| 34 | + defer UnregisterAllViews() |
| 35 | + |
| 36 | + reportingPeriod := 200 * time.Millisecond |
| 37 | + view.SetReportingPeriod(reportingPeriod) |
| 38 | + defer view.SetReportingPeriod(time.Minute) |
| 39 | + |
| 40 | + viewDataChan := make(chan *view.Data, 1) |
| 41 | + spanDataChan := make(chan *trace.SpanData, 1) |
| 42 | + exp := &mockExporter{viewDataChan: viewDataChan, spanDataChan: spanDataChan} |
| 43 | + view.RegisterExporter(exp) |
| 44 | + defer view.UnregisterExporter(exp) |
| 45 | + |
| 46 | + trace.RegisterExporter(exp) |
| 47 | + defer trace.UnregisterExporter(exp) |
| 48 | + |
| 49 | + pausePeriod := 28 * time.Millisecond |
| 50 | + deadline := time.Now().Add(reportingPeriod) |
| 51 | + _, rts := roundtripTrackingSpan(context.Background(), "a.b.c/D.Foo", trace.WithSampler(trace.AlwaysSample())) |
| 52 | + <-time.After(pausePeriod / 2) |
| 53 | + errMsg := "This is an error" |
| 54 | + rts.setError(errors.New(errMsg)) |
| 55 | + <-time.After(pausePeriod / 2) |
| 56 | + rts.end(context.Background()) |
| 57 | + |
| 58 | + // Verifying the spans since those don't |
| 59 | + // operate on a frequency. |
| 60 | + sd0 := <-spanDataChan |
| 61 | + // Comparing the name |
| 62 | + if g, w := sd0.Name, "a.b.c/D.Foo"; g != w { |
| 63 | + t.Errorf("SpanData.Name mismatch:: Got %q Want %q", g, w) |
| 64 | + } |
| 65 | + wantStatus := trace.Status{Code: trace.StatusCodeUnknown, Message: errMsg} |
| 66 | + if g, w := sd0.Status, wantStatus; g != w { |
| 67 | + t.Errorf("SpanData.Status mismatch:: Got %#v Want %#v", g, w) |
| 68 | + } |
| 69 | + minPeriod := pausePeriod |
| 70 | + gotPeriod := sd0.EndTime.Sub(sd0.StartTime) |
| 71 | + if gotPeriod < minPeriod { |
| 72 | + t.Errorf("SpanData.TimeSpent:: Got %s Want min: %s", gotPeriod, minPeriod) |
| 73 | + } |
| 74 | + |
| 75 | + wait := deadline.Sub(time.Now()) + 3*time.Millisecond |
| 76 | + <-time.After(wait) |
| 77 | + |
| 78 | + var vds []*view.Data |
| 79 | + maxWaitViewsTimer := time.NewTimer(wait) |
| 80 | + for done := false; !done; { |
| 81 | + select { |
| 82 | + case <-maxWaitViewsTimer.C: |
| 83 | + done = true |
| 84 | + break |
| 85 | + case vd := <-viewDataChan: |
| 86 | + // Great! |
| 87 | + vds = append(vds, vd) |
| 88 | + } |
| 89 | + } |
| 90 | + |
| 91 | + if len(vds) < 2 { |
| 92 | + t.Errorf("Got %d ViewData; expected at least 2", len(vds)) |
| 93 | + } |
| 94 | + |
| 95 | + vdLatency := vds[0] |
| 96 | + vdCalls := vds[1] |
| 97 | + if strings.HasSuffix(vdCalls.View.Name, "client/latency") { |
| 98 | + // The order of reporting was wrong, so swap them. |
| 99 | + vdLatency, vdCalls = vdCalls, vdLatency |
| 100 | + } |
| 101 | + |
| 102 | + // From this point on, we should have the proper views. |
| 103 | + |
| 104 | + // Start examining Latency view. |
| 105 | + wantvLatency := &view.View{ |
| 106 | + Name: "mongo/client/latency", |
| 107 | + Description: "The latency of the various calls", |
| 108 | + Measure: mLatencyMs, |
| 109 | + Aggregation: latencyDistribution, |
| 110 | + TagKeys: []tag.Key{keyError, keyMethod, keyStatus}, |
| 111 | + } |
| 112 | + if g, w := vdLatency.View, wantvLatency; !reflect.DeepEqual(g, w) { |
| 113 | + t.Errorf("Latency.ViewData:\nGot: %#v\nWant:%#v\n", g, w) |
| 114 | + } |
| 115 | + if g, w := len(vdLatency.Rows), 1; g != w { |
| 116 | + t.Errorf("Latency.ViewData.Rows: Got %d Wanted %d", g, w) |
| 117 | + } else { |
| 118 | + r0 := vdLatency.Rows[0] |
| 119 | + // We need to have the row with the tag "error" since we ended with an error" |
| 120 | + wantTags := []tag.Tag{{Key: keyError, Value: errMsg}} |
| 121 | + if !reflect.DeepEqual(wantTags, r0.Tags) { |
| 122 | + t.Errorf("Latency.ViewData.Rows[0].Tags mismatch\nGot: %#v\nWant:%#v\n", r0.Tags, wantTags) |
| 123 | + } |
| 124 | + |
| 125 | + // Compare the latency |
| 126 | + d0 := r0.Data.(*view.DistributionData) |
| 127 | + if g, w := d0.Count, int64(1); g != w { |
| 128 | + t.Errorf("DistributionData.Count:: Got %d Want %d", g, w) |
| 129 | + } |
| 130 | + if d0.Min != d0.Max || d0.Max != d0.Mean || d0.Min != d0.Mean { |
| 131 | + t.Errorf("DistributionData:: Expected all equal of these values:\nMin: %.4f Mean: %.4f Max: %.4f", d0.Min, d0.Mean, d0.Max) |
| 132 | + } |
| 133 | + if g, w := d0.Max, float64(pausePeriod)/float64(time.Millisecond); g < w { |
| 134 | + t.Errorf("Distribution.Max:: Got %.4f < Want %.4f", g, w) |
| 135 | + } |
| 136 | + } |
| 137 | + // End examining Latency view. |
| 138 | + |
| 139 | + // Start examining Calls view. |
| 140 | + wantvCalls := &view.View{ |
| 141 | + Name: "mongo/client/calls", |
| 142 | + Description: "The various calls", |
| 143 | + Measure: mLatencyMs, |
| 144 | + Aggregation: view.Count(), |
| 145 | + TagKeys: []tag.Key{keyError, keyMethod, keyStatus}, |
| 146 | + } |
| 147 | + if g, w := vdCalls.View, wantvCalls; !reflect.DeepEqual(g, w) { |
| 148 | + t.Errorf("Calls.ViewData:\nGot: %#v\nWant:%#v\n", g, w) |
| 149 | + } |
| 150 | + if g, w := len(vdCalls.Rows), 1; g != w { |
| 151 | + t.Errorf("Calls.ViewdAta.Rows: Got %d Wanted %d", g, w) |
| 152 | + } else { |
| 153 | + r0 := vdCalls.Rows[0] |
| 154 | + wantTags := []tag.Tag{{Key: keyError, Value: errMsg}} |
| 155 | + if !reflect.DeepEqual(wantTags, r0.Tags) { |
| 156 | + t.Errorf("Calls.ViewData.Rows[0].Tags mismatch\nGot: %#v\nWant:%#v\n", r0.Tags, wantTags) |
| 157 | + } |
| 158 | + |
| 159 | + // Now comparing the actual value |
| 160 | + d0 := r0.Data.(*view.CountData) |
| 161 | + if g, w := d0.Value, int64(1); g != w { |
| 162 | + t.Errorf("CountData.Count.Value:: Got %d Want %d", g, w) |
| 163 | + } |
| 164 | + } |
| 165 | + // End examining Calls view. |
| 166 | +} |
| 167 | + |
| 168 | +type mockExporter struct { |
| 169 | + viewDataChan chan *view.Data |
| 170 | + spanDataChan chan *trace.SpanData |
| 171 | +} |
| 172 | + |
| 173 | +var _ trace.Exporter = (*mockExporter)(nil) |
| 174 | +var _ view.Exporter = (*mockExporter)(nil) |
| 175 | + |
| 176 | +func (me *mockExporter) ExportView(vd *view.Data) { |
| 177 | + me.viewDataChan <- vd |
| 178 | +} |
| 179 | + |
| 180 | +func (me *mockExporter) ExportSpan(sd *trace.SpanData) { |
| 181 | + me.spanDataChan <- sd |
| 182 | +} |
0 commit comments