Repository: SimonBaeumer/commander (go)

Issue:
Add unit parsing for timeouts
It would be nice if parsing timeouts could add untis like this `20ms`.

See `time.ParseDuration`

Pull request description:
Add unit parsing for timeout config
fixes #38

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9572036..12f6d69 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,9 @@
 # v1.0.0
 
  - Add `add` command which automatically adds tests to your test suite
- - Add `retries` to test configs
+ - Changes to `config` properties
+    - Add `retries` to test configs
+    - Add time units to `timeout` config
 
 # v0.4.0
 
diff --git a/README.md b/README.md
index f84c105..9cb3f96 100644
--- a/README.md
+++ b/README.md
@@ -8,22 +8,12 @@
 
 `Commander` is an alternative to `bats` and `gotest.tools/icmd` for testing cli apps.
 
+ - It runs on `windows`, `osx` and `linux` 
+ - It is a self-contained binary - no need to install a heavy lib or language
+ - It is easy and fast to write
+ 
 [![asciicast](https://asciinema.org/a/GLgIexHSaSdic5iaX9GTQpr5f.svg)](https://asciinema.org/a/GLgIexHSaSdic5iaX9GTQpr5f?autoplay=1)
 
- - It runs on `windows`, `osx` and `linux`
- - It is a self-contained binary
- - It is easy - just run `commander add`
-
-
-- [Installation](#installation)
-  * [Linux and osx](#linux-and-osx)
-  * [Windows](#windows)
-- [Example](#example)
-- [Minimal test](#minimal-test)
-- [Usage](#usage)
-- [Development](#development)
-- [Misc](#misc)
- 
 For more information take a look at the [manual](docs/manual.md), the [examples](examples) or the [integration tests](integration).
 
 ## Installation
diff --git a/integration/unix/failing_suite.yaml b/integration/unix/failing_suite.yaml
index 374a869..36eab02 100644
--- a/integration/unix/failing_suite.yaml
+++ b/integration/unix/failing_suite.yaml
@@ -6,5 +6,5 @@ tests:
   test timeout:
     command: sleep 1
     config:
-      timeout: 10 #in milliseconds
+      timeout: 10ms
     exit-code: 0
\ No newline at end of file
diff --git a/integration/windows/failing_suite.yaml b/integration/windows/failing_suite.yaml
index 39d32e4..e39fee4 100644
--- a/integration/windows/failing_suite.yaml
+++ b/integration/windows/failing_suite.yaml
@@ -6,5 +6,5 @@ tests:
   test timeout:
     command: timeout 1
     config:
-      timeout: 10 #in milliseconds
+      timeout: 10ms
     exit-code: 0
\ No newline at end of file
diff --git a/pkg/app/add_command.go b/pkg/app/add_command.go
index 5bf8856..5be749d 100644
--- a/pkg/app/add_command.go
+++ b/pkg/app/add_command.go
@@ -58,7 +58,7 @@ func stringOrNil(str string) interface{} {
 }
 
 func convertConfig(config suite.YAMLTestConfig) suite.YAMLTestConfig {
-	if config.Dir == "" && len(config.Env) == 0 && config.Timeout == 0 {
+	if config.Dir == "" && len(config.Env) == 0 && config.Timeout == "" {
 		return suite.YAMLTestConfig{}
 	}
 	return config
diff --git a/pkg/cmd/command.go b/pkg/cmd/command.go
index f954fba..648f825 100644
--- a/pkg/cmd/command.go
+++ b/pkg/cmd/command.go
@@ -44,6 +44,18 @@ func (c *Command) SetTimeoutMS(ms int) {
 	c.Timeout = time.Duration(ms) * time.Millisecond
 }
 
+// SetTimeout sets the timeout given a time unit
+// Example: SetTimeout("100s") sets the timeout to 100 seconds
+func (c *Command) SetTimeout(timeout string) error {
+	d, err := time.ParseDuration(timeout)
+	if err != nil {
+		return err
+	}
+
+	c.Timeout = d
+	return nil
+}
+
 //Stdout returns the output to stdout
 func (c *Command) Stdout() string {
 	c.isExecuted("Stdout")
diff --git a/pkg/runtime/runtime.go b/pkg/runtime/runtime.go
index d4c47e7..03a7e15 100644
--- a/pkg/runtime/runtime.go
+++ b/pkg/runtime/runtime.go
@@ -38,7 +38,7 @@ type TestCase struct {
 type TestConfig struct {
 	Env     map[string]string
 	Dir     string
-	Timeout int
+	Timeout string
 	Retries int
 }
 
@@ -76,7 +76,7 @@ type CommandUnderTest struct {
 	Cmd     string
 	Env     map[string]string
 	Dir     string
-	Timeout int
+	Timeout string
 	Retries int
 }
 
@@ -136,7 +136,7 @@ func Start(tests []TestCase, maxConcurrent int) <-chan TestResult {
 func runTest(test TestCase) TestResult {
 	// cut = command under test
 	cut := cmd.NewCommand(test.Command.Cmd)
-	cut.SetTimeoutMS(test.Command.Timeout)
+	cut.SetTimeout(test.Command.Timeout)
 	cut.Dir = test.Command.Dir
 	for k, v := range test.Command.Env {
 		cut.AddEnv(k, v)
diff --git a/pkg/suite/yaml_suite.go b/pkg/suite/yaml_suite.go
index 7821118..793b4d8 100644
--- a/pkg/suite/yaml_suite.go
+++ b/pkg/suite/yaml_suite.go
@@ -17,7 +17,7 @@ type YAMLConfig struct {
 type YAMLTestConfig struct {
 	Env     map[string]string `yaml:"env,omitempty"`
 	Dir     string            `yaml:"dir,omitempty"`
-	Timeout int               `yaml:"timeout,omitempty"`
+	Timeout string            `yaml:"timeout,omitempty"`
 	Retries int               `yaml:"retries,omitempty"`
 }
 
@@ -218,7 +218,7 @@ func (y *YAMLConfig) mergeConfigs(local YAMLTestConfig, global YAMLTestConfig) Y
 		conf.Dir = local.Dir
 	}
 
-	if local.Timeout != 0 {
+	if local.Timeout != "" {
 		conf.Timeout = local.Timeout
 	}
 


Test patch:
diff --git a/integration/unix/config_test.yaml b/integration/unix/config_test.yaml
index f60c4de..abec91a 100644
--- a/integration/unix/config_test.yaml
+++ b/integration/unix/config_test.yaml
@@ -26,5 +26,5 @@ tests:
   it should work with timeout:
     command: echo hello
     config:
-      timeout: 100
+      timeout: 100ms
     exit-code: 0
\ No newline at end of file
diff --git a/integration/windows/config_test.yaml b/integration/windows/config_test.yaml
index 486a803..fccdf42 100644
--- a/integration/windows/config_test.yaml
+++ b/integration/windows/config_test.yaml
@@ -30,5 +30,5 @@ tests:
   should work with timeout:
     command: echo hello
     config:
-      timeout: 100
+      timeout: 100ms
     exit-code: 0
\ No newline at end of file
diff --git a/pkg/cmd/command_test.go b/pkg/cmd/command_test.go
index d869c7a..6f94724 100644
--- a/pkg/cmd/command_test.go
+++ b/pkg/cmd/command_test.go
@@ -75,3 +75,16 @@ func TestCommand_SetTimeoutMS(t *testing.T) {
 	c.SetTimeoutMS(100)
 	assert.Equal(t, 100*time.Millisecond, c.Timeout)
 }
+
+func TestCommand_SetTimeout(t *testing.T) {
+	c := NewCommand("echo test")
+	_ = c.SetTimeout("100s")
+	duration, _ := time.ParseDuration("100s")
+	assert.Equal(t, duration, c.Timeout)
+}
+
+func TestCommand_SetInvalidTimeout(t *testing.T) {
+	c := NewCommand("echo test")
+	err := c.SetTimeout("1")
+	assert.Equal(t, "time: missing unit in duration 1", err.Error())
+}
diff --git a/pkg/runtime/runtime_test.go b/pkg/runtime/runtime_test.go
index 9b878d9..baba5a7 100644
--- a/pkg/runtime/runtime_test.go
+++ b/pkg/runtime/runtime_test.go
@@ -49,7 +49,7 @@ func TestRuntime_WithEnvVariables(t *testing.T) {
 	s := TestCase{
 		Command: CommandUnderTest{
 			Cmd:     fmt.Sprintf("echo %s", envVar),
-			Timeout: 50,
+			Timeout: "50ms",
 			Env:     map[string]string{"KEY": "value"},
 		},
 		Expected: Expected{
@@ -87,7 +87,7 @@ func getExampleTestSuite() []TestCase {
 		{
 			Command: CommandUnderTest{
 				Cmd:     "echo hello",
-				Timeout: 50,
+				Timeout: "50ms",
 			},
 			Expected: Expected{
 				Stdout: ExpectedOut{
diff --git a/pkg/suite/yaml_suite_test.go b/pkg/suite/yaml_suite_test.go
index 413d376..19083ed 100644
--- a/pkg/suite/yaml_suite_test.go
+++ b/pkg/suite/yaml_suite_test.go
@@ -179,7 +179,7 @@ config:
         KEY: global
         ANOTHER_KEY: another_global
     dir: /home/commander/
-    timeout: 10
+    timeout: 10ms
     retries: 2
 
 tests:
@@ -189,19 +189,19 @@ tests:
            env:
                KEY: local
            dir: /home/test
-           timeout: 1
+           timeout: 1s
            retries: 10
 `)
 
 	got := ParseYAML(yaml)
 	assert.Equal(t, map[string]string{"KEY": "global", "ANOTHER_KEY": "another_global"}, got.GetGlobalConfig().Env)
 	assert.Equal(t, "/home/commander/", got.GetGlobalConfig().Dir)
-	assert.Equal(t, 10, got.GetGlobalConfig().Timeout)
+	assert.Equal(t, "10ms", got.GetGlobalConfig().Timeout)
 	assert.Equal(t, 2, got.GetGlobalConfig().Retries)
 
 	assert.Equal(t, map[string]string{"KEY": "local", "ANOTHER_KEY": "another_global"}, got.GetTests()[0].Command.Env)
 	assert.Equal(t, "/home/test", got.GetTests()[0].Command.Dir)
-	assert.Equal(t, 1, got.GetTests()[0].Command.Timeout)
+	assert.Equal(t, "1s", got.GetTests()[0].Command.Timeout)
 	assert.Equal(t, 10, got.GetTests()[0].Command.Retries)
 }