runner: add one-to-many error mapping for canonical error checking in BoGo tests Associated Issue: https://github.com/golang/go/issues/71066 Change-Id: I6d8e35f03249bd0b612e54e6c00452f50f4cb4bf Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/75507 Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go index 47fac16..8cbb46b 100644 --- a/ssl/test/runner/runner.go +++ b/ssl/test/runner/runner.go
@@ -94,7 +94,7 @@ // string for the shim in question. For example, it might map // “:NO_SHARED_CIPHER:” (a BoringSSL error string) to something // like “SSL_ERROR_NO_CYPHER_OVERLAP”. - ErrorMap map[string]string + ErrorMap map[string][]string // HalfRTTTickets is the number of half-RTT tickets the client should // expect before half-RTT data when testing 0-RTT. @@ -1450,16 +1450,42 @@ return nil } -func translateExpectedError(errorStr string) string { - if translated, ok := shimConfig.ErrorMap[errorStr]; ok { +// translateExpectedError uses a canonical BoringSSL error to produce +// a slice of expected canonical errors in bogo_shim_config.json. +func translateExpectedError(canonical string) []string { + if translated, found := shimConfig.ErrorMap[canonical]; found { return translated } - if *looseErrors { - return "" - } + // not specifying a canonical error will have the same effect as -loose-errors being true + // since the emptry string with match all error substrings. + return []string{canonical} +} - return errorStr +// formatErrors takes the semantic mapping from translateExpectedError +// and outputs human-readable digest of BoGo error state. +func formatErrors(expectedErrors []string, stderr, local, child, stdout, expectedLocal, expectedCanonical string) (string, string) { + got := fmt.Sprintf("\tstderr:\n\t\t%s\n\tlocal: %q\n\tchild: %q\n\tstdout: %s", stderr, local, child, stdout) + want := fmt.Sprintf("\tlocal: %q\n\tremote: %q", expectedLocal, expectedCanonical) + if slices.Equal(expectedErrors, []string{expectedCanonical}) { + return got, want + } + if len(expectedErrors) == 0 || expectedErrors == nil { + return got, want + " (no specified mapping)" + } + return got, want + " mapped to one of:\n\t\t" + strings.Join(expectedErrors, "\n\t\t") +} + +// matchError plucks the relevant canonical error from the provided +// slice if found; if the slice is empty/nil, strict error checking +// is presumed to be disabled. +func matchError(expectedErrors []string, stderr string) bool { + for _, expectedError := range expectedErrors { + if strings.Contains(stderr, expectedError) { + return true + } + } + return false } // shimInitialWrite is the data we expect from the shim when the @@ -1808,8 +1834,8 @@ } failed := localErr != nil || childErr != nil - expectedError := translateExpectedError(test.expectedError) - correctFailure := len(expectedError) == 0 || strings.Contains(stderr, expectedError) + expectedErrors := translateExpectedError(test.expectedError) + correctFailure := *looseErrors || matchError(expectedErrors, stderr) localErrString := "none" if localErr != nil { @@ -1825,21 +1851,25 @@ childErrString = childErr.Error() } + got, want := formatErrors(expectedErrors, + stderr, localErrString, childErrString, stdout, + test.expectedLocalError, test.expectedError) + var msg string switch { case failed && !test.shouldFail: msg = "unexpected failure" case !failed && test.shouldFail: - msg = fmt.Sprintf("unexpected success (wanted failure with %q / %q)", expectedError, test.expectedLocalError) + msg = fmt.Sprintf("unexpected success\nwant:\n%s\n", want) case failed && !correctFailure: - msg = fmt.Sprintf("bad error (wanted %q / %q)", expectedError, test.expectedLocalError) + msg = fmt.Sprintf("unexpected error\ngot:\n%s\n\nwant:\n%s\n", got, want) case mustFail: msg = "test failure" default: panic("internal error") } - return fmt.Errorf("%s: local error %q, child error %q, stdout:\n%s\nstderr:\n%s\n%s", msg, localErrString, childErrString, stdout, stderr, extraStderr) + return fmt.Errorf("%s\nextra stderr:\n%s", msg, extraStderr) } if len(extraStderr) > 0 || (!failed && len(stderr) > 0) {