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

Side by Side Diff: go/src/infra/gae/libs/wrapper/brokenfeatures.go

Issue 1240573002: Reland: Refactor current GAE abstraction library to be free of the SDK* (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: expand coverage range to fit 32bit test expectations Created 5 years, 5 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package wrapper
6
7 import (
8 "errors"
9 "fmt"
10 "runtime"
11 "strings"
12 "sync"
13 "unicode"
14 "unicode/utf8"
15 )
16
17 // FeatureBreaker allows a fake implementation to set and unset broken features.
18 // A feature is the Name of some method on the fake. So if you had:
19 // var fake interface{ FeatureBreaker, MCSingleReadWriter } = ...
20 //
21 // you could do:
22 // fake.BreakFeatures(memcache.ErrServerError, "Add", "Set")
23 //
24 // and then
25 // fake.Add(...) and fake.Set(...)
26 //
27 // would return the error.
28 //
29 // You may also pass nil as the error for BreakFeatures, and the fake will
30 // provide some suitable (but generic) error for those features (like a
31 // BAD_REQUEST or something like that).
32 type FeatureBreaker interface {
33 BreakFeatures(err error, feature ...string)
34 UnbreakFeatures(feature ...string)
35 }
36
37 // ErrBrokenFeaturesBroken is returned from IsBroken when BrokenFeatures itself
38 // isn't working correctly.
39 var ErrBrokenFeaturesBroken = errors.New("brokenFeatures: Unable to retrieve cal ler information")
40
41 // BrokenFeatures implements the FeatureBreaker interface, and is suitable for
42 // embedding within a fake service.
43 type BrokenFeatures struct {
44 lock sync.Mutex
45
46 broken map[string]error
47
48 // DefaultError is the default error to return when you call
49 // BreakFeatures(nil, ...). If this is unset and the user calls BreakFea tures
50 // with nil, BrokenFeatures will return a generic error.
51 DefaultError error
52 }
53
54 // BreakFeatures allows you to specify an MCSingleReadWriter function name
55 // to cause it to return memcache.ErrServerError. e.g.
56 //
57 // m.SetBrokenFeatures("Add")
58 //
59 // would return memcache.ErrServerError. You can reverse this by calling
60 // UnbreakFeatures("Add").
61 func (b *BrokenFeatures) BreakFeatures(err error, feature ...string) {
62 b.lock.Lock()
63 defer b.lock.Unlock()
64 if b.broken == nil {
65 b.broken = map[string]error{}
66 }
67
68 for _, f := range feature {
69 b.broken[f] = err
70 }
71 }
72
73 // UnbreakFeatures is the inverse of BreakFeatures, and will return the named
74 // features back to their original functionality.
75 func (b *BrokenFeatures) UnbreakFeatures(feature ...string) {
76 b.lock.Lock()
77 defer b.lock.Unlock()
78
79 for _, f := range feature {
80 delete(b.broken, f)
81 }
82 }
83
84 // IsBroken is to be called internally by the fake service on every
85 // publically-facing method. If it returns an error, the fake should return
86 // the error.
87 //
88 // Example:
89 // type MyService struct { BrokenFeatures }
90 // func (ms *MyService) Thingy() error {
91 // if err := ms.IsBroken(); err != nil {
92 // return err
93 // }
94 // ...
95 // }
96 //
97 // You can now do ms.SetBrokenFeatures("Thingy"), and Thingy will return an
98 // error.
99 //
100 // Note that IsBroken will keep walking the stack until it finds the first
101 // publically-exported method, which will allow you to put the IsBroken call
102 // in an internal helper method of your service implementation.
103 //
104 // Additionaly, IsBroken allows a very primitive form of overriding; it walks
105 // the stack until it finds the first method which is not called "IsBroken".
106 // This allows the embedding struct to call into BrokenFeatures.IsBroken from
107 // another IsBroken function, and still have it behave correctly.
108 func (b *BrokenFeatures) IsBroken() error {
109 if b.noBrokenFeatures() {
110 return nil
111 }
112
113 var name string
114 for off := 1; ; off++ { // offset of 1 skips ourselves by default
115 // TODO(riannucci): Profile this to see if it's having an advers e
116 // performance impact ont tests.
117 fn, _, _, ok := runtime.Caller(off)
118 if !ok {
119 return ErrBrokenFeaturesBroken
120 }
121 toks := strings.Split(runtime.FuncForPC(fn).Name(), ".")
122 name = toks[len(toks)-1]
123 firstRune, _ := utf8.DecodeRuneInString(name)
124 if !unicode.IsUpper(firstRune) {
125 // unexported method, keep walking till we find the firs t exported
126 // method. Do !IsUpper, since exported is defined by IsU pper and not
127 // !IsLower, and afaik, in unicode-land they're not dire ct opposites.
128 continue
129 }
130 if name == "IsBroken" {
131 // Allow users to override IsBroken, keep walking until we see a function
132 // which is named differently than IsBroken.
133 continue
134 }
135 break
136 }
137
138 b.lock.Lock()
139 defer b.lock.Unlock()
140 if err, ok := b.broken[name]; ok {
141 if err != nil {
142 return err
143 }
144 if b.DefaultError != nil {
145 return b.DefaultError
146 }
147 return fmt.Errorf("feature %q is broken", name)
148 }
149
150 return nil
151 }
152
153 func (b *BrokenFeatures) noBrokenFeatures() bool {
154 b.lock.Lock()
155 defer b.lock.Unlock()
156 return len(b.broken) == 0
157 }
OLDNEW
« no previous file with comments | « go/src/infra/gae/libs/meta/eg_test.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