Commit e34c0918 authored by Sergio Campamá's avatar Sergio Campamá Committed by Thomas Van Lenten

Improving the granularity parsing errors (#1623)

Add more context to GPBCodedInputStream failures.
Have GPBMessage parsing apis extract out the GPBCodedInputStream information and expose it.
Update HeaderDocs with pointers to all error domains/codes.
Expand the unittests to cover the full set of errors reported.

Fixes https://github.com/google/protobuf/issues/1618
parent 0ab7a7f7
...@@ -35,6 +35,39 @@ ...@@ -35,6 +35,39 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
CF_EXTERN_C_BEGIN
/// GPBCodedInputStream exception name. Exceptions raised from
/// GPBCodedInputStream contain an underlying error in the userInfo dictionary
/// under the GPBCodedInputStreamUnderlyingErrorKey key.
extern NSString *const GPBCodedInputStreamException;
/// The key under which the underlying NSError from the exception is stored.
extern NSString *const GPBCodedInputStreamUnderlyingErrorKey;
/// NSError domain used for GPBCodedInputStream errors.
extern NSString *const GPBCodedInputStreamErrorDomain;
/// Error code for NSError with GPBCodedInputStreamErrorDomain.
typedef NS_ENUM(NSInteger, GPBCodedInputStreamErrorCode) {
/// The size does not fit in the remaining bytes to be read.
GPBCodedInputStreamErrorInvalidSize = -100,
/// Attempted to read beyond the subsection limit.
GPBCodedInputStreamErrorSubsectionLimitReached = -101,
/// The requested subsection limit is invalid.
GPBCodedInputStreamErrorInvalidSubsectionLimit = -102,
/// Invalid tag read.
GPBCodedInputStreamErrorInvalidTag = -103,
/// Invalid UTF-8 character in a string.
GPBCodedInputStreamErrorInvalidUTF8 = -104,
/// Invalid VarInt read.
GPBCodedInputStreamErrorInvalidVarInt = -105,
/// The maximum recursion depth of messages was exceeded.
GPBCodedInputStreamErrorRecursionDepthExceeded = -106,
};
CF_EXTERN_C_END
/// Reads and decodes protocol message fields. /// Reads and decodes protocol message fields.
/// ///
/// The common uses of protocol buffers shouldn't need to use this class. /// The common uses of protocol buffers shouldn't need to use this class.
......
...@@ -36,17 +36,42 @@ ...@@ -36,17 +36,42 @@
#import "GPBUtilities_PackagePrivate.h" #import "GPBUtilities_PackagePrivate.h"
#import "GPBWireFormat.h" #import "GPBWireFormat.h"
NSString *const GPBCodedInputStreamException =
GPBNSStringifySymbol(GPBCodedInputStreamException);
NSString *const GPBCodedInputStreamUnderlyingErrorKey =
GPBNSStringifySymbol(GPBCodedInputStreamUnderlyingErrorKey);
NSString *const GPBCodedInputStreamErrorDomain =
GPBNSStringifySymbol(GPBCodedInputStreamErrorDomain);
static const NSUInteger kDefaultRecursionLimit = 64; static const NSUInteger kDefaultRecursionLimit = 64;
static void RaiseException(NSInteger code, NSString *reason) {
NSDictionary *errorInfo = nil;
if ([reason length]) {
errorInfo = @{ GPBErrorReasonKey: reason };
}
NSError *error = [NSError errorWithDomain:GPBCodedInputStreamErrorDomain
code:code
userInfo:errorInfo];
NSDictionary *exceptionInfo =
@{ GPBCodedInputStreamUnderlyingErrorKey: error };
[[[NSException alloc] initWithName:GPBCodedInputStreamException
reason:reason
userInfo:exceptionInfo] raise];
}
static void CheckSize(GPBCodedInputStreamState *state, size_t size) { static void CheckSize(GPBCodedInputStreamState *state, size_t size) {
size_t newSize = state->bufferPos + size; size_t newSize = state->bufferPos + size;
if (newSize > state->bufferSize) { if (newSize > state->bufferSize) {
[NSException raise:NSParseErrorException format:@""]; RaiseException(GPBCodedInputStreamErrorInvalidSize, nil);
} }
if (newSize > state->currentLimit) { if (newSize > state->currentLimit) {
// Fast forward to end of currentLimit; // Fast forward to end of currentLimit;
state->bufferPos = state->currentLimit; state->bufferPos = state->currentLimit;
[NSException raise:NSParseErrorException format:@""]; RaiseException(GPBCodedInputStreamErrorSubsectionLimitReached, nil);
} }
} }
...@@ -95,8 +120,8 @@ static int32_t ReadRawVarint32(GPBCodedInputStreamState *state) { ...@@ -95,8 +120,8 @@ static int32_t ReadRawVarint32(GPBCodedInputStreamState *state) {
return result; return result;
} }
} }
[NSException raise:NSParseErrorException RaiseException(GPBCodedInputStreamErrorInvalidVarInt,
format:@"Unable to read varint32"]; @"Invalid VarInt32");
} }
} }
} }
...@@ -115,7 +140,7 @@ static int64_t ReadRawVarint64(GPBCodedInputStreamState *state) { ...@@ -115,7 +140,7 @@ static int64_t ReadRawVarint64(GPBCodedInputStreamState *state) {
} }
shift += 7; shift += 7;
} }
[NSException raise:NSParseErrorException format:@"Unable to read varint64"]; RaiseException(GPBCodedInputStreamErrorInvalidVarInt, @"Invalid VarInt64");
return 0; return 0;
} }
...@@ -202,8 +227,7 @@ int32_t GPBCodedInputStreamReadTag(GPBCodedInputStreamState *state) { ...@@ -202,8 +227,7 @@ int32_t GPBCodedInputStreamReadTag(GPBCodedInputStreamState *state) {
state->lastTag = ReadRawVarint32(state); state->lastTag = ReadRawVarint32(state);
if (state->lastTag == 0) { if (state->lastTag == 0) {
// If we actually read zero, that's not a valid tag. // If we actually read zero, that's not a valid tag.
[NSException raise:NSParseErrorException RaiseException(GPBCodedInputStreamErrorInvalidTag, @"Last tag can't be 0");
format:@"Invalid last tag %d", state->lastTag];
} }
return state->lastTag; return state->lastTag;
} }
...@@ -226,8 +250,7 @@ NSString *GPBCodedInputStreamReadRetainedString( ...@@ -226,8 +250,7 @@ NSString *GPBCodedInputStreamReadRetainedString(
NSLog(@"UTF-8 failure, is some field type 'string' when it should be " NSLog(@"UTF-8 failure, is some field type 'string' when it should be "
@"'bytes'?"); @"'bytes'?");
#endif #endif
[NSException raise:NSParseErrorException RaiseException(GPBCodedInputStreamErrorInvalidUTF8, nil);
format:@"Invalid UTF-8 for a 'string'"];
} }
} }
return result; return result;
...@@ -262,8 +285,7 @@ size_t GPBCodedInputStreamPushLimit(GPBCodedInputStreamState *state, ...@@ -262,8 +285,7 @@ size_t GPBCodedInputStreamPushLimit(GPBCodedInputStreamState *state,
byteLimit += state->bufferPos; byteLimit += state->bufferPos;
size_t oldLimit = state->currentLimit; size_t oldLimit = state->currentLimit;
if (byteLimit > oldLimit) { if (byteLimit > oldLimit) {
[NSException raise:NSInvalidArgumentException RaiseException(GPBCodedInputStreamErrorInvalidSubsectionLimit, nil);
format:@"byteLimit > oldLimit: %tu > %tu", byteLimit, oldLimit];
} }
state->currentLimit = byteLimit; state->currentLimit = byteLimit;
return oldLimit; return oldLimit;
...@@ -286,8 +308,7 @@ BOOL GPBCodedInputStreamIsAtEnd(GPBCodedInputStreamState *state) { ...@@ -286,8 +308,7 @@ BOOL GPBCodedInputStreamIsAtEnd(GPBCodedInputStreamState *state) {
void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state, void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
int32_t value) { int32_t value) {
if (state->lastTag != value) { if (state->lastTag != value) {
[NSException raise:NSParseErrorException RaiseException(GPBCodedInputStreamErrorInvalidTag, @"Unexpected tag read");
format:@"Last tag: %d should be %d", state->lastTag, value];
} }
} }
...@@ -353,7 +374,7 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state, ...@@ -353,7 +374,7 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
SkipRawData(&state_, sizeof(int32_t)); SkipRawData(&state_, sizeof(int32_t));
return YES; return YES;
} }
[NSException raise:NSParseErrorException format:@"Invalid tag %d", tag]; RaiseException(GPBCodedInputStreamErrorInvalidTag, nil);
return NO; return NO;
} }
...@@ -414,9 +435,7 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state, ...@@ -414,9 +435,7 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
message:(GPBMessage *)message message:(GPBMessage *)message
extensionRegistry:(GPBExtensionRegistry *)extensionRegistry { extensionRegistry:(GPBExtensionRegistry *)extensionRegistry {
if (state_.recursionDepth >= kDefaultRecursionLimit) { if (state_.recursionDepth >= kDefaultRecursionLimit) {
[NSException raise:NSParseErrorException RaiseException(GPBCodedInputStreamErrorRecursionDepthExceeded, nil);
format:@"recursionDepth(%tu) >= %tu", state_.recursionDepth,
kDefaultRecursionLimit];
} }
++state_.recursionDepth; ++state_.recursionDepth;
[message mergeFromCodedInputStream:self extensionRegistry:extensionRegistry]; [message mergeFromCodedInputStream:self extensionRegistry:extensionRegistry];
...@@ -428,9 +447,7 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state, ...@@ -428,9 +447,7 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
- (void)readUnknownGroup:(int32_t)fieldNumber - (void)readUnknownGroup:(int32_t)fieldNumber
message:(GPBUnknownFieldSet *)message { message:(GPBUnknownFieldSet *)message {
if (state_.recursionDepth >= kDefaultRecursionLimit) { if (state_.recursionDepth >= kDefaultRecursionLimit) {
[NSException raise:NSParseErrorException RaiseException(GPBCodedInputStreamErrorRecursionDepthExceeded, nil);
format:@"recursionDepth(%tu) >= %tu", state_.recursionDepth,
kDefaultRecursionLimit];
} }
++state_.recursionDepth; ++state_.recursionDepth;
[message mergeFromCodedInputStream:self]; [message mergeFromCodedInputStream:self];
...@@ -443,9 +460,7 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state, ...@@ -443,9 +460,7 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
extensionRegistry:(GPBExtensionRegistry *)extensionRegistry { extensionRegistry:(GPBExtensionRegistry *)extensionRegistry {
int32_t length = ReadRawVarint32(&state_); int32_t length = ReadRawVarint32(&state_);
if (state_.recursionDepth >= kDefaultRecursionLimit) { if (state_.recursionDepth >= kDefaultRecursionLimit) {
[NSException raise:NSParseErrorException RaiseException(GPBCodedInputStreamErrorRecursionDepthExceeded, nil);
format:@"recursionDepth(%tu) >= %tu", state_.recursionDepth,
kDefaultRecursionLimit];
} }
size_t oldLimit = GPBCodedInputStreamPushLimit(&state_, length); size_t oldLimit = GPBCodedInputStreamPushLimit(&state_, length);
++state_.recursionDepth; ++state_.recursionDepth;
...@@ -461,9 +476,7 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state, ...@@ -461,9 +476,7 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
parentMessage:(GPBMessage *)parentMessage { parentMessage:(GPBMessage *)parentMessage {
int32_t length = ReadRawVarint32(&state_); int32_t length = ReadRawVarint32(&state_);
if (state_.recursionDepth >= kDefaultRecursionLimit) { if (state_.recursionDepth >= kDefaultRecursionLimit) {
[NSException raise:NSParseErrorException RaiseException(GPBCodedInputStreamErrorRecursionDepthExceeded, nil);
format:@"recursionDepth(%tu) >= %tu", state_.recursionDepth,
kDefaultRecursionLimit];
} }
size_t oldLimit = GPBCodedInputStreamPushLimit(&state_, length); size_t oldLimit = GPBCodedInputStreamPushLimit(&state_, length);
++state_.recursionDepth; ++state_.recursionDepth;
......
...@@ -49,12 +49,15 @@ extern NSString *const GPBMessageErrorDomain; ...@@ -49,12 +49,15 @@ extern NSString *const GPBMessageErrorDomain;
/// Error code for NSError with GPBMessageErrorDomain. /// Error code for NSError with GPBMessageErrorDomain.
typedef NS_ENUM(NSInteger, GPBMessageErrorCode) { typedef NS_ENUM(NSInteger, GPBMessageErrorCode) {
/// The data being parsed is bad and a message can not be created from it. /// Uncategorized error.
GPBMessageErrorCodeMalformedData = -100, GPBMessageErrorCodeOther = -100,
/// A message can't be serialized because it is missing required fields. /// A message can't be serialized because it is missing required fields.
GPBMessageErrorCodeMissingRequiredField = -101, GPBMessageErrorCodeMissingRequiredField = -101,
}; };
/// Key under which the error's reason is stored inside the userInfo dictionary.
extern NSString *const GPBErrorReasonKey;
CF_EXTERN_C_END CF_EXTERN_C_END
/// Base class for all of the generated message classes. /// Base class for all of the generated message classes.
...@@ -86,6 +89,9 @@ CF_EXTERN_C_END ...@@ -86,6 +89,9 @@ CF_EXTERN_C_END
/// @note In DEBUG builds, the parsed message is checked to be sure all required /// @note In DEBUG builds, the parsed message is checked to be sure all required
/// fields were provided, and the parse will fail if some are missing. /// fields were provided, and the parse will fail if some are missing.
/// ///
/// @note The errors returned are likely coming from the domain and codes listed
/// at the top of this file and GPBCodedInputStream.h.
///
/// @param data The data to parse. /// @param data The data to parse.
/// @param errorPtr An optional error pointer to fill in with a failure reason if /// @param errorPtr An optional error pointer to fill in with a failure reason if
/// the data can not be parsed. /// the data can not be parsed.
...@@ -101,6 +107,9 @@ CF_EXTERN_C_END ...@@ -101,6 +107,9 @@ CF_EXTERN_C_END
/// @note In DEBUG builds, the parsed message is checked to be sure all required /// @note In DEBUG builds, the parsed message is checked to be sure all required
/// fields were provided, and the parse will fail if some are missing. /// fields were provided, and the parse will fail if some are missing.
/// ///
/// @note The errors returned are likely coming from the domain and codes listed
/// at the top of this file and GPBCodedInputStream.h.
///
/// @param data The data to parse. /// @param data The data to parse.
/// @param extensionRegistry The extension registry to use to look up extensions. /// @param extensionRegistry The extension registry to use to look up extensions.
/// @param errorPtr An optional error pointer to fill in with a failure /// @param errorPtr An optional error pointer to fill in with a failure
...@@ -119,6 +128,9 @@ CF_EXTERN_C_END ...@@ -119,6 +128,9 @@ CF_EXTERN_C_END
/// @note In DEBUG builds, the parsed message is checked to be sure all required /// @note In DEBUG builds, the parsed message is checked to be sure all required
/// fields were provided, and the parse will fail if some are missing. /// fields were provided, and the parse will fail if some are missing.
/// ///
/// @note The errors returned are likely coming from the domain and codes listed
/// at the top of this file and GPBCodedInputStream.h.
///
/// @param input The stream to read data from. /// @param input The stream to read data from.
/// @param extensionRegistry The extension registry to use to look up extensions. /// @param extensionRegistry The extension registry to use to look up extensions.
/// @param errorPtr An optional error pointer to fill in with a failure /// @param errorPtr An optional error pointer to fill in with a failure
...@@ -139,6 +151,9 @@ CF_EXTERN_C_END ...@@ -139,6 +151,9 @@ CF_EXTERN_C_END
/// the required fields are set. So this method can be used to reload /// the required fields are set. So this method can be used to reload
/// messages that may not be complete. /// messages that may not be complete.
/// ///
/// @note The errors returned are likely coming from the domain and codes listed
/// at the top of this file and GPBCodedInputStream.h.
///
/// @param input The stream to read data from. /// @param input The stream to read data from.
/// @param extensionRegistry The extension registry to use to look up extensions. /// @param extensionRegistry The extension registry to use to look up extensions.
/// @param errorPtr An optional error pointer to fill in with a failure /// @param errorPtr An optional error pointer to fill in with a failure
...@@ -158,6 +173,9 @@ CF_EXTERN_C_END ...@@ -158,6 +173,9 @@ CF_EXTERN_C_END
/// @note In DEBUG builds, the parsed message is checked to be sure all required /// @note In DEBUG builds, the parsed message is checked to be sure all required
/// fields were provided, and the parse will fail if some are missing. /// fields were provided, and the parse will fail if some are missing.
/// ///
/// @note The errors returned are likely coming from the domain and codes listed
/// at the top of this file and GPBCodedInputStream.h.
///
/// @param data The data to parse. /// @param data The data to parse.
/// @param errorPtr An optional error pointer to fill in with a failure reason if /// @param errorPtr An optional error pointer to fill in with a failure reason if
/// the data can not be parsed. /// the data can not be parsed.
...@@ -171,6 +189,9 @@ CF_EXTERN_C_END ...@@ -171,6 +189,9 @@ CF_EXTERN_C_END
/// @note In DEBUG builds, the parsed message is checked to be sure all required /// @note In DEBUG builds, the parsed message is checked to be sure all required
/// fields were provided, and the parse will fail if some are missing. /// fields were provided, and the parse will fail if some are missing.
/// ///
/// @note The errors returned are likely coming from the domain and codes listed
/// at the top of this file and GPBCodedInputStream.h.
///
/// @param data The data to parse. /// @param data The data to parse.
/// @param extensionRegistry The extension registry to use to look up extensions. /// @param extensionRegistry The extension registry to use to look up extensions.
/// @param errorPtr An optional error pointer to fill in with a failure /// @param errorPtr An optional error pointer to fill in with a failure
...@@ -188,6 +209,9 @@ CF_EXTERN_C_END ...@@ -188,6 +209,9 @@ CF_EXTERN_C_END
/// the required fields are set. So this method can be used to reload /// the required fields are set. So this method can be used to reload
/// messages that may not be complete. /// messages that may not be complete.
/// ///
/// @note The errors returned are likely coming from the domain and codes listed
/// at the top of this file and GPBCodedInputStream.h.
///
/// @param input The stream to read data from. /// @param input The stream to read data from.
/// @param extensionRegistry The extension registry to use to look up extensions. /// @param extensionRegistry The extension registry to use to look up extensions.
/// @param errorPtr An optional error pointer to fill in with a failure /// @param errorPtr An optional error pointer to fill in with a failure
......
...@@ -53,6 +53,8 @@ ...@@ -53,6 +53,8 @@
NSString *const GPBMessageErrorDomain = NSString *const GPBMessageErrorDomain =
GPBNSStringifySymbol(GPBMessageErrorDomain); GPBNSStringifySymbol(GPBMessageErrorDomain);
NSString *const GPBErrorReasonKey = @"Reason";
static NSString *const kGPBDataCoderKey = @"GPBData"; static NSString *const kGPBDataCoderKey = @"GPBData";
// //
...@@ -96,20 +98,35 @@ static NSMutableDictionary *CloneExtensionMap(NSDictionary *extensionMap, ...@@ -96,20 +98,35 @@ static NSMutableDictionary *CloneExtensionMap(NSDictionary *extensionMap,
NSZone *zone) NSZone *zone)
__attribute__((ns_returns_retained)); __attribute__((ns_returns_retained));
#ifdef DEBUG
static NSError *MessageError(NSInteger code, NSDictionary *userInfo) { static NSError *MessageError(NSInteger code, NSDictionary *userInfo) {
return [NSError errorWithDomain:GPBMessageErrorDomain return [NSError errorWithDomain:GPBMessageErrorDomain
code:code code:code
userInfo:userInfo]; userInfo:userInfo];
} }
#endif
static NSError *MessageErrorWithReason(NSInteger code, NSString *reason) { static NSError *ErrorFromException(NSException *exception) {
NSDictionary *userInfo = nil; NSError *error = nil;
if ([reason length]) {
userInfo = @{ @"Reason" : reason }; if ([exception.name isEqual:GPBCodedInputStreamException]) {
NSDictionary *exceptionInfo = exception.userInfo;
error = exceptionInfo[GPBCodedInputStreamUnderlyingErrorKey];
} }
return MessageError(code, userInfo);
}
if (!error) {
NSString *reason = exception.reason;
NSDictionary *userInfo = nil;
if ([reason length]) {
userInfo = @{ GPBErrorReasonKey : reason };
}
error = [NSError errorWithDomain:GPBMessageErrorDomain
code:GPBMessageErrorCodeOther
userInfo:userInfo];
}
return error;
}
static void CheckExtension(GPBMessage *self, static void CheckExtension(GPBMessage *self,
GPBExtensionDescriptor *extension) { GPBExtensionDescriptor *extension) {
...@@ -817,8 +834,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { ...@@ -817,8 +834,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
[self release]; [self release];
self = nil; self = nil;
if (errorPtr) { if (errorPtr) {
*errorPtr = MessageErrorWithReason(GPBMessageErrorCodeMalformedData, *errorPtr = ErrorFromException(exception);
exception.reason);
} }
} }
#ifdef DEBUG #ifdef DEBUG
...@@ -849,8 +865,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { ...@@ -849,8 +865,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
[self release]; [self release];
self = nil; self = nil;
if (errorPtr) { if (errorPtr) {
*errorPtr = MessageErrorWithReason(GPBMessageErrorCodeMalformedData, *errorPtr = ErrorFromException(exception);
exception.reason);
} }
} }
#ifdef DEBUG #ifdef DEBUG
...@@ -1923,8 +1938,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { ...@@ -1923,8 +1938,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
@catch (NSException *exception) { @catch (NSException *exception) {
message = nil; message = nil;
if (errorPtr) { if (errorPtr) {
*errorPtr = MessageErrorWithReason(GPBMessageErrorCodeMalformedData, *errorPtr = ErrorFromException(exception);
exception.reason);
} }
} }
#ifdef DEBUG #ifdef DEBUG
......
...@@ -881,6 +881,103 @@ static NSData *DataFromCStr(const char *str) { ...@@ -881,6 +881,103 @@ static NSData *DataFromCStr(const char *str) {
XCTAssertEqualObjects(extsParse, extsOrig); XCTAssertEqualObjects(extsParse, extsOrig);
} }
- (void)testErrorSubsectionInvalidLimit {
NSData *data = DataFromCStr(
"\x0A\x08\x0A\x07\x12\x04\x72\x02\x4B\x50\x12\x04\x72\x02\x4B\x50");
NSError *error = nil;
NestedTestAllTypes *msg = [NestedTestAllTypes parseFromData:data
error:&error];
XCTAssertNil(msg);
XCTAssertNotNil(error);
XCTAssertEqualObjects(error.domain, GPBCodedInputStreamErrorDomain);
XCTAssertEqual(error.code, GPBCodedInputStreamErrorInvalidSubsectionLimit);
}
- (void)testErrorSubsectionLimitReached {
NSData *data = DataFromCStr("\x0A\x06\x12\x03\x72\x02\x4B\x50");
NSError *error = nil;
NestedTestAllTypes *msg = [NestedTestAllTypes parseFromData:data
error:&error];
XCTAssertNil(msg);
XCTAssertNotNil(error);
XCTAssertEqualObjects(error.domain, GPBCodedInputStreamErrorDomain);
XCTAssertEqual(error.code, GPBCodedInputStreamErrorSubsectionLimitReached);
}
- (void)testErrorInvalidVarint {
NSData *data = DataFromCStr("\x72\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF");
NSError *error = nil;
TestAllTypes *msg = [TestAllTypes parseFromData:data error:&error];
XCTAssertNil(msg);
XCTAssertNotNil(error);
XCTAssertEqualObjects(error.domain, GPBCodedInputStreamErrorDomain);
XCTAssertEqual(error.code, GPBCodedInputStreamErrorInvalidVarInt);
}
- (void)testErrorInvalidUTF8 {
NSData *data = DataFromCStr("\x72\x04\xF4\xFF\xFF\xFF");
NSError *error = nil;
TestAllTypes *msg = [TestAllTypes parseFromData:data error:&error];
XCTAssertNil(msg);
XCTAssertNotNil(error);
XCTAssertEqualObjects(error.domain, GPBCodedInputStreamErrorDomain);
XCTAssertEqual(error.code, GPBCodedInputStreamErrorInvalidUTF8);
}
- (void)testErrorInvalidSize {
NSData *data = DataFromCStr("\x72\x03\x4B\x50");
NSError *error = nil;
NestedTestAllTypes *msg = [NestedTestAllTypes parseFromData:data
error:&error];
XCTAssertNil(msg);
XCTAssertNotNil(error);
XCTAssertEqualObjects(error.domain, GPBCodedInputStreamErrorDomain);
XCTAssertEqual(error.code, GPBCodedInputStreamErrorInvalidSize);
}
- (void)testErrorInvalidTag {
NSData *data = DataFromCStr("\x0F");
NSError *error = nil;
NestedTestAllTypes *msg = [NestedTestAllTypes parseFromData:data
error:&error];
XCTAssertNil(msg);
XCTAssertNotNil(error);
XCTAssertEqualObjects(error.domain, GPBCodedInputStreamErrorDomain);
XCTAssertEqual(error.code, GPBCodedInputStreamErrorInvalidTag);
}
- (void)testErrorRecursionDepthReached {
NSData *data = DataFromCStr(
"\x0A\x86\x01\x0A\x83\x01\x0A\x80\x01\x0A\x7E\x0A\x7C\x0A\x7A\x0A\x78"
"\x0A\x76\x0A\x74\x0A\x72\x0A\x70\x0A\x6E\x0A\x6C\x0A\x6A\x0A\x68"
"\x0A\x66\x0A\x64\x0A\x62\x0A\x60\x0A\x5E\x0A\x5C\x0A\x5A\x0A\x58"
"\x0A\x56\x0A\x54\x0A\x52\x0A\x50\x0A\x4E\x0A\x4C\x0A\x4A\x0A\x48"
"\x0A\x46\x0A\x44\x0A\x42\x0A\x40\x0A\x3E\x0A\x3C\x0A\x3A\x0A\x38"
"\x0A\x36\x0A\x34\x0A\x32\x0A\x30\x0A\x2E\x0A\x2C\x0A\x2A\x0A\x28"
"\x0A\x26\x0A\x24\x0A\x22\x0A\x20\x0A\x1E\x0A\x1C\x0A\x1A\x0A\x18"
"\x0A\x16\x0A\x14\x0A\x12\x0A\x10\x0A\x0E\x0A\x0C\x0A\x0A\x0A\x08"
"\x0A\x06\x12\x04\x72\x02\x4B\x50");
NSError *error = nil;
NestedTestAllTypes *msg = [NestedTestAllTypes parseFromData:data
error:&error];
XCTAssertNil(msg);
XCTAssertNotNil(error);
XCTAssertEqualObjects(error.domain, GPBCodedInputStreamErrorDomain);
XCTAssertEqual(error.code, GPBCodedInputStreamErrorRecursionDepthExceeded);
}
#ifdef DEBUG
- (void)testErrorMissingRequiredField {
NSData *data = DataFromCStr("");
NSError *error = nil;
TestRequired *msg = [TestRequired parseFromData:data error:&error];
XCTAssertNil(msg);
XCTAssertNotNil(error);
XCTAssertEqualObjects(error.domain, GPBMessageErrorDomain);
XCTAssertEqual(error.code, GPBMessageErrorCodeMissingRequiredField);
}
#endif
#pragma mark - Subset from from map_tests.cc #pragma mark - Subset from from map_tests.cc
// TEST(GeneratedMapFieldTest, StandardWireFormat) // TEST(GeneratedMapFieldTest, StandardWireFormat)
...@@ -989,8 +1086,8 @@ static NSData *DataFromCStr(const char *str) { ...@@ -989,8 +1086,8 @@ static NSData *DataFromCStr(const char *str) {
TestMap *msg = [TestMap parseFromData:data error:&error]; TestMap *msg = [TestMap parseFromData:data error:&error];
XCTAssertNil(msg); XCTAssertNil(msg);
XCTAssertNotNil(error); XCTAssertNotNil(error);
XCTAssertEqualObjects(error.domain, GPBMessageErrorDomain); XCTAssertEqualObjects(error.domain, GPBCodedInputStreamErrorDomain);
XCTAssertEqual(error.code, GPBMessageErrorCodeMalformedData); XCTAssertEqual(error.code, GPBCodedInputStreamErrorInvalidSubsectionLimit);
} }
// TEST(GeneratedMapFieldTest, Proto2UnknownEnum) // TEST(GeneratedMapFieldTest, Proto2UnknownEnum)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment