test(bigquery): add testing for retry predicate (#3902)

Adds a minor nil check to the retry predicate, and adds a table test for the predicate function.

Release-As: bigquery/v1.17.0
diff --git a/bigquery/bigquery.go b/bigquery/bigquery.go
index 025c5e3..1beb248 100644
--- a/bigquery/bigquery.go
+++ b/bigquery/bigquery.go
@@ -166,6 +166,9 @@
 // retryable; these are returned by systems between the client and the BigQuery
 // service.
 func retryableError(err error) bool {
+	if err == nil {
+		return false
+	}
 	// Special case due to http2: https://github.com/googleapis/google-cloud-go/issues/1793
 	// Due to Go's default being higher for streams-per-connection than is accepted by the
 	// BQ backend, it's possible to get streams refused immediately after a connection is
diff --git a/bigquery/bigquery_test.go b/bigquery/bigquery_test.go
new file mode 100644
index 0000000..c5a05aa
--- /dev/null
+++ b/bigquery/bigquery_test.go
@@ -0,0 +1,93 @@
+// Copyright 2021 Google LLC
+//
+// 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 bigquery
+
+import (
+	"errors"
+	"net/http"
+	"testing"
+
+	"google.golang.org/api/googleapi"
+)
+
+func TestRetryableErrors(t *testing.T) {
+	for _, tc := range []struct {
+		description string
+		in          error
+		want        bool
+	}{
+		{
+			"nil error",
+			nil,
+			false,
+		},
+		{
+			"http stream closed",
+			errors.New("http2: stream closed"),
+			true,
+		},
+		{
+			"unavailable",
+			&googleapi.Error{
+				Code:    http.StatusServiceUnavailable,
+				Message: "foo",
+			},
+			true,
+		},
+		{
+			// not retried per https://google.aip.dev/194
+			"internal error",
+			&googleapi.Error{
+				Code: http.StatusInternalServerError,
+			},
+			false,
+		},
+		{
+			"internal w/backend reason",
+			&googleapi.Error{
+				Code:    http.StatusServiceUnavailable,
+				Message: "foo",
+				Errors: []googleapi.ErrorItem{
+					{Reason: "backendError", Message: "foo"},
+				},
+			},
+			true,
+		},
+		{
+			"internal w/rateLimitExceeded reason",
+			&googleapi.Error{
+				Code:    http.StatusServiceUnavailable,
+				Message: "foo",
+				Errors: []googleapi.ErrorItem{
+					{Reason: "rateLimitExceeded", Message: "foo"},
+				},
+			},
+			true,
+		},
+		{
+			"bad gateway error",
+			&googleapi.Error{
+				Code:    http.StatusBadGateway,
+				Message: "foo",
+			},
+			true,
+		},
+	} {
+		got := retryableError(tc.in)
+		if got != tc.want {
+			t.Errorf("case (%s) mismatch:  got %t want %t", tc.description, got, tc.want)
+		}
+	}
+}