delay_test.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. // Copyright 2011 Google Inc. All rights reserved.
  2. // Use of this source code is governed by the Apache 2.0
  3. // license that can be found in the LICENSE file.
  4. package delay
  5. import (
  6. "bytes"
  7. "encoding/gob"
  8. "errors"
  9. "fmt"
  10. "net/http"
  11. "net/http/httptest"
  12. "reflect"
  13. "testing"
  14. "github.com/golang/protobuf/proto"
  15. "golang.org/x/net/context"
  16. "google.golang.org/appengine/internal"
  17. "google.golang.org/appengine/taskqueue"
  18. )
  19. type CustomType struct {
  20. N int
  21. }
  22. type CustomInterface interface {
  23. N() int
  24. }
  25. type CustomImpl int
  26. func (c CustomImpl) N() int { return int(c) }
  27. // CustomImpl needs to be registered with gob.
  28. func init() {
  29. gob.Register(CustomImpl(0))
  30. }
  31. var (
  32. invalidFunc = Func("invalid", func() {})
  33. regFuncRuns = 0
  34. regFuncMsg = ""
  35. regFunc = Func("reg", func(c context.Context, arg string) {
  36. regFuncRuns++
  37. regFuncMsg = arg
  38. })
  39. custFuncTally = 0
  40. custFunc = Func("cust", func(c context.Context, ct *CustomType, ci CustomInterface) {
  41. a, b := 2, 3
  42. if ct != nil {
  43. a = ct.N
  44. }
  45. if ci != nil {
  46. b = ci.N()
  47. }
  48. custFuncTally += a + b
  49. })
  50. anotherCustFunc = Func("cust2", func(c context.Context, n int, ct *CustomType, ci CustomInterface) {
  51. })
  52. varFuncMsg = ""
  53. varFunc = Func("variadic", func(c context.Context, format string, args ...int) {
  54. // convert []int to []interface{} for fmt.Sprintf.
  55. as := make([]interface{}, len(args))
  56. for i, a := range args {
  57. as[i] = a
  58. }
  59. varFuncMsg = fmt.Sprintf(format, as...)
  60. })
  61. errFuncRuns = 0
  62. errFuncErr = errors.New("error!")
  63. errFunc = Func("err", func(c context.Context) error {
  64. errFuncRuns++
  65. if errFuncRuns == 1 {
  66. return nil
  67. }
  68. return errFuncErr
  69. })
  70. )
  71. type fakeContext struct {
  72. ctx context.Context
  73. logging [][]interface{}
  74. }
  75. func newFakeContext() *fakeContext {
  76. f := new(fakeContext)
  77. f.ctx = internal.WithCallOverride(context.Background(), f.call)
  78. f.ctx = internal.WithLogOverride(f.ctx, f.logf)
  79. return f
  80. }
  81. func (f *fakeContext) call(ctx context.Context, service, method string, in, out proto.Message) error {
  82. panic("should never be called")
  83. }
  84. var logLevels = map[int64]string{1: "INFO", 3: "ERROR"}
  85. func (f *fakeContext) logf(level int64, format string, args ...interface{}) {
  86. f.logging = append(f.logging, append([]interface{}{logLevels[level], format}, args...))
  87. }
  88. func TestInvalidFunction(t *testing.T) {
  89. c := newFakeContext()
  90. if got, want := invalidFunc.Call(c.ctx), fmt.Errorf("delay: func is invalid: %s", errFirstArg); got.Error() != want.Error() {
  91. t.Errorf("Incorrect error: got %q, want %q", got, want)
  92. }
  93. }
  94. func TestVariadicFunctionArguments(t *testing.T) {
  95. // Check the argument type validation for variadic functions.
  96. c := newFakeContext()
  97. calls := 0
  98. taskqueueAdder = func(c context.Context, t *taskqueue.Task, _ string) (*taskqueue.Task, error) {
  99. calls++
  100. return t, nil
  101. }
  102. varFunc.Call(c.ctx, "hi")
  103. varFunc.Call(c.ctx, "%d", 12)
  104. varFunc.Call(c.ctx, "%d %d %d", 3, 1, 4)
  105. if calls != 3 {
  106. t.Errorf("Got %d calls to taskqueueAdder, want 3", calls)
  107. }
  108. if got, want := varFunc.Call(c.ctx, "%d %s", 12, "a string is bad"), errors.New("delay: argument 3 has wrong type: string is not assignable to int"); got.Error() != want.Error() {
  109. t.Errorf("Incorrect error: got %q, want %q", got, want)
  110. }
  111. }
  112. func TestBadArguments(t *testing.T) {
  113. // Try running regFunc with different sets of inappropriate arguments.
  114. c := newFakeContext()
  115. tests := []struct {
  116. args []interface{} // all except context
  117. wantErr string
  118. }{
  119. {
  120. args: nil,
  121. wantErr: "delay: too few arguments to func: 1 < 2",
  122. },
  123. {
  124. args: []interface{}{"lala", 53},
  125. wantErr: "delay: too many arguments to func: 3 > 2",
  126. },
  127. {
  128. args: []interface{}{53},
  129. wantErr: "delay: argument 1 has wrong type: int is not assignable to string",
  130. },
  131. }
  132. for i, tc := range tests {
  133. got := regFunc.Call(c.ctx, tc.args...)
  134. if got.Error() != tc.wantErr {
  135. t.Errorf("Call %v: got %q, want %q", i, got, tc.wantErr)
  136. }
  137. }
  138. }
  139. func TestRunningFunction(t *testing.T) {
  140. c := newFakeContext()
  141. // Fake out the adding of a task.
  142. var task *taskqueue.Task
  143. taskqueueAdder = func(_ context.Context, tk *taskqueue.Task, queue string) (*taskqueue.Task, error) {
  144. if queue != "" {
  145. t.Errorf(`Got queue %q, expected ""`, queue)
  146. }
  147. task = tk
  148. return tk, nil
  149. }
  150. regFuncRuns, regFuncMsg = 0, "" // reset state
  151. const msg = "Why, hello!"
  152. regFunc.Call(c.ctx, msg)
  153. // Simulate the Task Queue service.
  154. req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload))
  155. if err != nil {
  156. t.Fatalf("Failed making http.Request: %v", err)
  157. }
  158. rw := httptest.NewRecorder()
  159. runFunc(c.ctx, rw, req)
  160. if regFuncRuns != 1 {
  161. t.Errorf("regFuncRuns: got %d, want 1", regFuncRuns)
  162. }
  163. if regFuncMsg != msg {
  164. t.Errorf("regFuncMsg: got %q, want %q", regFuncMsg, msg)
  165. }
  166. }
  167. func TestCustomType(t *testing.T) {
  168. c := newFakeContext()
  169. // Fake out the adding of a task.
  170. var task *taskqueue.Task
  171. taskqueueAdder = func(_ context.Context, tk *taskqueue.Task, queue string) (*taskqueue.Task, error) {
  172. if queue != "" {
  173. t.Errorf(`Got queue %q, expected ""`, queue)
  174. }
  175. task = tk
  176. return tk, nil
  177. }
  178. custFuncTally = 0 // reset state
  179. custFunc.Call(c.ctx, &CustomType{N: 11}, CustomImpl(13))
  180. // Simulate the Task Queue service.
  181. req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload))
  182. if err != nil {
  183. t.Fatalf("Failed making http.Request: %v", err)
  184. }
  185. rw := httptest.NewRecorder()
  186. runFunc(c.ctx, rw, req)
  187. if custFuncTally != 24 {
  188. t.Errorf("custFuncTally = %d, want 24", custFuncTally)
  189. }
  190. // Try the same, but with nil values; one is a nil pointer (and thus a non-nil interface value),
  191. // and the other is a nil interface value.
  192. custFuncTally = 0 // reset state
  193. custFunc.Call(c.ctx, (*CustomType)(nil), nil)
  194. // Simulate the Task Queue service.
  195. req, err = http.NewRequest("POST", path, bytes.NewBuffer(task.Payload))
  196. if err != nil {
  197. t.Fatalf("Failed making http.Request: %v", err)
  198. }
  199. rw = httptest.NewRecorder()
  200. runFunc(c.ctx, rw, req)
  201. if custFuncTally != 5 {
  202. t.Errorf("custFuncTally = %d, want 5", custFuncTally)
  203. }
  204. }
  205. func TestRunningVariadic(t *testing.T) {
  206. c := newFakeContext()
  207. // Fake out the adding of a task.
  208. var task *taskqueue.Task
  209. taskqueueAdder = func(_ context.Context, tk *taskqueue.Task, queue string) (*taskqueue.Task, error) {
  210. if queue != "" {
  211. t.Errorf(`Got queue %q, expected ""`, queue)
  212. }
  213. task = tk
  214. return tk, nil
  215. }
  216. varFuncMsg = "" // reset state
  217. varFunc.Call(c.ctx, "Amiga %d has %d KB RAM", 500, 512)
  218. // Simulate the Task Queue service.
  219. req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload))
  220. if err != nil {
  221. t.Fatalf("Failed making http.Request: %v", err)
  222. }
  223. rw := httptest.NewRecorder()
  224. runFunc(c.ctx, rw, req)
  225. const expected = "Amiga 500 has 512 KB RAM"
  226. if varFuncMsg != expected {
  227. t.Errorf("varFuncMsg = %q, want %q", varFuncMsg, expected)
  228. }
  229. }
  230. func TestErrorFunction(t *testing.T) {
  231. c := newFakeContext()
  232. // Fake out the adding of a task.
  233. var task *taskqueue.Task
  234. taskqueueAdder = func(_ context.Context, tk *taskqueue.Task, queue string) (*taskqueue.Task, error) {
  235. if queue != "" {
  236. t.Errorf(`Got queue %q, expected ""`, queue)
  237. }
  238. task = tk
  239. return tk, nil
  240. }
  241. errFunc.Call(c.ctx)
  242. // Simulate the Task Queue service.
  243. // The first call should succeed; the second call should fail.
  244. {
  245. req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload))
  246. if err != nil {
  247. t.Fatalf("Failed making http.Request: %v", err)
  248. }
  249. rw := httptest.NewRecorder()
  250. runFunc(c.ctx, rw, req)
  251. }
  252. {
  253. req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload))
  254. if err != nil {
  255. t.Fatalf("Failed making http.Request: %v", err)
  256. }
  257. rw := httptest.NewRecorder()
  258. runFunc(c.ctx, rw, req)
  259. if rw.Code != http.StatusInternalServerError {
  260. t.Errorf("Got status code %d, want %d", rw.Code, http.StatusInternalServerError)
  261. }
  262. wantLogging := [][]interface{}{
  263. {"ERROR", "delay: func failed (will retry): %v", errFuncErr},
  264. }
  265. if !reflect.DeepEqual(c.logging, wantLogging) {
  266. t.Errorf("Incorrect logging: got %+v, want %+v", c.logging, wantLogging)
  267. }
  268. }
  269. }