mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
* add jaeger support, link hot container & req span * adds jaeger support now with FN_JAEGER_URL, there's a simple tutorial in the operating/metrics.md file now and it's pretty easy to get up and running. * links a hot request span to a hot container span. when we change this to sample at a lower ratio we'll need to finagle the hot container span to always sample or something, otherwise we'll hide that info. at least, since we're sampling at 100% for now if this is flipped on, can see freeze/unfreeze etc. if they hit. this is useful for debugging. note that zipkin's exporter does not follow the link at all, hence jaeger... and they're backed by the Cloud Empire now (CNCF) so we'll probably use it anyway. * vendor: add thrift for jaeger
297 lines
7.3 KiB
Go
297 lines
7.3 KiB
Go
// Copyright 2015 Google Inc. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package gensupport
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"io/ioutil"
|
|
"reflect"
|
|
"testing"
|
|
"testing/iotest"
|
|
|
|
"google.golang.org/api/googleapi"
|
|
)
|
|
|
|
// getChunkAsString reads a chunk from mb, but does not call Next.
|
|
func getChunkAsString(t *testing.T, mb *MediaBuffer) (string, error) {
|
|
chunk, _, size, err := mb.Chunk()
|
|
|
|
buf, e := ioutil.ReadAll(chunk)
|
|
if e != nil {
|
|
t.Fatalf("Failed reading chunk: %v", e)
|
|
}
|
|
if size != len(buf) {
|
|
t.Fatalf("reported chunk size doesn't match actual chunk size: got: %v; want: %v", size, len(buf))
|
|
}
|
|
return string(buf), err
|
|
}
|
|
|
|
func TestChunking(t *testing.T) {
|
|
type testCase struct {
|
|
data string // the data to read from the Reader
|
|
finalErr error // error to return after data has been read
|
|
chunkSize int
|
|
wantChunks []string
|
|
}
|
|
|
|
for _, singleByteReads := range []bool{true, false} {
|
|
for _, tc := range []testCase{
|
|
{
|
|
data: "abcdefg",
|
|
finalErr: nil,
|
|
chunkSize: 3,
|
|
wantChunks: []string{"abc", "def", "g"},
|
|
},
|
|
{
|
|
data: "abcdefg",
|
|
finalErr: nil,
|
|
chunkSize: 1,
|
|
wantChunks: []string{"a", "b", "c", "d", "e", "f", "g"},
|
|
},
|
|
{
|
|
data: "abcdefg",
|
|
finalErr: nil,
|
|
chunkSize: 7,
|
|
wantChunks: []string{"abcdefg"},
|
|
},
|
|
{
|
|
data: "abcdefg",
|
|
finalErr: nil,
|
|
chunkSize: 8,
|
|
wantChunks: []string{"abcdefg"},
|
|
},
|
|
{
|
|
data: "abcdefg",
|
|
finalErr: io.ErrUnexpectedEOF,
|
|
chunkSize: 3,
|
|
wantChunks: []string{"abc", "def", "g"},
|
|
},
|
|
{
|
|
data: "abcdefg",
|
|
finalErr: io.ErrUnexpectedEOF,
|
|
chunkSize: 8,
|
|
wantChunks: []string{"abcdefg"},
|
|
},
|
|
} {
|
|
var r io.Reader = &errReader{buf: []byte(tc.data), err: tc.finalErr}
|
|
|
|
if singleByteReads {
|
|
r = iotest.OneByteReader(r)
|
|
}
|
|
|
|
mb := NewMediaBuffer(r, tc.chunkSize)
|
|
var gotErr error
|
|
got := []string{}
|
|
for {
|
|
chunk, err := getChunkAsString(t, mb)
|
|
if len(chunk) != 0 {
|
|
got = append(got, string(chunk))
|
|
}
|
|
if err != nil {
|
|
gotErr = err
|
|
break
|
|
}
|
|
mb.Next()
|
|
}
|
|
|
|
if !reflect.DeepEqual(got, tc.wantChunks) {
|
|
t.Errorf("Failed reading buffer: got: %v; want:%v", got, tc.wantChunks)
|
|
}
|
|
|
|
expectedErr := tc.finalErr
|
|
if expectedErr == nil {
|
|
expectedErr = io.EOF
|
|
}
|
|
if gotErr != expectedErr {
|
|
t.Errorf("Reading buffer error: got: %v; want: %v", gotErr, expectedErr)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestChunkCanBeReused(t *testing.T) {
|
|
er := &errReader{buf: []byte("abcdefg")}
|
|
mb := NewMediaBuffer(er, 3)
|
|
|
|
// expectChunk reads a chunk and checks that it got what was wanted.
|
|
expectChunk := func(want string, wantErr error) {
|
|
got, err := getChunkAsString(t, mb)
|
|
if err != wantErr {
|
|
t.Errorf("error reading buffer: got: %v; want: %v", err, wantErr)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Errorf("Failed reading buffer: got: %q; want:%q", got, want)
|
|
}
|
|
}
|
|
expectChunk("abc", nil)
|
|
// On second call, should get same chunk again.
|
|
expectChunk("abc", nil)
|
|
mb.Next()
|
|
expectChunk("def", nil)
|
|
expectChunk("def", nil)
|
|
mb.Next()
|
|
expectChunk("g", io.EOF)
|
|
expectChunk("g", io.EOF)
|
|
mb.Next()
|
|
expectChunk("", io.EOF)
|
|
}
|
|
|
|
func TestPos(t *testing.T) {
|
|
er := &errReader{buf: []byte("abcdefg")}
|
|
mb := NewMediaBuffer(er, 3)
|
|
|
|
expectChunkAtOffset := func(want int64, wantErr error) {
|
|
_, off, _, err := mb.Chunk()
|
|
if err != wantErr {
|
|
t.Errorf("error reading buffer: got: %v; want: %v", err, wantErr)
|
|
}
|
|
if got := off; got != want {
|
|
t.Errorf("resumable buffer Pos: got: %v; want: %v", got, want)
|
|
}
|
|
}
|
|
|
|
// We expect the first chunk to be at offset 0.
|
|
expectChunkAtOffset(0, nil)
|
|
// Fetching the same chunk should return the same offset.
|
|
expectChunkAtOffset(0, nil)
|
|
|
|
// Calling Next multiple times should only cause off to advance by 3, since off is not advanced until
|
|
// the chunk is actually read.
|
|
mb.Next()
|
|
mb.Next()
|
|
expectChunkAtOffset(3, nil)
|
|
|
|
mb.Next()
|
|
|
|
// Load the final 1-byte chunk.
|
|
expectChunkAtOffset(6, io.EOF)
|
|
|
|
// Next will advance 1 byte. But there are no more chunks, so off will not increase beyond 7.
|
|
mb.Next()
|
|
expectChunkAtOffset(7, io.EOF)
|
|
mb.Next()
|
|
expectChunkAtOffset(7, io.EOF)
|
|
}
|
|
|
|
// bytes.Reader implements both Reader and ReaderAt. The following types
|
|
// implement various combinations of Reader, ReaderAt and ContentTyper, by
|
|
// wrapping bytes.Reader. All implement at least ReaderAt, so they can be
|
|
// passed to ReaderAtToReader. The following table summarizes which types
|
|
// implement which interfaces:
|
|
//
|
|
// ReaderAt Reader ContentTyper
|
|
// reader x x
|
|
// typerReader x x x
|
|
// readerAt x
|
|
// typerReaderAt x x
|
|
|
|
// reader implements Reader, in addition to ReaderAt.
|
|
type reader struct {
|
|
r *bytes.Reader
|
|
}
|
|
|
|
func (r *reader) ReadAt(b []byte, off int64) (n int, err error) {
|
|
return r.r.ReadAt(b, off)
|
|
}
|
|
|
|
func (r *reader) Read(b []byte) (n int, err error) {
|
|
return r.r.Read(b)
|
|
}
|
|
|
|
// typerReader implements Reader and ContentTyper, in addition to ReaderAt.
|
|
type typerReader struct {
|
|
r *bytes.Reader
|
|
}
|
|
|
|
func (tr *typerReader) ReadAt(b []byte, off int64) (n int, err error) {
|
|
return tr.r.ReadAt(b, off)
|
|
}
|
|
|
|
func (tr *typerReader) Read(b []byte) (n int, err error) {
|
|
return tr.r.Read(b)
|
|
}
|
|
|
|
func (tr *typerReader) ContentType() string {
|
|
return "ctype"
|
|
}
|
|
|
|
// readerAt implements only ReaderAt.
|
|
type readerAt struct {
|
|
r *bytes.Reader
|
|
}
|
|
|
|
func (ra *readerAt) ReadAt(b []byte, off int64) (n int, err error) {
|
|
return ra.r.ReadAt(b, off)
|
|
}
|
|
|
|
// typerReaderAt implements ContentTyper, in addition to ReaderAt.
|
|
type typerReaderAt struct {
|
|
r *bytes.Reader
|
|
}
|
|
|
|
func (tra *typerReaderAt) ReadAt(b []byte, off int64) (n int, err error) {
|
|
return tra.r.ReadAt(b, off)
|
|
}
|
|
|
|
func (tra *typerReaderAt) ContentType() string {
|
|
return "ctype"
|
|
}
|
|
|
|
func TestAdapter(t *testing.T) {
|
|
data := "abc"
|
|
|
|
checkConversion := func(to io.Reader, wantTyper bool) {
|
|
if _, ok := to.(googleapi.ContentTyper); ok != wantTyper {
|
|
t.Errorf("reader implements typer? got: %v; want: %v", ok, wantTyper)
|
|
}
|
|
if typer, ok := to.(googleapi.ContentTyper); ok && typer.ContentType() != "ctype" {
|
|
t.Errorf("content type: got: %s; want: ctype", typer.ContentType())
|
|
}
|
|
buf, err := ioutil.ReadAll(to)
|
|
if err != nil {
|
|
t.Errorf("error reading data: %v", err)
|
|
return
|
|
}
|
|
if !bytes.Equal(buf, []byte(data)) {
|
|
t.Errorf("failed reading data: got: %s; want: %s", buf, data)
|
|
}
|
|
}
|
|
|
|
type testCase struct {
|
|
from io.ReaderAt
|
|
wantTyper bool
|
|
}
|
|
for _, tc := range []testCase{
|
|
{
|
|
from: &reader{bytes.NewReader([]byte(data))},
|
|
wantTyper: false,
|
|
},
|
|
{
|
|
// Reader and ContentTyper
|
|
from: &typerReader{bytes.NewReader([]byte(data))},
|
|
wantTyper: true,
|
|
},
|
|
{
|
|
// ReaderAt
|
|
from: &readerAt{bytes.NewReader([]byte(data))},
|
|
wantTyper: false,
|
|
},
|
|
{
|
|
// ReaderAt and ContentTyper
|
|
from: &typerReaderAt{bytes.NewReader([]byte(data))},
|
|
wantTyper: true,
|
|
},
|
|
} {
|
|
to := ReaderAtToReader(tc.from, int64(len(data)))
|
|
checkConversion(to, tc.wantTyper)
|
|
// tc.from is a ReaderAt, and should be treated like one, even
|
|
// if it also implements Reader. Specifically, it can be
|
|
// reused and read from the beginning.
|
|
to = ReaderAtToReader(tc.from, int64(len(data)))
|
|
checkConversion(to, tc.wantTyper)
|
|
}
|
|
}
|