Index: go/src/infra/gae/libs/memlock/memlock_test.go |
diff --git a/go/src/infra/gae/libs/memlock/memlock_test.go b/go/src/infra/gae/libs/memlock/memlock_test.go |
new file mode 100644 |
index 0000000000000000000000000000000000000000..49b892b763715e3036b4af2baa50b1d66c8563df |
--- /dev/null |
+++ b/go/src/infra/gae/libs/memlock/memlock_test.go |
@@ -0,0 +1,100 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package memlock |
+ |
+import ( |
+ "fmt" |
+ "infra/gae/libs/wrapper" |
+ "infra/gae/libs/wrapper/memory" |
+ "testing" |
+ "time" |
+ |
+ . "github.com/smartystreets/goconvey/convey" |
+ "golang.org/x/net/context" |
+ |
+ "appengine/memcache" |
+) |
+ |
+func init() { |
+ delay = time.Millisecond |
+ memcacheLockTime = time.Millisecond * 4 |
+} |
+ |
+func TestSimple(t *testing.T) { |
+ // TODO(riannucci): Mock time.After so that we don't have to delay for real. |
+ |
+ Convey("basic locking", t, func() { |
+ c := memory.Use(context.Background()) |
+ mc := wrapper.GetMC(c).(interface { |
+ wrapper.Testable |
+ wrapper.MCSingleReadWriter |
+ }) |
+ |
+ Convey("fails to acquire when memcache is down", func() { |
+ mc.BreakFeatures(nil, "Add") |
+ err := TryWithLock(c, "testkey", "id", func(check func() bool) error { |
+ // should never reach here |
+ So(false, ShouldBeTrue) |
+ return nil |
+ }) |
+ So(err, ShouldEqual, ErrFailedToLock) |
+ }) |
+ |
+ Convey("returns the inner error", func() { |
+ toRet := fmt.Errorf("sup") |
+ err := TryWithLock(c, "testkey", "id", func(check func() bool) error { |
+ return toRet |
+ }) |
+ So(err, ShouldEqual, toRet) |
+ }) |
+ |
+ Convey("can acquire when empty", func() { |
+ err := TryWithLock(c, "testkey", "id", func(check func() bool) error { |
+ So(check(), ShouldBeTrue) |
+ |
+ Convey("waiting for a while keeps refreshing the lock", func() { |
+ time.Sleep(memcacheLockTime * 8) |
+ So(check(), ShouldBeTrue) |
+ }) |
+ |
+ Convey("but sometimes we might lose it", func() { |
+ Convey("because it was evicted", func() { |
+ mc.Delete(memlockKeyPrefix + "testkey") |
+ time.Sleep(memcacheLockTime) |
+ So(check(), ShouldBeFalse) |
+ }) |
+ |
+ Convey("because it got evicted (but we race)", func() { |
+ mc.Set(&memcache.Item{ |
+ Key: memlockKeyPrefix + "testkey", |
+ Value: []byte(""), |
+ }) |
+ }) |
+ |
+ Convey("or because it was stolen", func() { |
+ mc.Set(&memcache.Item{ |
+ Key: memlockKeyPrefix + "testkey", |
+ Value: []byte("wat"), |
+ }) |
+ time.Sleep(memcacheLockTime) |
+ So(check(), ShouldBeFalse) |
+ }) |
+ |
+ Convey("or because of service issues", func() { |
+ mc.BreakFeatures(nil, "CompareAndSwap") |
+ time.Sleep(memcacheLockTime) |
+ So(check(), ShouldBeFalse) |
+ }) |
+ }) |
+ return nil |
+ }) |
+ So(err, ShouldBeNil) |
+ }) |
+ |
+ Convey("an empty context id is an error", func() { |
+ So(TryWithLock(c, "testkey", "", nil), ShouldEqual, ErrEmptyClientID) |
+ }) |
+ }) |
+} |