github.com/haraldrudell/parl@v0.4.176/pio/copy-context_test.go (about) 1 /* 2 © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package pio 7 8 import ( 9 "bytes" 10 "context" 11 "errors" 12 "io" 13 "testing" 14 15 "github.com/haraldrudell/parl" 16 "github.com/haraldrudell/parl/perrors" 17 ) 18 19 func TestCopyContext(t *testing.T) { 20 //t.Fail() 21 22 var text = []byte("Hello, World") 23 var noWrite = 0 24 25 var written int64 26 var err error 27 // has ReadFrom and WriteTo but not [io.Closable] 28 var reader io.Reader 29 // has ReadFrom and WriteTo but not [io.Closable] 30 var writer io.Writer 31 var ctx context.Context 32 var cancelFunc context.CancelFunc 33 var noBuffer []byte 34 35 // a copy that completes should not error 36 // - can use bytes.NewReader 37 // - can use bytes.Buffer, this is common with exec.Cmd 38 reader = bytes.NewBuffer(text) 39 writer = &bytes.Buffer{} 40 ctx = context.Background() 41 // uses WriterTo 42 written, err = CopyContext(writer, reader, noBuffer, ctx) 43 if err != nil { 44 t.Errorf("copyComplete err: %s", perrors.Short(err)) 45 } 46 if int(written) != len(text) { 47 t.Errorf("copyComplete written: %d exp %d", written, len(text)) 48 } 49 50 // cancel via thread should not write and instead return error 51 ctx = context.Background() 52 ctx = parl.AddNotifier1(ctx, func(stack parl.Stack) { 53 t.Log("contextCancelListener") 54 var _ parl.NotifierFunc 55 }) 56 ctx, cancelFunc = context.WithCancel(ctx) 57 reader = NewBReadCloser( 58 bytes.NewBuffer(text), 59 cancelFunc, 60 t, 61 ) 62 writer = NewBWriteCloser(&bytes.Buffer{}, t) 63 t.Logf("context is 0x%x", parl.Uintptr(ctx)) 64 written, err = CopyContext(writer, reader, noBuffer, ctx) 65 t.Logf("cancel CopyContext: written: %d err: %s", written, perrors.Short(err)) 66 if err == nil { 67 t.Error("cancelThread missing error") 68 } else if !errors.Is(err, context.Canceled) { 69 t.Errorf("cancelThread bad err: %s", perrors.Long(err)) 70 } 71 if int(written) != noWrite { 72 t.Errorf("copyComplete written: %d exp %d", written, noWrite) 73 } 74 75 } 76 77 // BReadCloser invokes cancelFunc on Read 78 // - is [io.Closable] to get thread 79 // - does not implement Write To 80 type BReadCloser struct { 81 b *bytes.Buffer 82 cancelFunc context.CancelFunc 83 t *testing.T 84 } 85 86 // BReadCloser invokes cancelFunc on Read 87 // - is [io.Closable] to get thread 88 // - does not implement Write To 89 func NewBReadCloser( 90 b *bytes.Buffer, 91 cancelFunc context.CancelFunc, 92 t *testing.T, 93 ) (b2 *BReadCloser) { 94 return &BReadCloser{ 95 b: b, 96 cancelFunc: cancelFunc, 97 t: t, 98 } 99 } 100 101 // Read invokes cancelFunc 102 func (b *BReadCloser) Read(p []byte) (n int, err error) { 103 b.t.Log("Read invoking cancelFunc") 104 b.cancelFunc() 105 return b.b.Read(p) 106 } 107 108 // Close makes [io.Closable] 109 func (b *BReadCloser) Close() (err error) { return } 110 111 // BWriteCloser logs if Write is invoked 112 // - does not implement readFrom 113 type BWriteCloser struct { 114 b *bytes.Buffer 115 t *testing.T 116 } 117 118 // BWriteCloser logs if Write is invoked 119 // - does not implement readFrom 120 func NewBWriteCloser(b *bytes.Buffer, t *testing.T) (b2 *BWriteCloser) { 121 return &BWriteCloser{b: b, t: t} 122 } 123 124 // BWriteCloser logs if Write is invoked 125 func (b *BWriteCloser) Write(p []byte) (n int, err error) { 126 b.t.Log("Write") 127 return b.b.Write(p) 128 } 129 130 // Close makes [io.Closable] 131 func (b *BWriteCloser) Close() (err error) { return }