Skip to content

test: Add intgeration test for GoogleCloudStorageOuputStream and GCsFileSystemImpl#336

Open
dheerajsngh wants to merge 31 commits into
GoogleCloudPlatform:mainfrom
dheerajsngh:analytics-write-4
Open

test: Add intgeration test for GoogleCloudStorageOuputStream and GCsFileSystemImpl#336
dheerajsngh wants to merge 31 commits into
GoogleCloudPlatform:mainfrom
dheerajsngh:analytics-write-4

Conversation

@dheerajsngh

@dheerajsngh dheerajsngh commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Type of Change

  • feat: A new feature
  • fix: A bug fix
  • docs: Documentation only changes
  • style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
  • refactor: A code change that neither fixes a bug nor adds a feature
  • perf: A code change that improves performance
  • test: Adding missing tests or correcting existing tests
  • chore: Changes to the build process or auxiliary tools and libraries such as documentation generation

Description

What?

  • Added integration tests (GoogleCloudStorageOutputStreamIntegrationTest) to verify the functionality of GoogleCloudStorageOutputStream.
  • Introduced supporting test utility classes (TestInputStreamInputFile, TestOutputStreamOutputFile, and IntegrationTestHelper) to help validate data writes, read-backs, and Parquet formatting.
  • Added integration tests in GcsFileSystemImplIntegrationTest for testing file creation with different configurations

Why?

Verify write behavior for different scenarios.

Checklist

  • PR title follows Conventional Commits (e.g., test(core): add integration tests for GCS writes)
  • All files include the Apache License 2.0 header
  • Documentation has been updated to reflect changes (N/A for test additions)

Generated/Assisted by Agent? Yes

# Conflicts:
#	client/src/main/java/com/google/cloud/gcs/analyticscore/client/GcsClientImpl.java
#	client/src/test/java/com/google/cloud/gcs/analyticscore/client/GcsClientImplTest.java
@dheerajsngh dheerajsngh requested a review from a team as a code owner July 6, 2026 10:25
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request implements a new GoogleCloudStorageOutputStream to enable standard OutputStream-based writing to GCS. It extends the existing client and file system layers to support write channel creation and includes robust integration testing to ensure reliability across various write scenarios, such as parallel composite uploads and checksum validation.

Highlights

  • New OutputStream Implementation: Added GoogleCloudStorageOutputStream to provide a standard Java OutputStream interface for writing data to GCS.
  • Interface Updates: Updated GcsClient and GcsFileSystem interfaces to support the creation of write channels.
  • Configuration Management: Introduced GcsWriteConfigurationUtil to centralize and manage GCS write session configurations.
  • Integration Testing: Added comprehensive integration tests for GoogleCloudStorageOutputStream and GcsFileSystemImpl to verify write behavior.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@dheerajsngh dheerajsngh changed the title test(integration): Add intgeration test for GoogleCloudStorageOuputStream and GCsFileSystemImpl test: Add intgeration test for GoogleCloudStorageOuputStream and GCsFileSystemImpl Jul 6, 2026
@codecov

codecov Bot commented Jul 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.83041% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.94%. Comparing base (949321b) to head (099926c).

Files with missing lines Patch % Lines
...nalyticscore/client/GcsWriteConfigurationUtil.java 96.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main     #336      +/-   ##
============================================
+ Coverage     97.43%   97.94%   +0.51%     
- Complexity      438      484      +46     
============================================
  Files            33       35       +2     
  Lines          1405     1512     +107     
  Branches        129      141      +12     
============================================
+ Hits           1369     1481     +112     
+ Misses           20       16       -4     
+ Partials         16       15       -1     
Files with missing lines Coverage Δ
.../cloud/gcs/analyticscore/client/GcsClientImpl.java 100.00% <100.00%> (+5.55%) ⬆️
...oud/gcs/analyticscore/client/GcsClientOptions.java 100.00% <100.00%> (ø)
...oud/gcs/analyticscore/client/GcsExceptionUtil.java 100.00% <100.00%> (ø)
...ud/gcs/analyticscore/client/GcsFileSystemImpl.java 100.00% <100.00%> (ø)
...loud/gcs/analyticscore/client/GcsWriteChannel.java 97.91% <100.00%> (-0.17%) ⬇️
...loud/gcs/analyticscore/client/GcsWriteOptions.java 100.00% <100.00%> (+1.40%) ⬆️
...yticscore/core/GoogleCloudStorageOutputStream.java 100.00% <100.00%> (ø)
...nalyticscore/client/GcsWriteConfigurationUtil.java 96.00% <96.00%> (ø)

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces GoogleCloudStorageOutputStream to support writing objects to Google Cloud Storage, wrapping a WritableByteChannel with standard OutputStream semantics. It refactors GcsWriteOptions by moving upload-related configurations to GcsClientOptions and delegates GCS object creation from GcsFileSystem to GcsClient. Feedback on the changes highlights a compilation failure in GcsFileSystemImplIntegrationTest due to the refactored write options, a potential NoSuchElementException when creating blob info with a bucket-only ID, and a potential DirectoryNotEmptyException during test directory cleanup. Additionally, the reviewer suggests adding a defensive null check for the created channel and replacing magic numbers with reusable KB and MB constants in the integration tests.

Comment on lines +198 to +210
GcsFileSystemOptions options = GcsFileSystemOptions.builder()
.setGcsClientOptions(GcsClientOptions.builder().build())
.build();
GcsFileSystemImpl gcsFileSystem = new GcsFileSystemImpl(options);

GcsItemId itemId = GcsItemId.builder()
.setBucketName(bucketName)
.setObjectName(objectName)
.build();

GcsWriteOptions writeOptions = GcsWriteOptions.builder()
.setUploadStrategy(GcsWriteOptions.UploadStrategy.PARALLEL_COMPOSITE_UPLOAD)
.build();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

critical

The GcsWriteOptions class was refactored in this PR to remove upload configurations (such as UploadType and setUploadStrategy), moving them to GcsClientOptions. Consequently, calling setUploadStrategy on GcsWriteOptions.builder() will cause a compilation failure.

Please configure the upload type on GcsClientOptions instead.

Suggested change
GcsFileSystemOptions options = GcsFileSystemOptions.builder()
.setGcsClientOptions(GcsClientOptions.builder().build())
.build();
GcsFileSystemImpl gcsFileSystem = new GcsFileSystemImpl(options);
GcsItemId itemId = GcsItemId.builder()
.setBucketName(bucketName)
.setObjectName(objectName)
.build();
GcsWriteOptions writeOptions = GcsWriteOptions.builder()
.setUploadStrategy(GcsWriteOptions.UploadStrategy.PARALLEL_COMPOSITE_UPLOAD)
.build();
GcsFileSystemOptions options = GcsFileSystemOptions.builder()
.setGcsClientOptions(GcsClientOptions.builder()
.setUploadType(GcsClientOptions.UploadType.PARALLEL_COMPOSITE_UPLOAD)
.build())
.build();
GcsFileSystemImpl gcsFileSystem = new GcsFileSystemImpl(options);
GcsItemId itemId = GcsItemId.builder()
.setBucketName(bucketName)
.setObjectName(objectName)
.build();
GcsWriteOptions writeOptions = GcsWriteOptions.builder().build();

Comment on lines +122 to +124
checkNotNull(itemId, "itemId should not be null");

BlobInfo blobInfo = createBlobInfo(itemId);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

To prevent a NoSuchElementException when calling createBlobInfo(itemId) with a bucket-only GcsItemId (where getObjectName() is empty), we should defensively validate that the itemId represents a GCS object before proceeding.

Suggested change
checkNotNull(itemId, "itemId should not be null");
BlobInfo blobInfo = createBlobInfo(itemId);
checkNotNull(itemId, "itemId should not be null");
checkArgument(itemId.isGcsObject(), "Expected GCS object to be provided. But got: " + itemId);
BlobInfo blobInfo = createBlobInfo(itemId);

Comment on lines +53 to +54
WritableByteChannel channel = gcsFileSystem.create(itemId, writeOptions);
return new GoogleCloudStorageOutputStream(channel);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Add a defensive null check for the returned WritableByteChannel to fail-fast during stream creation rather than throwing a NullPointerException later when write is called.

    WritableByteChannel channel = gcsFileSystem.create(itemId, writeOptions);
    checkNotNull(channel, "channel shouldn't be null");
    return new GoogleCloudStorageOutputStream(channel);

assertThat(customFs.getFileInfo(uri).getItemInfo().getSize()).isEqualTo((long) TEST_CONTENT.length);
}
} finally {
Files.deleteIfExists(tempDir);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Using Files.deleteIfExists(tempDir) on a non-empty directory will throw a DirectoryNotEmptyException and fail to clean up the temporary directory, leading to leaked files on the build agent. Since journaling writes files into this directory, we should delete it recursively.

      if (Files.exists(tempDir)) {
        try (java.util.stream.Stream<Path> walk = Files.walk(tempDir)) {
          walk.sorted(java.util.Comparator.reverseOrder())
              .map(Path::toFile)
              .forEach(java.io.File::delete);
        }
      }

Comment on lines +74 to +77
private static final String SUFFIX_TXT = ".txt";
private static final String SUFFIX_CSV = ".csv";
private static final String SUFFIX_BIN = ".bin";
private static final String SUFFIX_PARQUET = ".parquet";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Define reusable constants for KB and MB at the class level to avoid using raw arithmetic expressions and magic numbers for sizes and limits, in accordance with the repository's general rules.

  private static final String SUFFIX_TXT = ".txt";
  private static final String SUFFIX_CSV = ".csv";
  private static final String SUFFIX_BIN = ".bin";
  private static final String SUFFIX_PARQUET = ".parquet";

  private static final int KB = 1024;
  private static final int MB = 1024 * 1024;
References
  1. Avoid using hardcoded magic numbers or raw arithmetic expressions for sizes and limits in tests. Instead, define and use constants (such as KB = 1024) to make the code more readable and maintainable.

Comment on lines +274 to +280
GcsClientOptions clientOptions = GcsClientOptions.builder()
.setUploadChunkSize(256 * 1024)
.build();
GcsWriteOptions writeOptions = GcsWriteOptions.builder().build();

int totalSize = 1024 * 1024; // 1 MB total size
byte[] chunk = new byte[1024]; // 1 KB chunks written locally

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Use the defined KB and MB constants instead of raw arithmetic expressions and magic numbers.

Suggested change
GcsClientOptions clientOptions = GcsClientOptions.builder()
.setUploadChunkSize(256 * 1024)
.build();
GcsWriteOptions writeOptions = GcsWriteOptions.builder().build();
int totalSize = 1024 * 1024; // 1 MB total size
byte[] chunk = new byte[1024]; // 1 KB chunks written locally
GcsClientOptions clientOptions = GcsClientOptions.builder()
.setUploadChunkSize(256 * KB)
.build();
GcsWriteOptions writeOptions = GcsWriteOptions.builder().build();
int totalSize = 1 * MB; // 1 MB total size
byte[] chunk = new byte[KB]; // 1 KB chunks written locally
References
  1. Avoid using hardcoded magic numbers or raw arithmetic expressions for sizes and limits in tests. Instead, define and use constants (such as KB = 1024) to make the code more readable and maintainable.

GcsClientOptions clientOptions = GcsClientOptions.builder()
.setUploadType(GcsClientOptions.UploadType.PARALLEL_COMPOSITE_UPLOAD)
.setPcuBufferCount(2)
.setPcuBufferCapacity(16 * 1024 * 1024) // 16MB

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Use the defined MB constant instead of raw arithmetic expressions.

Suggested change
.setPcuBufferCapacity(16 * 1024 * 1024) // 16MB
.setPcuBufferCapacity(16 * MB)
References
  1. Avoid using hardcoded magic numbers or raw arithmetic expressions for sizes and limits in tests. Instead, define and use constants (such as KB = 1024) to make the code more readable and maintainable.

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.

1 participant