-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve Gradle command error messages (#99)
* Improve gradle errors by utilizing stderr * Code cleanup * Introduce formatted errors * Improve error messages * Fix lint issues * Update bitrise.yml * Update bitrise.yml * Update errors.go * Update errors.go * command v2 * Test scan dependencies error * Get back to go-utils v2.0.0-alpha.14 * Update androidcomponents_test.go
- Loading branch information
Showing
90 changed files
with
29,919 additions
and
149 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package androidcomponents | ||
|
||
import ( | ||
"errors" | ||
"io" | ||
"os/exec" | ||
"testing" | ||
|
||
commandv2 "github.com/bitrise-io/go-utils/v2/command" | ||
"github.com/bitrise-io/go-utils/v2/env" | ||
"github.com/bitrise-steplib/steps-install-missing-android-tools/mocks" | ||
"github.com/stretchr/testify/mock" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func Test_GivenCommand_WhenFails_ThenReturnsExitError(t *testing.T) { | ||
// TODO: androidcomponents.NewCommandError requires a command execution function to return an *exec.ExitError, | ||
// when the command was successfully executed, but returned non-zero exit status. | ||
// In go-utils/[email protected] the command package was updated to return a new custom error. | ||
// Upgrading to this or higher version breaks androidcomponents.NewCommandError. | ||
// This test ensures that the used go-utils/v2/command package works well with androidcomponents.NewCommandError. | ||
factory := commandv2.NewFactory(env.NewRepository()) | ||
cmd := factory.Create("bash", []string{"-c", "exit 1"}, nil) | ||
err := cmd.Run() | ||
var exitErr *exec.ExitError | ||
require.True(t, errors.As(err, &exitErr)) | ||
} | ||
|
||
func Test_GivenInstallerAndGradlePrintsToStderr_WhenScanDependencies_ThenErrorContainStderr(t *testing.T) { | ||
// Given | ||
var stderr io.Writer | ||
|
||
command := new(mocks.Command) | ||
command.On("Run").Run(func(args mock.Arguments) { | ||
_, err := stderr.Write([]byte("error reason")) | ||
require.NoError(t, err) | ||
}).Return(&exec.ExitError{}) | ||
command.On("PrintableCommandArgs").Return("./gradlew dependencies --stacktrace") | ||
|
||
factory := new(mocks.Factory) | ||
factory.On("Create", "./gradlew", []string{"dependencies", "--stacktrace"}, mock.Anything).Run(func(args mock.Arguments) { | ||
opts := args.Get(2).(*commandv2.Opts) | ||
stderr = opts.Stderr | ||
}).Return(command) | ||
|
||
installer := installer{ | ||
gradlewPath: "./gradlew", | ||
factory: factory, | ||
} | ||
|
||
// When | ||
err := installer.scanDependencies(false) | ||
|
||
// Then | ||
require.EqualError(t, err, "command failed with exit status -1 (./gradlew dependencies --stacktrace): error reason") | ||
} | ||
|
||
func Test_GivenInstallerAndGradleDoesNotPrintToStderr_WhenScanDependenciesAndLastAttempt_ThenErrorGenericErrorThrownAndStdoutLogged(t *testing.T) { | ||
// Given | ||
var stdout io.Writer | ||
|
||
command := new(mocks.Command) | ||
command.On("Run").Run(func(args mock.Arguments) { | ||
_, err := stdout.Write([]byte("Task failed")) | ||
require.NoError(t, err) | ||
}).Return(&exec.ExitError{}) | ||
command.On("PrintableCommandArgs").Return("./gradlew dependencies --stacktrace") | ||
|
||
factory := new(mocks.Factory) | ||
factory.On("Create", "./gradlew", []string{"dependencies", "--stacktrace"}, mock.Anything).Run(func(args mock.Arguments) { | ||
opts := args.Get(2).(*commandv2.Opts) | ||
stdout = opts.Stdout | ||
}).Return(command) | ||
|
||
logger := new(mocks.Logger) | ||
logger.On("Printf", "Task failed").Return() | ||
|
||
installer := installer{ | ||
gradlewPath: "./gradlew", | ||
factory: factory, | ||
logger: logger, | ||
} | ||
|
||
// When | ||
err := installer.scanDependencies(true) | ||
|
||
// Then | ||
require.EqualError(t, err, "command failed with exit status -1 (./gradlew dependencies --stacktrace): check the command's output for details") | ||
logger.AssertExpectations(t) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package androidcomponents | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"os/exec" | ||
) | ||
|
||
func NewCommandError(cmd string, err error, reason string) error { | ||
var exitErr *exec.ExitError | ||
if errors.As(err, &exitErr) { | ||
if len(reason) == 0 { | ||
return NewCommandExitError(cmd, exitErr) | ||
} | ||
|
||
return NewCommandExitErrorWithReason(cmd, exitErr, reason) | ||
} | ||
|
||
return NewCommandExecutionError(cmd, err) | ||
} | ||
|
||
type CommandExecutionError struct { | ||
cmd string | ||
err error | ||
} | ||
|
||
func NewCommandExecutionError(cmd string, err error) CommandExecutionError { | ||
return CommandExecutionError{ | ||
cmd: cmd, | ||
err: err, | ||
} | ||
} | ||
|
||
func (e CommandExecutionError) Error() string { | ||
return fmt.Sprintf("executing command failed (%s): %s", e.cmd, e.err) | ||
} | ||
|
||
func (e CommandExecutionError) Unwrap() error { | ||
return e.err | ||
} | ||
|
||
type CommandExitError struct { | ||
cmd string | ||
err *exec.ExitError | ||
suggestion error | ||
} | ||
|
||
func NewCommandExitError(cmd string, err *exec.ExitError) CommandExitError { | ||
return CommandExitError{ | ||
cmd: cmd, | ||
err: err, | ||
suggestion: errors.New("check the command's output for details"), | ||
} | ||
} | ||
|
||
func (e CommandExitError) Error() string { | ||
return fmt.Sprintf("command failed with exit status %d (%s): %s", e.err.ExitCode(), e.cmd, e.suggestion) | ||
} | ||
|
||
func (e CommandExitError) Unwrap() error { | ||
return e.suggestion | ||
} | ||
|
||
type CommandExitErrorWithReason struct { | ||
cmd string | ||
err *exec.ExitError | ||
reason error | ||
} | ||
|
||
func NewCommandExitErrorWithReason(cmd string, err *exec.ExitError, reason string) CommandExitErrorWithReason { | ||
return CommandExitErrorWithReason{ | ||
cmd: cmd, | ||
err: err, | ||
reason: errors.New(reason), | ||
} | ||
} | ||
|
||
func (e CommandExitErrorWithReason) Error() string { | ||
return fmt.Sprintf("command failed with exit status %d (%s): %s", e.err.ExitCode(), e.cmd, e.reason) | ||
} | ||
|
||
func (e CommandExitErrorWithReason) Unwrap() error { | ||
return e.reason | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.