mirror of
https://github.com/kubernetes-sigs/descheduler.git
synced 2026-01-26 05:14:13 +01:00
496 lines
10 KiB
Go
496 lines
10 KiB
Go
// Copyright 2015 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 bigquery
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
bq "google.golang.org/api/bigquery/v2"
|
|
)
|
|
|
|
func (fs *FieldSchema) GoString() string {
|
|
if fs == nil {
|
|
return "<nil>"
|
|
}
|
|
|
|
return fmt.Sprintf("{Name:%s Description:%s Repeated:%t Required:%t Type:%s Schema:%s}",
|
|
fs.Name,
|
|
fs.Description,
|
|
fs.Repeated,
|
|
fs.Required,
|
|
fs.Type,
|
|
fmt.Sprintf("%#v", fs.Schema),
|
|
)
|
|
}
|
|
|
|
func bqTableFieldSchema(desc, name, typ, mode string) *bq.TableFieldSchema {
|
|
return &bq.TableFieldSchema{
|
|
Description: desc,
|
|
Name: name,
|
|
Mode: mode,
|
|
Type: typ,
|
|
}
|
|
}
|
|
|
|
func fieldSchema(desc, name, typ string, repeated, required bool) *FieldSchema {
|
|
return &FieldSchema{
|
|
Description: desc,
|
|
Name: name,
|
|
Repeated: repeated,
|
|
Required: required,
|
|
Type: FieldType(typ),
|
|
}
|
|
}
|
|
|
|
func TestSchemaConversion(t *testing.T) {
|
|
testCases := []struct {
|
|
schema Schema
|
|
bqSchema *bq.TableSchema
|
|
}{
|
|
{
|
|
// required
|
|
bqSchema: &bq.TableSchema{
|
|
Fields: []*bq.TableFieldSchema{
|
|
bqTableFieldSchema("desc", "name", "STRING", "REQUIRED"),
|
|
},
|
|
},
|
|
schema: Schema{
|
|
fieldSchema("desc", "name", "STRING", false, true),
|
|
},
|
|
},
|
|
{
|
|
// repeated
|
|
bqSchema: &bq.TableSchema{
|
|
Fields: []*bq.TableFieldSchema{
|
|
bqTableFieldSchema("desc", "name", "STRING", "REPEATED"),
|
|
},
|
|
},
|
|
schema: Schema{
|
|
fieldSchema("desc", "name", "STRING", true, false),
|
|
},
|
|
},
|
|
{
|
|
// nullable, string
|
|
bqSchema: &bq.TableSchema{
|
|
Fields: []*bq.TableFieldSchema{
|
|
bqTableFieldSchema("desc", "name", "STRING", ""),
|
|
},
|
|
},
|
|
schema: Schema{
|
|
fieldSchema("desc", "name", "STRING", false, false),
|
|
},
|
|
},
|
|
{
|
|
// integer
|
|
bqSchema: &bq.TableSchema{
|
|
Fields: []*bq.TableFieldSchema{
|
|
bqTableFieldSchema("desc", "name", "INTEGER", ""),
|
|
},
|
|
},
|
|
schema: Schema{
|
|
fieldSchema("desc", "name", "INTEGER", false, false),
|
|
},
|
|
},
|
|
{
|
|
// float
|
|
bqSchema: &bq.TableSchema{
|
|
Fields: []*bq.TableFieldSchema{
|
|
bqTableFieldSchema("desc", "name", "FLOAT", ""),
|
|
},
|
|
},
|
|
schema: Schema{
|
|
fieldSchema("desc", "name", "FLOAT", false, false),
|
|
},
|
|
},
|
|
{
|
|
// boolean
|
|
bqSchema: &bq.TableSchema{
|
|
Fields: []*bq.TableFieldSchema{
|
|
bqTableFieldSchema("desc", "name", "BOOLEAN", ""),
|
|
},
|
|
},
|
|
schema: Schema{
|
|
fieldSchema("desc", "name", "BOOLEAN", false, false),
|
|
},
|
|
},
|
|
{
|
|
// timestamp
|
|
bqSchema: &bq.TableSchema{
|
|
Fields: []*bq.TableFieldSchema{
|
|
bqTableFieldSchema("desc", "name", "TIMESTAMP", ""),
|
|
},
|
|
},
|
|
schema: Schema{
|
|
fieldSchema("desc", "name", "TIMESTAMP", false, false),
|
|
},
|
|
},
|
|
{
|
|
// nested
|
|
bqSchema: &bq.TableSchema{
|
|
Fields: []*bq.TableFieldSchema{
|
|
{
|
|
Description: "An outer schema wrapping a nested schema",
|
|
Name: "outer",
|
|
Mode: "REQUIRED",
|
|
Type: "RECORD",
|
|
Fields: []*bq.TableFieldSchema{
|
|
bqTableFieldSchema("inner field", "inner", "STRING", ""),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
schema: Schema{
|
|
&FieldSchema{
|
|
Description: "An outer schema wrapping a nested schema",
|
|
Name: "outer",
|
|
Required: true,
|
|
Type: "RECORD",
|
|
Schema: []*FieldSchema{
|
|
{
|
|
Description: "inner field",
|
|
Name: "inner",
|
|
Type: "STRING",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
bqSchema := tc.schema.asTableSchema()
|
|
if !reflect.DeepEqual(bqSchema, tc.bqSchema) {
|
|
t.Errorf("converting to TableSchema: got:\n%v\nwant:\n%v", bqSchema, tc.bqSchema)
|
|
}
|
|
schema := convertTableSchema(tc.bqSchema)
|
|
if !reflect.DeepEqual(schema, tc.schema) {
|
|
t.Errorf("converting to Schema: got:\n%v\nwant:\n%v", schema, tc.schema)
|
|
}
|
|
}
|
|
}
|
|
|
|
type allStrings struct {
|
|
String string
|
|
ByteSlice []byte
|
|
}
|
|
|
|
type allSignedIntegers struct {
|
|
Int64 int64
|
|
Int32 int32
|
|
Int16 int16
|
|
Int8 int8
|
|
Int int
|
|
}
|
|
|
|
type allUnsignedIntegers struct {
|
|
Uint64 uint64
|
|
Uint32 uint32
|
|
Uint16 uint16
|
|
Uint8 uint8
|
|
Uintptr uintptr
|
|
Uint uint
|
|
}
|
|
|
|
type allFloat struct {
|
|
Float64 float64
|
|
Float32 float32
|
|
// NOTE: Complex32 and Complex64 are unsupported by BigQuery
|
|
}
|
|
|
|
type allBoolean struct {
|
|
Bool bool
|
|
}
|
|
|
|
type allTime struct {
|
|
Time time.Time
|
|
}
|
|
|
|
func TestSimpleInference(t *testing.T) {
|
|
testCases := []struct {
|
|
in interface{}
|
|
want Schema
|
|
}{
|
|
{
|
|
in: allSignedIntegers{},
|
|
want: Schema{
|
|
fieldSchema("", "Int64", "INTEGER", false, true),
|
|
fieldSchema("", "Int32", "INTEGER", false, true),
|
|
fieldSchema("", "Int16", "INTEGER", false, true),
|
|
fieldSchema("", "Int8", "INTEGER", false, true),
|
|
fieldSchema("", "Int", "INTEGER", false, true),
|
|
},
|
|
},
|
|
{
|
|
in: allUnsignedIntegers{},
|
|
want: Schema{
|
|
fieldSchema("", "Uint64", "INTEGER", false, true),
|
|
fieldSchema("", "Uint32", "INTEGER", false, true),
|
|
fieldSchema("", "Uint16", "INTEGER", false, true),
|
|
fieldSchema("", "Uint8", "INTEGER", false, true),
|
|
fieldSchema("", "Uintptr", "INTEGER", false, true),
|
|
fieldSchema("", "Uint", "INTEGER", false, true),
|
|
},
|
|
},
|
|
{
|
|
in: allFloat{},
|
|
want: Schema{
|
|
fieldSchema("", "Float64", "FLOAT", false, true),
|
|
fieldSchema("", "Float32", "FLOAT", false, true),
|
|
},
|
|
},
|
|
{
|
|
in: allBoolean{},
|
|
want: Schema{
|
|
fieldSchema("", "Bool", "BOOLEAN", false, true),
|
|
},
|
|
},
|
|
{
|
|
in: allTime{},
|
|
want: Schema{
|
|
fieldSchema("", "Time", "TIMESTAMP", false, true),
|
|
},
|
|
},
|
|
{
|
|
in: allStrings{},
|
|
want: Schema{
|
|
fieldSchema("", "String", "STRING", false, true),
|
|
fieldSchema("", "ByteSlice", "STRING", false, true),
|
|
},
|
|
},
|
|
}
|
|
for i, tc := range testCases {
|
|
got, err := InferSchema(tc.in)
|
|
if err != nil {
|
|
t.Fatalf("%d: error inferring TableSchema: %v", i, err)
|
|
}
|
|
if !reflect.DeepEqual(got, tc.want) {
|
|
t.Errorf("%d: inferring TableSchema: got:\n%#v\nwant:\n%#v", i, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
type containsNested struct {
|
|
hidden string
|
|
NotNested int
|
|
Nested struct {
|
|
Inside int
|
|
}
|
|
}
|
|
|
|
type containsDoubleNested struct {
|
|
NotNested int
|
|
Nested struct {
|
|
InsideNested struct {
|
|
Inside int
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNestedInference(t *testing.T) {
|
|
testCases := []struct {
|
|
in interface{}
|
|
want Schema
|
|
}{
|
|
{
|
|
in: containsNested{},
|
|
want: Schema{
|
|
fieldSchema("", "NotNested", "INTEGER", false, true),
|
|
&FieldSchema{
|
|
Name: "Nested",
|
|
Required: true,
|
|
Type: "RECORD",
|
|
Schema: []*FieldSchema{
|
|
{
|
|
Name: "Inside",
|
|
Type: "INTEGER",
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
in: containsDoubleNested{},
|
|
want: Schema{
|
|
fieldSchema("", "NotNested", "INTEGER", false, true),
|
|
&FieldSchema{
|
|
Name: "Nested",
|
|
Required: true,
|
|
Type: "RECORD",
|
|
Schema: []*FieldSchema{
|
|
{
|
|
Name: "InsideNested",
|
|
Required: true,
|
|
Type: "RECORD",
|
|
Schema: []*FieldSchema{
|
|
{
|
|
Name: "Inside",
|
|
Type: "INTEGER",
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for i, tc := range testCases {
|
|
got, err := InferSchema(tc.in)
|
|
if err != nil {
|
|
t.Fatalf("%d: error inferring TableSchema: %v", i, err)
|
|
}
|
|
if !reflect.DeepEqual(got, tc.want) {
|
|
t.Errorf("%d: inferring TableSchema: got:\n%#v\nwant:\n%#v", i, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
type simpleRepeated struct {
|
|
NotRepeated []byte
|
|
RepeatedByteSlice [][]byte
|
|
Repeated []int
|
|
}
|
|
|
|
type simpleNestedRepeated struct {
|
|
NotRepeated int
|
|
Repeated []struct {
|
|
Inside int
|
|
}
|
|
}
|
|
|
|
func TestRepeatedInference(t *testing.T) {
|
|
testCases := []struct {
|
|
in interface{}
|
|
want Schema
|
|
}{
|
|
{
|
|
in: simpleRepeated{},
|
|
want: Schema{
|
|
fieldSchema("", "NotRepeated", "STRING", false, true),
|
|
fieldSchema("", "RepeatedByteSlice", "STRING", true, false),
|
|
fieldSchema("", "Repeated", "INTEGER", true, false),
|
|
},
|
|
},
|
|
{
|
|
in: simpleNestedRepeated{},
|
|
want: Schema{
|
|
fieldSchema("", "NotRepeated", "INTEGER", false, true),
|
|
&FieldSchema{
|
|
Name: "Repeated",
|
|
Repeated: true,
|
|
Type: "RECORD",
|
|
Schema: []*FieldSchema{
|
|
{
|
|
Name: "Inside",
|
|
Type: "INTEGER",
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for i, tc := range testCases {
|
|
got, err := InferSchema(tc.in)
|
|
if err != nil {
|
|
t.Fatalf("%d: error inferring TableSchema: %v", i, err)
|
|
}
|
|
if !reflect.DeepEqual(got, tc.want) {
|
|
t.Errorf("%d: inferring TableSchema: got:\n%#v\nwant:\n%#v", i, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
type Embedded struct {
|
|
Embedded int
|
|
}
|
|
|
|
type nestedEmbedded struct {
|
|
Embedded
|
|
}
|
|
|
|
func TestSchemaErrors(t *testing.T) {
|
|
testCases := []struct {
|
|
in interface{}
|
|
err error
|
|
}{
|
|
{
|
|
in: []byte{},
|
|
err: errNoStruct,
|
|
},
|
|
{
|
|
in: new(int),
|
|
err: errNoStruct,
|
|
},
|
|
{
|
|
in: new(allStrings),
|
|
err: errNoStruct,
|
|
},
|
|
{
|
|
in: struct{ Complex complex64 }{},
|
|
err: errUnsupportedFieldType,
|
|
},
|
|
{
|
|
in: struct{ Map map[string]int }{},
|
|
err: errUnsupportedFieldType,
|
|
},
|
|
{
|
|
in: struct{ Chan chan bool }{},
|
|
err: errUnsupportedFieldType,
|
|
},
|
|
{
|
|
in: struct{ Ptr *int }{},
|
|
err: errUnsupportedFieldType,
|
|
},
|
|
{
|
|
in: struct{ Interface interface{} }{},
|
|
err: errUnsupportedFieldType,
|
|
},
|
|
{
|
|
in: struct{ MultiDimensional [][]int }{},
|
|
err: errUnsupportedFieldType,
|
|
},
|
|
{
|
|
in: struct{ MultiDimensional [][][]byte }{},
|
|
err: errUnsupportedFieldType,
|
|
},
|
|
{
|
|
in: struct{ ChanSlice []chan bool }{},
|
|
err: errUnsupportedFieldType,
|
|
},
|
|
{
|
|
in: struct{ NestedChan struct{ Chan []chan bool } }{},
|
|
err: errUnsupportedFieldType,
|
|
},
|
|
{
|
|
in: nestedEmbedded{},
|
|
err: errUnsupportedFieldType,
|
|
},
|
|
}
|
|
for i, tc := range testCases {
|
|
want := tc.err
|
|
_, got := InferSchema(tc.in)
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Errorf("%d: inferring TableSchema: got:\n%#v\nwant:\n%#v", i, got, want)
|
|
}
|
|
}
|
|
}
|