From a188693015157e944e471f0660c22b91542a1b71 Mon Sep 17 00:00:00 2001 From: Tariq Ibrahim Date: Mon, 27 Aug 2018 07:55:49 -0700 Subject: [PATCH] Adding unit tests for the util package (#4534) Signed-off-by: Tariq Ibrahim --- util/httputil/compression_test.go | 176 ++++++++++++++++++++++++++++++ util/stats/stats_test.go | 59 ++++++++++ util/strutil/quote_test.go | 5 + util/strutil/strconv_test.go | 16 +++ util/testutil/context.go | 42 +++++++ 5 files changed, 298 insertions(+) create mode 100644 util/httputil/compression_test.go create mode 100644 util/testutil/context.go diff --git a/util/httputil/compression_test.go b/util/httputil/compression_test.go new file mode 100644 index 0000000000..e7901a8dad --- /dev/null +++ b/util/httputil/compression_test.go @@ -0,0 +1,176 @@ +// Copyright 2016 The Prometheus 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 httputil + +import ( + "bytes" + "compress/gzip" + "compress/zlib" + "io/ioutil" + "net/http" + "net/http/httptest" + "testing" +) + +var ( + mux *http.ServeMux + server *httptest.Server +) + +func setup() func() { + mux = http.NewServeMux() + server = httptest.NewServer(mux) + return func() { + server.Close() + } +} + +func getCompressionHandlerFunc() CompressionHandler { + hf := func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte("Hello World!")) + } + return CompressionHandler{ + Handler: http.HandlerFunc(hf), + } +} + +func TestCompressionHandler_PlainText(t *testing.T) { + tearDown := setup() + defer tearDown() + + ch := getCompressionHandlerFunc() + mux.Handle("/foo_endpoint", ch) + + client := &http.Client{ + Transport: &http.Transport{ + DisableCompression: true, + }, + } + + resp, err := client.Get(server.URL + "/foo_endpoint") + + if err != nil { + t.Error("client get failed with unexpected error") + } + defer resp.Body.Close() + contents, err := ioutil.ReadAll(resp.Body) + + if err != nil { + t.Errorf("unexpected error while reading the response body: %s", err.Error()) + } + + expected := "Hello World!" + actual := string(contents) + if expected != actual { + t.Errorf("expected response with content %s, but got %s", expected, actual) + } +} + +func TestCompressionHandler_Gzip(t *testing.T) { + tearDown := setup() + defer tearDown() + + ch := getCompressionHandlerFunc() + mux.Handle("/foo_endpoint", ch) + + client := &http.Client{ + Transport: &http.Transport{ + DisableCompression: true, + }, + } + + req, _ := http.NewRequest("GET", server.URL+"/foo_endpoint", nil) + req.Header.Set(acceptEncodingHeader, gzipEncoding) + + resp, err := client.Do(req) + if err != nil { + t.Error("client get failed with unexpected error") + } + defer resp.Body.Close() + + if err != nil { + t.Errorf("unexpected error while reading the response body: %s", err.Error()) + } + + actualHeader := resp.Header.Get(contentEncodingHeader) + + if actualHeader != gzipEncoding { + t.Errorf("expected response with encoding header %s, but got %s", gzipEncoding, actualHeader) + } + + var buf bytes.Buffer + zr, _ := gzip.NewReader(resp.Body) + + _, err = buf.ReadFrom(zr) + if err != nil { + t.Error("unexpected error while reading from response body") + } + + actual := string(buf.Bytes()) + expected := "Hello World!" + if expected != actual { + t.Errorf("expected response with content %s, but got %s", expected, actual) + } +} + +func TestCompressionHandler_Deflate(t *testing.T) { + tearDown := setup() + defer tearDown() + + ch := getCompressionHandlerFunc() + mux.Handle("/foo_endpoint", ch) + + client := &http.Client{ + Transport: &http.Transport{ + DisableCompression: true, + }, + } + + req, _ := http.NewRequest("GET", server.URL+"/foo_endpoint", nil) + req.Header.Set(acceptEncodingHeader, deflateEncoding) + + resp, err := client.Do(req) + if err != nil { + t.Error("client get failed with unexpected error") + } + defer resp.Body.Close() + + if err != nil { + t.Errorf("unexpected error while reading the response body: %s", err.Error()) + } + + actualHeader := resp.Header.Get(contentEncodingHeader) + + if actualHeader != deflateEncoding { + t.Errorf("expected response with encoding header %s, but got %s", deflateEncoding, actualHeader) + } + + var buf bytes.Buffer + dr, err := zlib.NewReader(resp.Body) + if err != nil { + t.Error("unexpected error while reading from response body") + } + + _, err = buf.ReadFrom(dr) + if err != nil { + t.Error("unexpected error while reading from response body") + } + + actual := string(buf.Bytes()) + expected := "Hello World!" + if expected != actual { + t.Errorf("expected response with content %s, but got %s", expected, actual) + } +} diff --git a/util/stats/stats_test.go b/util/stats/stats_test.go index 55f1e9428a..6a4c2cedca 100644 --- a/util/stats/stats_test.go +++ b/util/stats/stats_test.go @@ -15,6 +15,8 @@ package stats import ( "encoding/json" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/prometheus/util/testutil" "regexp" "testing" "time" @@ -59,3 +61,60 @@ func TestQueryStatsWithTimers(t *testing.T) { t.Fatalf("Expected timings with one non-zero entry, but got %s.", actual) } } + +func TestQueryStatsWithSpanTimers(t *testing.T) { + qt := NewQueryTimers() + ctx := &testutil.MockContext{DoneCh: make(chan struct{})} + qst, _ := qt.GetSpanTimer(ctx, ExecQueueTime, prometheus.NewSummary(prometheus.SummaryOpts{})) + time.Sleep(5 * time.Millisecond) + qst.Finish() + qs := NewQueryStats(qt) + actual, err := json.Marshal(qs) + if err != nil { + t.Fatalf("Unexpected error during serialization: %v", err) + } + // Timing value is one of multiple fields, unit is seconds (float). + match, err := regexp.MatchString(`[,{]"execQueueTime":\d+\.\d+[,}]`, string(actual)) + if err != nil { + t.Fatalf("Unexpected error while matching string: %v", err) + } + if !match { + t.Fatalf("Expected timings with one non-zero entry, but got %s.", actual) + } +} + +func TestTimerGroup(t *testing.T) { + tg := NewTimerGroup() + execTotalTimer := tg.GetTimer(ExecTotalTime) + if tg.GetTimer(ExecTotalTime).String() != "Exec total time: 0s" { + t.Fatalf("Expected string %s, but got %s", "", execTotalTimer.String()) + } + execQueueTimer := tg.GetTimer(ExecQueueTime) + if tg.GetTimer(ExecQueueTime).String() != "Exec queue wait time: 0s" { + t.Fatalf("Expected string %s, but got %s", "", execQueueTimer.String()) + } + innerEvalTimer := tg.GetTimer(InnerEvalTime) + if tg.GetTimer(InnerEvalTime).String() != "Inner eval time: 0s" { + t.Fatalf("Expected string %s, but got %s", "", innerEvalTimer.String()) + } + queryPreparationTimer := tg.GetTimer(QueryPreparationTime) + if tg.GetTimer(QueryPreparationTime).String() != "Query preparation time: 0s" { + t.Fatalf("Expected string %s, but got %s", "", queryPreparationTimer.String()) + } + resultSortTimer := tg.GetTimer(ResultSortTime) + if tg.GetTimer(ResultSortTime).String() != "Result sorting time: 0s" { + t.Fatalf("Expected string %s, but got %s", "", resultSortTimer.String()) + } + evalTotalTimer := tg.GetTimer(EvalTotalTime) + if tg.GetTimer(EvalTotalTime).String() != "Eval total time: 0s" { + t.Fatalf("Expected string %s, but got %s", "", evalTotalTimer.String()) + } + + actual := tg.String() + expected := "Exec total time: 0s\nExec queue wait time: 0s\nInner eval time: 0s\nQuery preparation time: 0s\nResult sorting time: 0s\nEval total time: 0s\n" + + if actual != expected { + t.Fatalf("Expected timerGroup string %s, but got %s.", expected, actual) + } + +} diff --git a/util/strutil/quote_test.go b/util/strutil/quote_test.go index 0068ada0d2..32aea0940c 100644 --- a/util/strutil/quote_test.go +++ b/util/strutil/quote_test.go @@ -56,6 +56,7 @@ var unquotetests = []unQuoteTest{ {`'abc'`, "abc"}, {`'☺'`, "☺"}, {`'hello world'`, "hello world"}, + {`'\ahéllo world'`, "\ahéllo world"}, {`'\xFF'`, "\xFF"}, {`'\377'`, "\377"}, {`'\u1234'`, "\u1234"}, @@ -101,6 +102,10 @@ var misquoted = []string{ "\"\n\"", "\"\\n\n\"", "'\n'", + "`1`9`", + `1231`, + `'\xF'`, + `""12345"`, } func TestUnquote(t *testing.T) { diff --git a/util/strutil/strconv_test.go b/util/strutil/strconv_test.go index acbd092959..e47bb45e76 100644 --- a/util/strutil/strconv_test.go +++ b/util/strutil/strconv_test.go @@ -47,3 +47,19 @@ func TestLink(t *testing.T) { } } } + +func TestSanitizeLabelName(t *testing.T) { + actual := SanitizeLabelName("fooClientLABEL") + expected := "fooClientLABEL" + + if actual != expected { + t.Errorf("SanitizeLabelName failed for label (%s), want %s got %s", "fooClientLABEL", expected, actual) + } + + actual = SanitizeLabelName("barClient.LABEL$$##") + expected = "barClient_LABEL____" + + if actual != expected { + t.Errorf("SanitizeLabelName failed for label (%s), want %s got %s", "barClient.LABEL$$##", expected, actual) + } +} diff --git a/util/testutil/context.go b/util/testutil/context.go new file mode 100644 index 0000000000..cf730421b4 --- /dev/null +++ b/util/testutil/context.go @@ -0,0 +1,42 @@ +// Copyright 2016 The Prometheus 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 testutil + +import "time" + +// A MockContext provides a simple stub implementation of a Context +type MockContext struct { + Error error + DoneCh chan struct{} +} + +// Deadline always will return not set +func (c *MockContext) Deadline() (deadline time.Time, ok bool) { + return time.Time{}, false +} + +// Done returns a read channel for listening to the Done event +func (c *MockContext) Done() <-chan struct{} { + return c.DoneCh +} + +// Err returns the error, is nil if not set. +func (c *MockContext) Err() error { + return c.Error +} + +// Value ignores the Value and always returns nil +func (c *MockContext) Value(key interface{}) interface{} { + return nil +}