]> git.saurik.com Git - apple/security.git/blobdiff - MultiDeviceSimulator/MultiDeviceSimulatorTests/MultiDeviceNetworking.m
Security-58286.200.222.tar.gz
[apple/security.git] / MultiDeviceSimulator / MultiDeviceSimulatorTests / MultiDeviceNetworking.m
diff --git a/MultiDeviceSimulator/MultiDeviceSimulatorTests/MultiDeviceNetworking.m b/MultiDeviceSimulator/MultiDeviceSimulatorTests/MultiDeviceNetworking.m
new file mode 100644 (file)
index 0000000..d660f6a
--- /dev/null
@@ -0,0 +1,295 @@
+//
+//  MultiDeviceNetworking.m
+//  Security
+//
+
+#import "MultiDeviceNetworking.h"
+#import "MultiDeviceNetworkingProtocol.h"
+
+@interface MDNCounters ()
+@property (assign) unsigned long kvsSyncAndWait;
+@property (assign) unsigned long kvsFlush;
+@property (assign) unsigned long kvsSend;
+@property (assign) unsigned long kvsRecv;
+@property (assign) unsigned long kvsRecvAll;
+@property (strong) NSMutableDictionary<NSString *,NSNumber *> *kvsKeys;
+
+- (void)addCountToKey:(NSString *)key;
+@end
+
+
+@interface MDNConnection : NSObject <MultiDeviceNetworkingProtocol>
+@property (weak) MultiDeviceNetworking *network;
+@property NSXPCConnection *inConnection;
+@property NSXPCConnection *outConnection;
+@property MDNCounters *counters;
+@end
+
+@interface MultiDeviceNetworking () <NSXPCListenerDelegate>
+@property NSXPCListener *networkListener;
+@property NSMutableDictionary *kvs;
+@property NSMutableArray<MDNConnection *> *connections;
+@property dispatch_queue_t serialQueue;
+@property NSMutableDictionary<NSString *, XCTestExpectation *> *expectations;
+@end
+
+@implementation MDNCounters
+
+- (instancetype)init {
+    if ((self = [super init]) == NULL) {
+        return nil;
+    }
+    self.kvsKeys = [NSMutableDictionary dictionary];
+    return self;
+}
+
+- (NSDictionary *)summary{
+    NSDictionary *kvsKeys = @{};
+    @synchronized(self.kvsKeys) {
+        kvsKeys = [self.kvsKeys copy];
+    }
+    return @{
+             @"kvsSyncAndWait" : @(self.kvsSyncAndWait),
+             @"kvsFlush" : @(self.kvsFlush),
+             @"kvsSend" : @(self.kvsSend),
+             @"kvsRecv" : @(self.kvsRecv),
+             @"kvsRecvAll" : @(self.kvsRecvAll),
+             @"kvsKeys" : kvsKeys,
+             };
+}
+- (NSString *)description
+{
+    return [NSString stringWithFormat:@"<MDNCounters: %@>", [self summary]];
+}
+- (void)addCountToKey:(NSString *)key
+{
+    @synchronized(self.kvsKeys) {
+        NSNumber *number = self.kvsKeys[key];
+        self.kvsKeys[key] = @([number longValue] + 1);
+    }
+}
+
+@end
+
+@implementation MultiDeviceNetworking
+
+- (instancetype)init
+{
+    self = [super init];
+    if (self) {
+        self.networkListener = [NSXPCListener anonymousListener];
+        self.networkListener.delegate = self;
+        [self.networkListener resume];
+        self.kvs = [[NSMutableDictionary alloc] init];
+        self.connections = [NSMutableArray array];
+        self.serialQueue = dispatch_queue_create("MultiDeviceNetworking.flushQueue", NULL);
+        self.expectations = [NSMutableDictionary dictionary];
+    }
+    return self;
+}
+
+- (NSXPCListenerEndpoint *)endpoint
+{
+    return [self.networkListener endpoint];
+}
+
+- (void)dumpKVSState
+{
+    @synchronized(self.kvs) {
+        puts("KVS STATE");
+        [self.kvs enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull __unused stop) {
+            puts([[NSString stringWithFormat:@"%@ - %@", key, obj] UTF8String]);
+        }];
+    }
+}
+
+- (void)dumpCounters
+{
+    @synchronized(self.connections) {
+        puts("Network counters:");
+        for (MDNConnection *conn in self.connections) {
+            puts([[NSString stringWithFormat:@"%@", conn.counters] UTF8String]);
+        }
+    }
+}
+
+
+- (void)disconnectAll
+{
+    @synchronized(self.connections) {
+        for (MDNConnection *conn in self.connections) {
+            [conn.inConnection invalidate];
+            [conn.outConnection invalidate];
+        }
+        self.connections = [NSMutableArray array];
+    }
+}
+
+- (void)setTestExpectation:(XCTestExpectation *)expectation forKey:(NSString *)key
+{
+    self.expectations[key] = expectation;
+}
+
+- (void)clearTestExpectations
+{
+    self.expectations = [NSMutableDictionary dictionary];
+}
+
+- (void)fulfill:(NSString *)key
+{
+    [self.expectations[key] fulfill];
+}
+
+
+
+//MARK: - setup listener
+
+- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection
+{
+    newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MultiDeviceNetworkingProtocol)];
+
+    MDNConnection *conn = [[MDNConnection alloc] init];
+    conn.network = self;
+    conn.inConnection = newConnection;
+    newConnection.exportedObject = conn;
+
+    [self.connections addObject:conn];
+    [newConnection resume];
+
+    return YES;
+}
+
+@end
+
+
+//MARK: - KVS fun
+
+@implementation MDNConnection
+
+- (instancetype)init
+{
+    if ((self = [super init]) == nil)
+        return nil;
+    _counters = [[MDNCounters alloc] init];
+    return self;
+}
+
+- (void)MDNRegisterCallback:(NSXPCListenerEndpoint *)callback complete:(MDNComplete)complete
+{
+    self.outConnection = [[NSXPCConnection alloc] initWithListenerEndpoint:callback];
+    self.outConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MultiDeviceNetworkingCallbackProtocol)];
+
+    __typeof(self) weakSelf = self;
+    self.outConnection.invalidationHandler = ^{
+        __typeof(self) strongSelf = weakSelf;
+        strongSelf.outConnection = nil;
+    };
+
+
+    [self.outConnection resume];
+    complete(NULL, NULL);
+}
+
+- (void)MDNCloudPut:(NSDictionary *)values complete:(MDNComplete)complete {
+    MultiDeviceNetworking *network = self.network;
+    @synchronized(network.kvs) {
+        [network.kvs setValuesForKeysWithDictionary:values];
+    }
+    /* interact with test expections so that tests can check that something happned in KVS */
+    [network fulfill:@"Network"];
+    for (NSString *key in values.allKeys) {
+        NSString *dataSummary = @"";
+        id value = values[key];
+        if ([value isKindOfClass:[NSString class]]) {
+            dataSummary = [NSString stringWithFormat:@" = string[%ld]", [(NSString *)value length]];
+        } else if ([value isKindOfClass:[NSData class]]) {
+            NSUInteger length = [(NSData *)value length];
+            NSData *subdata = [(NSData *)value subdataWithRange:NSMakeRange(0, MIN(length, 4))];
+            dataSummary = [NSString stringWithFormat:@" = data[%lu][%@]", (unsigned long)length, subdata];
+        } else {
+            dataSummary = [NSString stringWithFormat:@" = other(%@)", [value description]];
+        }
+        NSLog(@"KVS key update: %@%@", key, dataSummary);
+        [network fulfill:key];
+        [self.counters addCountToKey:key];
+    }
+
+
+    self.counters.kvsSend++;
+    for (MDNConnection *conn in network.connections) {
+        if (conn == self || conn.outConnection == NULL) {
+             continue;
+        }
+        conn.counters.kvsRecv++;
+        [[conn.outConnection remoteObjectProxy] MDNCItemsChanged:values complete:^(NSDictionary *returnedValues, NSError *error) {
+            ;
+        }];
+    }
+    complete(@{}, NULL);
+}
+
+- (void)MDNCloudsynchronizeAndWait:(NSDictionary *)values complete:(MDNComplete)complete {
+    MultiDeviceNetworking *network = self.network;
+    NSDictionary *kvsCopy = NULL;
+    @synchronized(network.kvs) {
+        kvsCopy = [network.kvs copy];
+    }
+    self.counters.kvsSyncAndWait++;
+    [[self.outConnection synchronousRemoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
+        NSLog(@"foo: %@", error);
+        //abort();
+    }] MDNCItemsChanged:kvsCopy complete:^(NSDictionary *returnedValues, NSError *error) {
+    }];
+    dispatch_async(network.serialQueue, ^{
+        complete(@{}, NULL);
+    });
+}
+
+- (void)MDNCloudGet:(NSArray *)keys complete:(MDNComplete)complete{
+    MultiDeviceNetworking *network = self.network;
+    NSLog(@"asking for: %@", keys);
+    self.counters.kvsRecv++;
+    NSMutableDictionary *reply = [NSMutableDictionary dictionary];
+    @synchronized(network.kvs) {
+        for (id key in keys) {
+            reply[key] = network.kvs[key];
+        }
+    }
+    complete(reply, NULL);
+}
+
+- (void)MDNCloudGetAll:(MDNComplete)complete
+{
+    MultiDeviceNetworking *network = self.network;
+    NSDictionary *kvsCopy = NULL;
+    self.counters.kvsRecvAll++;
+    @synchronized(network.kvs) {
+        kvsCopy = [network.kvs copy];
+    }
+    complete(kvsCopy, NULL);
+}
+
+- (void)MDNCloudRemoveKeys:(NSArray<NSString *> *)keys complete:(MDNComplete)complete
+{
+    MultiDeviceNetworking *network = self.network;
+    @synchronized(network.kvs) {
+        if (keys) {
+            for (NSString *key in keys) {
+                network.kvs[key] = NULL;
+            }
+        } else {
+            network.kvs = [NSMutableDictionary dictionary];
+        }
+    }
+    complete(NULL, NULL);
+}
+
+- (void)MDNCloudFlush:(MDNComplete)complete
+{
+    self.counters.kvsFlush++;
+    dispatch_async(self.network.serialQueue, ^{
+        complete(@{}, NULL);
+    });
+}
+
+@end