blob: 5afd35f17bec4b644881170bcd1c6a5f0bf5ad94 [file] [log] [blame] [edit]
//
// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "Service/Tests/FunctionalTests/EDOServiceUIBaseTest.h"
#import "Service/Sources/EDOClientService.h"
#import "Service/Sources/EDOHostService.h"
#import "Service/Sources/NSObject+EDOValueObject.h"
#import "Service/Tests/TestsBundle/EDOTestDummy.h"
// Block tests to assure the block can be invoked remotely from another process.
@interface EDOUIBlockTest : EDOServiceUIBaseTest
@property(nonatomic) int numRemoteInvokes;
@property(nonatomic) EDOHostService *service;
@end
@implementation EDOUIBlockTest
- (void)setUp {
[super setUp];
[self launchApplicationWithPort:EDOTEST_APP_SERVICE_PORT initValue:10];
self.service = [EDOHostService serviceWithPort:2234
rootObject:self
queue:dispatch_get_main_queue()];
}
- (void)tearDown {
[self.service invalidate];
[super tearDown];
}
// Test that the simple block can be invoked remotely from another process.
- (void)testSimpleBlock {
EDOTestDummy *remoteDummy = [EDOClientService rootObjectWithPort:EDOTEST_APP_SERVICE_PORT];
__block BOOL blockAssignment = NO;
[remoteDummy voidWithBlock:^{
blockAssignment = YES;
}];
XCTAssertTrue(blockAssignment);
}
// Test that the simple block can be invoked remotely from another process.
- (void)testSimpleBlockWithReturn {
EDOTestDummy *remoteDummy = [EDOClientService rootObjectWithPort:EDOTEST_APP_SERVICE_PORT];
XCTAssertEqual([remoteDummy returnWithBlockDouble:^double {
return 100.0;
}],
100.0);
}
// Test that the different types of block can be invoked locally and remotely.
- (void)testInvokeDifferentTypesOfBlock {
EDOTestDummy *remoteDummy = [EDOClientService rootObjectWithPort:EDOTEST_APP_SERVICE_PORT];
self.numRemoteInvokes = 0;
// Assign a block that will outlive the method scope and can be safely invoked.
[self assignStackBlockToDummy:remoteDummy];
// This invocation happens remotely.
XCTAssertNoThrow([remoteDummy invokeBlock]);
// This invocation happens locally as a property.
XCTAssertNoThrow(remoteDummy.block());
// This invocation happens locally as a return.
XCTAssertNoThrow([remoteDummy returnBlock]());
// Assign a block that stays inside the same method scope.
XCTAssertNoThrow([remoteDummy voidWithBlockAssigned:^{
++self.numRemoteInvokes;
}]);
// Invoke the remote block remotely.
XCTAssertNoThrow([remoteDummy invokeBlock]);
XCTAssertEqual(self.numRemoteInvokes, 3);
}
// Test that the block can have structs as returns.
- (void)testBlockWithStructReturn {
EDOTestDummy *remoteDummy = [EDOClientService rootObjectWithPort:EDOTEST_APP_SERVICE_PORT];
EDOTestDummyStruct dummyStruct = [remoteDummy returnStructWithBlockStret:^EDOTestDummyStruct {
return (EDOTestDummyStruct){.value = 100, .a = 30.0, .x = 50, .z = 200};
}];
XCTAssertEqual(dummyStruct.value, 100);
XCTAssertEqual(dummyStruct.a, 30);
XCTAssertEqual(dummyStruct.x, 50);
XCTAssertEqual(dummyStruct.z, 200);
}
// Test that the block can have structs as returns.
- (void)testBlockWithStructArguments {
EDOTestDummy *remoteDummy = [EDOClientService rootObjectWithPort:EDOTEST_APP_SERVICE_PORT];
EDOTestDummy *dummyReturn =
[remoteDummy returnWithInt:5
dummyStruct:(EDOTestDummyStruct){.value = 150, .a = 30.0, .x = 50, .z = 200}
object:nil
blockComplex:^EDOTestDummy *(EDOTestDummyStruct dummy, int i, id object,
EDOTestDummy *test) {
XCTAssertEqual(dummy.a, 30);
XCTAssertEqual(dummy.x, 50);
XCTAssertEqual(dummy.z, 200);
XCTAssertEqual(i, 5);
XCTAssertEqual(dummy.value, 150);
// Random calculation inside the block to be validated outside.
test.value = i + dummy.value + 4;
return test;
}];
XCTAssertEqual(dummyReturn.value, 159);
}
// Test that the blocks that are bounced back and forth between processes should stay the same.
- (void)testBlockIsEqual {
EDOTestDummy *remoteDummy = [EDOClientService rootObjectWithPort:EDOTEST_APP_SERVICE_PORT];
void (^localBlock)(void) = ^{
// This block captures self to ensure that the block is moved to the heap when copied.
[self class];
};
void (^returnedBlock)(void) = [remoteDummy returnWithBlockObject:^id(EDOTestDummy *_) {
return localBlock;
}];
// Make sure those are the actual same block objects that can be invoked.
XCTAssertEqual(localBlock, returnedBlock);
XCTAssertEqual([remoteDummy returnBlock], [remoteDummy returnBlock]);
XCTAssertNoThrow(returnedBlock());
XCTAssertNoThrow([remoteDummy returnBlock]());
}
/**
* Test that makes sure local block is resolved to its address, when it is decoded from the service
* which is different from the service it is encoded.
*/
- (void)testBlockResolveToLocalAddress {
EDOTestDummy *remoteDummy = [EDOClientService rootObjectWithPort:EDOTEST_APP_SERVICE_PORT];
dispatch_queue_t backgroundQueue =
dispatch_queue_create("com.google.edo.testbackground", DISPATCH_QUEUE_SERIAL);
EDOHostService *backgroundService = [EDOHostService serviceWithPort:2235
rootObject:self
queue:backgroundQueue];
void (^localBlock)(void) = ^{
// This block captures self to ensure that the block is moved to the heap when copied.
[self class];
};
// Sending block to remote process through background eDO host.
dispatch_sync(backgroundQueue, ^{
remoteDummy.block = localBlock;
});
// Resolve the block from main thread eDO host.
id returnedBlock = remoteDummy.block;
XCTAssertEqual((id)localBlock, returnedBlock);
[backgroundService invalidate];
}
// Test that the block can also have out parameters and passByValue.
- (void)testBlockByValueAndOutArgument {
EDOTestDummy *remoteDummy = [EDOClientService rootObjectWithPort:EDOTEST_APP_SERVICE_PORT];
NSArray *arrayReturn = [remoteDummy returnWithBlockObject:^id(EDOTestDummy *dummy) {
// Use NSClassFromString to fetch EDOObject to make it private here.
XCTAssertEqual([dummy class], NSClassFromString(@"EDOObject"));
XCTAssertEqual(dummy.value, 10);
dummy.value += 10;
return [@[ @(dummy.value), @20 ] passByValue];
}];
// The returned array from the block, if not passByValue, should be resolved to the local array
// here.
XCTAssertEqual([arrayReturn class], NSClassFromString(@"EDOObject"));
XCTAssertEqualObjects(arrayReturn[0], @20);
XCTAssertEqualObjects(arrayReturn[1], @20);
EDOTestDummy *outDummy = [remoteDummy returnWithBlockOutObject:^(EDOTestDummy **dummy) {
*dummy = [EDO_REMOTE_CLASS(EDOTestDummy, EDOTEST_APP_SERVICE_PORT) classMethodWithNumber:@10];
}];
XCTAssertEqual(outDummy.value, 10);
}
- (void)assignStackBlockToDummy:(EDOTestDummy *)dummy {
void (^block)(void) = ^{
++self.numRemoteInvokes;
};
// Make sure the block will still live outside this function's scope.
dummy.block = block;
}
@end