Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
5#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h"
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
30static const UInt8 kDATE = 128;
31static 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
92static 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
108static 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
119TEST(FlutterStandardCodec, CanDecodeZeroLength) {
121 id decoded = [codec decode:[NSData data]];
122 ASSERT_TRUE(decoded == nil);
123}
124
125TEST(FlutterStandardCodec, CanEncodeAndDecodeNil) {
126 CheckEncodeDecode(nil, nil);
127}
128
129TEST(FlutterStandardCodec, CanEncodeAndDecodeNSNull) {
130 uint8_t bytes[1] = {0x00};
131 CheckEncodeDecode([NSNull null], [NSData dataWithBytes:bytes length:1]);
132}
133
134TEST(FlutterStandardCodec, CanEncodeAndDecodeYes) {
135 uint8_t bytes[1] = {0x01};
136 CheckEncodeDecode(@YES, [NSData dataWithBytes:bytes length:1]);
137}
138
139TEST(FlutterStandardCodec, CanEncodeAndDecodeNo) {
140 uint8_t bytes[1] = {0x02};
141 CheckEncodeDecode(@NO, [NSData dataWithBytes:bytes length:1]);
142}
143
144TEST(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
150TEST(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
156TEST(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
162TEST(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
170TEST(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
176TEST(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
182TEST(FlutterStandardCodec, CanEncodeAndDecodeSInt32) {
183 uint8_t bytes[5] = {0x03, 0x78, 0x56, 0x34, 0x12};
184 CheckEncodeDecode(@(0x12345678), [NSData dataWithBytes:bytes length:5]);
185}
186
187TEST(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
192TEST(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
198TEST(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
204TEST(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
210TEST(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
215TEST(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
220TEST(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
233TEST(FlutterStandardCodec, CanEncodeAndDecodeArray) {
234 NSArray* value = @[ [NSNull null], @"hello", @3.14, @47, @{@42 : @"nested"} ];
236}
237
238TEST(FlutterStandardCodec, CanEncodeAndDecodeDictionary) {
239 NSDictionary* value =
240 @{@"a" : @3.14,
241 @"b" : @47,
242 [NSNull null] : [NSNull null],
243 @3.14 : @[ @"nested" ]};
245}
246
247TEST(FlutterStandardCodec, CanEncodeAndDecodeByteArray) {
248 uint8_t bytes[4] = {0xBA, 0x5E, 0xBA, 0x11};
249 NSData* data = [NSData dataWithBytes:bytes length:4];
252}
253
254TEST(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
264TEST(FlutterStandardCodec, CanEncodeAndDecodeInt32Array) {
265 uint8_t bytes[8] = {0xBA, 0x5E, 0xBA, 0x11, 0xff, 0xff, 0xff, 0xff};
266 NSData* data = [NSData dataWithBytes:bytes length:8];
269}
270
271TEST(FlutterStandardCodec, CanEncodeAndDecodeInt64Array) {
272 uint8_t bytes[8] = {0xBA, 0x5E, 0xBA, 0x11, 0xff, 0xff, 0xff, 0xff};
273 NSData* data = [NSData dataWithBytes:bytes length:8];
276}
277
278TEST(FlutterStandardCodec, CanEncodeAndDecodeFloat32Array) {
279 uint8_t bytes[8] = {0xd8, 0x0f, 0x49, 0x40, 0x00, 0x00, 0x7a, 0x44};
280 NSData* data = [NSData dataWithBytes:bytes length:8];
283}
284
285TEST(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];
291}
292
293TEST(FlutterStandardCodec, HandlesMethodCallsWithNilArguments) {
296 NSData* encoded = [codec encodeMethodCall:call];
297 FlutterMethodCall* decoded = [codec decodeMethodCall:encoded];
298 ASSERT_TRUE([decoded isEqual:call]);
299}
300
301TEST(FlutterStandardCodec, HandlesMethodCallsWithSingleArgument) {
304 NSData* encoded = [codec encodeMethodCall:call];
305 FlutterMethodCall* decoded = [codec decodeMethodCall:encoded];
306 ASSERT_TRUE([decoded isEqual:call]);
307}
308
309TEST(FlutterStandardCodec, HandlesMethodCallsWithArgumentList) {
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
319TEST(FlutterStandardCodec, HandlesSuccessEnvelopesWithNilResult) {
321 NSData* encoded = [codec encodeSuccessEnvelope:nil];
322 id decoded = [codec decodeEnvelope:encoded];
323 ASSERT_TRUE(decoded == nil);
324}
325
326TEST(FlutterStandardCodec, HandlesSuccessEnvelopesWithSingleResult) {
328 NSData* encoded = [codec encodeSuccessEnvelope:@42];
329 id decoded = [codec decodeEnvelope:encoded];
330 ASSERT_TRUE([decoded isEqual:@42]);
331}
332
333TEST(FlutterStandardCodec, HandlesSuccessEnvelopesWithResultMap) {
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
341TEST(FlutterStandardCodec, HandlesErrorEnvelopes) {
343 NSDictionary* details = @{@"a" : @42, @42 : @"a"};
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
352TEST(FlutterStandardCodec, HandlesSubclasses) {
353 ExtendedReaderWriter* extendedReaderWriter = [[ExtendedReaderWriter alloc] init];
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}
#define TEST(S, s, D, expected)
const uint8_t uint32_t uint32_t GError ** error
uint8_t value
GAsyncResult * result
uint32_t * target
static const UInt8 kDATE
static const UInt8 kPAIR
static void CheckEncodeDecode(id value, NSData *expectedEncoding)
instancetype errorWithCode:message:details:(NSString *code,[message] NSString *_Nullable message,[details] id _Nullable details)
instancetype methodCallWithMethodName:arguments:(NSString *method,[arguments] id _Nullable arguments)
instancetype codecWithReaderWriter:(FlutterStandardReaderWriter *readerWriter)
NSData * encodeMethodCall:(FlutterMethodCall *call)
NSData * encodeErrorEnvelope:(FlutterError *error)
FlutterMethodCall * decodeMethodCall:(NSData *message)
NSData * encodeSuccessEnvelope:(id result)
nullable id readValueOfType:(UInt8 type)
void readBytes:length:(void *destination,[length] NSUInteger length)
instancetype typedDataWithInt32:(NSData *data)
instancetype typedDataWithBytes:(NSData *data)
instancetype typedDataWithInt64:(NSData *data)
instancetype typedDataWithFloat64:(NSData *data)
instancetype typedDataWithFloat32:(NSData *data)
void writeBytes:length:(const void *bytes,[length] NSUInteger length)
size_t length
const uintptr_t id