delay_test.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  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. dupeWhich = 0
  71. dupe1Func = Func("dupe", func(c context.Context) {
  72. if dupeWhich == 0 {
  73. dupeWhich = 1
  74. }
  75. })
  76. dupe2Func = Func("dupe", func(c context.Context) {
  77. if dupeWhich == 0 {
  78. dupeWhich = 2
  79. }
  80. })
  81. )
  82. type fakeContext struct {
  83. ctx context.Context
  84. logging [][]interface{}
  85. }
  86. func newFakeContext() *fakeContext {
  87. f := new(fakeContext)
  88. f.ctx = internal.WithCallOverride(context.Background(), f.call)
  89. f.ctx = internal.WithLogOverride(f.ctx, f.logf)
  90. return f
  91. }
  92. func (f *fakeContext) call(ctx context.Context, service, method string, in, out proto.Message) error {
  93. panic("should never be called")
  94. }
  95. var logLevels = map[int64]string{1: "INFO", 3: "ERROR"}
  96. func (f *fakeContext) logf(level int64, format string, args ...interface{}) {
  97. f.logging = append(f.logging, append([]interface{}{logLevels[level], format}, args...))
  98. }
  99. func TestInvalidFunction(t *testing.T) {
  100. c := newFakeContext()
  101. if got, want := invalidFunc.Call(c.ctx), fmt.Errorf("delay: func is invalid: %s", errFirstArg); got.Error() != want.Error() {
  102. t.Errorf("Incorrect error: got %q, want %q", got, want)
  103. }
  104. }
  105. func TestVariadicFunctionArguments(t *testing.T) {
  106. // Check the argument type validation for variadic functions.
  107. c := newFakeContext()
  108. calls := 0
  109. taskqueueAdder = func(c context.Context, t *taskqueue.Task, _ string) (*taskqueue.Task, error) {
  110. calls++
  111. return t, nil
  112. }
  113. varFunc.Call(c.ctx, "hi")
  114. varFunc.Call(c.ctx, "%d", 12)
  115. varFunc.Call(c.ctx, "%d %d %d", 3, 1, 4)
  116. if calls != 3 {
  117. t.Errorf("Got %d calls to taskqueueAdder, want 3", calls)
  118. }
  119. 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() {
  120. t.Errorf("Incorrect error: got %q, want %q", got, want)
  121. }
  122. }
  123. func TestBadArguments(t *testing.T) {
  124. // Try running regFunc with different sets of inappropriate arguments.
  125. c := newFakeContext()
  126. tests := []struct {
  127. args []interface{} // all except context
  128. wantErr string
  129. }{
  130. {
  131. args: nil,
  132. wantErr: "delay: too few arguments to func: 1 < 2",
  133. },
  134. {
  135. args: []interface{}{"lala", 53},
  136. wantErr: "delay: too many arguments to func: 3 > 2",
  137. },
  138. {
  139. args: []interface{}{53},
  140. wantErr: "delay: argument 1 has wrong type: int is not assignable to string",
  141. },
  142. }
  143. for i, tc := range tests {
  144. got := regFunc.Call(c.ctx, tc.args...)
  145. if got.Error() != tc.wantErr {
  146. t.Errorf("Call %v: got %q, want %q", i, got, tc.wantErr)
  147. }
  148. }
  149. }
  150. func TestRunningFunction(t *testing.T) {
  151. c := newFakeContext()
  152. // Fake out the adding of a task.
  153. var task *taskqueue.Task
  154. taskqueueAdder = func(_ context.Context, tk *taskqueue.Task, queue string) (*taskqueue.Task, error) {
  155. if queue != "" {
  156. t.Errorf(`Got queue %q, expected ""`, queue)
  157. }
  158. task = tk
  159. return tk, nil
  160. }
  161. regFuncRuns, regFuncMsg = 0, "" // reset state
  162. const msg = "Why, hello!"
  163. regFunc.Call(c.ctx, msg)
  164. // Simulate the Task Queue service.
  165. req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload))
  166. if err != nil {
  167. t.Fatalf("Failed making http.Request: %v", err)
  168. }
  169. rw := httptest.NewRecorder()
  170. runFunc(c.ctx, rw, req)
  171. if regFuncRuns != 1 {
  172. t.Errorf("regFuncRuns: got %d, want 1", regFuncRuns)
  173. }
  174. if regFuncMsg != msg {
  175. t.Errorf("regFuncMsg: got %q, want %q", regFuncMsg, msg)
  176. }
  177. }
  178. func TestCustomType(t *testing.T) {
  179. c := newFakeContext()
  180. // Fake out the adding of a task.
  181. var task *taskqueue.Task
  182. taskqueueAdder = func(_ context.Context, tk *taskqueue.Task, queue string) (*taskqueue.Task, error) {
  183. if queue != "" {
  184. t.Errorf(`Got queue %q, expected ""`, queue)
  185. }
  186. task = tk
  187. return tk, nil
  188. }
  189. custFuncTally = 0 // reset state
  190. custFunc.Call(c.ctx, &CustomType{N: 11}, CustomImpl(13))
  191. // Simulate the Task Queue service.
  192. req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload))
  193. if err != nil {
  194. t.Fatalf("Failed making http.Request: %v", err)
  195. }
  196. rw := httptest.NewRecorder()
  197. runFunc(c.ctx, rw, req)
  198. if custFuncTally != 24 {
  199. t.Errorf("custFuncTally = %d, want 24", custFuncTally)
  200. }
  201. // Try the same, but with nil values; one is a nil pointer (and thus a non-nil interface value),
  202. // and the other is a nil interface value.
  203. custFuncTally = 0 // reset state
  204. custFunc.Call(c.ctx, (*CustomType)(nil), nil)
  205. // Simulate the Task Queue service.
  206. req, err = http.NewRequest("POST", path, bytes.NewBuffer(task.Payload))
  207. if err != nil {
  208. t.Fatalf("Failed making http.Request: %v", err)
  209. }
  210. rw = httptest.NewRecorder()
  211. runFunc(c.ctx, rw, req)
  212. if custFuncTally != 5 {
  213. t.Errorf("custFuncTally = %d, want 5", custFuncTally)
  214. }
  215. }
  216. func TestRunningVariadic(t *testing.T) {
  217. c := newFakeContext()
  218. // Fake out the adding of a task.
  219. var task *taskqueue.Task
  220. taskqueueAdder = func(_ context.Context, tk *taskqueue.Task, queue string) (*taskqueue.Task, error) {
  221. if queue != "" {
  222. t.Errorf(`Got queue %q, expected ""`, queue)
  223. }
  224. task = tk
  225. return tk, nil
  226. }
  227. varFuncMsg = "" // reset state
  228. varFunc.Call(c.ctx, "Amiga %d has %d KB RAM", 500, 512)
  229. // Simulate the Task Queue service.
  230. req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload))
  231. if err != nil {
  232. t.Fatalf("Failed making http.Request: %v", err)
  233. }
  234. rw := httptest.NewRecorder()
  235. runFunc(c.ctx, rw, req)
  236. const expected = "Amiga 500 has 512 KB RAM"
  237. if varFuncMsg != expected {
  238. t.Errorf("varFuncMsg = %q, want %q", varFuncMsg, expected)
  239. }
  240. }
  241. func TestErrorFunction(t *testing.T) {
  242. c := newFakeContext()
  243. // Fake out the adding of a task.
  244. var task *taskqueue.Task
  245. taskqueueAdder = func(_ context.Context, tk *taskqueue.Task, queue string) (*taskqueue.Task, error) {
  246. if queue != "" {
  247. t.Errorf(`Got queue %q, expected ""`, queue)
  248. }
  249. task = tk
  250. return tk, nil
  251. }
  252. errFunc.Call(c.ctx)
  253. // Simulate the Task Queue service.
  254. // The first call should succeed; the second call should fail.
  255. {
  256. req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload))
  257. if err != nil {
  258. t.Fatalf("Failed making http.Request: %v", err)
  259. }
  260. rw := httptest.NewRecorder()
  261. runFunc(c.ctx, rw, req)
  262. }
  263. {
  264. req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload))
  265. if err != nil {
  266. t.Fatalf("Failed making http.Request: %v", err)
  267. }
  268. rw := httptest.NewRecorder()
  269. runFunc(c.ctx, rw, req)
  270. if rw.Code != http.StatusInternalServerError {
  271. t.Errorf("Got status code %d, want %d", rw.Code, http.StatusInternalServerError)
  272. }
  273. wantLogging := [][]interface{}{
  274. {"ERROR", "delay: func failed (will retry): %v", errFuncErr},
  275. }
  276. if !reflect.DeepEqual(c.logging, wantLogging) {
  277. t.Errorf("Incorrect logging: got %+v, want %+v", c.logging, wantLogging)
  278. }
  279. }
  280. }
  281. func TestDuplicateFunction(t *testing.T) {
  282. c := newFakeContext()
  283. // Fake out the adding of a task.
  284. var task *taskqueue.Task
  285. taskqueueAdder = func(_ context.Context, tk *taskqueue.Task, queue string) (*taskqueue.Task, error) {
  286. if queue != "" {
  287. t.Errorf(`Got queue %q, expected ""`, queue)
  288. }
  289. task = tk
  290. return tk, nil
  291. }
  292. if err := dupe1Func.Call(c.ctx); err == nil {
  293. t.Error("dupe1Func.Call did not return error")
  294. }
  295. if task != nil {
  296. t.Error("dupe1Func.Call posted a task")
  297. }
  298. if err := dupe2Func.Call(c.ctx); err != nil {
  299. t.Errorf("dupe2Func.Call error: %v", err)
  300. }
  301. if task == nil {
  302. t.Fatalf("dupe2Func.Call did not post a task")
  303. }
  304. // Simulate the Task Queue service.
  305. req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload))
  306. if err != nil {
  307. t.Fatalf("Failed making http.Request: %v", err)
  308. }
  309. rw := httptest.NewRecorder()
  310. runFunc(c.ctx, rw, req)
  311. if dupeWhich == 1 {
  312. t.Error("dupe2Func.Call used old registered function")
  313. } else if dupeWhich != 2 {
  314. t.Errorf("dupeWhich = %d; want 2", dupeWhich)
  315. }
  316. }