diff --git a/.golangci.yaml b/.golangci.yaml index f9bceda..3f94507 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -862,8 +862,8 @@ linters: - predeclared - promlinter - revive - - rowserrcheck - - sqlclosecheck + #- rowserrcheck # disabled, because of generics. https://github.com/golangci/golangci-lint/issues/2649 + #- sqlclosecheck # disabled, because of generics. https://github.com/golangci/golangci-lint/issues/2649 - staticcheck #- structcheck # deprecated, replaced by unused - stylecheck @@ -873,7 +873,7 @@ linters: - unparam - unused #- varcheck # deprecated, replaced by unused - - wastedassign + #- wastedassign #disabled because of generics. https://github.com/golangci/golangci-lint/issues/2649 - whitespace - bidichk - gomoddirectives diff --git a/README.md b/README.md index 71d529f..e0e71c6 100644 --- a/README.md +++ b/README.md @@ -52,8 +52,8 @@ func newSubAction2() *action.Action { } ``` -`Executable`: whether is an executable action. -`Execute` will find the first executable action of the root action and execute it. +- `Executable`: whether is an executable action. +- `Execute()` will find the first executable action of the root action and execute it. ## Documentation diff --git a/action_linux_test.go b/action_linux_test.go new file mode 100644 index 0000000..c30319f --- /dev/null +++ b/action_linux_test.go @@ -0,0 +1,100 @@ +package action + +import ( + "os" + "testing" + + "github.com/Netflix/go-expect" + "github.com/stretchr/testify/assert" +) + +type expectConsole interface { + ExpectString(string) + ExpectEOF() + SendLine(string) + Send(string) +} + +type consoleWithErrorHandling struct { + console *expect.Console + t *testing.T +} + +func (c *consoleWithErrorHandling) ExpectString(s string) { + if _, err := c.console.ExpectString(s); err != nil { + c.t.Helper() + c.t.Fatalf("ExpectString(%q) = %v", s, err) + } +} + +func (c *consoleWithErrorHandling) SendLine(s string) { + if _, err := c.console.SendLine(s); err != nil { + c.t.Helper() + c.t.Fatalf("SendLine(%q) = %v", s, err) + } +} + +func (c *consoleWithErrorHandling) Send(s string) { + if _, err := c.console.Send(s); err != nil { + c.t.Helper() + c.t.Fatalf("Send(%q) = %v", s, err) + } +} + +func (c *consoleWithErrorHandling) ExpectEOF() { + if _, err := c.console.ExpectEOF(); err != nil { + c.t.Helper() + c.t.Fatalf("ExpectEOF() = %v", err) + } +} + +func TestActionFind(t *testing.T) { + c, err := expect.NewConsole(expect.WithStdin(os.Stdin), expect.WithStdout(os.Stdout)) + if err != nil { + t.Fatalf("failed to create console: %v", err) + } + defer func() { _ = c.Close() }() + console := &consoleWithErrorHandling{console: c, t: t} + + t.Run("cannot find target without sub actions", func(t *testing.T) { + act := getRootAction(c.Tty()) + err = act.Execute() + assert.NoError(t, err) + + console.ExpectString("PersistentPreRun action name: root") + console.ExpectString("PreRun action name: root") + console.ExpectString("Run action name: root") + console.ExpectString("PostRun action name: root") + console.ExpectString("PersistentPostRun action name: root") + }) + + t.Run("execute the target action", func(t *testing.T) { + act := getRootAction(c.Tty()) + acts := getSubActions(c.Tty(), _rootActionName, 10) + acts[9].Executable = getTargetFunc(true) + _ = act.AddAction(acts...) + err = act.Execute() + assert.NoError(t, err) + console.ExpectString("PersistentPreRun action name: root-sub-action-10") + console.ExpectString("PreRun action name: root-sub-action-10") + console.ExpectString("Run action name: root-sub-action-10") + console.ExpectString("PostRun action name: root-sub-action-10") + console.ExpectString("PersistentPostRun action name: root-sub-action-10") + }) + + t.Run("execute the multi layers target action", func(t *testing.T) { + act := getRootAction(c.Tty()) + acts := getSubActions(c.Tty(), _rootActionName, 5) + subsubs := getSubActions(c.Tty(), "root-sub-action-5", 10) + subsubs[9].Executable = getTargetFunc(true) + _ = acts[4].AddAction(subsubs...) + _ = act.AddAction(acts...) + err = act.Execute() + assert.NoError(t, err) + console.ExpectString("PersistentPreRun action name: root-sub-action-5-sub-action-10") + console.ExpectString("PreRun action name: root-sub-action-5-sub-action-10") + console.ExpectString("Run action name: root-sub-action-5-sub-action-10") + console.ExpectString("PostRun action name: root-sub-action-5-sub-action-10") + console.ExpectString("PersistentPostRun action name: root-sub-action-5-sub-action-10") + }) +} diff --git a/action_test.go b/action_test.go index cf7505d..f453d10 100644 --- a/action_test.go +++ b/action_test.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "os" "testing" "time" @@ -12,9 +13,9 @@ import ( const _rootActionName = "root" -func getTestHookFunc(t *testing.T, name string) func(act *Action) error { +func getTestHookFunc(tty *os.File, name string) func(act *Action) error { return func(act *Action) error { - t.Logf("%s action name: %s", name, act.Name) + _, _ = fmt.Fprintf(tty, "%s action name: %s\n", name, act.Name) return nil } } @@ -25,19 +26,19 @@ func getTargetFunc(istarget bool) func(act *Action) bool { } } -func getRootAction(t *testing.T) *Action { +func getRootAction(tty *os.File) *Action { return &Action{ Name: "root", - PersistentPreRun: getTestHookFunc(t, "PersistentPreRun"), - PreRun: getTestHookFunc(t, "PreRun"), - Run: getTestHookFunc(t, "Run"), - PostRun: getTestHookFunc(t, "PostRun"), - PersistentPostRun: getTestHookFunc(t, "PersistentPostRun"), + PersistentPreRun: getTestHookFunc(tty, "PersistentPreRun"), + PreRun: getTestHookFunc(tty, "PreRun"), + Run: getTestHookFunc(tty, "Run"), + PostRun: getTestHookFunc(tty, "PostRun"), + PersistentPostRun: getTestHookFunc(tty, "PersistentPostRun"), Executable: getTargetFunc(false), } } -func getSubActions(t *testing.T, parent string, count int) []*Action { +func getSubActions(tty *os.File, parent string, count int) []*Action { if count < 1 { return nil } @@ -45,11 +46,11 @@ func getSubActions(t *testing.T, parent string, count int) []*Action { for i := 0; i < count; i++ { actions = append(actions, &Action{ Name: fmt.Sprintf("%s-sub-action-%d", parent, i+1), - PersistentPreRun: getTestHookFunc(t, "PersistentPreRun"), - PreRun: getTestHookFunc(t, "PreRun"), - Run: getTestHookFunc(t, "Run"), - PostRun: getTestHookFunc(t, "PostRun"), - PersistentPostRun: getTestHookFunc(t, "PersistentPostRun"), + PersistentPreRun: getTestHookFunc(tty, "PersistentPreRun"), + PreRun: getTestHookFunc(tty, "PreRun"), + Run: getTestHookFunc(tty, "Run"), + PostRun: getTestHookFunc(tty, "PostRun"), + PersistentPostRun: getTestHookFunc(tty, "PersistentPostRun"), Executable: getTargetFunc(false), }) } @@ -57,7 +58,7 @@ func getSubActions(t *testing.T, parent string, count int) []*Action { } func TestActionRunnable(t *testing.T) { - act := getRootAction(t) + act := getRootAction(os.Stdout) assert.True(t, act.Runnable()) act.Run = nil assert.False(t, act.Runnable()) @@ -65,13 +66,13 @@ func TestActionRunnable(t *testing.T) { func TestActionAddAction(t *testing.T) { t.Run("successfully add 5 actions", func(t *testing.T) { - act := getRootAction(t) - acts := getSubActions(t, _rootActionName, 5) + act := getRootAction(os.Stdout) + acts := getSubActions(os.Stdout, _rootActionName, 5) _ = act.AddAction(acts...) assert.Equal(t, 5, len(act.Actions())) }) t.Run("doesn't accept self as sub action", func(t *testing.T) { - act := getRootAction(t) + act := getRootAction(os.Stdout) err := act.AddAction(act) assert.EqualError(t, err, "action can't be a child of itself") }) @@ -79,8 +80,8 @@ func TestActionAddAction(t *testing.T) { func TestActionParent(t *testing.T) { t.Run("should returns parent action", func(t *testing.T) { - act := getRootAction(t) - acts := getSubActions(t, _rootActionName, 1) + act := getRootAction(os.Stdout) + acts := getSubActions(os.Stdout, _rootActionName, 1) _ = act.AddAction(acts...) assert.False(t, act.HasParent()) assert.True(t, act.HasSubActions()) @@ -90,8 +91,8 @@ func TestActionParent(t *testing.T) { } func TestActionRemove(t *testing.T) { - act := getRootAction(t) - acts := getSubActions(t, _rootActionName, 10) + act := getRootAction(os.Stdout) + acts := getSubActions(os.Stdout, _rootActionName, 10) _ = act.AddAction(acts...) acts[9].Executable = getTargetFunc(true) assert.Equal(t, acts[9], act.Find()) @@ -100,43 +101,15 @@ func TestActionRemove(t *testing.T) { } func TestActionRoot(t *testing.T) { - act := getRootAction(t) - acts := getSubActions(t, _rootActionName, 10) + act := getRootAction(os.Stdout) + acts := getSubActions(os.Stdout, _rootActionName, 10) _ = act.AddAction(acts...) assert.Equal(t, act, act) assert.Equal(t, act, acts[9].Root()) } -func TestActionFind(t *testing.T) { - t.Run("cannot find target without sub actions", func(t *testing.T) { - act := getRootAction(t) - err := act.Execute() - t.Log(err) - }) - - t.Run("execute the target action", func(t *testing.T) { - act := getRootAction(t) - acts := getSubActions(t, _rootActionName, 10) - acts[9].Executable = getTargetFunc(true) - _ = act.AddAction(acts...) - err := act.Execute() - t.Log(err) - }) - - t.Run("execute the multi layers target action", func(t *testing.T) { - act := getRootAction(t) - acts := getSubActions(t, _rootActionName, 10) - subsubs := getSubActions(t, "root-sub-action-10", 10) - subsubs[9].Executable = getTargetFunc(true) - _ = acts[9].AddAction(subsubs...) - _ = act.AddAction(acts...) - err := act.Execute() - t.Log(err) - }) -} - func TestActionExecuteContext(t *testing.T) { - act := getRootAction(t) + act := getRootAction(os.Stdout) act.Run = func(act *Action) error { for { select { @@ -155,3 +128,46 @@ func TestActionExecuteContext(t *testing.T) { err := act.ExecuteContext(ctx) assert.EqualError(t, err, "done") } + +func TestActionExecute(t *testing.T) { + t.Run("ignore action without Run", func(t *testing.T) { + act := getRootAction(os.Stdout) + assert.Nil(t, act.Execute()) + }) + + t.Run("returns error in PreRun", func(t *testing.T) { + act := getRootAction(os.Stdout) + act.PreRun = func(act *Action) error { + return errors.New("PreRun error") + } + err := act.Execute() + assert.EqualError(t, err, "PreRun error") + }) + + t.Run("returns error in PostRun", func(t *testing.T) { + act := getRootAction(os.Stdout) + act.PostRun = func(act *Action) error { + return errors.New("PostRun error") + } + err := act.Execute() + assert.EqualError(t, err, "PostRun error") + }) + + t.Run("returns error in PersistentPreRun", func(t *testing.T) { + act := getRootAction(os.Stdout) + act.PersistentPreRun = func(act *Action) error { + return errors.New("PersistentPreRun error") + } + err := act.Execute() + assert.EqualError(t, err, "PersistentPreRun error") + }) + + t.Run("returns error in PersistentPostRun", func(t *testing.T) { + act := getRootAction(os.Stdout) + act.PersistentPostRun = func(act *Action) error { + return errors.New("PersistentPostRun error") + } + err := act.Execute() + assert.EqualError(t, err, "PersistentPostRun error") + }) +} diff --git a/go.mod b/go.mod index 474d440..24a1003 100644 --- a/go.mod +++ b/go.mod @@ -2,9 +2,13 @@ module github.com/shipengqi/action go 1.18 -require github.com/stretchr/testify v1.8.0 +require ( + github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 + github.com/stretchr/testify v1.8.0 +) require ( + github.com/creack/pty v1.1.17 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 5164829..e9a0beb 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,7 @@ +github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= +github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= +github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= +github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -5,6 +9,7 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=