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 }