package api_test
import (
"math/rand"
"reflect"
"strings"
"testing"
"time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
apitesting "github.com/GoogleCloudPlatform/kubernetes/pkg/api/testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/google/gofuzz"
osapi "github.com/openshift/origin/pkg/api"
_ "github.com/openshift/origin/pkg/api/latest"
//"github.com/openshift/origin/pkg/api/v1beta1"
authorizationapi "github.com/openshift/origin/pkg/authorization/api"
config "github.com/openshift/origin/pkg/config/api"
image "github.com/openshift/origin/pkg/image/api"
template "github.com/openshift/origin/pkg/template/api"
)
func fuzzInternalObject(t *testing.T, forVersion string, item runtime.Object, seed int64) runtime.Object {
f := apitesting.FuzzerFor(t, forVersion, rand.NewSource(seed))
f.Funcs(
// Roles and RoleBindings maps are never nil
func(j *authorizationapi.Policy, c fuzz.Continue) {
j.Roles = make(map[string]authorizationapi.Role)
},
func(j *authorizationapi.PolicyBinding, c fuzz.Continue) {
j.RoleBindings = make(map[string]authorizationapi.RoleBinding)
},
func(j *template.Template, c fuzz.Continue) {
c.Fuzz(&j.ObjectMeta)
c.Fuzz(&j.Parameters)
// TODO: replace with structured type definition
j.Objects = []runtime.Object{}
},
func(j *image.Image, c fuzz.Continue) {
c.Fuzz(&j.ObjectMeta)
c.Fuzz(&j.DockerImageMetadata)
j.DockerImageMetadata.APIVersion = ""
j.DockerImageMetadata.Kind = ""
j.DockerImageMetadataVersion = []string{"pre012", "1.0"}[c.Rand.Intn(2)]
j.DockerImageReference = c.RandString()
},
func(j *runtime.EmbeddedObject, c fuzz.Continue) {
// runtime.EmbeddedObject causes a panic inside of fuzz because runtime.Object isn't handled.
},
func(j *config.Config, c fuzz.Continue) {
c.Fuzz(&j.ListMeta)
// TODO: replace with structured type definition
j.Items = []runtime.Object{}
},
func(t *time.Time, c fuzz.Continue) {
// This is necessary because the standard fuzzed time.Time object is
// completely nil, but when JSON unmarshals dates it fills in the
// unexported loc field with the time.UTC object, resulting in
// reflect.DeepEqual returning false in the round trip tests. We solve it
// by using a date that will be identical to the one JSON unmarshals.
*t = time.Date(2000, 1, 1, 1, 1, 1, 0, time.UTC)
},
func(u64 *uint64, c fuzz.Continue) {
// TODO: uint64's are NOT handled right.
*u64 = c.RandUint64() >> 8
},
)
f.Fuzz(item)
j, err := meta.TypeAccessor(item)
if err != nil {
t.Fatalf("Unexpected error %v for %#v", err, item)
}
j.SetKind("")
j.SetAPIVersion("")
return item
}
func roundTrip(t *testing.T, codec runtime.Codec, item runtime.Object) {
name := reflect.TypeOf(item).Elem().Name()
data, err := codec.Encode(item)
if err != nil {
t.Errorf("%v: %v (%#v)", name, err, item)
return
}
obj2, err := codec.Decode(data)
if err != nil {
t.Errorf("0: %v: %v\nCodec: %v\nData: %s\nSource: %#v", name, err, codec, string(data), item)
return
}
if !api.Semantic.DeepEqual(item, obj2) {
t.Errorf("1: %v: diff: %v\nCodec: %v\nData: %s\nSource: %#v\nFinal: %#v", name, util.ObjectGoPrintDiff(item, obj2), codec, string(data), item, obj2)
return
}
obj3 := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object)
err = codec.DecodeInto(data, obj3)
if err != nil {
t.Errorf("2: %v: %v", name, err)
return
}
if !api.Semantic.DeepEqual(item, obj3) {
t.Errorf("3: %v: diff: %v\nCodec: %v", name, util.ObjectDiff(item, obj3), codec)
return
}
}
var skipStandardVersions = map[string][]string{
"DockerImage": {"pre012", "1.0"},
}
const fuzzIters = 20
// For debugging problems
func TestSpecificKind(t *testing.T) {
api.Scheme.Log(t)
defer api.Scheme.Log(nil)
kind := "Role"
item, err := api.Scheme.New("", kind)
if err != nil {
t.Errorf("Couldn't make a %v? %v", kind, err)
return
}
seed := int64(2703387474910584091) //rand.Int63()
fuzzInternalObject(t, "", item, seed)
roundTrip(t, osapi.Codec, item)
}
func TestTypes(t *testing.T) {
for kind, reflectType := range api.Scheme.KnownTypes("") {
if !strings.Contains(reflectType.PkgPath(), "/origin/") {
continue
}
t.Logf("About to test %v", reflectType)
// Try a few times, since runTest uses random values.
for i := 0; i < fuzzIters; i++ {
item, err := api.Scheme.New("", kind)
if err != nil {
t.Errorf("Couldn't make a %v? %v", kind, err)
continue
}
if _, err := meta.TypeAccessor(item); err != nil {
t.Fatalf("%q is not a TypeMeta and cannot be tested - add it to nonRoundTrippableTypes: %v", kind, err)
}
seed := rand.Int63()
if versions, ok := skipStandardVersions[kind]; ok {
for _, v := range versions {
fuzzInternalObject(t, "", item, seed)
roundTrip(t, runtime.CodecFor(api.Scheme, v), item)
}
continue
}
fuzzInternalObject(t, "", item, seed)
roundTrip(t, osapi.Codec, item)
//fuzzInternalObject(t, "", item, seed)
//roundTrip(t, v1beta1.Codec, item)
}
}
}