From 7cf39c47c7b8345c01ff9e98c19c28c39de229ef Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Wed, 8 Apr 2015 13:43:51 -0700 Subject: [PATCH] add a test; coverage up to 84.7% --- pkg/controller/framework/controller_test.go | 102 +++++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/pkg/controller/framework/controller_test.go b/pkg/controller/framework/controller_test.go index 1f32aeb3c6d80..6fcfabaaa4748 100644 --- a/pkg/controller/framework/controller_test.go +++ b/pkg/controller/framework/controller_test.go @@ -18,15 +18,18 @@ package framework_test import ( "fmt" + "math/rand" "sync" + "testing" "time" - // "testing" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache" "github.com/GoogleCloudPlatform/kubernetes/pkg/controller/framework" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + + "github.com/google/gofuzz" ) func Example() { @@ -170,3 +173,100 @@ func ExampleInformer() { // b-controller // c-framework } + +func TestHammerController(t *testing.T) { + // This test executes a bunch of requests through the fake source and + // controller framework to make sure there's no locking/threading + // errors. + + // source simulates an apiserver object endpoint. + source := framework.NewFakeControllerSource() + + // Let's do threadsafe output to get predictable test results. + outputSetLock := sync.Mutex{} + // map of key to operations done on the key + outputSet := map[string][]string{} + + recordFunc := func(eventType string, obj interface{}) { + key, err := framework.DeletionHandlingMetaNamespaceKeyFunc(obj) + if err != nil { + t.Errorf("something wrong with key: %v", err) + key = "oops something went wrong with the key" + } + + // Record some output when items are deleted. + outputSetLock.Lock() + defer outputSetLock.Unlock() + outputSet[key] = append(outputSet[key], eventType) + } + + // Make a controller that immediately deletes anything added to it, and + // logs anything deleted. + _, controller := framework.NewInformer( + source, + &api.Pod{}, + time.Millisecond*100, + framework.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { recordFunc("add", obj) }, + UpdateFunc: func(obj interface{}) { recordFunc("update", obj) }, + DeleteFunc: func(obj interface{}) { recordFunc("delete", obj) }, + }, + ) + + // Run the controller and run it until we close stop. + stop := make(chan struct{}) + go controller.Run(stop) + + wg := sync.WaitGroup{} + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() + // Let's add a few objects to the source. + currentNames := util.StringSet{} + rs := rand.NewSource(rand.Int63()) + f := fuzz.New().NilChance(.5).NumElements(0, 2).RandSource(rs) + r := rand.New(rs) // Mustn't use r and f concurrently! + for i := 0; i < 750; i++ { + var name string + var isNew bool + if currentNames.Len() == 0 || r.Intn(3) == 1 { + f.Fuzz(&name) + isNew = true + } else { + l := currentNames.List() + name = l[r.Intn(len(l))] + } + + pod := &api.Pod{} + f.Fuzz(pod) + pod.ObjectMeta.Name = name + pod.ObjectMeta.Namespace = "default" + // Add, update, or delete randomly. + // Note that these pods are not valid-- the fake source doesn't + // call validation or anything. + if isNew { + currentNames.Insert(name) + source.Add(pod) + continue + } + switch r.Intn(2) { + case 0: + currentNames.Insert(name) + source.Modify(pod) + case 1: + currentNames.Delete(name) + source.Delete(pod) + } + } + }() + } + wg.Wait() + + // Let's wait for the controller to finish processing the things we just added. + time.Sleep(100 * time.Millisecond) + close(stop) + + outputSetLock.Lock() + t.Logf("got: %#v", outputSet) +}