[ObjC] Fix some tests to not violate GPBCodedOutputStream invariants.

If the class is writing to an NSOutputStream, the buffer must always
be used to batch things up. The tests were accidentally making a zero
length buffer which isn't valid.

Fix the tests to not do this.

Make the core code assert in debug to ensure the invariant can't be
broken.

PiperOrigin-RevId: 817153199
This commit is contained in:
Thomas Van Lenten 2025-10-09 06:09:20 -07:00 committed by Copybara-Service
parent c3ddacbd2e
commit d4bef77490
2 changed files with 24 additions and 15 deletions

View file

@ -185,11 +185,16 @@ static void GPBWriteRawLittleEndian64(GPBOutputBufferState *state, int64_t value
return [self initWithOutputStream:nil data:data];
}
// This initializer isn't exposed, but it is the designated initializer.
// Setting OutputStream and NSData is to control the buffering behavior/size
// of the work, but that is more obvious via the bufferSize: version.
// This initializer isn't publicly exposed, but it is the designated initializer.
// Setting OutputStream and NSData is to control the buffering behavior/size of the work.
- (instancetype)initWithOutputStream:(NSOutputStream *)output data:(NSMutableData *)data {
if ((self = [super init])) {
#if defined(DEBUG) && DEBUG
// The public interface (above) can't violate this, but the tests cheat and directly call
// so ensure the invariant is enforced.
NSAssert((output == nil) || ([data length] != 0),
@"Internal error, can't have an output stream and a zero length buffer");
#endif
buffer_ = [data retain];
state_.bytes = [data mutableBytes];
state_.size = [data length];

View file

@ -385,30 +385,34 @@
}
- (void)testThatItThrowsWhenWriteRawPtrFails {
NSOutputStream* output = [NSOutputStream outputStreamToMemory];
GPBCodedOutputStream* codedOutput =
[GPBCodedOutputStream streamWithOutputStream:output bufferSize:0]; // Skip buffering.
[output close]; // Close the output stream to force failure on write.
const char* cString = "raw";
const char* cString = "some test data to write";
// Make an outputString that won't be long enough for the data so the stream will
// eventually fail the write.
uint8_t buffer[2] = {0, 0};
NSOutputStream* output = [[[NSOutputStream alloc] initToBuffer:buffer
capacity:sizeof(buffer)] autorelease];
// Use a tiny buffers so `writeRawPtr:offset:length:` will eventaully write directly.
GPBCodedOutputStream* codedOutput = [GPBCodedOutputStream streamWithOutputStream:output
bufferSize:1];
XCTAssertThrowsSpecificNamed([codedOutput writeRawPtr:cString offset:0 length:strlen(cString)],
NSException, GPBCodedOutputStreamException_WriteFailed);
}
- (void)testThatDeallocNeverThrows {
// Output stream which can write precisely 1 byte of data before it is full.
uint8_t buffer[1] = {0};
// Output stream which can write precisely 1 byte of data before it's full.
NSOutputStream* output = [[[NSOutputStream alloc] initToBuffer:buffer
capacity:sizeof(buffer)] autorelease];
GPBCodedOutputStream* codedOutput = [[GPBCodedOutputStream alloc] initWithOutputStream:output];
NSMutableData* outputBuffer = [NSMutableData data];
GPBCodedOutputStream* codedOutput =
[[GPBCodedOutputStream alloc] initWithOutputStream:output data:outputBuffer];
// First byte and flush (filling it)
[codedOutput writeRawByte:0x23];
[codedOutput flush];
// Put one more byte in the output buffer.
// One more byte will fail on flush due to lack of space in the stream.
[codedOutput writeRawByte:0x42];
XCTAssertThrowsSpecificNamed([codedOutput flush], NSException,
GPBCodedOutputStreamException_WriteFailed);