func TestExecutor(t *testing.T) { tests := []struct { desc string err bool shouldLog bool log []string }{ { desc: "With error in state machine execution", err: true, }, { desc: "Success", shouldLog: true, log: []string{ "StateMachine[tester]: StateFn(Start) starting", "StateMachine[tester]: StateFn(Start) finished", "StateMachine[tester]: StateFn(Middle) starting", "StateMachine[tester]: StateFn(Middle) finished", "StateMachine[tester]: StateFn(End) starting", "StateMachine[tester]: StateFn(End) finished", "StateMachine[tester]: Execute() completed with no issues", "StateMachine[tester]: The following is the StateFn's called with this execution:", "StateMachine[tester]: \tStart", "StateMachine[tester]: \tMiddle", "StateMachine[tester]: \tEnd", }, }, } sm := &StateMachine{} for _, test := range tests { sm.err = test.err l := &logging{} exec := New("tester", sm.Start, Reset(sm.reset), LogFacility(l.Log)) if test.shouldLog { exec.Log(true) } else { exec.Log(false) } err := exec.Execute() switch { case err == nil && test.err: t.Errorf("Test %q: got err == nil, want err != nil", test.desc) continue case err != nil && !test.err: t.Errorf("Test %q: got err != %q, want err == nil", test.desc, err) continue } if diff := pretty.Diff(sm.callTrace, exec.Nodes()); len(diff) != 0 { t.Errorf("Test %q: node trace was no accurate got/want diff:\n%s", test.desc, strings.Join(diff, "\n")) } if diff := pretty.Diff(l.msgs, test.log); len(diff) != 0 { t.Errorf("Test %q: log was not as expected:\n%s", test.desc, strings.Join(diff, "\n")) } } }
func TestHistogramPrometheus(t *testing.T) { u := func(v int) *uint64 { n := uint64(v) return &n } f := func(v int) *float64 { n := float64(v) return &n } h := NewHistogram(Metadata{}, time.Hour, 10, 1) h.RecordValue(1) h.RecordValue(5) h.RecordValue(5) h.RecordValue(10) h.RecordValue(15000) // counts as 10 act := *h.ToPrometheusMetric().Histogram expSum := float64(1*1 + 2*5 + 2*10) exp := prometheusgo.Histogram{ SampleCount: u(5), SampleSum: &expSum, Bucket: []*prometheusgo.Bucket{ {CumulativeCount: u(1), UpperBound: f(1)}, {CumulativeCount: u(3), UpperBound: f(5)}, {CumulativeCount: u(5), UpperBound: f(10)}, }, } if !reflect.DeepEqual(act, exp) { t.Fatalf("expected differs from actual: %s", pretty.Diff(exp, act)) } }
func testOne(t *testing.T, input []byte) { templ := Template{} err := json.Unmarshal(input, &templ) if err != nil { t.Errorf("decode: %s", err) return } output, err := json.Marshal(templ) if err != nil { t.Errorf("marshal: %s", err) return } parsedInput := map[string]interface{}{} json.Unmarshal(input, &parsedInput) parsedOutput := map[string]interface{}{} json.Unmarshal(output, &parsedOutput) diffs := pretty.Diff(parsedInput, parsedOutput) for _, diff := range diffs { t.Errorf("%s", diff) } }
func update_local_config_handler(w http.ResponseWriter, req *http.Request, strings ...string) Reply { conf := &config.ProxyConfig{} err := conf.LoadIO(req.Body) if err != nil { err = errors.NewKeyError(req.URL.String(), http.StatusBadRequest, fmt.Sprintf("conf: could not parse config: %q", err)) return Reply{ err: err, status: http.StatusBadRequest, } } conf.Proxy.RedirectToken = proxy.bctl.Conf.Proxy.RedirectToken diff := pretty.Diff(proxy.bctl.Conf, conf) for _, d := range diff { log.Printf("update_local_config_handler: diff: %s\n", d) } proxy.bctl.Lock() proxy.bctl.Conf = conf proxy.bctl.DisableConfigUpdateUntil = time.Now().Add(time.Second * time.Duration(conf.Proxy.DisableConfigUpdateForSeconds)) proxy.bctl.Unlock() log.Printf("update_local_config_handler: next automatic config update is only allowed in %d seconds at %s\n", conf.Proxy.DisableConfigUpdateForSeconds, proxy.bctl.DisableConfigUpdateUntil.String()) return GoodReply() }
func TestSpec(t *testing.T) { bs, err := ioutil.ReadFile("spec.json") if err != nil { t.Error("Unable to open the spec.") } var spec Spec err = json.Unmarshal(bs, &spec) if err != nil { t.Error("JSON format was wrong", err) } i := 0 failed := 0 for i < len(spec.Examples) { example := spec.Examples[i] renderedHTML := RenderHTML(Parse(example.Markdown)) if renderedHTML != example.HTML { failed = failed + 1 t.Error("===== " + example.Name + " failed. =====") t.Error("===== MARKDOWN =====") t.Error(strings.Replace(example.Markdown, "\t", "→", -1)) t.Error("===== EXPECTED HTML =====") t.Error(example.HTML) t.Error("===== GOT HTML =====") t.Error(renderedHTML) t.Error(pretty.Diff(example.HTML, renderedHTML)) t.Error("\n\n") } i = i + 1 } passed := len(spec.Examples) - failed t.Log("Passed: ", passed, "/", len(spec.Examples)) }
func TestGetRateSummary(t *testing.T) { server, zillow := testFixtures(t, rateSummaryPath, func(values url.Values) { assertOnlyParam(t, values, stateParam, state) }) defer server.Close() request := RateSummaryRequest{State: state} result, err := zillow.GetRateSummary(request) if err != nil { t.Fatal(err) } expected := &RateSummary{ XMLName: xml.Name{Space: "http://www.zillow.com/static/xsd/RateSummary.xsd", Local: "rateSummary"}, Message: Message{ Text: "Request successfully processed", Code: 0, }, Today: []Rate{ Rate{LoanType: "thirtyYearFixed", Count: 1252, Value: 5.91}, Rate{LoanType: "fifteenYearFixed", Count: 839, Value: 5.68}, Rate{LoanType: "fiveOneARM", Count: 685, Value: 5.49}, }, LastWeek: []Rate{ Rate{LoanType: "thirtyYearFixed", Count: 8933, Value: 6.02}, Rate{LoanType: "fifteenYearFixed", Count: 5801, Value: 5.94}, Rate{LoanType: "fiveOneARM", Count: 3148, Value: 5.71}, }, } if !reflect.DeepEqual(result, expected) { t.Fatalf("expected:\n %#v\n\n but got:\n %#v\n\n diff:\n %s\n", pretty.Formatter(expected), pretty.Formatter(result), pretty.Diff(expected, result)) } }
func TestGetChart(t *testing.T) { server, zillow := testFixtures(t, chartPath, func(values url.Values) { assertOnlyParam(t, values, zpidParam, zpid) assertOnlyParam(t, values, unitTypeParam, unitType) assertOnlyParam(t, values, widthParam, strconv.Itoa(width)) assertOnlyParam(t, values, heightParam, strconv.Itoa(height)) }) defer server.Close() request := ChartRequest{Zpid: zpid, UnitType: unitType, Width: width, Height: height} result, err := zillow.GetChart(request) if err != nil { t.Fatal(err) } expected := &ChartResult{ XMLName: xml.Name{Space: "http://www.zillowstatic.com/vstatic/8d9b5f1/static/xsd/Chart.xsd", Local: "chart"}, Request: request, Message: Message{ Text: "Request successfully processed", Code: 0, }, Url: "http://www.zillow.com/app?chartDuration=1year&chartType=partner&height=150&page=webservice%2FGetChart&service=chart&showPercent=true&width=300&zpid=48749425", } if !reflect.DeepEqual(result, expected) { t.Fatalf("expected:\n %#v\n\n but got:\n %#v\n\n diff:\n %s\n", pretty.Formatter(expected), pretty.Formatter(result), pretty.Diff(expected, result)) } }
func TestUnmarshalJSON(t *testing.T) { type unmarshalJSONTest struct { dir string } tests := []unmarshalJSONTest{ {"testdata"}, } for _, test := range tests { units, err := Default.Scan(test.dir) if err != nil { t.Errorf("scan error: %s", err) continue } for _, unit := range units { data, err := json.Marshal(unit) if err != nil { t.Errorf("marshal error: %s", err) continue } unit2, err := UnmarshalJSON(data, UnitType(unit)) if err != nil { t.Errorf("UnmarshalJSON error: %s", err) continue } if !reflect.DeepEqual(unit, unit2) { t.Errorf("unit != unit2:\n%+v\n%+v\n%v", unit, unit2, strings.Join(pretty.Diff(unit, unit2), "\n")) } } } }
func TestMarshalableUnit(t *testing.T) { type unmarshalJSONTest struct { dir string } tests := []unmarshalJSONTest{ {"testdata"}, } for _, test := range tests { units, err := Default.Scan(test.dir) if err != nil { t.Errorf("scan error: %s", err) continue } for _, unit := range units { mu := &MarshalableUnit{unit} data, err := json.Marshal(mu) if err != nil { t.Errorf("marshal error: %s", err) continue } var mu2 *MarshalableUnit err = json.Unmarshal(data, &mu2) if err != nil { t.Errorf("Unmarshal error: %s", err) continue } if !reflect.DeepEqual(mu, mu2) { t.Errorf("mu != mu2:\n%+v\n%+v\n%v", mu, mu2, strings.Join(pretty.Diff(mu, mu2), "\n")) } } } }
func assertEqual(t *testing.T, expected, actual interface{}, message string) { if expected != actual { t.Error(message) for _, desc := range pretty.Diff(expected, actual) { t.Error(desc) } } }
func TestDeltas(t *testing.T) { tests := []struct { spec DeltaSpec wantRouteVars map[string]string }{ { spec: DeltaSpec{ Base: RepoRevSpec{RepoSpec: RepoSpec{URI: "samerepo"}, Rev: "baserev", CommitID: baseCommit}, Head: RepoRevSpec{RepoSpec: RepoSpec{URI: "samerepo"}, Rev: "headrev", CommitID: headCommit}, }, wantRouteVars: map[string]string{ "Repo": "samerepo", "Rev": baseRev.Rev, "CommitID": baseCommit, "DeltaHeadResolvedRev": "headrev===" + headCommit, }, }, { spec: DeltaSpec{ Base: baseRev, Head: headRev, }, wantRouteVars: map[string]string{ "Repo": "base.com/repo", "Rev": baseRev.Rev, "CommitID": baseCommit, "DeltaHeadResolvedRev": encodeCrossRepoRevSpecForDeltaHeadResolvedRev(headRev), }, }, } for _, test := range tests { vars := test.spec.RouteVars() if !reflect.DeepEqual(vars, test.wantRouteVars) { t.Errorf("got route vars != want\n\n%s", strings.Join(pretty.Diff(vars, test.wantRouteVars), "\n")) } spec, err := UnmarshalDeltaSpec(vars) if err != nil { t.Errorf("UnmarshalDeltaSpec(%+v): %s", vars, err) continue } if !reflect.DeepEqual(spec, test.spec) { t.Errorf("got spec != original spec\n\n%s", strings.Join(pretty.Diff(spec, test.spec), "\n")) } } }
func assertStartsWith(t *testing.T, strVal, prefix string, message string) { if strings.Index(strVal, prefix) == -1 { t.Error(message) for _, desc := range pretty.Diff(prefix, strVal[:len(prefix)]) { t.Error(desc) } } }
func assertEq(a assertable, expected, got interface{}, args ...interface{}) { if !reflect.DeepEqual(expected, got) { fatal(a, args, "\tEXPECTED: %v\n\tGOT: %v\n\tDIFF: %v", expected, got, fmt.Diff(expected, got)) } }
func equal(t *testing.T, expected, got interface{}, callDepth int, messages ...interface{}) { fn := func() { for _, desc := range pretty.Diff(expected, got) { t.Error(errorPrefix, desc) } if len(messages) > 0 { t.Error(errorPrefix, "-", fmt.Sprint(messages...)) } } assert(t, isEqual(expected, got), fn, callDepth+1) }
func TestGolangBackend(t *testing.T) { dir, err := ioutil.TempDir("", "gencode") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) } defer os.RemoveAll(dir) for _, tc := range []string{ "array.schema", "int.schema", } { inputF := filepath.Join("./testdata", tc) outputF := filepath.Join(dir, tc+".go") goldenF := inputF + ".golden.go" in, err := ioutil.ReadFile(inputF) if err != nil { t.Fatalf("%v: Failed to read: %v", tc, err) } s, err := schema.ParseSchema(bytes.NewReader(in)) if err != nil { t.Fatalf("%v: Failed schema.ParseSchema: %v", tc, err) } b := GolangBackend{Package: "testdata"} g, err := b.Generate(s) if err != nil { t.Fatalf("%v: Failed Generate: %v", tc, err) } out := []byte(g) if err = ioutil.WriteFile(outputF, out, 0777); err != nil { t.Fatalf("%v: Failed to write generated file: %v", tc, err) } if out, err := exec.Command("go", "build", outputF).CombinedOutput(); err != nil { t.Fatalf("%v: Failed to compile generated code (error: %v):\n%s", tc, err, out) } want, err := ioutil.ReadFile(goldenF) needUpdate := true if err != nil { t.Errorf("%v: Failed to read golden file: %v", tc, err) } else if diff := pretty.Diff(want, out); len(diff) != 0 { t.Errorf("%v: Diff(want, got) = %v", tc, strings.Join(diff, "\n")) } else { needUpdate = false } if needUpdate && *update { if err := ioutil.WriteFile(goldenF, out, 0777); err != nil { t.Errorf("%v: Failed to update golden file: %v", tc, err) } } } }
func equal(t *testing.T, exp, got interface{}, cd int, args ...interface{}) { fn := func() { for _, desc := range pretty.Diff(exp, got) { t.Error("!", desc) } if len(args) > 0 { t.Error("!", " -", fmt.Sprint(args...)) } } result := reflect.DeepEqual(exp, got) assert(t, result, fn, cd+1) }
func TestAExpr(t *testing.T) { for _, test := range aExprTests { var actualTree nodes.A_Expr err := json.Unmarshal([]byte(test.jsonText), &actualTree) if err != nil { t.Errorf("Unmarshal(%s)\nerror %s\n\n", test.jsonText, err) } else if !reflect.DeepEqual(actualTree, test.expectedNode) { t.Errorf("Unmarshal(%s)\ndiff %s\n\n", test.jsonText, pretty.Diff(test.expectedNode, actualTree)) } } }
func compareJson(a, b []byte) ([]string, error) { oa := make(map[string]interface{}) if err := json.Unmarshal(a, &oa); err != nil { return nil, maskAny(err) } ob := make(map[string]interface{}) if err := json.Unmarshal(b, &ob); err != nil { return nil, maskAny(err) } diffs := pretty.Diff(oa, ob) return diffs, nil }
func TestGetMonthlyPayments(t *testing.T) { server, zillow := testFixtures(t, monthlyPaymentsPath, func(values url.Values) { assertOnlyParam(t, values, priceParam, strconv.Itoa(price)) assertOnlyParam(t, values, downParam, strconv.Itoa(down)) assertOnlyParam(t, values, zipParam, zip) }) defer server.Close() request := MonthlyPaymentsRequest{Price: price, Down: down, Zip: zip} result, err := zillow.GetMonthlyPayments(request) if err != nil { t.Fatal(err) } expected := &MonthlyPayments{ XMLName: xml.Name{Space: "http://www.zillow.com/static/xsd/MonthlyPayments.xsd", Local: "paymentsSummary"}, Request: request, Message: Message{ Text: "Request successfully processed", Code: 0, }, Payments: []Payment{ { LoanType: "thirtyYearFixed", Rate: 5.9, MonthlyPrincipalAndInterest: 1512, MonthlyMortgageInsurance: 68, }, { LoanType: "fifteenYearFixed", Rate: 5.68, MonthlyPrincipalAndInterest: 1477, MonthlyMortgageInsurance: 68, }, { LoanType: "fiveOneARM", Rate: 5.71, MonthlyPrincipalAndInterest: 1482, MonthlyMortgageInsurance: 74, }, }, DownPayment: 45000, MonthlyPropertyTaxes: 193, MonthlyHazardInsurance: 49, } if !reflect.DeepEqual(result, expected) { t.Fatalf("expected:\n %#v\n\n but got:\n %#v\n\n diff:\n %s\n", pretty.Formatter(expected), pretty.Formatter(result), pretty.Diff(expected, result)) } }
func TestProducer(t *testing.T) { stack := new(mango.Stack) handler := stack.HandlerFunc(pact.Producer) testflight.WithServer(handler, func(r *testflight.Requester) { pact_str, err := ioutil.ReadFile("../pacts/my_consumer-my_producer.json") if err != nil { t.Error(err) } pacts := make(map[string]interface{}) err = json.Unmarshal(pact_str, &pacts) if err != nil { t.Error(err) } for _, i := range pacts["interactions"].([]interface{}) { interaction := i.(map[string]interface{}) t.Logf("Given %s", interaction["producer_state"]) t.Logf(" %s", interaction["description"]) request := interaction["request"].(map[string]interface{}) var actualResponse *testflight.Response switch request["method"] { case "get": actualResponse = r.Get(request["path"].(string) + "?" + request["query"].(string)) } expectedResponse := interaction["response"].(map[string]interface{}) assert.Equal(t, int(expectedResponse["status"].(float64)), actualResponse.StatusCode) for k, v := range expectedResponse["headers"].(map[string]interface{}) { assert.Equal(t, v, actualResponse.RawResponse.Header[k][0]) } responseBody := make(map[string]interface{}) err = json.Unmarshal([]byte(actualResponse.Body), &responseBody) if err != nil { t.Error(err) } for _, diff := range pretty.Diff(expectedResponse["body"], responseBody) { t.Log(diff) } assert.Equal(t, expectedResponse["body"], responseBody) } }) }
func assert(stmt *Stmt) (func(*Context) (interface{}, error), error) { return func(context *Context) (interface{}, error) { expect := stmt.Arg("expect", context) actual := stmt.Arg("actual", context) if !reflect.DeepEqual(expect, actual) { return nil, fmt.Errorf("%s:%d error expected: '%v', actual: '%v' \n%v", stmt.File, stmt.Line, expect, actual, strings.Join(pretty.Diff(expect, actual), "\n"), ) } return nil, nil }, nil }
func assertParseCases(t *testing.T, tests []parseCase) { for _, tt := range tests { program, err := Parse([]byte(tt.document)) if assert.NoError(t, err, "Parsing failed:\n%s", tt.document) { succ := assert.Equal( t, tt.program, program, "Got unexpected program when parsing:\n%s", tt.document, ) if !succ { lines := pretty.Diff(tt.program, program) t.Log("\n\t" + strings.Join(lines, "\n\t")) } } } }
func TestModelsInfo(t *testing.T) { for _, testCase := range modelsInfoTestCases { gen := Generator{ gen: nil, api: testCase.api, config: Config{}, } gen.extractModelsInfo() diffs := pretty.Diff(testCase.expectedModelsInfo, gen.modelsInfo) if len(diffs) > 0 { t.Errorf(failModelsInfoFormat, testCase.name, tests.FormattedDiff(diffs)) } } }
func singleParse(in string, want *File, mode ParseMode) func(t *testing.T) { return func(t *testing.T) { got, err := Parse([]byte(in), "", mode) if err != nil { t.Fatalf("Unexpected error in %q: %v", in, err) } checkNewlines(t, in, got.Lines) got.Lines = nil setPosRecurse(t, "", want, 0, false) setPosRecurse(t, in, got, 0, true) if !reflect.DeepEqual(got, want) { t.Fatalf("AST mismatch in %q\ndiff:\n%s", in, strings.Join(pretty.Diff(want, got), "\n"), ) } } }
func TestParse(t *testing.T) { for _, test := range parseTests { actualJSON, err := pg_query.ParseToJSON(test.input) if err != nil { t.Errorf("Parse(%s)\nerror %s\n\n", test.input, err) } else if actualJSON != test.expectedJSON { t.Errorf("Parse(%s)\nexpected %s\nactual %s\n\n", test.input, test.expectedJSON, actualJSON) } actualTree, err := pg_query.Parse(test.input) if err != nil { t.Errorf("Unmarshal(%s)\nerror %s\n\n", actualJSON, err) } else if !reflect.DeepEqual(actualTree, test.expectedTree) { t.Errorf("Unmarshal(%s)\ndiff %s\n\n", actualJSON, pretty.Diff(test.expectedTree, actualTree)) } } }
func TestGetRegionChart(t *testing.T) { server, zillow := testFixtures(t, regionChartPath, func(values url.Values) { assertOnlyParam(t, values, cityParam, city) assertOnlyParam(t, values, stateParam, state) assertOnlyParam(t, values, unitTypeParam, unitType) assertOnlyParam(t, values, widthParam, strconv.Itoa(width)) assertOnlyParam(t, values, heightParam, strconv.Itoa(height)) }) defer server.Close() request := RegionChartRequest{ City: city, State: state, UnitType: unitType, Width: width, Height: height, } result, err := zillow.GetRegionChart(request) if err != nil { t.Fatal(err) } expected := &RegionChartResult{ XMLName: xml.Name{Space: "http://www.zillow.com/static/xsd/RegionChart.xsd", Local: "regionchart"}, Request: request, Message: Message{ Text: "Request successfully processed", Code: 0, }, Url: "http://localhost:8080/app?chartDuration=1year&chartType=partner&cityRegionId=5470&countyRegionId=0&height=150&nationRegionId=0&page=webservice%2FGetRegionChart&service=chart&showCity=true&showPercent=true&stateRegionId=0&width=300&zipRegionId=0", Zindex: Value{Currency: "USD", Value: 463115}, } if !reflect.DeepEqual(result, expected) { t.Fatalf("expected:\n %#v\n\n but got:\n %#v\n\n diff:\n %s\n", pretty.Formatter(expected), pretty.Formatter(result), pretty.Diff(expected, result)) } }
func TestAnnotate(t *testing.T) { src := []byte(`a:=2`) want := annotate.Annotations{ {Start: 0, End: 1, Left: []byte(`<span class="pln">`), Right: []byte("</span>")}, {Start: 1, End: 2, Left: []byte(`<span class="pun">`), Right: []byte("</span>")}, {Start: 2, End: 3, Left: []byte(`<span class="pun">`), Right: []byte("</span>")}, {Start: 3, End: 4, Left: []byte(`<span class="dec">`), Right: []byte("</span>")}, } got, err := Annotate(src, HTMLAnnotator(DefaultHTMLConfig)) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(want, got) { t.Errorf("want %# v, got %# v\n\ndiff:\n%v", pretty.Formatter(want), pretty.Formatter(got), strings.Join(pretty.Diff(got, want), "\n")) for _, g := range got { t.Logf("%+v %q LEFT=%q RIGHT=%q", g, src[g.Start:g.End], g.Left, g.Right) } } }
// Verify that a log can be fetched in JSON format. func TestEntryDecoder(t *testing.T) { formatEntry := func(s Severity, now time.Time, file string, line int, msg string) string { buf := formatHeader(s, now, file, line, nil) buf.WriteString(msg) buf.WriteString("\n") defer logging.putBuffer(buf) return buf.String() } t1 := time.Now().Round(time.Microsecond) t2 := t1.Add(time.Microsecond) t3 := t2.Add(time.Microsecond) t4 := t3.Add(time.Microsecond) contents := formatEntry(InfoLog, t1, "clog_test.go", 136, "info") contents += formatEntry(WarningLog, t2, "clog_test.go", 137, "warning") contents += formatEntry(ErrorLog, t3, "clog_test.go", 138, "error") contents += formatEntry(FatalLog, t4, "clog_test.go", 139, "fatal") readAllEntries := func(contents string) []Entry { decoder := NewEntryDecoder(strings.NewReader(contents)) var entries []Entry var entry Entry for { if err := decoder.Decode(&entry); err != nil { if err == io.EOF { break } t.Fatal(err) } entries = append(entries, entry) } return entries } entries := readAllEntries(contents) expected := []Entry{ { Severity: 0, Time: t1.UnixNano(), File: `clog_test.go`, Line: 136, Message: `info`, }, { Severity: 1, Time: t2.UnixNano(), File: `clog_test.go`, Line: 137, Message: `warning`, }, { Severity: 2, Time: t3.UnixNano(), File: `clog_test.go`, Line: 138, Message: `error`, }, { Severity: 3, Time: t4.UnixNano(), File: `clog_test.go`, Line: 139, Message: `fatal`, }, } if !reflect.DeepEqual(expected, entries) { t.Fatalf("%s\n", strings.Join(pretty.Diff(expected, entries), "\n")) } entries = readAllEntries("file header\n\n\n" + contents) if !reflect.DeepEqual(expected, entries) { t.Fatalf("%s\n", strings.Join(pretty.Diff(expected, entries), "\n")) } }
// TestMetricsRecorder verifies that the metrics recorder properly formats the // statistics from various registries, both for Time Series and for Status // Summaries. func TestMetricsRecorder(t *testing.T) { defer leaktest.AfterTest(t)() // ======================================== // Construct a series of fake descriptors for use in test. // ======================================== nodeDesc := roachpb.NodeDescriptor{ NodeID: roachpb.NodeID(1), } storeDesc1 := roachpb.StoreDescriptor{ StoreID: roachpb.StoreID(1), Capacity: roachpb.StoreCapacity{ Capacity: 100, Available: 50, }, } storeDesc2 := roachpb.StoreDescriptor{ StoreID: roachpb.StoreID(2), Capacity: roachpb.StoreCapacity{ Capacity: 200, Available: 75, }, } // ======================================== // Create registries and add them to the recorder (two node-level, two // store-level). // ======================================== reg1 := metric.NewRegistry() reg2 := metric.NewRegistry() store1 := fakeStore{ storeID: roachpb.StoreID(1), desc: storeDesc1, registry: metric.NewRegistry(), } store2 := fakeStore{ storeID: roachpb.StoreID(2), desc: storeDesc2, registry: metric.NewRegistry(), } manual := hlc.NewManualClock(100) recorder := NewMetricsRecorder(hlc.NewClock(manual.UnixNano)) recorder.AddNodeRegistry("one.%s", reg1) recorder.AddNodeRegistry("two.%s", reg1) recorder.AddStore(store1) recorder.AddStore(store2) recorder.NodeStarted(nodeDesc, 50) // Ensure the metric system's view of time does not advance during this test // as the test expects time to not advance too far which would age the actual // data (e.g. in histogram's) unexpectedly. defer metric.TestingSetNow(func() time.Time { return time.Unix(0, manual.UnixNano()).UTC() })() // ======================================== // Generate Metrics Data & Expected Results // ======================================== // Flatten the four registries into an array for ease of use. regList := []struct { reg *metric.Registry prefix string source int64 isNode bool }{ { reg: reg1, prefix: "one.", source: 1, isNode: true, }, { reg: reg2, prefix: "two.", source: 1, isNode: true, }, { reg: store1.registry, prefix: "", source: int64(store1.storeID), isNode: false, }, { reg: store2.registry, prefix: "", source: int64(store2.storeID), isNode: false, }, } // Every registry will have a copy of the following metrics. metricNames := []struct { name string typ string val int64 }{ {"testGauge", "gauge", 20}, {"testGaugeFloat64", "floatgauge", 20}, {"testCounter", "counter", 5}, {"testRate", "rate", 2}, {"testHistogram", "histogram", 10}, {"testLatency", "latency", 10}, // Stats needed for store summaries. {"ranges", "counter", 1}, {"ranges.leader", "gauge", 1}, {"ranges.replicated", "gauge", 1}, {"ranges.available", "gauge", 1}, } // Add the metrics to each registry and set their values. At the same time, // generate expected time series results and status summary metric values. var expected []tspb.TimeSeriesData expectedNodeSummaryMetrics := make(map[string]float64) expectedStoreSummaryMetrics := make(map[string]float64) // addExpected generates expected data for a single metric data point. addExpected := func(prefix, name string, source, time, val int64, isNode bool) { // Generate time series data. tsPrefix := "cr.node." if !isNode { tsPrefix = "cr.store." } expect := tspb.TimeSeriesData{ Name: tsPrefix + prefix + name, Source: strconv.FormatInt(source, 10), Datapoints: []tspb.TimeSeriesDatapoint{ { TimestampNanos: time, Value: float64(val), }, }, } expected = append(expected, expect) // Generate status summary data. if isNode { expectedNodeSummaryMetrics[prefix+name] = float64(val) } else { // This can overwrite the previous value, but this is expected as // all stores in our tests have identical values; when comparing // status summaries, the same map is used as expected data for all // stores. expectedStoreSummaryMetrics[prefix+name] = float64(val) } } for _, reg := range regList { for _, data := range metricNames { switch data.typ { case "gauge": reg.reg.Gauge(data.name).Update(data.val) addExpected(reg.prefix, data.name, reg.source, 100, data.val, reg.isNode) case "floatgauge": reg.reg.GaugeFloat64(data.name).Update(float64(data.val)) addExpected(reg.prefix, data.name, reg.source, 100, data.val, reg.isNode) case "counter": reg.reg.Counter(data.name).Inc(data.val) addExpected(reg.prefix, data.name, reg.source, 100, data.val, reg.isNode) case "rate": reg.reg.Rates(data.name).Add(data.val) addExpected(reg.prefix, data.name+"-count", reg.source, 100, data.val, reg.isNode) for _, scale := range metric.DefaultTimeScales { // Rate data is subject to timing errors in tests. Zero out // these values. addExpected(reg.prefix, data.name+sep+scale.Name(), reg.source, 100, 0, reg.isNode) } case "histogram": reg.reg.Histogram(data.name, time.Second, 1000, 2).RecordValue(data.val) for _, q := range recordHistogramQuantiles { addExpected(reg.prefix, data.name+q.suffix, reg.source, 100, data.val, reg.isNode) } case "latency": reg.reg.Latency(data.name).RecordValue(data.val) // Latency is simply three histograms (at different resolution // time scales). for _, scale := range metric.DefaultTimeScales { for _, q := range recordHistogramQuantiles { addExpected(reg.prefix, data.name+sep+scale.Name()+q.suffix, reg.source, 100, data.val, reg.isNode) } } } } } // ======================================== // Verify time series data // ======================================== actual := recorder.GetTimeSeriesData() // Zero-out timing-sensitive rate values from actual data. for _, act := range actual { match, err := regexp.MatchString(`testRate-\d+m`, act.Name) if err != nil { t.Fatal(err) } if match { act.Datapoints[0].Value = 0.0 } } // Actual comparison is simple: sort the resulting arrays by time and name, // and use reflect.DeepEqual. sort.Sort(byTimeAndName(actual)) sort.Sort(byTimeAndName(expected)) if a, e := actual, expected; !reflect.DeepEqual(a, e) { t.Errorf("recorder did not yield expected time series collection; diff:\n %v", pretty.Diff(e, a)) } // ======================================== // Verify node summary generation // ======================================== expectedNodeSummary := &NodeStatus{ Desc: nodeDesc, BuildInfo: build.GetInfo(), StartedAt: 50, UpdatedAt: 100, Metrics: expectedNodeSummaryMetrics, StoreStatuses: []StoreStatus{ { Desc: storeDesc1, Metrics: expectedStoreSummaryMetrics, }, { Desc: storeDesc2, Metrics: expectedStoreSummaryMetrics, }, }, } nodeSummary := recorder.GetStatusSummary() if nodeSummary == nil { t.Fatalf("recorder did not return nodeSummary.") } sort.Sort(byStoreDescID(nodeSummary.StoreStatuses)) if a, e := nodeSummary, expectedNodeSummary; !reflect.DeepEqual(a, e) { t.Errorf("recorder did not produce expected NodeSummary; diff:\n %v", pretty.Diff(e, a)) } }
func Diff(a, b interface{}) []string { return pretty.Diff(a, b) }