Skip to content

test(b2): pin writer, reader and progress edges - #257

Open
ffumero2003 wants to merge 4 commits into
mainfrom
test-issue-204-b2-writer-reader-assertions
Open

ffumero2003 wants to merge 4 commits into
mainfrom
test-issue-204-b2-writer-reader-assertions

Conversation

@ffumero2003

Copy link
Copy Markdown
Contributor

Context

Part of the test-integrity audit (#97). #204 is a checklist of tests that check less than their names say. This PR covers its writer, reader and progress items (slice C4). It was waiting for #236, which changed the same writer and reader code. Sibling PRs: #232, #233, #240, #247, #248 and #249 (all merged).

What each test now proves

Item Test What it now asserts
Resumed pipe upload TestProgressResumePipeWithoutCallback Reads the uploaded object back and compares it with the pipe payload.
Terminal callback TestProgressCloseWaitsForInitialization The last progress callback is exactly the terminal value (0/-1 after cancelled initialization), not just "some callback happened".
Late progress delivery TestProgressCancellationHasNoCallbackAfterTerminal No progress update is queued after the terminal callback (asserted on the wake channel, not a snapshot).
One-byte reader chunks TestReaderChunkSizeOneAndDefaults (new) ChunkSize = 1 downloads in 1-byte chunks, with literal Range requests bytes=0-0, 1-1, 2-2; the default chunk size applies when unset.
Sticky buffer errors TestWriterBufferWriteErrorIsSticky (new) A failing write buffer's error is returned again by the next Write, on both the within-chunk and the chunk-crossing paths.
Writer status TestWriterStatusIncludesAllThreeConcurrentParts (new) With three concurrent parts in flight, Status().Progress has three entries, all complete at the end.
Direct Write after a part failure TestWriterDirectWriteUnblocksOnPermanentPartFailure (new) Calling Write directly (not through io.Copy) returns after a permanent part failure instead of blocking.

Not covered here: b2/reader.go:345 (Reader.Status for the last chunk). The stronger test exposed a production off-by-one: chunk IDs start at 0 but Reader.status() iterates from 1, so Progress comes back [1 0.5 1] instead of [1 1 0.5]. Filed as #256.

Coverage removed or narrowed: none. The only replaced assertion ("any callback occurred") became a non-empty check plus an exact check on the last callback.

Also in this branch: the 2-line fix to b2/core_edges_test.go from #252. #247 removed a helper that #249's test still used, so the b2 tests don't compile on main until one of these merges. The change is identical in both PRs and merges cleanly.

Verification

Each break was applied alone; the named test failed, then passed after revert. Rows marked "exact #204 mutation" are the breaks #204 names.

Item Break Failing output
1 — resumed pipe content — TestProgressResumePipeWithoutCallback Discard the Resume ReadFrom payload instead of writing it progress_review_test.go:110: uploaded content = "", <nil>; want pipe payload, nil
2 — last terminal callback — TestProgressCloseWaitsForInitialization Corrupt the terminal callback completed value (+1) progress_review_test.go:183: progress after Close = [{complete:1 total:-1}], want final 0/-1 after cancelled initialization
2 — last terminal callback — TestProgressCloseWaitsForInitialization Delete the terminal callback progress_review_test.go:183: progress after Close = [], want final 0/-1 after cancelled initialization
3 — late progress delivery — TestProgressCancellationHasNoCallbackAfterTerminal Flip add's stopped guard to !p.stopped progress_test.go:666: late progress update queued after terminal callback
3 — late progress delivery — TestProgressCancellationHasNoCallbackAfterTerminal Delete add's stopped guard progress_test.go:666: late progress update queued after terminal callback
4 — one-byte/default reader chunks — TestReaderChunkSizeOneAndDefaults Change ChunkSize < 1 to <= 1 (exact #204 mutation) writer_reader_edges_test.go:43: chunk size = 10000000, want 1
4 — one-byte/default reader chunks — TestReaderChunkSizeOneAndDefaults Flip ChunkSize < 1 to >= 1 writer_reader_edges_test.go:43: chunk size = 10000000, want 1
4 — one-byte/default reader chunks — TestReaderChunkSizeOneAndDefaults Delete the ChunkSize default guard writer_reader_edges_test.go:43: chunk size = 0, want 10000000
6 — sticky buffer errors — TestWriterBufferWriteErrorIsSticky Flip the within-chunk buffer error guard to err == nil writer_reader_edges_test.go:98: second Write = 1, <nil>; want 0, original buffer error
6 — sticky buffer errors — TestWriterBufferWriteErrorIsSticky Delete within-chunk setErr: make the buffer error non-sticky writer_reader_edges_test.go:98: second Write = 1, <nil>; want 0, original buffer error
6 — sticky buffer errors — TestWriterBufferWriteErrorIsSticky Flip the crossing-chunk buffer error guard to err == nil writer_reader_edges_test.go:95: first Write = 1, <nil>; want 0, buffer write failed
6 — sticky buffer errors — TestWriterBufferWriteErrorIsSticky Delete crossing-chunk setErr: make the buffer error non-sticky writer_reader_edges_test.go:98: second Write = 1, <nil>; want 0, original buffer error
7 — all three writer parts — TestWriterStatusIncludesAllThreeConcurrentParts Change i <= len(w.smap) to i < len(w.smap) writer_reader_edges_test.go:137: in-flight Progress = [1 1 0], want [1 1 1]
7 — all three writer parts — TestWriterStatusIncludesAllThreeConcurrentParts Delete the Writer.Status progress loop writer_reader_edges_test.go:137: in-flight Progress = [0 0 0], want [1 1 1]
8 — direct Write cancellation — TestWriterDirectWriteUnblocksOnPermanentPartFailure Disable sendChunk's ctx.Done select case writer_reader_edges_test.go:224: direct Write blocked after permanent part failure
8 — direct Write cancellation — TestWriterDirectWriteUnblocksOnPermanentPartFailure Delete sendChunk's ctx.Done select case (exact #204 mutation) writer_reader_edges_test.go:224: direct Write blocked after permanent part failure

With no B2 credentials set: go test -race -count=5 -shuffle=on ./b2/... passes, and the added and changed tests pass 30 runs in a row under -race.

make ci (tail)

go build ./...
go vet ./...
go mod tidy -diff
go mod verify
all modules verified
0 issues.
go test -race -shuffle=on ./...
ok  	github.com/Backblaze/blazer/b2	16.287s
ok  	github.com/Backblaze/blazer/b2/sync	4.265s
ok  	github.com/Backblaze/blazer/base	7.828s
ok  	github.com/Backblaze/blazer/internal/b2types	2.294s
ok  	github.com/Backblaze/blazer/internal/b2wiretest	1.901s
ok  	github.com/Backblaze/blazer/internal/blog	3.614s
ok  	github.com/Backblaze/blazer/internal/retry	3.206s
ok  	github.com/Backblaze/blazer/x/b2sim	3.362s
ok  	github.com/Backblaze/blazer/x/consistent	2.798s
ok  	github.com/Backblaze/blazer/x/transport	2.744s
ok  	github.com/Backblaze/blazer/x/window	3.412s
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build ./...
CGO_ENABLED=0 GOOS=linux GOARCH=386 go build ./...
CGO_ENABLED=0 GOOS=linux GOARCH=arm go build ./...
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build ./...
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build ./...
... scripts/*_test.sh: all ok

Part of #204

@ffumero2003 ffumero2003 added this to the 0.10.0 milestone Oct 30, 2026
@ffumero2003 ffumero2003 added the skip-changelog No changelog entry needed; skips the changelog check label Oct 30, 2026
@ffumero2003
ffumero2003 requested a review from goanpeca as a code owner October 30, 2026 22:10
@ffumero2003 ffumero2003 self-assigned this Oct 30, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-changelog No changelog entry needed; skips the changelog check

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant