Flutter iOS Embedder
FlutterChannelsTest.m
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 #import <OCMock/OCMock.h>
8 #import <XCTest/XCTest.h>
9 
11 
12 @protocol FlutterTaskQueue <NSObject>
13 @end
14 
16 @property(nonatomic, copy) NSString* channel;
17 @property(nonatomic, strong) NSData* message;
18 @property(nonatomic, strong) NSMutableDictionary<NSString*, FlutterBinaryMessageHandler>* handlers;
19 @end
20 @implementation MockBinaryMessenger
21 - (instancetype)init {
22  self = [super init];
23  if (self) {
24  _handlers = [[NSMutableDictionary<NSString*, FlutterBinaryMessageHandler> alloc] init];
25  }
26  return self;
27 }
28 
29 - (void)sendOnChannel:(NSString*)channel message:(NSData* _Nullable)message {
30  [self sendOnChannel:channel message:message binaryReply:nil];
31 }
32 
33 - (void)sendOnChannel:(NSString*)channel
34  message:(NSData* _Nullable)message
35  binaryReply:(FlutterBinaryReply _Nullable)callback {
36  self.channel = channel;
37  self.message = message;
38 }
39 
40 - (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(NSString*)channel
41  binaryMessageHandler:
42  (FlutterBinaryMessageHandler _Nullable)handler {
43  [self.handlers setObject:handler forKey:channel];
44  return 0;
45 }
46 
47 - (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection {
48 }
49 
50 @end
51 
52 @interface FlutterChannelsTest : XCTestCase
53 @end
54 
55 @implementation FlutterChannelsTest
56 
57 - (void)testMethodInvoke {
58  NSString* channelName = @"foo";
59  id binaryMessenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
60  id codec = OCMProtocolMock(@protocol(FlutterMethodCodec));
61  FlutterMethodChannel* channel = [[FlutterMethodChannel alloc] initWithName:channelName
62  binaryMessenger:binaryMessenger
63  codec:codec];
64  XCTAssertNotNil(channel);
65  NSData* encodedMethodCall = [@"hey" dataUsingEncoding:NSUTF8StringEncoding];
66  OCMStub([codec encodeMethodCall:[OCMArg any]]).andReturn(encodedMethodCall);
67  [channel invokeMethod:@"foo" arguments:@[ @(1) ]];
68  OCMVerify([binaryMessenger sendOnChannel:channelName message:encodedMethodCall]);
69 }
70 
71 - (void)testMethodInvokeWithReply {
72  NSString* channelName = @"foo";
73  id binaryMessenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
74  id codec = OCMProtocolMock(@protocol(FlutterMethodCodec));
75  FlutterMethodChannel* channel = [[FlutterMethodChannel alloc] initWithName:channelName
76  binaryMessenger:binaryMessenger
77  codec:codec];
78  XCTAssertNotNil(channel);
79  NSData* encodedMethodCall = [@"hey" dataUsingEncoding:NSUTF8StringEncoding];
80  OCMStub([codec encodeMethodCall:[OCMArg any]]).andReturn(encodedMethodCall);
81  XCTestExpectation* didCallReply = [self expectationWithDescription:@"didCallReply"];
82  OCMExpect([binaryMessenger sendOnChannel:channelName
83  message:encodedMethodCall
84  binaryReply:[OCMArg checkWithBlock:^BOOL(id obj) {
85  FlutterBinaryReply reply = obj;
86  reply(nil);
87  return YES;
88  }]]);
89  [channel invokeMethod:@"foo"
90  arguments:@[ @1 ]
91  result:^(id _Nullable result) {
92  [didCallReply fulfill];
93  XCTAssertEqual(FlutterMethodNotImplemented, result);
94  }];
95  OCMVerifyAll(binaryMessenger);
96  [self waitForExpectationsWithTimeout:1.0 handler:nil];
97 }
98 
99 - (void)testMethodMessageHandler {
100  NSString* channelName = @"foo";
101  id binaryMessenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
102  id codec = OCMProtocolMock(@protocol(FlutterMethodCodec));
103  FlutterMethodChannel* channel = [[FlutterMethodChannel alloc] initWithName:channelName
104  binaryMessenger:binaryMessenger
105  codec:codec];
106  XCTAssertNotNil(channel);
107 
108  NSData* encodedMethodCall = [@"hey" dataUsingEncoding:NSUTF8StringEncoding];
109  OCMStub([codec encodeMethodCall:[OCMArg any]]).andReturn(encodedMethodCall);
110  FlutterMethodCallHandler handler =
111  ^(FlutterMethodCall* _Nonnull call, FlutterResult _Nonnull result) {
112  };
113  [channel setMethodCallHandler:handler];
114  OCMVerify([binaryMessenger setMessageHandlerOnChannel:channelName
115  binaryMessageHandler:[OCMArg isNotNil]]);
116 }
117 
118 - (void)testCallMethodHandler {
119  NSString* channelName = @"foo";
120  MockBinaryMessenger* binaryMessenger = [[MockBinaryMessenger alloc] init];
121  id codec = OCMProtocolMock(@protocol(FlutterMethodCodec));
122  FlutterMethodChannel* channel = [[FlutterMethodChannel alloc] initWithName:channelName
123  binaryMessenger:binaryMessenger
124  codec:codec];
125  XCTAssertNotNil(channel);
126 
127  NSData* encodedMethodCall = [@"encoded" dataUsingEncoding:NSUTF8StringEncoding];
128  NSData* replyData = [@"reply" dataUsingEncoding:NSUTF8StringEncoding];
129  NSData* replyEnvelopeData = [@"reply-envelope" dataUsingEncoding:NSUTF8StringEncoding];
130  FlutterMethodCall* methodCall = [[FlutterMethodCall alloc] init];
131  OCMStub([codec decodeMethodCall:encodedMethodCall]).andReturn(methodCall);
132  OCMStub([codec encodeSuccessEnvelope:replyData]).andReturn(replyEnvelopeData);
133  XCTestExpectation* didCallHandler = [self expectationWithDescription:@"didCallHandler"];
134  XCTestExpectation* didCallReply = [self expectationWithDescription:@"didCallReply"];
135  FlutterMethodCallHandler handler =
136  ^(FlutterMethodCall* _Nonnull call, FlutterResult _Nonnull result) {
137  XCTAssertEqual(methodCall, call);
138  [didCallHandler fulfill];
139  result(replyData);
140  };
141  [channel setMethodCallHandler:handler];
142  binaryMessenger.handlers[channelName](encodedMethodCall, ^(NSData* reply) {
143  [didCallReply fulfill];
144  XCTAssertEqual(replyEnvelopeData, reply);
145  });
146  [self waitForExpectationsWithTimeout:1.0 handler:nil];
147 }
148 
149 - (void)testResize {
150  NSString* channelName = @"flutter/test";
151  id binaryMessenger = OCMStrictProtocolMock(@protocol(FlutterBinaryMessenger));
152  id codec = OCMProtocolMock(@protocol(FlutterMethodCodec));
154  [[FlutterBasicMessageChannel alloc] initWithName:channelName
155  binaryMessenger:binaryMessenger
156  codec:codec];
157  XCTAssertNotNil(channel);
158 
159  // The expected content was created from the following Dart code:
160  // MethodCall call = MethodCall('resize', ['flutter/test',3]);
161  // StandardMethodCodec().encodeMethodCall(call).buffer.asUint8List();
162  const unsigned char bytes[] = {7, 6, 114, 101, 115, 105, 122, 101, 12, 2,
163  7, 12, 102, 108, 117, 116, 116, 101, 114, 47,
164  116, 101, 115, 116, 3, 3, 0, 0, 0};
165  NSData* expectedMessage = [NSData dataWithBytes:bytes length:sizeof(bytes)];
166 
167  OCMExpect([binaryMessenger sendOnChannel:@"dev.flutter/channel-buffers" message:expectedMessage]);
168  [channel resizeChannelBuffer:3];
169  OCMVerifyAll(binaryMessenger);
170  [binaryMessenger stopMocking];
171 }
172 
173 - (bool)testSetWarnsOnOverflow {
174  NSString* channelName = @"flutter/test";
175  id binaryMessenger = OCMStrictProtocolMock(@protocol(FlutterBinaryMessenger));
176  id codec = OCMProtocolMock(@protocol(FlutterMethodCodec));
178  [[FlutterBasicMessageChannel alloc] initWithName:channelName
179  binaryMessenger:binaryMessenger
180  codec:codec];
181  XCTAssertNotNil(channel);
182 
183  // The expected content was created from the following Dart code:
184  // MethodCall call = MethodCall('overflow',['flutter/test', true]);
185  // StandardMethodCodec().encodeMethodCall(call).buffer.asUint8List();
186  const unsigned char bytes[] = {7, 8, 111, 118, 101, 114, 102, 108, 111, 119, 12, 2, 7, 12,
187  102, 108, 117, 116, 116, 101, 114, 47, 116, 101, 115, 116, 1};
188  NSData* expectedMessage = [NSData dataWithBytes:bytes length:sizeof(bytes)];
189 
190  OCMExpect([binaryMessenger sendOnChannel:@"dev.flutter/channel-buffers" message:expectedMessage]);
191  [channel setWarnsOnOverflow:NO];
192  OCMVerifyAll(binaryMessenger);
193  [binaryMessenger stopMocking];
194 }
195 
196 - (void)testBasicMessageChannelCleanup {
197  NSString* channelName = @"foo";
198  FlutterBinaryMessengerConnection connection = 123;
199  id binaryMessenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
200  id codec = OCMProtocolMock(@protocol(FlutterMethodCodec));
202  [[FlutterBasicMessageChannel alloc] initWithName:channelName
203  binaryMessenger:binaryMessenger
204  codec:codec];
205  FlutterMessageHandler handler = ^(id _Nullable message, FlutterReply callback) {
206  };
207  OCMStub([binaryMessenger setMessageHandlerOnChannel:channelName
208  binaryMessageHandler:[OCMArg any]])
209  .andReturn(connection);
210  [channel setMessageHandler:handler];
211  OCMVerify([binaryMessenger setMessageHandlerOnChannel:channelName
212  binaryMessageHandler:[OCMArg isNotNil]]);
213  [channel setMessageHandler:nil];
214  OCMVerify([binaryMessenger cleanUpConnection:connection]);
215 }
216 
217 - (void)testMethodChannelCleanup {
218  NSString* channelName = @"foo";
219  FlutterBinaryMessengerConnection connection = 123;
220  id binaryMessenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
221  id codec = OCMProtocolMock(@protocol(FlutterMethodCodec));
222  FlutterMethodChannel* channel = [[FlutterMethodChannel alloc] initWithName:channelName
223  binaryMessenger:binaryMessenger
224  codec:codec];
225  XCTAssertNotNil(channel);
226 
227  OCMStub([binaryMessenger setMessageHandlerOnChannel:channelName
228  binaryMessageHandler:[OCMArg any]])
229  .andReturn(connection);
230 
231  FlutterMethodCallHandler handler =
232  ^(FlutterMethodCall* _Nonnull call, FlutterResult _Nonnull result) {
233  };
234  [channel setMethodCallHandler:handler];
235  OCMVerify([binaryMessenger setMessageHandlerOnChannel:channelName
236  binaryMessageHandler:[OCMArg isNotNil]]);
237  [channel setMethodCallHandler:nil];
238  OCMVerify([binaryMessenger cleanUpConnection:connection]);
239 }
240 
241 - (void)testBasicMessageChannelTaskQueue {
242  NSString* channelName = @"foo";
243  FlutterBinaryMessengerConnection connection = 123;
244  id binaryMessenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
245  id codec = OCMProtocolMock(@protocol(FlutterMethodCodec));
246  id taskQueue = OCMProtocolMock(@protocol(FlutterTaskQueue));
248  [[FlutterBasicMessageChannel alloc] initWithName:channelName
249  binaryMessenger:binaryMessenger
250  codec:codec
251  taskQueue:taskQueue];
252  FlutterMessageHandler handler = ^(id _Nullable message, FlutterReply callback) {
253  };
254  OCMStub([binaryMessenger setMessageHandlerOnChannel:channelName
255  binaryMessageHandler:[OCMArg any]
256  taskQueue:taskQueue])
257  .andReturn(connection);
258  [channel setMessageHandler:handler];
259  OCMVerify([binaryMessenger setMessageHandlerOnChannel:channelName
260  binaryMessageHandler:[OCMArg isNotNil]
261  taskQueue:taskQueue]);
262  [channel setMessageHandler:nil];
263  OCMVerify([binaryMessenger cleanUpConnection:connection]);
264 }
265 
266 - (void)testBasicMessageChannelInvokeHandlerAfterChannelReleased {
267  NSString* channelName = @"foo";
268  __block NSString* handlerMessage;
269  __block FlutterBinaryMessageHandler messageHandler;
270  @autoreleasepool {
271  FlutterBinaryMessengerConnection connection = 123;
272  id binaryMessenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
273  id codec = OCMProtocolMock(@protocol(FlutterMessageCodec));
274  id taskQueue = OCMProtocolMock(@protocol(FlutterTaskQueue));
276  [[FlutterBasicMessageChannel alloc] initWithName:channelName
277  binaryMessenger:binaryMessenger
278  codec:codec
279  taskQueue:taskQueue];
280 
281  FlutterMessageHandler handler = ^(id _Nullable message, FlutterReply callback) {
282  handlerMessage = message;
283  };
284  OCMStub([binaryMessenger
285  setMessageHandlerOnChannel:channelName
286  binaryMessageHandler:[OCMArg checkWithBlock:^BOOL(
287  FlutterBinaryMessageHandler handler) {
288  messageHandler = handler;
289  return YES;
290  }]
291  taskQueue:taskQueue])
292  .andReturn(connection);
293  OCMStub([codec decode:[OCMArg any]]).andReturn(@"decoded message");
294  [channel setMessageHandler:handler];
295  }
296  // Channel is released, messageHandler should still retain the codec. The codec
297  // internally makes a `decode` call and updates the `handlerMessage` to "decoded message".
298  messageHandler([NSData data], ^(NSData* data){
299  });
300  XCTAssertEqualObjects(handlerMessage, @"decoded message");
301 }
302 
303 - (void)testMethodChannelInvokeHandlerAfterChannelReleased {
304  NSString* channelName = @"foo";
305  FlutterBinaryMessengerConnection connection = 123;
306  __block FlutterMethodCall* decodedMethodCall;
307  __block FlutterBinaryMessageHandler messageHandler;
308  @autoreleasepool {
309  id binaryMessenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
310  id codec = OCMProtocolMock(@protocol(FlutterMethodCodec));
311  id taskQueue = OCMProtocolMock(@protocol(FlutterTaskQueue));
312  FlutterMethodChannel* channel = [[FlutterMethodChannel alloc] initWithName:channelName
313  binaryMessenger:binaryMessenger
314  codec:codec
315  taskQueue:taskQueue];
316  FlutterMethodCallHandler handler = ^(FlutterMethodCall* call, FlutterResult result) {
317  decodedMethodCall = call;
318  };
319  OCMStub([binaryMessenger
320  setMessageHandlerOnChannel:channelName
321  binaryMessageHandler:[OCMArg checkWithBlock:^BOOL(
322  FlutterBinaryMessageHandler handler) {
323  messageHandler = handler;
324  return YES;
325  }]
326  taskQueue:taskQueue])
327  .andReturn(connection);
328  OCMStub([codec decodeMethodCall:[OCMArg any]])
329  .andReturn([FlutterMethodCall methodCallWithMethodName:@"decoded method call"
330  arguments:nil]);
331  [channel setMethodCallHandler:handler];
332  }
333  messageHandler([NSData data], ^(NSData* data){
334  });
335  XCTAssertEqualObjects(decodedMethodCall.method, @"decoded method call");
336 }
337 
338 - (void)testMethodChannelTaskQueue {
339  NSString* channelName = @"foo";
340  FlutterBinaryMessengerConnection connection = 123;
341  id binaryMessenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
342  id codec = OCMProtocolMock(@protocol(FlutterMethodCodec));
343  id taskQueue = OCMProtocolMock(@protocol(FlutterTaskQueue));
344  FlutterMethodChannel* channel = [[FlutterMethodChannel alloc] initWithName:channelName
345  binaryMessenger:binaryMessenger
346  codec:codec
347  taskQueue:taskQueue];
348  XCTAssertNotNil(channel);
349  FlutterMethodCallHandler handler = ^(FlutterMethodCall* call, FlutterResult result) {
350  };
351  OCMStub([binaryMessenger setMessageHandlerOnChannel:channelName
352  binaryMessageHandler:[OCMArg any]
353  taskQueue:taskQueue])
354  .andReturn(connection);
355  [channel setMethodCallHandler:handler];
356  OCMVerify([binaryMessenger setMessageHandlerOnChannel:channelName
357  binaryMessageHandler:[OCMArg isNotNil]
358  taskQueue:taskQueue]);
359  [channel setMethodCallHandler:nil];
360  OCMVerify([binaryMessenger cleanUpConnection:connection]);
361 }
362 
363 - (void)testEventChannelTaskQueue {
364  NSString* channelName = @"foo";
365  FlutterBinaryMessengerConnection connection = 123;
366  id binaryMessenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
367  id codec = OCMProtocolMock(@protocol(FlutterMethodCodec));
368  id taskQueue = OCMProtocolMock(@protocol(FlutterTaskQueue));
369  id handler = OCMProtocolMock(@protocol(FlutterStreamHandler));
370  FlutterEventChannel* channel = [[FlutterEventChannel alloc] initWithName:channelName
371  binaryMessenger:binaryMessenger
372  codec:codec
373  taskQueue:taskQueue];
374  XCTAssertNotNil(channel);
375  OCMStub([binaryMessenger setMessageHandlerOnChannel:channelName
376  binaryMessageHandler:[OCMArg any]
377  taskQueue:taskQueue])
378  .andReturn(connection);
379  [channel setStreamHandler:handler];
380  OCMVerify([binaryMessenger setMessageHandlerOnChannel:channelName
381  binaryMessageHandler:[OCMArg isNotNil]
382  taskQueue:taskQueue]);
383  [channel setStreamHandler:nil];
384  OCMVerify([binaryMessenger cleanUpConnection:connection]);
385 }
386 
387 @end
FLUTTER_ASSERT_ARC
#define FLUTTER_ASSERT_ARC
Definition: FlutterMacros.h:44
FlutterBasicMessageChannel
Definition: FlutterChannels.h:39
FlutterMethodChannel
Definition: FlutterChannels.h:222
-[FlutterMethodChannel invokeMethod:arguments:result:]
void invokeMethod:arguments:result:(NSString *method,[arguments] id _Nullable arguments,[result] FlutterResult _Nullable callback)
FlutterChannels.h
FlutterMethodCallHandler
void(^ FlutterMethodCallHandler)(FlutterMethodCall *call, FlutterResult result)
Definition: FlutterChannels.h:208
FlutterEventChannel
Definition: FlutterChannels.h:402
-[FlutterBasicMessageChannel resizeChannelBuffer:]
void resizeChannelBuffer:(NSInteger newSize)
Definition: FlutterChannels.mm:152
FlutterBinaryMessageHandler
void(^ FlutterBinaryMessageHandler)(NSData *_Nullable message, FlutterBinaryReply reply)
Definition: FlutterBinaryMessenger.h:30
-[FlutterMethodChannel invokeMethod:arguments:]
void invokeMethod:arguments:(NSString *method,[arguments] id _Nullable arguments)
-[FlutterMethodChannel setMethodCallHandler:]
void setMethodCallHandler:(FlutterMethodCallHandler _Nullable handler)
MockBinaryMessenger::handlers
NSMutableDictionary< NSString *, FlutterBinaryMessageHandler > * handlers
Definition: FlutterChannelsTest.m:18
MockBinaryMessenger::channel
NSString * channel
Definition: FlutterChannelsTest.m:16
FlutterMethodCall
Definition: FlutterCodecs.h:220
FlutterChannelsTest
Definition: FlutterChannelsTest.m:52
FlutterTaskQueue-p
Definition: platform_message_handler_ios.h:16
FlutterResult
void(^ FlutterResult)(id _Nullable result)
Definition: FlutterChannels.h:196
-[FlutterEventChannel setStreamHandler:]
void setStreamHandler:(NSObject< FlutterStreamHandler > *_Nullable handler)
MockBinaryMessenger
Definition: FlutterChannelsTest.m:15
FlutterReply
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterReply)(id _Nullable reply)
-[FlutterBasicMessageChannel setWarnsOnOverflow:]
void setWarnsOnOverflow:(BOOL warns)
Definition: FlutterChannels.mm:162
FlutterMessageCodec-p
Definition: FlutterCodecs.h:18
FlutterStreamHandler-p
Definition: FlutterChannels.h:358
-[FlutterBasicMessageChannel setMessageHandler:]
void setMessageHandler:(FlutterMessageHandler _Nullable handler)
MockBinaryMessenger::message
NSData * message
Definition: FlutterChannelsTest.m:17
FlutterMessageHandler
void(^ FlutterMessageHandler)(id _Nullable message, FlutterReply callback)
Definition: FlutterChannels.h:32
FlutterBinaryMessenger-p
Definition: FlutterBinaryMessenger.h:48
FlutterBinaryMessengerConnection
int64_t FlutterBinaryMessengerConnection
Definition: FlutterBinaryMessenger.h:32
FlutterBinaryReply
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterBinaryReply)(NSData *_Nullable reply)