Skip to content

Let CRAMContainerStreamWriter callers keep ownership of their stream - #1789

Merged
tfenne merged 2 commits into
masterfrom
tf_fix_cram_1092
Jul 26, 2026
Merged

Let CRAMContainerStreamWriter callers keep ownership of their stream#1789
tfenne merged 2 commits into
masterfrom
tf_fix_cram_1092

Conversation

@tfenne

@tfenne tfenne commented Jul 25, 2026

Copy link
Copy Markdown
Member

Closes #1092.

CRAMContainerStreamWriter takes an OutputStream created by the caller but unconditionally closed it in finish(), so code that manages the stream lifecycle itself had no way to finish writing without having its stream closed underneath it — and a subsequent close() by the owner would then fail.

Adds finish(writeEOFContainer, closeStream) so callers can opt out of the close. The existing finish(writeEOFContainer) overload delegates with closeStream=true, so behaviour is unchanged for every current caller, including CRAMFileWriter, which relies on it to close the file stream it owns.

The stream is flushed either way, so opting out never loses data.

Tests cover both overloads, that the single-argument form still closes (backwards compatibility), and that a caller can keep using its own stream afterwards.

Summary by CodeRabbit

  • New Features

    • Added an overload to CRAM writer finalization that lets callers choose whether the underlying output stream is closed.
    • The original single-argument finalization continues to close the stream by default.
  • Bug Fixes

    • Finalization now conditionally closes the stream only when requested, allowing callers to keep using the same stream after finalization.
    • Flushing, optional EOF container emission, indexing, and existing error-handling behavior are preserved.

…1092)

CRAMContainerStreamWriter takes an OutputStream created by the caller but
unconditionally closed it in finish(), so code that manages the stream's
lifecycle itself had no way to finish writing without also having its stream
closed underneath it -- and a subsequent close() by the owner would then fail.

Add finish(writeEOFContainer, closeStream) so the caller can opt out of the
close. The existing finish(writeEOFContainer) overload delegates with
closeStream=true, so behaviour is unchanged for all current callers, including
CRAMFileWriter, which relies on it to close the file stream it owns.

The stream is flushed either way, so opting out never loses data.
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7f6a6fd5-1b44-4941-8761-fa48f9b5cb9e

📥 Commits

Reviewing files that changed from the base of the PR and between c15932c and ab81611.

📒 Files selected for processing (1)
  • src/test/java/htsjdk/samtools/cram/CRAMContainerStreamWriterClosureTest.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/test/java/htsjdk/samtools/cram/CRAMContainerStreamWriterClosureTest.java

📝 Walkthrough

Walkthrough

CRAMContainerStreamWriter adds a finish overload that controls whether the supplied OutputStream is closed. The existing overload continues closing the stream by default, with tests covering both behaviors and continued stream use.

Changes

CRAM stream closure

Layer / File(s) Summary
Configurable finish API
src/main/java/htsjdk/samtools/CRAMContainerStreamWriter.java
Adds finish(boolean, boolean) and makes stream closure conditional while preserving the existing single-argument behavior.
Stream closure validation
src/test/java/htsjdk/samtools/cram/CRAMContainerStreamWriterClosureTest.java
Tests optional closure, default closure, CRAM output flushing, and writing additional data after finishing without closure.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: allowing callers to retain ownership of the output stream.
Linked Issues check ✅ Passed The changes satisfy issue #1092 by adding a finish mode that flushes without closing the caller-owned OutputStream.
Out of Scope Changes check ✅ Passed The diff stays focused on CRAMContainerStreamWriter stream-closure behavior and targeted tests, with no obvious unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch tf_fix_cram_1092

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/test/java/htsjdk/samtools/cram/CRAMContainerStreamWriterClosureTest.java`:
- Around line 57-68: Update CloseRecordingStream and
finish_does_not_close_stream_when_close_stream_is_false so the test records
whether flush() was invoked and asserts that flag after writer.finish(true,
false). Remove the size-based assertion, since ByteArrayOutputStream visibility
does not verify flushing, while preserving the assertion that the
caller-provided stream remains open.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d442414a-82ea-4241-9f64-55b0fd2d1584

📥 Commits

Reviewing files that changed from the base of the PR and between 2ae4aaa and c15932c.

📒 Files selected for processing (2)
  • src/main/java/htsjdk/samtools/CRAMContainerStreamWriter.java
  • src/test/java/htsjdk/samtools/cram/CRAMContainerStreamWriterClosureTest.java

Per review on #1789: ByteArrayOutputStream makes writes visible immediately and
inherits a no-op flush(), so asserting size() > 0 proved only that bytes had
been written -- it would have passed just as happily if finish(..., false) had
stopped flushing altogether.

Track flush() on the recording stream and assert that instead, so the test
actually pins the behaviour that matters: declining to close the stream must
not decline to flush it.

Verified by removing the outputStream.flush() call from finish(): the new
assertion fails, where the old one passed.
@tfenne

tfenne commented Jul 26, 2026

Copy link
Copy Markdown
Member Author

Good catch, thanks — fixed in the follow-up commit.

You were right that the assertion proved nothing about flushing: ByteArrayOutputStream makes writes visible immediately and inherits a no-op flush(), so size() > 0 only showed that bytes had been written. It would have passed just as happily if finish(..., false) had stopped flushing altogether.

CloseRecordingStream now tracks flush() alongside close(), and the test asserts the flag instead of the stream size.

I confirmed the new assertion actually bites by temporarily removing the outputStream.flush() call from finish():

AssertionError: declining the close must still flush the stream, or data can be lost
  expected [true] but found [false]

The old size() > 0 assertion passed under that same regression.

@tfenne
tfenne merged commit a431e97 into master Jul 26, 2026
5 checks passed
@tfenne
tfenne deleted the tf_fix_cram_1092 branch July 26, 2026 01:05
tfenne added a commit that referenced this pull request Jul 26, 2026
Three test classes added in #1789, #1791 and #1792 used snake_case method names
without the conventional test prefix, which is neither idiomatic Java nor what
this repo does: of 2417 test methods, 2020 start with "test" and only 199
contain an underscore at all.

Rename the nine offending methods in these three classes to testXxx camelCase.
Test bodies are untouched; this is naming only.
tfenne added a commit that referenced this pull request Jul 26, 2026
Three test classes added in #1789, #1791 and #1792 used snake_case method names
without the conventional test prefix, which is neither idiomatic Java nor what
this repo does: of 2417 test methods, 2020 start with "test" and only 199
contain an underscore at all.

Rename the nine offending methods in these three classes to testXxx camelCase.
Test bodies are untouched; this is naming only.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Closure of OutputStream in class CRAMContainerStreamWriter

1 participant