diff --git a/app/app.go b/app/app.go index 9ebd403..ac7d0bb 100644 --- a/app/app.go +++ b/app/app.go @@ -2,10 +2,13 @@ package app import ( "errors" + "fmt" + "io" "os" "gitlab.com/revalus/grm/commands" "gitlab.com/revalus/grm/config" + "gitlab.com/revalus/grm/echo" ) const ( @@ -19,15 +22,12 @@ const ( type GitRepositoryManager struct { cliArguments config.CliArguments configuration config.Configuration - console ConsoleOutput } func (g *GitRepositoryManager) Parse(args []string) { - co := ConsoleOutput{} - checkCriticalError := func(err error) { if err != nil { - co.ErrorfMsg("%v", err.Error()) + fmt.Printf("Error: %v", err.Error()) os.Exit(2) } } @@ -43,21 +43,22 @@ func (g *GitRepositoryManager) Parse(args []string) { configuration, err := config.GetRepositoryConfig(configFileContent, fileExcension) checkCriticalError(err) - co.Color = arguments.Color - g.console = co g.cliArguments = arguments g.configuration = configuration } -func (g *GitRepositoryManager) Run() int { +func (g *GitRepositoryManager) Run(w io.Writer) int { + + echo.Color(g.cliArguments.Color) + echo.Output(w) exitCode := 0 if len(g.cliArguments.LimitTags) != 0 { err := g.limitTags() if err != nil { - g.console.ErrorfMsg(err.Error()) + echo.ErrorfMsg(err.Error()) exitCode = 1 } } @@ -65,40 +66,40 @@ func (g *GitRepositoryManager) Run() int { if g.cliArguments.LimitName != "" { err := g.limitName() if err != nil { - g.console.ErrorfMsg(err.Error()) + echo.ErrorfMsg(err.Error()) exitCode = 1 } } if g.cliArguments.Sync && exitCode == 0 { - g.console.InfoFMsg("Synchronizing repositories") + echo.InfoFMsg("Synchronizing repositories") sync := commands.NewSynchronizer(g.configuration.Workspace) g.runCommand(sync) - g.console.InfoFMsg("All repositories are synced") + echo.InfoFMsg("All repositories are synced") } if g.cliArguments.Status && exitCode == 0 { - g.console.InfoFMsg("Current status of repositories") + echo.InfoFMsg("Current status of repositories") status := commands.NewStatusChecker(g.configuration.Workspace) g.runCommand(status) } if g.cliArguments.Version { - g.console.InfoFMsg("Current version: %v", VERSION) + echo.InfoFMsg("Current version: %v", VERSION) } return exitCode } func (g GitRepositoryManager) describeStatus(status commands.CommandStatus) { if status.Error { - g.console.ErrorStatusF("Repository \"%v\": an error occurred: %v", status.Name, status.Message) + echo.RedMessageF("Repository \"%v\": an error occurred: %v", status.Name, status.Message) return } if status.Changed { - g.console.ChangedStatusF("Repository \"%v\": %v", status.Name, status.Message) + echo.YellowMessageF("Repository \"%v\": %v", status.Name, status.Message) } else { - g.console.UnchangedStatusF("Repository \"%v\": %v", status.Name, status.Message) + echo.GreenMessageF("Repository \"%v\": %v", status.Name, status.Message) } } diff --git a/app/app_test.go b/app/app_test.go index c1471ad..314eb48 100644 --- a/app/app_test.go +++ b/app/app_test.go @@ -8,6 +8,7 @@ import ( "gitlab.com/revalus/grm/commands" "gitlab.com/revalus/grm/config" + "gitlab.com/revalus/grm/echo" ) type FakeCommandToTest struct { @@ -15,6 +16,21 @@ type FakeCommandToTest struct { triggerChanged bool } +type ExpectedMessageTester struct { + expectedMessages []string +} + +func (emt ExpectedMessageTester) Write(p []byte) (n int, err error) { + msg := string(p) + if !checkIsItemInSlice(msg, emt.expectedMessages) { + panic(fmt.Sprintf("the message \"%v\"does not match any of the given patterns: %#v", msg, emt.expectedMessages)) + } else { + fmt.Println(msg) + } + + return 0, nil +} + func (fk FakeCommandToTest) Command(repoCfg config.RepositoryConfig, cmdStatus chan commands.CommandStatus) { status := commands.CommandStatus{ Name: repoCfg.Name, @@ -100,7 +116,7 @@ func TestParseApplication(t *testing.T) { } } -func Example_test_sync_output() { +func TestOutputFromSync(t *testing.T) { grm := GitRepositoryManager{ configuration: config.Configuration{ Workspace: "/tmp", @@ -108,19 +124,20 @@ func Example_test_sync_output() { cliArguments: config.CliArguments{ Sync: true, Version: true, - }, - console: ConsoleOutput{ - Color: false, + Color: false, }, } - grm.Run() - // Output: - // Info: Synchronizing repositories - // Info: All repositories are synced - // Info: Current version: 0.3.0 + emt := ExpectedMessageTester{ + expectedMessages: []string{ + "Info: Synchronizing repositories\n", + "Info: All repositories are synced\n", + fmt.Sprintf("Info: Current version: %v\n", VERSION), + }, + } + grm.Run(emt) } -func Example_limit_test_tags() { +func TestLimitTags(t *testing.T) { grm := GitRepositoryManager{ cliArguments: config.CliArguments{ LimitTags: []string{"example"}, @@ -132,22 +149,24 @@ func Example_limit_test_tags() { {Name: "notExample"}, }, }, - console: ConsoleOutput{ - Color: false, - }, } fakeCommand := FakeCommandToTest{ triggerError: false, triggerChanged: false, } + emt := ExpectedMessageTester{ + expectedMessages: []string{ + "Repository \"example1\": response from fake command\n", + "Repository \"example2\": response from fake command\n", + }, + } + echo.Color(false) + echo.Output(emt) grm.limitTags() grm.runCommand(fakeCommand) - // Output: - // Repository "example1": response from fake command - // Repository "example2": response from fake command } -func Example_limit_name() { +func TestLimitName(t *testing.T) { grm := GitRepositoryManager{ cliArguments: config.CliArguments{ LimitName: "notExample", @@ -159,21 +178,22 @@ func Example_limit_name() { {Name: "notExample"}, }, }, - console: ConsoleOutput{ - Color: false, - }, } fakeCommand := FakeCommandToTest{ triggerError: false, triggerChanged: false, } + emt := ExpectedMessageTester{ + expectedMessages: []string{ + "Repository \"notExample\": response from fake command\n", + }, + } + echo.Color(false) + echo.Output(emt) grm.limitName() grm.runCommand(fakeCommand) - // Output: - // Repository "notExample": response from fake command } - -func Example_run_with_limit_not_existing_name() { +func TestRunWithNotExistingNameInLimit(t *testing.T) { grm := GitRepositoryManager{ cliArguments: config.CliArguments{ LimitName: "not-existing-name", @@ -185,16 +205,20 @@ func Example_run_with_limit_not_existing_name() { {Name: "notExample"}, }, }, - console: ConsoleOutput{ - Color: false, + } + emt := ExpectedMessageTester{ + expectedMessages: []string{ + "Error: no repository was found with the specified name\n", }, } - grm.Run() - // Output: - // Error: no repository was found with the specified name + echo.Color(false) + status := grm.Run(emt) + if status != 1 { + t.Errorf("Expected to get status %v, instead o this got %v", 1, status) + } } -func Example_run_with_limit_not_existing_tags() { +func TestRunWithNotExistingTagsInLimit(t *testing.T) { grm := GitRepositoryManager{ cliArguments: config.CliArguments{ LimitTags: []string{"not-existing-tag"}, @@ -206,16 +230,20 @@ func Example_run_with_limit_not_existing_tags() { {Name: "notExample"}, }, }, - console: ConsoleOutput{ - Color: false, + } + emt := ExpectedMessageTester{ + expectedMessages: []string{ + "Error: no repository was found with the specified tags\n", }, } - grm.Run() - // Output: - // Error: no repository was found with the specified tags + echo.Color(false) + status := grm.Run(emt) + if status != 1 { + t.Errorf("Expected to get status %v, instead o this got %v", 1, status) + } } -func Example_test_status_output() { +func TestGetStatusOutput(t *testing.T) { grm := GitRepositoryManager{ configuration: config.Configuration{ Workspace: "/tmp", @@ -223,11 +251,17 @@ func Example_test_status_output() { cliArguments: config.CliArguments{ Status: true, }, - console: ConsoleOutput{ - Color: false, + } + emt := ExpectedMessageTester{ + expectedMessages: []string{ + "Info: Current status of repositories\n", }, } - grm.Run() + echo.Color(false) + status := grm.Run(emt) + if status != 0 { + t.Errorf("Expected to get status %v, instead o this got %v", 1, status) + } // Output: // Info: Current status of repositories } diff --git a/app/console_output.go b/app/console_output.go deleted file mode 100644 index 1f8d0a1..0000000 --- a/app/console_output.go +++ /dev/null @@ -1,61 +0,0 @@ -package app - -import ( - "fmt" -) - -const ( - colorReset = "\033[0m" - colorRed = "\033[31m" - colorGreen = "\033[32m" - colorYellow = "\033[33m" - colorBlue = "\033[34m" -) - -type ConsoleOutput struct { - Color bool -} - -func (co ConsoleOutput) ErrorfMsg(format string, a ...interface{}) { - msg := fmt.Sprintf(format, a...) - if co.Color { - msg = fmt.Sprintf("%vError:%v %v", colorRed, colorReset, msg) - } else { - msg = fmt.Sprintf("Error: %v", msg) - } - fmt.Println(msg) -} - -func (co ConsoleOutput) InfoFMsg(format string, a ...interface{}) { - msg := fmt.Sprintf(format, a...) - if co.Color { - msg = fmt.Sprintf("%vInfo:%v %v", colorBlue, colorReset, msg) - } else { - msg = fmt.Sprintf("Info: %v", msg) - } - fmt.Println(msg) -} - -func (co ConsoleOutput) UnchangedStatusF(format string, a ...interface{}) { - msg := fmt.Sprintf(format, a...) - if co.Color { - msg = fmt.Sprintf("%v%v%v", colorGreen, msg, colorReset) - } - fmt.Println(msg) -} - -func (co ConsoleOutput) ChangedStatusF(format string, a ...interface{}) { - msg := fmt.Sprintf(format, a...) - if co.Color { - msg = fmt.Sprintf("%v%v%v", colorYellow, msg, colorReset) - } - fmt.Println(msg) -} - -func (co ConsoleOutput) ErrorStatusF(format string, a ...interface{}) { - msg := fmt.Sprintf(format, a...) - if co.Color { - msg = fmt.Sprintf("%v%v%v", colorRed, msg, colorReset) - } - fmt.Println(msg) -} diff --git a/echo/echo.go b/echo/echo.go new file mode 100644 index 0000000..3449a64 --- /dev/null +++ b/echo/echo.go @@ -0,0 +1,72 @@ +package echo + +import ( + "fmt" + "io" + "os" +) + +const ( + colorReset = "\033[0m" + colorRed = "\033[31m" + colorGreen = "\033[32m" + colorYellow = "\033[33m" + colorBlue = "\033[34m" +) + +var ( + useColor bool = false + output io.Writer = os.Stdout +) + +func Color(enabled bool) { + useColor = enabled +} + +func Output(writer io.Writer) { + output = writer +} + +func ErrorfMsg(format string, a ...interface{}) error { + msg := fmt.Sprintf(format, a...) + if useColor { + msg = fmt.Sprintf("%vError:%v %v", colorRed, colorReset, msg) + } else { + msg = fmt.Sprintf("Error: %v", msg) + } + return write(msg) +} + +func InfoFMsg(format string, a ...interface{}) error { + msg := fmt.Sprintf(format, a...) + if useColor { + msg = fmt.Sprintf("%vInfo:%v %v", colorBlue, colorReset, msg) + } else { + msg = fmt.Sprintf("Info: %v", msg) + } + return write(msg) +} + +func GreenMessageF(format string, a ...interface{}) error { + return writeWithColor(fmt.Sprintf(format, a...), colorGreen) +} + +func YellowMessageF(format string, a ...interface{}) error { + return writeWithColor(fmt.Sprintf(format, a...), colorYellow) +} +func RedMessageF(format string, a ...interface{}) error { + return writeWithColor(fmt.Sprintf(format, a...), colorRed) +} + +func writeWithColor(msg string, color string) error { + if useColor { + return write(fmt.Sprintf("%v%v%v", color, msg, colorReset)) + } + return write(msg) + +} + +func write(msg string) error { + _, err := fmt.Fprintln(output, msg) + return err +} diff --git a/echo/echo_test.go b/echo/echo_test.go new file mode 100644 index 0000000..00b4d08 --- /dev/null +++ b/echo/echo_test.go @@ -0,0 +1,151 @@ +package echo + +import ( + "fmt" + "os" + "testing" +) + +type ExpectedMessageTester struct { + expectedMessage string +} + +func (emt ExpectedMessageTester) Write(p []byte) (n int, err error) { + msg := string(p) + if msg != emt.expectedMessage { + return 0, fmt.Errorf("expected to get \"%v\", instead of this got \"%v\"", msg, emt.expectedMessage) + } + + return 0, nil +} + +func TestOverwriteColor(t *testing.T) { + Color(false) + if useColor { + t.Error("Expected that \"useColor\" will be false") + } + Color(true) + if !useColor { + t.Error("Expected that \"useColor\" will be true") + } +} + +func TestOverwriteWriter(t *testing.T) { + Output(os.Stderr) + if output != os.Stderr { + t.Error("Expected to receive addresses on os.Stderr") + } + Output(os.Stdout) + if output != os.Stdout { + t.Error("Expected to receive addresses on os.Stdout") + } +} + +func TestErrorfMsgWithoutColor(t *testing.T) { + useColor = false + output = ExpectedMessageTester{ + expectedMessage: "Error: test message\n", + } + err := ErrorfMsg("test message") + if err != nil { + t.Error(err) + } +} + +func TestErrorfMsgWithColor(t *testing.T) { + useColor = true + output = ExpectedMessageTester{ + expectedMessage: "\033[31mError:\033[0m test message\n", + } + err := ErrorfMsg("test message") + if err != nil { + t.Error(err) + } +} + +func TestInfoMsgFWithoutColor(t *testing.T) { + useColor = false + output = ExpectedMessageTester{ + expectedMessage: "Info: test message\n", + } + err := InfoFMsg("test message") + if err != nil { + t.Error(err) + } +} + +func TestInfoMsgFWithColor(t *testing.T) { + useColor = true + output = ExpectedMessageTester{ + expectedMessage: "\033[34mInfo:\033[0m test message\n", + } + err := InfoFMsg("test message") + if err != nil { + t.Error(err) + } +} + +func TestGreenMessageWithoutColor(t *testing.T) { + useColor = false + output = ExpectedMessageTester{ + expectedMessage: "test message\n", + } + err := GreenMessageF("test message") + if err != nil { + t.Error(err) + } +} + +func TestGreenMessageWithColor(t *testing.T) { + useColor = true + output = ExpectedMessageTester{ + expectedMessage: "\033[32mtest message\033[0m\n", + } + err := GreenMessageF("test message") + if err != nil { + t.Error(err) + } +} + +func TestYellowMessageWithout(t *testing.T) { + useColor = false + output = ExpectedMessageTester{ + expectedMessage: "test message\n", + } + err := YellowMessageF("test message") + if err != nil { + t.Error(err) + } +} + +func TestYellowMessageWithColor(t *testing.T) { + useColor = true + output = ExpectedMessageTester{ + expectedMessage: "\033[33mtest message\033[0m\n", + } + err := YellowMessageF("test message") + if err != nil { + t.Error(err) + } +} + +func TestRedMessageWithout(t *testing.T) { + useColor = false + output = ExpectedMessageTester{ + expectedMessage: "test message\n", + } + err := RedMessageF("test message") + if err != nil { + t.Error(err) + } +} +func TestRedMessageWithColor(t *testing.T) { + useColor = true + output = ExpectedMessageTester{ + expectedMessage: "\033[31mtest message\033[0m\n", + } + err := RedMessageF("test message") + if err != nil { + t.Error(err) + } +} diff --git a/main.go b/main.go index af24735..ccdca77 100644 --- a/main.go +++ b/main.go @@ -12,5 +12,6 @@ func main() { app := app.GitRepositoryManager{} app.Parse(os.Args) - os.Exit(app.Run()) + exitCode := app.Run(os.Stdout) + os.Exit(exitCode) }