blob: 192d5fdead32dee356d94e07402e90592e818734 [file] [log] [blame]
// Copyright 2015 The LUCI Authors.
//
// 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 coordinator
import (
"testing"
"time"
"github.com/luci/luci-go/common/clock/testclock"
"github.com/luci/luci-go/common/errors"
"github.com/luci/luci-go/logdog/common/storage/caching"
"github.com/luci/gae/filter/featureBreaker"
"github.com/luci/gae/impl/memory"
"github.com/luci/gae/service/memcache"
"golang.org/x/net/context"
. "github.com/smartystreets/goconvey/convey"
)
func testStorageCache(t *testing.T, compress bool) {
t.Parallel()
cloneItemsWithData := func(src []*caching.Item, data []byte) []*caching.Item {
dst := make([]*caching.Item, len(src))
for i, itm := range src {
clone := *itm
clone.Data = data
dst[i] = &clone
}
return dst
}
Convey(`Testing storage cache in a testing envrionment`, t, func() {
c := memory.Use(context.Background())
c, tc := testclock.UseTime(c, testclock.TestTimeLocal)
c, fb := featureBreaker.FilterMC(c, nil)
var cache StorageCache
if compress {
cache.compressionThreshold = 1
}
items := []*caching.Item{
{Schema: "test", Type: "type", Key: "foo", Data: []byte("foo")},
{Schema: "test", Type: "type", Key: "bar", Data: []byte("bar")},
{Schema: "test", Type: "othertype", Key: "foo", Data: []byte("foo2")},
{Schema: "otherschema", Type: "othertype", Key: "foo", Data: []byte("foo3")},
}
Convey(`Can load those items into cache`, func() {
cache.Put(c, time.Minute, items...)
stats, err := memcache.Stats(c)
So(err, ShouldBeNil)
So(stats.Items, ShouldEqual, 4)
Convey(`And retrieve those items from cache.`, func() {
oit := cloneItemsWithData(items, []byte("junk"))
cache.Get(c, oit...)
So(oit, ShouldResemble, items)
})
Convey(`Returns nil data if memcache.GetMulti is broken.`, func() {
fb.BreakFeatures(errors.New("test error"), "GetMulti")
cache.Get(c, items...)
So(items, ShouldResemble, cloneItemsWithData(items, nil))
})
})
Convey(`Does not load items into cache if memcache.SetMulti is broken.`, func() {
fb.BreakFeatures(errors.New("test error"), "SetMulti")
cache.Put(c, time.Minute, items...)
cache.Get(c, items...)
So(items, ShouldResemble, cloneItemsWithData(items, nil))
})
Convey(`Get on missing item returns nil data.`, func() {
cache.Get(c, items...)
So(items, ShouldResemble, cloneItemsWithData(items, nil))
})
Convey(`Will replace existing item value.`, func() {
cache.Put(c, time.Minute, items...)
other := cloneItemsWithData(items, []byte("ohaithere"))
cache.Put(c, time.Minute, other...)
cache.Get(c, items...)
So(items, ShouldResemble, other)
})
Convey(`Applies expiration (or lack thereof).`, func() {
cache.Put(c, time.Minute, items[0])
cache.Put(c, 0, items[1])
// items[0]
itm, err := memcache.GetKey(c, cache.mkCacheKey(items[0]))
So(err, ShouldBeNil)
So(itm.Key(), ShouldEqual, cache.mkCacheKey(items[0]))
So(len(itm.Value()), ShouldNotEqual, 0)
tc.Add(time.Minute + 1) // Expires items[0].
_, err = memcache.GetKey(c, cache.mkCacheKey(items[0]))
So(err, ShouldEqual, memcache.ErrCacheMiss)
// items[1]
itm, err = memcache.GetKey(c, cache.mkCacheKey(items[1]))
So(err, ShouldBeNil)
So(itm.Key(), ShouldEqual, cache.mkCacheKey(items[1]))
So(len(itm.Value()), ShouldNotEqual, 0)
})
})
}
func TestStorageCacheWithoutCompression(t *testing.T) {
testStorageCache(t, false)
}
func TestStorageCacheWithCompression(t *testing.T) {
testStorageCache(t, true)
}