mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
add functions/vendor files
This commit is contained in:
81
vendor/github.com/cactus/go-statsd-client/statsd/statsdtest/recorder.go
generated
vendored
Normal file
81
vendor/github.com/cactus/go-statsd-client/statsd/statsdtest/recorder.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
package statsdtest
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// RecordingSender implements statsd.Sender but parses individual Stats into a
|
||||
// buffer that can be later inspected instead of sending to some server. It
|
||||
// should constructed with NewRecordingSender().
|
||||
type RecordingSender struct {
|
||||
m sync.Mutex
|
||||
buffer Stats
|
||||
closed bool
|
||||
}
|
||||
|
||||
// NewRecordingSender creates a new RecordingSender for use by a statsd.Client.
|
||||
func NewRecordingSender() *RecordingSender {
|
||||
rs := &RecordingSender{}
|
||||
rs.buffer = make(Stats, 0)
|
||||
return rs
|
||||
}
|
||||
|
||||
// GetSent returns the stats that have been sent. Locks and copies the current
|
||||
// state of the sent Stats.
|
||||
//
|
||||
// The entire buffer of Stat objects (including Stat.Raw is copied).
|
||||
func (rs *RecordingSender) GetSent() Stats {
|
||||
rs.m.Lock()
|
||||
defer rs.m.Unlock()
|
||||
|
||||
results := make(Stats, len(rs.buffer))
|
||||
for i, e := range rs.buffer {
|
||||
results[i] = e
|
||||
results[i].Raw = make([]byte, len(e.Raw))
|
||||
for j, b := range e.Raw {
|
||||
results[i].Raw[j] = b
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// ClearSent locks the sender and clears any Stats that have been recorded.
|
||||
func (rs *RecordingSender) ClearSent() {
|
||||
rs.m.Lock()
|
||||
defer rs.m.Unlock()
|
||||
|
||||
rs.buffer = rs.buffer[:0]
|
||||
}
|
||||
|
||||
// Send parses the provided []byte into stat objects and then appends these to
|
||||
// the buffer of sent stats. Buffer operations are synchronized so it is safe
|
||||
// to call this from multiple goroutines (though contenion will impact
|
||||
// performance so don't use this during a benchmark). Send treats '\n' as a
|
||||
// delimiter between multiple sats in the same []byte.
|
||||
//
|
||||
// Calling after the Sender has been closed will return an error (and not
|
||||
// mutate the buffer).
|
||||
func (rs *RecordingSender) Send(data []byte) (int, error) {
|
||||
sent := ParseStats(data)
|
||||
|
||||
rs.m.Lock()
|
||||
defer rs.m.Unlock()
|
||||
if rs.closed {
|
||||
return 0, errors.New("writing to a closed sender")
|
||||
}
|
||||
|
||||
rs.buffer = append(rs.buffer, sent...)
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
// Close marks this sender as closed. Subsequent attempts to Send stats will
|
||||
// result in an error.
|
||||
func (rs *RecordingSender) Close() error {
|
||||
rs.m.Lock()
|
||||
defer rs.m.Unlock()
|
||||
|
||||
rs.closed = true
|
||||
return nil
|
||||
}
|
||||
57
vendor/github.com/cactus/go-statsd-client/statsd/statsdtest/recorder_test.go
generated
vendored
Normal file
57
vendor/github.com/cactus/go-statsd-client/statsd/statsdtest/recorder_test.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
package statsdtest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cactus/go-statsd-client/statsd"
|
||||
)
|
||||
|
||||
func TestRecordingSenderIsSender(t *testing.T) {
|
||||
// This ensures that if the Sender interface changes in the future we'll get
|
||||
// compile time failures should the RecordingSender not be updated to meet
|
||||
// the new definition. This keeps changes from inadvertently breaking tests
|
||||
// of folks that use go-statsd-client.
|
||||
var _ statsd.Sender = NewRecordingSender()
|
||||
}
|
||||
|
||||
func TestRecordingSender(t *testing.T) {
|
||||
start := time.Now()
|
||||
rs := new(RecordingSender)
|
||||
statter, err := statsd.NewClientWithSender(rs, "test")
|
||||
if err != nil {
|
||||
t.Errorf("failed to construct client")
|
||||
return
|
||||
}
|
||||
|
||||
statter.Inc("stat", 4444, 1.0)
|
||||
statter.Dec("stat", 5555, 1.0)
|
||||
statter.Set("set-stat", "some string", 1.0)
|
||||
|
||||
d := time.Since(start)
|
||||
statter.TimingDuration("timing", d, 1.0)
|
||||
|
||||
sent := rs.GetSent()
|
||||
if len(sent) != 4 {
|
||||
// just dive out because everything else relies on ordering
|
||||
t.Fatalf("Did not capture all stats sent; got: %s", sent)
|
||||
}
|
||||
|
||||
ms := float64(d) / float64(time.Millisecond)
|
||||
// somewhat fragile in that it assums float rendering within client *shrug*
|
||||
msStr := string(strconv.AppendFloat([]byte(""), ms, 'f', -1, 64))
|
||||
|
||||
expected := Stats{
|
||||
{[]byte("test.stat:4444|c"), "test.stat", "4444", "c", "", true},
|
||||
{[]byte("test.stat:-5555|c"), "test.stat", "-5555", "c", "", true},
|
||||
{[]byte("test.set-stat:some string|s"), "test.set-stat", "some string", "s", "", true},
|
||||
{[]byte(fmt.Sprintf("test.timing:%s|ms", msStr)), "test.timing", msStr, "ms", "", true},
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(sent, expected) {
|
||||
t.Errorf("got: %s, want: %s", sent, expected)
|
||||
}
|
||||
}
|
||||
140
vendor/github.com/cactus/go-statsd-client/statsd/statsdtest/stat.go
generated
vendored
Normal file
140
vendor/github.com/cactus/go-statsd-client/statsd/statsdtest/stat.go
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
package statsdtest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Stat contains the raw and extracted stat information from a stat that was
|
||||
// sent by the RecordingSender. Raw will always have the content that was
|
||||
// consumed for this specific stat and Parsed will be set if no errors were hit
|
||||
// pulling information out of it.
|
||||
type Stat struct {
|
||||
Raw []byte
|
||||
Stat string
|
||||
Value string
|
||||
Tag string
|
||||
Rate string
|
||||
Parsed bool
|
||||
}
|
||||
|
||||
// String fulfils the stringer interface
|
||||
func (s *Stat) String() string {
|
||||
return fmt.Sprintf("%s %s %s", s.Stat, s.Value, s.Rate)
|
||||
}
|
||||
|
||||
// ParseStats takes a sequence of bytes destined for a Statsd server and parses
|
||||
// it out into one or more Stat structs. Each struct includes both the raw
|
||||
// bytes (copied, so the src []byte may be reused if desired) as well as each
|
||||
// component it was able to parse out. If parsing was incomplete Stat.Parsed
|
||||
// will be set to false but no error is returned / kept.
|
||||
func ParseStats(src []byte) Stats {
|
||||
d := make([]byte, len(src))
|
||||
for i, b := range src {
|
||||
d[i] = b
|
||||
}
|
||||
// standard protocol indicates one stat per line
|
||||
entries := bytes.Split(d, []byte{'\n'})
|
||||
|
||||
result := make(Stats, len(entries))
|
||||
|
||||
for i, e := range entries {
|
||||
result[i] = Stat{Raw: e}
|
||||
ss := &result[i]
|
||||
|
||||
// : deliniates the stat name from the stat data
|
||||
marker := bytes.IndexByte(e, ':')
|
||||
if marker == -1 {
|
||||
continue
|
||||
}
|
||||
ss.Stat = string(e[0:marker])
|
||||
|
||||
// stat data folows ':' with the form {value}|{type tag}[|@{sample rate}]
|
||||
e = e[marker+1:]
|
||||
marker = bytes.IndexByte(e, '|')
|
||||
if marker == -1 {
|
||||
continue
|
||||
}
|
||||
|
||||
ss.Value = string(e[:marker])
|
||||
|
||||
e = e[marker+1:]
|
||||
marker = bytes.IndexByte(e, '|')
|
||||
if marker == -1 {
|
||||
// no sample rate
|
||||
ss.Tag = string(e)
|
||||
} else {
|
||||
ss.Tag = string(e[:marker])
|
||||
e = e[marker+1:]
|
||||
if len(e) == 0 || e[0] != '@' {
|
||||
// sample rate should be prefixed with '@'; bail otherwise
|
||||
continue
|
||||
}
|
||||
ss.Rate = string(e[1:])
|
||||
}
|
||||
|
||||
ss.Parsed = true
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Stats is a slice of Stat
|
||||
type Stats []Stat
|
||||
|
||||
// Unparsed returns any stats that were unable to be completely parsed.
|
||||
func (s Stats) Unparsed() Stats {
|
||||
var r Stats
|
||||
for _, e := range s {
|
||||
if !e.Parsed {
|
||||
r = append(r, e)
|
||||
}
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// CollectNamed returns all data sent for a given stat name.
|
||||
func (s Stats) CollectNamed(statName string) Stats {
|
||||
return s.Collect(func(e Stat) bool {
|
||||
return e.Stat == statName
|
||||
})
|
||||
}
|
||||
|
||||
// Collect gathers all stats that make some predicate true.
|
||||
func (s Stats) Collect(pred func(Stat) bool) Stats {
|
||||
var r Stats
|
||||
for _, e := range s {
|
||||
if pred(e) {
|
||||
r = append(r, e)
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Values returns the values associated with this Stats object.
|
||||
func (s Stats) Values() []string {
|
||||
if len(s) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
r := make([]string, len(s))
|
||||
for i, e := range s {
|
||||
r[i] = e.Value
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// String fulfils the stringer interface
|
||||
func (s Stats) String() string {
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
r := make([]string, len(s))
|
||||
for i, e := range s {
|
||||
r[i] = e.String()
|
||||
}
|
||||
return strings.Join(r, "\n")
|
||||
}
|
||||
154
vendor/github.com/cactus/go-statsd-client/statsd/statsdtest/stat_test.go
generated
vendored
Normal file
154
vendor/github.com/cactus/go-statsd-client/statsd/statsdtest/stat_test.go
generated
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
package statsdtest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type parsingTestCase struct {
|
||||
name string
|
||||
sent [][]byte
|
||||
expected Stats
|
||||
}
|
||||
|
||||
var (
|
||||
badStatNameOnly = []byte("foo.bar.baz:")
|
||||
bsnoStat = Stat{
|
||||
Raw: badStatNameOnly,
|
||||
Stat: "foo.bar.baz",
|
||||
Parsed: false,
|
||||
}
|
||||
|
||||
gaugeWithoutRate = []byte("foo.bar.baz:1.000|g")
|
||||
gworStat = Stat{
|
||||
Raw: gaugeWithoutRate,
|
||||
Stat: "foo.bar.baz",
|
||||
Value: "1.000",
|
||||
Tag: "g",
|
||||
Parsed: true,
|
||||
}
|
||||
|
||||
counterWithRate = []byte("foo.bar.baz:1.000|c|@0.75")
|
||||
cwrStat = Stat{
|
||||
Raw: counterWithRate,
|
||||
Stat: "foo.bar.baz",
|
||||
Value: "1.000",
|
||||
Tag: "c",
|
||||
Rate: "0.75",
|
||||
Parsed: true,
|
||||
}
|
||||
|
||||
stringStat = []byte(":some string value|s")
|
||||
sStat = Stat{
|
||||
Raw: stringStat,
|
||||
Stat: "",
|
||||
Value: "some string value",
|
||||
Tag: "s",
|
||||
Parsed: true,
|
||||
}
|
||||
|
||||
badValue = []byte("asoentuh")
|
||||
bvStat = Stat{Raw: badValue}
|
||||
|
||||
testCases = []parsingTestCase{
|
||||
{name: "no stat data",
|
||||
sent: [][]byte{badStatNameOnly},
|
||||
expected: Stats{bsnoStat}},
|
||||
{name: "trivial case",
|
||||
sent: [][]byte{gaugeWithoutRate},
|
||||
expected: Stats{gworStat}},
|
||||
{name: "multiple simple",
|
||||
sent: [][]byte{gaugeWithoutRate, counterWithRate},
|
||||
expected: Stats{gworStat, cwrStat}},
|
||||
{name: "mixed good and bad",
|
||||
sent: [][]byte{badValue, badValue, stringStat, badValue, counterWithRate, badValue},
|
||||
expected: Stats{bvStat, bvStat, sStat, bvStat, cwrStat, bvStat}},
|
||||
}
|
||||
)
|
||||
|
||||
func TestParseBytes(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
got := ParseStats(bytes.Join(tc.sent, []byte("\n")))
|
||||
want := tc.expected
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s: got: %+v, want: %+v", tc.name, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatsUnparsed(t *testing.T) {
|
||||
start := Stats{bsnoStat, gworStat, bsnoStat, bsnoStat, cwrStat}
|
||||
got := start.Unparsed()
|
||||
want := Stats{bsnoStat, bsnoStat, bsnoStat}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("got: %+v, want: %+v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatsCollectNamed(t *testing.T) {
|
||||
type test struct {
|
||||
name string
|
||||
start Stats
|
||||
want Stats
|
||||
matchOn string
|
||||
}
|
||||
|
||||
cases := []test{
|
||||
{"No matches",
|
||||
Stats{bsnoStat, cwrStat},
|
||||
nil,
|
||||
"foo"},
|
||||
{"One match",
|
||||
Stats{bsnoStat, Stat{Stat: "foo"}, cwrStat},
|
||||
Stats{Stat{Stat: "foo"}},
|
||||
"foo"},
|
||||
{"Two matches",
|
||||
Stats{bsnoStat, Stat{Stat: "foo"}, cwrStat},
|
||||
Stats{bsnoStat, cwrStat},
|
||||
"foo.bar.baz"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
got := c.start.CollectNamed(c.matchOn)
|
||||
if !reflect.DeepEqual(got, c.want) {
|
||||
t.Errorf("%s: got: %+v, want: %+v", c.name, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatsCollect(t *testing.T) {
|
||||
type test struct {
|
||||
name string
|
||||
start Stats
|
||||
want Stats
|
||||
pred func(Stat) bool
|
||||
}
|
||||
|
||||
cases := []test{
|
||||
{"Not called",
|
||||
Stats{},
|
||||
nil,
|
||||
func(_ Stat) bool { t.Errorf("should not be called"); return true }},
|
||||
{"Matches value = 1.000",
|
||||
Stats{bsnoStat, gworStat, cwrStat, sStat, bsnoStat},
|
||||
Stats{gworStat, cwrStat},
|
||||
func(s Stat) bool { return s.Value == "1.000" }},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
got := c.start.Collect(c.pred)
|
||||
if !reflect.DeepEqual(got, c.want) {
|
||||
t.Errorf("%s: got: %+v, want: %+v", c.name, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatsValues(t *testing.T) {
|
||||
start := Stats{bsnoStat, sStat, gworStat}
|
||||
got := start.Values()
|
||||
want := []string{bsnoStat.Value, sStat.Value, gworStat.Value}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("got: %+v, want: %+v", got, want)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user