| // Copyright 2011 Google Inc. All rights reserved. |
| // Use of this source code is governed by the Apache 2.0 |
| // license that can be found in the LICENSE file. |
| |
| // Package aetesting provides utilities for testing App Engine packages. |
| // This is not for testing user applications. |
| package aetesting |
| |
| import ( |
| "fmt" |
| "reflect" |
| "testing" |
| |
| "github.com/golang/protobuf/proto" |
| "golang.org/x/net/context" |
| |
| "google.golang.org/appengine/internal" |
| ) |
| |
| // FakeSingleContext returns a context whose Call invocations will be serviced |
| // by f, which should be a function that has two arguments of the input and output |
| // protocol buffer type, and one error return. |
| func FakeSingleContext(t *testing.T, service, method string, f interface{}) context.Context { |
| fv := reflect.ValueOf(f) |
| if fv.Kind() != reflect.Func { |
| t.Fatal("not a function") |
| } |
| ft := fv.Type() |
| if ft.NumIn() != 2 || ft.NumOut() != 1 { |
| t.Fatalf("f has %d in and %d out, want 2 in and 1 out", ft.NumIn(), ft.NumOut()) |
| } |
| for i := 0; i < 2; i++ { |
| at := ft.In(i) |
| if !at.Implements(protoMessageType) { |
| t.Fatalf("arg %d does not implement proto.Message", i) |
| } |
| } |
| if ft.Out(0) != errorType { |
| t.Fatalf("f's return is %v, want error", ft.Out(0)) |
| } |
| s := &single{ |
| t: t, |
| service: service, |
| method: method, |
| f: fv, |
| } |
| return internal.WithCallOverride(context.Background(), s.call) |
| } |
| |
| var ( |
| protoMessageType = reflect.TypeOf((*proto.Message)(nil)).Elem() |
| errorType = reflect.TypeOf((*error)(nil)).Elem() |
| ) |
| |
| type single struct { |
| t *testing.T |
| service, method string |
| f reflect.Value |
| } |
| |
| func (s *single) call(ctx context.Context, service, method string, in, out proto.Message) error { |
| if service == "__go__" { |
| if method == "GetNamespace" { |
| return nil // always yield an empty namespace |
| } |
| return fmt.Errorf("Unknown API call /%s.%s", service, method) |
| } |
| if service != s.service || method != s.method { |
| s.t.Fatalf("Unexpected call to /%s.%s", service, method) |
| } |
| ins := []reflect.Value{ |
| reflect.ValueOf(in), |
| reflect.ValueOf(out), |
| } |
| outs := s.f.Call(ins) |
| if outs[0].IsNil() { |
| return nil |
| } |
| return outs[0].Interface().(error) |
| } |