package multistep import ( "os" "reflect" "testing" "time" ) func TestDebugRunner_Impl(t *testing.T) { var raw interface{} raw = &DebugRunner{} if _, ok := raw.(Runner); !ok { t.Fatal("DebugRunner must be a runner.") } } func TestDebugRunner_Run(t *testing.T) { data := new(BasicStateBag) stepA := &TestStepAcc{Data: "a"} stepB := &TestStepAcc{Data: "b"} pauseFn := func(loc DebugLocation, name string, state StateBag) { key := "data" if loc == DebugLocationBeforeCleanup { key = "cleanup" } if _, ok := state.GetOk(key); !ok { state.Put(key, make([]string, 0, 5)) } data := state.Get(key).([]string) state.Put(key, append(data, name)) } r := &DebugRunner{ Steps: []Step{stepA, stepB}, PauseFn: pauseFn, } r.Run(data) // Test data expected := []string{"a", "TestStepAcc", "b", "TestStepAcc"} results := data.Get("data").([]string) if !reflect.DeepEqual(results, expected) { t.Errorf("unexpected results: %#v", results) } // Test cleanup expected = []string{"TestStepAcc", "b", "TestStepAcc", "a"} results = data.Get("cleanup").([]string) if !reflect.DeepEqual(results, expected) { t.Errorf("unexpected results: %#v", results) } } // confirm that can't run twice func TestDebugRunner_Run_Run(t *testing.T) { defer func() { recover() }() ch := make(chan chan bool) stepInt := &TestStepSync{ch} stepWait := &TestStepWaitForever{} r := &DebugRunner{Steps: []Step{stepInt, stepWait}} go r.Run(new(BasicStateBag)) // wait until really running <-ch // now try to run aain r.Run(new(BasicStateBag)) // should not get here in nominal codepath t.Errorf("Was able to run an already running DebugRunner") } func TestDebugRunner_Cancel(t *testing.T) { ch := make(chan chan bool) data := new(BasicStateBag) stepA := &TestStepAcc{Data: "a"} stepB := &TestStepAcc{Data: "b"} stepInt := &TestStepSync{ch} stepC := &TestStepAcc{Data: "c"} r := &DebugRunner{} r.Steps = []Step{stepA, stepB, stepInt, stepC} // cancelling an idle Runner is a no-op r.Cancel() go r.Run(data) // Wait until we reach the sync point responseCh := <-ch // Cancel then continue chain cancelCh := make(chan bool) go func() { r.Cancel() cancelCh <- true }() for { if _, ok := data.GetOk(StateCancelled); ok { responseCh <- true break } time.Sleep(10 * time.Millisecond) } <-cancelCh // Test run data expected := []string{"a", "b"} results := data.Get("data").([]string) if !reflect.DeepEqual(results, expected) { t.Errorf("unexpected result: %#v", results) } // Test cleanup data expected = []string{"b", "a"} results = data.Get("cleanup").([]string) if !reflect.DeepEqual(results, expected) { t.Errorf("unexpected result: %#v", results) } // Test that it says it is cancelled cancelled := data.Get(StateCancelled).(bool) if !cancelled { t.Errorf("not cancelled") } } func TestDebugPauseDefault(t *testing.T) { // Create a pipe pair so that writes/reads are blocked until we do it r, w, err := os.Pipe() if err != nil { t.Fatalf("err: %s", err) } // Set stdin so we can control it oldStdin := os.Stdin os.Stdin = r defer func() { os.Stdin = oldStdin }() // Start pausing complete := make(chan bool, 1) go func() { dr := &DebugRunner{Steps: []Step{ &TestStepAcc{Data: "a"}, }} dr.Run(new(BasicStateBag)) complete <- true }() select { case <-complete: t.Fatal("shouldn't have completed") case <-time.After(100 * time.Millisecond): } w.Write([]byte("\n\n")) select { case <-complete: case <-time.After(100 * time.Millisecond): t.Fatal("didn't complete") } }