diff --git a/channel/channel_test.go b/channel/channel_test.go index 3e73b4d..bf1ba79 100644 --- a/channel/channel_test.go +++ b/channel/channel_test.go @@ -76,6 +76,7 @@ func prepareChannel( t *testing.T, testName, payloadFile string, + chanOpts ...util.Option, ) (*channel.Channel, *transport.File) { l, _ := logging.NewInstance() @@ -90,7 +91,7 @@ func prepareChannel( t.Errorf("%s: encountered error creating File Transport, error: %s", testName, err) } - c, err := channel.NewChannel(l, transportObj) + c, err := channel.NewChannel(l, transportObj, chanOpts...) if err != nil { t.Errorf("%s: encountered error creating Channel, error: %s", testName, err) } diff --git a/channel/read.go b/channel/read.go index 5ce07cc..283060b 100644 --- a/channel/read.go +++ b/channel/read.go @@ -161,7 +161,7 @@ func (c *Channel) ReadAll() ([]byte, error) { } // ReadUntilFuzzy reads until a fuzzy match of the input is found. -func (c *Channel) ReadUntilFuzzy(b []byte) ([]byte, error) { +func (c *Channel) ReadUntilFuzzy(ctx context.Context, b []byte) ([]byte, error) { if len(b) == 0 { return nil, nil } @@ -169,6 +169,12 @@ func (c *Channel) ReadUntilFuzzy(b []byte) ([]byte, error) { var rb []byte for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + nb, err := c.Read() if err != nil { return nil, err @@ -193,10 +199,16 @@ func (c *Channel) ReadUntilFuzzy(b []byte) ([]byte, error) { // ReadUntilExplicit reads bytes out of the channel Q object until the bytes b are seen in the // output. Once the bytes are seen all read bytes are returned. -func (c *Channel) ReadUntilExplicit(b []byte) ([]byte, error) { +func (c *Channel) ReadUntilExplicit(ctx context.Context, b []byte) ([]byte, error) { var rb []byte for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + nb, err := c.Read() if err != nil { return nil, err diff --git a/channel/sendinput.go b/channel/sendinput.go index d2e7385..d84a1e7 100644 --- a/channel/sendinput.go +++ b/channel/sendinput.go @@ -42,7 +42,7 @@ func (c *Channel) SendInputB(input []byte, opts ...util.Option) ([]byte, error) return } - _, err = readUntilF(input) + _, err = readUntilF(ctx, input) if err != nil { cr <- &result{b: b, err: err} diff --git a/channel/sendinput_test.go b/channel/sendinput_test.go index ebb0697..a6ededa 100644 --- a/channel/sendinput_test.go +++ b/channel/sendinput_test.go @@ -4,8 +4,10 @@ import ( "bytes" "fmt" "testing" + "time" "github.com/scrapli/scrapligo/driver/opoptions" + "github.com/scrapli/scrapligo/driver/options" "github.com/scrapli/scrapligo/util" @@ -132,3 +134,30 @@ func TestSendInput(t *testing.T) { t.Run(testName, f) } } + +func TestSendCommandTimeout(t *testing.T) { + input := readFile(t, "send-input-timeout-in.txt") + + c, _ := prepareChannel( + t, + t.Name(), + "send-input-timeout-out.txt", + options.WithTimeoutOps(1*time.Millisecond), + ) + _, err := c.SendInput(string(input)) + + if err == nil { + t.Fatalf("%s: expecting an error but got none", t.Name()) + } + + expectedErr := "errTimeoutError: channel timeout sending input to device" + + if err.Error() != expectedErr { + t.Fatalf( + "%s: actual and expected errors do not match\nactual: %s\nexpected:%s", + t.Name(), + err.Error(), + expectedErr, + ) + } +} diff --git a/channel/sendinteractive.go b/channel/sendinteractive.go index b20dd40..771aef3 100644 --- a/channel/sendinteractive.go +++ b/channel/sendinteractive.go @@ -24,7 +24,7 @@ func (c *Channel) sendInteractive( cr chan *result, events []*SendInteractiveEvent, op *OperationOptions, - readUntilF func(b []byte) ([]byte, error), + readUntilF func(ctx context.Context, b []byte) ([]byte, error), ) { defer close(cr) @@ -48,7 +48,7 @@ func (c *Channel) sendInteractive( if e.ChannelResponse != "" && !e.HideInput { var nb []byte - nb, err = readUntilF([]byte(e.ChannelInput)) + nb, err = readUntilF(ctx, []byte(e.ChannelInput)) if err != nil { cr <- &result{b: nil, err: err} diff --git a/channel/test-fixtures/send-input-timeout-in.txt b/channel/test-fixtures/send-input-timeout-in.txt new file mode 100644 index 0000000..363b4f0 --- /dev/null +++ b/channel/test-fixtures/send-input-timeout-in.txt @@ -0,0 +1 @@ +show running-config diff --git a/channel/test-fixtures/send-input-timeout-out.txt b/channel/test-fixtures/send-input-timeout-out.txt new file mode 100644 index 0000000..42681a2 --- /dev/null +++ b/channel/test-fixtures/send-input-timeout-out.txt @@ -0,0 +1,3 @@ +RP/0/RP0/CPU0:iosxr75#terminal width 512 +RP/0/RP0/CPU0:iosxr75#terminal length 0 +RP/0/RP0/CPU0:iosxr75#show running-co