diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-10-09 05:04:08 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-10-09 05:18:19 +0900 |
| commit | 87b5c30ef6d5b7b8a10d6a5a8610054c403923e6 (patch) | |
| tree | 805116c39a623bb30fc532af47ea3ed78b7304c0 /message/output_test.go | |
| parent | df9b77b077d967d35082adb8ab3b00dd67d7bc97 (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.go | 173 |
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 +} |
