package tortoise_test import ( "sync" "testing" "time" "git.iamthefij.com/iamthefij/tortoise" ) func TestShellRunnerNoCallback(t *testing.T) { t.Parallel() cases := []struct { command string output string ReturnCode int }{ {"echo hello world", "hello world\n", 0}, {"echo hello world && exit 1", "hello world\n", 1}, } for _, c := range cases { c := c t.Run(c.command, func(t *testing.T) { t.Parallel() runner := tortoise.NewShellRunner() runner.Start() // Test command without callback if err := runner.AddCommand(c.command, nil); err != nil { t.Fatalf("unexpected error adding command: %v", err) } runner.Stop() result := runner.GetResults() if result == nil || result.Output != c.output || result.ReturnCode != c.ReturnCode { t.Fatalf("expected output '%s' and return code %d, got '%s' and %d", c.output, c.ReturnCode, result.Output, result.ReturnCode) } }) } } func TestShellRunnerCallback(t *testing.T) { t.Parallel() runner := tortoise.NewShellRunner() runner.Start() // Test command with callback outputString := "" callbackWait := sync.WaitGroup{} callbackWait.Add(1) if err := runner.AddCommand("echo callback a", func(result *tortoise.CommandResult) { if result.Output != "callback a\n" { t.Fatalf("expected 'callback a', got '%s'", result.Output) } outputString = outputString + "a" callbackWait.Done() }); err != nil { t.Fatalf("unexpected error adding command: %v", err) } callbackWait.Add(1) if err := runner.AddCommand("echo callback b", func(result *tortoise.CommandResult) { if result.Output != "callback b\n" { t.Fatalf("expected 'callback b', got '%s'", result.Output) } outputString = outputString + "b" callbackWait.Done() }); err != nil { t.Fatalf("unexpected error adding command: %v", err) } // Timeout waiting for callbacks done := make(chan struct{}) go func() { callbackWait.Wait() close(done) }() select { case <-done: case <-time.After(2 * time.Second): t.Fatal("callbacks timed out") } if outputString != "ab" { t.Fatal("callbacks was not reached in order:", outputString) } runner.Stop() // Make sure stop and kill both exit gracefully after the runner is stopped runner.Stop() runner.Kill() } func TestShellRunnerKillWithTimeout(t *testing.T) { t.Parallel() runner := tortoise.NewShellRunner() runner.Start() // Test command with callback callbackReached := false if err := runner.AddCommand("sleep 10 && echo callback test", func(result *tortoise.CommandResult) { callbackReached = true if result.Output != "callback test\n" { t.Fatalf("expected 'callback test', got '%s'", result.Output) } }); err != nil { t.Fatalf("unexpected error adding command: %v", err) } // Wait one second to make sure the command starts running time.Sleep(1 * time.Second) if err := runner.KillWithTimeout(1 * time.Second); err == nil { t.Fatal("expected error when killing commands, but got none") } if callbackReached { t.Fatal("callback was reached before kill") } } func TestStopPreventsNewCommands(t *testing.T) { runner := tortoise.NewShellRunner() runner.Start() runner.Stop() err := runner.AddCommand("echo should not run", nil) if err == nil { t.Fatal("expected error when adding command after stop, but got none") } } func TestAddingPriorToStart(t *testing.T) { runner := tortoise.NewShellRunner() err := runner.AddCommand("echo should not run", nil) if err == nil { t.Fatal("Should have failed to add prior to starting runner") } }