mirror of
https://github.com/restic/restic.git
synced 2025-12-15 19:34:23 +00:00
Vendor dependencies for GCS
This commit is contained in:
58
vendor/cloud.google.com/go/internal/atomiccache/atomiccache.go
generated
vendored
Normal file
58
vendor/cloud.google.com/go/internal/atomiccache/atomiccache.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 atomiccache provides a map-based cache that supports very fast
|
||||
// reads.
|
||||
package atomiccache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type mapType map[interface{}]interface{}
|
||||
|
||||
// Cache is a map-based cache that supports fast reads via use of atomics.
|
||||
// Writes are slow, requiring a copy of the entire cache.
|
||||
// The zero Cache is an empty cache, ready for use.
|
||||
type Cache struct {
|
||||
val atomic.Value // mapType
|
||||
mu sync.Mutex // used only by writers
|
||||
}
|
||||
|
||||
// Get returns the value of the cache at key. If there is no value,
|
||||
// getter is called to provide one, and the cache is updated.
|
||||
// The getter function may be called concurrently. It should be pure,
|
||||
// returning the same value for every call.
|
||||
func (c *Cache) Get(key interface{}, getter func() interface{}) interface{} {
|
||||
mp, _ := c.val.Load().(mapType)
|
||||
if v, ok := mp[key]; ok {
|
||||
return v
|
||||
}
|
||||
|
||||
// Compute value without lock.
|
||||
// Might duplicate effort but won't hold other computations back.
|
||||
newV := getter()
|
||||
|
||||
c.mu.Lock()
|
||||
mp, _ = c.val.Load().(mapType)
|
||||
newM := make(mapType, len(mp)+1)
|
||||
for k, v := range mp {
|
||||
newM[k] = v
|
||||
}
|
||||
newM[key] = newV
|
||||
c.val.Store(newM)
|
||||
c.mu.Unlock()
|
||||
return newV
|
||||
}
|
||||
46
vendor/cloud.google.com/go/internal/atomiccache/atomiccache_test.go
generated
vendored
Normal file
46
vendor/cloud.google.com/go/internal/atomiccache/atomiccache_test.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 atomiccache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
var c Cache
|
||||
called := false
|
||||
get := func(k interface{}) interface{} {
|
||||
return c.Get(k, func() interface{} {
|
||||
called = true
|
||||
return fmt.Sprintf("v%d", k)
|
||||
})
|
||||
}
|
||||
got := get(1)
|
||||
if want := "v1"; got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
if !called {
|
||||
t.Error("getter not called, expected a call")
|
||||
}
|
||||
called = false
|
||||
got = get(1)
|
||||
if want := "v1"; got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
if called {
|
||||
t.Error("getter unexpectedly called")
|
||||
}
|
||||
}
|
||||
444
vendor/cloud.google.com/go/internal/fields/fields.go
generated
vendored
Normal file
444
vendor/cloud.google.com/go/internal/fields/fields.go
generated
vendored
Normal file
@@ -0,0 +1,444 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 fields provides a view of the fields of a struct that follows the Go
|
||||
// rules, amended to consider tags and case insensitivity.
|
||||
//
|
||||
// Usage
|
||||
//
|
||||
// First define a function that interprets tags:
|
||||
//
|
||||
// func parseTag(st reflect.StructTag) (name string, keep bool, other interface{}, err error) { ... }
|
||||
//
|
||||
// The function's return values describe whether to ignore the field
|
||||
// completely or provide an alternate name, as well as other data from the
|
||||
// parse that is stored to avoid re-parsing.
|
||||
//
|
||||
// Then define a function to validate the type:
|
||||
//
|
||||
// func validate(t reflect.Type) error { ... }
|
||||
//
|
||||
// Then, if necessary, define a function to specify leaf types - types
|
||||
// which should be considered one field and not be recursed into:
|
||||
//
|
||||
// func isLeafType(t reflect.Type) bool { ... }
|
||||
//
|
||||
// eg:
|
||||
//
|
||||
// func isLeafType(t reflect.Type) bool {
|
||||
// return t == reflect.TypeOf(time.Time{})
|
||||
// }
|
||||
//
|
||||
// Next, construct a Cache, passing your functions. As its name suggests, a
|
||||
// Cache remembers validation and field information for a type, so subsequent
|
||||
// calls with the same type are very fast.
|
||||
//
|
||||
// cache := fields.NewCache(parseTag, validate, isLeafType)
|
||||
//
|
||||
// To get the fields of a struct type as determined by the above rules, call
|
||||
// the Fields method:
|
||||
//
|
||||
// fields, err := cache.Fields(reflect.TypeOf(MyStruct{}))
|
||||
//
|
||||
// The return value can be treated as a slice of Fields.
|
||||
//
|
||||
// Given a string, such as a key or column name obtained during unmarshalling,
|
||||
// call Match on the list of fields to find a field whose name is the best
|
||||
// match:
|
||||
//
|
||||
// field := fields.Match(name)
|
||||
//
|
||||
// Match looks for an exact match first, then falls back to a case-insensitive
|
||||
// comparison.
|
||||
package fields
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"cloud.google.com/go/internal/atomiccache"
|
||||
)
|
||||
|
||||
// A Field records information about a struct field.
|
||||
type Field struct {
|
||||
Name string // effective field name
|
||||
NameFromTag bool // did Name come from a tag?
|
||||
Type reflect.Type // field type
|
||||
Index []int // index sequence, for reflect.Value.FieldByIndex
|
||||
ParsedTag interface{} // third return value of the parseTag function
|
||||
|
||||
nameBytes []byte
|
||||
equalFold func(s, t []byte) bool
|
||||
}
|
||||
|
||||
type ParseTagFunc func(reflect.StructTag) (name string, keep bool, other interface{}, err error)
|
||||
|
||||
type ValidateFunc func(reflect.Type) error
|
||||
|
||||
type LeafTypesFunc func(reflect.Type) bool
|
||||
|
||||
// A Cache records information about the fields of struct types.
|
||||
//
|
||||
// A Cache is safe for use by multiple goroutines.
|
||||
type Cache struct {
|
||||
parseTag ParseTagFunc
|
||||
validate ValidateFunc
|
||||
leafTypes LeafTypesFunc
|
||||
cache atomiccache.Cache // from reflect.Type to cacheValue
|
||||
}
|
||||
|
||||
// NewCache constructs a Cache.
|
||||
//
|
||||
// Its first argument should be a function that accepts
|
||||
// a struct tag and returns four values: an alternative name for the field
|
||||
// extracted from the tag, a boolean saying whether to keep the field or ignore
|
||||
// it, additional data that is stored with the field information to avoid
|
||||
// having to parse the tag again, and an error.
|
||||
//
|
||||
// Its second argument should be a function that accepts a reflect.Type and
|
||||
// returns an error if the struct type is invalid in any way. For example, it
|
||||
// may check that all of the struct field tags are valid, or that all fields
|
||||
// are of an appropriate type.
|
||||
func NewCache(parseTag ParseTagFunc, validate ValidateFunc, leafTypes LeafTypesFunc) *Cache {
|
||||
if parseTag == nil {
|
||||
parseTag = func(reflect.StructTag) (string, bool, interface{}, error) {
|
||||
return "", true, nil, nil
|
||||
}
|
||||
}
|
||||
if validate == nil {
|
||||
validate = func(reflect.Type) error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if leafTypes == nil {
|
||||
leafTypes = func(reflect.Type) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return &Cache{
|
||||
parseTag: parseTag,
|
||||
validate: validate,
|
||||
leafTypes: leafTypes,
|
||||
}
|
||||
}
|
||||
|
||||
// A fieldScan represents an item on the fieldByNameFunc scan work list.
|
||||
type fieldScan struct {
|
||||
typ reflect.Type
|
||||
index []int
|
||||
}
|
||||
|
||||
// Fields returns all the exported fields of t, which must be a struct type. It
|
||||
// follows the standard Go rules for embedded fields, modified by the presence
|
||||
// of tags. The result is sorted lexicographically by index.
|
||||
//
|
||||
// These rules apply in the absence of tags:
|
||||
// Anonymous struct fields are treated as if their inner exported fields were
|
||||
// fields in the outer struct (embedding). The result includes all fields that
|
||||
// aren't shadowed by fields at higher level of embedding. If more than one
|
||||
// field with the same name exists at the same level of embedding, it is
|
||||
// excluded. An anonymous field that is not of struct type is treated as having
|
||||
// its type as its name.
|
||||
//
|
||||
// Tags modify these rules as follows:
|
||||
// A field's tag is used as its name.
|
||||
// An anonymous struct field with a name given in its tag is treated as
|
||||
// a field having that name, rather than an embedded struct (the struct's
|
||||
// fields will not be returned).
|
||||
// If more than one field with the same name exists at the same level of embedding,
|
||||
// but exactly one of them is tagged, then the tagged field is reported and the others
|
||||
// are ignored.
|
||||
func (c *Cache) Fields(t reflect.Type) (List, error) {
|
||||
if t.Kind() != reflect.Struct {
|
||||
panic("fields: Fields of non-struct type")
|
||||
}
|
||||
return c.cachedTypeFields(t)
|
||||
}
|
||||
|
||||
// A List is a list of Fields.
|
||||
type List []Field
|
||||
|
||||
// Match returns the field in the list whose name best matches the supplied
|
||||
// name, nor nil if no field does. If there is a field with the exact name, it
|
||||
// is returned. Otherwise the first field (sorted by index) whose name matches
|
||||
// case-insensitively is returned.
|
||||
func (l List) Match(name string) *Field {
|
||||
return l.MatchBytes([]byte(name))
|
||||
}
|
||||
|
||||
// MatchBytes is identical to Match, except that the argument is a byte slice.
|
||||
func (l List) MatchBytes(name []byte) *Field {
|
||||
var f *Field
|
||||
for i := range l {
|
||||
ff := &l[i]
|
||||
if bytes.Equal(ff.nameBytes, name) {
|
||||
return ff
|
||||
}
|
||||
if f == nil && ff.equalFold(ff.nameBytes, name) {
|
||||
f = ff
|
||||
}
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
type cacheValue struct {
|
||||
fields List
|
||||
err error
|
||||
}
|
||||
|
||||
// cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
|
||||
// This code has been copied and modified from
|
||||
// https://go.googlesource.com/go/+/go1.7.3/src/encoding/json/encode.go.
|
||||
func (c *Cache) cachedTypeFields(t reflect.Type) (List, error) {
|
||||
cv := c.cache.Get(t, func() interface{} {
|
||||
if err := c.validate(t); err != nil {
|
||||
return cacheValue{nil, err}
|
||||
}
|
||||
f, err := c.typeFields(t)
|
||||
return cacheValue{List(f), err}
|
||||
}).(cacheValue)
|
||||
return cv.fields, cv.err
|
||||
}
|
||||
|
||||
func (c *Cache) typeFields(t reflect.Type) ([]Field, error) {
|
||||
fields, err := c.listFields(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sort.Sort(byName(fields))
|
||||
// Delete all fields that are hidden by the Go rules for embedded fields.
|
||||
|
||||
// The fields are sorted in primary order of name, secondary order of field
|
||||
// index length. So the first field with a given name is the dominant one.
|
||||
var out []Field
|
||||
for advance, i := 0, 0; i < len(fields); i += advance {
|
||||
// One iteration per name.
|
||||
// Find the sequence of fields with the name of this first field.
|
||||
fi := fields[i]
|
||||
name := fi.Name
|
||||
for advance = 1; i+advance < len(fields); advance++ {
|
||||
fj := fields[i+advance]
|
||||
if fj.Name != name {
|
||||
break
|
||||
}
|
||||
}
|
||||
// Find the dominant field, if any, out of all fields that have the same name.
|
||||
dominant, ok := dominantField(fields[i : i+advance])
|
||||
if ok {
|
||||
out = append(out, dominant)
|
||||
}
|
||||
}
|
||||
sort.Sort(byIndex(out))
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *Cache) listFields(t reflect.Type) ([]Field, error) {
|
||||
// This uses the same condition that the Go language does: there must be a unique instance
|
||||
// of the match at a given depth level. If there are multiple instances of a match at the
|
||||
// same depth, they annihilate each other and inhibit any possible match at a lower level.
|
||||
// The algorithm is breadth first search, one depth level at a time.
|
||||
|
||||
// The current and next slices are work queues:
|
||||
// current lists the fields to visit on this depth level,
|
||||
// and next lists the fields on the next lower level.
|
||||
current := []fieldScan{}
|
||||
next := []fieldScan{{typ: t}}
|
||||
|
||||
// nextCount records the number of times an embedded type has been
|
||||
// encountered and considered for queueing in the 'next' slice.
|
||||
// We only queue the first one, but we increment the count on each.
|
||||
// If a struct type T can be reached more than once at a given depth level,
|
||||
// then it annihilates itself and need not be considered at all when we
|
||||
// process that next depth level.
|
||||
var nextCount map[reflect.Type]int
|
||||
|
||||
// visited records the structs that have been considered already.
|
||||
// Embedded pointer fields can create cycles in the graph of
|
||||
// reachable embedded types; visited avoids following those cycles.
|
||||
// It also avoids duplicated effort: if we didn't find the field in an
|
||||
// embedded type T at level 2, we won't find it in one at level 4 either.
|
||||
visited := map[reflect.Type]bool{}
|
||||
|
||||
var fields []Field // Fields found.
|
||||
|
||||
for len(next) > 0 {
|
||||
current, next = next, current[:0]
|
||||
count := nextCount
|
||||
nextCount = nil
|
||||
|
||||
// Process all the fields at this depth, now listed in 'current'.
|
||||
// The loop queues embedded fields found in 'next', for processing during the next
|
||||
// iteration. The multiplicity of the 'current' field counts is recorded
|
||||
// in 'count'; the multiplicity of the 'next' field counts is recorded in 'nextCount'.
|
||||
for _, scan := range current {
|
||||
t := scan.typ
|
||||
if visited[t] {
|
||||
// We've looked through this type before, at a higher level.
|
||||
// That higher level would shadow the lower level we're now at,
|
||||
// so this one can't be useful to us. Ignore it.
|
||||
continue
|
||||
}
|
||||
visited[t] = true
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
f := t.Field(i)
|
||||
|
||||
exported := (f.PkgPath == "")
|
||||
|
||||
// If a named field is unexported, ignore it. An anonymous
|
||||
// unexported field is processed, because it may contain
|
||||
// exported fields, which are visible.
|
||||
if !exported && !f.Anonymous {
|
||||
continue
|
||||
}
|
||||
|
||||
// Examine the tag.
|
||||
tagName, keep, other, err := c.parseTag(f.Tag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !keep {
|
||||
continue
|
||||
}
|
||||
if c.leafTypes(f.Type) {
|
||||
fields = append(fields, newField(f, tagName, other, scan.index, i))
|
||||
continue
|
||||
}
|
||||
|
||||
var ntyp reflect.Type
|
||||
if f.Anonymous {
|
||||
// Anonymous field of type T or *T.
|
||||
ntyp = f.Type
|
||||
if ntyp.Kind() == reflect.Ptr {
|
||||
ntyp = ntyp.Elem()
|
||||
}
|
||||
}
|
||||
|
||||
// Record fields with a tag name, non-anonymous fields, or
|
||||
// anonymous non-struct fields.
|
||||
if tagName != "" || ntyp == nil || ntyp.Kind() != reflect.Struct {
|
||||
if !exported {
|
||||
continue
|
||||
}
|
||||
fields = append(fields, newField(f, tagName, other, scan.index, i))
|
||||
if count[t] > 1 {
|
||||
// If there were multiple instances, add a second,
|
||||
// so that the annihilation code will see a duplicate.
|
||||
fields = append(fields, fields[len(fields)-1])
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Queue embedded struct fields for processing with next level,
|
||||
// but only if the embedded types haven't already been queued.
|
||||
if nextCount[ntyp] > 0 {
|
||||
nextCount[ntyp] = 2 // exact multiple doesn't matter
|
||||
continue
|
||||
}
|
||||
if nextCount == nil {
|
||||
nextCount = map[reflect.Type]int{}
|
||||
}
|
||||
nextCount[ntyp] = 1
|
||||
if count[t] > 1 {
|
||||
nextCount[ntyp] = 2 // exact multiple doesn't matter
|
||||
}
|
||||
var index []int
|
||||
index = append(index, scan.index...)
|
||||
index = append(index, i)
|
||||
next = append(next, fieldScan{ntyp, index})
|
||||
}
|
||||
}
|
||||
}
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
func newField(f reflect.StructField, tagName string, other interface{}, index []int, i int) Field {
|
||||
name := tagName
|
||||
if name == "" {
|
||||
name = f.Name
|
||||
}
|
||||
sf := Field{
|
||||
Name: name,
|
||||
NameFromTag: tagName != "",
|
||||
Type: f.Type,
|
||||
ParsedTag: other,
|
||||
nameBytes: []byte(name),
|
||||
}
|
||||
sf.equalFold = foldFunc(sf.nameBytes)
|
||||
sf.Index = append(sf.Index, index...)
|
||||
sf.Index = append(sf.Index, i)
|
||||
return sf
|
||||
}
|
||||
|
||||
// byName sorts fields using the following criteria, in order:
|
||||
// 1. name
|
||||
// 2. embedding depth
|
||||
// 3. tag presence (preferring a tagged field)
|
||||
// 4. index sequence.
|
||||
type byName []Field
|
||||
|
||||
func (x byName) Len() int { return len(x) }
|
||||
|
||||
func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
|
||||
func (x byName) Less(i, j int) bool {
|
||||
if x[i].Name != x[j].Name {
|
||||
return x[i].Name < x[j].Name
|
||||
}
|
||||
if len(x[i].Index) != len(x[j].Index) {
|
||||
return len(x[i].Index) < len(x[j].Index)
|
||||
}
|
||||
if x[i].NameFromTag != x[j].NameFromTag {
|
||||
return x[i].NameFromTag
|
||||
}
|
||||
return byIndex(x).Less(i, j)
|
||||
}
|
||||
|
||||
// byIndex sorts field by index sequence.
|
||||
type byIndex []Field
|
||||
|
||||
func (x byIndex) Len() int { return len(x) }
|
||||
|
||||
func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
|
||||
func (x byIndex) Less(i, j int) bool {
|
||||
xi := x[i].Index
|
||||
xj := x[j].Index
|
||||
ln := len(xi)
|
||||
if l := len(xj); l < ln {
|
||||
ln = l
|
||||
}
|
||||
for k := 0; k < ln; k++ {
|
||||
if xi[k] != xj[k] {
|
||||
return xi[k] < xj[k]
|
||||
}
|
||||
}
|
||||
return len(xi) < len(xj)
|
||||
}
|
||||
|
||||
// dominantField looks through the fields, all of which are known to have the
|
||||
// same name, to find the single field that dominates the others using Go's
|
||||
// embedding rules, modified by the presence of tags. If there are multiple
|
||||
// top-level fields, the boolean will be false: This condition is an error in
|
||||
// Go and we skip all the fields.
|
||||
func dominantField(fs []Field) (Field, bool) {
|
||||
// The fields are sorted in increasing index-length order, then by presence of tag.
|
||||
// That means that the first field is the dominant one. We need only check
|
||||
// for error cases: two fields at top level, either both tagged or neither tagged.
|
||||
if len(fs) > 1 && len(fs[0].Index) == len(fs[1].Index) && fs[0].NameFromTag == fs[1].NameFromTag {
|
||||
return Field{}, false
|
||||
}
|
||||
return fs[0], true
|
||||
}
|
||||
566
vendor/cloud.google.com/go/internal/fields/fields_test.go
generated
vendored
Normal file
566
vendor/cloud.google.com/go/internal/fields/fields_test.go
generated
vendored
Normal file
@@ -0,0 +1,566 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 fields
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"cloud.google.com/go/internal/testutil"
|
||||
)
|
||||
|
||||
type embed1 struct {
|
||||
Em1 int
|
||||
Dup int // annihilates with embed2.Dup
|
||||
Shadow int
|
||||
embed3
|
||||
}
|
||||
|
||||
type embed2 struct {
|
||||
Dup int
|
||||
embed3
|
||||
embed4
|
||||
}
|
||||
|
||||
type embed3 struct {
|
||||
Em3 int // annihilated because embed3 is in both embed1 and embed2
|
||||
embed5
|
||||
}
|
||||
|
||||
type embed4 struct {
|
||||
Em4 int
|
||||
Dup int // annihilation of Dup in embed1, embed2 hides this Dup
|
||||
*embed1 // ignored because it occurs at a higher level
|
||||
}
|
||||
|
||||
type embed5 struct {
|
||||
x int
|
||||
}
|
||||
|
||||
type Anonymous int
|
||||
|
||||
type S1 struct {
|
||||
Exported int
|
||||
unexported int
|
||||
Shadow int // shadows S1.Shadow
|
||||
embed1
|
||||
*embed2
|
||||
Anonymous
|
||||
}
|
||||
|
||||
type Time struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
var intType = reflect.TypeOf(int(0))
|
||||
|
||||
func field(name string, tval interface{}, index ...int) *Field {
|
||||
return &Field{
|
||||
Name: name,
|
||||
Type: reflect.TypeOf(tval),
|
||||
Index: index,
|
||||
}
|
||||
}
|
||||
|
||||
func tfield(name string, tval interface{}, index ...int) *Field {
|
||||
return &Field{
|
||||
Name: name,
|
||||
Type: reflect.TypeOf(tval),
|
||||
Index: index,
|
||||
NameFromTag: true,
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldsNoTags(t *testing.T) {
|
||||
c := NewCache(nil, nil, nil)
|
||||
got, err := c.Fields(reflect.TypeOf(S1{}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := []*Field{
|
||||
field("Exported", int(0), 0),
|
||||
field("Shadow", int(0), 2),
|
||||
field("Em1", int(0), 3, 0),
|
||||
field("Em4", int(0), 4, 2, 0),
|
||||
field("Anonymous", Anonymous(0), 5),
|
||||
}
|
||||
if msg, ok := compareFields(got, want); !ok {
|
||||
t.Error(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgainstJSONEncodingNoTags(t *testing.T) {
|
||||
// Demonstrates that this package produces the same set of fields as encoding/json.
|
||||
s1 := S1{
|
||||
Exported: 1,
|
||||
unexported: 2,
|
||||
Shadow: 3,
|
||||
embed1: embed1{
|
||||
Em1: 4,
|
||||
Dup: 5,
|
||||
Shadow: 6,
|
||||
embed3: embed3{
|
||||
Em3: 7,
|
||||
embed5: embed5{x: 8},
|
||||
},
|
||||
},
|
||||
embed2: &embed2{
|
||||
Dup: 9,
|
||||
embed3: embed3{
|
||||
Em3: 10,
|
||||
embed5: embed5{x: 11},
|
||||
},
|
||||
embed4: embed4{
|
||||
Em4: 12,
|
||||
Dup: 13,
|
||||
embed1: &embed1{Em1: 14},
|
||||
},
|
||||
},
|
||||
Anonymous: Anonymous(15),
|
||||
}
|
||||
var want S1
|
||||
jsonRoundTrip(t, s1, &want)
|
||||
var got S1
|
||||
got.embed2 = &embed2{} // need this because reflection won't create it
|
||||
fields, err := NewCache(nil, nil, nil).Fields(reflect.TypeOf(got))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
setFields(fields, &got, s1)
|
||||
if !testutil.Equal(got, want,
|
||||
cmp.AllowUnexported(S1{}, embed1{}, embed2{}, embed3{}, embed4{}, embed5{})) {
|
||||
t.Errorf("got\n%+v\nwant\n%+v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests use of LeafTypes parameter to NewCache
|
||||
func TestAgainstJSONEncodingEmbeddedTime(t *testing.T) {
|
||||
timeLeafFn := func(t reflect.Type) bool {
|
||||
return t == reflect.TypeOf(time.Time{})
|
||||
}
|
||||
// Demonstrates that this package can produce the same set of
|
||||
// fields as encoding/json for a struct with an embedded time.Time.
|
||||
now := time.Now().UTC()
|
||||
myt := Time{
|
||||
now,
|
||||
}
|
||||
var want Time
|
||||
jsonRoundTrip(t, myt, &want)
|
||||
var got Time
|
||||
fields, err := NewCache(nil, nil, timeLeafFn).Fields(reflect.TypeOf(got))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
setFields(fields, &got, myt)
|
||||
if !testutil.Equal(got, want) {
|
||||
t.Errorf("got\n%+v\nwant\n%+v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
type S2 struct {
|
||||
NoTag int
|
||||
XXX int `json:"tag"` // tag name takes precedence
|
||||
Anonymous `json:"anon"` // anonymous non-structs also get their name from the tag
|
||||
unexported int `json:"tag"`
|
||||
Embed `json:"em"` // embedded structs with tags become fields
|
||||
Tag int
|
||||
YYY int `json:"Tag"` // tag takes precedence over untagged field of the same name
|
||||
Empty int `json:""` // empty tag is noop
|
||||
tEmbed1
|
||||
tEmbed2
|
||||
}
|
||||
|
||||
type Embed struct {
|
||||
Em int
|
||||
}
|
||||
|
||||
type tEmbed1 struct {
|
||||
Dup int
|
||||
X int `json:"Dup2"`
|
||||
}
|
||||
|
||||
type tEmbed2 struct {
|
||||
Y int `json:"Dup"` // takes precedence over tEmbed1.Dup because it is tagged
|
||||
Z int `json:"Dup2"` // same name as tEmbed1.X and both tagged, so ignored
|
||||
}
|
||||
|
||||
func jsonTagParser(t reflect.StructTag) (name string, keep bool, other interface{}, err error) {
|
||||
s := t.Get("json")
|
||||
parts := strings.Split(s, ",")
|
||||
if parts[0] == "-" {
|
||||
return "", false, nil, nil
|
||||
}
|
||||
if len(parts) > 1 {
|
||||
other = parts[1:]
|
||||
}
|
||||
return parts[0], true, other, nil
|
||||
}
|
||||
|
||||
func validateFunc(t reflect.Type) (err error) {
|
||||
if t.Kind() != reflect.Struct {
|
||||
return errors.New("non-struct type used")
|
||||
}
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
if t.Field(i).Type.Kind() == reflect.Slice {
|
||||
return fmt.Errorf("slice field found at field %s on struct %s", t.Field(i).Name, t.Name())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestFieldsWithTags(t *testing.T) {
|
||||
got, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S2{}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := []*Field{
|
||||
field("NoTag", int(0), 0),
|
||||
tfield("tag", int(0), 1),
|
||||
tfield("anon", Anonymous(0), 2),
|
||||
tfield("em", Embed{}, 4),
|
||||
tfield("Tag", int(0), 6),
|
||||
field("Empty", int(0), 7),
|
||||
tfield("Dup", int(0), 8, 0),
|
||||
}
|
||||
if msg, ok := compareFields(got, want); !ok {
|
||||
t.Error(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgainstJSONEncodingWithTags(t *testing.T) {
|
||||
// Demonstrates that this package produces the same set of fields as encoding/json.
|
||||
s2 := S2{
|
||||
NoTag: 1,
|
||||
XXX: 2,
|
||||
Anonymous: 3,
|
||||
Embed: Embed{
|
||||
Em: 4,
|
||||
},
|
||||
tEmbed1: tEmbed1{
|
||||
Dup: 5,
|
||||
X: 6,
|
||||
},
|
||||
tEmbed2: tEmbed2{
|
||||
Y: 7,
|
||||
Z: 8,
|
||||
},
|
||||
}
|
||||
var want S2
|
||||
jsonRoundTrip(t, s2, &want)
|
||||
var got S2
|
||||
fields, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(got))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
setFields(fields, &got, s2)
|
||||
if !testutil.Equal(got, want, cmp.AllowUnexported(S2{})) {
|
||||
t.Errorf("got\n%+v\nwant\n%+v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnexportedAnonymousNonStruct(t *testing.T) {
|
||||
// An unexported anonymous non-struct field should not be recorded.
|
||||
// This is currently a bug in encoding/json.
|
||||
// https://github.com/golang/go/issues/18009
|
||||
type (
|
||||
u int
|
||||
v int
|
||||
S struct {
|
||||
u
|
||||
v `json:"x"`
|
||||
int
|
||||
}
|
||||
)
|
||||
|
||||
got, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S{}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(got) != 0 {
|
||||
t.Errorf("got %d fields, want 0", len(got))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnexportedAnonymousStruct(t *testing.T) {
|
||||
// An unexported anonymous struct with a tag is ignored.
|
||||
// This is currently a bug in encoding/json.
|
||||
// https://github.com/golang/go/issues/18009
|
||||
type (
|
||||
s1 struct{ X int }
|
||||
S2 struct {
|
||||
s1 `json:"Y"`
|
||||
}
|
||||
)
|
||||
got, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S2{}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(got) != 0 {
|
||||
t.Errorf("got %d fields, want 0", len(got))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDominantField(t *testing.T) {
|
||||
// With fields sorted by index length and then by tag presence,
|
||||
// the dominant field is always the first. Make sure all error
|
||||
// cases are caught.
|
||||
for _, test := range []struct {
|
||||
fields []Field
|
||||
wantOK bool
|
||||
}{
|
||||
// A single field is OK.
|
||||
{[]Field{{Index: []int{0}}}, true},
|
||||
{[]Field{{Index: []int{0}, NameFromTag: true}}, true},
|
||||
// A single field at top level is OK.
|
||||
{[]Field{{Index: []int{0}}, {Index: []int{1, 0}}}, true},
|
||||
{[]Field{{Index: []int{0}}, {Index: []int{1, 0}, NameFromTag: true}}, true},
|
||||
{[]Field{{Index: []int{0}, NameFromTag: true}, {Index: []int{1, 0}, NameFromTag: true}}, true},
|
||||
// A single tagged field is OK.
|
||||
{[]Field{{Index: []int{0}, NameFromTag: true}, {Index: []int{1}}}, true},
|
||||
// Two untagged fields at the same level is an error.
|
||||
{[]Field{{Index: []int{0}}, {Index: []int{1}}}, false},
|
||||
// Two tagged fields at the same level is an error.
|
||||
{[]Field{{Index: []int{0}, NameFromTag: true}, {Index: []int{1}, NameFromTag: true}}, false},
|
||||
} {
|
||||
_, gotOK := dominantField(test.fields)
|
||||
if gotOK != test.wantOK {
|
||||
t.Errorf("%v: got %t, want %t", test.fields, gotOK, test.wantOK)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIgnore(t *testing.T) {
|
||||
type S struct {
|
||||
X int `json:"-"`
|
||||
}
|
||||
got, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S{}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(got) != 0 {
|
||||
t.Errorf("got %d fields, want 0", len(got))
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsedTag(t *testing.T) {
|
||||
type S struct {
|
||||
X int `json:"name,omitempty"`
|
||||
}
|
||||
got, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S{}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := []*Field{
|
||||
{Name: "name", NameFromTag: true, Type: intType,
|
||||
Index: []int{0}, ParsedTag: []string{"omitempty"}},
|
||||
}
|
||||
if msg, ok := compareFields(got, want); !ok {
|
||||
t.Error(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateFunc(t *testing.T) {
|
||||
type MyInvalidStruct struct {
|
||||
A string
|
||||
B []int
|
||||
}
|
||||
|
||||
_, err := NewCache(nil, validateFunc, nil).Fields(reflect.TypeOf(MyInvalidStruct{}))
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
|
||||
type MyValidStruct struct {
|
||||
A string
|
||||
B int
|
||||
}
|
||||
_, err = NewCache(nil, validateFunc, nil).Fields(reflect.TypeOf(MyValidStruct{}))
|
||||
if err != nil {
|
||||
t.Fatalf("expected nil, got error: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func compareFields(got []Field, want []*Field) (msg string, ok bool) {
|
||||
if len(got) != len(want) {
|
||||
return fmt.Sprintf("got %d fields, want %d", len(got), len(want)), false
|
||||
}
|
||||
for i, g := range got {
|
||||
w := *want[i]
|
||||
if !fieldsEqual(&g, &w) {
|
||||
return fmt.Sprintf("got %+v, want %+v", g, w), false
|
||||
}
|
||||
}
|
||||
return "", true
|
||||
}
|
||||
|
||||
// Need this because Field contains a function, which cannot be compared even
|
||||
// by testutil.Equal.
|
||||
func fieldsEqual(f1, f2 *Field) bool {
|
||||
if f1 == nil || f2 == nil {
|
||||
return f1 == f2
|
||||
}
|
||||
return f1.Name == f2.Name &&
|
||||
f1.NameFromTag == f2.NameFromTag &&
|
||||
f1.Type == f2.Type &&
|
||||
testutil.Equal(f1.ParsedTag, f2.ParsedTag)
|
||||
}
|
||||
|
||||
// Set the fields of dst from those of src.
|
||||
// dst must be a pointer to a struct value.
|
||||
// src must be a struct value.
|
||||
func setFields(fields []Field, dst, src interface{}) {
|
||||
vsrc := reflect.ValueOf(src)
|
||||
vdst := reflect.ValueOf(dst).Elem()
|
||||
for _, f := range fields {
|
||||
fdst := vdst.FieldByIndex(f.Index)
|
||||
fsrc := vsrc.FieldByIndex(f.Index)
|
||||
fdst.Set(fsrc)
|
||||
}
|
||||
}
|
||||
|
||||
func jsonRoundTrip(t *testing.T, in, out interface{}) {
|
||||
bytes, err := json.Marshal(in)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := json.Unmarshal(bytes, out); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
type S3 struct {
|
||||
S4
|
||||
Abc int
|
||||
AbC int
|
||||
Tag int
|
||||
X int `json:"Tag"`
|
||||
unexported int
|
||||
}
|
||||
|
||||
type S4 struct {
|
||||
ABc int
|
||||
Y int `json:"Abc"` // ignored because of top-level Abc
|
||||
}
|
||||
|
||||
func TestMatchingField(t *testing.T) {
|
||||
fields, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S3{}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
want *Field
|
||||
}{
|
||||
// Exact match wins.
|
||||
{"Abc", field("Abc", int(0), 1)},
|
||||
{"AbC", field("AbC", int(0), 2)},
|
||||
{"ABc", field("ABc", int(0), 0, 0)},
|
||||
// If there are multiple matches but no exact match or tag,
|
||||
// the first field wins, lexicographically by index.
|
||||
// Here, "ABc" is at a deeper embedding level, but since S4 appears
|
||||
// first in S3, its index precedes the other fields of S3.
|
||||
{"abc", field("ABc", int(0), 0, 0)},
|
||||
// Tag name takes precedence over untagged field of the same name.
|
||||
{"Tag", tfield("Tag", int(0), 4)},
|
||||
// Unexported fields disappear.
|
||||
{"unexported", nil},
|
||||
// Untagged embedded structs disappear.
|
||||
{"S4", nil},
|
||||
} {
|
||||
if got := fields.Match(test.name); !fieldsEqual(got, test.want) {
|
||||
t.Errorf("match %q:\ngot %+v\nwant %+v", test.name, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgainstJSONMatchingField(t *testing.T) {
|
||||
s3 := S3{
|
||||
S4: S4{ABc: 1, Y: 2},
|
||||
Abc: 3,
|
||||
AbC: 4,
|
||||
Tag: 5,
|
||||
X: 6,
|
||||
unexported: 7,
|
||||
}
|
||||
var want S3
|
||||
jsonRoundTrip(t, s3, &want)
|
||||
v := reflect.ValueOf(want)
|
||||
fields, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S3{}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
got int
|
||||
}{
|
||||
{"Abc", 3},
|
||||
{"AbC", 4},
|
||||
{"ABc", 1},
|
||||
{"abc", 1},
|
||||
{"Tag", 6},
|
||||
} {
|
||||
f := fields.Match(test.name)
|
||||
if f == nil {
|
||||
t.Fatalf("%s: no match", test.name)
|
||||
}
|
||||
w := v.FieldByIndex(f.Index).Interface()
|
||||
if test.got != w {
|
||||
t.Errorf("%s: got %d, want %d", test.name, test.got, w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTagErrors(t *testing.T) {
|
||||
called := false
|
||||
c := NewCache(func(t reflect.StructTag) (string, bool, interface{}, error) {
|
||||
called = true
|
||||
s := t.Get("f")
|
||||
if s == "bad" {
|
||||
return "", false, nil, errors.New("error")
|
||||
}
|
||||
return s, true, nil, nil
|
||||
}, nil, nil)
|
||||
|
||||
type T struct {
|
||||
X int `f:"ok"`
|
||||
Y int `f:"bad"`
|
||||
}
|
||||
|
||||
_, err := c.Fields(reflect.TypeOf(T{}))
|
||||
if !called {
|
||||
t.Fatal("tag parser not called")
|
||||
}
|
||||
if err == nil {
|
||||
t.Error("want error, got nil")
|
||||
}
|
||||
// Second time, we should cache the error.
|
||||
called = false
|
||||
_, err = c.Fields(reflect.TypeOf(T{}))
|
||||
if called {
|
||||
t.Fatal("tag parser called on second time")
|
||||
}
|
||||
if err == nil {
|
||||
t.Error("want error, got nil")
|
||||
}
|
||||
}
|
||||
156
vendor/cloud.google.com/go/internal/fields/fold.go
generated
vendored
Normal file
156
vendor/cloud.google.com/go/internal/fields/fold.go
generated
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 fields
|
||||
|
||||
// This file was copied from https://go.googlesource.com/go/+/go1.7.3/src/encoding/json/fold.go.
|
||||
// Only the license and package were changed.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
caseMask = ^byte(0x20) // Mask to ignore case in ASCII.
|
||||
kelvin = '\u212a'
|
||||
smallLongEss = '\u017f'
|
||||
)
|
||||
|
||||
// foldFunc returns one of four different case folding equivalence
|
||||
// functions, from most general (and slow) to fastest:
|
||||
//
|
||||
// 1) bytes.EqualFold, if the key s contains any non-ASCII UTF-8
|
||||
// 2) equalFoldRight, if s contains special folding ASCII ('k', 'K', 's', 'S')
|
||||
// 3) asciiEqualFold, no special, but includes non-letters (including _)
|
||||
// 4) simpleLetterEqualFold, no specials, no non-letters.
|
||||
//
|
||||
// The letters S and K are special because they map to 3 runes, not just 2:
|
||||
// * S maps to s and to U+017F 'ſ' Latin small letter long s
|
||||
// * k maps to K and to U+212A 'K' Kelvin sign
|
||||
// See https://play.golang.org/p/tTxjOc0OGo
|
||||
//
|
||||
// The returned function is specialized for matching against s and
|
||||
// should only be given s. It's not curried for performance reasons.
|
||||
func foldFunc(s []byte) func(s, t []byte) bool {
|
||||
nonLetter := false
|
||||
special := false // special letter
|
||||
for _, b := range s {
|
||||
if b >= utf8.RuneSelf {
|
||||
return bytes.EqualFold
|
||||
}
|
||||
upper := b & caseMask
|
||||
if upper < 'A' || upper > 'Z' {
|
||||
nonLetter = true
|
||||
} else if upper == 'K' || upper == 'S' {
|
||||
// See above for why these letters are special.
|
||||
special = true
|
||||
}
|
||||
}
|
||||
if special {
|
||||
return equalFoldRight
|
||||
}
|
||||
if nonLetter {
|
||||
return asciiEqualFold
|
||||
}
|
||||
return simpleLetterEqualFold
|
||||
}
|
||||
|
||||
// equalFoldRight is a specialization of bytes.EqualFold when s is
|
||||
// known to be all ASCII (including punctuation), but contains an 's',
|
||||
// 'S', 'k', or 'K', requiring a Unicode fold on the bytes in t.
|
||||
// See comments on foldFunc.
|
||||
func equalFoldRight(s, t []byte) bool {
|
||||
for _, sb := range s {
|
||||
if len(t) == 0 {
|
||||
return false
|
||||
}
|
||||
tb := t[0]
|
||||
if tb < utf8.RuneSelf {
|
||||
if sb != tb {
|
||||
sbUpper := sb & caseMask
|
||||
if 'A' <= sbUpper && sbUpper <= 'Z' {
|
||||
if sbUpper != tb&caseMask {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
t = t[1:]
|
||||
continue
|
||||
}
|
||||
// sb is ASCII and t is not. t must be either kelvin
|
||||
// sign or long s; sb must be s, S, k, or K.
|
||||
tr, size := utf8.DecodeRune(t)
|
||||
switch sb {
|
||||
case 's', 'S':
|
||||
if tr != smallLongEss {
|
||||
return false
|
||||
}
|
||||
case 'k', 'K':
|
||||
if tr != kelvin {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
t = t[size:]
|
||||
|
||||
}
|
||||
if len(t) > 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// asciiEqualFold is a specialization of bytes.EqualFold for use when
|
||||
// s is all ASCII (but may contain non-letters) and contains no
|
||||
// special-folding letters.
|
||||
// See comments on foldFunc.
|
||||
func asciiEqualFold(s, t []byte) bool {
|
||||
if len(s) != len(t) {
|
||||
return false
|
||||
}
|
||||
for i, sb := range s {
|
||||
tb := t[i]
|
||||
if sb == tb {
|
||||
continue
|
||||
}
|
||||
if ('a' <= sb && sb <= 'z') || ('A' <= sb && sb <= 'Z') {
|
||||
if sb&caseMask != tb&caseMask {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// simpleLetterEqualFold is a specialization of bytes.EqualFold for
|
||||
// use when s is all ASCII letters (no underscores, etc) and also
|
||||
// doesn't contain 'k', 'K', 's', or 'S'.
|
||||
// See comments on foldFunc.
|
||||
func simpleLetterEqualFold(s, t []byte) bool {
|
||||
if len(s) != len(t) {
|
||||
return false
|
||||
}
|
||||
for i, b := range s {
|
||||
if b&caseMask != t[i]&caseMask {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
129
vendor/cloud.google.com/go/internal/fields/fold_test.go
generated
vendored
Normal file
129
vendor/cloud.google.com/go/internal/fields/fold_test.go
generated
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 fields
|
||||
|
||||
// This file was copied from https://go.googlesource.com/go/+/go1.7.3/src/encoding/json/fold_test.go.
|
||||
// Only the license and package were changed.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var foldTests = []struct {
|
||||
fn func(s, t []byte) bool
|
||||
s, t string
|
||||
want bool
|
||||
}{
|
||||
{equalFoldRight, "", "", true},
|
||||
{equalFoldRight, "a", "a", true},
|
||||
{equalFoldRight, "", "a", false},
|
||||
{equalFoldRight, "a", "", false},
|
||||
{equalFoldRight, "a", "A", true},
|
||||
{equalFoldRight, "AB", "ab", true},
|
||||
{equalFoldRight, "AB", "ac", false},
|
||||
{equalFoldRight, "sbkKc", "ſbKKc", true},
|
||||
{equalFoldRight, "SbKkc", "ſbKKc", true},
|
||||
{equalFoldRight, "SbKkc", "ſbKK", false},
|
||||
{equalFoldRight, "e", "é", false},
|
||||
{equalFoldRight, "s", "S", true},
|
||||
|
||||
{simpleLetterEqualFold, "", "", true},
|
||||
{simpleLetterEqualFold, "abc", "abc", true},
|
||||
{simpleLetterEqualFold, "abc", "ABC", true},
|
||||
{simpleLetterEqualFold, "abc", "ABCD", false},
|
||||
{simpleLetterEqualFold, "abc", "xxx", false},
|
||||
|
||||
{asciiEqualFold, "a_B", "A_b", true},
|
||||
{asciiEqualFold, "aa@", "aa`", false}, // verify 0x40 and 0x60 aren't case-equivalent
|
||||
}
|
||||
|
||||
func TestFold(t *testing.T) {
|
||||
for i, tt := range foldTests {
|
||||
if got := tt.fn([]byte(tt.s), []byte(tt.t)); got != tt.want {
|
||||
t.Errorf("%d. %q, %q = %v; want %v", i, tt.s, tt.t, got, tt.want)
|
||||
}
|
||||
truth := strings.EqualFold(tt.s, tt.t)
|
||||
if truth != tt.want {
|
||||
t.Errorf("strings.EqualFold doesn't agree with case %d", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFoldAgainstUnicode(t *testing.T) {
|
||||
const bufSize = 5
|
||||
buf1 := make([]byte, 0, bufSize)
|
||||
buf2 := make([]byte, 0, bufSize)
|
||||
var runes []rune
|
||||
for i := 0x20; i <= 0x7f; i++ {
|
||||
runes = append(runes, rune(i))
|
||||
}
|
||||
runes = append(runes, kelvin, smallLongEss)
|
||||
|
||||
funcs := []struct {
|
||||
name string
|
||||
fold func(s, t []byte) bool
|
||||
letter bool // must be ASCII letter
|
||||
simple bool // must be simple ASCII letter (not 'S' or 'K')
|
||||
}{
|
||||
{
|
||||
name: "equalFoldRight",
|
||||
fold: equalFoldRight,
|
||||
},
|
||||
{
|
||||
name: "asciiEqualFold",
|
||||
fold: asciiEqualFold,
|
||||
simple: true,
|
||||
},
|
||||
{
|
||||
name: "simpleLetterEqualFold",
|
||||
fold: simpleLetterEqualFold,
|
||||
simple: true,
|
||||
letter: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, ff := range funcs {
|
||||
for _, r := range runes {
|
||||
if r >= utf8.RuneSelf {
|
||||
continue
|
||||
}
|
||||
if ff.letter && !isASCIILetter(byte(r)) {
|
||||
continue
|
||||
}
|
||||
if ff.simple && (r == 's' || r == 'S' || r == 'k' || r == 'K') {
|
||||
continue
|
||||
}
|
||||
for _, r2 := range runes {
|
||||
buf1 := append(buf1[:0], 'x')
|
||||
buf2 := append(buf2[:0], 'x')
|
||||
buf1 = buf1[:1+utf8.EncodeRune(buf1[1:bufSize], r)]
|
||||
buf2 = buf2[:1+utf8.EncodeRune(buf2[1:bufSize], r2)]
|
||||
buf1 = append(buf1, 'x')
|
||||
buf2 = append(buf2, 'x')
|
||||
want := bytes.EqualFold(buf1, buf2)
|
||||
if got := ff.fold(buf1, buf2); got != want {
|
||||
t.Errorf("%s(%q, %q) = %v; want %v", ff.name, buf1, buf2, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isASCIILetter(b byte) bool {
|
||||
return ('A' <= b && b <= 'Z') || ('a' <= b && b <= 'z')
|
||||
}
|
||||
44
vendor/cloud.google.com/go/internal/kokoro/build.sh
generated
vendored
Executable file
44
vendor/cloud.google.com/go/internal/kokoro/build.sh
generated
vendored
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Fail on any error
|
||||
set -eo pipefail
|
||||
|
||||
# Display commands being run
|
||||
set -x
|
||||
|
||||
# cd to project dir on Kokoro instance
|
||||
cd git/gocloud
|
||||
|
||||
go version
|
||||
|
||||
# Set $GOPATH
|
||||
export GOPATH="$HOME/go"
|
||||
GOCLOUD_HOME=$GOPATH/src/cloud.google.com/go
|
||||
mkdir -p $GOCLOUD_HOME
|
||||
|
||||
# Move code into $GOPATH and get dependencies
|
||||
cp -R ./* $GOCLOUD_HOME
|
||||
cd $GOCLOUD_HOME
|
||||
go get -v ./...
|
||||
|
||||
# # Don't run integration tests until we can protect against code from
|
||||
# # untrusted forks reading and storing our service account key.
|
||||
# cd internal/kokoro
|
||||
# # Don't print out encryption keys, etc
|
||||
# set +x
|
||||
# key=$(cat $KOKORO_ARTIFACTS_DIR/keystore/*_encrypted_ba2d6f7723ed_key)
|
||||
# iv=$(cat $KOKORO_ARTIFACTS_DIR/keystore/*_encrypted_ba2d6f7723ed_iv)
|
||||
# pass=$(cat $KOKORO_ARTIFACTS_DIR/keystore/*_encrypted_ba2d6f7723ed_pass)
|
||||
|
||||
# openssl aes-256-cbc -K $key -iv $iv -pass pass:$pass -in kokoro-key.json.enc -out key.json -d
|
||||
# set -x
|
||||
|
||||
# export GCLOUD_TESTS_GOLANG_KEY="$(pwd)/key.json"
|
||||
# export GCLOUD_TESTS_GOLANG_PROJECT_ID="dulcet-port-762"
|
||||
# cd $GOCLOUD_HOME
|
||||
|
||||
# Run tests and tee output to log file, to be pushed to GCS as artifact.
|
||||
go test -race -v -short ./... 2>&1 | tee $KOKORO_ARTIFACTS_DIR/$KOKORO_GERRIT_REVISION.log
|
||||
|
||||
# Make sure README.md is up to date.
|
||||
make -C internal/readme test diff
|
||||
BIN
vendor/cloud.google.com/go/internal/kokoro/kokoro-key.json.enc
generated
vendored
Normal file
BIN
vendor/cloud.google.com/go/internal/kokoro/kokoro-key.json.enc
generated
vendored
Normal file
Binary file not shown.
94
vendor/cloud.google.com/go/internal/optional/optional.go
generated
vendored
Normal file
94
vendor/cloud.google.com/go/internal/optional/optional.go
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 optional provides versions of primitive types that can
|
||||
// be nil. These are useful in methods that update some of an API object's
|
||||
// fields.
|
||||
package optional
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
// Bool is either a bool or nil.
|
||||
Bool interface{}
|
||||
|
||||
// String is either a string or nil.
|
||||
String interface{}
|
||||
|
||||
// Int is either an int or nil.
|
||||
Int interface{}
|
||||
|
||||
// Uint is either a uint or nil.
|
||||
Uint interface{}
|
||||
|
||||
// Float64 is either a float64 or nil.
|
||||
Float64 interface{}
|
||||
)
|
||||
|
||||
// ToBool returns its argument as a bool.
|
||||
// It panics if its argument is nil or not a bool.
|
||||
func ToBool(v Bool) bool {
|
||||
x, ok := v.(bool)
|
||||
if !ok {
|
||||
doPanic("Bool", v)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// ToString returns its argument as a string.
|
||||
// It panics if its argument is nil or not a string.
|
||||
func ToString(v String) string {
|
||||
x, ok := v.(string)
|
||||
if !ok {
|
||||
doPanic("String", v)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// ToInt returns its argument as an int.
|
||||
// It panics if its argument is nil or not an int.
|
||||
func ToInt(v Int) int {
|
||||
x, ok := v.(int)
|
||||
if !ok {
|
||||
doPanic("Int", v)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// ToUint returns its argument as a uint.
|
||||
// It panics if its argument is nil or not a uint.
|
||||
func ToUint(v Uint) uint {
|
||||
x, ok := v.(uint)
|
||||
if !ok {
|
||||
doPanic("Uint", v)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// ToFloat64 returns its argument as a float64.
|
||||
// It panics if its argument is nil or not a float64.
|
||||
func ToFloat64(v Float64) float64 {
|
||||
x, ok := v.(float64)
|
||||
if !ok {
|
||||
doPanic("Float64", v)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func doPanic(capType string, v interface{}) {
|
||||
panic(fmt.Sprintf("optional.%s value should be %s, got %T", capType, strings.ToLower(capType), v))
|
||||
}
|
||||
64
vendor/cloud.google.com/go/internal/optional/optional_test.go
generated
vendored
Normal file
64
vendor/cloud.google.com/go/internal/optional/optional_test.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 optional
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestConvertSuccess(t *testing.T) {
|
||||
if got, want := ToBool(false), false; got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
if got, want := ToString(""), ""; got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
if got, want := ToInt(0), 0; got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
if got, want := ToUint(uint(0)), uint(0); got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
if got, want := ToFloat64(0.0), 0.0; got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertFailure(t *testing.T) {
|
||||
for _, f := range []func(){
|
||||
func() { ToBool(nil) },
|
||||
func() { ToBool(3) },
|
||||
func() { ToString(nil) },
|
||||
func() { ToString(3) },
|
||||
func() { ToInt(nil) },
|
||||
func() { ToInt("") },
|
||||
func() { ToUint(nil) },
|
||||
func() { ToUint("") },
|
||||
func() { ToFloat64(nil) },
|
||||
func() { ToFloat64("") },
|
||||
} {
|
||||
if !panics(f) {
|
||||
t.Error("got no panic, want panic")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func panics(f func()) (b bool) {
|
||||
defer func() {
|
||||
if recover() != nil {
|
||||
b = true
|
||||
}
|
||||
}()
|
||||
f()
|
||||
return false
|
||||
}
|
||||
78
vendor/cloud.google.com/go/internal/pretty/diff.go
generated
vendored
Normal file
78
vendor/cloud.google.com/go/internal/pretty/diff.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 pretty
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Diff compares the pretty-printed representation of two values. The second
|
||||
// return value reports whether the two values' representations are identical.
|
||||
// If it is false, the first return value contains the diffs.
|
||||
//
|
||||
// The output labels the first value "want" and the second "got".
|
||||
//
|
||||
// Diff works by invoking the "diff" command. It will only succeed in
|
||||
// environments where "diff" is on the shell path.
|
||||
func Diff(want, got interface{}) (string, bool, error) {
|
||||
fname1, err := writeToTemp(want)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
defer os.Remove(fname1)
|
||||
|
||||
fname2, err := writeToTemp(got)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
defer os.Remove(fname2)
|
||||
|
||||
cmd := exec.Command("diff", "-u", "--label=want", "--label=got", fname1, fname2)
|
||||
out, err := cmd.Output()
|
||||
if err == nil {
|
||||
return string(out), true, nil
|
||||
}
|
||||
eerr, ok := err.(*exec.ExitError)
|
||||
if !ok {
|
||||
return "", false, err
|
||||
}
|
||||
ws, ok := eerr.Sys().(syscall.WaitStatus)
|
||||
if !ok {
|
||||
return "", false, err
|
||||
}
|
||||
if ws.ExitStatus() != 1 {
|
||||
return "", false, err
|
||||
}
|
||||
// Exit status of 1 means no error, but diffs were found.
|
||||
return string(out), false, nil
|
||||
}
|
||||
|
||||
func writeToTemp(v interface{}) (string, error) {
|
||||
f, err := ioutil.TempFile("", "prettyDiff")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err := fmt.Fprintf(f, "%+v\n", Value(v)); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return f.Name(), nil
|
||||
}
|
||||
50
vendor/cloud.google.com/go/internal/pretty/diff_test.go
generated
vendored
Normal file
50
vendor/cloud.google.com/go/internal/pretty/diff_test.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 pretty
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestDiff(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
v1, v2 interface{}
|
||||
ok bool
|
||||
want string
|
||||
}{
|
||||
{5, 5, true, ""},
|
||||
{"foo", "foo", true, ""},
|
||||
{[]int{1, 2, 3}, []int{1, 0, 3}, false, `--- want
|
||||
+++ got
|
||||
@@ -1,5 +1,5 @@
|
||||
[]int{
|
||||
1,
|
||||
- 2,
|
||||
+ 0,
|
||||
3,
|
||||
}
|
||||
`},
|
||||
} {
|
||||
got, ok, err := Diff(test.v1, test.v2)
|
||||
if err != nil {
|
||||
t.Errorf("%v vs. %v: %v", test.v1, test.v2, err)
|
||||
continue
|
||||
}
|
||||
if ok != test.ok {
|
||||
t.Errorf("%v vs. %v: got %t, want %t", test.v1, test.v2, ok, test.ok)
|
||||
}
|
||||
if got != test.want {
|
||||
t.Errorf("%v vs. %v: got:\n%q\nwant:\n%q", test.v1, test.v2, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
254
vendor/cloud.google.com/go/internal/pretty/pretty.go
generated
vendored
Normal file
254
vendor/cloud.google.com/go/internal/pretty/pretty.go
generated
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 pretty implements a simple pretty-printer. It is intended for
|
||||
// debugging the output of tests.
|
||||
//
|
||||
// It follows pointers and produces multi-line output for complex values like
|
||||
// slices, maps and structs.
|
||||
package pretty
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Indent is the string output at each level of indentation.
|
||||
var Indent = " "
|
||||
|
||||
// Value returns a value that will print prettily when used as an
|
||||
// argument for the %v or %s format specifiers.
|
||||
// With no flags, struct fields and map keys with default values are omitted.
|
||||
// With the '+' or '#' flags, all values are displayed.
|
||||
//
|
||||
// This package does not detect cycles. Attempting to print a Value that
|
||||
// contains cycles will result in unbounded recursion.
|
||||
func Value(v interface{}) val { return val{v: v} }
|
||||
|
||||
type val struct{ v interface{} }
|
||||
|
||||
// Format implements the fmt.Formatter interface.
|
||||
func (v val) Format(s fmt.State, c rune) {
|
||||
if c == 'v' || c == 's' {
|
||||
fprint(s, reflect.ValueOf(v.v), state{
|
||||
defaults: s.Flag('+') || s.Flag('#'),
|
||||
})
|
||||
} else {
|
||||
fmt.Fprintf(s, "%%!%c(pretty.Val)", c)
|
||||
}
|
||||
}
|
||||
|
||||
type state struct {
|
||||
level int
|
||||
prefix, suffix string
|
||||
defaults bool
|
||||
}
|
||||
|
||||
const maxLevel = 100
|
||||
|
||||
var typeOfTime = reflect.TypeOf(time.Time{})
|
||||
|
||||
func fprint(w io.Writer, v reflect.Value, s state) {
|
||||
if s.level > maxLevel {
|
||||
fmt.Fprintln(w, "pretty: max nested depth exceeded")
|
||||
return
|
||||
}
|
||||
indent := strings.Repeat(Indent, s.level)
|
||||
fmt.Fprintf(w, "%s%s", indent, s.prefix)
|
||||
if isNil(v) {
|
||||
fmt.Fprintf(w, "nil%s", s.suffix)
|
||||
return
|
||||
}
|
||||
if v.Type().Kind() == reflect.Interface {
|
||||
v = v.Elem()
|
||||
}
|
||||
if v.Type() == typeOfTime {
|
||||
fmt.Fprintf(w, "%s%s", v.Interface(), s.suffix)
|
||||
return
|
||||
}
|
||||
for v.Type().Kind() == reflect.Ptr {
|
||||
fmt.Fprintf(w, "&")
|
||||
v = v.Elem()
|
||||
}
|
||||
switch v.Type().Kind() {
|
||||
default:
|
||||
fmt.Fprintf(w, "%s%s", short(v), s.suffix)
|
||||
|
||||
case reflect.Array:
|
||||
fmt.Fprintf(w, "%s{\n", v.Type())
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
fprint(w, v.Index(i), state{
|
||||
level: s.level + 1,
|
||||
prefix: "",
|
||||
suffix: ",",
|
||||
defaults: s.defaults,
|
||||
})
|
||||
fmt.Fprintln(w)
|
||||
}
|
||||
fmt.Fprintf(w, "%s}", indent)
|
||||
|
||||
case reflect.Slice:
|
||||
fmt.Fprintf(w, "%s{", v.Type())
|
||||
if v.Len() > 0 {
|
||||
fmt.Fprintln(w)
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
fprint(w, v.Index(i), state{
|
||||
level: s.level + 1,
|
||||
prefix: "",
|
||||
suffix: ",",
|
||||
defaults: s.defaults,
|
||||
})
|
||||
fmt.Fprintln(w)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(w, "%s}%s", indent, s.suffix)
|
||||
|
||||
case reflect.Map:
|
||||
fmt.Fprintf(w, "%s{", v.Type())
|
||||
if v.Len() > 0 {
|
||||
fmt.Fprintln(w)
|
||||
keys := v.MapKeys()
|
||||
maybeSort(keys, v.Type().Key())
|
||||
for _, key := range keys {
|
||||
val := v.MapIndex(key)
|
||||
if s.defaults || !isDefault(val) {
|
||||
fprint(w, val, state{
|
||||
level: s.level + 1,
|
||||
prefix: short(key) + ": ",
|
||||
suffix: ",",
|
||||
defaults: s.defaults,
|
||||
})
|
||||
fmt.Fprintln(w)
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(w, "%s}%s", indent, s.suffix)
|
||||
|
||||
case reflect.Struct:
|
||||
t := v.Type()
|
||||
fmt.Fprintf(w, "%s{\n", t)
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
f := v.Field(i)
|
||||
if s.defaults || !isDefault(f) {
|
||||
fprint(w, f, state{
|
||||
level: s.level + 1,
|
||||
prefix: t.Field(i).Name + ": ",
|
||||
suffix: ",",
|
||||
defaults: s.defaults,
|
||||
})
|
||||
fmt.Fprintln(w)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(w, "%s}%s", indent, s.suffix)
|
||||
}
|
||||
}
|
||||
|
||||
func isNil(v reflect.Value) bool {
|
||||
if !v.IsValid() {
|
||||
return true
|
||||
}
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
||||
return v.IsNil()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func isDefault(v reflect.Value) bool {
|
||||
if !v.IsValid() {
|
||||
return true
|
||||
}
|
||||
t := v.Type()
|
||||
switch t.Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
||||
return v.IsNil()
|
||||
default:
|
||||
if !v.CanInterface() {
|
||||
return false
|
||||
}
|
||||
return t.Comparable() && v.Interface() == reflect.Zero(t).Interface()
|
||||
}
|
||||
}
|
||||
|
||||
// short returns a short, one-line string for v.
|
||||
func short(v reflect.Value) string {
|
||||
if !v.IsValid() {
|
||||
return "nil"
|
||||
}
|
||||
if v.Type().Kind() == reflect.String {
|
||||
return fmt.Sprintf("%q", v)
|
||||
}
|
||||
return fmt.Sprintf("%v", v)
|
||||
}
|
||||
|
||||
func indent(w io.Writer, level int) {
|
||||
for i := 0; i < level; i++ {
|
||||
io.WriteString(w, Indent) // ignore errors
|
||||
}
|
||||
}
|
||||
|
||||
func maybeSort(vs []reflect.Value, t reflect.Type) {
|
||||
if less := lessFunc(t); less != nil {
|
||||
sort.Sort(&sorter{vs, less})
|
||||
}
|
||||
}
|
||||
|
||||
// lessFunc returns a function that implements the "<" operator
|
||||
// for the given type, or nil if the type doesn't support "<" .
|
||||
func lessFunc(t reflect.Type) func(v1, v2 interface{}) bool {
|
||||
switch t.Kind() {
|
||||
case reflect.String:
|
||||
return func(v1, v2 interface{}) bool { return v1.(string) < v2.(string) }
|
||||
case reflect.Int:
|
||||
return func(v1, v2 interface{}) bool { return v1.(int) < v2.(int) }
|
||||
case reflect.Int8:
|
||||
return func(v1, v2 interface{}) bool { return v1.(int8) < v2.(int8) }
|
||||
case reflect.Int16:
|
||||
return func(v1, v2 interface{}) bool { return v1.(int16) < v2.(int16) }
|
||||
case reflect.Int32:
|
||||
return func(v1, v2 interface{}) bool { return v1.(int32) < v2.(int32) }
|
||||
case reflect.Int64:
|
||||
return func(v1, v2 interface{}) bool { return v1.(int64) < v2.(int64) }
|
||||
case reflect.Uint:
|
||||
return func(v1, v2 interface{}) bool { return v1.(uint) < v2.(uint) }
|
||||
case reflect.Uint8:
|
||||
return func(v1, v2 interface{}) bool { return v1.(uint8) < v2.(uint8) }
|
||||
case reflect.Uint16:
|
||||
return func(v1, v2 interface{}) bool { return v1.(uint16) < v2.(uint16) }
|
||||
case reflect.Uint32:
|
||||
return func(v1, v2 interface{}) bool { return v1.(uint32) < v2.(uint32) }
|
||||
case reflect.Uint64:
|
||||
return func(v1, v2 interface{}) bool { return v1.(uint64) < v2.(uint64) }
|
||||
case reflect.Float32:
|
||||
return func(v1, v2 interface{}) bool { return v1.(float32) < v2.(float32) }
|
||||
case reflect.Float64:
|
||||
return func(v1, v2 interface{}) bool { return v1.(float64) < v2.(float64) }
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type sorter struct {
|
||||
vs []reflect.Value
|
||||
less func(v1, v2 interface{}) bool
|
||||
}
|
||||
|
||||
func (s *sorter) Len() int { return len(s.vs) }
|
||||
func (s *sorter) Swap(i, j int) { s.vs[i], s.vs[j] = s.vs[j], s.vs[i] }
|
||||
func (s *sorter) Less(i, j int) bool { return s.less(s.vs[i].Interface(), s.vs[j].Interface()) }
|
||||
105
vendor/cloud.google.com/go/internal/pretty/pretty_test.go
generated
vendored
Normal file
105
vendor/cloud.google.com/go/internal/pretty/pretty_test.go
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 pretty
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type S struct {
|
||||
X int
|
||||
Y bool
|
||||
z *string
|
||||
}
|
||||
|
||||
func TestSprint(t *testing.T) {
|
||||
Indent = "~"
|
||||
i := 17
|
||||
|
||||
for _, test := range []struct {
|
||||
value interface{}
|
||||
want string
|
||||
}{
|
||||
// primitives and pointer
|
||||
{nil, "nil"},
|
||||
{3, "3"},
|
||||
{9.8, "9.8"},
|
||||
{true, "true"},
|
||||
{"foo", `"foo"`},
|
||||
{&i, "&17"},
|
||||
// array and slice
|
||||
{[3]int{1, 2, 3}, "[3]int{\n~1,\n~2,\n~3,\n}"},
|
||||
{[]int{1, 2, 3}, "[]int{\n~1,\n~2,\n~3,\n}"},
|
||||
{[]int{}, "[]int{}"},
|
||||
{[]string{"foo"}, "[]string{\n~\"foo\",\n}"},
|
||||
// map
|
||||
{map[int]bool{}, "map[int]bool{}"},
|
||||
{map[int]bool{1: true, 2: false, 3: true},
|
||||
"map[int]bool{\n~1: true,\n~3: true,\n}"},
|
||||
// struct
|
||||
{S{}, "pretty.S{\n}"},
|
||||
{S{3, true, ptr("foo")},
|
||||
"pretty.S{\n~X: 3,\n~Y: true,\n~z: &\"foo\",\n}"},
|
||||
// interface
|
||||
{[]interface{}{&i}, "[]interface {}{\n~&17,\n}"},
|
||||
// nesting
|
||||
{[]S{{1, false, ptr("a")}, {2, true, ptr("b")}},
|
||||
`[]pretty.S{
|
||||
~pretty.S{
|
||||
~~X: 1,
|
||||
~~z: &"a",
|
||||
~},
|
||||
~pretty.S{
|
||||
~~X: 2,
|
||||
~~Y: true,
|
||||
~~z: &"b",
|
||||
~},
|
||||
}`},
|
||||
} {
|
||||
got := fmt.Sprintf("%v", Value(test.value))
|
||||
if got != test.want {
|
||||
t.Errorf("%v: got:\n%q\nwant:\n%q", test.value, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithDefaults(t *testing.T) {
|
||||
Indent = "~"
|
||||
for _, test := range []struct {
|
||||
value interface{}
|
||||
want string
|
||||
}{
|
||||
{map[int]bool{1: true, 2: false, 3: true},
|
||||
"map[int]bool{\n~1: true,\n~2: false,\n~3: true,\n}"},
|
||||
{S{}, "pretty.S{\n~X: 0,\n~Y: false,\n~z: nil,\n}"},
|
||||
} {
|
||||
got := fmt.Sprintf("%+v", Value(test.value))
|
||||
if got != test.want {
|
||||
t.Errorf("%v: got:\n%q\nwant:\n%q", test.value, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBadVerb(t *testing.T) {
|
||||
got := fmt.Sprintf("%d", Value(8))
|
||||
want := "%!d("
|
||||
if !strings.HasPrefix(got, want) {
|
||||
t.Errorf("got %q, want prefix %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func ptr(s string) *string { return &s }
|
||||
48
vendor/cloud.google.com/go/internal/readme/Makefile
generated
vendored
Normal file
48
vendor/cloud.google.com/go/internal/readme/Makefile
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
# Rebuild the README.md file at repo root by inserting code samples
|
||||
# from compilable go files.
|
||||
|
||||
SHELL=/bin/bash
|
||||
|
||||
GOCLOUD_HOME=$(GOPATH)/src/cloud.google.com/go
|
||||
README=$(GOCLOUD_HOME)/README.md
|
||||
|
||||
.PHONY: readme test test-good test-bad-go test-bad-md
|
||||
|
||||
readme:
|
||||
@tmp=$$(mktemp); \
|
||||
awk -f snipmd.awk snippets.go $(README) > $$tmp; \
|
||||
mv $$tmp $(README)
|
||||
|
||||
diff:
|
||||
diff $(README) <(awk -f snipmd.awk snippets.go $(README))
|
||||
|
||||
test: test-good test-bad-go test-bad-md
|
||||
@echo PASS
|
||||
|
||||
test-good:
|
||||
@echo testdata/good.md
|
||||
@cd testdata >& /dev/null; \
|
||||
diff -u want.md <(awk -f ../snipmd.awk snips.go good.md)
|
||||
@echo "testdata/want.md (round trip)"
|
||||
@cd testdata >& /dev/null; \
|
||||
diff -u want.md <(awk -f ../snipmd.awk snips.go want.md)
|
||||
|
||||
test-bad-go:
|
||||
@for f in testdata/bad-*.go; do \
|
||||
echo $$f; \
|
||||
if awk -f snipmd.awk $$f >& /dev/null; then \
|
||||
echo "$f succeeded, want failure"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
done
|
||||
|
||||
test-bad-md:
|
||||
@for f in testdata/bad-*.md; do \
|
||||
echo $$f; \
|
||||
if awk -f snipmd.awk testdata/snips.go $$f >& /dev/null; then \
|
||||
echo "$f succeeded, want failure"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
done
|
||||
|
||||
|
||||
123
vendor/cloud.google.com/go/internal/readme/snipmd.awk
generated
vendored
Normal file
123
vendor/cloud.google.com/go/internal/readme/snipmd.awk
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
# Copyright 2017 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
# snipmd inserts code snippets from Go source files into a markdown file.
|
||||
#
|
||||
# Call with one or more .go files and a .md file:
|
||||
#
|
||||
# awk -f snipmd.awk foo.go bar.go template.md
|
||||
#
|
||||
# In the Go files, start a snippet with
|
||||
# //[ NAME
|
||||
# and end it with
|
||||
# //]
|
||||
#
|
||||
# In the markdown, write
|
||||
# [snip]:# NAME
|
||||
# to insert the snippet NAME just below that line.
|
||||
# If there is already a code block after the [snip]:# line, it will be
|
||||
# replaced, so a previous output can be used as input.
|
||||
#
|
||||
# The following transformations are made to the Go code:
|
||||
# - The first tab of each line is removed.
|
||||
# - Trailing blank lines are removed.
|
||||
# - `ELLIPSIS` and `_ = ELLIPSIS` are replaced by `...`
|
||||
|
||||
|
||||
/^[ \t]*\/\/\[/ { # start snippet in Go file
|
||||
if (inGo()) {
|
||||
if ($2 == "") {
|
||||
die("missing snippet name")
|
||||
}
|
||||
curSnip = $2
|
||||
next
|
||||
}
|
||||
}
|
||||
|
||||
/^[ \t]*\/\/]/ { # end snippet in Go file
|
||||
if (inGo()) {
|
||||
if (curSnip != "") {
|
||||
# Remove all but one trailing newline.
|
||||
gsub(/\n+$/, "\n", snips[curSnip])
|
||||
curSnip = ""
|
||||
next
|
||||
} else {
|
||||
die("//] without corresponding //[")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ENDFILE {
|
||||
if (curSnip != "") {
|
||||
die("unclosed snippet: " curSnip)
|
||||
}
|
||||
}
|
||||
|
||||
# Skip code blocks in the input that immediately follow [snip]:# lines,
|
||||
# because we just inserted the snippet. Supports round-tripping.
|
||||
/^```go$/,/^```$/ {
|
||||
if (inMarkdown() && afterSnip) {
|
||||
next
|
||||
}
|
||||
}
|
||||
|
||||
# Matches every line.
|
||||
{
|
||||
if (curSnip != "") {
|
||||
line = $0
|
||||
# Remove initial tab, if any.
|
||||
if (line ~ /^\t/) {
|
||||
line = substr(line, 2)
|
||||
}
|
||||
# Replace ELLIPSIS.
|
||||
gsub(/_ = ELLIPSIS/, "...", line)
|
||||
gsub(/ELLIPSIS/, "...", line)
|
||||
|
||||
snips[curSnip] = snips[curSnip] line "\n"
|
||||
} else if (inMarkdown()) {
|
||||
afterSnip = 0
|
||||
# Copy .md to output.
|
||||
print
|
||||
}
|
||||
}
|
||||
|
||||
$1 ~ /\[snip\]:#/ { # Snippet marker in .md file.
|
||||
if (inMarkdown()) {
|
||||
# We expect '[snip]:#' to be followed by '(NAME)'
|
||||
if ($2 !~ /\(.*\)/) {
|
||||
die("bad snip spec: " $0)
|
||||
}
|
||||
name = substr($2, 2, length($2)-2)
|
||||
if (snips[name] == "") {
|
||||
die("no snippet named " name)
|
||||
}
|
||||
printf("```go\n%s```\n", snips[name])
|
||||
afterSnip = 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function inMarkdown() {
|
||||
return match(FILENAME, /\.md$/)
|
||||
}
|
||||
|
||||
function inGo() {
|
||||
return match(FILENAME, /\.go$/)
|
||||
}
|
||||
|
||||
|
||||
function die(msg) {
|
||||
printf("%s:%d: %s\n", FILENAME, FNR, msg) > "/dev/stderr"
|
||||
exit 1
|
||||
}
|
||||
241
vendor/cloud.google.com/go/internal/readme/snippets.go
generated
vendored
Normal file
241
vendor/cloud.google.com/go/internal/readme/snippets.go
generated
vendored
Normal file
@@ -0,0 +1,241 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// This file holds samples that are embedded into README.md.
|
||||
|
||||
// This file has to compile, but need not execute.
|
||||
// If it fails to compile, fix it, then run `make` to regenerate README.md.
|
||||
|
||||
package readme
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/bigquery"
|
||||
"cloud.google.com/go/datastore"
|
||||
"cloud.google.com/go/logging"
|
||||
"cloud.google.com/go/pubsub"
|
||||
"cloud.google.com/go/spanner"
|
||||
"cloud.google.com/go/storage"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2"
|
||||
"google.golang.org/api/iterator"
|
||||
"google.golang.org/api/option"
|
||||
)
|
||||
|
||||
var ctx context.Context
|
||||
|
||||
const END = 0
|
||||
|
||||
func auth() {
|
||||
//[ auth
|
||||
client, err := storage.NewClient(ctx)
|
||||
//]
|
||||
_ = client
|
||||
_ = err
|
||||
}
|
||||
|
||||
func auth2() {
|
||||
//[ auth-JSON
|
||||
client, err := storage.NewClient(ctx, option.WithServiceAccountFile("path/to/keyfile.json"))
|
||||
//]
|
||||
_ = client
|
||||
_ = err
|
||||
}
|
||||
|
||||
func auth3() {
|
||||
var ELLIPSIS oauth2.TokenSource
|
||||
//[ auth-ts
|
||||
tokenSource := ELLIPSIS
|
||||
client, err := storage.NewClient(ctx, option.WithTokenSource(tokenSource))
|
||||
//]
|
||||
_ = client
|
||||
_ = err
|
||||
}
|
||||
|
||||
func datastoreSnippets() {
|
||||
//[ datastore-1
|
||||
client, err := datastore.NewClient(ctx, "my-project-id")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
//]
|
||||
|
||||
//[ datastore-2
|
||||
type Post struct {
|
||||
Title string
|
||||
Body string `datastore:",noindex"`
|
||||
PublishedAt time.Time
|
||||
}
|
||||
keys := []*datastore.Key{
|
||||
datastore.NameKey("Post", "post1", nil),
|
||||
datastore.NameKey("Post", "post2", nil),
|
||||
}
|
||||
posts := []*Post{
|
||||
{Title: "Post 1", Body: "...", PublishedAt: time.Now()},
|
||||
{Title: "Post 2", Body: "...", PublishedAt: time.Now()},
|
||||
}
|
||||
if _, err := client.PutMulti(ctx, keys, posts); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
//]
|
||||
}
|
||||
|
||||
func storageSnippets() {
|
||||
//[ storage-1
|
||||
client, err := storage.NewClient(ctx)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
//]
|
||||
|
||||
//[ storage-2
|
||||
// Read the object1 from bucket.
|
||||
rc, err := client.Bucket("bucket").Object("object1").NewReader(ctx)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer rc.Close()
|
||||
body, err := ioutil.ReadAll(rc)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
//]
|
||||
_ = body
|
||||
}
|
||||
|
||||
func pubsubSnippets() {
|
||||
//[ pubsub-1
|
||||
client, err := pubsub.NewClient(ctx, "project-id")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
//]
|
||||
|
||||
const ELLIPSIS = 0
|
||||
|
||||
//[ pubsub-2
|
||||
// Publish "hello world" on topic1.
|
||||
topic := client.Topic("topic1")
|
||||
res := topic.Publish(ctx, &pubsub.Message{
|
||||
Data: []byte("hello world"),
|
||||
})
|
||||
// The publish happens asynchronously.
|
||||
// Later, you can get the result from res:
|
||||
_ = ELLIPSIS
|
||||
msgID, err := res.Get(ctx)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Use a callback to receive messages via subscription1.
|
||||
sub := client.Subscription("subscription1")
|
||||
err = sub.Receive(ctx, func(ctx context.Context, m *pubsub.Message) {
|
||||
fmt.Println(m.Data)
|
||||
m.Ack() // Acknowledge that we've consumed the message.
|
||||
})
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
//]
|
||||
_ = msgID
|
||||
}
|
||||
|
||||
func bqSnippets() {
|
||||
//[ bq-1
|
||||
c, err := bigquery.NewClient(ctx, "my-project-ID")
|
||||
if err != nil {
|
||||
// TODO: Handle error.
|
||||
}
|
||||
//]
|
||||
|
||||
//[ bq-2
|
||||
// Construct a query.
|
||||
q := c.Query(`
|
||||
SELECT year, SUM(number)
|
||||
FROM [bigquery-public-data:usa_names.usa_1910_2013]
|
||||
WHERE name = "William"
|
||||
GROUP BY year
|
||||
ORDER BY year
|
||||
`)
|
||||
// Execute the query.
|
||||
it, err := q.Read(ctx)
|
||||
if err != nil {
|
||||
// TODO: Handle error.
|
||||
}
|
||||
// Iterate through the results.
|
||||
for {
|
||||
var values []bigquery.Value
|
||||
err := it.Next(&values)
|
||||
if err == iterator.Done {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
// TODO: Handle error.
|
||||
}
|
||||
fmt.Println(values)
|
||||
}
|
||||
//]
|
||||
}
|
||||
|
||||
func loggingSnippets() {
|
||||
//[ logging-1
|
||||
ctx := context.Background()
|
||||
client, err := logging.NewClient(ctx, "my-project")
|
||||
if err != nil {
|
||||
// TODO: Handle error.
|
||||
}
|
||||
//]
|
||||
//[ logging-2
|
||||
logger := client.Logger("my-log")
|
||||
logger.Log(logging.Entry{Payload: "something happened!"})
|
||||
//]
|
||||
|
||||
//[ logging-3
|
||||
err = client.Close()
|
||||
if err != nil {
|
||||
// TODO: Handle error.
|
||||
}
|
||||
//]
|
||||
}
|
||||
|
||||
func spannerSnippets() {
|
||||
//[ spanner-1
|
||||
client, err := spanner.NewClient(ctx, "projects/P/instances/I/databases/D")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
//]
|
||||
|
||||
//[ spanner-2
|
||||
// Simple Reads And Writes
|
||||
_, err = client.Apply(ctx, []*spanner.Mutation{
|
||||
spanner.Insert("Users",
|
||||
[]string{"name", "email"},
|
||||
[]interface{}{"alice", "a@example.com"})})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
row, err := client.Single().ReadRow(ctx, "Users",
|
||||
spanner.Key{"alice"}, []string{"email"})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
//]
|
||||
_ = row
|
||||
}
|
||||
23
vendor/cloud.google.com/go/internal/readme/testdata/bad-no-name.go
generated
vendored
Normal file
23
vendor/cloud.google.com/go/internal/readme/testdata/bad-no-name.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 readme
|
||||
|
||||
import "fmt"
|
||||
|
||||
func f() {
|
||||
//[
|
||||
fmt.Println()
|
||||
//]
|
||||
}
|
||||
19
vendor/cloud.google.com/go/internal/readme/testdata/bad-no-open.go
generated
vendored
Normal file
19
vendor/cloud.google.com/go/internal/readme/testdata/bad-no-open.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 readme
|
||||
|
||||
func f() {
|
||||
//]
|
||||
}
|
||||
2
vendor/cloud.google.com/go/internal/readme/testdata/bad-nosnip.md
generated
vendored
Normal file
2
vendor/cloud.google.com/go/internal/readme/testdata/bad-nosnip.md
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
[snip]:# (unknown)
|
||||
|
||||
1
vendor/cloud.google.com/go/internal/readme/testdata/bad-spec.md
generated
vendored
Normal file
1
vendor/cloud.google.com/go/internal/readme/testdata/bad-spec.md
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
[snip]:# missing-parens
|
||||
21
vendor/cloud.google.com/go/internal/readme/testdata/bad-unclosed.go
generated
vendored
Normal file
21
vendor/cloud.google.com/go/internal/readme/testdata/bad-unclosed.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 readme
|
||||
|
||||
// unclosed snippet
|
||||
|
||||
func f() {
|
||||
//[ X
|
||||
}
|
||||
18
vendor/cloud.google.com/go/internal/readme/testdata/good.md
generated
vendored
Normal file
18
vendor/cloud.google.com/go/internal/readme/testdata/good.md
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
This template is for testing snipmd.awk.
|
||||
|
||||
Put the first snippet here.
|
||||
|
||||
[snip]:# (first)
|
||||
|
||||
And now the second.
|
||||
[snip]:# (second)
|
||||
|
||||
A top-level snippet.
|
||||
|
||||
[snip]:# (top-level)
|
||||
|
||||
```go
|
||||
// A code block that is not included.
|
||||
```
|
||||
|
||||
And we're done.
|
||||
39
vendor/cloud.google.com/go/internal/readme/testdata/snips.go
generated
vendored
Normal file
39
vendor/cloud.google.com/go/internal/readme/testdata/snips.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 readme
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func f() {
|
||||
ELLIPSIS := 3
|
||||
//[ first
|
||||
fmt.Println("hello")
|
||||
x := ELLIPSIS
|
||||
//]
|
||||
|
||||
//[ second
|
||||
if x > 2 {
|
||||
_ = ELLIPSIS
|
||||
}
|
||||
//]
|
||||
}
|
||||
|
||||
//[ top-level
|
||||
var ErrBad = errors.New("bad")
|
||||
|
||||
//]
|
||||
30
vendor/cloud.google.com/go/internal/readme/testdata/want.md
generated
vendored
Normal file
30
vendor/cloud.google.com/go/internal/readme/testdata/want.md
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
This template is for testing snipmd.awk.
|
||||
|
||||
Put the first snippet here.
|
||||
|
||||
[snip]:# (first)
|
||||
```go
|
||||
fmt.Println("hello")
|
||||
x := ...
|
||||
```
|
||||
|
||||
And now the second.
|
||||
[snip]:# (second)
|
||||
```go
|
||||
if x > 2 {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
A top-level snippet.
|
||||
|
||||
[snip]:# (top-level)
|
||||
```go
|
||||
var ErrBad = errors.New("bad")
|
||||
```
|
||||
|
||||
```go
|
||||
// A code block that is not included.
|
||||
```
|
||||
|
||||
And we're done.
|
||||
56
vendor/cloud.google.com/go/internal/retry.go
generated
vendored
Normal file
56
vendor/cloud.google.com/go/internal/retry.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
gax "github.com/googleapis/gax-go"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// Retry calls the supplied function f repeatedly according to the provided
|
||||
// backoff parameters. It returns when one of the following occurs:
|
||||
// When f's first return value is true, Retry immediately returns with f's second
|
||||
// return value.
|
||||
// When the provided context is done, Retry returns with an error that
|
||||
// includes both ctx.Error() and the last error returned by f.
|
||||
func Retry(ctx context.Context, bo gax.Backoff, f func() (stop bool, err error)) error {
|
||||
return retry(ctx, bo, f, gax.Sleep)
|
||||
}
|
||||
|
||||
func retry(ctx context.Context, bo gax.Backoff, f func() (stop bool, err error),
|
||||
sleep func(context.Context, time.Duration) error) error {
|
||||
var lastErr error
|
||||
for {
|
||||
stop, err := f()
|
||||
if stop {
|
||||
return err
|
||||
}
|
||||
// Remember the last "real" error from f.
|
||||
if err != nil && err != context.Canceled && err != context.DeadlineExceeded {
|
||||
lastErr = err
|
||||
}
|
||||
p := bo.Pause()
|
||||
if cerr := sleep(ctx, p); cerr != nil {
|
||||
if lastErr != nil {
|
||||
return fmt.Errorf("%v; last function err: %v", cerr, lastErr)
|
||||
}
|
||||
return cerr
|
||||
}
|
||||
}
|
||||
}
|
||||
64
vendor/cloud.google.com/go/internal/retry_test.go
generated
vendored
Normal file
64
vendor/cloud.google.com/go/internal/retry_test.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 internal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
gax "github.com/googleapis/gax-go"
|
||||
)
|
||||
|
||||
func TestRetry(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
// Without a context deadline, retry will run until the function
|
||||
// says not to retry any more.
|
||||
n := 0
|
||||
endRetry := errors.New("end retry")
|
||||
err := retry(ctx, gax.Backoff{},
|
||||
func() (bool, error) {
|
||||
n++
|
||||
if n < 10 {
|
||||
return false, nil
|
||||
}
|
||||
return true, endRetry
|
||||
},
|
||||
func(context.Context, time.Duration) error { return nil })
|
||||
if got, want := err, endRetry; got != want {
|
||||
t.Errorf("got %v, want %v", err, endRetry)
|
||||
}
|
||||
if n != 10 {
|
||||
t.Errorf("n: got %d, want %d", n, 10)
|
||||
}
|
||||
|
||||
// If the context has a deadline, sleep will return an error
|
||||
// and end the function.
|
||||
n = 0
|
||||
err = retry(ctx, gax.Backoff{},
|
||||
func() (bool, error) { return false, nil },
|
||||
func(context.Context, time.Duration) error {
|
||||
n++
|
||||
if n < 10 {
|
||||
return nil
|
||||
}
|
||||
return context.DeadlineExceeded
|
||||
})
|
||||
if err == nil {
|
||||
t.Error("got nil, want error")
|
||||
}
|
||||
}
|
||||
32
vendor/cloud.google.com/go/internal/rpcreplay/Makefile
generated
vendored
Normal file
32
vendor/cloud.google.com/go/internal/rpcreplay/Makefile
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# Copyright 2017 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
# Makefile for building Go files from protos.
|
||||
|
||||
# Change these to match your environment.
|
||||
PROTOC=$(HOME)/bin/protoc
|
||||
PROTOC_GO_PLUGIN_DIR=$(GOPATH)/bin
|
||||
PROTOBUF_REPO=$(HOME)/git-repos/protobuf
|
||||
|
||||
gen-protos: sync-protobuf
|
||||
for d in proto/*; do \
|
||||
PATH=$(PATH):$(PROTOC_GO_PLUGIN_DIR) \
|
||||
$(PROTOC) --go_out=plugins=grpc:$$d \
|
||||
-I $$d -I $(PROTOBUF_REPO)/src $$d/*.proto; \
|
||||
done
|
||||
|
||||
|
||||
sync-protobuf:
|
||||
cd $(PROTOBUF_REPO); git pull
|
||||
|
||||
106
vendor/cloud.google.com/go/internal/rpcreplay/doc.go
generated
vendored
Normal file
106
vendor/cloud.google.com/go/internal/rpcreplay/doc.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 rpcreplay supports the capture and replay of gRPC calls. Its main goal is
|
||||
to improve testing. Once one captures the calls of a test that runs against a real
|
||||
service, one has an "automatic mock" that can be replayed against the same test,
|
||||
yielding a unit test that is fast and flake-free.
|
||||
|
||||
|
||||
Recording
|
||||
|
||||
To record a sequence of gRPC calls to a file, create a Recorder and pass its
|
||||
DialOptions to grpc.Dial:
|
||||
|
||||
rec, err := rpcreplay.NewRecorder("service.replay", nil)
|
||||
if err != nil { ... }
|
||||
defer func() {
|
||||
if err := rec.Close(); err != nil { ... }
|
||||
}()
|
||||
conn, err := grpc.Dial(serverAddress, rec.DialOptions()...)
|
||||
|
||||
It's essential to close the Recorder when the interaction is finished.
|
||||
|
||||
There is also a NewRecorderWriter function for capturing to an arbitrary
|
||||
io.Writer.
|
||||
|
||||
|
||||
Replaying
|
||||
|
||||
Replaying a captured file looks almost identical: create a Replayer and use
|
||||
its DialOptions. (Since we're reading the file and not writing it, we don't
|
||||
have to be as careful about the error returned from Close).
|
||||
|
||||
rep, err := rpcreplay.NewReplayer("service.replay")
|
||||
if err != nil { ... }
|
||||
defer rep.Close()
|
||||
conn, err := grpc.Dial(serverAddress, rep.DialOptions()...)
|
||||
|
||||
|
||||
Initial State
|
||||
|
||||
A test might use random or time-sensitive values, for instance to create unique
|
||||
resources for isolation from other tests. The test therefore has initial values --
|
||||
the current time, a random seed -- that differ from run to run. You must record this
|
||||
initial state and re-establish it on replay.
|
||||
|
||||
To record the initial state, serialize it into a []byte and pass it as the second
|
||||
argument to NewRecorder:
|
||||
|
||||
timeNow := time.Now()
|
||||
b, err := timeNow.MarshalBinary()
|
||||
if err != nil { ... }
|
||||
rec, err := rpcreplay.NewRecorder("service.replay", b)
|
||||
|
||||
On replay, get the bytes from Replayer.Initial:
|
||||
|
||||
rep, err := rpcreplay.NewReplayer("service.replay")
|
||||
if err != nil { ... }
|
||||
defer rep.Close()
|
||||
err = timeNow.UnmarshalBinary(rep.Initial())
|
||||
if err != nil { ... }
|
||||
|
||||
|
||||
Nondeterminism
|
||||
|
||||
A nondeterministic program may invoke RPCs in a different order each time
|
||||
it is run. The order in which RPCs are called during recording may differ
|
||||
from the order during replay.
|
||||
|
||||
The replayer matches incoming to recorded requests by method name and request
|
||||
contents, so nondeterminism is only a concern for identical requests that result
|
||||
in different responses. A nondeterministic program whose behavior differs
|
||||
depending on the order of such RPCs probably has a race condition: since both the
|
||||
recorded sequence of RPCs and the sequence during replay are valid orderings, the
|
||||
program should behave the same under both.
|
||||
|
||||
|
||||
Other Replayer Differences
|
||||
|
||||
Besides the differences in replay mentioned above, other differences may cause issues
|
||||
for some programs. We list them here.
|
||||
|
||||
The Replayer delivers a response to an RPC immediately, without waiting for other
|
||||
incoming RPCs. This can violate causality. For example, in a Pub/Sub program where
|
||||
one goroutine publishes and another subscribes, during replay the Subscribe call may
|
||||
finish before the Publish call begins.
|
||||
|
||||
For streaming RPCs, the Replayer delivers the result of Send and Recv calls in
|
||||
the order they were recorded. No attempt is made to match message contents.
|
||||
|
||||
At present, this package does not record or replay stream headers and trailers, or
|
||||
the result of the CloseSend method.
|
||||
*/
|
||||
package rpcreplay // import "cloud.google.com/go/internal/rpcreplay"
|
||||
47
vendor/cloud.google.com/go/internal/rpcreplay/example_test.go
generated
vendored
Normal file
47
vendor/cloud.google.com/go/internal/rpcreplay/example_test.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 rpcreplay_test
|
||||
|
||||
var serverAddress string
|
||||
|
||||
// func Example_NewRecorder() {
|
||||
// rec, err := rpcreplay.NewRecorder("service.replay", nil)
|
||||
// if err != nil {
|
||||
// // TODO: Handle error.
|
||||
// }
|
||||
// defer func() {
|
||||
// if err := rec.Close(); err != nil {
|
||||
// // TODO: Handle error.
|
||||
// }
|
||||
// }()
|
||||
// conn, err := grpc.Dial(serverAddress, rec.DialOptions()...)
|
||||
// if err != nil {
|
||||
// // TODO: Handle error.
|
||||
// }
|
||||
// _ = conn // TODO: use connection
|
||||
// }
|
||||
|
||||
// func Example_NewReplayer() {
|
||||
// rep, err := rpcreplay.NewReplayer("service.replay")
|
||||
// if err != nil {
|
||||
// // TODO: Handle error.
|
||||
// }
|
||||
// defer rep.Close()
|
||||
// conn, err := grpc.Dial(serverAddress, rep.DialOptions()...)
|
||||
// if err != nil {
|
||||
// // TODO: Handle error.
|
||||
// }
|
||||
// _ = conn // TODO: use connection
|
||||
// }
|
||||
121
vendor/cloud.google.com/go/internal/rpcreplay/fake_test.go
generated
vendored
Normal file
121
vendor/cloud.google.com/go/internal/rpcreplay/fake_test.go
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 rpcreplay
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
|
||||
pb "cloud.google.com/go/internal/rpcreplay/proto/intstore"
|
||||
)
|
||||
|
||||
// intStoreServer is an in-memory implementation of IntStore.
|
||||
type intStoreServer struct {
|
||||
pb.IntStoreServer
|
||||
|
||||
Addr string
|
||||
l net.Listener
|
||||
gsrv *grpc.Server
|
||||
|
||||
items map[string]int32
|
||||
}
|
||||
|
||||
func newIntStoreServer() *intStoreServer {
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
s := &intStoreServer{
|
||||
Addr: l.Addr().String(),
|
||||
l: l,
|
||||
gsrv: grpc.NewServer(),
|
||||
}
|
||||
pb.RegisterIntStoreServer(s.gsrv, s)
|
||||
go s.gsrv.Serve(s.l)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *intStoreServer) stop() {
|
||||
s.gsrv.Stop()
|
||||
s.l.Close()
|
||||
}
|
||||
|
||||
func (s *intStoreServer) Set(_ context.Context, item *pb.Item) (*pb.SetResponse, error) {
|
||||
old := s.setItem(item)
|
||||
return &pb.SetResponse{PrevValue: old}, nil
|
||||
}
|
||||
|
||||
func (s *intStoreServer) setItem(item *pb.Item) int32 {
|
||||
if s.items == nil {
|
||||
s.items = map[string]int32{}
|
||||
}
|
||||
old := s.items[item.Name]
|
||||
s.items[item.Name] = item.Value
|
||||
return old
|
||||
}
|
||||
|
||||
func (s *intStoreServer) Get(_ context.Context, req *pb.GetRequest) (*pb.Item, error) {
|
||||
val, ok := s.items[req.Name]
|
||||
if !ok {
|
||||
return nil, grpc.Errorf(codes.NotFound, "%q", req.Name)
|
||||
}
|
||||
return &pb.Item{Name: req.Name, Value: val}, nil
|
||||
}
|
||||
|
||||
func (s *intStoreServer) ListItems(_ *pb.ListItemsRequest, ss pb.IntStore_ListItemsServer) error {
|
||||
for name, val := range s.items {
|
||||
if err := ss.Send(&pb.Item{Name: name, Value: val}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *intStoreServer) SetStream(ss pb.IntStore_SetStreamServer) error {
|
||||
n := 0
|
||||
for {
|
||||
item, err := ss.Recv()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.setItem(item)
|
||||
n++
|
||||
}
|
||||
return ss.SendAndClose(&pb.Summary{Count: int32(n)})
|
||||
}
|
||||
|
||||
func (s *intStoreServer) StreamChat(ss pb.IntStore_StreamChatServer) error {
|
||||
for {
|
||||
item, err := ss.Recv()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ss.Send(item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
454
vendor/cloud.google.com/go/internal/rpcreplay/proto/intstore/intstore.pb.go
generated
vendored
Normal file
454
vendor/cloud.google.com/go/internal/rpcreplay/proto/intstore/intstore.pb.go
generated
vendored
Normal file
@@ -0,0 +1,454 @@
|
||||
// Code generated by protoc-gen-go.
|
||||
// source: intstore.proto
|
||||
// DO NOT EDIT!
|
||||
|
||||
/*
|
||||
Package intstore is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
intstore.proto
|
||||
|
||||
It has these top-level messages:
|
||||
Item
|
||||
SetResponse
|
||||
GetRequest
|
||||
Summary
|
||||
ListItemsRequest
|
||||
*/
|
||||
package intstore
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import (
|
||||
context "golang.org/x/net/context"
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
type Item struct {
|
||||
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
||||
Value int32 `protobuf:"varint,2,opt,name=value" json:"value,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Item) Reset() { *m = Item{} }
|
||||
func (m *Item) String() string { return proto.CompactTextString(m) }
|
||||
func (*Item) ProtoMessage() {}
|
||||
func (*Item) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
|
||||
func (m *Item) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Item) GetValue() int32 {
|
||||
if m != nil {
|
||||
return m.Value
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type SetResponse struct {
|
||||
PrevValue int32 `protobuf:"varint,1,opt,name=prev_value,json=prevValue" json:"prev_value,omitempty"`
|
||||
}
|
||||
|
||||
func (m *SetResponse) Reset() { *m = SetResponse{} }
|
||||
func (m *SetResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*SetResponse) ProtoMessage() {}
|
||||
func (*SetResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||
|
||||
func (m *SetResponse) GetPrevValue() int32 {
|
||||
if m != nil {
|
||||
return m.PrevValue
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type GetRequest struct {
|
||||
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
||||
}
|
||||
|
||||
func (m *GetRequest) Reset() { *m = GetRequest{} }
|
||||
func (m *GetRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*GetRequest) ProtoMessage() {}
|
||||
func (*GetRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
||||
|
||||
func (m *GetRequest) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Summary struct {
|
||||
Count int32 `protobuf:"varint,1,opt,name=count" json:"count,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Summary) Reset() { *m = Summary{} }
|
||||
func (m *Summary) String() string { return proto.CompactTextString(m) }
|
||||
func (*Summary) ProtoMessage() {}
|
||||
func (*Summary) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
|
||||
|
||||
func (m *Summary) GetCount() int32 {
|
||||
if m != nil {
|
||||
return m.Count
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type ListItemsRequest struct {
|
||||
}
|
||||
|
||||
func (m *ListItemsRequest) Reset() { *m = ListItemsRequest{} }
|
||||
func (m *ListItemsRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ListItemsRequest) ProtoMessage() {}
|
||||
func (*ListItemsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Item)(nil), "intstore.Item")
|
||||
proto.RegisterType((*SetResponse)(nil), "intstore.SetResponse")
|
||||
proto.RegisterType((*GetRequest)(nil), "intstore.GetRequest")
|
||||
proto.RegisterType((*Summary)(nil), "intstore.Summary")
|
||||
proto.RegisterType((*ListItemsRequest)(nil), "intstore.ListItemsRequest")
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// Client API for IntStore service
|
||||
|
||||
type IntStoreClient interface {
|
||||
Set(ctx context.Context, in *Item, opts ...grpc.CallOption) (*SetResponse, error)
|
||||
Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*Item, error)
|
||||
// A server-to-client streaming RPC.
|
||||
ListItems(ctx context.Context, in *ListItemsRequest, opts ...grpc.CallOption) (IntStore_ListItemsClient, error)
|
||||
// A client-to-server streaming RPC.
|
||||
SetStream(ctx context.Context, opts ...grpc.CallOption) (IntStore_SetStreamClient, error)
|
||||
// A Bidirectional streaming RPC.
|
||||
StreamChat(ctx context.Context, opts ...grpc.CallOption) (IntStore_StreamChatClient, error)
|
||||
}
|
||||
|
||||
type intStoreClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewIntStoreClient(cc *grpc.ClientConn) IntStoreClient {
|
||||
return &intStoreClient{cc}
|
||||
}
|
||||
|
||||
func (c *intStoreClient) Set(ctx context.Context, in *Item, opts ...grpc.CallOption) (*SetResponse, error) {
|
||||
out := new(SetResponse)
|
||||
err := grpc.Invoke(ctx, "/intstore.IntStore/Set", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *intStoreClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*Item, error) {
|
||||
out := new(Item)
|
||||
err := grpc.Invoke(ctx, "/intstore.IntStore/Get", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *intStoreClient) ListItems(ctx context.Context, in *ListItemsRequest, opts ...grpc.CallOption) (IntStore_ListItemsClient, error) {
|
||||
stream, err := grpc.NewClientStream(ctx, &_IntStore_serviceDesc.Streams[0], c.cc, "/intstore.IntStore/ListItems", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &intStoreListItemsClient{stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type IntStore_ListItemsClient interface {
|
||||
Recv() (*Item, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type intStoreListItemsClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *intStoreListItemsClient) Recv() (*Item, error) {
|
||||
m := new(Item)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *intStoreClient) SetStream(ctx context.Context, opts ...grpc.CallOption) (IntStore_SetStreamClient, error) {
|
||||
stream, err := grpc.NewClientStream(ctx, &_IntStore_serviceDesc.Streams[1], c.cc, "/intstore.IntStore/SetStream", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &intStoreSetStreamClient{stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type IntStore_SetStreamClient interface {
|
||||
Send(*Item) error
|
||||
CloseAndRecv() (*Summary, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type intStoreSetStreamClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *intStoreSetStreamClient) Send(m *Item) error {
|
||||
return x.ClientStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *intStoreSetStreamClient) CloseAndRecv() (*Summary, error) {
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := new(Summary)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *intStoreClient) StreamChat(ctx context.Context, opts ...grpc.CallOption) (IntStore_StreamChatClient, error) {
|
||||
stream, err := grpc.NewClientStream(ctx, &_IntStore_serviceDesc.Streams[2], c.cc, "/intstore.IntStore/StreamChat", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &intStoreStreamChatClient{stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type IntStore_StreamChatClient interface {
|
||||
Send(*Item) error
|
||||
Recv() (*Item, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type intStoreStreamChatClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *intStoreStreamChatClient) Send(m *Item) error {
|
||||
return x.ClientStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *intStoreStreamChatClient) Recv() (*Item, error) {
|
||||
m := new(Item)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Server API for IntStore service
|
||||
|
||||
type IntStoreServer interface {
|
||||
Set(context.Context, *Item) (*SetResponse, error)
|
||||
Get(context.Context, *GetRequest) (*Item, error)
|
||||
// A server-to-client streaming RPC.
|
||||
ListItems(*ListItemsRequest, IntStore_ListItemsServer) error
|
||||
// A client-to-server streaming RPC.
|
||||
SetStream(IntStore_SetStreamServer) error
|
||||
// A Bidirectional streaming RPC.
|
||||
StreamChat(IntStore_StreamChatServer) error
|
||||
}
|
||||
|
||||
func RegisterIntStoreServer(s *grpc.Server, srv IntStoreServer) {
|
||||
s.RegisterService(&_IntStore_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _IntStore_Set_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Item)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(IntStoreServer).Set(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/intstore.IntStore/Set",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(IntStoreServer).Set(ctx, req.(*Item))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _IntStore_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(IntStoreServer).Get(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/intstore.IntStore/Get",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(IntStoreServer).Get(ctx, req.(*GetRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _IntStore_ListItems_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(ListItemsRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(IntStoreServer).ListItems(m, &intStoreListItemsServer{stream})
|
||||
}
|
||||
|
||||
type IntStore_ListItemsServer interface {
|
||||
Send(*Item) error
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type intStoreListItemsServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *intStoreListItemsServer) Send(m *Item) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func _IntStore_SetStream_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(IntStoreServer).SetStream(&intStoreSetStreamServer{stream})
|
||||
}
|
||||
|
||||
type IntStore_SetStreamServer interface {
|
||||
SendAndClose(*Summary) error
|
||||
Recv() (*Item, error)
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type intStoreSetStreamServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *intStoreSetStreamServer) SendAndClose(m *Summary) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *intStoreSetStreamServer) Recv() (*Item, error) {
|
||||
m := new(Item)
|
||||
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func _IntStore_StreamChat_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(IntStoreServer).StreamChat(&intStoreStreamChatServer{stream})
|
||||
}
|
||||
|
||||
type IntStore_StreamChatServer interface {
|
||||
Send(*Item) error
|
||||
Recv() (*Item, error)
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type intStoreStreamChatServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *intStoreStreamChatServer) Send(m *Item) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *intStoreStreamChatServer) Recv() (*Item, error) {
|
||||
m := new(Item)
|
||||
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
var _IntStore_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "intstore.IntStore",
|
||||
HandlerType: (*IntStoreServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Set",
|
||||
Handler: _IntStore_Set_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Get",
|
||||
Handler: _IntStore_Get_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "ListItems",
|
||||
Handler: _IntStore_ListItems_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "SetStream",
|
||||
Handler: _IntStore_SetStream_Handler,
|
||||
ClientStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "StreamChat",
|
||||
Handler: _IntStore_StreamChat_Handler,
|
||||
ServerStreams: true,
|
||||
ClientStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "intstore.proto",
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("intstore.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 273 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x91, 0x4f, 0x4b, 0xc3, 0x40,
|
||||
0x10, 0xc5, 0xb3, 0xfd, 0xa3, 0xcd, 0x08, 0x45, 0x87, 0x0a, 0x25, 0x20, 0x86, 0x3d, 0xe5, 0xa0,
|
||||
0x21, 0xd4, 0xa3, 0x47, 0x0f, 0xa5, 0xe0, 0x29, 0x0b, 0x5e, 0x25, 0xca, 0x80, 0x05, 0xb3, 0x1b,
|
||||
0x77, 0x27, 0x05, 0xbf, 0x84, 0x9f, 0x59, 0x36, 0x5b, 0x9b, 0xd2, 0x78, 0xdb, 0xb7, 0xf3, 0x66,
|
||||
0xde, 0x6f, 0x76, 0x61, 0xbe, 0xd5, 0xec, 0xd8, 0x58, 0xca, 0x1b, 0x6b, 0xd8, 0xe0, 0xec, 0x4f,
|
||||
0xcb, 0x02, 0x26, 0x1b, 0xa6, 0x1a, 0x11, 0x26, 0xba, 0xaa, 0x69, 0x29, 0x52, 0x91, 0xc5, 0x65,
|
||||
0x77, 0xc6, 0x05, 0x4c, 0x77, 0xd5, 0x67, 0x4b, 0xcb, 0x51, 0x2a, 0xb2, 0x69, 0x19, 0x84, 0xbc,
|
||||
0x83, 0x0b, 0x45, 0x5c, 0x92, 0x6b, 0x8c, 0x76, 0x84, 0x37, 0x00, 0x8d, 0xa5, 0xdd, 0x6b, 0x70,
|
||||
0x8a, 0xce, 0x19, 0xfb, 0x9b, 0x97, 0xce, 0x9d, 0x02, 0xac, 0xbd, 0xfb, 0xab, 0x25, 0xc7, 0xff,
|
||||
0xa5, 0xc8, 0x5b, 0x38, 0x57, 0x6d, 0x5d, 0x57, 0xf6, 0xdb, 0x07, 0xbe, 0x9b, 0x56, 0xf3, 0x7e,
|
||||
0x4c, 0x10, 0x12, 0xe1, 0xf2, 0x79, 0xeb, 0xd8, 0x63, 0xba, 0xfd, 0xa0, 0xd5, 0xcf, 0x08, 0x66,
|
||||
0x1b, 0xcd, 0xca, 0xef, 0x80, 0x39, 0x8c, 0x15, 0x31, 0xce, 0xf3, 0xc3, 0x96, 0xde, 0x9b, 0x5c,
|
||||
0xf7, 0xfa, 0x08, 0x58, 0x46, 0x78, 0x0f, 0xe3, 0x35, 0x31, 0x2e, 0xfa, 0x7a, 0x8f, 0x98, 0x9c,
|
||||
0x4c, 0x91, 0x11, 0x3e, 0x42, 0x7c, 0xc8, 0xc7, 0xa4, 0x2f, 0x9f, 0x42, 0x0d, 0x5b, 0x0b, 0x81,
|
||||
0x2b, 0x88, 0x15, 0xb1, 0x62, 0x4b, 0x55, 0x3d, 0x20, 0xbc, 0x3a, 0x22, 0x0c, 0x4f, 0x20, 0xa3,
|
||||
0xcc, 0xf7, 0x40, 0x68, 0x78, 0xfa, 0xa8, 0x86, 0x6b, 0x0d, 0x52, 0x32, 0x51, 0x88, 0xb7, 0xb3,
|
||||
0xee, 0x63, 0x1f, 0x7e, 0x03, 0x00, 0x00, 0xff, 0xff, 0x22, 0x28, 0xa0, 0x49, 0xea, 0x01, 0x00,
|
||||
0x00,
|
||||
}
|
||||
54
vendor/cloud.google.com/go/internal/rpcreplay/proto/intstore/intstore.proto
generated
vendored
Normal file
54
vendor/cloud.google.com/go/internal/rpcreplay/proto/intstore/intstore.proto
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// IntStore is a service for testing the rpcreplay package.
|
||||
// It is a simple key-value store for integers.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package intstore;
|
||||
|
||||
service IntStore {
|
||||
rpc Set(Item) returns (SetResponse) {}
|
||||
|
||||
rpc Get(GetRequest) returns (Item) {}
|
||||
|
||||
// A server-to-client streaming RPC.
|
||||
rpc ListItems(ListItemsRequest) returns (stream Item) {}
|
||||
|
||||
// A client-to-server streaming RPC.
|
||||
rpc SetStream(stream Item) returns (Summary) {}
|
||||
|
||||
// A Bidirectional streaming RPC.
|
||||
rpc StreamChat(stream Item) returns (stream Item) {}
|
||||
}
|
||||
|
||||
message Item {
|
||||
string name = 1;
|
||||
int32 value = 2;
|
||||
}
|
||||
|
||||
message SetResponse {
|
||||
int32 prev_value = 1;
|
||||
}
|
||||
|
||||
message GetRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message Summary {
|
||||
int32 count = 1;
|
||||
}
|
||||
|
||||
message ListItemsRequest {}
|
||||
170
vendor/cloud.google.com/go/internal/rpcreplay/proto/rpcreplay/rpcreplay.pb.go
generated
vendored
Normal file
170
vendor/cloud.google.com/go/internal/rpcreplay/proto/rpcreplay/rpcreplay.pb.go
generated
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
// Code generated by protoc-gen-go.
|
||||
// source: rpcreplay.proto
|
||||
// DO NOT EDIT!
|
||||
|
||||
/*
|
||||
Package rpcreplay is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
rpcreplay.proto
|
||||
|
||||
It has these top-level messages:
|
||||
Entry
|
||||
*/
|
||||
package rpcreplay
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
import google_protobuf "github.com/golang/protobuf/ptypes/any"
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
type Entry_Kind int32
|
||||
|
||||
const (
|
||||
Entry_TYPE_UNSPECIFIED Entry_Kind = 0
|
||||
// A unary request.
|
||||
// method: the full name of the method
|
||||
// message: the request proto
|
||||
// is_error: false
|
||||
// ref_index: 0
|
||||
Entry_REQUEST Entry_Kind = 1
|
||||
// A unary response.
|
||||
// method: the full name of the method
|
||||
// message:
|
||||
// if is_error: a google.rpc.Status proto
|
||||
// else: the response proto
|
||||
// ref_index: index in the sequence of Entries of matching request (1-based)
|
||||
Entry_RESPONSE Entry_Kind = 2
|
||||
// A method that creates a stream.
|
||||
// method: the full name of the method
|
||||
// message:
|
||||
// if is_error: a google.rpc.Status proto
|
||||
// else: nil
|
||||
// ref_index: 0
|
||||
Entry_CREATE_STREAM Entry_Kind = 3
|
||||
// A call to Send on the client returned by a stream-creating method.
|
||||
// method: unset
|
||||
// message: the proto being sent
|
||||
// is_error: false
|
||||
// ref_index: index of matching CREATE_STREAM entry (1-based)
|
||||
Entry_SEND Entry_Kind = 4
|
||||
// A call to Recv on the client returned by a stream-creating method.
|
||||
// method: unset
|
||||
// message:
|
||||
// if is_error: a google.rpc.Status proto, or nil on EOF
|
||||
// else: the received message
|
||||
// ref_index: index of matching CREATE_STREAM entry
|
||||
Entry_RECV Entry_Kind = 5
|
||||
)
|
||||
|
||||
var Entry_Kind_name = map[int32]string{
|
||||
0: "TYPE_UNSPECIFIED",
|
||||
1: "REQUEST",
|
||||
2: "RESPONSE",
|
||||
3: "CREATE_STREAM",
|
||||
4: "SEND",
|
||||
5: "RECV",
|
||||
}
|
||||
var Entry_Kind_value = map[string]int32{
|
||||
"TYPE_UNSPECIFIED": 0,
|
||||
"REQUEST": 1,
|
||||
"RESPONSE": 2,
|
||||
"CREATE_STREAM": 3,
|
||||
"SEND": 4,
|
||||
"RECV": 5,
|
||||
}
|
||||
|
||||
func (x Entry_Kind) String() string {
|
||||
return proto.EnumName(Entry_Kind_name, int32(x))
|
||||
}
|
||||
func (Entry_Kind) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 0} }
|
||||
|
||||
// An Entry represents a single RPC activity, typically a request or response.
|
||||
type Entry struct {
|
||||
Kind Entry_Kind `protobuf:"varint,1,opt,name=kind,enum=rpcreplay.Entry_Kind" json:"kind,omitempty"`
|
||||
Method string `protobuf:"bytes,2,opt,name=method" json:"method,omitempty"`
|
||||
Message *google_protobuf.Any `protobuf:"bytes,3,opt,name=message" json:"message,omitempty"`
|
||||
IsError bool `protobuf:"varint,4,opt,name=is_error,json=isError" json:"is_error,omitempty"`
|
||||
RefIndex int32 `protobuf:"varint,5,opt,name=ref_index,json=refIndex" json:"ref_index,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Entry) Reset() { *m = Entry{} }
|
||||
func (m *Entry) String() string { return proto.CompactTextString(m) }
|
||||
func (*Entry) ProtoMessage() {}
|
||||
func (*Entry) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
|
||||
func (m *Entry) GetKind() Entry_Kind {
|
||||
if m != nil {
|
||||
return m.Kind
|
||||
}
|
||||
return Entry_TYPE_UNSPECIFIED
|
||||
}
|
||||
|
||||
func (m *Entry) GetMethod() string {
|
||||
if m != nil {
|
||||
return m.Method
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Entry) GetMessage() *google_protobuf.Any {
|
||||
if m != nil {
|
||||
return m.Message
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Entry) GetIsError() bool {
|
||||
if m != nil {
|
||||
return m.IsError
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Entry) GetRefIndex() int32 {
|
||||
if m != nil {
|
||||
return m.RefIndex
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Entry)(nil), "rpcreplay.Entry")
|
||||
proto.RegisterEnum("rpcreplay.Entry_Kind", Entry_Kind_name, Entry_Kind_value)
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("rpcreplay.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 289 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x44, 0x8e, 0xdf, 0x4e, 0xc2, 0x30,
|
||||
0x14, 0xc6, 0x2d, 0x6c, 0x30, 0x0e, 0xfe, 0xa9, 0x0d, 0x9a, 0xa1, 0x37, 0x0b, 0x57, 0xf3, 0xa6,
|
||||
0x24, 0xf8, 0x04, 0x04, 0x8e, 0x09, 0x31, 0x22, 0xb6, 0xc3, 0xc4, 0x1b, 0x17, 0x70, 0x05, 0x17,
|
||||
0xa1, 0x25, 0xdd, 0x4c, 0xdc, 0x6b, 0xf8, 0xc4, 0x66, 0x13, 0xf4, 0xae, 0xbf, 0x7e, 0xbf, 0x9c,
|
||||
0xef, 0x83, 0x33, 0xbb, 0x7b, 0xb3, 0x6a, 0xb7, 0x59, 0x14, 0x7c, 0x67, 0x4d, 0x6e, 0x58, 0xeb,
|
||||
0xef, 0xe3, 0xaa, 0xbb, 0x36, 0x66, 0xbd, 0x51, 0xfd, 0x2a, 0x58, 0x7e, 0xae, 0xfa, 0x0b, 0xbd,
|
||||
0xb7, 0x7a, 0xdf, 0x35, 0x70, 0x51, 0xe7, 0xb6, 0x60, 0x37, 0xe0, 0x7c, 0xa4, 0x3a, 0xf1, 0x49,
|
||||
0x40, 0xc2, 0xd3, 0xc1, 0x05, 0xff, 0xbf, 0x57, 0xe5, 0xfc, 0x3e, 0xd5, 0x89, 0xa8, 0x14, 0x76,
|
||||
0x09, 0x8d, 0xad, 0xca, 0xdf, 0x4d, 0xe2, 0xd7, 0x02, 0x12, 0xb6, 0xc4, 0x9e, 0x18, 0x87, 0xe6,
|
||||
0x56, 0x65, 0xd9, 0x62, 0xad, 0xfc, 0x7a, 0x40, 0xc2, 0xf6, 0xa0, 0xc3, 0x7f, 0x9b, 0xf9, 0xa1,
|
||||
0x99, 0x0f, 0x75, 0x21, 0x0e, 0x12, 0xeb, 0x82, 0x97, 0x66, 0xb1, 0xb2, 0xd6, 0x58, 0xdf, 0x09,
|
||||
0x48, 0xe8, 0x89, 0x66, 0x9a, 0x61, 0x89, 0xec, 0x1a, 0x5a, 0x56, 0xad, 0xe2, 0x54, 0x27, 0xea,
|
||||
0xcb, 0x77, 0x03, 0x12, 0xba, 0xc2, 0xb3, 0x6a, 0x35, 0x29, 0xb9, 0xf7, 0x0a, 0x4e, 0xb9, 0x86,
|
||||
0x75, 0x80, 0x46, 0x2f, 0x33, 0x8c, 0xe7, 0x53, 0x39, 0xc3, 0xd1, 0xe4, 0x6e, 0x82, 0x63, 0x7a,
|
||||
0xc4, 0xda, 0xd0, 0x14, 0xf8, 0x34, 0x47, 0x19, 0x51, 0xc2, 0x8e, 0xc1, 0x13, 0x28, 0x67, 0x8f,
|
||||
0x53, 0x89, 0xb4, 0xc6, 0xce, 0xe1, 0x64, 0x24, 0x70, 0x18, 0x61, 0x2c, 0x23, 0x81, 0xc3, 0x07,
|
||||
0x5a, 0x67, 0x1e, 0x38, 0x12, 0xa7, 0x63, 0xea, 0x94, 0x2f, 0x81, 0xa3, 0x67, 0xea, 0x2e, 0x1b,
|
||||
0xd5, 0xdc, 0xdb, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe7, 0x9b, 0x9d, 0x4f, 0x54, 0x01, 0x00,
|
||||
0x00,
|
||||
}
|
||||
71
vendor/cloud.google.com/go/internal/rpcreplay/proto/rpcreplay/rpcreplay.proto
generated
vendored
Normal file
71
vendor/cloud.google.com/go/internal/rpcreplay/proto/rpcreplay/rpcreplay.proto
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package rpcreplay;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
|
||||
// An Entry represents a single RPC activity, typically a request or response.
|
||||
message Entry {
|
||||
enum Kind {
|
||||
TYPE_UNSPECIFIED = 0;
|
||||
|
||||
// A unary request.
|
||||
// method: the full name of the method
|
||||
// message: the request proto
|
||||
// is_error: false
|
||||
// ref_index: 0
|
||||
REQUEST = 1;
|
||||
|
||||
// A unary response.
|
||||
// method: the full name of the method
|
||||
// message:
|
||||
// if is_error: a google.rpc.Status proto
|
||||
// else: the response proto
|
||||
// ref_index: index in the sequence of Entries of matching request (1-based)
|
||||
RESPONSE = 2;
|
||||
|
||||
// A method that creates a stream.
|
||||
// method: the full name of the method
|
||||
// message:
|
||||
// if is_error: a google.rpc.Status proto
|
||||
// else: nil
|
||||
// ref_index: 0
|
||||
CREATE_STREAM = 3;
|
||||
|
||||
// A call to Send on the client returned by a stream-creating method.
|
||||
// method: unset
|
||||
// message: the proto being sent
|
||||
// is_error: false
|
||||
// ref_index: index of matching CREATE_STREAM entry (1-based)
|
||||
SEND = 4; // message sent on stream
|
||||
|
||||
// A call to Recv on the client returned by a stream-creating method.
|
||||
// method: unset
|
||||
// message:
|
||||
// if is_error: a google.rpc.Status proto, or nil on EOF
|
||||
// else: the received message
|
||||
// ref_index: index of matching CREATE_STREAM entry
|
||||
RECV = 5; // message received from stream
|
||||
}
|
||||
|
||||
Kind kind = 1;
|
||||
string method = 2; // method name
|
||||
google.protobuf.Any message = 3; // request, response or error status
|
||||
bool is_error = 4; // was response an error?
|
||||
int32 ref_index = 5; // for RESPONSE, index of matching request;
|
||||
// for SEND/RECV, index of CREATE_STREAM
|
||||
}
|
||||
689
vendor/cloud.google.com/go/internal/rpcreplay/rpcreplay.go
generated
vendored
Normal file
689
vendor/cloud.google.com/go/internal/rpcreplay/rpcreplay.go
generated
vendored
Normal file
@@ -0,0 +1,689 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 rpcreplay
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
pb "cloud.google.com/go/internal/rpcreplay/proto/rpcreplay"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
"github.com/golang/protobuf/ptypes/any"
|
||||
spb "google.golang.org/genproto/googleapis/rpc/status"
|
||||
)
|
||||
|
||||
// A Recorder records RPCs for later playback.
|
||||
type Recorder struct {
|
||||
mu sync.Mutex
|
||||
w *bufio.Writer
|
||||
f *os.File
|
||||
next int
|
||||
err error
|
||||
}
|
||||
|
||||
// NewRecorder creates a recorder that writes to filename. The file will
|
||||
// also store the initial bytes for retrieval during replay.
|
||||
//
|
||||
// You must call Close on the Recorder to ensure that all data is written.
|
||||
func NewRecorder(filename string, initial []byte) (*Recorder, error) {
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rec, err := NewRecorderWriter(f, initial)
|
||||
if err != nil {
|
||||
_ = f.Close()
|
||||
return nil, err
|
||||
}
|
||||
rec.f = f
|
||||
return rec, nil
|
||||
}
|
||||
|
||||
// NewRecorderWriter creates a recorder that writes to w. The initial
|
||||
// bytes will also be written to w for retrieval during replay.
|
||||
//
|
||||
// You must call Close on the Recorder to ensure that all data is written.
|
||||
func NewRecorderWriter(w io.Writer, initial []byte) (*Recorder, error) {
|
||||
bw := bufio.NewWriter(w)
|
||||
if err := writeHeader(bw, initial); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Recorder{w: bw, next: 1}, nil
|
||||
}
|
||||
|
||||
// DialOptions returns the options that must be passed to grpc.Dial
|
||||
// to enable recording.
|
||||
func (r *Recorder) DialOptions() []grpc.DialOption {
|
||||
return []grpc.DialOption{
|
||||
grpc.WithUnaryInterceptor(r.interceptUnary),
|
||||
grpc.WithStreamInterceptor(r.interceptStream),
|
||||
}
|
||||
}
|
||||
|
||||
// Close saves any unwritten information.
|
||||
func (r *Recorder) Close() error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
if r.err != nil {
|
||||
return r.err
|
||||
}
|
||||
err := r.w.Flush()
|
||||
if r.f != nil {
|
||||
if err2 := r.f.Close(); err == nil {
|
||||
err = err2
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Intercepts all unary (non-stream) RPCs.
|
||||
func (r *Recorder) interceptUnary(ctx context.Context, method string, req, res interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
ereq := &entry{
|
||||
kind: pb.Entry_REQUEST,
|
||||
method: method,
|
||||
msg: message{msg: req.(proto.Message)},
|
||||
}
|
||||
|
||||
refIndex, err := r.writeEntry(ereq)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ierr := invoker(ctx, method, req, res, cc, opts...)
|
||||
eres := &entry{
|
||||
kind: pb.Entry_RESPONSE,
|
||||
refIndex: refIndex,
|
||||
}
|
||||
// If the error is not a gRPC status, then something more
|
||||
// serious is wrong. More significantly, we have no way
|
||||
// of serializing an arbitrary error. So just return it
|
||||
// without recording the response.
|
||||
if _, ok := status.FromError(ierr); !ok {
|
||||
r.mu.Lock()
|
||||
r.err = fmt.Errorf("saw non-status error in %s response: %v (%T)", method, ierr, ierr)
|
||||
r.mu.Unlock()
|
||||
return ierr
|
||||
}
|
||||
eres.msg.set(res, ierr)
|
||||
if _, err := r.writeEntry(eres); err != nil {
|
||||
return err
|
||||
}
|
||||
return ierr
|
||||
}
|
||||
|
||||
func (r *Recorder) writeEntry(e *entry) (int, error) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
if r.err != nil {
|
||||
return 0, r.err
|
||||
}
|
||||
err := writeEntry(r.w, e)
|
||||
if err != nil {
|
||||
r.err = err
|
||||
return 0, err
|
||||
}
|
||||
n := r.next
|
||||
r.next++
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (r *Recorder) interceptStream(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
cstream, serr := streamer(ctx, desc, cc, method, opts...)
|
||||
e := &entry{
|
||||
kind: pb.Entry_CREATE_STREAM,
|
||||
method: method,
|
||||
}
|
||||
e.msg.set(nil, serr)
|
||||
refIndex, err := r.writeEntry(e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &recClientStream{
|
||||
ctx: ctx,
|
||||
rec: r,
|
||||
cstream: cstream,
|
||||
refIndex: refIndex,
|
||||
}, serr
|
||||
}
|
||||
|
||||
// A recClientStream implements the gprc.ClientStream interface.
|
||||
// It behaves exactly like the default ClientStream, but also
|
||||
// records all messages sent and received.
|
||||
type recClientStream struct {
|
||||
ctx context.Context
|
||||
rec *Recorder
|
||||
cstream grpc.ClientStream
|
||||
refIndex int
|
||||
}
|
||||
|
||||
func (rcs *recClientStream) Context() context.Context { return rcs.ctx }
|
||||
|
||||
func (rcs *recClientStream) SendMsg(m interface{}) error {
|
||||
serr := rcs.cstream.SendMsg(m)
|
||||
e := &entry{
|
||||
kind: pb.Entry_SEND,
|
||||
refIndex: rcs.refIndex,
|
||||
}
|
||||
e.msg.set(m, serr)
|
||||
if _, err := rcs.rec.writeEntry(e); err != nil {
|
||||
return err
|
||||
}
|
||||
return serr
|
||||
}
|
||||
|
||||
func (rcs *recClientStream) RecvMsg(m interface{}) error {
|
||||
serr := rcs.cstream.RecvMsg(m)
|
||||
e := &entry{
|
||||
kind: pb.Entry_RECV,
|
||||
refIndex: rcs.refIndex,
|
||||
}
|
||||
e.msg.set(m, serr)
|
||||
if _, err := rcs.rec.writeEntry(e); err != nil {
|
||||
return err
|
||||
}
|
||||
return serr
|
||||
}
|
||||
|
||||
func (rcs *recClientStream) Header() (metadata.MD, error) {
|
||||
// TODO(jba): record.
|
||||
return rcs.cstream.Header()
|
||||
}
|
||||
|
||||
func (rcs *recClientStream) Trailer() metadata.MD {
|
||||
// TODO(jba): record.
|
||||
return rcs.cstream.Trailer()
|
||||
}
|
||||
|
||||
func (rcs *recClientStream) CloseSend() error {
|
||||
// TODO(jba): record.
|
||||
return rcs.cstream.CloseSend()
|
||||
}
|
||||
|
||||
// A Replayer replays a set of RPCs saved by a Recorder.
|
||||
type Replayer struct {
|
||||
initial []byte // initial state
|
||||
log func(format string, v ...interface{}) // for debugging
|
||||
|
||||
mu sync.Mutex
|
||||
calls []*call
|
||||
streams []*stream
|
||||
}
|
||||
|
||||
// A call represents a unary RPC, with a request and response (or error).
|
||||
type call struct {
|
||||
method string
|
||||
request proto.Message
|
||||
response message
|
||||
}
|
||||
|
||||
// A stream represents a gRPC stream, with an initial create-stream call, followed by
|
||||
// zero or more sends and/or receives.
|
||||
type stream struct {
|
||||
method string
|
||||
createIndex int
|
||||
createErr error // error from create call
|
||||
sends []message
|
||||
recvs []message
|
||||
}
|
||||
|
||||
// NewReplayer creates a Replayer that reads from filename.
|
||||
func NewReplayer(filename string) (*Replayer, error) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return NewReplayerReader(f)
|
||||
}
|
||||
|
||||
// NewReplayerReader creates a Replayer that reads from r.
|
||||
func NewReplayerReader(r io.Reader) (*Replayer, error) {
|
||||
rep := &Replayer{
|
||||
log: func(string, ...interface{}) {},
|
||||
}
|
||||
if err := rep.read(r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rep, nil
|
||||
}
|
||||
|
||||
// read reads the stream of recorded entries.
|
||||
// It matches requests with responses, with each pair grouped
|
||||
// into a call struct.
|
||||
func (rep *Replayer) read(r io.Reader) error {
|
||||
r = bufio.NewReader(r)
|
||||
bytes, err := readHeader(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rep.initial = bytes
|
||||
|
||||
callsByIndex := map[int]*call{}
|
||||
streamsByIndex := map[int]*stream{}
|
||||
for i := 1; ; i++ {
|
||||
e, err := readEntry(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e == nil {
|
||||
break
|
||||
}
|
||||
switch e.kind {
|
||||
case pb.Entry_REQUEST:
|
||||
callsByIndex[i] = &call{
|
||||
method: e.method,
|
||||
request: e.msg.msg,
|
||||
}
|
||||
|
||||
case pb.Entry_RESPONSE:
|
||||
call := callsByIndex[e.refIndex]
|
||||
if call == nil {
|
||||
return fmt.Errorf("replayer: no request for response #%d", i)
|
||||
}
|
||||
delete(callsByIndex, e.refIndex)
|
||||
call.response = e.msg
|
||||
rep.calls = append(rep.calls, call)
|
||||
|
||||
case pb.Entry_CREATE_STREAM:
|
||||
s := &stream{method: e.method, createIndex: i}
|
||||
s.createErr = e.msg.err
|
||||
streamsByIndex[i] = s
|
||||
rep.streams = append(rep.streams, s)
|
||||
|
||||
case pb.Entry_SEND:
|
||||
s := streamsByIndex[e.refIndex]
|
||||
if s == nil {
|
||||
return fmt.Errorf("replayer: no stream for send #%d", i)
|
||||
}
|
||||
s.sends = append(s.sends, e.msg)
|
||||
|
||||
case pb.Entry_RECV:
|
||||
s := streamsByIndex[e.refIndex]
|
||||
if s == nil {
|
||||
return fmt.Errorf("replayer: no stream for recv #%d", i)
|
||||
}
|
||||
s.recvs = append(s.recvs, e.msg)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("replayer: unknown kind %s", e.kind)
|
||||
}
|
||||
}
|
||||
if len(callsByIndex) > 0 {
|
||||
return fmt.Errorf("replayer: %d unmatched requests", len(callsByIndex))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DialOptions returns the options that must be passed to grpc.Dial
|
||||
// to enable replaying.
|
||||
func (r *Replayer) DialOptions() []grpc.DialOption {
|
||||
return []grpc.DialOption{
|
||||
// On replay, we make no RPCs, which means the connection may be closed
|
||||
// before the normally async Dial completes. Making the Dial synchronous
|
||||
// fixes that.
|
||||
grpc.WithBlock(),
|
||||
grpc.WithUnaryInterceptor(r.interceptUnary),
|
||||
grpc.WithStreamInterceptor(r.interceptStream),
|
||||
}
|
||||
}
|
||||
|
||||
// Initial returns the initial state saved by the Recorder.
|
||||
func (r *Replayer) Initial() []byte { return r.initial }
|
||||
|
||||
// SetLogFunc sets a function to be used for debug logging. The function
|
||||
// should be safe to be called from multiple goroutines.
|
||||
func (r *Replayer) SetLogFunc(f func(format string, v ...interface{})) {
|
||||
r.log = f
|
||||
}
|
||||
|
||||
// Close closes the Replayer.
|
||||
func (r *Replayer) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Replayer) interceptUnary(_ context.Context, method string, req, res interface{}, _ *grpc.ClientConn, _ grpc.UnaryInvoker, _ ...grpc.CallOption) error {
|
||||
mreq := req.(proto.Message)
|
||||
r.log("request %s (%s)", method, req)
|
||||
call := r.extractCall(method, mreq)
|
||||
if call == nil {
|
||||
return fmt.Errorf("replayer: request not found: %s", mreq)
|
||||
}
|
||||
r.log("returning %v", call.response)
|
||||
if call.response.err != nil {
|
||||
return call.response.err
|
||||
}
|
||||
proto.Merge(res.(proto.Message), call.response.msg) // copy msg into res
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Replayer) interceptStream(ctx context.Context, _ *grpc.StreamDesc, _ *grpc.ClientConn, method string, _ grpc.Streamer, _ ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
r.log("create-stream %s", method)
|
||||
str := r.extractStream(method)
|
||||
if str == nil {
|
||||
return nil, fmt.Errorf("replayer: stream not found for method %s", method)
|
||||
}
|
||||
if str.createErr != nil {
|
||||
return nil, str.createErr
|
||||
}
|
||||
return &repClientStream{ctx: ctx, str: str}, nil
|
||||
}
|
||||
|
||||
type repClientStream struct {
|
||||
ctx context.Context
|
||||
str *stream
|
||||
}
|
||||
|
||||
func (rcs *repClientStream) Context() context.Context { return rcs.ctx }
|
||||
|
||||
func (rcs *repClientStream) SendMsg(m interface{}) error {
|
||||
if len(rcs.str.sends) == 0 {
|
||||
return fmt.Errorf("replayer: no more sends for stream %s, created at index %d",
|
||||
rcs.str.method, rcs.str.createIndex)
|
||||
}
|
||||
// TODO(jba): Do not assume that the sends happen in the same order on replay.
|
||||
msg := rcs.str.sends[0]
|
||||
rcs.str.sends = rcs.str.sends[1:]
|
||||
return msg.err
|
||||
}
|
||||
|
||||
func (rcs *repClientStream) RecvMsg(m interface{}) error {
|
||||
if len(rcs.str.recvs) == 0 {
|
||||
return fmt.Errorf("replayer: no more receives for stream %s, created at index %d",
|
||||
rcs.str.method, rcs.str.createIndex)
|
||||
}
|
||||
msg := rcs.str.recvs[0]
|
||||
rcs.str.recvs = rcs.str.recvs[1:]
|
||||
if msg.err != nil {
|
||||
return msg.err
|
||||
}
|
||||
proto.Merge(m.(proto.Message), msg.msg) // copy msg into m
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rcs *repClientStream) Header() (metadata.MD, error) {
|
||||
log.Printf("replay: stream metadata not supported")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (rcs *repClientStream) Trailer() metadata.MD {
|
||||
log.Printf("replay: stream metadata not supported")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rcs *repClientStream) CloseSend() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// extractCall finds the first call in the list with the same method
|
||||
// and request. It returns nil if it can't find such a call.
|
||||
func (r *Replayer) extractCall(method string, req proto.Message) *call {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
for i, call := range r.calls {
|
||||
if call == nil {
|
||||
continue
|
||||
}
|
||||
if method == call.method && proto.Equal(req, call.request) {
|
||||
r.calls[i] = nil // nil out this call so we don't reuse it
|
||||
return call
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Replayer) extractStream(method string) *stream {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
for i, stream := range r.streams {
|
||||
if stream == nil {
|
||||
continue
|
||||
}
|
||||
if method == stream.method {
|
||||
r.streams[i] = nil
|
||||
return stream
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fprint reads the entries from filename and writes them to w in human-readable form.
|
||||
// It is intended for debugging.
|
||||
func Fprint(w io.Writer, filename string) error {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
return FprintReader(w, f)
|
||||
}
|
||||
|
||||
// FprintReader reads the entries from r and writes them to w in human-readable form.
|
||||
// It is intended for debugging.
|
||||
func FprintReader(w io.Writer, r io.Reader) error {
|
||||
initial, err := readHeader(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(w, "initial state: %q\n", string(initial))
|
||||
for i := 1; ; i++ {
|
||||
e, err := readEntry(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
s := "message"
|
||||
if e.msg.err != nil {
|
||||
s = "error"
|
||||
}
|
||||
fmt.Fprintf(w, "#%d: kind: %s, method: %s, ref index: %d, %s:\n",
|
||||
i, e.kind, e.method, e.refIndex, s)
|
||||
if e.msg.err == nil {
|
||||
if err := proto.MarshalText(w, e.msg.msg); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(w, "%v\n", e.msg.err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// An entry holds one gRPC action (request, response, etc.).
|
||||
type entry struct {
|
||||
kind pb.Entry_Kind
|
||||
method string
|
||||
msg message
|
||||
refIndex int // index of corresponding request or create-stream
|
||||
}
|
||||
|
||||
func (e1 *entry) equal(e2 *entry) bool {
|
||||
if e1 == nil && e2 == nil {
|
||||
return true
|
||||
}
|
||||
if e1 == nil || e2 == nil {
|
||||
return false
|
||||
}
|
||||
return e1.kind == e2.kind &&
|
||||
e1.method == e2.method &&
|
||||
proto.Equal(e1.msg.msg, e2.msg.msg) &&
|
||||
errEqual(e1.msg.err, e2.msg.err) &&
|
||||
e1.refIndex == e2.refIndex
|
||||
}
|
||||
|
||||
func errEqual(e1, e2 error) bool {
|
||||
if e1 == e2 {
|
||||
return true
|
||||
}
|
||||
s1, ok1 := status.FromError(e1)
|
||||
s2, ok2 := status.FromError(e2)
|
||||
if !ok1 || !ok2 {
|
||||
return false
|
||||
}
|
||||
return proto.Equal(s1.Proto(), s2.Proto())
|
||||
}
|
||||
|
||||
// message holds either a single proto.Message or an error.
|
||||
type message struct {
|
||||
msg proto.Message
|
||||
err error
|
||||
}
|
||||
|
||||
func (m *message) set(msg interface{}, err error) {
|
||||
m.err = err
|
||||
if err != io.EOF && msg != nil {
|
||||
m.msg = msg.(proto.Message)
|
||||
}
|
||||
}
|
||||
|
||||
// File format:
|
||||
// header
|
||||
// sequence of Entry protos
|
||||
//
|
||||
// Header format:
|
||||
// magic string
|
||||
// a record containing the bytes of the initial state
|
||||
|
||||
const magic = "RPCReplay"
|
||||
|
||||
func writeHeader(w io.Writer, initial []byte) error {
|
||||
if _, err := io.WriteString(w, magic); err != nil {
|
||||
return err
|
||||
}
|
||||
return writeRecord(w, initial)
|
||||
}
|
||||
|
||||
func readHeader(r io.Reader) ([]byte, error) {
|
||||
var buf [len(magic)]byte
|
||||
if _, err := io.ReadFull(r, buf[:]); err != nil {
|
||||
if err == io.EOF {
|
||||
err = errors.New("rpcreplay: empty replay file")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if string(buf[:]) != magic {
|
||||
return nil, errors.New("rpcreplay: not a replay file (does not begin with magic string)")
|
||||
}
|
||||
bytes, err := readRecord(r)
|
||||
if err == io.EOF {
|
||||
err = errors.New("rpcreplay: missing initial state")
|
||||
}
|
||||
return bytes, err
|
||||
}
|
||||
|
||||
func writeEntry(w io.Writer, e *entry) error {
|
||||
var m proto.Message
|
||||
if e.msg.err != nil && e.msg.err != io.EOF {
|
||||
s, ok := status.FromError(e.msg.err)
|
||||
if !ok {
|
||||
return fmt.Errorf("rpcreplay: error %v is not a Status", e.msg.err)
|
||||
}
|
||||
m = s.Proto()
|
||||
} else {
|
||||
m = e.msg.msg
|
||||
}
|
||||
var a *any.Any
|
||||
var err error
|
||||
if m != nil {
|
||||
a, err = ptypes.MarshalAny(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
pe := &pb.Entry{
|
||||
Kind: e.kind,
|
||||
Method: e.method,
|
||||
Message: a,
|
||||
IsError: e.msg.err != nil,
|
||||
RefIndex: int32(e.refIndex),
|
||||
}
|
||||
bytes, err := proto.Marshal(pe)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeRecord(w, bytes)
|
||||
}
|
||||
|
||||
func readEntry(r io.Reader) (*entry, error) {
|
||||
buf, err := readRecord(r)
|
||||
if err == io.EOF {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pe pb.Entry
|
||||
if err := proto.Unmarshal(buf, &pe); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var msg message
|
||||
if pe.Message != nil {
|
||||
var any ptypes.DynamicAny
|
||||
if err := ptypes.UnmarshalAny(pe.Message, &any); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pe.IsError {
|
||||
msg.err = status.ErrorProto(any.Message.(*spb.Status))
|
||||
} else {
|
||||
msg.msg = any.Message
|
||||
}
|
||||
} else if pe.IsError {
|
||||
msg.err = io.EOF
|
||||
} else if pe.Kind != pb.Entry_CREATE_STREAM {
|
||||
return nil, errors.New("rpcreplay: entry with nil message and false is_error")
|
||||
}
|
||||
return &entry{
|
||||
kind: pe.Kind,
|
||||
method: pe.Method,
|
||||
msg: msg,
|
||||
refIndex: int(pe.RefIndex),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// A record consists of an unsigned 32-bit little-endian length L followed by L
|
||||
// bytes.
|
||||
|
||||
func writeRecord(w io.Writer, data []byte) error {
|
||||
if err := binary.Write(w, binary.LittleEndian, uint32(len(data))); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := w.Write(data)
|
||||
return err
|
||||
}
|
||||
|
||||
func readRecord(r io.Reader) ([]byte, error) {
|
||||
var size uint32
|
||||
if err := binary.Read(r, binary.LittleEndian, &size); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf := make([]byte, size)
|
||||
if _, err := io.ReadFull(r, buf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
362
vendor/cloud.google.com/go/internal/rpcreplay/rpcreplay_test.go
generated
vendored
Normal file
362
vendor/cloud.google.com/go/internal/rpcreplay/rpcreplay_test.go
generated
vendored
Normal file
@@ -0,0 +1,362 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 rpcreplay
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
ipb "cloud.google.com/go/internal/rpcreplay/proto/intstore"
|
||||
rpb "cloud.google.com/go/internal/rpcreplay/proto/rpcreplay"
|
||||
"cloud.google.com/go/internal/testutil"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func TestRecordIO(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
want := []byte{1, 2, 3}
|
||||
if err := writeRecord(buf, want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := readRecord(buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeaderIO(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
want := []byte{1, 2, 3}
|
||||
if err := writeHeader(buf, want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := readHeader(buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !testutil.Equal(got, want) {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
|
||||
// readHeader errors
|
||||
for _, contents := range []string{"", "badmagic", "gRPCReplay"} {
|
||||
if _, err := readHeader(bytes.NewBufferString(contents)); err == nil {
|
||||
t.Errorf("%q: got nil, want error", contents)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEntryIO(t *testing.T) {
|
||||
for i, want := range []*entry{
|
||||
{
|
||||
kind: rpb.Entry_REQUEST,
|
||||
method: "method",
|
||||
msg: message{msg: &rpb.Entry{}},
|
||||
refIndex: 7,
|
||||
},
|
||||
{
|
||||
kind: rpb.Entry_RESPONSE,
|
||||
method: "method",
|
||||
msg: message{err: status.Error(codes.NotFound, "not found")},
|
||||
refIndex: 8,
|
||||
},
|
||||
{
|
||||
kind: rpb.Entry_RECV,
|
||||
method: "method",
|
||||
msg: message{err: io.EOF},
|
||||
refIndex: 3,
|
||||
},
|
||||
} {
|
||||
buf := &bytes.Buffer{}
|
||||
if err := writeEntry(buf, want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := readEntry(buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !got.equal(want) {
|
||||
t.Errorf("#%d: got %v, want %v", i, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var initialState = []byte{1, 2, 3}
|
||||
|
||||
func TestRecord(t *testing.T) {
|
||||
srv := newIntStoreServer()
|
||||
defer srv.stop()
|
||||
buf := record(t, srv)
|
||||
|
||||
gotIstate, err := readHeader(buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !testutil.Equal(gotIstate, initialState) {
|
||||
t.Fatalf("got %v, want %v", gotIstate, initialState)
|
||||
}
|
||||
item := &ipb.Item{Name: "a", Value: 1}
|
||||
wantEntries := []*entry{
|
||||
// Set
|
||||
{
|
||||
kind: rpb.Entry_REQUEST,
|
||||
method: "/intstore.IntStore/Set",
|
||||
msg: message{msg: item},
|
||||
},
|
||||
{
|
||||
kind: rpb.Entry_RESPONSE,
|
||||
msg: message{msg: &ipb.SetResponse{PrevValue: 0}},
|
||||
refIndex: 1,
|
||||
},
|
||||
// Get
|
||||
{
|
||||
kind: rpb.Entry_REQUEST,
|
||||
method: "/intstore.IntStore/Get",
|
||||
msg: message{msg: &ipb.GetRequest{Name: "a"}},
|
||||
},
|
||||
{
|
||||
kind: rpb.Entry_RESPONSE,
|
||||
msg: message{msg: item},
|
||||
refIndex: 3,
|
||||
},
|
||||
{
|
||||
kind: rpb.Entry_REQUEST,
|
||||
method: "/intstore.IntStore/Get",
|
||||
msg: message{msg: &ipb.GetRequest{Name: "x"}},
|
||||
},
|
||||
{
|
||||
kind: rpb.Entry_RESPONSE,
|
||||
msg: message{err: status.Error(codes.NotFound, `"x"`)},
|
||||
refIndex: 5,
|
||||
},
|
||||
// ListItems
|
||||
{ // entry #7
|
||||
kind: rpb.Entry_CREATE_STREAM,
|
||||
method: "/intstore.IntStore/ListItems",
|
||||
},
|
||||
{
|
||||
kind: rpb.Entry_SEND,
|
||||
msg: message{msg: &ipb.ListItemsRequest{}},
|
||||
refIndex: 7,
|
||||
},
|
||||
{
|
||||
kind: rpb.Entry_RECV,
|
||||
msg: message{msg: item},
|
||||
refIndex: 7,
|
||||
},
|
||||
{
|
||||
kind: rpb.Entry_RECV,
|
||||
msg: message{err: io.EOF},
|
||||
refIndex: 7,
|
||||
},
|
||||
// SetStream
|
||||
{ // entry #11
|
||||
kind: rpb.Entry_CREATE_STREAM,
|
||||
method: "/intstore.IntStore/SetStream",
|
||||
},
|
||||
{
|
||||
kind: rpb.Entry_SEND,
|
||||
msg: message{msg: &ipb.Item{Name: "b", Value: 2}},
|
||||
refIndex: 11,
|
||||
},
|
||||
{
|
||||
kind: rpb.Entry_SEND,
|
||||
msg: message{msg: &ipb.Item{Name: "c", Value: 3}},
|
||||
refIndex: 11,
|
||||
},
|
||||
{
|
||||
kind: rpb.Entry_RECV,
|
||||
msg: message{msg: &ipb.Summary{Count: 2}},
|
||||
refIndex: 11,
|
||||
},
|
||||
|
||||
// StreamChat
|
||||
{ // entry #15
|
||||
kind: rpb.Entry_CREATE_STREAM,
|
||||
method: "/intstore.IntStore/StreamChat",
|
||||
},
|
||||
{
|
||||
kind: rpb.Entry_SEND,
|
||||
msg: message{msg: &ipb.Item{Name: "d", Value: 4}},
|
||||
refIndex: 15,
|
||||
},
|
||||
{
|
||||
kind: rpb.Entry_RECV,
|
||||
msg: message{msg: &ipb.Item{Name: "d", Value: 4}},
|
||||
refIndex: 15,
|
||||
},
|
||||
{
|
||||
kind: rpb.Entry_SEND,
|
||||
msg: message{msg: &ipb.Item{Name: "e", Value: 5}},
|
||||
refIndex: 15,
|
||||
},
|
||||
{
|
||||
kind: rpb.Entry_RECV,
|
||||
msg: message{msg: &ipb.Item{Name: "e", Value: 5}},
|
||||
refIndex: 15,
|
||||
},
|
||||
{
|
||||
kind: rpb.Entry_RECV,
|
||||
msg: message{err: io.EOF},
|
||||
refIndex: 15,
|
||||
},
|
||||
}
|
||||
for i, w := range wantEntries {
|
||||
g, err := readEntry(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("%#d: %v", i+1, err)
|
||||
}
|
||||
if !g.equal(w) {
|
||||
t.Errorf("#%d:\ngot %+v\nwant %+v", i+1, g, w)
|
||||
}
|
||||
}
|
||||
g, err := readEntry(buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if g != nil {
|
||||
t.Errorf("\ngot %+v\nwant nil", g)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReplay(t *testing.T) {
|
||||
srv := newIntStoreServer()
|
||||
defer srv.stop()
|
||||
|
||||
buf := record(t, srv)
|
||||
rep, err := NewReplayerReader(buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, want := rep.Initial(), initialState; !testutil.Equal(got, want) {
|
||||
t.Fatalf("got %v, want %v", got, want)
|
||||
}
|
||||
// Replay the test.
|
||||
testService(t, srv.Addr, rep.DialOptions())
|
||||
}
|
||||
|
||||
func record(t *testing.T, srv *intStoreServer) *bytes.Buffer {
|
||||
buf := &bytes.Buffer{}
|
||||
rec, err := NewRecorderWriter(buf, initialState)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
testService(t, srv.Addr, rec.DialOptions())
|
||||
if err := rec.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
func testService(t *testing.T, addr string, opts []grpc.DialOption) {
|
||||
conn, err := grpc.Dial(addr,
|
||||
append([]grpc.DialOption{grpc.WithInsecure()}, opts...)...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
client := ipb.NewIntStoreClient(conn)
|
||||
ctx := context.Background()
|
||||
item := &ipb.Item{Name: "a", Value: 1}
|
||||
res, err := client.Set(ctx, item)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if res.PrevValue != 0 {
|
||||
t.Errorf("got %d, want 0", res.PrevValue)
|
||||
}
|
||||
got, err := client.Get(ctx, &ipb.GetRequest{Name: "a"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !proto.Equal(got, item) {
|
||||
t.Errorf("got %v, want %v", got, item)
|
||||
}
|
||||
_, err = client.Get(ctx, &ipb.GetRequest{Name: "x"})
|
||||
if err == nil {
|
||||
t.Fatal("got nil, want error")
|
||||
}
|
||||
if _, ok := status.FromError(err); !ok {
|
||||
t.Errorf("got error type %T, want a grpc/status.Status", err)
|
||||
}
|
||||
|
||||
wantItems := []*ipb.Item{item}
|
||||
lic, err := client.ListItems(ctx, &ipb.ListItemsRequest{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i := 0; ; i++ {
|
||||
item, err := lic.Recv()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if i >= len(wantItems) || !proto.Equal(item, wantItems[i]) {
|
||||
t.Fatalf("%d: bad item", i)
|
||||
}
|
||||
}
|
||||
|
||||
ssc, err := client.SetStream(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
must := func(err error) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
for i, name := range []string{"b", "c"} {
|
||||
must(ssc.Send(&ipb.Item{Name: name, Value: int32(i + 2)}))
|
||||
}
|
||||
summary, err := ssc.CloseAndRecv()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, want := summary.Count, int32(2); got != want {
|
||||
t.Fatalf("got %d, want %d", got, want)
|
||||
}
|
||||
|
||||
chatc, err := client.StreamChat(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i, name := range []string{"d", "e"} {
|
||||
item := &ipb.Item{Name: name, Value: int32(i + 4)}
|
||||
must(chatc.Send(item))
|
||||
got, err := chatc.Recv()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !proto.Equal(got, item) {
|
||||
t.Errorf("got %v, want %v", got, item)
|
||||
}
|
||||
}
|
||||
must(chatc.CloseSend())
|
||||
if _, err := chatc.Recv(); err != io.EOF {
|
||||
t.Fatalf("got %v, want EOF", err)
|
||||
}
|
||||
}
|
||||
99
vendor/cloud.google.com/go/internal/testutil/cmp.go
generated
vendored
Normal file
99
vendor/cloud.google.com/go/internal/testutil/cmp.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
var (
|
||||
alwaysEqual = cmp.Comparer(func(_, _ interface{}) bool { return true })
|
||||
|
||||
defaultCmpOptions = []cmp.Option{
|
||||
// Use proto.Equal for protobufs
|
||||
cmp.Comparer(proto.Equal),
|
||||
// NaNs compare equal
|
||||
cmp.FilterValues(func(x, y float64) bool {
|
||||
return math.IsNaN(x) && math.IsNaN(y)
|
||||
}, alwaysEqual),
|
||||
cmp.FilterValues(func(x, y float32) bool {
|
||||
return math.IsNaN(float64(x)) && math.IsNaN(float64(y))
|
||||
}, alwaysEqual),
|
||||
}
|
||||
)
|
||||
|
||||
// Equal tests two values for equality.
|
||||
func Equal(x, y interface{}, opts ...cmp.Option) bool {
|
||||
// Put default options at the end. Order doesn't matter.
|
||||
opts = append(opts[:len(opts):len(opts)], defaultCmpOptions...)
|
||||
return cmp.Equal(x, y, opts...)
|
||||
}
|
||||
|
||||
// Diff reports the differences between two values.
|
||||
// Diff(x, y) == "" iff Equal(x, y).
|
||||
func Diff(x, y interface{}, opts ...cmp.Option) string {
|
||||
// Put default options at the end. Order doesn't matter.
|
||||
opts = append(opts[:len(opts):len(opts)], defaultCmpOptions...)
|
||||
return cmp.Diff(x, y, opts...)
|
||||
}
|
||||
|
||||
// TODO(jba): remove the code below when cmpopts becomes available.
|
||||
|
||||
// IgnoreUnexported returns an Option that only ignores the immediate unexported
|
||||
// fields of a struct, including anonymous fields of unexported types.
|
||||
// In particular, unexported fields within the struct's exported fields
|
||||
// of struct types, including anonymous fields, will not be ignored unless the
|
||||
// type of the field itself is also passed to IgnoreUnexported.
|
||||
func IgnoreUnexported(typs ...interface{}) cmp.Option {
|
||||
ux := newUnexportedFilter(typs...)
|
||||
return cmp.FilterPath(ux.filter, cmp.Ignore())
|
||||
}
|
||||
|
||||
type unexportedFilter struct{ m map[reflect.Type]bool }
|
||||
|
||||
func newUnexportedFilter(typs ...interface{}) unexportedFilter {
|
||||
ux := unexportedFilter{m: make(map[reflect.Type]bool)}
|
||||
for _, typ := range typs {
|
||||
t := reflect.TypeOf(typ)
|
||||
if t == nil || t.Kind() != reflect.Struct {
|
||||
panic(fmt.Sprintf("invalid struct type: %T", typ))
|
||||
}
|
||||
ux.m[t] = true
|
||||
}
|
||||
return ux
|
||||
}
|
||||
func (xf unexportedFilter) filter(p cmp.Path) bool {
|
||||
if len(p) < 2 {
|
||||
return false
|
||||
}
|
||||
sf, ok := p[len(p)-1].(cmp.StructField)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return xf.m[p[len(p)-2].Type()] && !isExported(sf.Name())
|
||||
}
|
||||
|
||||
// isExported reports whether the identifier is exported.
|
||||
func isExported(id string) bool {
|
||||
r, _ := utf8.DecodeRuneInString(id)
|
||||
return unicode.IsUpper(r)
|
||||
}
|
||||
67
vendor/cloud.google.com/go/internal/testutil/context.go
generated
vendored
Normal file
67
vendor/cloud.google.com/go/internal/testutil/context.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 contains helper functions for writing tests.
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
)
|
||||
|
||||
const (
|
||||
envProjID = "GCLOUD_TESTS_GOLANG_PROJECT_ID"
|
||||
envPrivateKey = "GCLOUD_TESTS_GOLANG_KEY"
|
||||
)
|
||||
|
||||
// ProjID returns the project ID to use in integration tests, or the empty
|
||||
// string if none is configured.
|
||||
func ProjID() string {
|
||||
projID := os.Getenv(envProjID)
|
||||
if projID == "" {
|
||||
return ""
|
||||
}
|
||||
return projID
|
||||
}
|
||||
|
||||
// TokenSource returns the OAuth2 token source to use in integration tests,
|
||||
// or nil if none is configured. If the environment variable is unset,
|
||||
// TokenSource will try to find 'Application Default Credentials'. Else,
|
||||
// TokenSource will return nil.
|
||||
// TokenSource will log.Fatal if the token source is specified but missing or invalid.
|
||||
func TokenSource(ctx context.Context, scopes ...string) oauth2.TokenSource {
|
||||
key := os.Getenv(envPrivateKey)
|
||||
if key == "" { // Try for application default credentials.
|
||||
ts, err := google.DefaultTokenSource(ctx, scopes...)
|
||||
if err != nil {
|
||||
log.Println("No 'Application Default Credentials' found.")
|
||||
return nil
|
||||
}
|
||||
return ts
|
||||
}
|
||||
jsonKey, err := ioutil.ReadFile(key)
|
||||
if err != nil {
|
||||
log.Fatalf("Cannot read the JSON key file, err: %v", err)
|
||||
}
|
||||
conf, err := google.JWTConfigFromJSON(jsonKey, scopes...)
|
||||
if err != nil {
|
||||
log.Fatalf("google.JWTConfigFromJSON: %v", err)
|
||||
}
|
||||
return conf.TokenSource(ctx)
|
||||
}
|
||||
73
vendor/cloud.google.com/go/internal/testutil/server.go
generated
vendored
Normal file
73
vendor/cloud.google.com/go/internal/testutil/server.go
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright 2016 Google Inc. All Rights Reserved.
|
||||
|
||||
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 (
|
||||
"net"
|
||||
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// A Server is an in-process gRPC server, listening on a system-chosen port on
|
||||
// the local loopback interface. Servers are for testing only and are not
|
||||
// intended to be used in production code.
|
||||
//
|
||||
// To create a server, make a new Server, register your handlers, then call
|
||||
// Start:
|
||||
//
|
||||
// srv, err := NewServer()
|
||||
// ...
|
||||
// mypb.RegisterMyServiceServer(srv.Gsrv, &myHandler)
|
||||
// ....
|
||||
// srv.Start()
|
||||
//
|
||||
// Clients should connect to the server with no security:
|
||||
//
|
||||
// conn, err := grpc.Dial(srv.Addr, grpc.WithInsecure())
|
||||
// ...
|
||||
type Server struct {
|
||||
Addr string
|
||||
l net.Listener
|
||||
Gsrv *grpc.Server
|
||||
}
|
||||
|
||||
// NewServer creates a new Server. The Server will be listening for gRPC connections
|
||||
// at the address named by the Addr field, without TLS.
|
||||
func NewServer(opts ...grpc.ServerOption) (*Server, error) {
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := &Server{
|
||||
Addr: l.Addr().String(),
|
||||
l: l,
|
||||
Gsrv: grpc.NewServer(opts...),
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Start causes the server to start accepting incoming connections.
|
||||
// Call Start after registering handlers.
|
||||
func (s *Server) Start() {
|
||||
go s.Gsrv.Serve(s.l)
|
||||
}
|
||||
|
||||
// Close shuts down the server.
|
||||
func (s *Server) Close() {
|
||||
s.Gsrv.Stop()
|
||||
s.l.Close()
|
||||
}
|
||||
35
vendor/cloud.google.com/go/internal/testutil/server_test.go
generated
vendored
Normal file
35
vendor/cloud.google.com/go/internal/testutil/server_test.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 (
|
||||
"testing"
|
||||
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func TestNewServer(t *testing.T) {
|
||||
srv, err := NewServer()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
srv.Start()
|
||||
conn, err := grpc.Dial(srv.Addr, grpc.WithInsecure())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
conn.Close()
|
||||
srv.Close()
|
||||
}
|
||||
93
vendor/cloud.google.com/go/internal/testutil/unique.go
generated
vendored
Normal file
93
vendor/cloud.google.com/go/internal/testutil/unique.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// This file supports generating unique IDs so that multiple test executions
|
||||
// don't interfere with each other, and cleaning up old entities that may
|
||||
// remain if tests exit early.
|
||||
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var startTime = time.Now().UTC()
|
||||
|
||||
// A UIDSpace manages a set of unique IDs distinguished by a prefix.
|
||||
type UIDSpace struct {
|
||||
Prefix string
|
||||
re *regexp.Regexp
|
||||
mu sync.Mutex
|
||||
count int
|
||||
}
|
||||
|
||||
func NewUIDSpace(prefix string) *UIDSpace {
|
||||
return &UIDSpace{
|
||||
Prefix: prefix,
|
||||
re: regexp.MustCompile("^" + regexp.QuoteMeta(prefix) + `-(\d{4})(\d{2})(\d{2})-(\d+)-\d+$`),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// New generates a new unique ID . The ID consists of the UIDSpace's prefix, a
|
||||
// timestamp, and a counter value. All unique IDs generated in the same test
|
||||
// execution will have the same timestamp.
|
||||
//
|
||||
// Aside from the characters in the prefix, IDs contain only letters, numbers
|
||||
// and hyphens.
|
||||
func (s *UIDSpace) New() string { return s.newID(startTime) }
|
||||
|
||||
func (s *UIDSpace) newID(t time.Time) string {
|
||||
s.mu.Lock()
|
||||
c := s.count
|
||||
s.count++
|
||||
s.mu.Unlock()
|
||||
// Write the time as a date followed by nanoseconds from midnight of that date.
|
||||
// That makes it easier to see the approximate time of the ID when it is displayed.
|
||||
y, m, d := t.Date()
|
||||
ns := t.Sub(time.Date(y, m, d, 0, 0, 0, 0, time.UTC))
|
||||
// Zero-pad the counter for lexical sort order for IDs with the same timestamp.
|
||||
return fmt.Sprintf("%s-%04d%02d%02d-%d-%04d", s.Prefix, y, m, d, ns, c)
|
||||
}
|
||||
|
||||
// Timestamp extracts the timestamp of uid, which must have been generated by
|
||||
// s. The second return value is true on success, false if there was a problem.
|
||||
func (s *UIDSpace) Timestamp(uid string) (time.Time, bool) {
|
||||
subs := s.re.FindStringSubmatch(uid)
|
||||
if subs == nil {
|
||||
return time.Time{}, false
|
||||
}
|
||||
y, err1 := strconv.Atoi(subs[1])
|
||||
m, err2 := strconv.Atoi(subs[2])
|
||||
d, err3 := strconv.Atoi(subs[3])
|
||||
ns, err4 := strconv.Atoi(subs[4])
|
||||
if err1 != nil || err2 != nil || err3 != nil || err4 != nil {
|
||||
return time.Time{}, false
|
||||
}
|
||||
return time.Date(y, time.Month(m), d, 0, 0, 0, ns, time.UTC), true
|
||||
}
|
||||
|
||||
// Older reports whether uid was created by m and has a timestamp older than
|
||||
// the current time by at least d.
|
||||
func (s *UIDSpace) Older(uid string, d time.Duration) bool {
|
||||
ts, ok := s.Timestamp(uid)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return time.Since(ts) > d
|
||||
}
|
||||
62
vendor/cloud.google.com/go/internal/testutil/unique_test.go
generated
vendored
Normal file
62
vendor/cloud.google.com/go/internal/testutil/unique_test.go
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
s := NewUIDSpace("prefix")
|
||||
tm := time.Date(2017, 1, 6, 0, 0, 0, 21, time.UTC)
|
||||
got := s.newID(tm)
|
||||
want := "prefix-20170106-21-0000"
|
||||
if got != want {
|
||||
t.Errorf("got %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimestamp(t *testing.T) {
|
||||
s := NewUIDSpace("unique-ID")
|
||||
uid := s.New()
|
||||
got, ok := s.Timestamp(uid)
|
||||
if !ok {
|
||||
t.Fatal("got ok = false, want true")
|
||||
}
|
||||
if !startTime.Equal(got) {
|
||||
t.Errorf("got %s, want %s", got, startTime)
|
||||
}
|
||||
|
||||
got, ok = s.Timestamp("unique-ID-20160308-123-8")
|
||||
if !ok {
|
||||
t.Fatal("got false, want true")
|
||||
}
|
||||
if want := time.Date(2016, 3, 8, 0, 0, 0, 123, time.UTC); !want.Equal(got) {
|
||||
t.Errorf("got %s, want %s", got, want)
|
||||
}
|
||||
if _, ok = s.Timestamp("invalid-time-1234"); ok {
|
||||
t.Error("got true, want false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOlder(t *testing.T) {
|
||||
s := NewUIDSpace("uid")
|
||||
// A non-matching ID returns false.
|
||||
id2 := NewUIDSpace("different-prefix").New()
|
||||
if got, want := s.Older(id2, time.Second), false; got != want {
|
||||
t.Errorf("got %t, want %t", got, want)
|
||||
}
|
||||
}
|
||||
83
vendor/cloud.google.com/go/internal/tracecontext/tracecontext.go
generated
vendored
Normal file
83
vendor/cloud.google.com/go/internal/tracecontext/tracecontext.go
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 tracecontext provides encoders and decoders for Stackdriver Trace contexts.
|
||||
package tracecontext
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
const (
|
||||
versionID = 0
|
||||
traceIDField = 0
|
||||
spanIDField = 1
|
||||
optsField = 2
|
||||
|
||||
traceIDLen = 16
|
||||
spanIDLen = 8
|
||||
optsLen = 1
|
||||
|
||||
// Len represents the length of trace context.
|
||||
Len = 1 + 1 + traceIDLen + 1 + spanIDLen + 1 + optsLen
|
||||
)
|
||||
|
||||
// Encode encodes trace ID, span ID and options into dst. The number of bytes
|
||||
// written will be returned. If len(dst) isn't big enough to fit the trace context,
|
||||
// a negative number is returned.
|
||||
func Encode(dst []byte, traceID []byte, spanID uint64, opts byte) (n int) {
|
||||
if len(dst) < Len {
|
||||
return -1
|
||||
}
|
||||
var offset = 0
|
||||
putByte := func(b byte) { dst[offset] = b; offset++ }
|
||||
putUint64 := func(u uint64) { binary.LittleEndian.PutUint64(dst[offset:], u); offset += 8 }
|
||||
|
||||
putByte(versionID)
|
||||
putByte(traceIDField)
|
||||
for _, b := range traceID {
|
||||
putByte(b)
|
||||
}
|
||||
putByte(spanIDField)
|
||||
putUint64(spanID)
|
||||
putByte(optsField)
|
||||
putByte(opts)
|
||||
|
||||
return offset
|
||||
}
|
||||
|
||||
// Decode decodes the src into a trace ID, span ID and options. If src doesn't
|
||||
// contain a valid trace context, ok = false is returned.
|
||||
func Decode(src []byte) (traceID []byte, spanID uint64, opts byte, ok bool) {
|
||||
if len(src) < Len {
|
||||
return traceID, spanID, 0, false
|
||||
}
|
||||
var offset = 0
|
||||
readByte := func() byte { b := src[offset]; offset++; return b }
|
||||
readUint64 := func() uint64 { v := binary.LittleEndian.Uint64(src[offset:]); offset += 8; return v }
|
||||
|
||||
if readByte() != versionID {
|
||||
return traceID, spanID, 0, false
|
||||
}
|
||||
for offset < len(src) {
|
||||
switch readByte() {
|
||||
case traceIDField:
|
||||
traceID = src[offset : offset+traceIDLen]
|
||||
offset += traceIDLen
|
||||
case spanIDField:
|
||||
spanID = readUint64()
|
||||
case optsField:
|
||||
opts = readByte()
|
||||
}
|
||||
}
|
||||
return traceID, spanID, opts, true
|
||||
}
|
||||
136
vendor/cloud.google.com/go/internal/tracecontext/tracecontext_test.go
generated
vendored
Normal file
136
vendor/cloud.google.com/go/internal/tracecontext/tracecontext_test.go
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 tracecontext
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"cloud.google.com/go/internal/testutil"
|
||||
)
|
||||
|
||||
var validData = []byte{0, 0, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 1, 97, 98, 99, 100, 101, 102, 103, 104, 2, 1}
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
data []byte
|
||||
wantTraceID []byte
|
||||
wantSpanID uint64
|
||||
wantOpts byte
|
||||
wantOk bool
|
||||
}{
|
||||
{
|
||||
name: "nil data",
|
||||
data: nil,
|
||||
wantTraceID: nil,
|
||||
wantSpanID: 0,
|
||||
wantOpts: 0,
|
||||
wantOk: false,
|
||||
},
|
||||
{
|
||||
name: "short data",
|
||||
data: []byte{0, 0, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77},
|
||||
wantTraceID: nil,
|
||||
wantSpanID: 0,
|
||||
wantOpts: 0,
|
||||
wantOk: false,
|
||||
},
|
||||
{
|
||||
name: "wrong field number",
|
||||
data: []byte{0, 1, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77},
|
||||
wantTraceID: nil,
|
||||
wantSpanID: 0,
|
||||
wantOpts: 0,
|
||||
wantOk: false,
|
||||
},
|
||||
{
|
||||
name: "valid data",
|
||||
data: validData,
|
||||
wantTraceID: []byte{64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79},
|
||||
wantSpanID: 0x6867666564636261,
|
||||
wantOpts: 1,
|
||||
wantOk: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
gotTraceID, gotSpanID, gotOpts, gotOk := Decode(tt.data)
|
||||
if !testutil.Equal(gotTraceID, tt.wantTraceID) {
|
||||
t.Errorf("%s: Decode() gotTraceID = %v, want %v", tt.name, gotTraceID, tt.wantTraceID)
|
||||
}
|
||||
if gotSpanID != tt.wantSpanID {
|
||||
t.Errorf("%s: Decode() gotSpanID = %v, want %v", tt.name, gotSpanID, tt.wantSpanID)
|
||||
}
|
||||
if gotOpts != tt.wantOpts {
|
||||
t.Errorf("%s: Decode() gotOpts = %v, want %v", tt.name, gotOpts, tt.wantOpts)
|
||||
}
|
||||
if gotOk != tt.wantOk {
|
||||
t.Errorf("%s: Decode() gotOk = %v, want %v", tt.name, gotOk, tt.wantOk)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncode(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
dst []byte
|
||||
traceID []byte
|
||||
spanID uint64
|
||||
opts byte
|
||||
wantN int
|
||||
wantData []byte
|
||||
}{
|
||||
{
|
||||
name: "short data",
|
||||
dst: make([]byte, 0),
|
||||
traceID: []byte("00112233445566"),
|
||||
spanID: 0x6867666564636261,
|
||||
opts: 1,
|
||||
wantN: -1,
|
||||
wantData: make([]byte, 0),
|
||||
},
|
||||
{
|
||||
name: "valid data",
|
||||
dst: make([]byte, Len),
|
||||
traceID: []byte{64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79},
|
||||
spanID: 0x6867666564636261,
|
||||
opts: 1,
|
||||
wantN: Len,
|
||||
wantData: validData,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
gotN := Encode(tt.dst, tt.traceID, tt.spanID, tt.opts)
|
||||
if gotN != tt.wantN {
|
||||
t.Errorf("%s: n = %v, want %v", tt.name, gotN, tt.wantN)
|
||||
}
|
||||
if gotData := tt.dst; !testutil.Equal(gotData, tt.wantData) {
|
||||
t.Errorf("%s: dst = %v, want %v", tt.name, gotData, tt.wantData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecode(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
Decode(validData)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncode(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
traceID := make([]byte, 16)
|
||||
var opts byte
|
||||
Encode(validData, traceID, 0, opts)
|
||||
}
|
||||
}
|
||||
6
vendor/cloud.google.com/go/internal/version/update_version.sh
generated
vendored
Executable file
6
vendor/cloud.google.com/go/internal/version/update_version.sh
generated
vendored
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
today=$(date +%Y%m%d)
|
||||
|
||||
sed -i -r -e 's/const Repo = "([0-9]{8})"/const Repo = "'$today'"/' $GOFILE
|
||||
|
||||
71
vendor/cloud.google.com/go/internal/version/version.go
generated
vendored
Normal file
71
vendor/cloud.google.com/go/internal/version/version.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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.
|
||||
|
||||
//go:generate ./update_version.sh
|
||||
|
||||
// Package version contains version information for Google Cloud Client
|
||||
// Libraries for Go, as reported in request headers.
|
||||
package version
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Repo is the current version of the client libraries in this
|
||||
// repo. It should be a date in YYYYMMDD format.
|
||||
const Repo = "20170621"
|
||||
|
||||
// Go returns the Go runtime version. The returned string
|
||||
// has no whitespace.
|
||||
func Go() string {
|
||||
return goVersion
|
||||
}
|
||||
|
||||
var goVersion = goVer(runtime.Version())
|
||||
|
||||
const develPrefix = "devel +"
|
||||
|
||||
func goVer(s string) string {
|
||||
if strings.HasPrefix(s, develPrefix) {
|
||||
s = s[len(develPrefix):]
|
||||
if p := strings.IndexFunc(s, unicode.IsSpace); p >= 0 {
|
||||
s = s[:p]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
if strings.HasPrefix(s, "go1") {
|
||||
s = s[2:]
|
||||
var prerelease string
|
||||
if p := strings.IndexFunc(s, notSemverRune); p >= 0 {
|
||||
s, prerelease = s[:p], s[p:]
|
||||
}
|
||||
if strings.HasSuffix(s, ".") {
|
||||
s += "0"
|
||||
} else if strings.Count(s, ".") < 2 {
|
||||
s += ".0"
|
||||
}
|
||||
if prerelease != "" {
|
||||
s += "-" + prerelease
|
||||
}
|
||||
return s
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func notSemverRune(r rune) bool {
|
||||
return strings.IndexRune("0123456789.", r) < 0
|
||||
}
|
||||
35
vendor/cloud.google.com/go/internal/version/version_test.go
generated
vendored
Normal file
35
vendor/cloud.google.com/go/internal/version/version_test.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 version
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGoVer(t *testing.T) {
|
||||
for _, tst := range []struct {
|
||||
in, want string
|
||||
}{
|
||||
{"go1.8", "1.8.0"},
|
||||
{"go1.7.3", "1.7.3"},
|
||||
{"go1.8.typealias", "1.8.0-typealias"},
|
||||
{"go1.8beta1", "1.8.0-beta1"},
|
||||
{"go1.8rc2", "1.8.0-rc2"},
|
||||
{"devel +824f981dd4b7 Tue Apr 29 21:41:54 2014 -0400", "824f981dd4b7"},
|
||||
{"foo bar zipzap", ""},
|
||||
} {
|
||||
if got := goVer(tst.in); got != tst.want {
|
||||
t.Errorf("goVer(%q) = %q, want %q", tst.in, got, tst.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user