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

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

Issue 1230303003: Revert "Refactor current GAE abstraction library to be free of the SDK*" (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: 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
« no previous file with comments | « go/.gitignore ('k') | go/src/infra/gae/libs/gae/brokenfeatures_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 gae
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 RunIfNotBroken when BrokenFeatures
38 // itself 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 // RunIfNotBroken is to be called internally by the fake service on every
85 // publically-facing method. If it returns an error, the fake should return the
86 // error.
87 //
88 // Example:
89 // type MyService struct { BrokenFeatures }
90 // func (ms *MyService) Thingy() error {
91 // if err := ms.RunIfNotBroken(); 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 RunIfNotBroken will keep walking the stack until it finds the
101 // first publically-exported method, which will allow you to put the
102 // RunIfNotBroken call in an internal helper method of your service
103 // implementation.
104 //
105 // Additionaly, RunIfNotBroken allows a very primitive form of overriding; it
106 // walks the stack until it finds the first method which is not called
107 // "RunIfNotBroken". This allows the embedding struct to call into
108 // BrokenFeatures.RunIfNotBroken from another RunIfNotBroken function, and
109 // still have it behave correctly.
110 func (b *BrokenFeatures) RunIfNotBroken(f func() error) error {
111 if b.noBrokenFeatures() {
112 return f()
113 }
114
115 var name string
116 for off := 1; ; off++ { // offset of 1 skips ourselves by default
117 // TODO(riannucci): Profile this to see if it's having an advers e
118 // performance impact on tests.
119 fn, _, _, ok := runtime.Caller(off)
120 if !ok {
121 return ErrBrokenFeaturesBroken
122 }
123 toks := strings.Split(runtime.FuncForPC(fn).Name(), ".")
124 name = toks[len(toks)-1]
125 firstRune, _ := utf8.DecodeRuneInString(name)
126 if !unicode.IsUpper(firstRune) {
127 // unexported method, keep walking till we find the firs t exported
128 // method. Do !IsUpper, since exported is defined by IsU pper and not
129 // !IsLower, and afaik, in unicode-land they're not dire ct opposites.
130 continue
131 }
132 if name == "RunIfNotBroken" {
133 // Allow users to override RunIfNotBroken, keep walking until we see
134 // a function which is named differently than RunIfNotBr oken.
135 continue
136 }
137 break
138 }
139
140 b.lock.Lock()
141 err, ok := b.broken[name]
142 defer b.lock.Unlock()
143
144 if ok {
145 if err != nil {
146 return err
147 }
148 if b.DefaultError != nil {
149 return b.DefaultError
150 }
151 return fmt.Errorf("feature %q is broken", name)
152 }
153
154 return f()
155 }
156
157 func (b *BrokenFeatures) noBrokenFeatures() bool {
158 b.lock.Lock()
159 defer b.lock.Unlock()
160 return len(b.broken) == 0
161 }
OLDNEW
« no previous file with comments | « go/.gitignore ('k') | go/src/infra/gae/libs/gae/brokenfeatures_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698