mirror of
https://github.com/containers/podman.git
synced 2025-05-20 16:47:39 +08:00

Final followup to #22270. That PR added a temporary convention allowing a new form of ExitWithError(), one with an exit code and stderr substring. In order to allow bite-size progress, the old no-args form was still allowed. This PR removes support for no-args ExitWithError(). This PR also adds one piece of new functionality: passing "" (empty string) as the stderr arg means "expect exit code but fail if there's anything at all in stderr". Signed-off-by: Ed Santiago <santiago@redhat.com>
154 lines
4.2 KiB
Go
154 lines
4.2 KiB
Go
package utils
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/onsi/gomega/format"
|
|
"github.com/onsi/gomega/gexec"
|
|
"github.com/onsi/gomega/types"
|
|
)
|
|
|
|
type podmanSession interface {
|
|
ExitCode() int
|
|
ErrorToString() string
|
|
}
|
|
|
|
type ExitMatcher struct {
|
|
types.GomegaMatcher
|
|
ExpectedExitCode int
|
|
ExitCode int
|
|
ExpectedStderr string
|
|
msg string
|
|
}
|
|
|
|
// ExitWithError checks both exit code and stderr, fails if either does not match
|
|
// Modeled after the gomega Exit() matcher and also operates on sessions.
|
|
func ExitWithError(expectExitCode int, expectStderr string) *ExitMatcher {
|
|
return &ExitMatcher{ExpectedExitCode: expectExitCode, ExpectedStderr: expectStderr}
|
|
}
|
|
|
|
// Match follows gexec.Matcher interface.
|
|
func (matcher *ExitMatcher) Match(actual interface{}) (success bool, err error) {
|
|
session, ok := actual.(podmanSession)
|
|
if !ok {
|
|
return false, fmt.Errorf("ExitWithError must be passed a gexec.Exiter (Missing method ExitCode() int) Got:\n#{format.Object(actual, 1)}")
|
|
}
|
|
|
|
matcher.ExitCode = session.ExitCode()
|
|
if matcher.ExitCode == -1 {
|
|
matcher.msg = "Expected process to exit. It did not."
|
|
return false, nil
|
|
}
|
|
|
|
// Check exit code first. If it's not what we want, there's no point
|
|
// in checking error substrings
|
|
if matcher.ExitCode != matcher.ExpectedExitCode {
|
|
matcher.msg = fmt.Sprintf("Command exited with status %d (expected %d)", matcher.ExitCode, matcher.ExpectedExitCode)
|
|
return false, nil
|
|
}
|
|
|
|
if matcher.ExpectedStderr != "" {
|
|
if !strings.Contains(session.ErrorToString(), matcher.ExpectedStderr) {
|
|
matcher.msg = fmt.Sprintf("Command exited %d as expected, but did not emit '%s'", matcher.ExitCode, matcher.ExpectedStderr)
|
|
return false, nil
|
|
}
|
|
} else {
|
|
if session.ErrorToString() != "" {
|
|
matcher.msg = "Command exited with expected exit status, but emitted unwanted stderr"
|
|
return false, nil
|
|
}
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func (matcher *ExitMatcher) FailureMessage(_ interface{}) (message string) {
|
|
return matcher.msg
|
|
}
|
|
|
|
func (matcher *ExitMatcher) NegatedFailureMessage(_ interface{}) (message string) {
|
|
panic("There is no conceivable reason to call Not(ExitWithError) !")
|
|
}
|
|
|
|
func (matcher *ExitMatcher) MatchMayChangeInTheFuture(actual interface{}) bool {
|
|
session, ok := actual.(*gexec.Session)
|
|
if ok {
|
|
return session.ExitCode() == -1
|
|
}
|
|
return true
|
|
}
|
|
|
|
// ExitCleanly asserts that a PodmanSession exits 0 and with no stderr
|
|
func ExitCleanly() types.GomegaMatcher {
|
|
return &exitCleanlyMatcher{}
|
|
}
|
|
|
|
type exitCleanlyMatcher struct {
|
|
msg string
|
|
}
|
|
|
|
func (matcher *exitCleanlyMatcher) Match(actual interface{}) (success bool, err error) {
|
|
session, ok := actual.(podmanSession)
|
|
if !ok {
|
|
return false, fmt.Errorf("ExitCleanly must be passed a PodmanSession; Got:\n %+v\n%q", actual, format.Object(actual, 1))
|
|
}
|
|
|
|
exitcode := session.ExitCode()
|
|
stderr := session.ErrorToString()
|
|
if exitcode != 0 {
|
|
matcher.msg = fmt.Sprintf("Command failed with exit status %d", exitcode)
|
|
if stderr != "" {
|
|
matcher.msg += ". See above for error message."
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
// Exit status is 0. Now check for anything on stderr
|
|
if stderr != "" {
|
|
matcher.msg = fmt.Sprintf("Unexpected warnings seen on stderr: %q", stderr)
|
|
return false, nil
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func (matcher *exitCleanlyMatcher) FailureMessage(_ interface{}) (message string) {
|
|
return matcher.msg
|
|
}
|
|
|
|
func (matcher *exitCleanlyMatcher) NegatedFailureMessage(_ interface{}) (message string) {
|
|
// FIXME - I see no situation in which we could ever want this?
|
|
return matcher.msg + " (NOT!)"
|
|
}
|
|
|
|
type ValidJSONMatcher struct {
|
|
types.GomegaMatcher
|
|
}
|
|
|
|
func BeValidJSON() *ValidJSONMatcher {
|
|
return &ValidJSONMatcher{}
|
|
}
|
|
|
|
func (matcher *ValidJSONMatcher) Match(actual interface{}) (success bool, err error) {
|
|
s, ok := actual.(string)
|
|
if !ok {
|
|
return false, fmt.Errorf("ValidJSONMatcher expects a string, not %q", actual)
|
|
}
|
|
|
|
var i interface{}
|
|
if err := json.Unmarshal([]byte(s), &i); err != nil {
|
|
return false, err
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
func (matcher *ValidJSONMatcher) FailureMessage(actual interface{}) (message string) {
|
|
return format.Message(actual, "to be valid JSON")
|
|
}
|
|
|
|
func (matcher *ValidJSONMatcher) NegatedFailureMessage(actual interface{}) (message string) {
|
|
return format.Message(actual, "to _not_ be valid JSON")
|
|
}
|