// Copyright 2018 Intel Corporation. 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 main import ( "flag" "fmt" "testing" "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/rest" v2 "github.com/intel/intel-device-plugins-for-kubernetes/pkg/apis/fpga.intel.com/v2" listers "github.com/intel/intel-device-plugins-for-kubernetes/pkg/client/listers/fpga.intel.com/v2" ) type fakeAfNamespaceLister struct { af *v2.AcceleratorFunction err error } func init() { flag.Set("v", "4") ///Enable debug output } func (nl *fakeAfNamespaceLister) Get(name string) (*v2.AcceleratorFunction, error) { return nl.af, nl.err } func (nl *fakeAfNamespaceLister) List(selector labels.Selector) (ret []*v2.AcceleratorFunction, err error) { return nil, nil } type fakeAfLister struct { af *v2.AcceleratorFunction err error } func (l *fakeAfLister) AcceleratorFunctions(namespace string) listers.AcceleratorFunctionNamespaceLister { return &fakeAfNamespaceLister{ af: l.af, err: l.err, } } func (l *fakeAfLister) List(selector labels.Selector) (ret []*v2.AcceleratorFunction, err error) { return nil, nil } func TestSyncAfHandler(t *testing.T) { tcases := []struct { name string key string afLister *fakeAfLister expectedErr bool }{ { name: "Wrong key format", key: "wrong/key/format/of/object", }, { name: "Known key", key: "default/arria10-nlb0", afLister: &fakeAfLister{ af: &v2.AcceleratorFunction{ ObjectMeta: metav1.ObjectMeta{ Name: "arria10-nlb0", }, Spec: v2.AcceleratorFunctionSpec{ AfuID: "d8424dc4a4a3c413f89e433683f9040b", }, }, }, }, { name: "Unknown key", key: "default/unknown", }, { name: "Unhandled error", key: "default/unknown", afLister: &fakeAfLister{ err: fmt.Errorf("Some fake error"), }, expectedErr: true, }, } for _, tt := range tcases { pm := newPatcherManager() c, err := newController(pm, &rest.Config{}) if err != nil { t.Fatalf("Test case '%s': %+v", tt.name, err) } if tt.afLister != nil { c.afLister = tt.afLister } err = c.syncAfHandler(tt.key) if err != nil && !tt.expectedErr { t.Errorf("Test case '%s': unexpected error: %+v", tt.name, err) } if err == nil && tt.expectedErr { t.Errorf("Test case '%s': expected error, but got success", tt.name) } } } type fakeRegionNamespaceLister struct { region *v2.FpgaRegion err error } func (nl *fakeRegionNamespaceLister) Get(name string) (*v2.FpgaRegion, error) { return nl.region, nl.err } func (nl *fakeRegionNamespaceLister) List(selector labels.Selector) (ret []*v2.FpgaRegion, err error) { return nil, nil } type fakeRegionLister struct { region *v2.FpgaRegion err error } func (l *fakeRegionLister) FpgaRegions(namespace string) listers.FpgaRegionNamespaceLister { return &fakeRegionNamespaceLister{ region: l.region, err: l.err, } } func (l *fakeRegionLister) List(selector labels.Selector) (ret []*v2.FpgaRegion, err error) { return nil, nil } func TestSyncRegionHandler(t *testing.T) { tcases := []struct { name string key string regionLister *fakeRegionLister expectedErr bool }{ { name: "Wrong key format", key: "wrong/key/format/of/object", }, { name: "Known key", key: "default/arria10", regionLister: &fakeRegionLister{ region: &v2.FpgaRegion{ ObjectMeta: metav1.ObjectMeta{ Name: "arria10", }, Spec: v2.FpgaRegionSpec{ InterfaceID: "ce48969398f05f33946d560708be108a", }, }, }, }, { name: "Unknown key", key: "default/unknown", }, { name: "Unhandled error", key: "default/unknown", regionLister: &fakeRegionLister{ err: fmt.Errorf("Some fake error"), }, expectedErr: true, }, } for _, tt := range tcases { pm := newPatcherManager() c, err := newController(pm, &rest.Config{}) if err != nil { t.Fatalf("Test case '%s': %+v", tt.name, err) } if tt.regionLister != nil { c.regionLister = tt.regionLister } err = c.syncRegionHandler(tt.key) if err != nil && !tt.expectedErr { t.Errorf("Test case '%s': unexpected error: %+v", tt.name, err) } if err == nil && tt.expectedErr { t.Errorf("Test case '%s': expected error, but got success", tt.name) } } } type fakeQueue struct { obj *fpgaObjectKey shutdown bool } func (q *fakeQueue) Add(item interface{}) {} func (q *fakeQueue) AddAfter(item interface{}, duration time.Duration) { } func (q *fakeQueue) AddRateLimited(item interface{}) {} func (q *fakeQueue) Done(item interface{}) {} func (q *fakeQueue) Forget(item interface{}) {} func (q *fakeQueue) Get() (item interface{}, shutdown bool) { if q.obj == nil { return nil, q.shutdown } return *q.obj, q.shutdown } func (q *fakeQueue) Len() int { return 0 } func (q *fakeQueue) NumRequeues(item interface{}) int { return 0 } func (q *fakeQueue) ShutDown() {} func (q *fakeQueue) ShuttingDown() bool { return false } func TestProcessNextWorkItem(t *testing.T) { tcases := []struct { name string obj *fpgaObjectKey shutdown bool afLister *fakeAfLister regionLister *fakeRegionLister expectedContinue bool }{ { name: "Receive invalid object", expectedContinue: true, }, { name: "Shutdown queue", shutdown: true, }, { name: "Receive unknown kind of object", obj: &fpgaObjectKey{ kind: "unknown", }, expectedContinue: true, }, { name: "Receive object with AF key", obj: &fpgaObjectKey{ kind: "af", }, expectedContinue: true, }, { name: "Receive object with Region key", obj: &fpgaObjectKey{ kind: "region", }, expectedContinue: true, }, { name: "Receive broken object with AF key", obj: &fpgaObjectKey{ kind: "af", }, afLister: &fakeAfLister{ err: fmt.Errorf("some fake error"), }, expectedContinue: true, }, { name: "Receive broken object with Region key", obj: &fpgaObjectKey{ kind: "region", }, regionLister: &fakeRegionLister{ err: fmt.Errorf("some fake error"), }, expectedContinue: true, }, } for _, tt := range tcases { pm := newPatcherManager() c, err := newController(pm, &rest.Config{}) if err != nil { t.Fatalf("Test case '%s': %+v", tt.name, err) } c.queue = &fakeQueue{ shutdown: tt.shutdown, obj: tt.obj, } if tt.afLister != nil { c.afLister = tt.afLister } if tt.regionLister != nil { c.regionLister = tt.regionLister } toContinue := c.processNextWorkItem() if toContinue != tt.expectedContinue { t.Errorf("Test case '%s': expected toContinue flag %v, but got %v", tt.name, tt.expectedContinue, toContinue) } } } func TestCreateEventhandler(t *testing.T) { funcs := createEventHandler("testkind", &fakeQueue{}) funcs.AddFunc(&v2.FpgaRegion{}) funcs.UpdateFunc(nil, &v2.FpgaRegion{}) funcs.DeleteFunc(&v2.FpgaRegion{}) } func TestRun(t *testing.T) { tcases := []struct { name string expectedErr bool }{ { name: "Fail to wait for AF caches to sync", expectedErr: true, }, } for _, tt := range tcases { pm := newPatcherManager() c, err := newController(pm, &rest.Config{}) if err != nil { t.Fatalf("Test case '%s': %+v", tt.name, err) } close(c.stopCh) err = c.run(0) if err != nil && !tt.expectedErr { t.Errorf("Test case '%s': unexpected error: %+v", tt.name, err) } if err == nil && tt.expectedErr { t.Errorf("Test case '%s': expected error, but got success", tt.name) } } } func TestNewController(t *testing.T) { tcases := []struct { name string configHost string expectedErr bool }{ { name: "wrong config", configHost: "hostname/somepath", expectedErr: true, }, { name: "successful creation", }, } for _, tt := range tcases { config := &rest.Config{ Host: tt.configHost, } pm := newPatcherManager() c, err := newController(pm, config) if err != nil && !tt.expectedErr { t.Errorf("Test case '%s': unexpected error: %+v", tt.name, err) } if err == nil && tt.expectedErr { t.Errorf("Test case '%s': expected error, but got success", tt.name) } if err == nil && c == nil { t.Errorf("Test case '%s': no controller created", tt.name) } } }