From 61fc1d6e31ab11752ec13385bd08447021790071 Mon Sep 17 00:00:00 2001 From: Mikkel Nygaard Ravn Date: Wed, 22 Mar 2017 20:23:23 +0100 Subject: [PATCH] Better value coverage of Flutter standard codec on iOS. Unit tests added. (#3498) --- .../ios/framework/Source/FlutterChannels.mm | 175 +++++++----- .../framework/Source/FlutterStandardCodec.mm | 74 ++++- .../Source/flutter_standard_codec_unittest.mm | 267 ++++++++++++------ 3 files changed, 348 insertions(+), 168 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterChannels.mm b/shell/platform/darwin/ios/framework/Source/FlutterChannels.mm index 0f6362861..a15e4edcd 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterChannels.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterChannels.mm @@ -38,33 +38,33 @@ } - (void)sendMessage:(id)message { - [_messenger sendBinaryMessage:[_codec encode:message] - channelName:_name]; + [_messenger sendBinaryMessage:[_codec encode:message] channelName:_name]; } - (void)sendMessage:(id)message replyHandler:(FlutterReplyHandler)handler { + FlutterBinaryReplyHandler replyHandler = ^(NSData* reply) { + if (handler) + handler([_codec decode:reply]); + }; [_messenger sendBinaryMessage:[_codec encode:message] channelName:_name - binaryReplyHandler:^(NSData* reply) { - if (handler) - handler([_codec decode:reply]); - }]; + binaryReplyHandler:replyHandler]; } - (void)setMessageHandler:(FlutterMessageHandler)handler { - if (handler) { - [_messenger setBinaryMessageHandlerOnChannel:_name - binaryMessageHandler:^( - NSData* message, - FlutterBinaryReplyHandler replyHandler) { - handler([_codec decode:message], ^(id reply) { - replyHandler([_codec encode:reply]); - }); - }]; - } else { + if (!handler) { [_messenger setBinaryMessageHandlerOnChannel:_name binaryMessageHandler:nil]; + return; } + FlutterBinaryMessageHandler messageHandler = + ^(NSData* message, FlutterBinaryReplyHandler replyHandler) { + handler([_codec decode:message], ^(id reply) { + replyHandler([_codec encode:reply]); + }); + }; + [_messenger setBinaryMessageHandlerOnChannel:_name + binaryMessageHandler:messageHandler]; } @end @@ -82,6 +82,7 @@ - (instancetype)initWithCode:(NSString*)code message:(NSString*)message details:(id)details { + NSAssert(code, @"Code cannot be nil"); if (self = [super init]) { _code = [code retain]; _message = [message retain]; @@ -96,6 +97,23 @@ [_details release]; [super dealloc]; } + +- (BOOL)isEqual:(id)object { + if (self == object) + return YES; + if (![object isKindOfClass:[FlutterError class]]) + return NO; + FlutterError* other = (FlutterError*)object; + return [self.code isEqual:other.code] && + ((!self.message && !other.message) || + [self.message isEqual:other.message]) && + ((!self.details && !other.details) || + [self.details isEqual:other.details]); +} + +- (NSUInteger)hash { + return [self.code hash] ^ [self.message hash] ^ [self.details hash]; +} @end @implementation FlutterMethodCall @@ -106,6 +124,7 @@ } - (instancetype)initWithMethodName:(NSString*)method arguments:(id)arguments { + NSAssert(method, @"Method name cannot be nil"); if (self = [super init]) { _method = [method retain]; _arguments = [arguments retain]; @@ -118,6 +137,21 @@ [_arguments release]; [super dealloc]; } + +- (BOOL)isEqual:(id)object { + if (self == object) + return YES; + if (![object isKindOfClass:[FlutterMethodCall class]]) + return NO; + FlutterMethodCall* other = (FlutterMethodCall*)object; + return [self.method isEqual:[other method]] && + ((!self.arguments && !other.arguments) || + [self.arguments isEqual:other.arguments]); +} + +- (NSUInteger)hash { + return [self.method hash] ^ [self.arguments hash]; +} @end @implementation FlutterMethodChannel { @@ -153,37 +187,48 @@ } - (void)invokeMethod:(NSString*)method arguments:(id)arguments { - [_messenger sendBinaryMessage:[_codec encodeMethodCall:[FlutterMethodCall methodCallWithMethodName:method arguments:arguments]] - channelName:_name]; + FlutterMethodCall* methodCall = + [FlutterMethodCall methodCallWithMethodName:method arguments:arguments]; + NSData* message = [_codec encodeMethodCall:methodCall]; + [_messenger sendBinaryMessage:message channelName:_name]; } - (void)invokeMethod:(NSString*)method arguments:(id)arguments resultReceiver:(FlutterResultReceiver)resultReceiver { - [_messenger sendBinaryMessage:[_codec encodeMethodCall:[FlutterMethodCall methodCallWithMethodName:method arguments:arguments]] - channelName:_name - binaryReplyHandler:^(NSData* reply) { - if (resultReceiver) { - FlutterError* flutterError = nil; - id result = [_codec decodeEnvelope:reply error:&flutterError]; - resultReceiver(result, flutterError); - } - }]; + FlutterMethodCall* methodCall = + [FlutterMethodCall methodCallWithMethodName:method arguments:arguments]; + NSData* message = [_codec encodeMethodCall:methodCall]; + FlutterBinaryReplyHandler replyHandler = ^(NSData* reply) { + if (resultReceiver) { + FlutterError* flutterError = nil; + id result = [_codec decodeEnvelope:reply error:&flutterError]; + resultReceiver(result, flutterError); + } + }; + [_messenger sendBinaryMessage:message + channelName:_name + binaryReplyHandler:replyHandler]; } - (void)setMethodCallHandler:(FlutterMethodCallHandler)handler { - [_messenger - setBinaryMessageHandlerOnChannel:_name - binaryMessageHandler:^(NSData* message, - FlutterBinaryReplyHandler reply) { - FlutterMethodCall* call = [_codec decodeMethodCall:message]; - handler(call, ^(id result, FlutterError* error) { - if (error) - reply([_codec encodeErrorEnvelope:error]); - else - reply([_codec encodeSuccessEnvelope:result]); - }); - }]; + if (!handler) { + [_messenger setBinaryMessageHandlerOnChannel:_name + binaryMessageHandler:nil]; + return; + } + FlutterBinaryMessageHandler messageHandler = + ^(NSData* message, FlutterBinaryReplyHandler reply) { + FlutterMethodCall* call = [_codec decodeMethodCall:message]; + handler(call, ^(id result, FlutterError* error) { + if (error) + reply([_codec encodeErrorEnvelope:error]); + else + reply([_codec encodeSuccessEnvelope:result]); + }); + }; + [_messenger setBinaryMessageHandlerOnChannel:_name + binaryMessageHandler:messageHandler]; } - (void)setStreamHandler:(FlutterStreamHandler)handler { @@ -192,33 +237,29 @@ binaryMessageHandler:nil]; return; } - [_messenger - setBinaryMessageHandlerOnChannel:_name - binaryMessageHandler:^(NSData* message, - FlutterBinaryReplyHandler reply) { - FlutterMethodCall* call = [_codec decodeMethodCall:message]; - handler( - call, - ^(id result, FlutterError* error) { - if (error) - reply([_codec encodeErrorEnvelope:error]); - else - reply([_codec encodeSuccessEnvelope:nil]); - }, - ^(id event, FlutterError* error, BOOL done) { - if (error) - [_messenger - sendBinaryMessage:[_codec - encodeErrorEnvelope:error] - channelName:_name]; - else if (done) - [_messenger sendBinaryMessage:[NSData data] - channelName:_name]; - else - [_messenger sendBinaryMessage: - [_codec encodeSuccessEnvelope:event] - channelName:_name]; - }); - }]; + FlutterBinaryMessageHandler messageHandler = ^( + NSData* message, FlutterBinaryReplyHandler reply) { + FlutterMethodCall* call = [_codec decodeMethodCall:message]; + FlutterResultReceiver resultReceiver = ^(id result, FlutterError* error) { + if (error) + reply([_codec encodeErrorEnvelope:error]); + else + reply([_codec encodeSuccessEnvelope:nil]); + }; + FlutterEventReceiver eventReceiver = + ^(id event, FlutterError* error, BOOL done) { + if (error) + [_messenger sendBinaryMessage:[_codec encodeErrorEnvelope:error] + channelName:_name]; + else if (done) + [_messenger sendBinaryMessage:[NSData data] channelName:_name]; + else + [_messenger sendBinaryMessage:[_codec encodeSuccessEnvelope:event] + channelName:_name]; + }; + handler(call, resultReceiver, eventReceiver); + }; + [_messenger setBinaryMessageHandlerOnChannel:_name + binaryMessageHandler:messageHandler]; } @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterStandardCodec.mm b/shell/platform/darwin/ios/framework/Source/FlutterStandardCodec.mm index d110251ce..c8e22a78f 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterStandardCodec.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterStandardCodec.mm @@ -89,19 +89,20 @@ case 0: { result = [reader readValue]; NSAssert(![reader hasMore], @"Corrupted standard envelope"); - } - break; + } break; case 1: { id code = [reader readValue]; id message = [reader readValue]; id details = [reader readValue]; NSAssert(![reader hasMore], @"Corrupted standard envelope"); - NSAssert([code isKindOfClass:[NSString class]], @"Invalid standard envelope"); - NSAssert(message == nil || [message isKindOfClass:[NSString class]], @"Invalid standard envelope"); - *error = [FlutterError errorWithCode:code message:message details:details]; + NSAssert([code isKindOfClass:[NSString class]], + @"Invalid standard envelope"); + NSAssert(message == nil || [message isKindOfClass:[NSString class]], + @"Invalid standard envelope"); + *error = + [FlutterError errorWithCode:code message:message details:details]; result = nil; - } - break; + } break; } return result; } @@ -144,6 +145,7 @@ using namespace shell; - (instancetype)initWithData:(NSData*)data type:(FlutterStandardDataType)type { UInt8 elementSize = elementSizeForFlutterStandardDataType(type); + NSAssert(data, @"Data cannot be nil"); NSAssert(data.length % elementSize == 0, @"Data must contain integral number of elements"); if (self = [super init]) { @@ -159,6 +161,20 @@ using namespace shell; [_data release]; [super dealloc]; } + +- (BOOL)isEqual:(id)object { + if (self == object) + return YES; + if (![object isKindOfClass:[FlutterStandardTypedData class]]) + return NO; + FlutterStandardTypedData* other = (FlutterStandardTypedData*)object; + return self.type == other.type && self.elementCount == other.elementCount && + [self.data isEqual:other.data]; +} + +- (NSUInteger)hash { + return [self.data hash] ^ self.type; +} @end @implementation FlutterStandardBigInteger @@ -167,6 +183,7 @@ using namespace shell; } - (instancetype)initWithHex:(NSString*)hex { + NSAssert(hex, @"Hex cannot be nil"); if (self = [super init]) { _hex = [hex retain]; } @@ -177,6 +194,19 @@ using namespace shell; [_hex release]; [super dealloc]; } + +- (BOOL)isEqual:(id)object { + if (self == object) + return YES; + if (![object isKindOfClass:[FlutterStandardBigInteger class]]) + return NO; + FlutterStandardBigInteger* other = (FlutterStandardBigInteger*)object; + return [self.hex isEqual:other.hex]; +} + +- (NSUInteger)hash { + return [self.hex hash]; +} @end #pragma mark - Writer and reader of standard codec @@ -242,24 +272,37 @@ using namespace shell; } else if ([value isKindOfClass:[NSNumber class]]) { NSNumber* number = value; const char* type = [number objCType]; - if (strcmp(type, @encode(BOOL)) == 0) { + if ([self isBool:number type:type]) { BOOL b = number.boolValue; [self writeByte:(b ? FlutterStandardFieldTrue : FlutterStandardFieldFalse)]; - } else if (strcmp(type, @encode(int)) == 0) { + } else if (strcmp(type, @encode(signed int)) == 0 || + strcmp(type, @encode(signed short)) == 0 || + strcmp(type, @encode(unsigned short)) == 0 || + strcmp(type, @encode(signed char)) == 0 || + strcmp(type, @encode(unsigned char)) == 0) { SInt32 n = number.intValue; [self writeByte:FlutterStandardFieldInt32]; [_data appendBytes:(UInt8*)&n length:4]; - } else if (strcmp(type, @encode(long)) == 0) { + } else if (strcmp(type, @encode(signed long)) == 0 || + strcmp(type, @encode(unsigned int)) == 0) { SInt64 n = number.longValue; [self writeByte:FlutterStandardFieldInt64]; [_data appendBytes:(UInt8*)&n length:8]; - } else if (strcmp(type, @encode(double)) == 0) { + } else if (strcmp(type, @encode(double)) == 0 || + strcmp(type, @encode(float)) == 0) { Float64 f = number.doubleValue; [self writeByte:FlutterStandardFieldFloat64]; [_data appendBytes:(UInt8*)&f length:8]; + } else if (strcmp(type, @encode(unsigned long)) == 0 || + strcmp(type, @encode(signed long long)) == 0 || + strcmp(type, @encode(unsigned long long)) == 0) { + NSString* hex = + [NSString stringWithFormat:@"%llx", number.unsignedLongLongValue]; + [self writeByte:FlutterStandardFieldIntHex]; + [self writeUTF8:hex]; } else { - NSLog(@"Unsupported value: %@", value); + NSLog(@"Unsupported value: %@ of type %s", value, type); NSAssert(NO, @"Unsupported value for standard codec"); } } else if ([value isKindOfClass:[NSString class]]) { @@ -292,10 +335,15 @@ using namespace shell; [self writeValue:[dict objectForKey:key]]; } } else { - NSLog(@"Unsupported value: %@", value); + NSLog(@"Unsupported value: %@ of type %@", value, [value class]); NSAssert(NO, @"Unsupported value for standard codec"); } } + +- (BOOL)isBool:(NSNumber*)number type:(const char*)type { + return strcmp(type, @encode(signed char)) == 0 && + [NSStringFromClass([number class]) isEqual:@"__NSCFBoolean"]; +} @end @implementation FlutterStandardReader { diff --git a/shell/platform/darwin/ios/framework/Source/flutter_standard_codec_unittest.mm b/shell/platform/darwin/ios/framework/Source/flutter_standard_codec_unittest.mm index 81e2f79de..4c404d076 100644 --- a/shell/platform/darwin/ios/framework/Source/flutter_standard_codec_unittest.mm +++ b/shell/platform/darwin/ios/framework/Source/flutter_standard_codec_unittest.mm @@ -5,89 +5,137 @@ #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterCodecs.h" #include "gtest/gtest.h" -TEST(FlutterStandardCodec, CanEncodeAndDecodeNil) { +void checkEncodeDecode(id value, NSData* expectedEncoding) { FlutterStandardMessageCodec* codec = [FlutterStandardMessageCodec sharedInstance]; - NSData* encoded = [codec encode:nil]; + NSData* encoded = [codec encode:value]; + ASSERT_TRUE([encoded isEqual:expectedEncoding]); id decoded = [codec decode:encoded]; - ASSERT_TRUE(decoded == nil); + if (value == nil || value == [NSNull null]) + ASSERT_TRUE(decoded == nil); + else + ASSERT_TRUE([value isEqual:decoded]); } -TEST(FlutterStandardCodec, CanEncodeAndDecodeNSNull) { +void checkEncodeDecode(id value) { FlutterStandardMessageCodec* codec = [FlutterStandardMessageCodec sharedInstance]; - NSData* encoded = [codec encode:[NSNull null]]; + NSData* encoded = [codec encode:value]; id decoded = [codec decode:encoded]; - ASSERT_TRUE(decoded == nil); + if (value == nil || value == [NSNull null]) + ASSERT_TRUE(decoded == nil); + else + ASSERT_TRUE([value isEqual:decoded]); } -TEST(FlutterStandardCodec, CanEncodeAndDecodeInt32) { - FlutterStandardMessageCodec* codec = - [FlutterStandardMessageCodec sharedInstance]; - NSData* encoded = [codec encode:@-78]; - NSNumber* decoded = [codec decode:encoded]; - ASSERT_TRUE([@-78 isEqualTo:decoded]); +TEST(FlutterStandardCodec, CanEncodeAndDecodeNil) { + char bytes[1] = {0x00}; + checkEncodeDecode(nil, [NSData dataWithBytes:bytes length:1]); +} + +TEST(FlutterStandardCodec, CanEncodeAndDecodeNSNull) { + char bytes[1] = {0x00}; + checkEncodeDecode([NSNull null], [NSData dataWithBytes:bytes length:1]); +} + +TEST(FlutterStandardCodec, CanEncodeAndDecodeYes) { + char bytes[1] = {0x01}; + checkEncodeDecode(@YES, [NSData dataWithBytes:bytes length:1]); +} + +TEST(FlutterStandardCodec, CanEncodeAndDecodeNo) { + char bytes[1] = {0x02}; + checkEncodeDecode(@NO, [NSData dataWithBytes:bytes length:1]); +} + +TEST(FlutterStandardCodec, CanEncodeAndDecodeUInt8) { + char bytes[5] = {0x03, 0xfe, 0x00, 0x00, 0x00}; + UInt8 value = 0xfe; + checkEncodeDecode(@(value), [NSData dataWithBytes:bytes length:5]); +} + +TEST(FlutterStandardCodec, CanEncodeAndDecodeUInt16) { + char bytes[5] = {0x03, 0xdc, 0xfe, 0x00, 0x00}; + UInt16 value = 0xfedc; + checkEncodeDecode(@(value), [NSData dataWithBytes:bytes length:5]); +} + +TEST(FlutterStandardCodec, CanEncodeAndDecodeUInt32) { + char bytes[9] = {0x04, 0x09, 0xba, 0xdc, 0xfe, 0x00, 0x00, 0x00, 0x00}; + UInt32 value = 0xfedcba09; + checkEncodeDecode(@(value), [NSData dataWithBytes:bytes length:9]); } -TEST(FlutterStandardCodec, CanEncodeAndDecodeInt64) { +TEST(FlutterStandardCodec, CanEncodeAndDecodeUInt64AsHexString) { FlutterStandardMessageCodec* codec = [FlutterStandardMessageCodec sharedInstance]; - NSData* encoded = [codec encode:@78000000001]; - NSNumber* decoded = [codec decode:encoded]; - ASSERT_TRUE([@78000000001 isEqualTo:decoded]); + UInt64 u64 = 0xfffffffffffffffa; + NSData* encoded = [codec encode:@(u64)]; + FlutterStandardBigInteger* decoded = [codec decode:encoded]; + ASSERT_TRUE([decoded.hex isEqual:@"fffffffffffffffa"]); +} + +TEST(FlutterStandardCodec, CanEncodeAndDecodeSInt8) { + char bytes[5] = {0x03, 0xfe, 0xff, 0xff, 0xff}; + SInt8 value = 0xfe; + checkEncodeDecode(@(value), [NSData dataWithBytes:bytes length:5]); +} + +TEST(FlutterStandardCodec, CanEncodeAndDecodeSInt16) { + char bytes[5] = {0x03, 0xdc, 0xfe, 0xff, 0xff}; + SInt16 value = 0xfedc; + checkEncodeDecode(@(value), [NSData dataWithBytes:bytes length:5]); +} + +TEST(FlutterStandardCodec, CanEncodeAndDecodeSInt32) { + char bytes[5] = {0x03, 0x78, 0x56, 0x34, 0x12}; + checkEncodeDecode(@(0x12345678), [NSData dataWithBytes:bytes length:5]); +} + +TEST(FlutterStandardCodec, CanEncodeAndDecodeSInt64) { + char bytes[9] = {0x04, 0xef, 0xcd, 0xab, 0x90, 0x78, 0x56, 0x34, 0x12}; + checkEncodeDecode(@(0x1234567890abcdef), + [NSData dataWithBytes:bytes length:9]); +} + +TEST(FlutterStandardCodec, CanEncodeAndDecodeBigInteger) { + FlutterStandardBigInteger* value = [FlutterStandardBigInteger + bigIntegerWithHex:@"-abcdef0123456789abcdef01234567890"]; + checkEncodeDecode(value); +} + +TEST(FlutterStandardCodec, CanEncodeAndDecodeFloat32) { + char bytes[9] = {0x06, 0x00, 0x00, 0x00, 0x60, 0xfb, 0x21, 0x09, 0x40}; + checkEncodeDecode(@3.1415927f, [NSData dataWithBytes:bytes length:9]); } TEST(FlutterStandardCodec, CanEncodeAndDecodeFloat64) { - FlutterStandardMessageCodec* codec = - [FlutterStandardMessageCodec sharedInstance]; - NSData* encoded = [codec encode:@3.14]; - NSNumber* decoded = [codec decode:encoded]; - ASSERT_TRUE([@3.14 isEqualTo:decoded]); + char bytes[9] = {0x06, 0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40}; + checkEncodeDecode(@3.14159265358979311599796346854, + [NSData dataWithBytes:bytes length:9]); } TEST(FlutterStandardCodec, CanEncodeAndDecodeString) { - FlutterStandardMessageCodec* codec = - [FlutterStandardMessageCodec sharedInstance]; - NSData* encoded = [codec encode:@"hello world"]; - NSString* decoded = [codec decode:encoded]; - ASSERT_TRUE([@"hello world" isEqualTo:decoded]); + char bytes[13] = {0x07, 0x0b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, + 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64}; + checkEncodeDecode(@"hello world", [NSData dataWithBytes:bytes length:13]); } TEST(FlutterStandardCodec, CanEncodeAndDecodeStringWithNonAsciiCodePoint) { - FlutterStandardMessageCodec* codec = - [FlutterStandardMessageCodec sharedInstance]; - NSData* encoded = [codec encode:@"hello \u263A world"]; - NSString* decoded = [codec decode:encoded]; - ASSERT_TRUE([@"hello \u263A world" isEqualTo:decoded]); + char bytes[7] = {0x07, 0x05, 0x68, 0xe2, 0x98, 0xba, 0x77}; + checkEncodeDecode(@"h\u263Aw", [NSData dataWithBytes:bytes length:7]); } TEST(FlutterStandardCodec, CanEncodeAndDecodeStringWithNonBMPCodePoint) { - FlutterStandardMessageCodec* codec = - [FlutterStandardMessageCodec sharedInstance]; - NSData* encoded = [codec encode:@"hello \U0001F602 world"]; - NSString* decoded = [codec decode:encoded]; - ASSERT_TRUE([@"hello \U0001F602 world" isEqualTo:decoded]); -} - -TEST(FlutterStandardCodec, CanEncodeAndDecodeBigInteger) { - FlutterStandardMessageCodec* codec = - [FlutterStandardMessageCodec sharedInstance]; - NSData* encoded = - [codec encode:[FlutterStandardBigInteger - bigIntegerWithHex:@"-abcdef120902390239021321abfdec"]]; - FlutterStandardBigInteger* decoded = [codec decode:encoded]; - ASSERT_TRUE([@"-abcdef120902390239021321abfdec" isEqualTo:decoded.hex]); + char bytes[8] = {0x07, 0x06, 0x68, 0xf0, 0x9f, 0x98, 0x82, 0x77}; + checkEncodeDecode(@"h\U0001F602w", [NSData dataWithBytes:bytes length:8]); } TEST(FlutterStandardCodec, CanEncodeAndDecodeArray) { NSArray* value = @[ [NSNull null], @"hello", @3.14, @47, @{ @42 : @"nested" } ]; - FlutterStandardMessageCodec* codec = - [FlutterStandardMessageCodec sharedInstance]; - NSData* encoded = [codec encode:value]; - NSArray* decoded = [codec decode:encoded]; - ASSERT_TRUE([value isEqualTo:decoded]); + checkEncodeDecode(value); } TEST(FlutterStandardCodec, CanEncodeAndDecodeDictionary) { @@ -97,11 +145,7 @@ TEST(FlutterStandardCodec, CanEncodeAndDecodeDictionary) { [NSNull null] : [NSNull null], @3.14 : @[ @"nested" ] }; - FlutterStandardMessageCodec* codec = - [FlutterStandardMessageCodec sharedInstance]; - NSData* encoded = [codec encode:value]; - NSDictionary* decoded = [codec decode:encoded]; - ASSERT_TRUE([value isEqualTo:decoded]); + checkEncodeDecode(value); } TEST(FlutterStandardCodec, CanEncodeAndDecodeByteArray) { @@ -109,14 +153,7 @@ TEST(FlutterStandardCodec, CanEncodeAndDecodeByteArray) { NSData* data = [NSData dataWithBytes:bytes length:4]; FlutterStandardTypedData* value = [FlutterStandardTypedData typedDataWithBytes:data]; - FlutterStandardMessageCodec* codec = - [FlutterStandardMessageCodec sharedInstance]; - NSData* encoded = [codec encode:value]; - FlutterStandardTypedData* decoded = [codec decode:encoded]; - ASSERT_TRUE(decoded.type == FlutterStandardDataTypeUInt8); - ASSERT_TRUE(decoded.elementCount == 4); - ASSERT_TRUE(decoded.elementSize == 1); - ASSERT_TRUE([data isEqualTo:decoded.data]); + checkEncodeDecode(value); } TEST(FlutterStandardCodec, CanEncodeAndDecodeInt32Array) { @@ -124,14 +161,7 @@ TEST(FlutterStandardCodec, CanEncodeAndDecodeInt32Array) { NSData* data = [NSData dataWithBytes:bytes length:8]; FlutterStandardTypedData* value = [FlutterStandardTypedData typedDataWithInt32:data]; - FlutterStandardMessageCodec* codec = - [FlutterStandardMessageCodec sharedInstance]; - NSData* encoded = [codec encode:value]; - FlutterStandardTypedData* decoded = [codec decode:encoded]; - ASSERT_TRUE(decoded.type == FlutterStandardDataTypeInt32); - ASSERT_TRUE(decoded.elementCount == 2); - ASSERT_TRUE(decoded.elementSize == 4); - ASSERT_TRUE([data isEqualTo:decoded.data]); + checkEncodeDecode(value); } TEST(FlutterStandardCodec, CanEncodeAndDecodeInt64Array) { @@ -139,14 +169,7 @@ TEST(FlutterStandardCodec, CanEncodeAndDecodeInt64Array) { NSData* data = [NSData dataWithBytes:bytes length:8]; FlutterStandardTypedData* value = [FlutterStandardTypedData typedDataWithInt64:data]; - FlutterStandardMessageCodec* codec = - [FlutterStandardMessageCodec sharedInstance]; - NSData* encoded = [codec encode:value]; - FlutterStandardTypedData* decoded = [codec decode:encoded]; - ASSERT_TRUE(decoded.type == FlutterStandardDataTypeInt64); - ASSERT_TRUE(decoded.elementCount == 1); - ASSERT_TRUE(decoded.elementSize == 8); - ASSERT_TRUE([data isEqualTo:decoded.data]); + checkEncodeDecode(value); } TEST(FlutterStandardCodec, CanEncodeAndDecodeFloat64Array) { @@ -155,12 +178,80 @@ TEST(FlutterStandardCodec, CanEncodeAndDecodeFloat64Array) { NSData* data = [NSData dataWithBytes:bytes length:16]; FlutterStandardTypedData* value = [FlutterStandardTypedData typedDataWithFloat64:data]; - FlutterStandardMessageCodec* codec = - [FlutterStandardMessageCodec sharedInstance]; - NSData* encoded = [codec encode:value]; - FlutterStandardTypedData* decoded = [codec decode:encoded]; - ASSERT_TRUE(decoded.type == FlutterStandardDataTypeFloat64); - ASSERT_TRUE(decoded.elementCount == 2); - ASSERT_TRUE(decoded.elementSize == 8); - ASSERT_TRUE([data isEqualTo:decoded.data]); + checkEncodeDecode(value); +} + +TEST(FlutterStandardCodec, HandlesMethodCallsWithNilArguments) { + FlutterStandardMethodCodec* codec = + [FlutterStandardMethodCodec sharedInstance]; + FlutterMethodCall* call = + [FlutterMethodCall methodCallWithMethodName:@"hello" arguments:nil]; + NSData* encoded = [codec encodeMethodCall:call]; + FlutterMethodCall* decoded = [codec decodeMethodCall:encoded]; + ASSERT_TRUE([decoded isEqual:call]); +} + +TEST(FlutterStandardCodec, HandlesMethodCallsWithSingleArgument) { + FlutterStandardMethodCodec* codec = + [FlutterStandardMethodCodec sharedInstance]; + FlutterMethodCall* call = + [FlutterMethodCall methodCallWithMethodName:@"hello" arguments:@42]; + NSData* encoded = [codec encodeMethodCall:call]; + FlutterMethodCall* decoded = [codec decodeMethodCall:encoded]; + ASSERT_TRUE([decoded isEqual:call]); +} + +TEST(FlutterStandardCodec, HandlesMethodCallsWithArgumentList) { + FlutterStandardMethodCodec* codec = + [FlutterStandardMethodCodec sharedInstance]; + NSArray* arguments = @[ @42, @"world" ]; + FlutterMethodCall* call = + [FlutterMethodCall methodCallWithMethodName:@"hello" arguments:arguments]; + NSData* encoded = [codec encodeMethodCall:call]; + FlutterMethodCall* decoded = [codec decodeMethodCall:encoded]; + ASSERT_TRUE([decoded isEqual:call]); +} + +TEST(FlutterStandardCodec, HandlesSuccessEnvelopesWithNilResult) { + FlutterStandardMethodCodec* codec = + [FlutterStandardMethodCodec sharedInstance]; + NSData* encoded = [codec encodeSuccessEnvelope:nil]; + FlutterError* error = nil; + id decoded = [codec decodeEnvelope:encoded error:&error]; + ASSERT_TRUE(error == nil); + ASSERT_TRUE(decoded == nil); +} + +TEST(FlutterStandardCodec, HandlesSuccessEnvelopesWithSingleResult) { + FlutterStandardMethodCodec* codec = + [FlutterStandardMethodCodec sharedInstance]; + NSData* encoded = [codec encodeSuccessEnvelope:@42]; + FlutterError* decodedError = nil; + id decodedResult = [codec decodeEnvelope:encoded error:&decodedError]; + ASSERT_TRUE(decodedError == nil); + ASSERT_TRUE([decodedResult isEqual:@42]); +} + +TEST(FlutterStandardCodec, HandlesSuccessEnvelopesWithResultMap) { + FlutterStandardMethodCodec* codec = + [FlutterStandardMethodCodec sharedInstance]; + NSDictionary* result = @{ @"a" : @42, @42 : @"a" }; + NSData* encoded = [codec encodeSuccessEnvelope:result]; + FlutterError* decodedError = nil; + id decodedResult = [codec decodeEnvelope:encoded error:&decodedError]; + ASSERT_TRUE([decodedResult isEqual:result]); +} + +TEST(FlutterStandardCodec, HandlesErrorEnvelopes) { + FlutterStandardMethodCodec* codec = + [FlutterStandardMethodCodec sharedInstance]; + NSDictionary* details = @{ @"a" : @42, @42 : @"a" }; + FlutterError* error = [FlutterError errorWithCode:@"errorCode" + message:@"something failed" + details:details]; + NSData* encoded = [codec encodeErrorEnvelope:error]; + FlutterError* decodedError = nil; + id decodedResult = [codec decodeEnvelope:encoded error:&decodedError]; + ASSERT_TRUE(decodedResult == nil); + ASSERT_TRUE([decodedError isEqual:error]); } -- GitLab