Flutter iOS Embedder
flutter_standard_codec_unittest.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
6 
7 #include <CoreFoundation/CoreFoundation.h>
8 
9 #include "gtest/gtest.h"
10 
12 
13 @interface Pair : NSObject
14 @property(atomic, readonly, strong, nullable) NSObject* left;
15 @property(atomic, readonly, strong, nullable) NSObject* right;
16 - (instancetype)initWithLeft:(NSObject*)first right:(NSObject*)right;
17 @end
18 
19 @implementation Pair
20 - (instancetype)initWithLeft:(NSObject*)left right:(NSObject*)right {
21  self = [super init];
22  if (self) {
23  _left = left;
24  _right = right;
25  }
26  return self;
27 }
28 @end
29 
30 static const UInt8 kDATE = 128;
31 static const UInt8 kPAIR = 129;
32 
34 - (void)writeValue:(id)value;
35 @end
36 
37 @implementation ExtendedWriter
38 - (void)writeValue:(id)value {
39  if ([value isKindOfClass:[NSDate class]]) {
40  [self writeByte:kDATE];
41  NSDate* date = value;
42  NSTimeInterval time = date.timeIntervalSince1970;
43  SInt64 ms = (SInt64)(time * 1000.0);
44  [self writeBytes:&ms length:8];
45  } else if ([value isKindOfClass:[Pair class]]) {
46  Pair* pair = value;
47  [self writeByte:kPAIR];
48  [self writeValue:pair.left];
49  [self writeValue:pair.right];
50  } else {
51  [super writeValue:value];
52  }
53 }
54 @end
55 
57 - (id)readValueOfType:(UInt8)type;
58 @end
59 
60 @implementation ExtendedReader
61 - (id)readValueOfType:(UInt8)type {
62  switch (type) {
63  case kDATE: {
64  SInt64 value;
65  [self readBytes:&value length:8];
66  NSTimeInterval time = [NSNumber numberWithLong:value].doubleValue / 1000.0;
67  return [NSDate dateWithTimeIntervalSince1970:time];
68  }
69  case kPAIR: {
70  return [[Pair alloc] initWithLeft:[self readValue] right:[self readValue]];
71  }
72  default:
73  return [super readValueOfType:type];
74  }
75 }
76 @end
77 
79 - (FlutterStandardWriter*)writerWithData:(NSMutableData*)data;
80 - (FlutterStandardReader*)readerWithData:(NSData*)data;
81 @end
82 
83 @implementation ExtendedReaderWriter
84 - (FlutterStandardWriter*)writerWithData:(NSMutableData*)data {
85  return [[ExtendedWriter alloc] initWithData:data];
86 }
87 - (FlutterStandardReader*)readerWithData:(NSData*)data {
88  return [[ExtendedReader alloc] initWithData:data];
89 }
90 @end
91 
92 static void CheckEncodeDecode(id value, NSData* expectedEncoding) {
94  NSData* encoded = [codec encode:value];
95  if (expectedEncoding == nil) {
96  ASSERT_TRUE(encoded == nil);
97  } else {
98  ASSERT_TRUE([encoded isEqual:expectedEncoding]);
99  }
100  id decoded = [codec decode:encoded];
101  if (value == nil || value == [NSNull null]) {
102  ASSERT_TRUE(decoded == nil);
103  } else {
104  ASSERT_TRUE([value isEqual:decoded]);
105  }
106 }
107 
108 static void CheckEncodeDecode(id value) {
110  NSData* encoded = [codec encode:value];
111  id decoded = [codec decode:encoded];
112  if (value == nil || value == [NSNull null]) {
113  ASSERT_TRUE(decoded == nil);
114  } else {
115  ASSERT_TRUE([value isEqual:decoded]);
116  }
117 }
118 
119 TEST(FlutterStandardCodec, CanDecodeZeroLength) {
121  id decoded = [codec decode:[NSData data]];
122  ASSERT_TRUE(decoded == nil);
123 }
124 
125 TEST(FlutterStandardCodec, CanEncodeAndDecodeNil) {
126  CheckEncodeDecode(nil, nil);
127 }
128 
129 TEST(FlutterStandardCodec, CanEncodeAndDecodeNSNull) {
130  uint8_t bytes[1] = {0x00};
131  CheckEncodeDecode([NSNull null], [NSData dataWithBytes:bytes length:1]);
132 }
133 
134 TEST(FlutterStandardCodec, CanEncodeAndDecodeYes) {
135  uint8_t bytes[1] = {0x01};
136  CheckEncodeDecode(@YES, [NSData dataWithBytes:bytes length:1]);
137 }
138 
139 TEST(FlutterStandardCodec, CanEncodeAndDecodeNo) {
140  uint8_t bytes[1] = {0x02};
141  CheckEncodeDecode(@NO, [NSData dataWithBytes:bytes length:1]);
142 }
143 
144 TEST(FlutterStandardCodec, CanEncodeAndDecodeUInt8) {
145  uint8_t bytes[5] = {0x03, 0xfe, 0x00, 0x00, 0x00};
146  UInt8 value = 0xfe;
147  CheckEncodeDecode(@(value), [NSData dataWithBytes:bytes length:5]);
148 }
149 
150 TEST(FlutterStandardCodec, CanEncodeAndDecodeUInt16) {
151  uint8_t bytes[5] = {0x03, 0xdc, 0xfe, 0x00, 0x00};
152  UInt16 value = 0xfedc;
153  CheckEncodeDecode(@(value), [NSData dataWithBytes:bytes length:5]);
154 }
155 
156 TEST(FlutterStandardCodec, CanEncodeAndDecodeUInt32) {
157  uint8_t bytes[9] = {0x04, 0x09, 0xba, 0xdc, 0xfe, 0x00, 0x00, 0x00, 0x00};
158  UInt32 value = 0xfedcba09;
159  CheckEncodeDecode(@(value), [NSData dataWithBytes:bytes length:9]);
160 }
161 
162 TEST(FlutterStandardCodec, CanEncodeUInt64) {
164  UInt64 u64 = 0xfffffffffffffffa;
165  uint8_t bytes[9] = {0x04, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
166  NSData* encoded = [codec encode:@(u64)];
167  ASSERT_TRUE([encoded isEqual:[NSData dataWithBytes:bytes length:9]]);
168 }
169 
170 TEST(FlutterStandardCodec, CanEncodeAndDecodeSInt8) {
171  uint8_t bytes[5] = {0x03, 0xfe, 0xff, 0xff, 0xff};
172  SInt8 value = 0xfe;
173  CheckEncodeDecode(@(value), [NSData dataWithBytes:bytes length:5]);
174 }
175 
176 TEST(FlutterStandardCodec, CanEncodeAndDecodeSInt16) {
177  uint8_t bytes[5] = {0x03, 0xdc, 0xfe, 0xff, 0xff};
178  SInt16 value = 0xfedc;
179  CheckEncodeDecode(@(value), [NSData dataWithBytes:bytes length:5]);
180 }
181 
182 TEST(FlutterStandardCodec, CanEncodeAndDecodeSInt32) {
183  uint8_t bytes[5] = {0x03, 0x78, 0x56, 0x34, 0x12};
184  CheckEncodeDecode(@(0x12345678), [NSData dataWithBytes:bytes length:5]);
185 }
186 
187 TEST(FlutterStandardCodec, CanEncodeAndDecodeSInt64) {
188  uint8_t bytes[9] = {0x04, 0xef, 0xcd, 0xab, 0x90, 0x78, 0x56, 0x34, 0x12};
189  CheckEncodeDecode(@(0x1234567890abcdef), [NSData dataWithBytes:bytes length:9]);
190 }
191 
192 TEST(FlutterStandardCodec, CanEncodeAndDecodeFloat32) {
193  uint8_t bytes[16] = {0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
194  0x00, 0x00, 0x00, 0x60, 0xfb, 0x21, 0x09, 0x40};
195  CheckEncodeDecode(@3.1415927f, [NSData dataWithBytes:bytes length:16]);
196 }
197 
198 TEST(FlutterStandardCodec, CanEncodeAndDecodeFloat64) {
199  uint8_t bytes[16] = {0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
200  0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40};
201  CheckEncodeDecode(@3.14159265358979311599796346854, [NSData dataWithBytes:bytes length:16]);
202 }
203 
204 TEST(FlutterStandardCodec, CanEncodeAndDecodeString) {
205  uint8_t bytes[13] = {0x07, 0x0b, 0x68, 0x65, 0x6c, 0x6c, 0x6f,
206  0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64};
207  CheckEncodeDecode(@"hello world", [NSData dataWithBytes:bytes length:13]);
208 }
209 
210 TEST(FlutterStandardCodec, CanEncodeAndDecodeStringWithNonAsciiCodePoint) {
211  uint8_t bytes[7] = {0x07, 0x05, 0x68, 0xe2, 0x98, 0xba, 0x77};
212  CheckEncodeDecode(@"h\u263Aw", [NSData dataWithBytes:bytes length:7]);
213 }
214 
215 TEST(FlutterStandardCodec, CanEncodeAndDecodeStringWithNonBMPCodePoint) {
216  uint8_t bytes[8] = {0x07, 0x06, 0x68, 0xf0, 0x9f, 0x98, 0x82, 0x77};
217  CheckEncodeDecode(@"h\U0001F602w", [NSData dataWithBytes:bytes length:8]);
218 }
219 
220 TEST(FlutterStandardCodec, CanEncodeAndDecodeIndirectString) {
221  // This test ensures that an indirect NSString, whose internal string buffer
222  // can't be simply returned by `CFStringGetCStringPtr`, can be encoded without
223  // violating the memory sanitizer. This test only works with `--asan` flag.
224  // See https://github.com/flutter/flutter/issues/142101
225  uint8_t bytes[7] = {0x07, 0x05, 0x68, 0xe2, 0x98, 0xba, 0x77};
226  NSString* target = @"h\u263Aw";
227  // Ensures that this is an indirect string so that this test makes sense.
228  ASSERT_TRUE(CFStringGetCStringPtr((__bridge CFStringRef)target, kCFStringEncodingUTF8) ==
229  nullptr);
230  CheckEncodeDecode(target, [NSData dataWithBytes:bytes length:7]);
231 }
232 
233 TEST(FlutterStandardCodec, CanEncodeAndDecodeArray) {
234  NSArray* value = @[ [NSNull null], @"hello", @3.14, @47, @{@42 : @"nested"} ];
235  CheckEncodeDecode(value);
236 }
237 
238 TEST(FlutterStandardCodec, CanEncodeAndDecodeDictionary) {
239  NSDictionary* value =
240  @{@"a" : @3.14,
241  @"b" : @47,
242  [NSNull null] : [NSNull null],
243  @3.14 : @[ @"nested" ]};
244  CheckEncodeDecode(value);
245 }
246 
247 TEST(FlutterStandardCodec, CanEncodeAndDecodeByteArray) {
248  uint8_t bytes[4] = {0xBA, 0x5E, 0xBA, 0x11};
249  NSData* data = [NSData dataWithBytes:bytes length:4];
251  CheckEncodeDecode(value);
252 }
253 
254 TEST(FlutterStandardCodec, CanEncodeAndDecodeNSData) {
256  uint8_t bytes[4] = {0xBA, 0x5E, 0xBA, 0x11};
257  NSData* data = [NSData dataWithBytes:bytes length:4];
259 
260  NSData* encoded = [codec encode:data];
261  ASSERT_TRUE([encoded isEqual:[codec encode:standardData]]);
262 }
263 
264 TEST(FlutterStandardCodec, CanEncodeAndDecodeInt32Array) {
265  uint8_t bytes[8] = {0xBA, 0x5E, 0xBA, 0x11, 0xff, 0xff, 0xff, 0xff};
266  NSData* data = [NSData dataWithBytes:bytes length:8];
268  CheckEncodeDecode(value);
269 }
270 
271 TEST(FlutterStandardCodec, CanEncodeAndDecodeInt64Array) {
272  uint8_t bytes[8] = {0xBA, 0x5E, 0xBA, 0x11, 0xff, 0xff, 0xff, 0xff};
273  NSData* data = [NSData dataWithBytes:bytes length:8];
275  CheckEncodeDecode(value);
276 }
277 
278 TEST(FlutterStandardCodec, CanEncodeAndDecodeFloat32Array) {
279  uint8_t bytes[8] = {0xd8, 0x0f, 0x49, 0x40, 0x00, 0x00, 0x7a, 0x44};
280  NSData* data = [NSData dataWithBytes:bytes length:8];
282  CheckEncodeDecode(value);
283 }
284 
285 TEST(FlutterStandardCodec, CanEncodeAndDecodeFloat64Array) {
286  uint8_t bytes[16] = {0xBA, 0x5E, 0xBA, 0x11, 0xff, 0xff, 0xff, 0xff,
287  0xBA, 0x5E, 0xBA, 0x11, 0xff, 0xff, 0xff, 0xff};
288  NSData* data = [NSData dataWithBytes:bytes length:16];
290  CheckEncodeDecode(value);
291 }
292 
293 TEST(FlutterStandardCodec, HandlesMethodCallsWithNilArguments) {
294  FlutterStandardMethodCodec* codec = [FlutterStandardMethodCodec sharedInstance];
296  NSData* encoded = [codec encodeMethodCall:call];
297  FlutterMethodCall* decoded = [codec decodeMethodCall:encoded];
298  ASSERT_TRUE([decoded isEqual:call]);
299 }
300 
301 TEST(FlutterStandardCodec, HandlesMethodCallsWithSingleArgument) {
302  FlutterStandardMethodCodec* codec = [FlutterStandardMethodCodec sharedInstance];
304  NSData* encoded = [codec encodeMethodCall:call];
305  FlutterMethodCall* decoded = [codec decodeMethodCall:encoded];
306  ASSERT_TRUE([decoded isEqual:call]);
307 }
308 
309 TEST(FlutterStandardCodec, HandlesMethodCallsWithArgumentList) {
310  FlutterStandardMethodCodec* codec = [FlutterStandardMethodCodec sharedInstance];
311  NSArray* arguments = @[ @42, @"world" ];
313  arguments:arguments];
314  NSData* encoded = [codec encodeMethodCall:call];
315  FlutterMethodCall* decoded = [codec decodeMethodCall:encoded];
316  ASSERT_TRUE([decoded isEqual:call]);
317 }
318 
319 TEST(FlutterStandardCodec, HandlesSuccessEnvelopesWithNilResult) {
320  FlutterStandardMethodCodec* codec = [FlutterStandardMethodCodec sharedInstance];
321  NSData* encoded = [codec encodeSuccessEnvelope:nil];
322  id decoded = [codec decodeEnvelope:encoded];
323  ASSERT_TRUE(decoded == nil);
324 }
325 
326 TEST(FlutterStandardCodec, HandlesSuccessEnvelopesWithSingleResult) {
327  FlutterStandardMethodCodec* codec = [FlutterStandardMethodCodec sharedInstance];
328  NSData* encoded = [codec encodeSuccessEnvelope:@42];
329  id decoded = [codec decodeEnvelope:encoded];
330  ASSERT_TRUE([decoded isEqual:@42]);
331 }
332 
333 TEST(FlutterStandardCodec, HandlesSuccessEnvelopesWithResultMap) {
334  FlutterStandardMethodCodec* codec = [FlutterStandardMethodCodec sharedInstance];
335  NSDictionary* result = @{@"a" : @42, @42 : @"a"};
336  NSData* encoded = [codec encodeSuccessEnvelope:result];
337  id decoded = [codec decodeEnvelope:encoded];
338  ASSERT_TRUE([decoded isEqual:result]);
339 }
340 
341 TEST(FlutterStandardCodec, HandlesErrorEnvelopes) {
342  FlutterStandardMethodCodec* codec = [FlutterStandardMethodCodec sharedInstance];
343  NSDictionary* details = @{@"a" : @42, @42 : @"a"};
344  FlutterError* error = [FlutterError errorWithCode:@"errorCode"
345  message:@"something failed"
346  details:details];
347  NSData* encoded = [codec encodeErrorEnvelope:error];
348  id decoded = [codec decodeEnvelope:encoded];
349  ASSERT_TRUE([decoded isEqual:error]);
350 }
351 
352 TEST(FlutterStandardCodec, HandlesSubclasses) {
353  ExtendedReaderWriter* extendedReaderWriter = [[ExtendedReaderWriter alloc] init];
355  [FlutterStandardMessageCodec codecWithReaderWriter:extendedReaderWriter];
356  Pair* pair = [[Pair alloc] initWithLeft:@1 right:@2];
357  NSData* encoded = [codec encode:pair];
358  Pair* decoded = [codec decode:encoded];
359  ASSERT_TRUE([pair.left isEqual:decoded.left]);
360  ASSERT_TRUE([pair.right isEqual:decoded.right]);
361 }
+[FlutterStandardTypedData typedDataWithFloat64:]
instancetype typedDataWithFloat64:(NSData *data)
Definition: FlutterStandardCodec.mm:162
-[FlutterStandardWriter writeValue:]
void writeValue:(id value)
Definition: FlutterStandardCodec.mm:347
+[FlutterMethodCall methodCallWithMethodName:arguments:]
instancetype methodCallWithMethodName:arguments:(NSString *method,[arguments] id _Nullable arguments)
-[FlutterStandardReader readBytes:length:]
void readBytes:length:(void *destination,[length] NSUInteger length)
Definition: FlutterStandardCodec.mm:371
-[FlutterStandardWriter writeByte:]
void writeByte:(UInt8 value)
Definition: FlutterStandardCodec.mm:213
FlutterError
Definition: FlutterCodecs.h:246
+[FlutterStandardTypedData typedDataWithBytes:]
instancetype typedDataWithBytes:(NSData *data)
Definition: FlutterStandardCodec.mm:146
+[FlutterStandardTypedData typedDataWithInt64:]
instancetype typedDataWithInt64:(NSData *data)
Definition: FlutterStandardCodec.mm:154
Pair::right
NSObject * right
Definition: flutter_standard_codec_unittest.mm:15
FlutterStandardReader
Definition: FlutterCodecs.h:132
FlutterStandardReaderWriter
Definition: FlutterCodecs.h:172
+[FlutterStandardTypedData typedDataWithInt32:]
instancetype typedDataWithInt32:(NSData *data)
Definition: FlutterStandardCodec.mm:150
+[FlutterError errorWithCode:message:details:]
instancetype errorWithCode:message:details:(NSString *code,[message] NSString *_Nullable message,[details] id _Nullable details)
FlutterStandardMessageCodec
Definition: FlutterCodecs.h:209
FlutterMethodCall
Definition: FlutterCodecs.h:220
Pair
Definition: flutter_standard_codec_unittest.mm:13
FlutterStandardWriter
Definition: FlutterCodecs.h:92
-[FlutterMessageCodec-p encode:]
NSData *_Nullable encode:(id _Nullable message)
+[FlutterStandardTypedData typedDataWithFloat32:]
instancetype typedDataWithFloat32:(NSData *data)
Definition: FlutterStandardCodec.mm:158
FlutterCodecs.h
CheckEncodeDecode
static void CheckEncodeDecode(id value, NSData *expectedEncoding)
Definition: flutter_standard_codec_unittest.mm:92
ExtendedWriter
Definition: flutter_standard_codec_unittest.mm:33
+[FlutterStandardMessageCodec codecWithReaderWriter:]
instancetype codecWithReaderWriter:(FlutterStandardReaderWriter *readerWriter)
Definition: FlutterStandardCodec.mm:24
FlutterStandardTypedData
Definition: FlutterCodecs.h:300
-[FlutterStandardReader readValue]
nullable id readValue()
Definition: FlutterStandardCodec.mm:400
-[FlutterMessageCodec-p decode:]
id _Nullable decode:(NSData *_Nullable message)
kDATE
static const UInt8 kDATE
Definition: flutter_standard_codec_unittest.mm:30
-[FlutterStandardReader readValueOfType:]
nullable id readValueOfType:(UInt8 type)
Definition: FlutterStandardCodec.mm:429
Pair::left
NSObject * left
Definition: flutter_standard_codec_unittest.mm:14
-[ExtendedWriter writeValue:]
void writeValue:(id value)
Definition: flutter_standard_codec_unittest.mm:38
-[FlutterStandardWriter writeBytes:length:]
void writeBytes:length:(const void *bytes,[length] NSUInteger length)
Definition: FlutterStandardCodec.mm:217
FLUTTER_ASSERT_ARC
Definition: VsyncWaiterIosTest.mm:15
FlutterStandardMethodCodec
Definition: FlutterCodecs.h:469
ExtendedReaderWriter
Definition: flutter_standard_codec_unittest.mm:78
TEST
TEST(FlutterStandardCodec, CanDecodeZeroLength)
Definition: flutter_standard_codec_unittest.mm:119
ExtendedReader
Definition: flutter_standard_codec_unittest.mm:56
+[FlutterMessageCodec-p sharedInstance]
instancetype sharedInstance()
kPAIR
static const UInt8 kPAIR
Definition: flutter_standard_codec_unittest.mm:31