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

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: fix coverage numbers 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
« no previous file with comments | « go/src/infra/gae/libs/meta/eg.go ('k') | go/src/infra/gae/libs/wrapper/brokenfeatures_test.go » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..4e4524b203bf57efc8e3af5150761512ce4b1282
--- /dev/null
+++ b/go/src/infra/gae/libs/wrapper/brokenfeatures.go
@@ -0,0 +1,157 @@
+// 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"
+ "fmt"
+ "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
+
+ // DefaultError is the default error to return when you call
+ // BreakFeatures(nil, ...). If this is unset and the user calls BreakFeatures
+ // with nil, BrokenFeatures will return a generic error.
+ DefaultError 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()
+ if b.broken == nil {
+ b.broken = map[string]error{}
+ }
+
+ 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()
+
+ 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 {
+ if b.noBrokenFeatures() {
+ return nil
+ }
+
+ var name string
+ for off := 1; ; off++ { // offset of 1 skips ourselves by default
+ // 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.
+ continue
+ }
+ if name == "IsBroken" {
+ // Allow users to override IsBroken, keep walking until we see a function
+ // which is named differently than IsBroken.
+ continue
+ }
+ break
+ }
+
+ b.lock.Lock()
+ defer b.lock.Unlock()
+ if err, ok := b.broken[name]; ok {
+ if err != nil {
+ return err
+ }
+ if b.DefaultError != nil {
+ return b.DefaultError
+ }
+ return fmt.Errorf("feature %q is broken", name)
+ }
+
+ return nil
+}
+
+func (b *BrokenFeatures) noBrokenFeatures() bool {
+ b.lock.Lock()
+ defer b.lock.Unlock()
+ return len(b.broken) == 0
+}
« no previous file with comments | « go/src/infra/gae/libs/meta/eg.go ('k') | go/src/infra/gae/libs/wrapper/brokenfeatures_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698