1/* 2Copyright 2014 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package cache 18 19import ( 20 "reflect" 21 "testing" 22 "time" 23 24 "k8s.io/apimachinery/pkg/util/clock" 25 "k8s.io/apimachinery/pkg/util/sets" 26 "k8s.io/apimachinery/pkg/util/wait" 27) 28 29func TestTTLExpirationBasic(t *testing.T) { 30 testObj := testStoreObject{id: "foo", val: "bar"} 31 deleteChan := make(chan string, 1) 32 ttlStore := NewFakeExpirationStore( 33 testStoreKeyFunc, deleteChan, 34 &FakeExpirationPolicy{ 35 NeverExpire: sets.NewString(), 36 RetrieveKeyFunc: func(obj interface{}) (string, error) { 37 return obj.(*TimestampedEntry).Obj.(testStoreObject).id, nil 38 }, 39 }, 40 clock.RealClock{}, 41 ) 42 err := ttlStore.Add(testObj) 43 if err != nil { 44 t.Errorf("Unable to add obj %#v", testObj) 45 } 46 item, exists, err := ttlStore.Get(testObj) 47 if err != nil { 48 t.Errorf("Failed to get from store, %v", err) 49 } 50 if exists || item != nil { 51 t.Errorf("Got unexpected item %#v", item) 52 } 53 key, _ := testStoreKeyFunc(testObj) 54 select { 55 case delKey := <-deleteChan: 56 if delKey != key { 57 t.Errorf("Unexpected delete for key %s", key) 58 } 59 case <-time.After(wait.ForeverTestTimeout): 60 t.Errorf("Unexpected timeout waiting on delete") 61 } 62 close(deleteChan) 63} 64 65func TestReAddExpiredItem(t *testing.T) { 66 deleteChan := make(chan string, 1) 67 exp := &FakeExpirationPolicy{ 68 NeverExpire: sets.NewString(), 69 RetrieveKeyFunc: func(obj interface{}) (string, error) { 70 return obj.(*TimestampedEntry).Obj.(testStoreObject).id, nil 71 }, 72 } 73 ttlStore := NewFakeExpirationStore( 74 testStoreKeyFunc, deleteChan, exp, clock.RealClock{}) 75 testKey := "foo" 76 testObj := testStoreObject{id: testKey, val: "bar"} 77 err := ttlStore.Add(testObj) 78 if err != nil { 79 t.Errorf("Unable to add obj %#v", testObj) 80 } 81 82 // This get will expire the item. 83 item, exists, err := ttlStore.Get(testObj) 84 if err != nil { 85 t.Errorf("Failed to get from store, %v", err) 86 } 87 if exists || item != nil { 88 t.Errorf("Got unexpected item %#v", item) 89 } 90 91 key, _ := testStoreKeyFunc(testObj) 92 differentValue := "different_bar" 93 err = ttlStore.Add( 94 testStoreObject{id: testKey, val: differentValue}) 95 if err != nil { 96 t.Errorf("Failed to add second value") 97 } 98 99 select { 100 case delKey := <-deleteChan: 101 if delKey != key { 102 t.Errorf("Unexpected delete for key %s", key) 103 } 104 case <-time.After(wait.ForeverTestTimeout): 105 t.Errorf("Unexpected timeout waiting on delete") 106 } 107 exp.NeverExpire = sets.NewString(testKey) 108 item, exists, err = ttlStore.GetByKey(testKey) 109 if err != nil { 110 t.Errorf("Failed to get from store, %v", err) 111 } 112 if !exists || item == nil || item.(testStoreObject).val != differentValue { 113 t.Errorf("Got unexpected item %#v", item) 114 } 115 close(deleteChan) 116} 117 118func TestTTLList(t *testing.T) { 119 testObjs := []testStoreObject{ 120 {id: "foo", val: "bar"}, 121 {id: "foo1", val: "bar1"}, 122 {id: "foo2", val: "bar2"}, 123 } 124 expireKeys := sets.NewString(testObjs[0].id, testObjs[2].id) 125 deleteChan := make(chan string, len(testObjs)) 126 defer close(deleteChan) 127 128 ttlStore := NewFakeExpirationStore( 129 testStoreKeyFunc, deleteChan, 130 &FakeExpirationPolicy{ 131 NeverExpire: sets.NewString(testObjs[1].id), 132 RetrieveKeyFunc: func(obj interface{}) (string, error) { 133 return obj.(*TimestampedEntry).Obj.(testStoreObject).id, nil 134 }, 135 }, 136 clock.RealClock{}, 137 ) 138 for _, obj := range testObjs { 139 err := ttlStore.Add(obj) 140 if err != nil { 141 t.Errorf("Unable to add obj %#v", obj) 142 } 143 } 144 listObjs := ttlStore.List() 145 if len(listObjs) != 1 || !reflect.DeepEqual(listObjs[0], testObjs[1]) { 146 t.Errorf("List returned unexpected results %#v", listObjs) 147 } 148 149 // Make sure all our deletes come through in an acceptable rate (1/100ms) 150 for expireKeys.Len() != 0 { 151 select { 152 case delKey := <-deleteChan: 153 if !expireKeys.Has(delKey) { 154 t.Errorf("Unexpected delete for key %s", delKey) 155 } 156 expireKeys.Delete(delKey) 157 case <-time.After(wait.ForeverTestTimeout): 158 t.Errorf("Unexpected timeout waiting on delete") 159 return 160 } 161 } 162} 163 164func TestTTLPolicy(t *testing.T) { 165 fakeTime := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) 166 ttl := 30 * time.Second 167 exactlyOnTTL := fakeTime.Add(-ttl) 168 expiredTime := fakeTime.Add(-(ttl + 1)) 169 170 policy := TTLPolicy{ttl, clock.NewFakeClock(fakeTime)} 171 item := testStoreObject{id: "foo", val: "bar"} 172 itemkey, _ := testStoreKeyFunc(item) 173 fakeTimestampedEntry := &TimestampedEntry{Obj: item, Timestamp: exactlyOnTTL, key: itemkey} 174 if policy.IsExpired(fakeTimestampedEntry) { 175 t.Errorf("TTL cache should not expire entries exactly on ttl") 176 } 177 fakeTimestampedEntry.Timestamp = fakeTime 178 if policy.IsExpired(fakeTimestampedEntry) { 179 t.Errorf("TTL Cache should not expire entries before ttl") 180 } 181 fakeTimestampedEntry.Timestamp = expiredTime 182 if !policy.IsExpired(fakeTimestampedEntry) { 183 t.Errorf("TTL Cache should expire entries older than ttl") 184 } 185 for _, ttl = range []time.Duration{0, -1} { 186 policy.TTL = ttl 187 if policy.IsExpired(fakeTimestampedEntry) { 188 t.Errorf("TTL policy should only expire entries when initialized with a ttl > 0") 189 } 190 } 191} 192