aboutsummaryrefslogtreecommitdiffhomepage
path: root/message/output_test.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-10-09 05:04:08 +0900
committerOphestra <cat@gensokyo.uk>2025-10-09 05:18:19 +0900
commit87b5c30ef6d5b7b8a10d6a5a8610054c403923e6 (patch)
tree805116c39a623bb30fc532af47ea3ed78b7304c0 /message/output_test.go
parentdf9b77b077d967d35082adb8ab3b00dd67d7bc97 (diff)
message: relocate from container
This package is quite useful. This change allows it to be imported without importing container. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'message/output_test.go')
-rw-r--r--message/output_test.go173
1 files changed, 173 insertions, 0 deletions
diff --git a/message/output_test.go b/message/output_test.go
new file mode 100644
index 00000000..ab69fe52
--- /dev/null
+++ b/message/output_test.go
@@ -0,0 +1,173 @@
+package message_test
+
+import (
+ "bytes"
+ "errors"
+ "reflect"
+ "strconv"
+ "syscall"
+ "testing"
+
+ "hakurei.app/container/stub"
+ "hakurei.app/message"
+)
+
+func TestSuspendable(t *testing.T) {
+ // copied from output.go
+ const suspendBufMax = 1 << 24
+
+ const (
+ // equivalent to len(want.pt)
+ nSpecialPtEquiv = -iota - 1
+ // equivalent to len(want.w)
+ nSpecialWEquiv
+ // suspends writer before executing test case, implies nSpecialWEquiv
+ nSpecialSuspend
+ // offset: resume writer and measure against dump instead, implies nSpecialPtEquiv
+ nSpecialDump
+ )
+
+ // shares the same writer
+ steps := []struct {
+ name string
+ w, pt []byte
+ err error
+ wantErr error
+ n int
+ }{
+ {"simple", []byte{0xde, 0xad, 0xbe, 0xef}, []byte{0xde, 0xad, 0xbe, 0xef},
+ nil, nil, nSpecialPtEquiv},
+
+ {"error", []byte{0xb, 0xad}, []byte{0xb, 0xad},
+ stub.UniqueError(0), stub.UniqueError(0), nSpecialPtEquiv},
+
+ {"suspend short", []byte{0}, nil,
+ nil, nil, nSpecialSuspend},
+ {"sw short 0", []byte{0xca, 0xfe, 0xba, 0xbe}, nil,
+ nil, nil, nSpecialWEquiv},
+ {"sw short 1", []byte{0xff}, nil,
+ nil, nil, nSpecialWEquiv},
+ {"resume short", nil, []byte{0, 0xca, 0xfe, 0xba, 0xbe, 0xff}, nil, nil,
+ nSpecialDump},
+
+ {"long pt", bytes.Repeat([]byte{0xff}, suspendBufMax+1), bytes.Repeat([]byte{0xff}, suspendBufMax+1),
+ nil, nil, nSpecialPtEquiv},
+
+ {"suspend fill", bytes.Repeat([]byte{0xfe}, suspendBufMax), nil,
+ nil, nil, nSpecialSuspend},
+ {"drop", []byte{0}, nil,
+ nil, syscall.ENOMEM, 0},
+ {"drop error", []byte{0}, nil,
+ stub.UniqueError(1), syscall.ENOMEM, 0},
+ {"resume fill", nil, bytes.Repeat([]byte{0xfe}, suspendBufMax),
+ nil, nil, nSpecialDump - 2},
+
+ {"suspend fill partial", bytes.Repeat([]byte{0xfd}, suspendBufMax-0xf), nil,
+ nil, nil, nSpecialSuspend},
+ {"partial write", bytes.Repeat([]byte{0xad}, 0x1f), nil,
+ nil, syscall.ENOMEM, 0xf},
+ {"full drop", []byte{0}, nil,
+ nil, syscall.ENOMEM, 0},
+ {"resume fill partial", nil, append(bytes.Repeat([]byte{0xfd}, suspendBufMax-0xf), bytes.Repeat([]byte{0xad}, 0xf)...),
+ nil, nil, nSpecialDump - 0x10 - 1},
+ }
+
+ var dw expectWriter
+
+ w := message.Suspendable{Downstream: &dw}
+ for _, step := range steps {
+ // these share the same writer, so cannot be subtests
+ t.Logf("writing step %q", step.name)
+ dw.expect, dw.err = step.pt, step.err
+
+ var (
+ gotN int
+ gotErr error
+ )
+
+ wantN := step.n
+ switch wantN {
+ case nSpecialPtEquiv:
+ wantN = len(step.pt)
+ gotN, gotErr = w.Write(step.w)
+
+ case nSpecialWEquiv:
+ wantN = len(step.w)
+ gotN, gotErr = w.Write(step.w)
+
+ case nSpecialSuspend:
+ s := w.IsSuspended()
+ if ok := w.Suspend(); s && ok {
+ t.Fatal("Suspend: unexpected success")
+ }
+
+ wantN = len(step.w)
+ gotN, gotErr = w.Write(step.w)
+
+ default:
+ if wantN <= nSpecialDump {
+ if !w.IsSuspended() {
+ t.Fatal("IsSuspended unexpected false")
+ }
+
+ resumed, dropped, n, err := w.Resume()
+ if !resumed {
+ t.Fatal("Resume: resumed = false")
+ }
+ if wantDropped := nSpecialDump - wantN; int(dropped) != wantDropped {
+ t.Errorf("Resume: dropped = %d, want %d", dropped, wantDropped)
+ }
+
+ wantN = len(step.pt)
+ gotN, gotErr = int(n), err
+ } else {
+ gotN, gotErr = w.Write(step.w)
+ }
+ }
+
+ if gotN != wantN {
+ t.Errorf("Write: n = %d, want %d", gotN, wantN)
+ }
+
+ if !reflect.DeepEqual(gotErr, step.wantErr) {
+ t.Errorf("Write: %v", gotErr)
+ }
+
+ if dw.expect != nil {
+ t.Errorf("expect: %q", string(dw.expect))
+ }
+ }
+}
+
+// expectWriter compares Write calls to expect.
+type expectWriter struct {
+ expect []byte
+ err error
+
+ // optional consecutive write
+ next []byte
+
+ // optional, calls Error on failure if not nil
+ t *testing.T
+}
+
+func (w *expectWriter) Write(p []byte) (n int, err error) {
+ defer func() { w.expect = w.next; w.next = nil }()
+
+ n, err = len(p), w.err
+ if w.expect == nil {
+ n, err = 0, errors.New("unexpected call to Write: "+strconv.Quote(string(p)))
+ if w.t != nil {
+ w.t.Error(err.Error())
+ }
+ return
+ }
+ if string(p) != string(w.expect) {
+ n, err = 0, errors.New("p = "+strconv.Quote(string(p))+", want "+strconv.Quote(string(w.expect)))
+ if w.t != nil {
+ w.t.Error(err.Error())
+ }
+ return
+ }
+ return
+}