Explorar o código

Merge pull request #362 from facebook/nlutsenko.autobahn

Improve Xcode-based Autobahn testing.
Nikita Lutsenko %!s(int64=9) %!d(string=hai) anos
pai
achega
6f5e1d3175

+ 2 - 0
.gitignore

@@ -22,3 +22,5 @@ profile
 *.moved-aside
 DerivedData
 extern/
+
+*.pyc

+ 0 - 333
SRWebSocketTests/SRTAutobahnTests.m

@@ -1,333 +0,0 @@
-//
-// Copyright 2012 Square Inc.
-// Portions Copyright (c) 2016-present, Facebook, Inc.
-// 
-// All rights reserved.
-// 
-// This source code is licensed under the BSD-style license found in the
-// LICENSE file in the root directory of this source tree. An additional grant 
-// of patent rights can be found in the PATENTS file in the same directory.
-//
-
-#import <XCTest/XCTest.h>
-#import <XCTest/XCTestRun.h>
-#import <SocketRocket/SRWebSocket.h>
-#import "SRTWebSocketOperation.h"
-#import "SenTestCase+SRTAdditions.h"
-
-#define SRLogDebug(format, ...) 
-//#define SRLogDebug(format, ...) NSLog(format, __VA_ARGS__)
-
-@interface SRTAutobahnTests : XCTestCase
-@end
-
-@interface TestOperation : SRTWebSocketOperation <SRWebSocketDelegate>
-- (instancetype)initWithBaseURL:(NSURL *)url testNumber:(NSInteger)testNumber agent:(NSString *)agent;
-@end
-
-
-@interface CaseGetterOperation : SRTWebSocketOperation <SRWebSocketDelegate>
-- (instancetype)initWithBaseURL:(NSURL *)url;
-
-@property (nonatomic, readonly) NSInteger caseCount;
-
-@end
-
-@interface NSInvocation (SRTBlockInvocation)
-
-+ (NSInvocation *)invocationWithBlock:(dispatch_block_t)block;
-
-@end
-
-@interface SRTBlockInvoker
-
-- (instancetype)initWithBlock:(dispatch_block_t)block;
-
-- (void)invoke;
-
-@end
-
-@interface UpdateOperation : SRTWebSocketOperation <SRWebSocketDelegate>
-
-- (instancetype)initWithBaseURL:(NSURL *)url agent:(NSString *)agent;
-
-@end
-
-@interface TestInfoOperation : SRTWebSocketOperation <SRWebSocketDelegate>
-
-@property (nonatomic) NSDictionary *info;
-
-- (instancetype)initWithBaseURL:(NSURL *)url caseNumber:(NSInteger)caseNumber;
-
-@end
-
-@interface TestResultsOperation : SRTWebSocketOperation <SRWebSocketDelegate>
-
-@property (nonatomic) NSDictionary *info;
-
-- (instancetype)initWithBaseURL:(NSURL *)url caseNumber:(NSInteger)caseNumber agent:(NSString *)agent;
-
-@end
-
-@implementation SRTAutobahnTests {
-    SRWebSocket *_curWebSocket; 
-    NSInteger _testCount;
-    NSInteger _curTest;
-    NSMutableArray *_sockets;
-    NSString *_testURLString;
-    NSURL *_prefixURL;
-    NSString *_agent;
-    NSString *_description;
-}
-
-- (instancetype)initWithInvocation:(NSInvocation *)anInvocation description:(NSString *)description;
-{
-    self = [self initWithInvocation:anInvocation];
-    if (self) {
-        _description = description;
-    }
-    return self;
-}
-
-- (instancetype)initWithInvocation:(NSInvocation *)anInvocation;
-{
-    self = [super initWithInvocation:anInvocation];
-    if (self) {
-        _testURLString = [[NSProcessInfo processInfo].environment objectForKey:@"SR_TEST_URL"];
-        _prefixURL = [NSURL URLWithString:_testURLString];
-        _agent = [NSBundle bundleForClass:[self class]].bundleIdentifier;
-    }
-    return self;
-}
-
-- (NSUInteger)testCaseCount;
-{
-    if (self.invocation) {
-        return [super testCaseCount];
-    }
-    
-    CaseGetterOperation *caseGetter = [[CaseGetterOperation alloc] initWithBaseURL:_prefixURL];
-    
-    [caseGetter start];
-    
-    [self runCurrentRunLoopUntilTestPasses:^BOOL{
-        return caseGetter.isFinished;
-    } timeout:20.0];
-    
-    XCTAssertNil(caseGetter.error, @"CaseGetter should have successfully returned the number of testCases. Instead got error %@", caseGetter.error);
-    
-    NSInteger caseCount = caseGetter.caseCount;
-    
-    return caseCount;
-}
-
-- (BOOL)isEmpty;
-{
-    return NO;
-}
-
-- (void)performTest:(XCTestCaseRun *) aRun
-{
-    if (self.invocation) {
-        [super performTest:aRun];
-        return;
-    }
-    [aRun start];
-    for (NSUInteger i = 1; i <= aRun.test.testCaseCount; i++) {
-        SEL sel = @selector(performTestWithNumber:);
-        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:sel]];
-        
-        invocation.selector = sel;
-        invocation.target = self;
-        
-        [invocation setArgument:&i atIndex:2];
-        
-        NSString *description = [self caseDescriptionForCaseNumber:i];
-
-        XCTestCase *testCase = [[[self class] alloc] initWithInvocation:invocation description:description];
-        
-        XCTestCaseRun *run = [[XCTestCaseRun alloc] initWithTest:testCase];
-        
-        [testCase performTest:run];
-    }
-    [aRun stop];
-    
-    [self updateReports];
-}
-
-- (NSInteger)testNum;
-{
-    NSInteger i;
-    [self.invocation getArgument:&i atIndex:2];
-    return i;
-}
-
-- (NSString *)caseDescriptionForCaseNumber:(NSInteger)caseNumber;
-{
-    TestInfoOperation *testInfoOperation = [[TestInfoOperation alloc] initWithBaseURL:_prefixURL caseNumber:caseNumber];
-    
-    [testInfoOperation start];
-    
-    [self runCurrentRunLoopUntilTestPasses:^BOOL{
-        return testInfoOperation.isFinished;
-    } timeout:60 * 60];
-    
-    XCTAssertNil(testInfoOperation.error, @"Updating the report should not have errored");
-    
-    return [NSString stringWithFormat:@"%@ - %@", [testInfoOperation.info objectForKey:@"id"], [testInfoOperation.info objectForKey:@"description"]];
-}
-                                            
-- (NSString *)description;
-{
-    if (_description) {
-        return _description;
-    } else {
-        return @"Autobahn Test Harness";
-    }
-}
-
-+ (id) defaultTestSuite
-{
-    return [[[self class] alloc] init];
-}
-
-- (void)performTestWithNumber:(NSInteger)testNumber;
-{
-    NSOperationQueue *testQueue = [[NSOperationQueue alloc] init];
-    
-    testQueue.maxConcurrentOperationCount = 1;
-    
-    TestOperation *testOp = [[TestOperation alloc] initWithBaseURL:_prefixURL testNumber:testNumber agent:_agent];
-    [testQueue addOperation:testOp];
-    
-    TestResultsOperation *resultOp = [[TestResultsOperation alloc] initWithBaseURL:_prefixURL caseNumber:testNumber agent:_agent];
-    [resultOp addDependency:testOp];
-    [testQueue addOperation:resultOp];
-    
-    testQueue.suspended = NO;
-
-    [self runCurrentRunLoopUntilTestPasses:^BOOL{
-        return resultOp.isFinished;
-    } timeout:60 * 60];
-    
-    XCTAssertTrue(!testOp.error, @"Test operation should not have failed");
-    XCTAssertEqualObjects(@"OK", [resultOp.info objectForKey:@"behavior"], @"Test behavior should be OK");
-}
-
-- (void)updateReports;
-{
-    UpdateOperation *updateReportOperation = [[UpdateOperation alloc] initWithBaseURL:_prefixURL agent:_agent];
-    
-    [updateReportOperation start];
-    
-    [self runCurrentRunLoopUntilTestPasses:^BOOL{
-        return updateReportOperation.isFinished;
-    } timeout:60 * 60];
-    
-    XCTAssertNil(updateReportOperation.error, @"Updating the report should not have errored");
-}
-
-@end
-
-@implementation TestOperation {
-    NSInteger _testNumber;
-}
-
-- (instancetype)initWithBaseURL:(NSURL *)url testNumber:(NSInteger)testNumber agent:(NSString *)agent;
-{   
-    
-    NSString *path = [[url URLByAppendingPathComponent:@"runCase"] absoluteString];
-    path = [path stringByAppendingFormat:@"?case=%@&agent=%@", @(testNumber), agent];
-    
-    self = [super initWithURL:[NSURL URLWithString:path]];
-    if (self) {
-        _testNumber = testNumber;
-    }
-    return self;
-}
-
-- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message;
-{
-    [webSocket send:message];
-}
-
-@end
-
-
-@implementation CaseGetterOperation
-
-@synthesize caseCount = _caseCount;
-
-- (instancetype)initWithBaseURL:(NSURL *)url;
-{
-    self = [super initWithURL:[url URLByAppendingPathComponent:@"getCaseCount"]];
-    if (self) {
-    }
-    return self;
-}
-
-- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message;
-{
-    _caseCount = [message integerValue];
-}
-
-@end
-
-
-@implementation UpdateOperation
-
-- (instancetype)initWithBaseURL:(NSURL *)url agent:(NSString *)agent;
-{
-    NSString *path = [[url URLByAppendingPathComponent:@"updateReports"] absoluteString];
-    path = [path stringByAppendingFormat:@"?agent=%@", agent];
-    
-    return [super initWithURL:[NSURL URLWithString:path]];
-}
-
-- (void)start;
-{
-    [super start];
-    NSLog(@"Updating Reports!");
-}
-
-@end
-
-
-@implementation TestInfoOperation
-
-@synthesize info = _info;
-
-- (instancetype)initWithBaseURL:(NSURL *)url caseNumber:(NSInteger)caseNumber;
-{
-    NSString *path = [[url URLByAppendingPathComponent:@"getCaseInfo"] absoluteString];
-    path = [path stringByAppendingFormat:@"?case=%@", @(caseNumber)];
-    
-    return [super initWithURL:[NSURL URLWithString:path]];
-}
-
-- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(NSString *)message;
-{
-    self.info = [NSJSONSerialization JSONObjectWithData:[message dataUsingEncoding:NSUTF8StringEncoding] options:0 error:NULL];
-}
-
-@end
-
-
-@implementation TestResultsOperation
-
-@synthesize info = _info;
-
-- (instancetype)initWithBaseURL:(NSURL *)url caseNumber:(NSInteger)caseNumber agent:(NSString *)agent;
-{
-    NSString *path = [[url URLByAppendingPathComponent:@"getCaseStatus"] absoluteString];
-    path = [path stringByAppendingFormat:@"?case=%@&agent=%@", @(caseNumber), agent];
-    
-    return [super initWithURL:[NSURL URLWithString:path]];
-}
-
-- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(NSString *)message;
-{
-    self.info = [NSJSONSerialization JSONObjectWithData:[message dataUsingEncoding:NSUTF8StringEncoding] options:0 error:NULL];
-}
-
-@end

+ 0 - 26
SRWebSocketTests/SRTWebSocketOperation.h

@@ -1,26 +0,0 @@
-//
-//  SRTWebSocketOperation.h
-//  SocketRocket
-//
-//  Created by Mike Lewis on 1/28/12.
-//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
-//
-
-#import <Foundation/Foundation.h>
-
-#import <SocketRocket/SRWebSocket.h>
-
-@interface SRTWebSocketOperation : NSOperation <SRWebSocketDelegate>
-
-- (instancetype)initWithURL:(NSURL *)URL;
-
-@property (nonatomic) BOOL isFinished;
-@property (nonatomic) BOOL isExecuting;
-
-@property (nonatomic, readonly, retain) NSError *error;
-
-// We override these methods.  Please call super
-- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;
-- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;
-
-@end

+ 0 - 2
SRWebSocketTests/en.lproj/InfoPlist.strings

@@ -1,2 +0,0 @@
-/* Localized versions of Info.plist keys */
-

+ 0 - 8
SRWebSocketTests/foo.mm

@@ -1,8 +0,0 @@
-//
-//  foo.m
-//  SocketRocket
-//
-//  Created by Mike Lewis on 10/31/11.
-//  Copyright (c) 2011 __MyCompanyName__. All rights reserved.
-//
-

+ 58 - 48
SocketRocket.xcodeproj/project.pbxproj

@@ -18,6 +18,10 @@
 		3345DC881C52ACD70083CCB8 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208301450F597009315AF /* Foundation.framework */; };
 		3345DC8A1C52ACD70083CCB8 /* SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = F6A12CCF145119B700C1D980 /* SRWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		555E0EB41C51E57A00E6BB92 /* SocketRocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 555E0EB11C51E56D00E6BB92 /* SocketRocket.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		8105E4801CDD67B400AA12DB /* SRTAutobahnTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8105E47A1CDD679A00AA12DB /* SRTAutobahnTests.m */; };
+		8105E4821CDD67BD00AA12DB /* SRTWebSocketOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 8105E4771CDD679A00AA12DB /* SRTWebSocketOperation.m */; };
+		8105E4841CDD67CE00AA12DB /* XCTestCase+SRTAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 8105E47E1CDD679A00AA12DB /* XCTestCase+SRTAdditions.m */; };
+		8105E4AE1CDD6E6200AA12DB /* SRAutobahnOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 8105E4AD1CDD6E6200AA12DB /* SRAutobahnOperation.m */; };
 		811934BC1CDAF725003AB243 /* SocketRocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 555E0EB11C51E56D00E6BB92 /* SocketRocket.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		811934BE1CDAF725003AB243 /* SocketRocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 555E0EB11C51E56D00E6BB92 /* SocketRocket.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		811934C01CDAF726003AB243 /* SocketRocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 555E0EB11C51E56D00E6BB92 /* SocketRocket.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -56,7 +60,6 @@
 		F6016C8814620EC70037BB3D /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD3145122FC00C1D980 /* Security.framework */; };
 		F6016C8914620ECC0037BB3D /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD3145122FC00C1D980 /* Security.framework */; };
 		F6016C8A1462143C0037BB3D /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD51451231B00C1D980 /* CFNetwork.framework */; };
-		F60CC2A114D4EA0500A005E4 /* SRTWebSocketOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F60CC2A014D4EA0500A005E4 /* SRTWebSocketOperation.m */; };
 		F61A0DC81625F44D00365EBD /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F61A0DC71625F44D00365EBD /* Default-568h@2x.png */; };
 		F62417E614D52F3C003CE997 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F62417E514D52F3C003CE997 /* UIKit.framework */; };
 		F62417E714D52F3C003CE997 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208301450F597009315AF /* Foundation.framework */; };
@@ -81,10 +84,7 @@
 		F6AE451D145906A70022AF3C /* libSocketRocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B2082D1450F597009315AF /* libSocketRocket.a */; };
 		F6AE4520145906B20022AF3C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208301450F597009315AF /* Foundation.framework */; };
 		F6AE45241459071C0022AF3C /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD51451231B00C1D980 /* CFNetwork.framework */; };
-		F6AE4528145907D30022AF3C /* SenTestCase+SRTAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = F6AE4527145907D30022AF3C /* SenTestCase+SRTAdditions.m */; };
 		F6BDA806145900D200FE3253 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208301450F597009315AF /* Foundation.framework */; };
-		F6BDA80C145900D200FE3253 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F6BDA80A145900D200FE3253 /* InfoPlist.strings */; };
-		F6BDA8161459016900FE3253 /* SRTAutobahnTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F6BDA8151459016900FE3253 /* SRTAutobahnTests.m */; };
 		F6C41C98145F7C6100641356 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F6C41C95145F7C4700641356 /* libicucore.dylib */; };
 /* End PBXBuildFile section */
 
@@ -102,6 +102,14 @@
 		2D4227621BB4358C000C1A6C /* SocketRocket.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SocketRocket.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		3345DC901C52ACD70083CCB8 /* SocketRocket.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SocketRocket.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		555E0EB11C51E56D00E6BB92 /* SocketRocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SocketRocket.h; sourceTree = "<group>"; };
+		8105E4761CDD679A00AA12DB /* SRTWebSocketOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SRTWebSocketOperation.h; sourceTree = "<group>"; };
+		8105E4771CDD679A00AA12DB /* SRTWebSocketOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SRTWebSocketOperation.m; sourceTree = "<group>"; };
+		8105E4791CDD679A00AA12DB /* SRWebSocketTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SRWebSocketTests-Info.plist"; sourceTree = "<group>"; };
+		8105E47A1CDD679A00AA12DB /* SRTAutobahnTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SRTAutobahnTests.m; sourceTree = "<group>"; };
+		8105E47D1CDD679A00AA12DB /* XCTestCase+SRTAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XCTestCase+SRTAdditions.h"; sourceTree = "<group>"; };
+		8105E47E1CDD679A00AA12DB /* XCTestCase+SRTAdditions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "XCTestCase+SRTAdditions.m"; sourceTree = "<group>"; };
+		8105E4AC1CDD6E6200AA12DB /* SRAutobahnOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRAutobahnOperation.h; sourceTree = "<group>"; };
+		8105E4AD1CDD6E6200AA12DB /* SRAutobahnOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRAutobahnOperation.m; sourceTree = "<group>"; };
 		811934B11CDAF711003AB243 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		81B31C0F1CDC404100D86D43 /* SRIOConsumer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRIOConsumer.h; sourceTree = "<group>"; };
 		81B31C101CDC404100D86D43 /* SRIOConsumer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRIOConsumer.m; sourceTree = "<group>"; };
@@ -111,8 +119,6 @@
 		81B31C2C1CDC406B00D86D43 /* SRHash.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRHash.m; sourceTree = "<group>"; };
 		81B31C5D1CDC444900D86D43 /* SRRunLoopThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRRunLoopThread.h; sourceTree = "<group>"; };
 		81B31C5E1CDC444900D86D43 /* SRRunLoopThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRRunLoopThread.m; sourceTree = "<group>"; };
-		F60CC29F14D4EA0500A005E4 /* SRTWebSocketOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRTWebSocketOperation.h; sourceTree = "<group>"; };
-		F60CC2A014D4EA0500A005E4 /* SRTWebSocketOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRTWebSocketOperation.m; sourceTree = "<group>"; };
 		F61A0DC71625F44D00365EBD /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "en.lproj/Default-568h@2x.png"; sourceTree = "<group>"; };
 		F62417E314D52F3C003CE997 /* TestChat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestChat.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		F62417E514D52F3C003CE997 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
@@ -138,14 +144,9 @@
 		F6A12CD0145119B700C1D980 /* SRWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRWebSocket.m; sourceTree = "<group>"; };
 		F6A12CD3145122FC00C1D980 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
 		F6A12CD51451231B00C1D980 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
-		F6AE4526145907D30022AF3C /* SenTestCase+SRTAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SenTestCase+SRTAdditions.h"; sourceTree = "<group>"; };
-		F6AE4527145907D30022AF3C /* SenTestCase+SRTAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "SenTestCase+SRTAdditions.m"; sourceTree = "<group>"; };
 		F6B2082D1450F597009315AF /* libSocketRocket.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libSocketRocket.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		F6B208301450F597009315AF /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
 		F6BDA802145900D200FE3253 /* SRWebSocketTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SRWebSocketTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
-		F6BDA809145900D200FE3253 /* SRWebSocketTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SRWebSocketTests-Info.plist"; sourceTree = "<group>"; };
-		F6BDA80B145900D200FE3253 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		F6BDA8151459016900FE3253 /* SRTAutobahnTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRTAutobahnTests.m; sourceTree = "<group>"; };
 		F6C41C95145F7C4700641356 /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = usr/lib/libicucore.dylib; sourceTree = SDKROOT; };
 /* End PBXFileReference section */
 
@@ -219,6 +220,45 @@
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
+		8105E4741CDD679A00AA12DB /* Tests */ = {
+			isa = PBXGroup;
+			children = (
+				8105E47A1CDD679A00AA12DB /* SRTAutobahnTests.m */,
+				8105E4751CDD679A00AA12DB /* Operations */,
+				8105E47C1CDD679A00AA12DB /* Utilities */,
+				8105E4781CDD679A00AA12DB /* Resources */,
+			);
+			path = Tests;
+			sourceTree = "<group>";
+		};
+		8105E4751CDD679A00AA12DB /* Operations */ = {
+			isa = PBXGroup;
+			children = (
+				8105E4761CDD679A00AA12DB /* SRTWebSocketOperation.h */,
+				8105E4771CDD679A00AA12DB /* SRTWebSocketOperation.m */,
+				8105E4AC1CDD6E6200AA12DB /* SRAutobahnOperation.h */,
+				8105E4AD1CDD6E6200AA12DB /* SRAutobahnOperation.m */,
+			);
+			path = Operations;
+			sourceTree = "<group>";
+		};
+		8105E4781CDD679A00AA12DB /* Resources */ = {
+			isa = PBXGroup;
+			children = (
+				8105E4791CDD679A00AA12DB /* SRWebSocketTests-Info.plist */,
+			);
+			path = Resources;
+			sourceTree = "<group>";
+		};
+		8105E47C1CDD679A00AA12DB /* Utilities */ = {
+			isa = PBXGroup;
+			children = (
+				8105E47D1CDD679A00AA12DB /* XCTestCase+SRTAdditions.h */,
+				8105E47E1CDD679A00AA12DB /* XCTestCase+SRTAdditions.m */,
+			);
+			path = Utilities;
+			sourceTree = "<group>";
+		};
 		811934B01CDAF711003AB243 /* Resources */ = {
 			isa = PBXGroup;
 			children = (
@@ -317,7 +357,7 @@
 			isa = PBXGroup;
 			children = (
 				F6B208321450F597009315AF /* SocketRocket */,
-				F6BDA807145900D200FE3253 /* SRWebSocketTests */,
+				8105E4741CDD679A00AA12DB /* Tests */,
 				F62417EA14D52F3C003CE997 /* TestChat */,
 				F6B2082F1450F597009315AF /* Frameworks */,
 				F6B2082E1450F597009315AF /* Products */,
@@ -366,28 +406,6 @@
 			path = SocketRocket;
 			sourceTree = "<group>";
 		};
-		F6BDA807145900D200FE3253 /* SRWebSocketTests */ = {
-			isa = PBXGroup;
-			children = (
-				F6BDA808145900D200FE3253 /* Supporting Files */,
-				F6BDA8151459016900FE3253 /* SRTAutobahnTests.m */,
-				F6AE4526145907D30022AF3C /* SenTestCase+SRTAdditions.h */,
-				F6AE4527145907D30022AF3C /* SenTestCase+SRTAdditions.m */,
-				F60CC29F14D4EA0500A005E4 /* SRTWebSocketOperation.h */,
-				F60CC2A014D4EA0500A005E4 /* SRTWebSocketOperation.m */,
-			);
-			path = SRWebSocketTests;
-			sourceTree = "<group>";
-		};
-		F6BDA808145900D200FE3253 /* Supporting Files */ = {
-			isa = PBXGroup;
-			children = (
-				F6BDA809145900D200FE3253 /* SRWebSocketTests-Info.plist */,
-				F6BDA80A145900D200FE3253 /* InfoPlist.strings */,
-			);
-			name = "Supporting Files";
-			sourceTree = "<group>";
-		};
 /* End PBXGroup section */
 
 /* Begin PBXHeadersBuildPhase section */
@@ -619,7 +637,6 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				F6BDA80C145900D200FE3253 /* InfoPlist.strings in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -689,9 +706,10 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				F6BDA8161459016900FE3253 /* SRTAutobahnTests.m in Sources */,
-				F6AE4528145907D30022AF3C /* SenTestCase+SRTAdditions.m in Sources */,
-				F60CC2A114D4EA0500A005E4 /* SRTWebSocketOperation.m in Sources */,
+				8105E4AE1CDD6E6200AA12DB /* SRAutobahnOperation.m in Sources */,
+				8105E4841CDD67CE00AA12DB /* XCTestCase+SRTAdditions.m in Sources */,
+				8105E4801CDD67B400AA12DB /* SRTAutobahnTests.m in Sources */,
+				8105E4821CDD67BD00AA12DB /* SRTWebSocketOperation.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -722,14 +740,6 @@
 			name = MainStoryboard.storyboard;
 			sourceTree = "<group>";
 		};
-		F6BDA80A145900D200FE3253 /* InfoPlist.strings */ = {
-			isa = PBXVariantGroup;
-			children = (
-				F6BDA80B145900D200FE3253 /* en */,
-			);
-			name = InfoPlist.strings;
-			sourceTree = "<group>";
-		};
 /* End PBXVariantGroup section */
 
 /* Begin XCBuildConfiguration section */
@@ -1055,7 +1065,7 @@
 		F6BDA811145900D200FE3253 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				INFOPLIST_FILE = "SRWebSocketTests/SRWebSocketTests-Info.plist";
+				INFOPLIST_FILE = "$(SRCROOT)/Tests/Resources/SRWebSocketTests-Info.plist";
 				IPHONEOS_DEPLOYMENT_TARGET = 6.0;
 				OTHER_LDFLAGS = (
 					"-all_load",
@@ -1071,7 +1081,7 @@
 		F6BDA812145900D200FE3253 /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				INFOPLIST_FILE = "SRWebSocketTests/SRWebSocketTests-Info.plist";
+				INFOPLIST_FILE = "$(SRCROOT)/Tests/Resources/SRWebSocketTests-Info.plist";
 				IPHONEOS_DEPLOYMENT_TARGET = 6.0;
 				OTHER_LDFLAGS = (
 					"-all_load",

+ 1 - 1
SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocket.xcscheme

@@ -65,7 +65,7 @@
             ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
             <ActionContent
                title = "Run Script"
-               scriptText = "PIDFILE=$TMPDIR/srtharness.pid&#10;if [ -r $PIDFILE ]; then&#10;    EXISTING_PID=`cat $PIDFILE`&#10;    echo &quot;Killing SRTextharneess PID:&quot; $EXISTING_PID&#10;    kill $EXISTING_PID&#10;    rm $PIDFILE&#10;fi&#10;&#10;&#10;open $PROJECT_DIR/reports/clients/index.html"
+               scriptText = "PIDFILE=$TMPDIR/srtharness.pid&#10;if [ -r $PIDFILE ]; then&#10;    EXISTING_PID=`cat $PIDFILE`&#10;    echo &quot;Killing SRTestharness PID:&quot; $EXISTING_PID&#10;    kill $EXISTING_PID&#10;    rm $PIDFILE&#10;fi&#10;&#10;&#10;open $PROJECT_DIR/pages/results/index.html&#10;"
                shellToInvoke = "/bin/bash">
                <EnvironmentBuildable>
                   <BuildableReference

+ 39 - 0
Tests/Operations/SRAutobahnOperation.h

@@ -0,0 +1,39 @@
+//
+// Copyright (c) 2016-present, Facebook, Inc.
+// All rights reserved.
+//
+// This source code is licensed under the BSD-style license found in the
+// LICENSE file in the root directory of this source tree. An additional grant
+// of patent rights can be found in the PATENTS file in the same directory.
+//
+
+#import "SRTWebSocketOperation.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef void(^SRAutobahnSocketMessageHandler)(SRWebSocket *socket, id _Nullable message);
+
+@interface SRAutobahnOperation : SRTWebSocketOperation
+
+- (instancetype)initWithServerURL:(NSURL *)url
+                  testCommandPath:(NSString *)path
+                       caseNumber:(nullable NSNumber *)caseNumber
+                            agent:(nullable NSString *)agent
+                   messageHandler:(SRAutobahnSocketMessageHandler)messageHandler;
+
+@end
+
+extern SRAutobahnOperation *SRAutobahnTestOperation(NSURL *serverURL, NSInteger caseNumber, NSString *agent);
+
+typedef void(^SRAutobahnTestResultHandler)(NSDictionary *_Nullable result);
+extern SRAutobahnOperation *SRAutobahnTestResultOperation(NSURL *serverURL, NSInteger caseNumber, NSString *agent, SRAutobahnTestResultHandler resultHandler);
+
+typedef void(^SRAutobahnTestCaseInfoHandler)(NSDictionary *_Nullable caseInfo);
+extern SRAutobahnOperation *SRAutobahnTestCaseInfoOperation(NSURL *serverURL, NSInteger caseNumber, SRAutobahnTestCaseInfoHandler handler);
+
+typedef void(^SRAutobahnTestCaseCountHandler)(NSInteger caseCount);
+extern SRAutobahnOperation *SRAutobahnTestCaseCountOperation(NSURL *serverURL, NSString *agent, SRAutobahnTestCaseCountHandler handler);
+
+extern SRAutobahnOperation *SRAutobahnTestUpdateReportsOperation(NSURL *serverURL, NSString *agent);
+
+NS_ASSUME_NONNULL_END

+ 114 - 0
Tests/Operations/SRAutobahnOperation.m

@@ -0,0 +1,114 @@
+//
+// Copyright (c) 2016-present, Facebook, Inc.
+// All rights reserved.
+//
+// This source code is licensed under the BSD-style license found in the
+// LICENSE file in the root directory of this source tree. An additional grant
+// of patent rights can be found in the PATENTS file in the same directory.
+//
+
+#import "SRAutobahnOperation.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface SRAutobahnOperation ()
+
+@property (nonatomic, copy, readonly) SRAutobahnSocketMessageHandler messageHandler;
+
+@end
+
+@implementation SRAutobahnOperation
+
+- (instancetype)initWithServerURL:(NSURL *)url
+                  testCommandPath:(NSString *)path
+                       caseNumber:(nullable NSNumber *)caseNumber
+                            agent:(nullable NSString *)agent
+                   messageHandler:(SRAutobahnSocketMessageHandler)messageHandler
+{
+    NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
+    components.path = (components.path ? [components.path stringByAppendingPathComponent:path] : path);
+
+    NSMutableArray<NSURLQueryItem *> *queryItems = [NSMutableArray arrayWithCapacity:2];
+    if (caseNumber) {
+        [queryItems addObject:[NSURLQueryItem queryItemWithName:@"case" value:caseNumber.stringValue]];
+    }
+    if (agent) {
+        [queryItems addObject:[NSURLQueryItem queryItemWithName:@"agent" value:agent]];
+    }
+    components.queryItems = queryItems;
+    self = [self initWithURL:components.URL];
+    if (!self) return self;
+
+    _messageHandler = [messageHandler copy];
+
+    return self;
+}
+
+- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message
+{
+    self.messageHandler(webSocket, message);
+}
+
+@end
+
+SRAutobahnOperation *SRAutobahnTestOperation(NSURL *serverURL, NSInteger caseNumber, NSString *agent)
+{
+    return [[SRAutobahnOperation alloc] initWithServerURL:serverURL
+                                          testCommandPath:@"/runCase"
+                                               caseNumber:@(caseNumber)
+                                                    agent:agent
+                                           messageHandler:^(SRWebSocket * _Nonnull socket, id  _Nullable message) {
+                                               [socket send:message];
+                                           }];
+}
+
+extern SRAutobahnOperation *SRAutobahnTestResultOperation(NSURL *serverURL, NSInteger caseNumber, NSString *agent, SRAutobahnTestResultHandler resultHandler)
+{
+    return [[SRAutobahnOperation alloc] initWithServerURL:serverURL
+                                          testCommandPath:@"/getCaseStatus"
+                                               caseNumber:@(caseNumber)
+                                                    agent:agent
+                                           messageHandler:^(SRWebSocket * _Nonnull socket, id  _Nullable message) {
+                                               NSData *messageData = [message dataUsingEncoding:NSUTF8StringEncoding];
+                                               NSDictionary *result = [NSJSONSerialization JSONObjectWithData:messageData options:0 error:NULL];
+                                               resultHandler(result);
+                                           }];
+}
+
+extern SRAutobahnOperation *SRAutobahnTestCaseInfoOperation(NSURL *serverURL, NSInteger caseNumber, SRAutobahnTestCaseInfoHandler handler)
+{
+    return [[SRAutobahnOperation alloc] initWithServerURL:serverURL
+                                          testCommandPath:@"/getCaseInfo"
+                                               caseNumber:@(caseNumber)
+                                                    agent:nil
+                                           messageHandler:^(SRWebSocket * _Nonnull socket, id  _Nullable message) {
+                                               NSData *messageData = [message dataUsingEncoding:NSUTF8StringEncoding];
+                                               NSDictionary *result = [NSJSONSerialization JSONObjectWithData:messageData options:0 error:NULL];
+                                               handler(result);
+                                           }];
+}
+
+extern SRAutobahnOperation *SRAutobahnTestCaseCountOperation(NSURL *serverURL, NSString *agent, SRAutobahnTestCaseCountHandler handler)
+{
+    return [[SRAutobahnOperation alloc] initWithServerURL:serverURL
+                                          testCommandPath:@"/getCaseCount"
+                                               caseNumber:nil
+                                                    agent:agent
+                                           messageHandler:^(SRWebSocket * _Nonnull socket, id  _Nullable message) {
+                                               NSInteger count = [message integerValue];
+                                               handler(count);
+                                           }];
+}
+
+extern SRAutobahnOperation *SRAutobahnTestUpdateReportsOperation(NSURL *serverURL, NSString *agent)
+{
+    return [[SRAutobahnOperation alloc] initWithServerURL:serverURL
+                                          testCommandPath:@"/updateReports"
+                                               caseNumber:nil
+                                                    agent:agent
+                                           messageHandler:^(SRWebSocket * _Nonnull socket, id  _Nullable message) {
+                                               // Nothing to do
+                                           }];
+}
+
+NS_ASSUME_NONNULL_END

+ 27 - 0
Tests/Operations/SRTWebSocketOperation.h

@@ -0,0 +1,27 @@
+//
+// Copyright 2012 Square Inc.
+// Portions Copyright (c) 2016-present, Facebook, Inc.
+//
+// All rights reserved.
+//
+// This source code is licensed under the BSD-style license found in the
+// LICENSE file in the root directory of this source tree. An additional grant
+// of patent rights can be found in the PATENTS file in the same directory.
+//
+
+#import <SocketRocket/SRWebSocket.h>
+
+@interface SRTWebSocketOperation : NSOperation <SRWebSocketDelegate>
+
+@property (nonatomic) BOOL isFinished;
+@property (nonatomic) BOOL isExecuting;
+
+@property (nonatomic, strong, readonly) NSError *error;
+
+- (instancetype)initWithURL:(NSURL *)URL;
+
+// We override these methods.  Please call super
+- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean NS_REQUIRES_SUPER;
+- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error NS_REQUIRES_SUPER;
+
+@end

+ 7 - 4
SRWebSocketTests/SRTWebSocketOperation.m → Tests/Operations/SRTWebSocketOperation.m

@@ -1,9 +1,12 @@
 //
-//  SRTWebSocketOperation.m
-//  SocketRocket
+// Copyright 2012 Square Inc.
+// Portions Copyright (c) 2016-present, Facebook, Inc.
 //
-//  Created by Mike Lewis on 1/28/12.
-//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
+// All rights reserved.
+//
+// This source code is licensed under the BSD-style license found in the
+// LICENSE file in the root directory of this source tree. An additional grant
+// of patent rights can be found in the PATENTS file in the same directory.
 //
 
 #import "SRTWebSocketOperation.h"

+ 0 - 0
SRWebSocketTests/SRWebSocketTests-Info.plist → Tests/Resources/SRWebSocketTests-Info.plist


+ 203 - 0
Tests/SRTAutobahnTests.m

@@ -0,0 +1,203 @@
+//
+// Copyright 2012 Square Inc.
+// Portions Copyright (c) 2016-present, Facebook, Inc.
+//
+// All rights reserved.
+//
+// This source code is licensed under the BSD-style license found in the
+// LICENSE file in the root directory of this source tree. An additional grant
+// of patent rights can be found in the PATENTS file in the same directory.
+//
+
+@import XCTest;
+
+#import <SocketRocket/SRWebSocket.h>
+
+#import "SRTWebSocketOperation.h"
+#import "SRAutobahnOperation.h"
+#import "XCTestCase+SRTAdditions.h"
+
+#define SRLogDebug(format, ...)
+//#define SRLogDebug(format, ...) NSLog(format, __VA_ARGS__)
+
+@interface SRTAutobahnTests : XCTestCase
+@end
+
+@interface NSInvocation (SRTBlockInvocation)
+
++ (NSInvocation *)invocationWithBlock:(dispatch_block_t)block;
+
+@end
+
+@interface SRTBlockInvoker
+
+- (instancetype)initWithBlock:(dispatch_block_t)block;
+
+- (void)invoke;
+
+@end
+
+@implementation SRTAutobahnTests {
+    SRWebSocket *_curWebSocket;
+    NSInteger _testCount;
+    NSInteger _curTest;
+    NSMutableArray *_sockets;
+    NSString *_testURLString;
+    NSURL *_prefixURL;
+    NSString *_agent;
+    NSString *_description;
+}
+
+- (instancetype)initWithInvocation:(NSInvocation *)anInvocation description:(NSString *)description;
+{
+    self = [self initWithInvocation:anInvocation];
+    if (self) {
+        _description = description;
+    }
+    return self;
+}
+
+- (instancetype)initWithInvocation:(NSInvocation *)anInvocation;
+{
+    self = [super initWithInvocation:anInvocation];
+    if (self) {
+        _testURLString = [[NSProcessInfo processInfo].environment objectForKey:@"SR_TEST_URL"];
+        _prefixURL = [NSURL URLWithString:_testURLString];
+        _agent = [NSBundle bundleForClass:[self class]].bundleIdentifier;
+    }
+    return self;
+}
+
+- (NSUInteger)testCaseCount;
+{
+    if (self.invocation) {
+        return [super testCaseCount];
+    }
+
+    __block NSUInteger count = 0;
+    SRAutobahnOperation *caseGetter = SRAutobahnTestCaseCountOperation(_prefixURL, _agent, ^(NSInteger caseCount) {
+        count = caseCount;
+    });
+    [caseGetter start];
+
+    [self runCurrentRunLoopUntilTestPasses:^BOOL{
+        return caseGetter.isFinished;
+    } timeout:20.0];
+
+    XCTAssertNil(caseGetter.error, @"CaseGetter should have successfully returned the number of testCases. Instead got error %@", caseGetter.error);
+    return count;
+}
+
+- (BOOL)isEmpty;
+{
+    return NO;
+}
+
+- (void)performTest:(XCTestCaseRun *) aRun
+{
+    if (self.invocation) {
+        [super performTest:aRun];
+        return;
+    }
+    [aRun start];
+    for (NSUInteger i = 1; i <= aRun.test.testCaseCount; i++) {
+        SEL sel = @selector(performTestWithNumber:);
+        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:sel]];
+
+        invocation.selector = sel;
+        invocation.target = self;
+
+        [invocation setArgument:&i atIndex:2];
+
+        NSString *description = [self caseDescriptionForCaseNumber:i];
+
+        XCTestCase *testCase = [[[self class] alloc] initWithInvocation:invocation description:description];
+
+        XCTestCaseRun *run = [[XCTestCaseRun alloc] initWithTest:testCase];
+
+        [testCase performTest:run];
+    }
+    [aRun stop];
+
+    [self updateReports];
+}
+
+- (NSInteger)testNum;
+{
+    NSInteger i;
+    [self.invocation getArgument:&i atIndex:2];
+    return i;
+}
+
+- (NSString *)caseDescriptionForCaseNumber:(NSInteger)caseNumber;
+{
+    __block NSDictionary *caseInfo = nil;
+    SRAutobahnOperation *testInfoOperation = SRAutobahnTestCaseInfoOperation(_prefixURL, caseNumber, ^(NSDictionary * _Nullable caseInfo) {
+        caseInfo = caseInfo;
+    });
+
+    [testInfoOperation start];
+
+    [self runCurrentRunLoopUntilTestPasses:^BOOL{
+        return testInfoOperation.isFinished;
+    } timeout:60 * 60];
+
+    XCTAssertNil(testInfoOperation.error, @"Updating the report should not have errored");
+
+    return [NSString stringWithFormat:@"%@ - %@", caseInfo[@"id"], caseInfo[@"description"]];
+}
+
+- (NSString *)description;
+{
+    if (_description) {
+        return _description;
+    } else {
+        return @"Autobahn Test Harness";
+    }
+}
+
++ (id) defaultTestSuite
+{
+    return [[[self class] alloc] init];
+}
+
+- (void)performTestWithNumber:(NSInteger)testNumber;
+{
+    NSOperationQueue *testQueue = [[NSOperationQueue alloc] init];
+
+    testQueue.maxConcurrentOperationCount = 1;
+
+    SRAutobahnOperation *testOp = SRAutobahnTestOperation(_prefixURL, testNumber, _agent);
+    [testQueue addOperation:testOp];
+
+    __block NSDictionary *resultInfo = nil;
+
+    SRAutobahnOperation *resultOp = SRAutobahnTestResultOperation(_prefixURL, testNumber, _agent, ^(NSDictionary * _Nullable result) {
+        resultInfo = result;
+    });
+    [resultOp addDependency:testOp];
+    [testQueue addOperation:resultOp];
+
+    testQueue.suspended = NO;
+
+    [self runCurrentRunLoopUntilTestPasses:^BOOL{
+        return resultOp.isFinished;
+    } timeout:60 * 60];
+
+    XCTAssertTrue(!testOp.error, @"Test operation should not have failed");
+    XCTAssertEqualObjects(@"OK", resultInfo[@"behavior"], @"Test behavior should be OK");
+}
+
+- (void)updateReports
+{
+    SRAutobahnOperation *operation = SRAutobahnTestUpdateReportsOperation(_prefixURL, _agent);
+    [operation start];
+
+    [self runCurrentRunLoopUntilTestPasses:^BOOL{
+        return operation.isFinished;
+    } timeout:60 * 60];
+
+    XCTAssertNil(operation.error, @"Updating the report should not have errored");
+}
+
+@end

+ 5 - 9
SRWebSocketTests/SenTestCase+SRTAdditions.h → Tests/Utilities/XCTestCase+SRTAdditions.h

@@ -1,22 +1,18 @@
 //
 // Copyright 2012 Square Inc.
 // Portions Copyright (c) 2016-present, Facebook, Inc.
-// 
+//
 // All rights reserved.
-// 
+//
 // This source code is licensed under the BSD-style license found in the
-// LICENSE file in the root directory of this source tree. An additional grant 
+// LICENSE file in the root directory of this source tree. An additional grant
 // of patent rights can be found in the PATENTS file in the same directory.
 //
 
 #import <XCTest/XCTest.h>
 
+@interface XCTestCase (SRTAdditions)
 
-typedef BOOL (^PXPredicateBlock)();
-
-
-@interface XCTest (PXAdditions)
-
-- (void)runCurrentRunLoopUntilTestPasses:(PXPredicateBlock)predicate timeout:(NSTimeInterval)timeout;
+- (void)runCurrentRunLoopUntilTestPasses:(BOOL (^)())predicate timeout:(NSTimeInterval)timeout;
 
 @end

+ 10 - 13
SRWebSocketTests/SenTestCase+SRTAdditions.m → Tests/Utilities/XCTestCase+SRTAdditions.m

@@ -1,32 +1,29 @@
 //
 // Copyright 2012 Square Inc.
 // Portions Copyright (c) 2016-present, Facebook, Inc.
-// 
+//
 // All rights reserved.
-// 
+//
 // This source code is licensed under the BSD-style license found in the
-// LICENSE file in the root directory of this source tree. An additional grant 
+// LICENSE file in the root directory of this source tree. An additional grant
 // of patent rights can be found in the PATENTS file in the same directory.
 //
 
-#import "SenTestCase+SRTAdditions.h"
-
+#import "XCTestCase+SRTAdditions.h"
 
 @implementation XCTestCase (SRTAdditions)
 
-- (void)runCurrentRunLoopUntilTestPasses:(PXPredicateBlock)predicate timeout:(NSTimeInterval)timeout;
+- (void)runCurrentRunLoopUntilTestPasses:(BOOL (^)())predicate timeout:(NSTimeInterval)timeout
 {
     NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeout];
-    
+
     NSTimeInterval timeoutTime = [timeoutDate timeIntervalSinceReferenceDate];
-    NSTimeInterval currentTime;
-    
-    for (currentTime = [NSDate timeIntervalSinceReferenceDate];
-         !predicate() && currentTime < timeoutTime;
-         currentTime = [NSDate timeIntervalSinceReferenceDate]) {
+    NSTimeInterval currentTime = [NSDate timeIntervalSinceReferenceDate];
+
+    while (!predicate() && currentTime < timeoutTime) {
         [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
+        currentTime = [NSDate timeIntervalSinceReferenceDate];
     }
-    
     XCTAssertTrue(currentTime <= timeoutTime, @"Timed out");
 }