aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/cloud.google.com/go/errors
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/cloud.google.com/go/errors')
-rw-r--r--vendor/cloud.google.com/go/errors/error_logging_test.go206
-rw-r--r--vendor/cloud.google.com/go/errors/errors.go453
-rw-r--r--vendor/cloud.google.com/go/errors/errors_test.go212
-rw-r--r--vendor/cloud.google.com/go/errors/stack_test.go118
4 files changed, 989 insertions, 0 deletions
diff --git a/vendor/cloud.google.com/go/errors/error_logging_test.go b/vendor/cloud.google.com/go/errors/error_logging_test.go
new file mode 100644
index 000000000..559613652
--- /dev/null
+++ b/vendor/cloud.google.com/go/errors/error_logging_test.go
@@ -0,0 +1,206 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package errors
+
+import (
+ "bytes"
+ "errors"
+ "log"
+ "strings"
+ "testing"
+
+ "cloud.google.com/go/logging"
+ "golang.org/x/net/context"
+ "google.golang.org/api/option"
+)
+
+type fakeLogger struct {
+ entry *logging.Entry
+ fail bool
+}
+
+func (c *fakeLogger) LogSync(ctx context.Context, e logging.Entry) error {
+ if c.fail {
+ return errors.New("request failed")
+ }
+ c.entry = &e
+ return nil
+}
+
+func (c *fakeLogger) Close() error {
+ return nil
+}
+
+func newTestClientUsingLogging(c *fakeLogger) *Client {
+ newLoggerInterface = func(ctx context.Context, project string, opts ...option.ClientOption) (loggerInterface, error) {
+ return c, nil
+ }
+ t, err := NewClient(context.Background(), testProjectID, "myservice", "v1.000", true)
+ if err != nil {
+ panic(err)
+ }
+ t.RepanicDefault = false
+ return t
+}
+
+func TestCatchNothingUsingLogging(t *testing.T) {
+ fl := &fakeLogger{}
+ c := newTestClientUsingLogging(fl)
+ defer func() {
+ e := fl.entry
+ if e != nil {
+ t.Errorf("got error report, expected none")
+ }
+ }()
+ defer c.Catch(ctx)
+}
+
+func entryMessage(e *logging.Entry) string {
+ return e.Payload.(map[string]interface{})["message"].(string)
+}
+
+func commonLoggingChecks(t *testing.T, e *logging.Entry, panickingFunction string) {
+ if e.Payload.(map[string]interface{})["serviceContext"].(map[string]string)["service"] != "myservice" {
+ t.Errorf("error report didn't contain service name")
+ }
+ if e.Payload.(map[string]interface{})["serviceContext"].(map[string]string)["version"] != "v1.000" {
+ t.Errorf("error report didn't contain version name")
+ }
+ if !strings.Contains(entryMessage(e), "hello, error") {
+ t.Errorf("error report didn't contain message")
+ }
+ if !strings.Contains(entryMessage(e), panickingFunction) {
+ t.Errorf("error report didn't contain stack trace")
+ }
+}
+
+func TestCatchPanicUsingLogging(t *testing.T) {
+ fl := &fakeLogger{}
+ c := newTestClientUsingLogging(fl)
+ defer func() {
+ e := fl.entry
+ if e == nil {
+ t.Fatalf("got no error report, expected one")
+ }
+ commonLoggingChecks(t, e, "TestCatchPanic")
+ if !strings.Contains(entryMessage(e), "divide by zero") {
+ t.Errorf("error report didn't contain recovered value")
+ }
+ }()
+ defer c.Catch(ctx, WithMessage("hello, error"))
+ var x int
+ x = x / x
+}
+
+func TestCatchPanicNilClientUsingLogging(t *testing.T) {
+ buf := new(bytes.Buffer)
+ log.SetOutput(buf)
+ defer func() {
+ recover()
+ body := buf.String()
+ if !strings.Contains(body, "divide by zero") {
+ t.Errorf("error report didn't contain recovered value")
+ }
+ if !strings.Contains(body, "hello, error") {
+ t.Errorf("error report didn't contain message")
+ }
+ if !strings.Contains(body, "TestCatchPanicNilClient") {
+ t.Errorf("error report didn't contain recovered value")
+ }
+ }()
+ var c *Client
+ defer c.Catch(ctx, WithMessage("hello, error"))
+ var x int
+ x = x / x
+}
+
+func TestLogFailedReportsUsingLogging(t *testing.T) {
+ fl := &fakeLogger{fail: true}
+ c := newTestClientUsingLogging(fl)
+ buf := new(bytes.Buffer)
+ log.SetOutput(buf)
+ defer func() {
+ recover()
+ body := buf.String()
+ if !strings.Contains(body, "hello, error") {
+ t.Errorf("error report didn't contain message")
+ }
+ if !strings.Contains(body, "errors.TestLogFailedReports") {
+ t.Errorf("error report didn't contain stack trace")
+ }
+ if !strings.Contains(body, "divide by zero") {
+ t.Errorf("error report didn't contain recovered value")
+ }
+ }()
+ defer c.Catch(ctx, WithMessage("hello, error"))
+ var x int
+ x = x / x
+}
+
+func TestCatchNilPanicUsingLogging(t *testing.T) {
+ fl := &fakeLogger{}
+ c := newTestClientUsingLogging(fl)
+ defer func() {
+ e := fl.entry
+ if e == nil {
+ t.Fatalf("got no error report, expected one")
+ }
+ commonLoggingChecks(t, e, "TestCatchNilPanic")
+ if !strings.Contains(entryMessage(e), "nil") {
+ t.Errorf("error report didn't contain recovered value")
+ }
+ }()
+ b := true
+ defer c.Catch(ctx, WithMessage("hello, error"), PanicFlag(&b))
+ panic(nil)
+}
+
+func TestNotCatchNilPanicUsingLogging(t *testing.T) {
+ fl := &fakeLogger{}
+ c := newTestClientUsingLogging(fl)
+ defer func() {
+ e := fl.entry
+ if e != nil {
+ t.Errorf("got error report, expected none")
+ }
+ }()
+ defer c.Catch(ctx, WithMessage("hello, error"))
+ panic(nil)
+}
+
+func TestReportUsingLogging(t *testing.T) {
+ fl := &fakeLogger{}
+ c := newTestClientUsingLogging(fl)
+ c.Report(ctx, nil, "hello, ", "error")
+ e := fl.entry
+ if e == nil {
+ t.Fatalf("got no error report, expected one")
+ }
+ commonLoggingChecks(t, e, "TestReport")
+}
+
+func TestReportfUsingLogging(t *testing.T) {
+ fl := &fakeLogger{}
+ c := newTestClientUsingLogging(fl)
+ c.Reportf(ctx, nil, "hello, error 2+%d=%d", 2, 2+2)
+ e := fl.entry
+ if e == nil {
+ t.Fatalf("got no error report, expected one")
+ }
+ commonLoggingChecks(t, e, "TestReportf")
+ if !strings.Contains(entryMessage(e), "2+2=4") {
+ t.Errorf("error report didn't contain formatted message")
+ }
+}
diff --git a/vendor/cloud.google.com/go/errors/errors.go b/vendor/cloud.google.com/go/errors/errors.go
new file mode 100644
index 000000000..d5e78b8f4
--- /dev/null
+++ b/vendor/cloud.google.com/go/errors/errors.go
@@ -0,0 +1,453 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package errors is a Google Stackdriver Error Reporting library.
+//
+// This package is still experimental and subject to change.
+//
+// See https://cloud.google.com/error-reporting/ for more information.
+//
+// To initialize a client, use the NewClient function. Generally you will want
+// to do this on program initialization. The NewClient function takes as
+// arguments a context, the project name, a service name, and a version string.
+// The service name and version string identify the running program, and are
+// included in error reports. The version string can be left empty. NewClient
+// also takes a bool that indicates whether to report errors using Stackdriver
+// Logging, which will result in errors appearing in both the logs and the error
+// dashboard. This is useful if you are already a user of Stackdriver Logging.
+//
+// import "cloud.google.com/go/errors"
+// ...
+// errorsClient, err = errors.NewClient(ctx, projectID, "myservice", "v1.0", true)
+//
+// The client can recover panics in your program and report them as errors.
+// To use this functionality, defer its Catch method, as you would any other
+// function for recovering panics.
+//
+// func foo(ctx context.Context, ...) {
+// defer errorsClient.Catch(ctx)
+// ...
+// }
+//
+// Catch writes an error report containing the recovered value and a stack trace
+// to Stackdriver Error Reporting.
+//
+// There are various options you can add to the call to Catch that modify how
+// panics are handled.
+//
+// WithMessage and WithMessagef add a custom message after the recovered value,
+// using fmt.Sprint and fmt.Sprintf respectively.
+//
+// defer errorsClient.Catch(ctx, errors.WithMessagef("x=%d", x))
+//
+// WithRequest fills in various fields in the error report with information
+// about an http.Request that's being handled.
+//
+// defer errorsClient.Catch(ctx, errors.WithRequest(httpReq))
+//
+// By default, after recovering a panic, Catch will panic again with the
+// recovered value. You can turn off this behavior with the Repanic option.
+//
+// defer errorsClient.Catch(ctx, errors.Repanic(false))
+//
+// You can also change the default behavior for the client by changing the
+// RepanicDefault field.
+//
+// errorsClient.RepanicDefault = false
+//
+// It is also possible to write an error report directly without recovering a
+// panic, using Report or Reportf.
+//
+// if err != nil {
+// errorsClient.Reportf(ctx, r, "unexpected error %v", err)
+// }
+//
+// If you try to write an error report with a nil client, or if the client
+// fails to write the report to the server, the error report is logged using
+// log.Println.
+package errors // import "cloud.google.com/go/errors"
+
+import (
+ "bytes"
+ "fmt"
+ "log"
+ "net/http"
+ "runtime"
+ "strings"
+ "time"
+
+ api "cloud.google.com/go/errorreporting/apiv1beta1"
+ "cloud.google.com/go/internal/version"
+ "cloud.google.com/go/logging"
+ "github.com/golang/protobuf/ptypes/timestamp"
+ gax "github.com/googleapis/gax-go"
+ "golang.org/x/net/context"
+ "google.golang.org/api/option"
+ erpb "google.golang.org/genproto/googleapis/devtools/clouderrorreporting/v1beta1"
+)
+
+const (
+ userAgent = `gcloud-golang-errorreporting/20160701`
+)
+
+type apiInterface interface {
+ ReportErrorEvent(ctx context.Context, req *erpb.ReportErrorEventRequest, opts ...gax.CallOption) (*erpb.ReportErrorEventResponse, error)
+ Close() error
+}
+
+var newApiInterface = func(ctx context.Context, opts ...option.ClientOption) (apiInterface, error) {
+ client, err := api.NewReportErrorsClient(ctx, opts...)
+ if err != nil {
+ return nil, err
+ }
+ client.SetGoogleClientInfo("gccl", version.Repo)
+ return client, nil
+}
+
+type loggerInterface interface {
+ LogSync(ctx context.Context, e logging.Entry) error
+ Close() error
+}
+
+type logger struct {
+ *logging.Logger
+ c *logging.Client
+}
+
+func (l logger) Close() error {
+ return l.c.Close()
+}
+
+var newLoggerInterface = func(ctx context.Context, projectID string, opts ...option.ClientOption) (loggerInterface, error) {
+ lc, err := logging.NewClient(ctx, projectID, opts...)
+ if err != nil {
+ return nil, fmt.Errorf("creating Logging client: %v", err)
+ }
+ l := lc.Logger("errorreports")
+ return logger{l, lc}, nil
+}
+
+type sender interface {
+ send(ctx context.Context, r *http.Request, message string)
+ close() error
+}
+
+// errorApiSender sends error reports using the Stackdriver Error Reporting API.
+type errorApiSender struct {
+ apiClient apiInterface
+ projectID string
+ serviceContext erpb.ServiceContext
+}
+
+// loggingSender sends error reports using the Stackdriver Logging API.
+type loggingSender struct {
+ logger loggerInterface
+ projectID string
+ serviceContext map[string]string
+ client *logging.Client
+}
+
+type Client struct {
+ sender
+ // RepanicDefault determines whether Catch will re-panic after recovering a
+ // panic. This behavior can be overridden for an individual call to Catch using
+ // the Repanic option.
+ RepanicDefault bool
+}
+
+func NewClient(ctx context.Context, projectID, serviceName, serviceVersion string, useLogging bool, opts ...option.ClientOption) (*Client, error) {
+ if useLogging {
+ l, err := newLoggerInterface(ctx, projectID, opts...)
+ if err != nil {
+ return nil, fmt.Errorf("creating Logging client: %v", err)
+ }
+ sender := &loggingSender{
+ logger: l,
+ projectID: projectID,
+ serviceContext: map[string]string{
+ "service": serviceName,
+ },
+ }
+ if serviceVersion != "" {
+ sender.serviceContext["version"] = serviceVersion
+ }
+ c := &Client{
+ sender: sender,
+ RepanicDefault: true,
+ }
+ return c, nil
+ } else {
+ a, err := newApiInterface(ctx, opts...)
+ if err != nil {
+ return nil, fmt.Errorf("creating Error Reporting client: %v", err)
+ }
+ c := &Client{
+ sender: &errorApiSender{
+ apiClient: a,
+ projectID: "projects/" + projectID,
+ serviceContext: erpb.ServiceContext{
+ Service: serviceName,
+ Version: serviceVersion,
+ },
+ },
+ RepanicDefault: true,
+ }
+ return c, nil
+ }
+}
+
+// Close closes any resources held by the client.
+// Close should be called when the client is no longer needed.
+// It need not be called at program exit.
+func (c *Client) Close() error {
+ err := c.sender.close()
+ c.sender = nil
+ return err
+}
+
+// An Option is an optional argument to Catch.
+type Option interface {
+ isOption()
+}
+
+// PanicFlag returns an Option that can inform Catch that a panic has occurred.
+// If *p is true when Catch is called, an error report is made even if recover
+// returns nil. This allows Catch to report an error for panic(nil).
+// If p is nil, the option is ignored.
+//
+// Here is an example of how to use PanicFlag:
+//
+// func foo(ctx context.Context, ...) {
+// hasPanicked := true
+// defer errorsClient.Catch(ctx, errors.PanicFlag(&hasPanicked))
+// ...
+// ...
+// // We have reached the end of the function, so we're not panicking.
+// hasPanicked = false
+// }
+func PanicFlag(p *bool) Option { return panicFlag{p} }
+
+type panicFlag struct {
+ *bool
+}
+
+func (h panicFlag) isOption() {}
+
+// Repanic returns an Option that determines whether Catch will re-panic after
+// it reports an error. This overrides the default in the client.
+func Repanic(r bool) Option { return repanic(r) }
+
+type repanic bool
+
+func (r repanic) isOption() {}
+
+// WithRequest returns an Option that informs Catch or Report of an http.Request
+// that is being handled. Information from the Request is included in the error
+// report, if one is made.
+func WithRequest(r *http.Request) Option { return withRequest{r} }
+
+type withRequest struct {
+ *http.Request
+}
+
+func (w withRequest) isOption() {}
+
+// WithMessage returns an Option that sets a message to be included in the error
+// report, if one is made. v is converted to a string with fmt.Sprint.
+func WithMessage(v ...interface{}) Option { return message(v) }
+
+type message []interface{}
+
+func (m message) isOption() {}
+
+// WithMessagef returns an Option that sets a message to be included in the error
+// report, if one is made. format and v are converted to a string with fmt.Sprintf.
+func WithMessagef(format string, v ...interface{}) Option { return messagef{format, v} }
+
+type messagef struct {
+ format string
+ v []interface{}
+}
+
+func (m messagef) isOption() {}
+
+// Catch tries to recover a panic; if it succeeds, it writes an error report.
+// It should be called by deferring it, like any other function for recovering
+// panics.
+//
+// Catch can be called concurrently with other calls to Catch, Report or Reportf.
+func (c *Client) Catch(ctx context.Context, opt ...Option) {
+ panicked := false
+ for _, o := range opt {
+ switch o := o.(type) {
+ case panicFlag:
+ panicked = panicked || o.bool != nil && *o.bool
+ }
+ }
+ x := recover()
+ if x == nil && !panicked {
+ return
+ }
+ var (
+ r *http.Request
+ shouldRepanic = true
+ messages = []string{fmt.Sprint(x)}
+ )
+ if c != nil {
+ shouldRepanic = c.RepanicDefault
+ }
+ for _, o := range opt {
+ switch o := o.(type) {
+ case repanic:
+ shouldRepanic = bool(o)
+ case withRequest:
+ r = o.Request
+ case message:
+ messages = append(messages, fmt.Sprint(o...))
+ case messagef:
+ messages = append(messages, fmt.Sprintf(o.format, o.v...))
+ }
+ }
+ c.logInternal(ctx, r, true, strings.Join(messages, " "))
+ if shouldRepanic {
+ panic(x)
+ }
+}
+
+// Report writes an error report unconditionally, instead of only when a panic
+// occurs.
+// If r is non-nil, information from the Request is included in the error report.
+//
+// Report can be called concurrently with other calls to Catch, Report or Reportf.
+func (c *Client) Report(ctx context.Context, r *http.Request, v ...interface{}) {
+ c.logInternal(ctx, r, false, fmt.Sprint(v...))
+}
+
+// Reportf writes an error report unconditionally, instead of only when a panic
+// occurs.
+// If r is non-nil, information from the Request is included in the error report.
+//
+// Reportf can be called concurrently with other calls to Catch, Report or Reportf.
+func (c *Client) Reportf(ctx context.Context, r *http.Request, format string, v ...interface{}) {
+ c.logInternal(ctx, r, false, fmt.Sprintf(format, v...))
+}
+
+func (c *Client) logInternal(ctx context.Context, r *http.Request, isPanic bool, msg string) {
+ // limit the stack trace to 16k.
+ var buf [16384]byte
+ stack := buf[0:runtime.Stack(buf[:], false)]
+ message := msg + "\n" + chopStack(stack, isPanic)
+ if c == nil {
+ log.Println("Error report used nil client:", message)
+ return
+ }
+ c.send(ctx, r, message)
+}
+
+func (s *loggingSender) send(ctx context.Context, r *http.Request, message string) {
+ payload := map[string]interface{}{
+ "eventTime": time.Now().In(time.UTC).Format(time.RFC3339Nano),
+ "message": message,
+ "serviceContext": s.serviceContext,
+ }
+ if r != nil {
+ payload["context"] = map[string]interface{}{
+ "httpRequest": map[string]interface{}{
+ "method": r.Method,
+ "url": r.Host + r.RequestURI,
+ "userAgent": r.UserAgent(),
+ "referrer": r.Referer(),
+ "remoteIp": r.RemoteAddr,
+ },
+ }
+ }
+ e := logging.Entry{
+ Severity: logging.Error,
+ Payload: payload,
+ }
+ err := s.logger.LogSync(ctx, e)
+ if err != nil {
+ log.Println("Error writing error report:", err, "report:", payload)
+ }
+}
+
+func (s *loggingSender) close() error {
+ return s.client.Close()
+}
+
+func (s *errorApiSender) send(ctx context.Context, r *http.Request, message string) {
+ time := time.Now()
+ var errorContext *erpb.ErrorContext
+ if r != nil {
+ errorContext = &erpb.ErrorContext{
+ HttpRequest: &erpb.HttpRequestContext{
+ Method: r.Method,
+ Url: r.Host + r.RequestURI,
+ UserAgent: r.UserAgent(),
+ Referrer: r.Referer(),
+ RemoteIp: r.RemoteAddr,
+ },
+ }
+ }
+ req := erpb.ReportErrorEventRequest{
+ ProjectName: s.projectID,
+ Event: &erpb.ReportedErrorEvent{
+ EventTime: &timestamp.Timestamp{
+ Seconds: time.Unix(),
+ Nanos: int32(time.Nanosecond()),
+ },
+ ServiceContext: &s.serviceContext,
+ Message: message,
+ Context: errorContext,
+ },
+ }
+ _, err := s.apiClient.ReportErrorEvent(ctx, &req)
+ if err != nil {
+ log.Println("Error writing error report:", err, "report:", message)
+ }
+}
+
+func (s *errorApiSender) close() error {
+ return s.apiClient.Close()
+}
+
+// chopStack trims a stack trace so that the function which panics or calls
+// Report is first.
+func chopStack(s []byte, isPanic bool) string {
+ var f []byte
+ if isPanic {
+ f = []byte("panic(")
+ } else {
+ f = []byte("cloud.google.com/go/errors.(*Client).Report")
+ }
+
+ lfFirst := bytes.IndexByte(s, '\n')
+ if lfFirst == -1 {
+ return string(s)
+ }
+ stack := s[lfFirst:]
+ panicLine := bytes.Index(stack, f)
+ if panicLine == -1 {
+ return string(s)
+ }
+ stack = stack[panicLine+1:]
+ for i := 0; i < 2; i++ {
+ nextLine := bytes.IndexByte(stack, '\n')
+ if nextLine == -1 {
+ return string(s)
+ }
+ stack = stack[nextLine+1:]
+ }
+ return string(s[:lfFirst+1]) + string(stack)
+}
diff --git a/vendor/cloud.google.com/go/errors/errors_test.go b/vendor/cloud.google.com/go/errors/errors_test.go
new file mode 100644
index 000000000..1494be7e2
--- /dev/null
+++ b/vendor/cloud.google.com/go/errors/errors_test.go
@@ -0,0 +1,212 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package errors
+
+import (
+ "bytes"
+ "errors"
+ "log"
+ "strings"
+ "testing"
+
+ gax "github.com/googleapis/gax-go"
+
+ "golang.org/x/net/context"
+ "google.golang.org/api/option"
+ erpb "google.golang.org/genproto/googleapis/devtools/clouderrorreporting/v1beta1"
+)
+
+const testProjectID = "testproject"
+
+type fakeReportErrorsClient struct {
+ req *erpb.ReportErrorEventRequest
+ fail bool
+}
+
+func (c *fakeReportErrorsClient) ReportErrorEvent(ctx context.Context, req *erpb.ReportErrorEventRequest, _ ...gax.CallOption) (*erpb.ReportErrorEventResponse, error) {
+ if c.fail {
+ return nil, errors.New("request failed")
+ }
+ c.req = req
+ return &erpb.ReportErrorEventResponse{}, nil
+}
+
+func (c *fakeReportErrorsClient) Close() error {
+ return nil
+}
+
+func newTestClient(c *fakeReportErrorsClient) *Client {
+ newApiInterface = func(ctx context.Context, opts ...option.ClientOption) (apiInterface, error) {
+ return c, nil
+ }
+ t, err := NewClient(context.Background(), testProjectID, "myservice", "v1.000", false)
+ if err != nil {
+ panic(err)
+ }
+ t.RepanicDefault = false
+ return t
+}
+
+var ctx context.Context
+
+func init() {
+ ctx = context.Background()
+}
+
+func TestCatchNothing(t *testing.T) {
+ fc := &fakeReportErrorsClient{}
+ c := newTestClient(fc)
+ defer func() {
+ r := fc.req
+ if r != nil {
+ t.Errorf("got error report, expected none")
+ }
+ }()
+ defer c.Catch(ctx)
+}
+
+func commonChecks(t *testing.T, req *erpb.ReportErrorEventRequest, panickingFunction string) {
+ if req.Event.ServiceContext.Service != "myservice" {
+ t.Errorf("error report didn't contain service name")
+ }
+ if req.Event.ServiceContext.Version != "v1.000" {
+ t.Errorf("error report didn't contain version name")
+ }
+ if !strings.Contains(req.Event.Message, "hello, error") {
+ t.Errorf("error report didn't contain message")
+ }
+ if !strings.Contains(req.Event.Message, panickingFunction) {
+ t.Errorf("error report didn't contain stack trace")
+ }
+}
+
+func TestCatchPanic(t *testing.T) {
+ fc := &fakeReportErrorsClient{}
+ c := newTestClient(fc)
+ defer func() {
+ r := fc.req
+ if r == nil {
+ t.Fatalf("got no error report, expected one")
+ }
+ commonChecks(t, r, "errors.TestCatchPanic")
+ if !strings.Contains(r.Event.Message, "divide by zero") {
+ t.Errorf("error report didn't contain recovered value")
+ }
+ }()
+ defer c.Catch(ctx, WithMessage("hello, error"))
+ var x int
+ x = x / x
+}
+
+func TestCatchPanicNilClient(t *testing.T) {
+ buf := new(bytes.Buffer)
+ log.SetOutput(buf)
+ defer func() {
+ recover()
+ body := buf.String()
+ if !strings.Contains(body, "divide by zero") {
+ t.Errorf("error report didn't contain recovered value")
+ }
+ if !strings.Contains(body, "hello, error") {
+ t.Errorf("error report didn't contain message")
+ }
+ if !strings.Contains(body, "TestCatchPanicNilClient") {
+ t.Errorf("error report didn't contain recovered value")
+ }
+ }()
+ var c *Client
+ defer c.Catch(ctx, WithMessage("hello, error"))
+ var x int
+ x = x / x
+}
+
+func TestLogFailedReports(t *testing.T) {
+ fc := &fakeReportErrorsClient{fail: true}
+ c := newTestClient(fc)
+ buf := new(bytes.Buffer)
+ log.SetOutput(buf)
+ defer func() {
+ recover()
+ body := buf.String()
+ if !strings.Contains(body, "hello, error") {
+ t.Errorf("error report didn't contain message")
+ }
+ if !strings.Contains(body, "errors.TestLogFailedReports") {
+ t.Errorf("error report didn't contain stack trace")
+ }
+ if !strings.Contains(body, "divide by zero") {
+ t.Errorf("error report didn't contain recovered value")
+ }
+ }()
+ defer c.Catch(ctx, WithMessage("hello, error"))
+ var x int
+ x = x / x
+}
+
+func TestCatchNilPanic(t *testing.T) {
+ fc := &fakeReportErrorsClient{}
+ c := newTestClient(fc)
+ defer func() {
+ r := fc.req
+ if r == nil {
+ t.Fatalf("got no error report, expected one")
+ }
+ commonChecks(t, r, "errors.TestCatchNilPanic")
+ if !strings.Contains(r.Event.Message, "nil") {
+ t.Errorf("error report didn't contain recovered value")
+ }
+ }()
+ b := true
+ defer c.Catch(ctx, WithMessage("hello, error"), PanicFlag(&b))
+ panic(nil)
+}
+
+func TestNotCatchNilPanic(t *testing.T) {
+ fc := &fakeReportErrorsClient{}
+ c := newTestClient(fc)
+ defer func() {
+ r := fc.req
+ if r != nil {
+ t.Errorf("got error report, expected none")
+ }
+ }()
+ defer c.Catch(ctx, WithMessage("hello, error"))
+ panic(nil)
+}
+
+func TestReport(t *testing.T) {
+ fc := &fakeReportErrorsClient{}
+ c := newTestClient(fc)
+ c.Report(ctx, nil, "hello, ", "error")
+ r := fc.req
+ if r == nil {
+ t.Fatalf("got no error report, expected one")
+ }
+ commonChecks(t, r, "errors.TestReport")
+}
+
+func TestReportf(t *testing.T) {
+ fc := &fakeReportErrorsClient{}
+ c := newTestClient(fc)
+ c.Reportf(ctx, nil, "hello, error 2+%d=%d", 2, 2+2)
+ r := fc.req
+ if r == nil {
+ t.Fatalf("got no error report, expected one")
+ }
+ commonChecks(t, r, "errors.TestReportf")
+ if !strings.Contains(r.Event.Message, "2+2=4") {
+ t.Errorf("error report didn't contain formatted message")
+ }
+}
diff --git a/vendor/cloud.google.com/go/errors/stack_test.go b/vendor/cloud.google.com/go/errors/stack_test.go
new file mode 100644
index 000000000..25cd4c60b
--- /dev/null
+++ b/vendor/cloud.google.com/go/errors/stack_test.go
@@ -0,0 +1,118 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package errors
+
+import "testing"
+
+func TestChopStack(t *testing.T) {
+ for _, test := range []struct {
+ name string
+ in []byte
+ expected string
+ isPanic bool
+ }{
+ {
+ name: "Catch",
+ in: []byte(`goroutine 20 [running]:
+runtime/debug.Stack()
+ /gopath/src/runtime/debug/stack.go:24 +0x79
+cloud.google.com/go/errors.(*Client).logInternal()
+ /gopath/src/cloud.google.com/go/errors/errors.go:259 +0x18b
+cloud.google.com/go/errors.(*Client).Catch()
+ /gopath/src/cloud.google.com/go/errors/errors.go:219 +0x6ed
+panic()
+ /gopath/src/runtime/panic.go:458 +0x243
+cloud.google.com/go/errors_test.TestCatchPanic()
+ /gopath/src/cloud.google.com/go/errors/errors_test.go:93 +0x171
+testing.tRunner()
+ /gopath/src/testing/testing.go:610 +0x81
+created by testing.(*T).Run
+ /gopath/src/testing/testing.go:646 +0x2ec
+`),
+ expected: `goroutine 20 [running]:
+cloud.google.com/go/errors_test.TestCatchPanic()
+ /gopath/src/cloud.google.com/go/errors/errors_test.go:93 +0x171
+testing.tRunner()
+ /gopath/src/testing/testing.go:610 +0x81
+created by testing.(*T).Run
+ /gopath/src/testing/testing.go:646 +0x2ec
+`,
+ isPanic: true,
+ },
+ {
+ name: "function not found",
+ in: []byte(`goroutine 20 [running]:
+runtime/debug.Stack()
+ /gopath/src/runtime/debug/stack.go:24 +0x79
+cloud.google.com/go/errors.(*Client).logInternal()
+ /gopath/src/cloud.google.com/go/errors/errors.go:259 +0x18b
+cloud.google.com/go/errors.(*Client).Catch()
+ /gopath/src/cloud.google.com/go/errors/errors.go:219 +0x6ed
+cloud.google.com/go/errors_test.TestCatchPanic()
+ /gopath/src/cloud.google.com/go/errors/errors_test.go:93 +0x171
+testing.tRunner()
+ /gopath/src/testing/testing.go:610 +0x81
+created by testing.(*T).Run
+ /gopath/src/testing/testing.go:646 +0x2ec
+`),
+ expected: `goroutine 20 [running]:
+runtime/debug.Stack()
+ /gopath/src/runtime/debug/stack.go:24 +0x79
+cloud.google.com/go/errors.(*Client).logInternal()
+ /gopath/src/cloud.google.com/go/errors/errors.go:259 +0x18b
+cloud.google.com/go/errors.(*Client).Catch()
+ /gopath/src/cloud.google.com/go/errors/errors.go:219 +0x6ed
+cloud.google.com/go/errors_test.TestCatchPanic()
+ /gopath/src/cloud.google.com/go/errors/errors_test.go:93 +0x171
+testing.tRunner()
+ /gopath/src/testing/testing.go:610 +0x81
+created by testing.(*T).Run
+ /gopath/src/testing/testing.go:646 +0x2ec
+`,
+ isPanic: true,
+ },
+ {
+ name: "Report",
+ in: []byte(` goroutine 39 [running]:
+runtime/debug.Stack()
+ /gopath/runtime/debug/stack.go:24 +0x79
+cloud.google.com/go/errors.(*Client).logInternal()
+ /gopath/cloud.google.com/go/errors/errors.go:259 +0x18b
+cloud.google.com/go/errors.(*Client).Report()
+ /gopath/cloud.google.com/go/errors/errors.go:248 +0x4ed
+cloud.google.com/go/errors_test.TestReport()
+ /gopath/cloud.google.com/go/errors/errors_test.go:137 +0x2a1
+testing.tRunner()
+ /gopath/testing/testing.go:610 +0x81
+created by testing.(*T).Run
+ /gopath/testing/testing.go:646 +0x2ec
+`),
+ expected: ` goroutine 39 [running]:
+cloud.google.com/go/errors_test.TestReport()
+ /gopath/cloud.google.com/go/errors/errors_test.go:137 +0x2a1
+testing.tRunner()
+ /gopath/testing/testing.go:610 +0x81
+created by testing.(*T).Run
+ /gopath/testing/testing.go:646 +0x2ec
+`,
+ isPanic: false,
+ },
+ } {
+ out := chopStack(test.in, test.isPanic)
+ if out != test.expected {
+ t.Errorf("case %q: chopStack(%q, %t): got %q want %q", test.name, test.in, test.isPanic, out, test.expected)
+ }
+ }
+}