Make query_checker tests pass in go1.5.
Go 1.5 has moved to storing time zone data in a zip file. The error messages
returned have also changed, causing the query_checker tests to break under 1.5.
This CL addresses the issue by allowing tests to return one of a range of
errors. If the test specifies an expectation of vErrorContaining, the error
returned by the code-under-test will match if:
* its ID matches the expected error
* its error message contains the text of the expected error
This is more lenient than the previous test. Also, the offset parameter is not
checked.
Change-Id: I6bb264a9b171783380fa9a9d0e0ee635cc858534
diff --git a/v23/syncbase/nosql/internal/query/query_checker/query_checker_test.go b/v23/syncbase/nosql/internal/query/query_checker/query_checker_test.go
index 2e2057d..93947b2 100644
--- a/v23/syncbase/nosql/internal/query/query_checker/query_checker_test.go
+++ b/v23/syncbase/nosql/internal/query/query_checker/query_checker_test.go
@@ -19,8 +19,61 @@
"v.io/v23/verror"
_ "v.io/x/ref/runtime/factories/generic"
"v.io/x/ref/test"
+ "strings"
)
+// oneOf represents an expectation that more than one type of error may be
+// returned by the corresponding test.
+type oneOf []interface{}
+
+func (o oneOf) matches(err error) bool {
+ for _, e := range o {
+ switch e := e.(type) {
+ case error:
+ if e.Error() == err.Error() {
+ return true
+ }
+ case vErrorContaining:
+ vErr, ok := err.(verror.E)
+ if !ok {
+ continue
+ }
+ if e.matches(vErr) {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// containing represents an expectation that an error will be of the given
+// VError type and will contain the given string.
+type vErrorContaining struct {
+ id verror.ID
+ message string
+}
+
+func newVErrorContaining(id verror.ID, message string) vErrorContaining {
+ return vErrorContaining{
+ id: id,
+ message: message,
+ }
+}
+
+func (v vErrorContaining) matches(err verror.E) bool {
+ if err.ID != v.id {
+ return false
+ }
+ if !strings.Contains(err.Error(), v.message) {
+ return false
+ }
+ return true
+}
+
+func (v vErrorContaining) String() string {
+ return fmt.Sprintf("VError containing ID=%s, message=%s", v.id, v.message)
+}
+
type mockDB struct {
ctx *context.T
}
@@ -80,7 +133,7 @@
type parseSelectErrorTest struct {
query string
- err error
+ err interface{}
}
func TestQueryChecker(t *testing.T) {
@@ -432,17 +485,53 @@
{"select v from Customer where v.ZipCode is not 94303", syncql.NewErrIsIsNotRequireRhsNil(db.GetContext(), 46)},
{"select v from Customer where v.ZipCode is not true", syncql.NewErrIsIsNotRequireRhsNil(db.GetContext(), 46)},
{"select v from Customer where v.ZipCode is not 943.03", syncql.NewErrIsIsNotRequireRhsNil(db.GetContext(), 46)},
- {"select v from Customer where Type(v) = \"Customer\" and Year(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 74, errors.New("unknown time zone ABC"))},
- {"select v from Customer where Type(v) = \"Customer\" and Month(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 75, errors.New("unknown time zone ABC"))},
- {"select v from Customer where Type(v) = \"Customer\" and Day(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 73, errors.New("unknown time zone ABC"))},
- {"select v from Customer where Type(v) = \"Customer\" and Hour(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 74, errors.New("unknown time zone ABC"))},
- {"select v from Customer where Type(v) = \"Customer\" and Minute(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 76, errors.New("unknown time zone ABC"))},
- {"select v from Customer where Type(v) = \"Customer\" and Second(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 76, errors.New("unknown time zone ABC"))},
+ // TODO(jkline): check offset for the 1.5 tests.
+ {"select v from Customer where Type(v) = \"Customer\" and Year(v.InvoiceDate, \"ABC\") = 2015", oneOf([]interface{}{
+ // Go1.4
+ syncql.NewErrLocationConversionError(db.GetContext(), 74, errors.New("unknown time zone ABC")),
+ // Go1.5
+ newVErrorContaining(syncql.ErrLocationConversionError.ID, "cannot find ABC in zip file"),
+ })},
+ {"select v from Customer where Type(v) = \"Customer\" and Month(v.InvoiceDate, \"ABC\") = 2015", oneOf([]interface{} {
+ // Go1.4
+ syncql.NewErrLocationConversionError(db.GetContext(), 75, errors.New("unknown time zone ABC")),
+ // Go1.5
+ newVErrorContaining(syncql.ErrLocationConversionError.ID, "cannot find ABC in zip file"),
+ })},
+ {"select v from Customer where Type(v) = \"Customer\" and Day(v.InvoiceDate, \"ABC\") = 2015", oneOf([]interface{} {
+ // Go1.4
+ syncql.NewErrLocationConversionError(db.GetContext(), 73, errors.New("unknown time zone ABC")),
+ // Go1.5
+ newVErrorContaining(syncql.ErrLocationConversionError.ID, "cannot find ABC in zip file"),
+ })},
+ {"select v from Customer where Type(v) = \"Customer\" and Hour(v.InvoiceDate, \"ABC\") = 2015", oneOf([]interface{} {
+ // Go1.4
+ syncql.NewErrLocationConversionError(db.GetContext(), 74, errors.New("unknown time zone ABC")),
+ // Go1.5
+ newVErrorContaining(syncql.ErrLocationConversionError.ID, "cannot find ABC in zip file"),
+ })},
+ {"select v from Customer where Type(v) = \"Customer\" and Minute(v.InvoiceDate, \"ABC\") = 2015", oneOf([]interface{} {
+ // Go1.4
+ syncql.NewErrLocationConversionError(db.GetContext(), 76, errors.New("unknown time zone ABC")),
+ // Go1.5
+ newVErrorContaining(syncql.ErrLocationConversionError.ID, "cannot find ABC in zip file"),
+ })},
+ {"select v from Customer where Type(v) = \"Customer\" and Second(v.InvoiceDate, \"ABC\") = 2015", oneOf([]interface{} {
+ // Go1.4
+ syncql.NewErrLocationConversionError(db.GetContext(), 76, errors.New("unknown time zone ABC")),
+ // Go1.5
+ newVErrorContaining(syncql.ErrLocationConversionError.ID, "cannot find ABC in zip file"),
+ })},
{"select v from Customer where Type(v) = \"Customer\" and Now(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrFunctionArgCount(db.GetContext(), 54, "Now", 0, 2)},
{"select v from Customer where Type(v) = \"Customer\" and Lowercase(v.Name, 2) = \"smith\"", syncql.NewErrFunctionArgCount(db.GetContext(), 54, "Lowercase", 1, 2)},
{"select v from Customer where Type(v) = \"Customer\" and Uppercase(v.Name, 2) = \"SMITH\"", syncql.NewErrFunctionArgCount(db.GetContext(), 54, "Uppercase", 1, 2)},
{"select Time() from Customer", syncql.NewErrFunctionArgCount(db.GetContext(), 7, "Time", 2, 0)},
- {"select Year(v.InvoiceDate, \"Foo\") from Customer where Type(v) = \"Invoice\"", syncql.NewErrLocationConversionError(db.GetContext(), 27, errors.New("unknown time zone Foo"))},
+ {"select Year(v.InvoiceDate, \"Foo\") from Customer where Type(v) = \"Invoice\"", oneOf([]interface{}{
+ // Go1.4
+ syncql.NewErrLocationConversionError(db.GetContext(), 27, errors.New("unknown time zone Foo")),
+ // Go1.5
+ newVErrorContaining(syncql.ErrLocationConversionError.ID, "cannot find Foo in zip file"),
+ })},
{"select K from Customer where Type(v) = \"Invoice\"", syncql.NewErrDidYouMeanLowercaseK(db.GetContext(), 7)},
{"select V from Customer where Type(v) = \"Invoice\"", syncql.NewErrDidYouMeanLowercaseV(db.GetContext(), 7)},
{"select k from Customer where K = \"001\"", syncql.NewErrDidYouMeanLowercaseK(db.GetContext(), 29)},
@@ -457,8 +546,15 @@
} else {
err := query_checker.Check(&db, s)
// Test both that the IDs compare and the text compares (since the offset needs to match).
- if verror.ErrorID(err) != verror.ErrorID(test.err) || err.Error() != test.err.Error() {
- t.Errorf("query: %s; got %v, want %v", test.query, err, test.err)
+ switch e := test.err.(type) {
+ case error:
+ if verror.ErrorID(err) != verror.ErrorID(e) || err.Error() != e.Error() {
+ t.Errorf("query: %s; got %v, want %v", test.query, err, test.err)
+ }
+ case oneOf:
+ if !e.matches(err) {
+ t.Errorf("query: %s; got %v, want one of %s", test.query, err, e)
+ }
}
}
}
diff --git a/v23/syncbase/nosql/internal/query/query_functions/date_funcs.go b/v23/syncbase/nosql/internal/query/query_functions/date_funcs.go
index 6ec82e8..b2ea0b4 100644
--- a/v23/syncbase/nosql/internal/query/query_functions/date_funcs.go
+++ b/v23/syncbase/nosql/internal/query/query_functions/date_funcs.go
@@ -15,9 +15,9 @@
"v.io/syncbase/v23/syncbase/nosql/syncql"
)
-// If possible, check if arg is convertable to a location. Fields and not yet computed
+// If possible, check if arg is convertible to a location. Fields and not yet computed
// functions cannot be checked and will just return nil.
-func checkIfPossibleThatArgIsConvertableToLocation(db query_db.Database, arg *query_parser.Operand) error {
+func checkIfPossibleThatArgIsConvertibleToLocation(db query_db.Database, arg *query_parser.Operand) error {
var locStr *query_parser.Operand
var err error
switch arg.Type {
@@ -187,8 +187,8 @@
func secondArgLocationCheck(db query_db.Database, off int64, args []*query_parser.Operand) error {
// At this point, for the args that can be evaluated before execution, it is known that
// there are two args, a time followed by a string.
- // Just need to check that the 2nd arg is convertable to a location.
- if err := checkIfPossibleThatArgIsConvertableToLocation(db, args[1]); err != nil {
+ // Just need to check that the 2nd arg is convertible to a location.
+ if err := checkIfPossibleThatArgIsConvertibleToLocation(db, args[1]); err != nil {
return err
}
return nil