mirror of
https://github.com/Luzifer/nginx-sso.git
synced 2024-12-21 05:11:17 +00:00
435 lines
8.6 KiB
Go
435 lines
8.6 KiB
Go
// 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()
|
|
}
|