|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435 |
- // Copyright 2017, OpenCensus Authors
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- //
-
- package view
-
- import (
- "context"
- "errors"
- "sync"
- "testing"
- "time"
-
- "go.opencensus.io/stats"
- "go.opencensus.io/tag"
- )
-
- func Test_Worker_ViewRegistration(t *testing.T) {
- someError := errors.New("some error")
-
- sc1 := make(chan *Data)
-
- type registration struct {
- c chan *Data
- vID string
- err error
- }
- type testCase struct {
- label string
- registrations []registration
- }
- tcs := []testCase{
- {
- "register v1ID",
- []registration{
- {
- sc1,
- "v1ID",
- nil,
- },
- },
- },
- {
- "register v1ID+v2ID",
- []registration{
- {
- sc1,
- "v1ID",
- nil,
- },
- },
- },
- {
- "register to v1ID; ??? to v1ID and view with same ID",
- []registration{
- {
- sc1,
- "v1ID",
- nil,
- },
- {
- sc1,
- "v1SameNameID",
- someError,
- },
- },
- },
- }
-
- mf1 := stats.Float64("MF1/Test_Worker_ViewSubscription", "desc MF1", "unit")
- mf2 := stats.Float64("MF2/Test_Worker_ViewSubscription", "desc MF2", "unit")
-
- for _, tc := range tcs {
- t.Run(tc.label, func(t *testing.T) {
- restart()
-
- views := map[string]*View{
- "v1ID": {
- Name: "VF1",
- Measure: mf1,
- Aggregation: Count(),
- },
- "v1SameNameID": {
- Name: "VF1",
- Description: "desc duplicate name VF1",
- Measure: mf1,
- Aggregation: Sum(),
- },
- "v2ID": {
- Name: "VF2",
- Measure: mf2,
- Aggregation: Count(),
- },
- "vNilID": nil,
- }
-
- for _, r := range tc.registrations {
- v := views[r.vID]
- err := Register(v)
- if (err != nil) != (r.err != nil) {
- t.Errorf("%v: Register() = %v, want %v", tc.label, err, r.err)
- }
- }
- })
- }
- }
-
- func Test_Worker_RecordFloat64(t *testing.T) {
- restart()
-
- someError := errors.New("some error")
- m := stats.Float64("Test_Worker_RecordFloat64/MF1", "desc MF1", "unit")
-
- k1, _ := tag.NewKey("k1")
- k2, _ := tag.NewKey("k2")
- ctx, err := tag.New(context.Background(),
- tag.Insert(k1, "v1"),
- tag.Insert(k2, "v2"),
- )
- if err != nil {
- t.Fatal(err)
- }
-
- v1 := &View{"VF1", "desc VF1", []tag.Key{k1, k2}, m, Count()}
- v2 := &View{"VF2", "desc VF2", []tag.Key{k1, k2}, m, Count()}
-
- type want struct {
- v *View
- rows []*Row
- err error
- }
- type testCase struct {
- label string
- registrations []*View
- records []float64
- wants []want
- }
-
- tcs := []testCase{
- {
- label: "0",
- registrations: []*View{},
- records: []float64{1, 1},
- wants: []want{{v1, nil, someError}, {v2, nil, someError}},
- },
- {
- label: "1",
- registrations: []*View{v1},
- records: []float64{1, 1},
- wants: []want{
- {
- v1,
- []*Row{
- {
- []tag.Tag{{Key: k1, Value: "v1"}, {Key: k2, Value: "v2"}},
- &CountData{Value: 2},
- },
- },
- nil,
- },
- {v2, nil, someError},
- },
- },
- {
- label: "2",
- registrations: []*View{v1, v2},
- records: []float64{1, 1},
- wants: []want{
- {
- v1,
- []*Row{
- {
- []tag.Tag{{Key: k1, Value: "v1"}, {Key: k2, Value: "v2"}},
- &CountData{Value: 2},
- },
- },
- nil,
- },
- {
- v2,
- []*Row{
- {
- []tag.Tag{{Key: k1, Value: "v1"}, {Key: k2, Value: "v2"}},
- &CountData{Value: 2},
- },
- },
- nil,
- },
- },
- },
- }
-
- for _, tc := range tcs {
- for _, v := range tc.registrations {
- if err := Register(v); err != nil {
- t.Fatalf("%v: Register(%v) = %v; want no errors", tc.label, v.Name, err)
- }
- }
-
- for _, value := range tc.records {
- stats.Record(ctx, m.M(value))
- }
-
- for _, w := range tc.wants {
- gotRows, err := RetrieveData(w.v.Name)
- if (err != nil) != (w.err != nil) {
- t.Fatalf("%s: RetrieveData(%v) = %v; want error = %v", tc.label, w.v.Name, err, w.err)
- }
- for _, got := range gotRows {
- if !containsRow(w.rows, got) {
- t.Errorf("%s: got row %#v; want none", tc.label, got)
- break
- }
- }
- for _, want := range w.rows {
- if !containsRow(gotRows, want) {
- t.Errorf("%s: got none; want %#v'", tc.label, want)
- break
- }
- }
- }
-
- // Cleaning up.
- Unregister(tc.registrations...)
- }
- }
-
- func TestReportUsage(t *testing.T) {
- ctx := context.Background()
-
- m := stats.Int64("measure", "desc", "unit")
-
- tests := []struct {
- name string
- view *View
- wantMaxCount int64
- }{
- {
- name: "cum",
- view: &View{Name: "cum1", Measure: m, Aggregation: Count()},
- wantMaxCount: 8,
- },
- {
- name: "cum2",
- view: &View{Name: "cum1", Measure: m, Aggregation: Count()},
- wantMaxCount: 8,
- },
- }
-
- for _, tt := range tests {
- restart()
- SetReportingPeriod(25 * time.Millisecond)
-
- if err := Register(tt.view); err != nil {
- t.Fatalf("%v: cannot register: %v", tt.name, err)
- }
-
- e := &countExporter{}
- RegisterExporter(e)
-
- stats.Record(ctx, m.M(1))
- stats.Record(ctx, m.M(1))
- stats.Record(ctx, m.M(1))
- stats.Record(ctx, m.M(1))
-
- time.Sleep(50 * time.Millisecond)
-
- stats.Record(ctx, m.M(1))
- stats.Record(ctx, m.M(1))
- stats.Record(ctx, m.M(1))
- stats.Record(ctx, m.M(1))
-
- time.Sleep(50 * time.Millisecond)
-
- e.Lock()
- count := e.count
- e.Unlock()
- if got, want := count, tt.wantMaxCount; got > want {
- t.Errorf("%v: got count data = %v; want at most %v", tt.name, got, want)
- }
- }
-
- }
-
- func Test_SetReportingPeriodReqNeverBlocks(t *testing.T) {
- t.Parallel()
-
- worker := newWorker()
- durations := []time.Duration{-1, 0, 10, 100 * time.Millisecond}
- for i, duration := range durations {
- ackChan := make(chan bool, 1)
- cmd := &setReportingPeriodReq{c: ackChan, d: duration}
- cmd.handleCommand(worker)
-
- select {
- case <-ackChan:
- case <-time.After(500 * time.Millisecond): // Arbitrarily using 500ms as the timeout duration.
- t.Errorf("#%d: duration %v blocks", i, duration)
- }
- }
- }
-
- func TestWorkerStarttime(t *testing.T) {
- restart()
-
- ctx := context.Background()
- m := stats.Int64("measure/TestWorkerStarttime", "desc", "unit")
- v := &View{
- Name: "testview",
- Measure: m,
- Aggregation: Count(),
- }
-
- SetReportingPeriod(25 * time.Millisecond)
- if err := Register(v); err != nil {
- t.Fatalf("cannot register to %v: %v", v.Name, err)
- }
-
- e := &vdExporter{}
- RegisterExporter(e)
- defer UnregisterExporter(e)
-
- stats.Record(ctx, m.M(1))
- stats.Record(ctx, m.M(1))
- stats.Record(ctx, m.M(1))
- stats.Record(ctx, m.M(1))
-
- time.Sleep(50 * time.Millisecond)
-
- stats.Record(ctx, m.M(1))
- stats.Record(ctx, m.M(1))
- stats.Record(ctx, m.M(1))
- stats.Record(ctx, m.M(1))
-
- time.Sleep(50 * time.Millisecond)
-
- e.Lock()
- if len(e.vds) == 0 {
- t.Fatal("Got no view data; want at least one")
- }
-
- var start time.Time
- for _, vd := range e.vds {
- if start.IsZero() {
- start = vd.Start
- }
- if !vd.Start.Equal(start) {
- t.Errorf("Cumulative view data start time = %v; want %v", vd.Start, start)
- }
- }
- e.Unlock()
- }
-
- func TestUnregisterReportsUsage(t *testing.T) {
- restart()
- ctx := context.Background()
-
- m1 := stats.Int64("measure", "desc", "unit")
- view1 := &View{Name: "count", Measure: m1, Aggregation: Count()}
- m2 := stats.Int64("measure2", "desc", "unit")
- view2 := &View{Name: "count2", Measure: m2, Aggregation: Count()}
-
- SetReportingPeriod(time.Hour)
-
- if err := Register(view1, view2); err != nil {
- t.Fatalf("cannot register: %v", err)
- }
-
- e := &countExporter{}
- RegisterExporter(e)
-
- stats.Record(ctx, m1.M(1))
- stats.Record(ctx, m2.M(1))
- stats.Record(ctx, m2.M(1))
-
- Unregister(view2)
-
- // Unregister should only flush view2, so expect the count of 2.
- want := int64(2)
-
- e.Lock()
- got := e.totalCount
- e.Unlock()
- if got != want {
- t.Errorf("got count data = %v; want %v", got, want)
- }
- }
-
- type countExporter struct {
- sync.Mutex
- count int64
- totalCount int64
- }
-
- func (e *countExporter) ExportView(vd *Data) {
- if len(vd.Rows) == 0 {
- return
- }
- d := vd.Rows[0].Data.(*CountData)
-
- e.Lock()
- defer e.Unlock()
- e.count = d.Value
- e.totalCount += d.Value
- }
-
- type vdExporter struct {
- sync.Mutex
- vds []*Data
- }
-
- func (e *vdExporter) ExportView(vd *Data) {
- e.Lock()
- defer e.Unlock()
-
- e.vds = append(e.vds, vd)
- }
-
- // restart stops the current processors and creates a new one.
- func restart() {
- defaultWorker.stop()
- defaultWorker = newWorker()
- go defaultWorker.start()
- }
|