Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1050)

Unified Diff: go/src/infra/gae/libs/wrapper/brokenfeatures.go

Issue 1151473003: Better attempt at an appengine wrapper. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: better typecheck Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: go/src/infra/gae/libs/wrapper/brokenfeatures.go
diff --git a/go/src/infra/gae/libs/wrapper/brokenfeatures.go b/go/src/infra/gae/libs/wrapper/brokenfeatures.go
new file mode 100644
index 0000000000000000000000000000000000000000..4b414eb5293977bc63be7441fe80049d2121d78b
--- /dev/null
+++ b/go/src/infra/gae/libs/wrapper/brokenfeatures.go
@@ -0,0 +1,159 @@
+// 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 wrapper
+
+import (
+ "errors"
+ "runtime"
+ "strings"
+ "sync"
+ "unicode"
+ "unicode/utf8"
+)
+
+// FeatureBreaker allows a fake implementation to set and unset broken features.
+// A feature is the Name of some method on the fake. So if you had:
+// var fake interface{ FeatureBreaker, MCSingleReadWriter } = ...
+//
+// you could do:
+// fake.BreakFeatures(memcache.ErrServerError, "Add", "Set")
+//
+// and then
+// fake.Add(...) and fake.Set(...)
+//
+// would return the error.
+//
+// You may also pass nil as the error for BreakFeatures, and the fake will
+// provide some suitable (but generic) error for those features (like a
+// BAD_REQUEST or something like that).
+type FeatureBreaker interface {
+ BreakFeatures(err error, feature ...string)
+ UnbreakFeatures(feature ...string)
+}
+
+// ErrBrokenFeaturesBroken is returned from IsBroken when BrokenFeatures itself
+// isn't working correctly.
+var ErrBrokenFeaturesBroken = errors.New("brokenFeatures: Unable to retrieve caller information")
+
+// BrokenFeatures implements the FeatureBreaker interface, and is suitable for
+// embedding within a fake service.
+type BrokenFeatures struct {
+ lock sync.Mutex
+
+ broken map[string]error
+ dfltErr error
+}
+
+// NewBrokenFeatures creates an embeddable *BrokenFeatures which is set to
+// return err for a given broken feature.
+func NewBrokenFeatures(err error) *BrokenFeatures {
M-A Ruel 2015/05/27 00:27:11 I still think this function is unneeded.
iannucci 2015/05/27 02:40:56 Removed, made dfltErr exported
+ return &BrokenFeatures{broken: map[string]error{}, dfltErr: err}
+}
+
+// initLocked makes sure the map is allocated, in case the user decided to
+// do &BrokenFeatures{} themselves.
+func (b *BrokenFeatures) initLocked() {
+ if b.broken == nil {
+ b.broken = map[string]error{}
+ }
+}
+
+// BreakFeatures allows you to specify an MCSingleReadWriter function name
+// to cause it to return memcache.ErrServerError. e.g.
+//
+// m.SetBrokenFeatures("Add")
+//
+// would return memcache.ErrServerError. You can reverse this by calling
+// UnbreakFeatures("Add").
+func (b *BrokenFeatures) BreakFeatures(err error, feature ...string) {
+ b.lock.Lock()
+ defer b.lock.Unlock()
+ b.initLocked()
M-A Ruel 2015/05/27 00:27:11 Inline initLocked here since it's now the only cal
iannucci 2015/05/27 02:40:56 done
+
+ for _, f := range feature {
+ b.broken[f] = err
+ }
+}
+
+// UnbreakFeatures is the inverse of BreakFeatures, and will return the named
+// features back to their original functionality.
+func (b *BrokenFeatures) UnbreakFeatures(feature ...string) {
+ b.lock.Lock()
+ defer b.lock.Unlock()
+ b.initLocked()
M-A Ruel 2015/05/27 00:27:11 It's not needed, delete(nil, ...) works, e.g. http
iannucci 2015/05/27 02:40:57 cool, done
+
+ for _, f := range feature {
+ delete(b.broken, f)
+ }
+}
+
+// IsBroken is to be called internally by the fake service on every
+// publically-facing method. If it returns an error, the fake should return
+// the error.
+//
+// Example:
+// type MyService struct { *BrokenFeatures }
+// func (ms *MyService) Thingy() error {
+// if err := ms.IsBroken(); err != nil {
+// return err
+// }
+// ...
+// }
+//
+// You can now do ms.SetBrokenFeatures("Thingy"), and Thingy will return an
+// error.
+//
+// Note that IsBroken will keep walking the stack until it finds the first
+// publically-exported method, which will allow you to put the IsBroken call
+// in an internal helper method of your service implementation.
+//
+// Additionaly, IsBroken allows a very primitive form of overriding; it walks
+// the stack until it finds the first method which is not called "IsBroken".
+// This allows the embedding struct to call into BrokenFeatures.IsBroken from
+// another IsBroken function, and still have it behave correctly.
+func (b *BrokenFeatures) IsBroken() error {
+ b.lock.Lock()
+ defer b.lock.Unlock()
+ b.initLocked()
M-A Ruel 2015/05/27 00:27:11 it's not needed here.
iannucci 2015/05/27 02:40:56 done
+
+ if len(b.broken) == 0 {
+ return nil
+ }
+ off := 1 // offset of 1 skips ourselves by default
+
+ for {
M-A Ruel 2015/05/27 00:27:11 for off := 1; ; off++ {
iannucci 2015/05/27 02:40:57 done
+ // TODO(riannucci): Profile this to see if it's having an adverse
+ // performance impact ont tests.
+ fn, _, _, ok := runtime.Caller(off)
+ if !ok {
+ return ErrBrokenFeaturesBroken
+ }
+ toks := strings.Split(runtime.FuncForPC(fn).Name(), ".")
+ name := toks[len(toks)-1]
+ firstRune, _ := utf8.DecodeRuneInString(name)
+ if !unicode.IsUpper(firstRune) {
+ // unexported method, keep walking till we find the first exported
+ // method. Do !IsUpper, since exported is defined by IsUpper and not
+ // !IsLower, and afaik, in unicode-land they're not direct opposites.
+ off++
M-A Ruel 2015/05/27 00:27:11 remove
iannucci 2015/05/27 02:40:56 done
+ continue
+ }
+ if name == "IsBroken" {
+ // Allow users to override IsBroken, keep walking until we see a function
+ // which is named differently than IsBroken.
+ off++
M-A Ruel 2015/05/27 00:27:11 remove
iannucci 2015/05/27 02:40:57 done
+ continue
+ }
+ if err, ok := b.broken[name]; ok {
+ if err != nil {
+ return err
+ }
+ return b.dfltErr
+ }
+ break
+ }
+
+ return nil
+}

Powered by Google App Engine
This is Rietveld 408576698