From 46b2e73c3333c65fa437d9ae7c223061ddd0e022 Mon Sep 17 00:00:00 2001 From: Alex Billingsley Date: Mon, 1 Feb 2016 16:05:24 -0500 Subject: [PATCH 1/3] Update AFNetworking Dependencies to 2.6.3 and SocketRocket to 0.4.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes the need for SRHTTPRequestOperation making it easier to adopt AFNetworking out of the box Improves Unit Test tooling Fixes SSE Unit Tests Still has failing unit tests because of “API violation - creating expectations while already in waiting mode.” --- Podfile | 20 +- Podfile.lock | 42 +- .../project.pbxproj | 115 +-- .../xcschemes/Documentation.xcscheme | 13 +- .../xcschemes/SignalR.Client.OSX.xcscheme | 13 +- .../SignalR.Client.Tests.OSX.xcscheme | 13 +- .../SignalR.Client.Tests.iOS.xcscheme | 13 +- .../xcschemes/SignalR.Client.iOS.xcscheme | 13 +- .../xcschemes/SignalR.Samples.OSX.xcscheme | 13 +- .../xcschemes/SignalR.Samples.iOS.xcscheme | 13 +- .../SignalR.Client.Tests.OSX-Info.plist | 2 +- .../SignalR.Client.Tests.iOS-Info.plist | 2 +- .../SignalR.Client.Tests/SRMockNegotiate.h | 22 + .../SignalR.Client.Tests/SRMockNegotiate.m | 81 +++ .../SignalR.Client.Tests/SRMockNetwork.h | 2 +- .../SignalR.Client.Tests/SRMockNetwork.m | 4 +- .../SRServerSentEventsTransportTests.m | 668 +++++------------- .../SignalR.Samples.OSX-Info.plist | 2 +- .../SignalR.Samples.iOS-Info.plist | 2 +- .../Transports/SRServerSentEventsTransport.m | 176 ++--- .../SREventSourceRequestSerializer.h | 27 + .../SREventSourceRequestSerializer.m | 38 + .../SREventSourceResponseSerializer.h | 27 + .../SREventSourceResponseSerializer.m | 73 ++ .../ServerSentEvents/SRServerSentEvent.h | 2 +- 25 files changed, 695 insertions(+), 701 deletions(-) create mode 100644 SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNegotiate.h create mode 100644 SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNegotiate.m create mode 100755 SignalR.Client/Transports/ServerSentEvents/SREventSourceRequestSerializer.h create mode 100755 SignalR.Client/Transports/ServerSentEvents/SREventSourceRequestSerializer.m create mode 100755 SignalR.Client/Transports/ServerSentEvents/SREventSourceResponseSerializer.h create mode 100755 SignalR.Client/Transports/ServerSentEvents/SREventSourceResponseSerializer.m diff --git a/Podfile b/Podfile index b174abba..730b3b10 100644 --- a/Podfile +++ b/Podfile @@ -2,33 +2,33 @@ xcodeproj 'SignalR.Client.ObjC/SignalR.Client.ObjC' workspace 'SignalR.Client.ObjC' target :"SignalR.Client.iOS", :exclusive => true do - platform :ios, '6.0' - pod 'AFNetworking', '2.2.3' - pod 'SocketRocket', '0.4' + platform :ios, '7.0' + pod 'AFNetworking', '2.6.3' + pod 'SocketRocket', '0.4.2' end target :"SignalR.Client.OSX", :exclusive => true do - platform :osx, '10.8' - pod 'AFNetworking', '2.2.3' - pod 'SocketRocket', '0.4' + platform :osx, '10.9' + pod 'AFNetworking', '2.6.3' + pod 'SocketRocket', '0.4.2' end target :"SignalR.Client.Tests.OSX", :exclusive => true do - platform :osx, '10.8' + platform :osx, '10.9' pod 'OCMock' end target :"SignalR.Client.Tests.iOS", :exclusive => true do - platform :ios, '6.0' + platform :ios, '7.0' pod 'OCMock' end target :"SignalR.Samples.iOS", :exclusive => true do - platform :ios, '6.0' + platform :ios, '7.0' end target :"SignalR.Samples.OSX", :exclusive => true do - platform :osx, '10.8' + platform :osx, '10.9' end diff --git a/Podfile.lock b/Podfile.lock index 432aab35..da6361c8 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,34 +1,36 @@ PODS: - - AFNetworking (2.2.3): - - AFNetworking/NSURLConnection (= 2.2.3) - - AFNetworking/NSURLSession (= 2.2.3) - - AFNetworking/Reachability (= 2.2.3) - - AFNetworking/Security (= 2.2.3) - - AFNetworking/Serialization (= 2.2.3) - - AFNetworking/UIKit (= 2.2.3) - - AFNetworking/NSURLConnection (2.2.3): + - AFNetworking (2.6.3): + - AFNetworking/NSURLConnection (= 2.6.3) + - AFNetworking/NSURLSession (= 2.6.3) + - AFNetworking/Reachability (= 2.6.3) + - AFNetworking/Security (= 2.6.3) + - AFNetworking/Serialization (= 2.6.3) + - AFNetworking/UIKit (= 2.6.3) + - AFNetworking/NSURLConnection (2.6.3): - AFNetworking/Reachability - AFNetworking/Security - AFNetworking/Serialization - - AFNetworking/NSURLSession (2.2.3): - - AFNetworking/NSURLConnection - - AFNetworking/Reachability (2.2.3) - - AFNetworking/Security (2.2.3) - - AFNetworking/Serialization (2.2.3) - - AFNetworking/UIKit (2.2.3): + - AFNetworking/NSURLSession (2.6.3): + - AFNetworking/Reachability + - AFNetworking/Security + - AFNetworking/Serialization + - AFNetworking/Reachability (2.6.3) + - AFNetworking/Security (2.6.3) + - AFNetworking/Serialization (2.6.3) + - AFNetworking/UIKit (2.6.3): - AFNetworking/NSURLConnection - AFNetworking/NSURLSession - OCMock (2.2.2) - - SocketRocket (0.4) + - SocketRocket (0.4.2) DEPENDENCIES: - - AFNetworking (= 2.2.3) + - AFNetworking (= 2.6.3) - OCMock - - SocketRocket (= 0.4) + - SocketRocket (= 0.4.2) SPEC CHECKSUMS: - AFNetworking: fd6df10652c90fe082b188b5af0660f123423f80 + AFNetworking: cb8d14a848e831097108418f5d49217339d4eb60 OCMock: c8b8928d457c8f1873d563537ab1cc31bef40b87 - SocketRocket: 01443933b7bc362621325f4607b80b4286a99707 + SocketRocket: ffe08119b00ef982f6c37052a4705a057c8494ad -COCOAPODS: 0.38.2 +COCOAPODS: 0.39.0 diff --git a/SignalR.Client.ObjC/SignalR.Client.ObjC.xcodeproj/project.pbxproj b/SignalR.Client.ObjC/SignalR.Client.ObjC.xcodeproj/project.pbxproj index dcac9b09..ae119db6 100644 --- a/SignalR.Client.ObjC/SignalR.Client.ObjC.xcodeproj/project.pbxproj +++ b/SignalR.Client.ObjC/SignalR.Client.ObjC.xcodeproj/project.pbxproj @@ -163,6 +163,16 @@ 3994AAAD1714D06E00E452F7 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3994AAAA1714D06900E452F7 /* Security.framework */; }; 3994AAB01714D09200E452F7 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 3994AAAE1714D08E00E452F7 /* libicucore.dylib */; }; 3994AAB11714D09400E452F7 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 3994AAAE1714D08E00E452F7 /* libicucore.dylib */; }; + 39A27EFA1C5FD151009B7459 /* SREventSourceRequestSerializer.h in Headers */ = {isa = PBXBuildFile; fileRef = 39A27EF81C5FD151009B7459 /* SREventSourceRequestSerializer.h */; }; + 39A27EFB1C5FD151009B7459 /* SREventSourceRequestSerializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 39A27EF91C5FD151009B7459 /* SREventSourceRequestSerializer.m */; }; + 39A27EFE1C5FD242009B7459 /* SREventSourceResponseSerializer.h in Headers */ = {isa = PBXBuildFile; fileRef = 39A27EFC1C5FD242009B7459 /* SREventSourceResponseSerializer.h */; }; + 39A27EFF1C5FD242009B7459 /* SREventSourceResponseSerializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 39A27EFD1C5FD242009B7459 /* SREventSourceResponseSerializer.m */; }; + 39A27F001C5FD42D009B7459 /* SREventSourceRequestSerializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 39A27EF91C5FD151009B7459 /* SREventSourceRequestSerializer.m */; }; + 39A27F011C5FD42F009B7459 /* SREventSourceResponseSerializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 39A27EFD1C5FD242009B7459 /* SREventSourceResponseSerializer.m */; }; + 39A27F021C5FD76A009B7459 /* SRServerSentEventsTransportTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D05649B71B6EF87F00020B31 /* SRServerSentEventsTransportTests.m */; }; + 39A27F031C5FD76B009B7459 /* SRServerSentEventsTransportTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D05649B71B6EF87F00020B31 /* SRServerSentEventsTransportTests.m */; }; + 39A27F061C5FF8F0009B7459 /* SRMockNegotiate.m in Sources */ = {isa = PBXBuildFile; fileRef = 39A27F051C5FF8F0009B7459 /* SRMockNegotiate.m */; }; + 39A27F071C5FF8F0009B7459 /* SRMockNegotiate.m in Sources */ = {isa = PBXBuildFile; fileRef = 39A27F051C5FF8F0009B7459 /* SRMockNegotiate.m */; }; 39AF0D7E17138E3800E13E6E /* SRWebSocketTransport.h in Headers */ = {isa = PBXBuildFile; fileRef = 39AF0D7C17138E3800E13E6E /* SRWebSocketTransport.h */; settings = {ATTRIBUTES = (Public, ); }; }; 39AF0D7F17138E3800E13E6E /* SRWebSocketTransport.m in Sources */ = {isa = PBXBuildFile; fileRef = 39AF0D7D17138E3800E13E6E /* SRWebSocketTransport.m */; }; 39AF0D821713935A00E13E6E /* SRWebSocketConnectionInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 39AF0D801713935A00E13E6E /* SRWebSocketConnectionInfo.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -200,8 +210,6 @@ 39FA642A1891655300C035DC /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 39FA64281891654500C035DC /* XCTest.framework */; }; A9291598313CC816F1114BA7 /* libPods-SignalR.Client.Tests.iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C493383CF341D38AA4D479F /* libPods-SignalR.Client.Tests.iOS.a */; }; B032892F3D1F25CA84B4DE26 /* libPods-SignalR.Client.OSX.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EB242D5EF8E1786500EAE628 /* libPods-SignalR.Client.OSX.a */; }; - D05649B81B6EF87F00020B31 /* SRServerSentEventsTransportTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D05649B71B6EF87F00020B31 /* SRServerSentEventsTransportTests.m */; }; - D05649B91B6EF87F00020B31 /* SRServerSentEventsTransportTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D05649B71B6EF87F00020B31 /* SRServerSentEventsTransportTests.m */; }; D05649BB1B7058FF00020B31 /* SRWebSocketTransportTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D05649BA1B7058FF00020B31 /* SRWebSocketTransportTests.m */; }; D05649BC1B7058FF00020B31 /* SRWebSocketTransportTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D05649BA1B7058FF00020B31 /* SRWebSocketTransportTests.m */; }; D05649BE1B71441000020B31 /* SRLongPollingTransportTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D05649BD1B71441000020B31 /* SRLongPollingTransportTests.m */; }; @@ -326,6 +334,12 @@ 3994AAA51714D01900E452F7 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; }; 3994AAAA1714D06900E452F7 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 3994AAAE1714D08E00E452F7 /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = usr/lib/libicucore.dylib; sourceTree = SDKROOT; }; + 39A27EF81C5FD151009B7459 /* SREventSourceRequestSerializer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SREventSourceRequestSerializer.h; sourceTree = ""; }; + 39A27EF91C5FD151009B7459 /* SREventSourceRequestSerializer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SREventSourceRequestSerializer.m; sourceTree = ""; }; + 39A27EFC1C5FD242009B7459 /* SREventSourceResponseSerializer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SREventSourceResponseSerializer.h; sourceTree = ""; }; + 39A27EFD1C5FD242009B7459 /* SREventSourceResponseSerializer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SREventSourceResponseSerializer.m; sourceTree = ""; }; + 39A27F041C5FF8F0009B7459 /* SRMockNegotiate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SRMockNegotiate.h; path = SignalR.Client.Tests/SRMockNegotiate.h; sourceTree = ""; }; + 39A27F051C5FF8F0009B7459 /* SRMockNegotiate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SRMockNegotiate.m; path = SignalR.Client.Tests/SRMockNegotiate.m; sourceTree = ""; }; 39AF0D7C17138E3800E13E6E /* SRWebSocketTransport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRWebSocketTransport.h; sourceTree = ""; }; 39AF0D7D17138E3800E13E6E /* SRWebSocketTransport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRWebSocketTransport.m; sourceTree = ""; }; 39AF0D801713935A00E13E6E /* SRWebSocketConnectionInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SRWebSocketConnectionInfo.h; path = WebSockets/SRWebSocketConnectionInfo.h; sourceTree = ""; }; @@ -665,6 +679,10 @@ 3920785015AF13F6009B959E /* ServerSentEvents */ = { isa = PBXGroup; children = ( + 39A27EFC1C5FD242009B7459 /* SREventSourceResponseSerializer.h */, + 39A27EFD1C5FD242009B7459 /* SREventSourceResponseSerializer.m */, + 39A27EF81C5FD151009B7459 /* SREventSourceRequestSerializer.h */, + 39A27EF91C5FD151009B7459 /* SREventSourceRequestSerializer.m */, 3920785115AF13F6009B959E /* SRChunkBuffer.h */, 3920785215AF13F6009B959E /* SRChunkBuffer.m */, 3920785315AF13F6009B959E /* SREventSourceStreamReader.h */, @@ -787,6 +805,8 @@ D05649BD1B71441000020B31 /* SRLongPollingTransportTests.m */, 3955D59C1B9794A8005AD0CF /* SRMockNetwork.h */, 3955D59D1B9794A8005AD0CF /* SRMockNetwork.m */, + 39A27F041C5FF8F0009B7459 /* SRMockNegotiate.h */, + 39A27F051C5FF8F0009B7459 /* SRMockNegotiate.m */, ); name = SignalR.Client.Tests; sourceTree = ""; @@ -875,12 +895,14 @@ 3920789315AF13F6009B959E /* SRNegotiationResponse.h in Headers */, 3920789515AF13F6009B959E /* SRLog.h in Headers */, 3920789615AF13F6009B959E /* SRChunkBuffer.h in Headers */, + 39A27EFE1C5FD242009B7459 /* SREventSourceResponseSerializer.h in Headers */, 3920789815AF13F6009B959E /* SREventSourceStreamReader.h in Headers */, 3920789D15AF13F6009B959E /* SRAutoTransport.h in Headers */, 3920788115AF13F6009B959E /* NSObject+SRJSON.h in Headers */, 392078A015AF13F6009B959E /* SRClientTransportInterface.h in Headers */, 392078A115AF13F6009B959E /* SRHttpBasedTransport.h in Headers */, 392078A315AF13F6009B959E /* SRLongPollingTransport.h in Headers */, + 39A27EFA1C5FD151009B7459 /* SREventSourceRequestSerializer.h in Headers */, 392078A515AF13F6009B959E /* SRServerSentEventsTransport.h in Headers */, 39AF0D7E17138E3800E13E6E /* SRWebSocketTransport.h in Headers */, 39AF0D821713935A00E13E6E /* SRWebSocketConnectionInfo.h in Headers */, @@ -972,8 +994,8 @@ 39E6FCD015C6BDC600434098 /* Sources */, 39E6FCD115C6BDC600434098 /* Frameworks */, 39E6FCD215C6BDC600434098 /* Resources */, - 39E6FCD315C6BDC600434098 /* ShellScript */, 9DC2EB4DAEF87C59FF882FB5 /* Copy Pods Resources */, + E7CB64192A8E0E0DEC70EDBA /* Embed Pods Frameworks */, ); buildRules = ( ); @@ -993,8 +1015,8 @@ 39E6FD1215C6BF4000434098 /* Sources */, 39E6FD1315C6BF4000434098 /* Frameworks */, 39E6FD1415C6BF4000434098 /* Resources */, - 39E6FD1515C6BF4000434098 /* ShellScript */, 6EC1074B81D5D81279870B7F /* Copy Pods Resources */, + 6FB19AAEA87EE65EE4B1D8E8 /* Embed Pods Frameworks */, ); buildRules = ( ); @@ -1012,7 +1034,7 @@ 392077FE15AF13C3009B959E /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0640; + LastUpgradeCheck = 0720; ORGANIZATIONNAME = "DyKnow LLC"; }; buildConfigurationList = 3920780115AF13C3009B959E /* Build configuration list for PBXProject "SignalR.Client.ObjC" */; @@ -1111,33 +1133,22 @@ shellPath = /bin/sh; shellScript = "/usr/local/bin/appledoc \\\n--project-name \"SignalR-ObjC\" \\\n--project-company \"DyKnow\" \\\n--company-id \"com.dyknow\" \\\n--docset-atom-filename \"SignalR-ObjC.atom\" \\\n--docset-feed-url \"http://dyknow.github.com/SignalR-ObjC/%DOCSETATOMFILENAME\" \\\n--docset-package-url \"http://dyknow.github.com/SignalR-ObjC/%DOCSETPACKAGEFILENAME\" \\\n--docset-fallback-url \"http://dyknow.github.com/SignalR-ObjC/\" \\\n--output \"~/Documentation/SignalR-ObjC\" \\\n--publish-docset \\\n--logformat xcode \\\n--keep-undocumented-objects \\\n--keep-undocumented-members \\\n--keep-intermediate-files \\\n--no-repeat-first-par \\\n--no-warn-invalid-crossref \\\n--ignore \"*.m\" \\\n\"${PROJECT_DIR}/../SignalR.Client\""; }; - 39E6FCD315C6BDC600434098 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n"; - }; - 39E6FD1515C6BF4000434098 /* ShellScript */ = { + 5E58010E027C503BCB2DC2B1 /* Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); + name = "Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n"; + shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-SignalR.Client.OSX/Pods-SignalR.Client.OSX-resources.sh\"\n"; + showEnvVarsInLog = 0; }; - 5E58010E027C503BCB2DC2B1 /* Copy Pods Resources */ = { + 6EC1074B81D5D81279870B7F /* Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1149,22 +1160,22 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-SignalR.Client.OSX/Pods-SignalR.Client.OSX-resources.sh\"\n"; + shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-SignalR.Client.Tests.iOS/Pods-SignalR.Client.Tests.iOS-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 6EC1074B81D5D81279870B7F /* Copy Pods Resources */ = { + 6FB19AAEA87EE65EE4B1D8E8 /* Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Copy Pods Resources"; + name = "Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-SignalR.Client.Tests.iOS/Pods-SignalR.Client.Tests.iOS-resources.sh\"\n"; + shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-SignalR.Client.Tests.iOS/Pods-SignalR.Client.Tests.iOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; 799AACE0F774B852C4BDD0D2 /* Check Pods Manifest.lock */ = { @@ -1227,6 +1238,21 @@ shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; + E7CB64192A8E0E0DEC70EDBA /* Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-SignalR.Client.Tests.OSX/Pods-SignalR.Client.Tests.OSX-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; E8B9469FB1BAE4CB8F6EC87B /* Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1263,8 +1289,10 @@ 3919638115AF222E00DDC946 /* SRHubInvocation.m in Sources */, 3919638215AF222E00DDC946 /* SRHubProxy.m in Sources */, 39F7D7B31833FC99002590A6 /* SRServerSentEvent.m in Sources */, + 39A27F001C5FD42D009B7459 /* SREventSourceRequestSerializer.m in Sources */, 3919638415AF222E00DDC946 /* SRHubRegistrationData.m in Sources */, 3919638515AF222E00DDC946 /* SRHubResult.m in Sources */, + 39A27F011C5FD42F009B7459 /* SREventSourceResponseSerializer.m in Sources */, 3919638715AF222E00DDC946 /* SRSubscription.m in Sources */, 3919638915AF223A00DDC946 /* NSObject+SRJSON.m in Sources */, 3919638C15AF223A00DDC946 /* SRExceptionHelper.m in Sources */, @@ -1296,7 +1324,9 @@ 3920787715AF13F6009B959E /* SRHubRegistrationData.m in Sources */, 3920787915AF13F6009B959E /* SRHubResult.m in Sources */, 3920787E15AF13F6009B959E /* SRSubscription.m in Sources */, + 39A27EFF1C5FD242009B7459 /* SREventSourceResponseSerializer.m in Sources */, 3920788215AF13F6009B959E /* NSObject+SRJSON.m in Sources */, + 39A27EFB1C5FD151009B7459 /* SREventSourceRequestSerializer.m in Sources */, 3920788915AF13F6009B959E /* SRExceptionHelper.m in Sources */, 3920788E15AF13F6009B959E /* SRVersion.m in Sources */, 3920789115AF13F6009B959E /* SRConnection.m in Sources */, @@ -1339,9 +1369,10 @@ 39E6FD2D15C6BFCE00434098 /* SRJSONTests.m in Sources */, 39E6FD5F15C6EAFB00434098 /* NSObject+SRJSON.m in Sources */, D05649BC1B7058FF00020B31 /* SRWebSocketTransportTests.m in Sources */, - D05649B91B6EF87F00020B31 /* SRServerSentEventsTransportTests.m in Sources */, + 39A27F071C5FF8F0009B7459 /* SRMockNegotiate.m in Sources */, 3955D59F1B9794FD005AD0CF /* SRMockNetwork.m in Sources */, 39BB8D7715CAA576007B7245 /* SRHubConnectionTests.m in Sources */, + 39A27F021C5FD76A009B7459 /* SRServerSentEventsTransportTests.m in Sources */, 3940059515CC6A2C006673E2 /* SRChunkBufferTests.m in Sources */, 3940059A15CC6DD6006673E2 /* SRConnectionTests.m in Sources */, 39C97CF015D3DDD600F66B50 /* SRHubProxyTests.m in Sources */, @@ -1354,13 +1385,14 @@ files = ( 39E6FD2C15C6BFCE00434098 /* SRJSONTests.m in Sources */, 39E6FD6115C6EAFC00434098 /* NSObject+SRJSON.m in Sources */, + 39A27F031C5FD76B009B7459 /* SRServerSentEventsTransportTests.m in Sources */, D05649BE1B71441000020B31 /* SRLongPollingTransportTests.m in Sources */, - D05649B81B6EF87F00020B31 /* SRServerSentEventsTransportTests.m in Sources */, D05649BB1B7058FF00020B31 /* SRWebSocketTransportTests.m in Sources */, 3955D59E1B9794FC005AD0CF /* SRMockNetwork.m in Sources */, 39BB8D7615CAA576007B7245 /* SRHubConnectionTests.m in Sources */, 3940059415CC6A2C006673E2 /* SRChunkBufferTests.m in Sources */, 3940059915CC6DD6006673E2 /* SRConnectionTests.m in Sources */, + 39A27F061C5FF8F0009B7459 /* SRMockNegotiate.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1462,6 +1494,7 @@ INFOPLIST_FILE = "SignalR.Samples.OSX/SignalR.Samples.OSX-Info.plist"; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = "-ObjC"; + PRODUCT_BUNDLE_IDENTIFIER = "com.SignalR.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; USER_HEADER_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)/**"; @@ -1484,6 +1517,7 @@ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; INFOPLIST_FILE = "SignalR.Samples.OSX/SignalR.Samples.OSX-Info.plist"; OTHER_LDFLAGS = "-ObjC"; + PRODUCT_BUNDLE_IDENTIFIER = "com.SignalR.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; USER_HEADER_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)/**"; @@ -1588,6 +1622,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -1604,8 +1639,8 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 6.0; - MACOSX_DEPLOYMENT_TARGET = 10.8; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + MACOSX_DEPLOYMENT_TARGET = 10.9; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; }; @@ -1635,8 +1670,8 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 6.0; - MACOSX_DEPLOYMENT_TARGET = 10.8; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + MACOSX_DEPLOYMENT_TARGET = 10.9; SDKROOT = iphoneos; VALIDATE_PRODUCT = NO; }; @@ -1710,6 +1745,7 @@ GCC_PREFIX_HEADER = "SignalR.Samples.iOS/SignalR.Samples.iOS-Prefix.pch"; INFOPLIST_FILE = "SignalR.Samples.iOS/SignalR.Samples.iOS-Info.plist"; OTHER_LDFLAGS = "-ObjC"; + PRODUCT_BUNDLE_IDENTIFIER = "com.SignalR.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = "1,2"; USER_HEADER_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)/**"; @@ -1726,6 +1762,7 @@ INFOPLIST_FILE = "SignalR.Samples.iOS/SignalR.Samples.iOS-Info.plist"; OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; OTHER_LDFLAGS = "-ObjC"; + PRODUCT_BUNDLE_IDENTIFIER = "com.SignalR.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = "1,2"; USER_HEADER_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)/**"; @@ -1740,18 +1777,13 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "\\\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\\\"", - "\\\"$(SYSTEM_APPS_DIR)/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks\\\"", - "$(inherited)", - "$(DEVELOPER_FRAMEWORKS_DIR)", - ); GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "SignalR.Client.Tests.OSX/SignalR.Client.Tests.OSX-Prefix.pch"; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; INFOPLIST_FILE = "SignalR.Client.Tests.OSX/SignalR.Client.Tests.OSX-Info.plist"; ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.SignalR.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; }; @@ -1765,17 +1797,12 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COMBINE_HIDPI_IMAGES = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - FRAMEWORK_SEARCH_PATHS = ( - "\\\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\\\"", - "\\\"$(SYSTEM_APPS_DIR)/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks\\\"", - "$(inherited)", - "$(DEVELOPER_FRAMEWORKS_DIR)", - ); GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "SignalR.Client.Tests.OSX/SignalR.Client.Tests.OSX-Prefix.pch"; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; INFOPLIST_FILE = "SignalR.Client.Tests.OSX/SignalR.Client.Tests.OSX-Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = "com.SignalR.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; }; @@ -1796,6 +1823,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "SignalR.Client.Tests.iOS/SignalR.Client.Tests.iOS-Prefix.pch"; INFOPLIST_FILE = "SignalR.Client.Tests.iOS/SignalR.Client.Tests.iOS-Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = "com.SignalR.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -1815,6 +1843,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "SignalR.Client.Tests.iOS/SignalR.Client.Tests.iOS-Prefix.pch"; INFOPLIST_FILE = "SignalR.Client.Tests.iOS/SignalR.Client.Tests.iOS-Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = "com.SignalR.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; VALIDATE_PRODUCT = YES; }; diff --git a/SignalR.Client.ObjC/SignalR.Client.ObjC.xcodeproj/xcshareddata/xcschemes/Documentation.xcscheme b/SignalR.Client.ObjC/SignalR.Client.ObjC.xcodeproj/xcshareddata/xcschemes/Documentation.xcscheme index bfcb9045..ddbea46e 100644 --- a/SignalR.Client.ObjC/SignalR.Client.ObjC.xcodeproj/xcshareddata/xcschemes/Documentation.xcscheme +++ b/SignalR.Client.ObjC/SignalR.Client.ObjC.xcodeproj/xcshareddata/xcschemes/Documentation.xcscheme @@ -1,6 +1,6 @@ + shouldUseLaunchSchemeArgsEnv = "YES"> + + + shouldUseLaunchSchemeArgsEnv = "YES"> @@ -48,15 +48,18 @@ ReferencedContainer = "container:SignalR.Client.ObjC.xcodeproj"> + + + shouldUseLaunchSchemeArgsEnv = "YES"> @@ -48,15 +48,18 @@ ReferencedContainer = "container:SignalR.Client.ObjC.xcodeproj"> + + + shouldUseLaunchSchemeArgsEnv = "YES"> @@ -48,15 +48,18 @@ ReferencedContainer = "container:SignalR.Client.ObjC.xcodeproj"> + + + shouldUseLaunchSchemeArgsEnv = "YES"> @@ -48,15 +48,18 @@ ReferencedContainer = "container:SignalR.Client.ObjC.xcodeproj"> + + + shouldUseLaunchSchemeArgsEnv = "YES"> @@ -48,15 +48,18 @@ ReferencedContainer = "container:SignalR.Client.ObjC.xcodeproj"> + + @@ -72,10 +75,10 @@ diff --git a/SignalR.Client.ObjC/SignalR.Client.ObjC.xcodeproj/xcshareddata/xcschemes/SignalR.Samples.iOS.xcscheme b/SignalR.Client.ObjC/SignalR.Client.ObjC.xcodeproj/xcshareddata/xcschemes/SignalR.Samples.iOS.xcscheme index a3e7f4bd..f10a97a8 100644 --- a/SignalR.Client.ObjC/SignalR.Client.ObjC.xcodeproj/xcshareddata/xcschemes/SignalR.Samples.iOS.xcscheme +++ b/SignalR.Client.ObjC/SignalR.Client.ObjC.xcodeproj/xcshareddata/xcschemes/SignalR.Samples.iOS.xcscheme @@ -1,6 +1,6 @@ + shouldUseLaunchSchemeArgsEnv = "YES"> @@ -48,15 +48,18 @@ ReferencedContainer = "container:SignalR.Client.ObjC.xcodeproj"> + + @@ -72,10 +75,10 @@ diff --git a/SignalR.Client.ObjC/SignalR.Client.Tests.OSX/SignalR.Client.Tests.OSX-Info.plist b/SignalR.Client.ObjC/SignalR.Client.Tests.OSX/SignalR.Client.Tests.OSX-Info.plist index d16bc778..169b6f71 100644 --- a/SignalR.Client.ObjC/SignalR.Client.Tests.OSX/SignalR.Client.Tests.OSX-Info.plist +++ b/SignalR.Client.ObjC/SignalR.Client.Tests.OSX/SignalR.Client.Tests.OSX-Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier - com.SignalR.${PRODUCT_NAME:rfc1034identifier} + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType diff --git a/SignalR.Client.ObjC/SignalR.Client.Tests.iOS/SignalR.Client.Tests.iOS-Info.plist b/SignalR.Client.ObjC/SignalR.Client.Tests.iOS/SignalR.Client.Tests.iOS-Info.plist index d16bc778..169b6f71 100644 --- a/SignalR.Client.ObjC/SignalR.Client.Tests.iOS/SignalR.Client.Tests.iOS-Info.plist +++ b/SignalR.Client.ObjC/SignalR.Client.Tests.iOS/SignalR.Client.Tests.iOS-Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier - com.SignalR.${PRODUCT_NAME:rfc1034identifier} + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType diff --git a/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNegotiate.h b/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNegotiate.h new file mode 100644 index 00000000..54b345a0 --- /dev/null +++ b/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNegotiate.h @@ -0,0 +1,22 @@ +// +// SRMockNegotiate.h +// SignalR.Client.ObjC +// +// Created by Alex Billingsley on 2/1/16. +// Copyright © 2016 DyKnow LLC. All rights reserved. +// + +#import +#import "SRClientTransportInterface.h" + +@interface SRMockNegotiate : NSObject + ++ (id)mockNegotiateForTransport:(id )transport + statusCode:(NSNumber *)statusCode + json:(id)json; + ++ (id)mockNegotiateForTransport:(id )transport + statusCode:(NSNumber *)statusCode + error:(NSError *)error; + +@end diff --git a/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNegotiate.m b/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNegotiate.m new file mode 100644 index 00000000..aa35984d --- /dev/null +++ b/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNegotiate.m @@ -0,0 +1,81 @@ +// +// SRMockNegotiate.m +// SignalR.Client.ObjC +// +// Created by Alex Billingsley on 2/1/16. +// Copyright © 2016 DyKnow LLC. All rights reserved. +// + +#import "SRMockNegotiate.h" +#import +#import +#import "SRClientTransportInterface.h" +#import "SRNegotiationResponse.h" + +@implementation SRMockNegotiate + ++ (id)negotiateStub:(id)mock +statusCode:(NSNumber *)statusCode + json:(id)json + callback:(NSInteger)callbackIndex { + [[[mock stub] andDo:^(NSInvocation *invocation) { + void (^completionHandler)(SRNegotiationResponse * response, NSError *error); + __unsafe_unretained void (^negotiateCallback)(SRNegotiationResponse *, NSError *) = nil; + [invocation getArgument: &negotiateCallback atIndex:callbackIndex]; + completionHandler = negotiateCallback; + + if ([statusCode isEqual: @200]) { + if (completionHandler) { + completionHandler([[SRNegotiationResponse alloc ]initWithDictionary:json], nil); + } + } else { + if (completionHandler) { + completionHandler(nil, json); + } + } + }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; + return mock; +} + ++ (id)mockNegotiateForTransport:(id )transport + statusCode:(NSNumber *)statusCode + json:(id)json; { + return [self mockNegotiateForTransport:transport statusCode:statusCode json:json callback:4]; +} + ++ (id)mockNegotiateForTransport:(id )transport + statusCode:(NSNumber *)statusCode + json:(id)json + callback:(NSInteger)callbackIndex; { + return [[self class] mockNegotiateForMockTransport:[OCMockObject partialMockForObject:transport] statusCode:statusCode json:json callback:callbackIndex]; +} + ++ (id)mockNegotiateForMockTransport:(id)transportMock + statusCode:(NSNumber *)statusCode + json:(id)json + callback:(NSInteger)callbackIndex; { + return [[self class] negotiateStub:transportMock statusCode:statusCode json:json callback:callbackIndex]; +} + + ++ (id)mockNegotiateForTransport:(id )transport + statusCode:(NSNumber *)statusCode + error:(NSError *)error { + return [self mockNegotiateForTransport:transport statusCode:statusCode error:error callback:4]; +} + ++ (id)mockNegotiateForTransport:(id )transport + statusCode:(NSNumber *)statusCode + error:(NSError *)error + callback:(NSInteger)callbackIndex; { + return [[self class] mockNegotiateForMockTransport:[OCMockObject partialMockForObject:transport] statusCode:statusCode error:error callback:callbackIndex]; +} + ++ (id)mockNegotiateForMockTransport:(id)transportMock + statusCode:(NSNumber *)statusCode + error:(NSError *)error + callback:(NSInteger)callbackIndex; { + return [[self class] negotiateStub:transportMock statusCode:statusCode json:error callback:callbackIndex]; +} + +@end diff --git a/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNetwork.h b/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNetwork.h index c7c17804..99de9c25 100644 --- a/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNetwork.h +++ b/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNetwork.h @@ -27,7 +27,7 @@ + (id)mockHttpRequestOperationForClass:(Class)aClass statusCode:(NSNumber *)statusCode - error:(NSError *)error + responseString:(NSString *)responseString success:(NSInteger)successIndex error:(NSInteger)errorIndex; diff --git a/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNetwork.m b/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNetwork.m index 387afff5..92963ab2 100644 --- a/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNetwork.m +++ b/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNetwork.m @@ -20,7 +20,7 @@ + (id)stub:(id)mock [[[mock stub] andDo:^(NSInvocation *invocation) { if ([statusCode isEqual: @200]) { void (^successBlock)(AFHTTPRequestOperation *operation, id responseObject) = nil; - [invocation getArgument:&successBlock atIndex:2]; + [invocation getArgument:&successBlock atIndex:successIndex]; if (successBlock) { if ([json isKindOfClass:[NSString class]]) { [[[mock stub] andReturn:[json dataUsingEncoding:NSUTF8StringEncoding]] responseData]; @@ -38,7 +38,7 @@ + (id)stub:(id)mock } } else { void (^errorBlock)(AFHTTPRequestOperation *operation, NSError *error) = nil; - [invocation getArgument:&errorBlock atIndex:3]; + [invocation getArgument:&errorBlock atIndex:errorIndex]; if (errorBlock) { errorBlock(mock, json); } diff --git a/SignalR.Client.ObjC/SignalR.Client.Tests/SRServerSentEventsTransportTests.m b/SignalR.Client.ObjC/SignalR.Client.Tests/SRServerSentEventsTransportTests.m index aa994fed..5a3251e7 100644 --- a/SignalR.Client.ObjC/SignalR.Client.Tests/SRServerSentEventsTransportTests.m +++ b/SignalR.Client.ObjC/SignalR.Client.Tests/SRServerSentEventsTransportTests.m @@ -13,21 +13,8 @@ #import "SRConnection.h" #import "SRConnectionDelegate.h" #import "SRNegotiationResponse.h" - -@interface SRHTTPRequestOperation : AFHTTPRequestOperation - -typedef void (^AFURLConnectionOperationDidReceiveURLResponseBlock)(AFHTTPRequestOperation *operation, NSHTTPURLResponse *response); - -- (void)setDidReceiveResponseBlock:(void (^)(AFHTTPRequestOperation *operation, NSHTTPURLResponse *response))block; -- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success - failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; - -@property (readwrite, nonatomic, strong) NSHTTPURLResponse *response; -@property (readwrite, nonatomic, copy) AFURLConnectionOperationDidReceiveURLResponseBlock urlResponseBlock; -@property (nonatomic, strong) NSOutputStream *outputStream; - - -@end +#import "SRMockNetwork.h" +#import "SRMockNegotiate.h" @interface SRConnection (UnitTest) @property (strong, nonatomic, readwrite) NSNumber * disconnectTimeout; @@ -41,10 +28,8 @@ @interface SRServerSentEventsTransport () @interface SSE_NetworkMock: NSObject @property (readwrite, nonatomic, strong) id mock; //only call this directly if you don't want to trigger the stream.opened callback -@property (readwrite, nonatomic, copy) void (^onGotResponse)(AFHTTPRequestOperation *operation, NSHTTPURLResponse *response); @property (readwrite, nonatomic, copy) void (^onFailure)(AFHTTPRequestOperation *operation, NSError *error); --(void) openingResponse: (NSString*) initialData; --(void) message: (NSString*) messageStr; + @property (readwrite, nonatomic, strong) NSData* lastData; @property (readwrite, nonatomic, strong) id dataDelegate; @end @@ -55,14 +40,7 @@ - (id) init self = [super init]; __weak __typeof(&*self)weakSelf = self; - _mock = [OCMockObject niceMockForClass:[SRHTTPRequestOperation class]]; - [[[_mock stub] andDo:^(NSInvocation *invocation) { - __strong __typeof(&*weakSelf)strongSelf = weakSelf; - void (^callbackOut)(AFHTTPRequestOperation *operation, NSHTTPURLResponse *response); - [invocation getArgument: &callbackOut atIndex: 2]; - strongSelf.onGotResponse = callbackOut; - }] setDidReceiveResponseBlock: [OCMArg any]]; - + _mock = [OCMockObject niceMockForClass:[AFHTTPRequestOperation class]]; [[[_mock stub] andDo:^(NSInvocation *invocation) { __strong __typeof(&*weakSelf)strongSelf = weakSelf; void (^failureOut)(AFHTTPRequestOperation *operation, NSError *error); @@ -77,33 +55,47 @@ - (id) init return self; } --(void) openingResponse: (NSString*) initialData -{ +- (void)prepareForOpeningResponse:(void (^)())then { + return [self prepareForOpeningResponse:nil then:then]; +} + +- (void)prepareForOpeningResponse:(NSString *)response then:(void (^)())then { NSOutputStream* dataStream = [[NSOutputStream alloc] initToMemory]; [[[self.mock stub] andReturn: dataStream] outputStream]; - self.onGotResponse(self.mock, nil); - - if (!initialData) { - initialData = @""; + + if (!response) { + response = @""; } + NSData* data = [response dataUsingEncoding:NSUTF8StringEncoding]; - NSData* data = [initialData dataUsingEncoding:NSUTF8StringEncoding]; id streamChanges = [OCMockObject niceMockForClass: [NSStream class]]; [[[streamChanges stub] andReturn:data] propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; - [dataStream.delegate stream:streamChanges handleEvent:NSStreamEventOpenCompleted]; - _lastData = data; + + if (then) { + then(); + } _dataDelegate = dataStream.delegate; + _lastData = data; + + if (self.dataDelegate) { + [self.dataDelegate stream:streamChanges handleEvent:NSStreamEventOpenCompleted]; + } } --(void) message: (NSString*) messageStr -{ - NSMutableData* data = [[NSMutableData alloc] initWithData: _lastData]; - [data appendData:[messageStr dataUsingEncoding:NSUTF8StringEncoding]]; - +- (void)prepareForNextResponse:(NSString *)response then:(void (^)())then { + NSMutableData* prior = [[NSMutableData alloc] initWithData: _lastData]; + NSData* data = [response dataUsingEncoding:NSUTF8StringEncoding]; + [prior appendData:data]; id streamChanges = [OCMockObject niceMockForClass: [NSStream class]]; - [[[streamChanges stub] andReturn:data] propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; - [_dataDelegate stream:streamChanges handleEvent:NSStreamEventHasSpaceAvailable]; - _lastData = data; + [[[streamChanges stub] andReturn:prior] propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; + + if (then) { + then(); + } + + if (self.dataDelegate) { + [self.dataDelegate stream:streamChanges handleEvent:NSStreamEventHasSpaceAvailable]; + } } - (void)dealloc { @@ -162,60 +154,25 @@ - (void)tearDown { } - (void)testStartCallsTheCompletionHandlerAfterSuccess { - XCTestExpectation *expectation = [self expectationWithDescription:@"Handler called"]; - - __block void (^onGotResponse)(AFHTTPRequestOperation *operation, NSHTTPURLResponse *response); - __block void (^onFailure)(NSError*); - - id mock = [OCMockObject niceMockForClass:[SRHTTPRequestOperation class]]; - [[[mock stub] andDo:^(NSInvocation *invocation) { - __unsafe_unretained void (^successCallback)(AFHTTPRequestOperation *, NSHTTPURLResponse *) = nil; - [invocation getArgument: &successCallback atIndex: 2]; - onGotResponse = successCallback; - }] setDidReceiveResponseBlock: [OCMArg any]]; - - [[[mock stub] andDo:^(NSInvocation *invocation) { - __unsafe_unretained void (^failureCallback)(NSError *) = nil; - [invocation getArgument: &failureCallback atIndex: 3]; - onFailure = failureCallback; - }] setCompletionBlockWithSuccess: [OCMArg any] failure: [OCMArg any]]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Handler called"]; - // Here we stub the alloc class method ** - [[[mock stub] andReturn:mock] alloc]; - // And we stub initWithParam: passing the param we will pass to the method to test - [[[mock stub] andReturn:mock] initWithRequest:[OCMArg any]]; + SSE_NetworkMock *NetworkMock = [[SSE_NetworkMock alloc] init]; - SRConnection* connection = [SRConnection alloc]; - [connection initWithURLString:@"http://localhost:0000"]; + SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; connection.connectionToken = @"10101010101"; connection.connectionId = @"10101"; [connection changeState:disconnected toState:connected]; - - __weak __typeof(&*mock)weakMock = mock; SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init]; sse.serverSentEventsOperationQueue = nil;//set to nil to get around weird ARC OCMock bugs http://stackoverflow.com/questions/18121902/using-ocmock-on-nsoperation-gives-bad-access - [sse start: connection connectionData:@"12345" completionHandler:^(id response, NSError *error){ - [expectation fulfill]; + [NetworkMock prepareForOpeningResponse:^{ + [sse start: connection connectionData:@"12345" completionHandler:^(id response, NSError *error){ + [expectation fulfill]; + }]; }]; - - //Now we need to send it the data it expects - NSOutputStream* dataStream = [[NSOutputStream alloc] initToMemory]; - [[[mock stub] andReturn: dataStream] outputStream]; - - onGotResponse(mock, nil); - - [dataStream.delegate stream:dataStream handleEvent:NSStreamEventOpenCompleted]; - - NSString* responseStr = @"data: {}\n"; - NSData* data = [responseStr dataUsingEncoding:NSUTF8StringEncoding]; - id streamChanges = [OCMockObject niceMockForClass: [NSStream class]]; - [[[streamChanges stub] andReturn:data] propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; - [dataStream.delegate stream:streamChanges handleEvent:NSStreamEventHasSpaceAvailable]; - - + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error) { NSLog(@"Timeout Error: %@", error); @@ -226,59 +183,22 @@ - (void)testStartCallsTheCompletionHandlerAfterSuccess { - (void)testParsesInitialBuffer { XCTestExpectation *expectation = [self expectationWithDescription:@"Handler called"]; - __block void (^onGotResponse)(AFHTTPRequestOperation *operation, NSHTTPURLResponse *response); - __block void (^onFailure)(NSError*); + SSE_NetworkMock *NetworkMock = [[SSE_NetworkMock alloc] init]; - id mock = [OCMockObject niceMockForClass:[SRHTTPRequestOperation class]]; - [[[mock stub] andDo:^(NSInvocation *invocation) { - __unsafe_unretained void (^successCallback)(AFHTTPRequestOperation *, NSHTTPURLResponse *) = nil; - [invocation getArgument: &successCallback atIndex: 2]; - onGotResponse = successCallback; - }] setDidReceiveResponseBlock: [OCMArg any]]; - - [[[mock stub] andDo:^(NSInvocation *invocation) { - __unsafe_unretained void (^failureCallback)(NSError *) = nil; - [invocation getArgument: &failureCallback atIndex: 3]; - onFailure = failureCallback; - }] setCompletionBlockWithSuccess: [OCMArg any] failure: [OCMArg any]]; - - - // Here we stub the alloc class method ** - [[[mock stub] andReturn:mock] alloc]; - // And we stub initWithParam: passing the param we will pass to the method to test - [[[mock stub] andReturn:mock] initWithRequest:[OCMArg any]]; - - SRConnection* connection = [SRConnection alloc]; - [connection initWithURLString:@"http://localhost:0000"]; + SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; connection.connectionToken = @"10101010101"; connection.connectionId = @"10101"; [connection changeState:disconnected toState:connected]; - - __weak __typeof(&*mock)weakMock = mock; SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init]; sse.serverSentEventsOperationQueue = nil;//set to nil to get around weird ARC OCMock bugs http://stackoverflow.com/questions/18121902/using-ocmock-on-nsoperation-gives-bad-access - [sse start: connection connectionData:@"12345" completionHandler:^(id response, NSError *error){ - [expectation fulfill]; + [NetworkMock prepareForOpeningResponse:@"data: {}\n" then:^{ + [sse start: connection connectionData:@"12345" completionHandler:^(id response, NSError *error){ + [expectation fulfill]; + }]; }]; - //Now we need to send it the data it expects - NSOutputStream* dataStream = [[NSOutputStream alloc] initToMemory]; - [[[mock stub] andReturn: dataStream] outputStream]; - - onGotResponse(mock, nil); - - - NSString* responseStr = @"data: {}\n"; - NSData* data = [responseStr dataUsingEncoding:NSUTF8StringEncoding]; - - id streamChanges = [OCMockObject niceMockForClass: [NSStream class]]; - [[[streamChanges stub] andReturn:data] propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; - - [dataStream.delegate stream:streamChanges handleEvent:NSStreamEventOpenCompleted]; - - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error) { NSLog(@"Timeout Error: %@", error); @@ -289,35 +209,12 @@ - (void)testParsesInitialBuffer { - (void)testIgnoresInitializedAndEmptyLinesWhenParsingMessages { XCTestExpectation *expectation = [self expectationWithDescription:@"Handler called"]; - __block void (^onGotResponse)(AFHTTPRequestOperation *operation, NSHTTPURLResponse *response); - __block void (^onFailure)(NSError*); - - id mock = [OCMockObject niceMockForClass:[SRHTTPRequestOperation class]]; - [[[mock stub] andDo:^(NSInvocation *invocation) { - __unsafe_unretained void (^successCallback)(AFHTTPRequestOperation *, NSHTTPURLResponse *) = nil; - [invocation getArgument: &successCallback atIndex: 2]; - onGotResponse = successCallback; - }] setDidReceiveResponseBlock: [OCMArg any]]; - - [[[mock stub] andDo:^(NSInvocation *invocation) { - __unsafe_unretained void (^failureCallback)(NSError *) = nil; - [invocation getArgument: &failureCallback atIndex: 3]; - onFailure = failureCallback; - }] setCompletionBlockWithSuccess: [OCMArg any] failure: [OCMArg any]]; + SSE_NetworkMock *NetworkMock = [[SSE_NetworkMock alloc] init]; - - // Here we stub the alloc class method ** - [[[mock stub] andReturn:mock] alloc]; - // And we stub initWithParam: passing the param we will pass to the method to test - [[[mock stub] andReturn:mock] initWithRequest:[OCMArg any]]; - - SRConnection* connection = [SRConnection alloc]; - [connection initWithURLString:@"http://localhost:0000"]; + SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; connection.connectionToken = @"10101010101"; connection.connectionId = @"10101"; [connection changeState:disconnected toState:connected]; - - __weak __typeof(&*mock)weakMock = mock; connection.received = ^(NSDictionary * data){ if ([[data valueForKey:@"M"] isEqualToString:@"message"] @@ -330,23 +227,9 @@ - (void)testIgnoresInitializedAndEmptyLinesWhenParsingMessages { SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init]; sse.serverSentEventsOperationQueue = nil;//set to nil to get around weird ARC OCMock bugs http://stackoverflow.com/questions/18121902/using-ocmock-on-nsoperation-gives-bad-access - [sse start: connection connectionData:@"12345" completionHandler:^(id response, NSError *error){}]; - - //Now we need to send it the data it expects - NSOutputStream* dataStream = [[NSOutputStream alloc] initToMemory]; - [[[mock stub] andReturn: dataStream] outputStream]; - - onGotResponse(mock, nil); - - - NSString* responseStr = @"data: initialized\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}\n\n"; - NSData* data = [responseStr dataUsingEncoding:NSUTF8StringEncoding]; - - id streamChanges = [OCMockObject niceMockForClass: [NSStream class]]; - [[[streamChanges stub] andReturn:data] propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; - - [dataStream.delegate stream:streamChanges handleEvent:NSStreamEventOpenCompleted]; - + [NetworkMock prepareForOpeningResponse:@"data: initialized\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}\n\n" then:^{ + [sse start: connection connectionData:@"12345" completionHandler:^(id response, NSError *error){}]; + }]; [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error) { @@ -358,55 +241,24 @@ - (void)testIgnoresInitializedAndEmptyLinesWhenParsingMessages { - (void)testConnectionInitialFailureUsesCallback { XCTestExpectation *expectation = [self expectationWithDescription:@"Handler called"]; - __block void (^onGotResponse)(AFHTTPRequestOperation *operation, NSHTTPURLResponse *response); - __block void (^onFailure)(AFHTTPRequestOperation *operation, NSError *error); - - id mock = [OCMockObject niceMockForClass:[SRHTTPRequestOperation class]]; - [[[mock stub] andDo:^(NSInvocation *invocation) { - __unsafe_unretained void (^successCallback)(AFHTTPRequestOperation *, NSHTTPURLResponse *) = nil; - [invocation getArgument: &successCallback atIndex: 2]; - onGotResponse = successCallback; - }] setDidReceiveResponseBlock: [OCMArg any]]; - - [[[mock stub] andDo:^(NSInvocation *invocation) { - __unsafe_unretained void (^failureCallback)(AFHTTPRequestOperation *, NSError *) = nil; - [invocation getArgument: &failureCallback atIndex: 3]; - onFailure = failureCallback; - }] setCompletionBlockWithSuccess: [OCMArg any] failure: [OCMArg any]]; - - - // Here we stub the alloc class method ** - [[[mock stub] andReturn:mock] alloc]; - // And we stub initWithParam: passing the param we will pass to the method to test - [[[mock stub] andReturn:mock] initWithRequest:[OCMArg any]]; - - SRConnection* connection = [SRConnection alloc]; - [connection initWithURLString:@"http://localhost:0000"]; + SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; connection.connectionToken = @"10101010101"; connection.connectionId = @"10101"; [connection changeState:disconnected toState:connected]; - - __weak __typeof(&*mock)weakMock = mock; SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init]; sse.serverSentEventsOperationQueue = nil;//set to nil to get around weird ARC OCMock bugs http://stackoverflow.com/questions/18121902/using-ocmock-on-nsoperation-gives-bad-access + [SRMockNetwork mockHttpRequestOperationForClass:[AFHTTPRequestOperation class] + statusCode:@400 + error:[[NSError alloc] initWithDomain:@"EXPECTED" code:42 userInfo:nil]]; + [sse start: connection connectionData:@"12345" completionHandler:^(id response, NSError *error){ - if (error) { + if(error) { [expectation fulfill]; } }]; - //Now we need to send it the data it expects - NSOutputStream* dataStream = [[NSOutputStream alloc] initToMemory]; - [[[mock stub] andReturn: dataStream] outputStream]; - - //set up test to verify we do not retry connection after failure - id failmock = [OCMockObject mockForClass:[SRHTTPRequestOperation class]]; - [[failmock reject] alloc]; - - onFailure(mock, [[NSError alloc] initWithDomain:@"EXPECTED" code:42 userInfo:nil]); - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error) { NSLog(@"Timeout Error: %@", error); @@ -416,11 +268,11 @@ - (void)testConnectionInitialFailureUsesCallback { - (void)testConnectionErrorRetries__RetriesAfterADelay__CommunicatesLifeCycleViaConnection { XCTestExpectation *initialized = [self expectationWithDescription:@"initialized"]; - SSE_NetworkMock* NetConnect = [[SSE_NetworkMock alloc] init]; + SSE_NetworkMock *NetworkMock = [[SSE_NetworkMock alloc] init]; SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; __weak __typeof(&*connection)weakConnection = connection; __weak __typeof(&*self)weakSelf = self; - __weak __typeof(&*NetConnect)weakNetConnect = NetConnect; + __weak __typeof(&*NetworkMock)weakNetConnect = NetworkMock; connection.connectionToken = @"10101010101"; connection.connectionId = @"10101"; connection.disconnectTimeout = @30; @@ -430,14 +282,12 @@ - (void)testConnectionErrorRetries__RetriesAfterADelay__CommunicatesLifeCycleVia SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init]; sse.serverSentEventsOperationQueue = nil;//set to nil to get around weird ARC OCMock bugs http://stackoverflow.com/questions/18121902/using-ocmock-on-nsoperation-gives-bad-access - [sse start: connection connectionData:@"12345" completionHandler:^(id response, NSError *error){ - [initialized fulfill]; + [NetworkMock prepareForOpeningResponse:@"data: initialized\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}\n\n" then:^{ + [sse start: connection connectionData:@"12345" completionHandler:^(id response, NSError *error){ + [initialized fulfill]; + }]; }]; - //gets the response and pulls down data successfully at start - [NetConnect openingResponse: @"data: initialized\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}\n\n"]; - - //have to pump messages to continue [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error) { NSLog(@"Timeout Error: %@", error); @@ -448,13 +298,13 @@ - (void)testConnectionErrorRetries__RetriesAfterADelay__CommunicatesLifeCycleVia __strong __typeof(&*weakNetConnect)strongNetConnect = weakNetConnect; //spoiler: we expect this to reconnect and for connection to communicate that out - XCTestExpectation *expectation = [strongSelf expectationWithDescription:@"Retrying callback called"]; - XCTestExpectation *expectation2 = [strongSelf expectationWithDescription:@"Retry callback called"]; + XCTestExpectation *reconnecting = [strongSelf expectationWithDescription:@"reconnecting"]; + XCTestExpectation *reconnected = [strongSelf expectationWithDescription:@"reconnected"]; strongConnection.reconnecting = ^(){ - [expectation fulfill]; + [reconnecting fulfill]; }; strongConnection.reconnected = ^(){ - [expectation2 fulfill]; + [reconnected fulfill]; }; //do setup to simulate and verify the reconnect delay @@ -480,7 +330,7 @@ - (void)testConnectionErrorRetries__RetriesAfterADelay__CommunicatesLifeCycleVia SSE_NetworkMock* NetReconnect = [[SSE_NetworkMock alloc]init]; reconnectAfterTimeoutCallback();//simulating retry timeout - [NetReconnect openingResponse:nil]; + [NetReconnect prepareForOpeningResponse:nil]; //todo: verify request url was reconnect not connect, but then you have to sovle ARC [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error){ @@ -494,11 +344,11 @@ - (void)testConnectionErrorRetries__RetriesAfterADelay__CommunicatesLifeCycleVia - (void)testLostConnectionAbortsAllConnectionsAndReconnects { // happens when healthy connection misses too many heartbeats XCTestExpectation *initialized = [self expectationWithDescription:@"initialized"]; - SSE_NetworkMock* NetConnect = [[SSE_NetworkMock alloc] init]; + SSE_NetworkMock* NetworkMock = [[SSE_NetworkMock alloc] init]; SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; __weak __typeof(&*self)weakSelf = self; __weak __typeof(&*connection)weakConnection = connection; - __weak __typeof(&*NetConnect)weakNetConnect = NetConnect; + __weak __typeof(&*NetworkMock)weakNetConnect = NetworkMock; connection.connectionToken = @"10101010101"; connection.connectionId = @"10101"; connection.disconnectTimeout = @30; @@ -509,12 +359,13 @@ - (void)testLostConnectionAbortsAllConnectionsAndReconnects { id queueMock = [OCMockObject niceMockForClass:[NSOperationQueue class]]; [[queueMock expect] cancelAllOperations]; sse.serverSentEventsOperationQueue = queueMock; - [sse start: connection connectionData:@"12345" completionHandler:^(id response, NSError *error){ - [initialized fulfill]; + + [NetworkMock prepareForOpeningResponse:@"data: initialized\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}\n\n" then:^{ + [sse start: connection connectionData:@"12345" completionHandler:^(id response, NSError *error){ + [initialized fulfill]; + }]; }]; - //gets the response and pulls down data successfully at start - [NetConnect openingResponse: @"data: initialized\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}\n\n"]; - + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error) { NSLog(@"Timeout Error: %@", error); return; @@ -550,7 +401,7 @@ - (void)testLostConnectionAbortsAllConnectionsAndReconnects { reconnectDelay.afterWait(); XCTAssertEqual(2, reconnectDelay.waitTime, "Unexpected reconnect delay"); - [NetReconnect openingResponse:nil]; + [NetReconnect prepareForOpeningResponse:nil]; [strongSelf waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error){ NSLog(@"Sub-Timeout Error: %@", error); @@ -574,30 +425,22 @@ - (void)testDisconnectsOnReconnectTimeout { __block SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init]; sse.serverSentEventsOperationQueue = nil;//set to nil to avoid ARC error http://stackoverflow.com/questions/18121902/using-ocmock-on-nsoperation-gives-bad-access - id pmock = [OCMockObject partialMockForObject: sse]; - [[[pmock stub] andDo:^(NSInvocation *invocation) { - void (^ callbackOut)(SRNegotiationResponse * response, NSError *error); - __unsafe_unretained void (^successCallback)(SRNegotiationResponse *response, NSError *error) = nil; - [invocation getArgument: &successCallback atIndex: 4]; - callbackOut = successCallback; - callbackOut([[SRNegotiationResponse alloc ]initWithDictionary:@{ - @"ConnectionId": @"10101", - @"ConnectionToken": @"10101010101", - @"DisconnectTimeout": @30, - @"ProtocolVersion": @"1.3.0.0", - @"TransportConnectTimeout": @10 - }], nil); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; - + id json = @{ + @"ConnectionId": @"10101", + @"ConnectionToken": @"10101010101", + @"DisconnectTimeout": @30, + @"ProtocolVersion": @"1.3.0.0", + @"TransportConnectTimeout": @10 + }; + [SRMockNegotiate mockNegotiateForTransport:sse statusCode:@200 json:json]; connection.started = ^{ [initialized fulfill]; }; - [connection start:sse]; - - //gets the response and pulls down data successfully at start - [NetConnect openingResponse: @"data: initialized\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}\n\n"]; + [NetConnect prepareForOpeningResponse:@"data: initialized\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}\n\n" then:^{ + [connection start:sse]; + }]; [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error) { @@ -664,36 +507,12 @@ - (void)xtestHandlesDisconnectMessageFromConnection { - (void)testHandlesExtraEmptyLinesWhenParsingMessages { XCTestExpectation *expectation = [self expectationWithDescription:@"Handler called"]; - __block void (^onGotResponse)(AFHTTPRequestOperation *operation, NSHTTPURLResponse *response); - __block void (^onFailure)(NSError*); - - id mock = [OCMockObject niceMockForClass:[SRHTTPRequestOperation class]]; - [[[mock stub] andDo:^(NSInvocation *invocation) { - __unsafe_unretained void (^successCallback)(AFHTTPRequestOperation *, NSHTTPURLResponse *) = nil; - [invocation getArgument: &successCallback atIndex: 2]; - onGotResponse = successCallback; - }] setDidReceiveResponseBlock: [OCMArg any]]; - - [[[mock stub] andDo:^(NSInvocation *invocation) { - __unsafe_unretained void (^failureCallback)(NSError *) = nil; - [invocation getArgument: &failureCallback atIndex: 3]; - onFailure = failureCallback; - }] setCompletionBlockWithSuccess: [OCMArg any] failure: [OCMArg any]]; - - - // Here we stub the alloc class method ** - [[[mock stub] andReturn:mock] alloc]; - // And we stub initWithParam: passing the param we will pass to the method to test - [[[mock stub] andReturn:mock] initWithRequest:[OCMArg any]]; - - SRConnection* connection = [SRConnection alloc]; - [connection initWithURLString:@"http://localhost:0000"]; + SSE_NetworkMock * NetworkMock = [[SSE_NetworkMock alloc] init]; + SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; connection.connectionToken = @"10101010101"; connection.connectionId = @"10101"; [connection changeState:disconnected toState:connected]; - __weak __typeof(&*mock)weakMock = mock; - connection.received = ^(NSString * data){ if (data) { [expectation fulfill]; @@ -703,23 +522,9 @@ - (void)testHandlesExtraEmptyLinesWhenParsingMessages { SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init]; sse.serverSentEventsOperationQueue = nil;//set to nil to get around weird ARC OCMock bugs http://stackoverflow.com/questions/18121902/using-ocmock-on-nsoperation-gives-bad-access - [sse start: connection connectionData:@"12345" completionHandler:^(id response, NSError *error){}]; - - //Now we need to send it the data it expects - NSOutputStream* dataStream = [[NSOutputStream alloc] initToMemory]; - [[[mock stub] andReturn: dataStream] outputStream]; - - onGotResponse(mock, nil); - - - NSString* responseStr = @"data: initialized\n\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}\n\n"; - NSData* data = [responseStr dataUsingEncoding:NSUTF8StringEncoding]; - - id streamChanges = [OCMockObject niceMockForClass: [NSStream class]]; - [[[streamChanges stub] andReturn:data] propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; - - [dataStream.delegate stream:streamChanges handleEvent:NSStreamEventOpenCompleted]; - + [NetworkMock prepareForOpeningResponse:@"data: initialized\n\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}\n\n" then:^{ + [sse start: connection connectionData:@"12345" completionHandler:^(id response, NSError *error){}]; + }]; [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error) { @@ -731,35 +536,11 @@ - (void)testHandlesExtraEmptyLinesWhenParsingMessages { - (void)testHandlesNewLinesSpreadOutOverReads { XCTestExpectation *expectation = [self expectationWithDescription:@"Handler called"]; - __block void (^onGotResponse)(AFHTTPRequestOperation *operation, NSHTTPURLResponse *response); - __block void (^onFailure)(NSError*); - - id mock = [OCMockObject niceMockForClass:[SRHTTPRequestOperation class]]; - [[[mock stub] andDo:^(NSInvocation *invocation) { - __unsafe_unretained void (^successCallback)(AFHTTPRequestOperation *, NSHTTPURLResponse *) = nil; - [invocation getArgument: &successCallback atIndex: 2]; - onGotResponse = successCallback; - }] setDidReceiveResponseBlock: [OCMArg any]]; - - [[[mock stub] andDo:^(NSInvocation *invocation) { - __unsafe_unretained void (^failureCallback)(NSError *) = nil; - [invocation getArgument: &failureCallback atIndex: 3]; - onFailure = failureCallback; - }] setCompletionBlockWithSuccess: [OCMArg any] failure: [OCMArg any]]; - - - // Here we stub the alloc class method ** - [[[mock stub] andReturn:mock] alloc]; - // And we stub initWithParam: passing the param we will pass to the method to test - [[[mock stub] andReturn:mock] initWithRequest:[OCMArg any]]; - - SRConnection* connection = [SRConnection alloc]; - [connection initWithURLString:@"http://localhost:0000"]; + SSE_NetworkMock * NetworkMock = [[SSE_NetworkMock alloc] init]; + SRConnection* connection = [[SRConnection alloc]initWithURLString:@"http://localhost:0000"]; connection.connectionToken = @"10101010101"; connection.connectionId = @"10101"; [connection changeState:disconnected toState:connected]; - - __weak __typeof(&*mock)weakMock = mock; connection.received = ^(NSDictionary * data){ if ([[data valueForKey:@"M"] isEqualToString:@"message"] @@ -769,38 +550,15 @@ - (void)testHandlesNewLinesSpreadOutOverReads { } }; - SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init]; sse.serverSentEventsOperationQueue = nil;//set to nil to get around weird ARC OCMock bugs http://stackoverflow.com/questions/18121902/using-ocmock-on-nsoperation-gives-bad-access - [sse start: connection connectionData:@"12345" completionHandler:^(id response, NSError *error){}]; - - //Now we need to send it the data it expects - NSOutputStream* dataStream = [[NSOutputStream alloc] initToMemory]; - [[[mock stub] andReturn: dataStream] outputStream]; - - onGotResponse(mock, nil); - - - NSString* responseStr = @"data: initialized\n\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}"; - NSMutableData* data1 = [ responseStr dataUsingEncoding:NSUTF8StringEncoding]; - - id streamChanges = [OCMockObject niceMockForClass: [NSStream class]]; - [[[streamChanges stub] andReturn:data1] propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; - - [dataStream.delegate stream:streamChanges handleEvent:NSStreamEventOpenCompleted]; - - //currently, SSE uses the same stream over and over and expects that same stream - //to have all previous bytes in it. That said bc this is async, to actually test - //the differences, we will create a separate stream - NSMutableData* data2 = [[NSMutableData alloc] initWithData: data1]; - NSString* responseStrEnd = @"\n"; - [data2 appendData:[responseStrEnd dataUsingEncoding:NSUTF8StringEncoding]]; - id moreStreamChanges = [OCMockObject niceMockForClass: [NSStream class]]; - [[[moreStreamChanges stub] andReturn:data2] propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; - - [dataStream.delegate stream:moreStreamChanges handleEvent:NSStreamEventHasSpaceAvailable]; + [NetworkMock prepareForOpeningResponse:@"data: initialized\n\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}" then:^{ + [sse start: connection connectionData:@"12345" completionHandler:^(id response, NSError *error){}]; + }]; + [NetworkMock prepareForNextResponse:@"\n" then:nil]; + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error) { NSLog(@"Timeout Error: %@", error); @@ -815,21 +573,14 @@ - (void)testTransportCanTimeoutWhenItDoesNotReceiveInitializeMessage { SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init]; sse.serverSentEventsOperationQueue = nil;//set to nil to avoid ARC error http://stackoverflow.com/questions/18121902/using-ocmock-on-nsoperation-gives-bad-access - id pmock = [OCMockObject partialMockForObject: sse]; - [[[pmock stub] andDo:^(NSInvocation *invocation) { - void (^callbackOut)(SRNegotiationResponse * response, NSError *error); - __unsafe_unretained void (^successCallback)(SRNegotiationResponse *, NSError *) = nil; - [invocation getArgument: &successCallback atIndex: 4]; - callbackOut = successCallback; - callbackOut([[SRNegotiationResponse alloc ]initWithDictionary:@{ - @"ConnectionId": @"10101", - @"ConnectionToken": @"10101010101", - @"DisconnectTimeout": @30, - @"ProtocolVersion": @"1.3.0.0", - @"TransportConnectTimeout": @10 - }], nil); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; - + id json = @{ + @"ConnectionId": @"10101", + @"ConnectionToken": @"10101010101", + @"DisconnectTimeout": @30, + @"ProtocolVersion": @"1.3.0.0", + @"TransportConnectTimeout": @10 + }; + [SRMockNegotiate mockNegotiateForTransport:sse statusCode:@200 json:json]; connection.started = ^{ XCTAssert(NO, @"Connection started"); @@ -856,28 +607,22 @@ - (void)testTransportCanTimeoutWhenItDoesNotReceiveInitializeMessage { - (void)testStart_Stop_StartTriggersTheCorrectCallbacks { XCTestExpectation *initialized = [self expectationWithDescription:@"initialized"]; - SSE_NetworkMock* NetConnect = [[SSE_NetworkMock alloc] init]; + SSE_NetworkMock* NetworkMock = [[SSE_NetworkMock alloc] init]; SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; connection.transportConnectTimeout =@10; SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init]; sse.serverSentEventsOperationQueue = nil;//set to nil to avoid ARC error http://stackoverflow.com/questions/18121902/using-ocmock-on-nsoperation-gives-bad-access - id pmock = [OCMockObject partialMockForObject: sse]; - [[[pmock stub] andDo:^(NSInvocation *invocation) { - void (^ callbackOut)(SRNegotiationResponse * response, NSError *error); - __unsafe_unretained void (^successCallback)(SRNegotiationResponse *, NSError *) = nil; - [invocation getArgument: &successCallback atIndex: 4]; - callbackOut = successCallback; - callbackOut([[SRNegotiationResponse alloc ]initWithDictionary:@{ - @"ConnectionId": @"10101", - @"ConnectionToken": @"10101010101", - @"DisconnectTimeout": @30, - @"ProtocolVersion": @"1.3.0.0", - @"TransportConnectTimeout": @10 - }], nil); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; - + id json = @{ + @"ConnectionId": @"10101", + @"ConnectionToken": @"10101010101", + @"DisconnectTimeout": @30, + @"ProtocolVersion": @"1.3.0.0", + @"TransportConnectTimeout": @10 + }; + [SRMockNegotiate mockNegotiateForTransport:sse statusCode:@200 json:json]; + __block BOOL firstClosedCalled = NO; __block int startCount = 0; @@ -897,9 +642,11 @@ - (void)testStart_Stop_StartTriggersTheCorrectCallbacks { [connection start:sse]; [connection stop]; - [connection start:sse]; - [NetConnect openingResponse: @"data: initialized\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}\n\n"]; + [NetworkMock prepareForOpeningResponse:@"data: initialized\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}\n\n" then:^{ + [connection start:sse]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error) { NSLog(@"Timeout Error: %@", error); @@ -916,29 +663,25 @@ - (void)xtestPingIntervalStopsTheConnectionOn401s { SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init]; sse.serverSentEventsOperationQueue = nil;//set to nil to avoid ARC error http://stackoverflow.com/questions/18121902/using-ocmock-on-nsoperation-gives-bad-access - id pmock = [OCMockObject partialMockForObject: sse]; - [[[pmock stub] andDo:^(NSInvocation *invocation) { - void (^ callbackOut)(SRNegotiationResponse * response, NSError *error); - __unsafe_unretained void (^successCallback)(SRNegotiationResponse *, NSError *) = nil; - [invocation getArgument: &successCallback atIndex: 4]; - callbackOut = successCallback; - callbackOut([[SRNegotiationResponse alloc ]initWithDictionary:@{ - @"ConnectionId": @"10101", - @"ConnectionToken": @"10101010101", - @"DisconnectTimeout": @30, - @"ProtocolVersion": @"1.3.0.0", - @"TransportConnectTimeout": @10 - }], nil); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; + id json = @{ + @"ConnectionId": @"10101", + @"ConnectionToken": @"10101010101", + @"DisconnectTimeout": @30, + @"ProtocolVersion": @"1.3.0.0", + @"TransportConnectTimeout": @10 + }; + [SRMockNegotiate mockNegotiateForTransport:sse statusCode:@200 json:json]; connection.error = ^(NSError *error){ [initialized fulfill]; XCTAssert(NO, @"todo: verify it's a 401"); }; - [connection start:sse]; - [NetConnect openingResponse: @"data: initialized\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}\n\n"]; + [NetConnect prepareForOpeningResponse: @"data: initialized\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}\n\n" then:^{ + [connection start:sse]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error) { NSLog(@"Timeout Error: %@", error); @@ -954,29 +697,25 @@ - (void)xtestPingIntervalStopsTheConnectionOn403s { SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init]; sse.serverSentEventsOperationQueue = nil;//set to nil to avoid ARC error http://stackoverflow.com/questions/18121902/using-ocmock-on-nsoperation-gives-bad-access - id pmock = [OCMockObject partialMockForObject: sse]; - [[[pmock stub] andDo:^(NSInvocation *invocation) { - void (^ callbackOut)(SRNegotiationResponse * response, NSError *error); - __unsafe_unretained void (^successCallback)(SRNegotiationResponse *, NSError *) = nil; - [invocation getArgument: &successCallback atIndex: 4]; - callbackOut = successCallback; - callbackOut([[SRNegotiationResponse alloc ]initWithDictionary:@{ - @"ConnectionId": @"10101", - @"ConnectionToken": @"10101010101", - @"DisconnectTimeout": @30, - @"ProtocolVersion": @"1.3.0.0", - @"TransportConnectTimeout": @10 - }], nil); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; + id json = @{ + @"ConnectionId": @"10101", + @"ConnectionToken": @"10101010101", + @"DisconnectTimeout": @30, + @"ProtocolVersion": @"1.3.0.0", + @"TransportConnectTimeout": @10 + }; + [SRMockNegotiate mockNegotiateForTransport:sse statusCode:@200 json:json]; connection.error = ^(NSError *error){ [initialized fulfill]; XCTAssert(NO, @"todo: verify it's a 403"); }; - [connection start:sse]; - [NetConnect openingResponse: @"data: initialized\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}\n\n"]; + [NetConnect prepareForOpeningResponse:@"data: initialized\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}\n\n" then:^{ + [connection start:sse]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error) { NSLog(@"Timeout Error: %@", error); @@ -996,48 +735,37 @@ - (void)xtestConnectionDataFlowsWithAllRequestsToServer { - (void)testReconnectExceedingTheReconnectWindowResultsInTheConnectionDisconnect { XCTestExpectation *initialized = [self expectationWithDescription:@"initialized"]; - SSE_NetworkMock* NetConnect = [[SSE_NetworkMock alloc] init]; + SSE_NetworkMock* NetworkMock = [[SSE_NetworkMock alloc] init]; SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; __weak __typeof(&*self)weakSelf = self; __weak __typeof(&*connection)weakConnection = connection; - __weak __typeof(&*NetConnect)weakNetConnect = NetConnect; __block SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init]; sse.serverSentEventsOperationQueue = nil;//set to nil to avoid ARC error http://stackoverflow.com/questions/18121902/using-ocmock-on-nsoperation-gives-bad-access - id pmock = [OCMockObject partialMockForObject: sse]; - [[[pmock stub] andDo:^(NSInvocation *invocation) { - void (^ callbackOut)(SRNegotiationResponse * response, NSError *error); - __unsafe_unretained void (^successCallback)(SRNegotiationResponse *, NSError *) = nil; - [invocation getArgument: &successCallback atIndex: 4]; - callbackOut = successCallback; - callbackOut([[SRNegotiationResponse alloc ] - initWithDictionary:@{ - @"ConnectionId": @"10101", - @"ConnectionToken": @"10101010101", - @"DisconnectTimeout": @30, - @"ProtocolVersion": @"1.3.0.0", - @"TransportConnectTimeout": @10 - }], nil); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; - + id json = @{ + @"ConnectionId": @"10101", + @"ConnectionToken": @"10101010101", + @"DisconnectTimeout": @30, + @"ProtocolVersion": @"1.3.0.0", + @"TransportConnectTimeout": @10 + }; + [SRMockNegotiate mockNegotiateForTransport:sse statusCode:@200 json:json]; connection.started = ^{ [initialized fulfill]; }; - [connection start:sse]; - - //gets the response and pulls down data successfully at start - [NetConnect openingResponse: @"data: initialized\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}\n\n"]; - + [NetworkMock prepareForOpeningResponse:@"data: initialized\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}\n\n" then:^{ + [connection start:sse]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error) { NSLog(@"Timeout Error: %@", error); return; } __strong __typeof(&*weakSelf)strongSelf = weakSelf; __strong __typeof(&*weakConnection)strongConnection = weakConnection; - __strong __typeof(&*weakNetConnect)strongNetConnect = weakNetConnect; //trigger an error to see //spoiler: we expect this to fail @@ -1054,7 +782,7 @@ - (void)testReconnectExceedingTheReconnectWindowResultsInTheConnectionDisconnect //do setup to simulate and verify the reconnect delay SSE_WaitBlock* reconnectBlock = [[SSE_WaitBlock alloc]init:[[sse reconnectDelay] doubleValue]]; NSError *cancelledError = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:nil]; - strongNetConnect.onFailure(strongNetConnect.mock, cancelledError); + NetworkMock.onFailure(NetworkMock.mock, cancelledError); [reconnectBlock.mock stopMocking];//dont want to accidentally get other blocks //we will be calling open again, so lets recapture the data to verify @@ -1080,31 +808,20 @@ - (void)testReconnectExceedingTheReconnectWindowResultsInTheConnectionDisconnect - (void)testConnectionCanBeStoppedDuringTransportStart { XCTestExpectation *initialized = [self expectationWithDescription:@"initialized"]; - SSE_NetworkMock* NetConnect = [[SSE_NetworkMock alloc] init]; + SSE_NetworkMock* NetworkMock = [[SSE_NetworkMock alloc] init]; SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; - __weak __typeof(&*self)weakSelf = self; - __weak __typeof(&*connection)weakConnection = connection; - __weak __typeof(&*NetConnect)weakNetConnect = NetConnect; - __block SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init]; + SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init]; sse.serverSentEventsOperationQueue = nil;//set to nil to avoid ARC error http://stackoverflow.com/questions/18121902/using-ocmock-on-nsoperation-gives-bad-access - id pmock = [OCMockObject partialMockForObject: sse]; - [[[pmock stub] andDo:^(NSInvocation *invocation) { - void (^ callbackOut)(SRNegotiationResponse * response, NSError *error); - __unsafe_unretained void (^successCallback)(SRNegotiationResponse *, NSError *) = nil; - [invocation getArgument: &successCallback atIndex: 4]; - callbackOut = successCallback; - callbackOut([[SRNegotiationResponse alloc ] - initWithDictionary:@{ - @"ConnectionId": @"10101", - @"ConnectionToken": @"10101010101", - @"DisconnectTimeout": @30, - @"ProtocolVersion": @"1.3.0.0", - @"TransportConnectTimeout": @10 - }], nil); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; - + id json = @{ + @"ConnectionId": @"10101", + @"ConnectionToken": @"10101010101", + @"DisconnectTimeout": @30, + @"ProtocolVersion": @"1.3.0.0", + @"TransportConnectTimeout": @10 + }; + [SRMockNegotiate mockNegotiateForTransport:sse statusCode:@200 json:json]; connection.closed = ^{ [initialized fulfill]; @@ -1118,31 +835,25 @@ - (void)testConnectionCanBeStoppedDuringTransportStart { XCTAssert(NO, @"start was triggered"); }; - [connection start:sse]; - - //gets the response but has not completed intialize - [NetConnect openingResponse: @""]; + [NetworkMock prepareForOpeningResponse:^{ + [connection start:sse]; + }]; [connection stop]; [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error) { NSLog(@"Timeout Error: %@", error); return; } - __strong __typeof(&*weakConnection)strongConnection = weakConnection; - XCTAssertEqual(strongConnection.state, disconnected, @"Connection was not disconnected"); - XCTAssertEqual(strongConnection.transport, nil, @"Transport was not cleared after stop"); + XCTAssertEqual(connection.state, disconnected, @"Connection was not disconnected"); + XCTAssertEqual(connection.transport, nil, @"Transport was not cleared after stop"); }]; } - (void)testConnectionCanBeStoppedPriorToTransportStart { XCTestExpectation *initialized = [self expectationWithDescription:@"initialized"]; - SSE_NetworkMock* NetConnect = [[SSE_NetworkMock alloc] init]; SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; - __weak __typeof(&*self)weakSelf = self; - __weak __typeof(&*connection)weakConnection = connection; - __weak __typeof(&*NetConnect)weakNetConnect = NetConnect; - __block SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init]; + SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init]; sse.serverSentEventsOperationQueue = nil;//set to nil to avoid ARC error http://stackoverflow.com/questions/18121902/using-ocmock-on-nsoperation-gives-bad-access id pmock = [OCMockObject partialMockForObject: sse]; @@ -1172,19 +883,17 @@ - (void)testConnectionCanBeStoppedPriorToTransportStart { if (error) { NSLog(@"Timeout Error: %@", error); return; } - __strong __typeof(&*weakConnection)strongConnection = weakConnection; - XCTAssertEqual(strongConnection.state, disconnected, @"Connection was not disconnected"); - XCTAssertEqual(strongConnection.transport, nil, @"Transport was not cleared after stop"); + XCTAssertEqual(connection.state, disconnected, @"Connection was not disconnected"); + XCTAssertEqual(connection.transport, nil, @"Transport was not cleared after stop"); }]; } - (void)testTransportCanSendAndReceiveMessagesOnConnect { XCTestExpectation *initialized = [self expectationWithDescription:@"initialized"]; - SSE_NetworkMock* NetConnect = [[SSE_NetworkMock alloc] init]; + SSE_NetworkMock* NetworkMock = [[SSE_NetworkMock alloc] init]; SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; - __weak __typeof(&*self)weakSelf = self; __weak __typeof(&*connection)weakConnection = connection; - __weak __typeof(&*NetConnect)weakNetConnect = NetConnect; + __weak __typeof(&*NetworkMock)weakNetworkMock = NetworkMock; __block SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init]; sse.serverSentEventsOperationQueue = nil;//set to nil to avoid ARC error @@ -1214,8 +923,8 @@ - (void)testTransportCanSendAndReceiveMessagesOnConnect { __strong __typeof(&*weakConnection)strongConnection = weakConnection; [strongConnection send:@"test" completionHandler:^(id response, NSError *error) { //after sending receive two more - __strong __typeof(&*weakNetConnect)strongNetConnect = weakNetConnect; - [strongNetConnect message:@"data: {\"M\":[{\"H\":\"hubname\", \"M\":\"message3\", \"A\": \"12345\"}]}\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message4\", \"A\": \"12345\"}]}\n\n"]; + __strong __typeof(&*weakNetworkMock)strongNetworkMock = weakNetworkMock; + [strongNetworkMock prepareForNextResponse:@"data: {\"M\":[{\"H\":\"hubname\", \"M\":\"message3\", \"A\": \"12345\"}]}\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message4\", \"A\": \"12345\"}]}\n\n" then:nil]; }]; }; @@ -1231,9 +940,10 @@ - (void)testTransportCanSendAndReceiveMessagesOnConnect { } }; - [connection start:sse]; - - [NetConnect openingResponse: @"data: initialized\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message1\", \"A\": \"12345\"}]}\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message2\", \"A\": \"12345\"}]}\n\n"]; + [NetworkMock prepareForOpeningResponse:@"data: initialized\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message1\", \"A\": \"12345\"}]}\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message2\", \"A\": \"12345\"}]}\n\n" then:^{ + [connection start:sse]; + + }]; [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error) { diff --git a/SignalR.Client.ObjC/SignalR.Samples.OSX/SignalR.Samples.OSX-Info.plist b/SignalR.Client.ObjC/SignalR.Samples.OSX/SignalR.Samples.OSX-Info.plist index aefde366..47c86eea 100644 --- a/SignalR.Client.ObjC/SignalR.Samples.OSX/SignalR.Samples.OSX-Info.plist +++ b/SignalR.Client.ObjC/SignalR.Samples.OSX/SignalR.Samples.OSX-Info.plist @@ -9,7 +9,7 @@ CFBundleIconFile icon.icns CFBundleIdentifier - com.SignalR.${PRODUCT_NAME:rfc1034identifier} + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/SignalR.Client.ObjC/SignalR.Samples.iOS/SignalR.Samples.iOS-Info.plist b/SignalR.Client.ObjC/SignalR.Samples.iOS/SignalR.Samples.iOS-Info.plist index 2561ef50..f005ea46 100644 --- a/SignalR.Client.ObjC/SignalR.Samples.iOS/SignalR.Samples.iOS-Info.plist +++ b/SignalR.Client.ObjC/SignalR.Samples.iOS/SignalR.Samples.iOS-Info.plist @@ -19,7 +19,7 @@ CFBundleIdentifier - com.SignalR.${PRODUCT_NAME:rfc1034identifier} + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/SignalR.Client/Transports/SRServerSentEventsTransport.m b/SignalR.Client/Transports/SRServerSentEventsTransport.m index 58a781d3..4e990da4 100644 --- a/SignalR.Client/Transports/SRServerSentEventsTransport.m +++ b/SignalR.Client/Transports/SRServerSentEventsTransport.m @@ -29,45 +29,15 @@ #import "SRLog.h" #import "SRChunkBuffer.h" #import "SRServerSentEvent.h" - -@interface SRHTTPRequestOperation : AFHTTPRequestOperation - -typedef void (^AFURLConnectionOperationDidReceiveURLResponseBlock)(AFHTTPRequestOperation *operation, NSHTTPURLResponse *response); - -@property (readwrite, nonatomic, strong) NSHTTPURLResponse *response; -@property (readwrite, nonatomic, copy) AFURLConnectionOperationDidReceiveURLResponseBlock urlResponseBlock; - -@end - -@implementation SRHTTPRequestOperation - -@dynamic response; - -- (void)setDidReceiveResponseBlock:(void (^)(AFHTTPRequestOperation *operation, NSHTTPURLResponse *response))block { - self.urlResponseBlock = block; -} - -#pragma mark - -#pragma mark NSURLConnection Delegate - -- (void)connection:(NSURLConnection *)__unused connection -didReceiveResponse:(NSURLResponse *)response { - self.response = (NSHTTPURLResponse *)response; - if (self.urlResponseBlock) { - dispatch_sync(dispatch_get_main_queue(), ^{ - self.urlResponseBlock(self, self.response); - }); - } - [super connection:connection didReceiveResponse:response]; -} - -@end +#import "SREventSourceRequestSerializer.h" +#import "SREventSourceResponseSerializer.h" typedef void (^SRCompletionHandler)(id response, NSError *error); @interface SRServerSentEventsTransport () @property (assign) BOOL stop; +@property (strong, nonatomic, readwrite) SREventSourceStreamReader *eventSource; @property (strong, nonatomic, readwrite) NSOperationQueue *serverSentEventsOperationQueue; @property (copy) SRCompletionHandler completionHandler; @property (strong, nonatomic, readwrite) NSBlockOperation * connectTimeoutOperation; @@ -162,95 +132,89 @@ - (void)open:(id )connection connectionData:(NSString *)c SRLogServerSentEvents(@"SSE: GET %@", url); - NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] requestWithMethod:@"GET" URLString:url parameters:parameters error:nil]; + __weak __typeof(&*self)weakSelf = self; + __weak __typeof(&*connection)weakConnection = connection; + NSMutableURLRequest *request = [[SREventSourceRequestSerializer serializer] requestWithMethod:@"GET" URLString:url parameters:parameters error:nil]; [connection prepareRequest:request]; //TODO: prepareRequest [request setTimeoutInterval:240]; - [request setValue:@"text/event-stream" forHTTPHeaderField:@"Accept"]; [request setValue:@"Keep-Alive" forHTTPHeaderField:@"Connection"]; //TODO: prepareRequest - SRHTTPRequestOperation *operation = [[SRHTTPRequestOperation alloc] initWithRequest:request]; - [operation setResponseSerializer:[AFJSONResponseSerializer serializer]]; + AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; + [operation setResponseSerializer:[SREventSourceResponseSerializer serializer]]; //operation.shouldUseCredentialStorage = self.shouldUseCredentialStorage; //operation.credential = self.credential; //operation.securityPolicy = self.securityPolicy; - [operation setDidReceiveResponseBlock:^(AFHTTPRequestOperation *operation, NSHTTPURLResponse *response) { - eventSource = [[SREventSourceStreamReader alloc] initWithStream:operation.outputStream]; - __weak __typeof(&*self)weakSelf = self; - __weak __typeof(&*connection)weakConnection = connection; + _eventSource = [[SREventSourceStreamReader alloc] initWithStream:operation.outputStream]; + _eventSource.opened = ^() { + __strong __typeof(&*weakConnection)strongConnection = weakConnection; - eventSource.opened = ^() { - __strong __typeof(&*weakConnection)strongConnection = weakConnection; - - // This will noop if we're not in the reconnecting state - if([strongConnection changeState:reconnecting toState:connected]) { - // Raise the reconnect event if the connection comes back up - [strongConnection didReconnect]; + // This will noop if we're not in the reconnecting state + if([strongConnection changeState:reconnecting toState:connected]) { + // Raise the reconnect event if the connection comes back up + [strongConnection didReconnect]; + } + }; + _eventSource.message = ^(SRServerSentEvent * sseEvent) { + __strong __typeof(&*weakSelf)strongSelf = weakSelf; + __strong __typeof(&*weakConnection)strongConnection = weakConnection; + + if([sseEvent.event isEqual:@"data"]) { + NSString *data = [[NSString alloc] initWithData:sseEvent.data encoding:NSUTF8StringEncoding]; + if([data caseInsensitiveCompare:@"initialized"] == NSOrderedSame) { + return; } - }; - eventSource.message = ^(SRServerSentEvent * sseEvent) { - __strong __typeof(&*weakSelf)strongSelf = weakSelf; - __strong __typeof(&*weakConnection)strongConnection = weakConnection; - if([sseEvent.event isEqual:@"data"]) { - NSString *data = [[NSString alloc] initWithData:sseEvent.data encoding:NSUTF8StringEncoding]; - if([data caseInsensitiveCompare:@"initialized"] == NSOrderedSame) { - return; - } + BOOL shouldReconnect = NO; + BOOL disconnect = NO; + [strongSelf processResponse:strongConnection response:data shouldReconnect:&shouldReconnect disconnected:&disconnect]; + if (strongSelf.completionHandler) { + [NSObject cancelPreviousPerformRequestsWithTarget:strongSelf.connectTimeoutOperation + selector:@selector(start) + object:nil]; + strongSelf.connectTimeoutOperation = nil; - BOOL shouldReconnect = NO; - BOOL disconnect = NO; - [strongSelf processResponse:strongConnection response:data shouldReconnect:&shouldReconnect disconnected:&disconnect]; - if (strongSelf.completionHandler) { - [NSObject cancelPreviousPerformRequestsWithTarget:self.connectTimeoutOperation - selector:@selector(start) - object:nil]; - self.connectTimeoutOperation = nil; - - strongSelf.completionHandler(nil, nil); - strongSelf.completionHandler = nil; - } - - if(disconnect) { - SRLogServerSentEvents(@"disconnect received should disconnect"); - _stop = YES; - [strongConnection disconnect]; - } + strongSelf.completionHandler(nil, nil); + strongSelf.completionHandler = nil; } - }; - eventSource.closed = ^(NSError *exception) { //server ended without error - __strong __typeof(&*weakSelf)strongSelf = weakSelf; - __strong __typeof(&*weakConnection)strongConnection = weakConnection; - - SRLogServerSentEvents(@"did close"); - if (exception != nil ){ - // Check if the request is aborted - BOOL isRequestAborted = [SRExceptionHelper isRequestAborted:exception]; - - if (!isRequestAborted) { - // Don't raise exceptions if the request was aborted (connection was stopped). - [strongConnection didReceiveError:exception]; - } + if(disconnect) { + SRLogServerSentEvents(@"disconnect received should disconnect"); + _stop = YES; + [strongConnection disconnect]; } + } + }; + _eventSource.closed = ^(NSError *exception) { //server ended without error + __strong __typeof(&*weakSelf)strongSelf = weakSelf; + __strong __typeof(&*weakConnection)strongConnection = weakConnection; + + SRLogServerSentEvents(@"did close"); + + if (exception != nil ){ + // Check if the request is aborted + BOOL isRequestAborted = [SRExceptionHelper isRequestAborted:exception]; - //release eventSource, no other scopes have access, would like to release before - //eventSource will be nil for this scope before reconnect can call open, even if - //it wasn't doing a timeout first - eventSource = nil; - - if (_stop) { - [strongSelf completeAbort]; - } - else if ([strongSelf tryCompleteAbort]) { + if (!isRequestAborted) { + // Don't raise exceptions if the request was aborted (connection was stopped). + [strongConnection didReceiveError:exception]; } - else { - [strongSelf reconnect:strongConnection data:connectionData]; - } - }; - [eventSource start]; - }]; - - __weak __typeof(&*self)weakSelf = self; + } + + //release eventSource, no other scopes have access, would like to release before + //eventSource will be nil for this scope before reconnect can call open, even if + //it wasn't doing a timeout first + _eventSource = nil; + + if (strongSelf.stop) { + [strongSelf completeAbort]; + } + else if ([strongSelf tryCompleteAbort]) { + } + else { + [strongSelf reconnect:strongConnection data:connectionData]; + } + }; + [_eventSource start]; [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { } failure:^(AFHTTPRequestOperation *operation, NSError *error) { diff --git a/SignalR.Client/Transports/ServerSentEvents/SREventSourceRequestSerializer.h b/SignalR.Client/Transports/ServerSentEvents/SREventSourceRequestSerializer.h new file mode 100755 index 00000000..b0669b91 --- /dev/null +++ b/SignalR.Client/Transports/ServerSentEvents/SREventSourceRequestSerializer.h @@ -0,0 +1,27 @@ +// +// SREventSourceRequestSerializer.h +// SignalR +// +// Created by Alex Billingsley on 2/1/16. +// Copyright (c) 2011 DyKnow LLC. (http://dyknow.com/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and +// to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of +// the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +#import "AFURLRequestSerialization.h" + +@interface SREventSourceRequestSerializer : AFHTTPRequestSerializer + +@end diff --git a/SignalR.Client/Transports/ServerSentEvents/SREventSourceRequestSerializer.m b/SignalR.Client/Transports/ServerSentEvents/SREventSourceRequestSerializer.m new file mode 100755 index 00000000..3739f552 --- /dev/null +++ b/SignalR.Client/Transports/ServerSentEvents/SREventSourceRequestSerializer.m @@ -0,0 +1,38 @@ +// SREventSourceRequestSerializer.m +// SignalR +// +// Created by Alex Billingsley on 2/1/16. +// Copyright (c) 2011 DyKnow LLC. (http://dyknow.com/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and +// to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of +// the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +#import "SREventSourceRequestSerializer.h" + +@implementation SREventSourceRequestSerializer + +#pragma mark - AFURLRequestSerializer + +- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request + withParameters:(NSDictionary *)parameters + error:(NSError *__autoreleasing *)error +{ + NSMutableURLRequest *mutableRequest = [[super requestBySerializingRequest:request withParameters:parameters error:error] mutableCopy]; + [mutableRequest setValue:@"text/event-stream" forHTTPHeaderField:@"Accept"]; + + return mutableRequest; +} + +@end diff --git a/SignalR.Client/Transports/ServerSentEvents/SREventSourceResponseSerializer.h b/SignalR.Client/Transports/ServerSentEvents/SREventSourceResponseSerializer.h new file mode 100755 index 00000000..9a54224a --- /dev/null +++ b/SignalR.Client/Transports/ServerSentEvents/SREventSourceResponseSerializer.h @@ -0,0 +1,27 @@ +// +// SREventSourceResponseSerializer.h +// SignalR +// +// Created by Alex Billingsley on 2/1/16. +// Copyright (c) 2011 DyKnow LLC. (http://dyknow.com/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and +// to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of +// the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +#import "AFURLResponseSerialization.h" + +@interface SREventSourceResponseSerializer : AFHTTPResponseSerializer + +@end diff --git a/SignalR.Client/Transports/ServerSentEvents/SREventSourceResponseSerializer.m b/SignalR.Client/Transports/ServerSentEvents/SREventSourceResponseSerializer.m new file mode 100755 index 00000000..4eb61445 --- /dev/null +++ b/SignalR.Client/Transports/ServerSentEvents/SREventSourceResponseSerializer.m @@ -0,0 +1,73 @@ +// SREventSourceResponseSerializer.m +// +// Copyright (c) 2013 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "SREventSourceResponseSerializer.h" + +@implementation SREventSourceResponseSerializer + ++ (instancetype)serializer { + return [[self alloc] init]; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.acceptableContentTypes = [NSSet setWithObjects:@"text/event-stream", nil]; + + return self; +} + +#pragma mark - AFURLResponseSerialization + +- (id)responseObjectForResponse:(NSURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error +{ + return [super responseObjectForResponse:response data:data error:error]; +} + +#pragma mark - NSSecureCoding + +- (id)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + SREventSourceResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; + return serializer; +} + +@end diff --git a/SignalR.Client/Transports/ServerSentEvents/SRServerSentEvent.h b/SignalR.Client/Transports/ServerSentEvents/SRServerSentEvent.h index 291677e0..290b30d7 100644 --- a/SignalR.Client/Transports/ServerSentEvents/SRServerSentEvent.h +++ b/SignalR.Client/Transports/ServerSentEvents/SRServerSentEvent.h @@ -65,5 +65,5 @@ */ + (instancetype)eventWithFields:(NSDictionary *)fields; -+ (BOOL)tryParseEvent:(NSString *)line sseEvent:(SRServerSentEvent **)sseEvent __attribute__((deprecated)); ++ (BOOL)tryParseEvent:(NSString *)line sseEvent:(SRServerSentEvent **)sseEvent; @end \ No newline at end of file From 95b3b988b27dc1ecf14215bcc6ce01d51d36156a Mon Sep 17 00:00:00 2001 From: Alex Billingsley Date: Mon, 1 Feb 2016 16:43:47 -0500 Subject: [PATCH 2/3] Fixes web sockets unit tests to only include one waitFor --- .../SRWebSocketTransportTests.m | 136 +++++++----------- 1 file changed, 49 insertions(+), 87 deletions(-) diff --git a/SignalR.Client.ObjC/SRWebSocketTransportTests.m b/SignalR.Client.ObjC/SRWebSocketTransportTests.m index 6ccf427e..50c2fd6f 100644 --- a/SignalR.Client.ObjC/SRWebSocketTransportTests.m +++ b/SignalR.Client.ObjC/SRWebSocketTransportTests.m @@ -191,6 +191,16 @@ - (void)testConnectionErrorRetries_RetriesAfterADelay_CommunicatesLifeCycleViaCo connection.connectionId = @"10101"; [connection changeState:disconnected toState:connected]; + XCTestExpectation *reconnecting = [self expectationWithDescription:@"Retrying callback called"]; + connection.reconnecting = ^(){ + [reconnecting fulfill]; + }; + + XCTestExpectation *reconnected = [self expectationWithDescription:@"Retry callback called"]; + connection.reconnected = ^(){ + [reconnected fulfill]; + }; + SRWebSocketTransport* ws = [[ SRWebSocketTransport alloc] init]; [ws start: connection connectionData:@"12345" completionHandler:^(id response, NSError *error){ if (error) { @@ -203,39 +213,16 @@ - (void)testConnectionErrorRetries_RetriesAfterADelay_CommunicatesLifeCycleViaCo [ws webSocketDidOpen: mock]; [ws webSocket:mock didReceiveMessage:@"{\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}"]; - __weak __typeof(&*connection)weakConnection = connection; - __weak __typeof(&*self)weakSelf = self; + WS_WaitBlock *reconnectDelay = [[WS_WaitBlock alloc] init:[ws.reconnectDelay intValue]]; + [ws webSocket:mock didCloseWithCode:0 reason:@"Stream end encountered" wasClean:NO]; + [[reconnectDelay mock] stopMocking]; + reconnectDelay.afterWait(); + [ws webSocketDidOpen:mock]; + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error) { NSLog(@"Timeout Error: %@", error); - } else { - __strong __typeof(&*weakSelf)strongSelf = weakSelf; - __strong __typeof(&*weakConnection)strongConnection = weakConnection; - XCTestExpectation *reconnecting = [strongSelf expectationWithDescription:@"Retrying callback called"]; - strongConnection.reconnecting = ^(){ - [reconnecting fulfill]; - }; - [ws webSocket:mock didCloseWithCode:0 reason:@"Stream end encountered" wasClean:NO]; - - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { - if (error){ - NSLog(@"Sub-Timeout Error: %@", error); - } else { - XCTestExpectation *reconnected = [strongSelf expectationWithDescription:@"Retry callback called"]; - strongConnection.reconnected = ^(){ - [reconnected fulfill]; - }; - [ws webSocketDidOpen:mock]; - - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { - if (error){ - NSLog(@"Sub-Timeout Error: %@", error); - } - }]; - } - }]; } - }]; } @@ -255,6 +242,16 @@ - (void)testConnectionErrorRetries_RetriesAfterADelay_CommunicatesLifeCycleViaCo connection.transportConnectTimeout = @10; [connection changeState:disconnected toState:connected]; + XCTestExpectation *reconnecting = [self expectationWithDescription:@"Retrying callback called"]; + connection.reconnecting = ^(){ + [reconnecting fulfill]; + }; + + XCTestExpectation *reconnected = [self expectationWithDescription:@"Retry callback called"]; + connection.reconnected = ^(){ + [reconnected fulfill]; + }; + SRWebSocketTransport* ws = [[ SRWebSocketTransport alloc] init]; [ws start: connection connectionData:@"12345" completionHandler:^(id response, NSError *error){ if (error) { @@ -267,39 +264,16 @@ - (void)testConnectionErrorRetries_RetriesAfterADelay_CommunicatesLifeCycleViaCo [ws webSocketDidOpen: mock]; [ws webSocket:mock didReceiveMessage:@"{\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}"]; - __weak __typeof(&*connection)weakConnection = connection; - __weak __typeof(&*self)weakSelf = self; + WS_WaitBlock *reconnectDelay = [[WS_WaitBlock alloc] init:[ws.reconnectDelay intValue]]; + [ws webSocket:mock didCloseWithCode:1001 reason:@"Somevalid reason" wasClean:YES]; + [[reconnectDelay mock] stopMocking]; + reconnectDelay.afterWait(); + [ws webSocketDidOpen:mock]; + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error) { NSLog(@"Timeout Error: %@", error); - } else { - __strong __typeof(&*weakSelf)strongSelf = weakSelf; - __strong __typeof(&*weakConnection)strongConnection = weakConnection; - XCTestExpectation *reconnecting = [strongSelf expectationWithDescription:@"Retrying callback called"]; - strongConnection.reconnecting = ^(){ - [reconnecting fulfill]; - }; - [ws webSocket:mock didCloseWithCode:1001 reason:@"Somevalid reason" wasClean:YES]; - - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { - if (error){ - NSLog(@"Sub-Timeout Error: %@", error); - } else { - XCTestExpectation *reconnected = [strongSelf expectationWithDescription:@"Retry callback called"]; - strongConnection.reconnected = ^(){ - [reconnected fulfill]; - }; - [ws webSocketDidOpen:mock]; - - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { - if (error){ - NSLog(@"Sub-Timeout Error: %@", error); - } - }]; - } - }]; - } - + } }]; } @@ -319,6 +293,16 @@ - (void)testLostConnectionAbortsAllConnectionsAndReconnects { connection.connectionId = @"10101"; [connection changeState:disconnected toState:connected]; + XCTestExpectation *reconnecting = [self expectationWithDescription:@"Retrying callback called"]; + connection.reconnecting = ^(){ + [reconnecting fulfill]; + }; + + XCTestExpectation *reconnected = [self expectationWithDescription:@"Retry callback called"]; + connection.reconnected = ^(){ + [reconnected fulfill]; + }; + SRWebSocketTransport* ws = [[ SRWebSocketTransport alloc] init]; [ws start: connection connectionData:@"12345" completionHandler:^(id response, NSError *error){ if (error) { @@ -331,37 +315,15 @@ - (void)testLostConnectionAbortsAllConnectionsAndReconnects { [ws webSocketDidOpen: mock]; [ws webSocket:mock didReceiveMessage:@"{\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}"]; - __weak __typeof(&*connection)weakConnection = connection; - __weak __typeof(&*self)weakSelf = self; + WS_WaitBlock *reconnectDelay = [[WS_WaitBlock alloc] init:[ws.reconnectDelay intValue]]; + [ws lostConnection:connection]; + [[reconnectDelay mock] stopMocking]; + reconnectDelay.afterWait(); + [ws webSocketDidOpen:mock]; + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error) { NSLog(@"Timeout Error: %@", error); - } else { - __strong __typeof(&*weakSelf)strongSelf = weakSelf; - __strong __typeof(&*weakConnection)strongConnection = weakConnection; - XCTestExpectation *reconnecting = [strongSelf expectationWithDescription:@"Retrying callback called"]; - strongConnection.reconnecting = ^(){ - [reconnecting fulfill]; - }; - [ws lostConnection:strongConnection]; - - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { - if (error){ - NSLog(@"Sub-Timeout Error: %@", error); - } else { - XCTestExpectation *reconnected = [strongSelf expectationWithDescription:@"Retry callback called"]; - strongConnection.reconnected = ^(){ - [reconnected fulfill]; - }; - [ws webSocketDidOpen:mock]; - - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { - if (error){ - NSLog(@"Sub-Timeout Error: %@", error); - } - }]; - } - }]; } }]; } From c133638a240363c6497f4d3f807a8b713b058081 Mon Sep 17 00:00:00 2001 From: Alex Billingsley Date: Tue, 2 Feb 2016 08:46:24 -0500 Subject: [PATCH 3/3] Fixes remaining tests and some final cleanup --- .../SRLongPollingTransportTests.m | 45 +- .../SRWebSocketTransportTests.m | 186 +++------ .../project.pbxproj | 16 +- .../SignalR.Client.Tests/SRConnectionTests.m | 73 +--- .../SRMockClientTransport.h | 62 +++ .../SRMockClientTransport.m | 253 ++++++++++++ .../SignalR.Client.Tests/SRMockNegotiate.h | 22 - .../SignalR.Client.Tests/SRMockNegotiate.m | 81 ---- .../SRServerSentEventsTransportTests.m | 384 +++++++----------- .../Transports/SRServerSentEventsTransport.m | 5 +- 10 files changed, 583 insertions(+), 544 deletions(-) create mode 100644 SignalR.Client.ObjC/SignalR.Client.Tests/SRMockClientTransport.h create mode 100644 SignalR.Client.ObjC/SignalR.Client.Tests/SRMockClientTransport.m delete mode 100644 SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNegotiate.h delete mode 100644 SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNegotiate.m diff --git a/SignalR.Client.ObjC/SRLongPollingTransportTests.m b/SignalR.Client.ObjC/SRLongPollingTransportTests.m index 7fbd5169..97056e62 100644 --- a/SignalR.Client.ObjC/SRLongPollingTransportTests.m +++ b/SignalR.Client.ObjC/SRLongPollingTransportTests.m @@ -14,6 +14,7 @@ #import "SRConnectionInterface.h" #import "SRNegotiationResponse.h" #import "SRMockNetwork.h" +#import "SRMockClientTransport.h" @interface SRLongPollingTransport () @property (strong, nonatomic, readwrite) NSOperationQueue *pollingOperationQueue; @@ -108,20 +109,14 @@ - (void) testFailureStopsAndRestartLongPolling { SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; SRLongPollingTransport* lp = [[ SRLongPollingTransport alloc] init]; - id pmock = [OCMockObject partialMockForObject: lp]; - [[[pmock stub] andDo:^(NSInvocation *invocation) { - void (^ callbackOut)(SRNegotiationResponse * response, NSError *error); - __unsafe_unretained void (^successCallback)(SRNegotiationResponse *response, NSError *error) = nil; - [invocation getArgument: &successCallback atIndex: 4]; - callbackOut = successCallback; - callbackOut([[SRNegotiationResponse alloc ]initWithDictionary:@{ - @"ConnectionId": @"10101", - @"ConnectionToken": @"10101010101", - @"DisconnectTimeout": @30, - @"ProtocolVersion": @"1.3.0.0", - @"TransportConnectTimeout": @10 - }], nil); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; + id json = @{ + @"ConnectionId": @"10101", + @"ConnectionToken": @"10101010101", + @"DisconnectTimeout": @30, + @"ProtocolVersion": @"1.3.0.0", + @"TransportConnectTimeout": @10 + }; + [SRMockClientTransport negotiateForTransport:lp statusCode:@200 json:json]; id connect1 = [SRMockNetwork mockHttpRequestOperationForClass:[AFHTTPRequestOperation class] statusCode:@500 @@ -149,20 +144,14 @@ - (void)testConnectionInitialNotCancelledPollsAgainAfterDelay { SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; SRLongPollingTransport* lp = [[ SRLongPollingTransport alloc] init]; - id mockTransport = [OCMockObject partialMockForObject: lp]; - [[[mockTransport stub] andDo:^(NSInvocation *invocation) { - void (^ callbackOut)(SRNegotiationResponse * response, NSError *error); - __unsafe_unretained void (^successCallback)(SRNegotiationResponse *response, NSError *error) = nil; - [invocation getArgument: &successCallback atIndex: 4]; - callbackOut = successCallback; - callbackOut([[SRNegotiationResponse alloc ]initWithDictionary:@{ - @"ConnectionId": @"10101", - @"ConnectionToken": @"10101010101", - @"DisconnectTimeout": @30, - @"ProtocolVersion": @"1.3.0.0", - @"TransportConnectTimeout": @10 - }], nil); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; + id json = @{ + @"ConnectionId": @"10101", + @"ConnectionToken": @"10101010101", + @"DisconnectTimeout": @30, + @"ProtocolVersion": @"1.3.0.0", + @"TransportConnectTimeout": @10 + }; + id mockTransport = [SRMockClientTransport negotiateForTransport:lp statusCode:@200 json:json]; [[[mockTransport stub] andForwardToRealObject] poll:[OCMArg any] connectionData:[OCMArg isNil] completionHandler:[OCMArg isNotNil]]; [[[mockTransport stub] andDo:^(NSInvocation * invocation) { diff --git a/SignalR.Client.ObjC/SRWebSocketTransportTests.m b/SignalR.Client.ObjC/SRWebSocketTransportTests.m index 50c2fd6f..eb109ccb 100644 --- a/SignalR.Client.ObjC/SRWebSocketTransportTests.m +++ b/SignalR.Client.ObjC/SRWebSocketTransportTests.m @@ -13,6 +13,7 @@ #import "SRConnection.h" #import "SRConnectionInterface.h" #import "SRNegotiationResponse.h" +#import "SRMockClientTransport.h" @interface SRConnection (UnitTest) @property (strong, nonatomic, readwrite) NSNumber * disconnectTimeout; @@ -341,21 +342,14 @@ - (void)testDisconnectsOnReconnectTimeout { SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; SRWebSocketTransport* ws = [[ SRWebSocketTransport alloc] init]; - id pmock = [OCMockObject partialMockForObject: ws]; - [[[pmock stub] andDo:^(NSInvocation *invocation) { - void (^ callbackOut)(SRNegotiationResponse * response, NSError *error); - __unsafe_unretained void (^successCallback)(SRNegotiationResponse *response, NSError *error) = nil; - [invocation getArgument: &successCallback atIndex: 4]; - callbackOut = successCallback; - callbackOut([[SRNegotiationResponse alloc ]initWithDictionary:@{ - @"ConnectionId": @"10101", - @"ConnectionToken": @"10101010101", - @"DisconnectTimeout": @30, - @"ProtocolVersion": @"1.3.0.0", - @"TransportConnectTimeout": @10 - }], nil); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; - + id json = @{ + @"ConnectionId": @"10101", + @"ConnectionToken": @"10101010101", + @"DisconnectTimeout": @30, + @"ProtocolVersion": @"1.3.0.0", + @"TransportConnectTimeout": @10 + }; + [SRMockClientTransport negotiateForTransport:ws statusCode:@200 json:json]; [connection setStarted:^{ [initialized fulfill]; @@ -422,21 +416,15 @@ - (void)testTransportCanTimeoutWhenItDoesNotReceiveInitializeMessage { SRWebSocketTransport* ws = [[ SRWebSocketTransport alloc] init]; - id pmock = [OCMockObject partialMockForObject: ws]; - [[[pmock stub] andDo:^(NSInvocation *invocation) { - void (^ callbackOut)(SRNegotiationResponse * response, NSError *error); - __unsafe_unretained void (^successCallback)(SRNegotiationResponse *response, NSError *error) = nil; - [invocation getArgument: &successCallback atIndex: 4]; - callbackOut = successCallback; - callbackOut([[SRNegotiationResponse alloc ]initWithDictionary:@{ - @"ConnectionId": @"10101", - @"ConnectionToken": @"10101010101", - @"DisconnectTimeout": @30, - @"ProtocolVersion": @"1.3.0.0", - @"TransportConnectTimeout": @10 - }], nil); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; - + id json = @{ + @"ConnectionId": @"10101", + @"ConnectionToken": @"10101010101", + @"DisconnectTimeout": @30, + @"ProtocolVersion": @"1.3.0.0", + @"TransportConnectTimeout": @10 + }; + [SRMockClientTransport negotiateForTransport:ws statusCode:@200 json:json]; + connection.started = ^{ XCTAssert(NO, @"Connection started"); }; @@ -472,20 +460,14 @@ - (void)testStart_Stop_StartTriggersTheCorrectCallbacks { SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; SRWebSocketTransport* ws = [[ SRWebSocketTransport alloc] init]; - id pmock = [OCMockObject partialMockForObject: ws]; - [[[pmock stub] andDo:^(NSInvocation *invocation) { - void (^ callbackOut)(SRNegotiationResponse * response, NSError *error); - __unsafe_unretained void (^successCallback)(SRNegotiationResponse *response, NSError *error) = nil; - [invocation getArgument: &successCallback atIndex: 4]; - callbackOut = successCallback; - callbackOut([[SRNegotiationResponse alloc ]initWithDictionary:@{ - @"ConnectionId": @"10101", - @"ConnectionToken": @"10101010101", - @"DisconnectTimeout": @30, - @"ProtocolVersion": @"1.3.0.0", - @"TransportConnectTimeout": @10 - }], nil); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; + id json = @{ + @"ConnectionId": @"10101", + @"ConnectionToken": @"10101010101", + @"DisconnectTimeout": @30, + @"ProtocolVersion": @"1.3.0.0", + @"TransportConnectTimeout": @10 + }; + [SRMockClientTransport negotiateForTransport:ws statusCode:@200 json:json]; __block BOOL firstErrorFailedCalled = NO; __block int startCount = 0; @@ -532,20 +514,14 @@ - (void)xtestPingIntervalStopsTheConnectionOn401s { SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; SRWebSocketTransport* ws = [[ SRWebSocketTransport alloc] init]; - id pmock = [OCMockObject partialMockForObject: ws]; - [[[pmock stub] andDo:^(NSInvocation *invocation) { - void (^ callbackOut)(SRNegotiationResponse * response, NSError *error); - __unsafe_unretained void (^successCallback)(SRNegotiationResponse *response, NSError *error) = nil; - [invocation getArgument: &successCallback atIndex: 4]; - callbackOut = successCallback; - callbackOut([[SRNegotiationResponse alloc ]initWithDictionary:@{ - @"ConnectionId": @"10101", - @"ConnectionToken": @"10101010101", - @"DisconnectTimeout": @30, - @"ProtocolVersion": @"1.3.0.0", - @"TransportConnectTimeout": @10 - }], nil); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; + id json = @{ + @"ConnectionId": @"10101", + @"ConnectionToken": @"10101010101", + @"DisconnectTimeout": @30, + @"ProtocolVersion": @"1.3.0.0", + @"TransportConnectTimeout": @10 + }; + [SRMockClientTransport negotiateForTransport:ws statusCode:@200 json:json]; connection.error = ^(NSError *error){ [initialized fulfill]; @@ -577,20 +553,14 @@ - (void)xtestPingIntervalStopsTheConnectionOn403s { SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; SRWebSocketTransport* ws = [[ SRWebSocketTransport alloc] init]; - id pmock = [OCMockObject partialMockForObject: ws]; - [[[pmock stub] andDo:^(NSInvocation *invocation) { - void (^ callbackOut)(SRNegotiationResponse * response, NSError *error); - __unsafe_unretained void (^successCallback)(SRNegotiationResponse *response, NSError *error) = nil; - [invocation getArgument: &successCallback atIndex: 4]; - callbackOut = successCallback; - callbackOut([[SRNegotiationResponse alloc ]initWithDictionary:@{ - @"ConnectionId": @"10101", - @"ConnectionToken": @"10101010101", - @"DisconnectTimeout": @30, - @"ProtocolVersion": @"1.3.0.0", - @"TransportConnectTimeout": @10 - }], nil); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; + id json = @{ + @"ConnectionId": @"10101", + @"ConnectionToken": @"10101010101", + @"DisconnectTimeout": @30, + @"ProtocolVersion": @"1.3.0.0", + @"TransportConnectTimeout": @10 + }; + [SRMockClientTransport negotiateForTransport:ws statusCode:@200 json:json]; connection.error = ^(NSError *error){ [initialized fulfill]; @@ -636,20 +606,14 @@ - (void)testConnectionCanBeStoppedDuringTransportStart { SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; SRWebSocketTransport* ws = [[ SRWebSocketTransport alloc] init]; - id pmock = [OCMockObject partialMockForObject: ws]; - [[[pmock stub] andDo:^(NSInvocation *invocation) { - void (^ callbackOut)(SRNegotiationResponse * response, NSError *error); - __unsafe_unretained void (^successCallback)(SRNegotiationResponse *response, NSError *error) = nil; - [invocation getArgument: &successCallback atIndex: 4]; - callbackOut = successCallback; - callbackOut([[SRNegotiationResponse alloc ]initWithDictionary:@{ - @"ConnectionId": @"10101", - @"ConnectionToken": @"10101010101", - @"DisconnectTimeout": @30, - @"ProtocolVersion": @"1.3.0.0", - @"TransportConnectTimeout": @10 - }], nil); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; + id json = @{ + @"ConnectionId": @"10101", + @"ConnectionToken": @"10101010101", + @"DisconnectTimeout": @30, + @"ProtocolVersion": @"1.3.0.0", + @"TransportConnectTimeout": @10 + }; + [SRMockClientTransport negotiateForTransport:ws statusCode:@200 json:json]; connection.closed = ^{ [initialized fulfill]; @@ -739,21 +703,15 @@ - (void)testTransportCanSendAndReceiveMessagesOnConnect { SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; SRWebSocketTransport* ws = [[ SRWebSocketTransport alloc] init]; - id pmock = [OCMockObject partialMockForObject: ws]; - [[[pmock stub] andDo:^(NSInvocation *invocation) { - void (^ callbackOut)(SRNegotiationResponse * response, NSError *error); - __unsafe_unretained void (^successCallback)(SRNegotiationResponse *response, NSError *error) = nil; - [invocation getArgument: &successCallback atIndex: 4]; - callbackOut = successCallback; - callbackOut([[SRNegotiationResponse alloc ]initWithDictionary:@{ - @"ConnectionId": @"10101", - @"ConnectionToken": @"10101010101", - @"DisconnectTimeout": @30, - @"ProtocolVersion": @"1.3.0.0", - @"TransportConnectTimeout": @10 - }], nil); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; - + id json = @{ + @"ConnectionId": @"10101", + @"ConnectionToken": @"10101010101", + @"DisconnectTimeout": @30, + @"ProtocolVersion": @"1.3.0.0", + @"TransportConnectTimeout": @10 + }; + [SRMockClientTransport negotiateForTransport:ws statusCode:@200 json:json]; + __block NSMutableArray* values = [[NSMutableArray alloc] init]; __weak __typeof(&*connection)weakConnection = connection; @@ -796,24 +754,14 @@ - (void)testTransportThrowsAnErrorIfProtocolVersionIsIncorrect{ SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; SRWebSocketTransport* ws = [[ SRWebSocketTransport alloc] init]; - id pmock = [OCMockObject partialMockForObject: ws]; - [[[pmock stub] andDo:^(NSInvocation *invocation) { - __unsafe_unretained void (^ callbackOut)(SRNegotiationResponse * response, NSError *error); - [invocation getArgument: &callbackOut atIndex: 4]; - callbackOut([[SRNegotiationResponse alloc ] - initWithDictionary:@{ - @"ConnectionId": @"10101", - @"ConnectionToken": @"10101010101", - @"DisconnectTimeout": @30, - @"ProtocolVersion": @"2.0.0.0", - @"TransportConnectTimeout": @10 - }], nil); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; - [[[pmock stub] andDo:^(NSInvocation * invocation) { - __unsafe_unretained void (^ callbackOut)(id * response, NSError *error); - [invocation getArgument: &callbackOut atIndex: 5]; - callbackOut(nil, nil);//SSE just falls back to httpbase, just verify we are allowed through - }] send:[OCMArg any] data:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; + id json = @{ + @"ConnectionId": @"10101", + @"ConnectionToken": @"10101010101", + @"DisconnectTimeout": @30, + @"ProtocolVersion": @"2.0.0.0", + @"TransportConnectTimeout": @10 + }; + [SRMockClientTransport negotiateForTransport:ws statusCode:@200 json:json]; BOOL failed = NO; @try diff --git a/SignalR.Client.ObjC/SignalR.Client.ObjC.xcodeproj/project.pbxproj b/SignalR.Client.ObjC/SignalR.Client.ObjC.xcodeproj/project.pbxproj index ae119db6..40cf9cf2 100644 --- a/SignalR.Client.ObjC/SignalR.Client.ObjC.xcodeproj/project.pbxproj +++ b/SignalR.Client.ObjC/SignalR.Client.ObjC.xcodeproj/project.pbxproj @@ -171,8 +171,8 @@ 39A27F011C5FD42F009B7459 /* SREventSourceResponseSerializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 39A27EFD1C5FD242009B7459 /* SREventSourceResponseSerializer.m */; }; 39A27F021C5FD76A009B7459 /* SRServerSentEventsTransportTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D05649B71B6EF87F00020B31 /* SRServerSentEventsTransportTests.m */; }; 39A27F031C5FD76B009B7459 /* SRServerSentEventsTransportTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D05649B71B6EF87F00020B31 /* SRServerSentEventsTransportTests.m */; }; - 39A27F061C5FF8F0009B7459 /* SRMockNegotiate.m in Sources */ = {isa = PBXBuildFile; fileRef = 39A27F051C5FF8F0009B7459 /* SRMockNegotiate.m */; }; - 39A27F071C5FF8F0009B7459 /* SRMockNegotiate.m in Sources */ = {isa = PBXBuildFile; fileRef = 39A27F051C5FF8F0009B7459 /* SRMockNegotiate.m */; }; + 39A27F061C5FF8F0009B7459 /* SRMockClientTransport.m in Sources */ = {isa = PBXBuildFile; fileRef = 39A27F051C5FF8F0009B7459 /* SRMockClientTransport.m */; }; + 39A27F071C5FF8F0009B7459 /* SRMockClientTransport.m in Sources */ = {isa = PBXBuildFile; fileRef = 39A27F051C5FF8F0009B7459 /* SRMockClientTransport.m */; }; 39AF0D7E17138E3800E13E6E /* SRWebSocketTransport.h in Headers */ = {isa = PBXBuildFile; fileRef = 39AF0D7C17138E3800E13E6E /* SRWebSocketTransport.h */; settings = {ATTRIBUTES = (Public, ); }; }; 39AF0D7F17138E3800E13E6E /* SRWebSocketTransport.m in Sources */ = {isa = PBXBuildFile; fileRef = 39AF0D7D17138E3800E13E6E /* SRWebSocketTransport.m */; }; 39AF0D821713935A00E13E6E /* SRWebSocketConnectionInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 39AF0D801713935A00E13E6E /* SRWebSocketConnectionInfo.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -338,8 +338,8 @@ 39A27EF91C5FD151009B7459 /* SREventSourceRequestSerializer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SREventSourceRequestSerializer.m; sourceTree = ""; }; 39A27EFC1C5FD242009B7459 /* SREventSourceResponseSerializer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SREventSourceResponseSerializer.h; sourceTree = ""; }; 39A27EFD1C5FD242009B7459 /* SREventSourceResponseSerializer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SREventSourceResponseSerializer.m; sourceTree = ""; }; - 39A27F041C5FF8F0009B7459 /* SRMockNegotiate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SRMockNegotiate.h; path = SignalR.Client.Tests/SRMockNegotiate.h; sourceTree = ""; }; - 39A27F051C5FF8F0009B7459 /* SRMockNegotiate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SRMockNegotiate.m; path = SignalR.Client.Tests/SRMockNegotiate.m; sourceTree = ""; }; + 39A27F041C5FF8F0009B7459 /* SRMockClientTransport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SRMockClientTransport.h; path = SignalR.Client.Tests/SRMockClientTransport.h; sourceTree = ""; }; + 39A27F051C5FF8F0009B7459 /* SRMockClientTransport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SRMockClientTransport.m; path = SignalR.Client.Tests/SRMockClientTransport.m; sourceTree = ""; }; 39AF0D7C17138E3800E13E6E /* SRWebSocketTransport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRWebSocketTransport.h; sourceTree = ""; }; 39AF0D7D17138E3800E13E6E /* SRWebSocketTransport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRWebSocketTransport.m; sourceTree = ""; }; 39AF0D801713935A00E13E6E /* SRWebSocketConnectionInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SRWebSocketConnectionInfo.h; path = WebSockets/SRWebSocketConnectionInfo.h; sourceTree = ""; }; @@ -805,8 +805,8 @@ D05649BD1B71441000020B31 /* SRLongPollingTransportTests.m */, 3955D59C1B9794A8005AD0CF /* SRMockNetwork.h */, 3955D59D1B9794A8005AD0CF /* SRMockNetwork.m */, - 39A27F041C5FF8F0009B7459 /* SRMockNegotiate.h */, - 39A27F051C5FF8F0009B7459 /* SRMockNegotiate.m */, + 39A27F041C5FF8F0009B7459 /* SRMockClientTransport.h */, + 39A27F051C5FF8F0009B7459 /* SRMockClientTransport.m */, ); name = SignalR.Client.Tests; sourceTree = ""; @@ -1369,7 +1369,7 @@ 39E6FD2D15C6BFCE00434098 /* SRJSONTests.m in Sources */, 39E6FD5F15C6EAFB00434098 /* NSObject+SRJSON.m in Sources */, D05649BC1B7058FF00020B31 /* SRWebSocketTransportTests.m in Sources */, - 39A27F071C5FF8F0009B7459 /* SRMockNegotiate.m in Sources */, + 39A27F071C5FF8F0009B7459 /* SRMockClientTransport.m in Sources */, 3955D59F1B9794FD005AD0CF /* SRMockNetwork.m in Sources */, 39BB8D7715CAA576007B7245 /* SRHubConnectionTests.m in Sources */, 39A27F021C5FD76A009B7459 /* SRServerSentEventsTransportTests.m in Sources */, @@ -1392,7 +1392,7 @@ 39BB8D7615CAA576007B7245 /* SRHubConnectionTests.m in Sources */, 3940059415CC6A2C006673E2 /* SRChunkBufferTests.m in Sources */, 3940059915CC6DD6006673E2 /* SRConnectionTests.m in Sources */, - 39A27F061C5FF8F0009B7459 /* SRMockNegotiate.m in Sources */, + 39A27F061C5FF8F0009B7459 /* SRMockClientTransport.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/SignalR.Client.ObjC/SignalR.Client.Tests/SRConnectionTests.m b/SignalR.Client.ObjC/SignalR.Client.Tests/SRConnectionTests.m index 587442ad..45ae95b8 100644 --- a/SignalR.Client.ObjC/SignalR.Client.Tests/SRConnectionTests.m +++ b/SignalR.Client.ObjC/SignalR.Client.Tests/SRConnectionTests.m @@ -12,6 +12,7 @@ #import "SRVersion.h" #import "SRClientTransportInterface.h" #import "SRNegotiationResponse.h" +#import "SRMockClientTransport.h" @interface SRConnectionTests : XCTestCase @@ -32,31 +33,21 @@ - (void)tearDown - (void) testTransportErrorCausesError { id transport = [OCMockObject niceMockForProtocol:@protocol(SRClientTransportInterface)]; - __block void(^done)(id response, NSError *error); - [[[transport stub] andDo:^(NSInvocation *invocation) { - __unsafe_unretained void (^callbackOut)(id response, NSError* err); - [invocation getArgument: &callbackOut atIndex: 4]; - done = callbackOut; - }] start:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; - [[[transport stub] andDo:^(NSInvocation *invocation) { - __unsafe_unretained void (^successCallback)(SRNegotiationResponse *, NSError *) = nil; - [invocation getArgument: &successCallback atIndex: 4]; - successCallback([[SRNegotiationResponse alloc ] - initWithDictionary:@{ - @"ConnectionId": @"10101", - @"ConnectionToken": @"10101010101", - @"DisconnectTimeout": @30, - @"ProtocolVersion": @"1.3.0.0" - }], nil); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; - + [SRMockClientTransport startForMockTransport:transport statusCode:@400 error:[[NSError alloc]initWithDomain:@"Expected" code:42 userInfo:nil]]; + id json = @{ + @"ConnectionId": @"10101", + @"ConnectionToken": @"10101010101", + @"DisconnectTimeout": @30, + @"ProtocolVersion": @"1.3.0.0" + }; + [SRMockClientTransport negotiateForMockTransport:transport statusCode:@200 json:json]; + SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; id willErrorOnError = [self expectationWithDescription:@"gets closed when transport errors out"]; connection.error = ^(NSError *error){ [willErrorOnError fulfill]; }; [connection start: transport]; - done(nil, [[NSError alloc]initWithDomain:@"Expected" code:42 userInfo:nil]); [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error){ NSLog(@"Sub-Timeout Error: %@", error); @@ -67,31 +58,21 @@ - (void) testTransportErrorCausesError - (void) testTransportErrorCausesClosed { id transport = [OCMockObject niceMockForProtocol:@protocol(SRClientTransportInterface)]; - __block void(^done)(id response, NSError *error); - [[[transport stub] andDo:^(NSInvocation *invocation) { - __unsafe_unretained void (^callbackOut)(id response, NSError* err); - [invocation getArgument: &callbackOut atIndex: 4]; - done = callbackOut; - }] start:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; - [[[transport stub] andDo:^(NSInvocation *invocation) { - __unsafe_unretained void (^successCallback)(SRNegotiationResponse *, NSError *) = nil; - [invocation getArgument: &successCallback atIndex: 4]; - successCallback([[SRNegotiationResponse alloc ] - initWithDictionary:@{ - @"ConnectionId": @"10101", - @"ConnectionToken": @"10101010101", - @"DisconnectTimeout": @30, - @"ProtocolVersion": @"1.3.0.0" - }], nil); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; - + [SRMockClientTransport startForMockTransport:transport statusCode:@400 error:[[NSError alloc]initWithDomain:@"Expected" code:42 userInfo:nil]]; + id json = @{ + @"ConnectionId": @"10101", + @"ConnectionToken": @"10101010101", + @"DisconnectTimeout": @30, + @"ProtocolVersion": @"1.3.0.0" + }; + [SRMockClientTransport negotiateForMockTransport:transport statusCode:@200 json:json]; + SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; id willCloseOnError = [self expectationWithDescription:@"gets closed when transport errors out"]; connection.closed = ^{ [willCloseOnError fulfill]; }; [connection start: transport]; - done(nil, [[NSError alloc]initWithDomain:@"Expected" code:42 userInfo:nil]); [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error){ NSLog(@"Sub-Timeout Error: %@", error); @@ -102,13 +83,8 @@ - (void) testTransportErrorCausesClosed - (void) testTransportNegotiateCausesError { id transport = [OCMockObject niceMockForProtocol:@protocol(SRClientTransportInterface)]; - - [[[transport stub] andDo:^(NSInvocation *invocation) { - __unsafe_unretained void (^successCallback)(SRNegotiationResponse *, NSError *) = nil; - [invocation getArgument: &successCallback atIndex: 4]; - successCallback(nil, [NSError errorWithDomain:@"UNIT TEST" code:NSURLErrorTimedOut userInfo:nil]); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; - + [SRMockClientTransport negotiateForMockTransport:transport statusCode:@400 error:[NSError errorWithDomain:@"UNIT TEST" code:NSURLErrorTimedOut userInfo:nil]]; + SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; id willErrorOnError = [self expectationWithDescription:@"gets closed when transport errors out"]; connection.error = ^(NSError *error){ @@ -125,13 +101,8 @@ - (void) testTransportNegotiateCausesError - (void) testTransportNegotiateCausesClosed { id transport = [OCMockObject niceMockForProtocol:@protocol(SRClientTransportInterface)]; + [SRMockClientTransport negotiateForMockTransport:transport statusCode:@400 error:[NSError errorWithDomain:@"UNIT TEST" code:NSURLErrorTimedOut userInfo:nil]]; - [[[transport stub] andDo:^(NSInvocation *invocation) { - __unsafe_unretained void (^successCallback)(SRNegotiationResponse *, NSError *) = nil; - [invocation getArgument: &successCallback atIndex: 4]; - successCallback(nil, [NSError errorWithDomain:@"UNIT TEST" code:NSURLErrorTimedOut userInfo:nil]); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; - SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; id willCloseOnError = [self expectationWithDescription:@"gets closed when transport errors out"]; connection.closed = ^{ diff --git a/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockClientTransport.h b/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockClientTransport.h new file mode 100644 index 00000000..289af234 --- /dev/null +++ b/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockClientTransport.h @@ -0,0 +1,62 @@ +// +// SRMockClientTransport.h +// SignalR.Client.ObjC +// +// Created by Alex Billingsley on 2/1/16. +// Copyright © 2016 DyKnow LLC. All rights reserved. +// + +#import +#import "SRClientTransportInterface.h" + +@interface SRMockClientTransport : NSObject + ++ (id)negotiateForTransport:(id )transport + statusCode:(NSNumber *)statusCode + json:(id)json; + ++ (id)negotiateForMockTransport:(id)transportMock + statusCode:(NSNumber *)statusCode + json:(id)json; + ++ (id)negotiateForTransport:(id )transport + statusCode:(NSNumber *)statusCode + error:(NSError *)error; + ++ (id)negotiateForMockTransport:(id)transportMock + statusCode:(NSNumber *)statusCode + error:(NSError *)error; + ++ (id)startForTransport:(id )transport + statusCode:(NSNumber *)statusCode + json:(id)json; + ++ (id)startForMockTransport:(id )transportMock + statusCode:(NSNumber *)statusCode + json:(id)json; + ++ (id)startForTransport:(id )transport + statusCode:(NSNumber *)statusCode + error:(NSError *)error; + ++ (id)startForMockTransport:(id)transportMock + statusCode:(NSNumber *)statusCode + error:(NSError *)error; + ++ (id)sendForTransport:(id )transport + statusCode:(NSNumber *)statusCode + json:(id)json; + ++ (id)sendForMockTransport:(id)transportMock + statusCode:(NSNumber *)statusCode + json:(id)json; + ++ (id)sendForTransport:(id )transport + statusCode:(NSNumber *)statusCode + error:(NSError *)error; + ++ (id)sendForMockTransport:(id)transportMock + statusCode:(NSNumber *)statusCode + error:(NSError *)error; + +@end diff --git a/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockClientTransport.m b/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockClientTransport.m new file mode 100644 index 00000000..a53ee84d --- /dev/null +++ b/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockClientTransport.m @@ -0,0 +1,253 @@ +// +// SRMockClientTransport.m +// SignalR.Client.ObjC +// +// Created by Alex Billingsley on 2/1/16. +// Copyright © 2016 DyKnow LLC. All rights reserved. +// + +#import "SRMockClientTransport.h" +#import +#import +#import "SRClientTransportInterface.h" +#import "SRNegotiationResponse.h" + +@implementation SRMockClientTransport + ++ (id)negotiateStub:(id)mock +statusCode:(NSNumber *)statusCode + json:(id)json + callback:(NSInteger)callbackIndex { + [[[mock stub] andDo:^(NSInvocation *invocation) { + void (^completionHandler)(SRNegotiationResponse * response, NSError *error); + __unsafe_unretained void (^negotiateCallback)(SRNegotiationResponse *, NSError *) = nil; + [invocation getArgument: &negotiateCallback atIndex:callbackIndex]; + completionHandler = negotiateCallback; + + if ([statusCode isEqual: @200]) { + if (completionHandler) { + completionHandler([[SRNegotiationResponse alloc ]initWithDictionary:json], nil); + } + } else { + if (completionHandler) { + completionHandler(nil, json); + } + } + }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; + return mock; +} + ++ (id)negotiateForTransport:(id )transport + statusCode:(NSNumber *)statusCode + json:(id)json; { + return [self negotiateForTransport:transport statusCode:statusCode json:json callback:4]; +} + ++ (id)negotiateForMockTransport:(id)transportMock + statusCode:(NSNumber *)statusCode + json:(id)json; { + return [self negotiateForMockTransport:transportMock statusCode:statusCode json:json callback:4]; +} + ++ (id)negotiateForTransport:(id )transport + statusCode:(NSNumber *)statusCode + json:(id)json + callback:(NSInteger)callbackIndex; { + return [[self class] negotiateForMockTransport:[OCMockObject partialMockForObject:transport] statusCode:statusCode json:json callback:callbackIndex]; +} + ++ (id)negotiateForMockTransport:(id)transportMock + statusCode:(NSNumber *)statusCode + json:(id)json + callback:(NSInteger)callbackIndex; { + return [[self class] negotiateStub:transportMock statusCode:statusCode json:json callback:callbackIndex]; +} + + ++ (id)negotiateForTransport:(id )transport + statusCode:(NSNumber *)statusCode + error:(NSError *)error { + return [self negotiateForTransport:transport statusCode:statusCode error:error callback:4]; +} + ++ (id)negotiateForMockTransport:(id)transportMock + statusCode:(NSNumber *)statusCode + error:(NSError *)error { + return [self negotiateForMockTransport:transportMock statusCode:statusCode error:error callback:4]; +} + ++ (id)negotiateForTransport:(id )transport + statusCode:(NSNumber *)statusCode + error:(NSError *)error + callback:(NSInteger)callbackIndex; { + return [[self class] negotiateForMockTransport:[OCMockObject partialMockForObject:transport] statusCode:statusCode error:error callback:callbackIndex]; +} + ++ (id)negotiateForMockTransport:(id)transportMock + statusCode:(NSNumber *)statusCode + error:(NSError *)error + callback:(NSInteger)callbackIndex; { + return [[self class] negotiateStub:transportMock statusCode:statusCode json:error callback:callbackIndex]; +} + +#pragma mark - +#pragma mark Start + ++ (id)startStub:(id)mock + statusCode:(NSNumber *)statusCode + json:(id)json + callback:(NSInteger)callbackIndex { + [[[mock stub] andDo:^(NSInvocation *invocation) { + void (^completionHandler)(id response, NSError *error); + __unsafe_unretained void (^startCallback)(id, NSError *) = nil; + [invocation getArgument: &startCallback atIndex:callbackIndex]; + completionHandler = startCallback; + + if ([statusCode isEqual: @200]) { + if (completionHandler) { + completionHandler(json, nil); + } + } else { + if (completionHandler) { + completionHandler(nil, json); + } + } + }] start:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; + return mock; +} + ++ (id)startForTransport:(id )transport + statusCode:(NSNumber *)statusCode + json:(id)json; { + return [self startForTransport:transport statusCode:statusCode json:json callback:4]; +} + ++ (id)startForMockTransport:(id )transportMock + statusCode:(NSNumber *)statusCode + json:(id)json; { + return [self startForMockTransport:transportMock statusCode:statusCode json:json callback:4]; +} + ++ (id)startForTransport:(id )transport + statusCode:(NSNumber *)statusCode + json:(id)json + callback:(NSInteger)callbackIndex; { + return [[self class] startForMockTransport:[OCMockObject partialMockForObject:transport] statusCode:statusCode json:json callback:callbackIndex]; +} + ++ (id)startForMockTransport:(id)transportMock + statusCode:(NSNumber *)statusCode + json:(id)json + callback:(NSInteger)callbackIndex; { + return [[self class] startStub:transportMock statusCode:statusCode json:json callback:callbackIndex]; +} + + ++ (id)startForTransport:(id )transport + statusCode:(NSNumber *)statusCode + error:(NSError *)error { + return [self startForTransport:transport statusCode:statusCode error:error callback:4]; +} + + ++ (id)startForMockTransport:(id)transportMock + statusCode:(NSNumber *)statusCode + error:(NSError *)error { + return [self startForMockTransport:transportMock statusCode:statusCode error:error callback:4]; +} + ++ (id)startForTransport:(id )transport + statusCode:(NSNumber *)statusCode + error:(NSError *)error + callback:(NSInteger)callbackIndex; { + return [[self class] startForMockTransport:[OCMockObject partialMockForObject:transport] statusCode:statusCode error:error callback:callbackIndex]; +} + ++ (id)startForMockTransport:(id)transportMock + statusCode:(NSNumber *)statusCode + error:(NSError *)error + callback:(NSInteger)callbackIndex; { + return [[self class] startStub:transportMock statusCode:statusCode json:error callback:callbackIndex]; +} + +#pragma mark - +#pragma mark Send + ++ (id)sendStub:(id)mock + data:(id)dataStub + statusCode:(NSNumber *)statusCode + json:(id)json + callback:(NSInteger)callbackIndex { + [[[mock stub] andDo:^(NSInvocation *invocation) { + void (^completionHandler)(id response, NSError *error); + __unsafe_unretained void (^sendCallback)(id, NSError *) = nil; + [invocation getArgument: &sendCallback atIndex:callbackIndex]; + completionHandler = sendCallback; + + if ([statusCode isEqual: @200]) { + if (completionHandler) { + completionHandler(json, nil); + } + } else { + if (completionHandler) { + completionHandler(nil, json); + } + } + }] send:[OCMArg any] data:dataStub connectionData:[OCMArg any] completionHandler:[OCMArg any]]; + return mock; +} + ++ (id)sendForTransport:(id )transport + statusCode:(NSNumber *)statusCode + json:(id)json; { + return [self sendForTransport:transport statusCode:statusCode json:json callback:5]; +} + ++ (id)sendForMockTransport:(id)transportMock + statusCode:(NSNumber *)statusCode + json:(id)json; { + return [self sendForMockTransport:transportMock statusCode:statusCode json:json callback:5]; +} + ++ (id)sendForTransport:(id )transport + statusCode:(NSNumber *)statusCode + json:(id)json + callback:(NSInteger)callbackIndex; { + return [[self class] sendForMockTransport:[OCMockObject partialMockForObject:transport] statusCode:statusCode json:json callback:callbackIndex]; +} + ++ (id)sendForMockTransport:(id)transportMock + statusCode:(NSNumber *)statusCode + json:(id)json + callback:(NSInteger)callbackIndex; { + return [[self class] sendStub:transportMock data:[OCMArg any] statusCode:statusCode json:json callback:callbackIndex]; +} + + ++ (id)sendForTransport:(id )transport + statusCode:(NSNumber *)statusCode + error:(NSError *)error { + return [self sendForTransport:transport statusCode:statusCode error:error callback:5]; +} + ++ (id)sendForMockTransport:(id)transportMock + statusCode:(NSNumber *)statusCode + error:(NSError *)error { + return [self sendForMockTransport:transportMock statusCode:statusCode error:error callback:5]; +} + ++ (id)sendForTransport:(id )transport + statusCode:(NSNumber *)statusCode + error:(NSError *)error + callback:(NSInteger)callbackIndex; { + return [[self class] sendForMockTransport:[OCMockObject partialMockForObject:transport] statusCode:statusCode error:error callback:callbackIndex]; +} + ++ (id)sendForMockTransport:(id)transportMock + statusCode:(NSNumber *)statusCode + error:(NSError *)error + callback:(NSInteger)callbackIndex; { + return [[self class] sendStub:transportMock data:[OCMArg any] statusCode:statusCode json:error callback:callbackIndex]; +} + +@end diff --git a/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNegotiate.h b/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNegotiate.h deleted file mode 100644 index 54b345a0..00000000 --- a/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNegotiate.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// SRMockNegotiate.h -// SignalR.Client.ObjC -// -// Created by Alex Billingsley on 2/1/16. -// Copyright © 2016 DyKnow LLC. All rights reserved. -// - -#import -#import "SRClientTransportInterface.h" - -@interface SRMockNegotiate : NSObject - -+ (id)mockNegotiateForTransport:(id )transport - statusCode:(NSNumber *)statusCode - json:(id)json; - -+ (id)mockNegotiateForTransport:(id )transport - statusCode:(NSNumber *)statusCode - error:(NSError *)error; - -@end diff --git a/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNegotiate.m b/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNegotiate.m deleted file mode 100644 index aa35984d..00000000 --- a/SignalR.Client.ObjC/SignalR.Client.Tests/SRMockNegotiate.m +++ /dev/null @@ -1,81 +0,0 @@ -// -// SRMockNegotiate.m -// SignalR.Client.ObjC -// -// Created by Alex Billingsley on 2/1/16. -// Copyright © 2016 DyKnow LLC. All rights reserved. -// - -#import "SRMockNegotiate.h" -#import -#import -#import "SRClientTransportInterface.h" -#import "SRNegotiationResponse.h" - -@implementation SRMockNegotiate - -+ (id)negotiateStub:(id)mock -statusCode:(NSNumber *)statusCode - json:(id)json - callback:(NSInteger)callbackIndex { - [[[mock stub] andDo:^(NSInvocation *invocation) { - void (^completionHandler)(SRNegotiationResponse * response, NSError *error); - __unsafe_unretained void (^negotiateCallback)(SRNegotiationResponse *, NSError *) = nil; - [invocation getArgument: &negotiateCallback atIndex:callbackIndex]; - completionHandler = negotiateCallback; - - if ([statusCode isEqual: @200]) { - if (completionHandler) { - completionHandler([[SRNegotiationResponse alloc ]initWithDictionary:json], nil); - } - } else { - if (completionHandler) { - completionHandler(nil, json); - } - } - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; - return mock; -} - -+ (id)mockNegotiateForTransport:(id )transport - statusCode:(NSNumber *)statusCode - json:(id)json; { - return [self mockNegotiateForTransport:transport statusCode:statusCode json:json callback:4]; -} - -+ (id)mockNegotiateForTransport:(id )transport - statusCode:(NSNumber *)statusCode - json:(id)json - callback:(NSInteger)callbackIndex; { - return [[self class] mockNegotiateForMockTransport:[OCMockObject partialMockForObject:transport] statusCode:statusCode json:json callback:callbackIndex]; -} - -+ (id)mockNegotiateForMockTransport:(id)transportMock - statusCode:(NSNumber *)statusCode - json:(id)json - callback:(NSInteger)callbackIndex; { - return [[self class] negotiateStub:transportMock statusCode:statusCode json:json callback:callbackIndex]; -} - - -+ (id)mockNegotiateForTransport:(id )transport - statusCode:(NSNumber *)statusCode - error:(NSError *)error { - return [self mockNegotiateForTransport:transport statusCode:statusCode error:error callback:4]; -} - -+ (id)mockNegotiateForTransport:(id )transport - statusCode:(NSNumber *)statusCode - error:(NSError *)error - callback:(NSInteger)callbackIndex; { - return [[self class] mockNegotiateForMockTransport:[OCMockObject partialMockForObject:transport] statusCode:statusCode error:error callback:callbackIndex]; -} - -+ (id)mockNegotiateForMockTransport:(id)transportMock - statusCode:(NSNumber *)statusCode - error:(NSError *)error - callback:(NSInteger)callbackIndex; { - return [[self class] negotiateStub:transportMock statusCode:statusCode json:error callback:callbackIndex]; -} - -@end diff --git a/SignalR.Client.ObjC/SignalR.Client.Tests/SRServerSentEventsTransportTests.m b/SignalR.Client.ObjC/SignalR.Client.Tests/SRServerSentEventsTransportTests.m index 5a3251e7..61ddc10a 100644 --- a/SignalR.Client.ObjC/SignalR.Client.Tests/SRServerSentEventsTransportTests.m +++ b/SignalR.Client.ObjC/SignalR.Client.Tests/SRServerSentEventsTransportTests.m @@ -14,7 +14,7 @@ #import "SRConnectionDelegate.h" #import "SRNegotiationResponse.h" #import "SRMockNetwork.h" -#import "SRMockNegotiate.h" +#import "SRMockClientTransport.h" @interface SRConnection (UnitTest) @property (strong, nonatomic, readwrite) NSNumber * disconnectTimeout; @@ -270,9 +270,6 @@ - (void)testConnectionErrorRetries__RetriesAfterADelay__CommunicatesLifeCycleVia XCTestExpectation *initialized = [self expectationWithDescription:@"initialized"]; SSE_NetworkMock *NetworkMock = [[SSE_NetworkMock alloc] init]; SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; - __weak __typeof(&*connection)weakConnection = connection; - __weak __typeof(&*self)weakSelf = self; - __weak __typeof(&*NetworkMock)weakNetConnect = NetworkMock; connection.connectionToken = @"10101010101"; connection.connectionId = @"10101"; connection.disconnectTimeout = @30; @@ -291,52 +288,31 @@ - (void)testConnectionErrorRetries__RetriesAfterADelay__CommunicatesLifeCycleVia [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error) { NSLog(@"Timeout Error: %@", error); - } else { - //and then something horrible happens - __strong __typeof(&*weakSelf)strongSelf = weakSelf; - __strong __typeof(&*weakConnection)strongConnection = weakConnection; - __strong __typeof(&*weakNetConnect)strongNetConnect = weakNetConnect; - - //spoiler: we expect this to reconnect and for connection to communicate that out - XCTestExpectation *reconnecting = [strongSelf expectationWithDescription:@"reconnecting"]; - XCTestExpectation *reconnected = [strongSelf expectationWithDescription:@"reconnected"]; - strongConnection.reconnecting = ^(){ - [reconnecting fulfill]; - }; - strongConnection.reconnected = ^(){ - [reconnected fulfill]; - }; - - //do setup to simulate and verify the reconnect delay - __block void (^reconnectAfterTimeoutCallback)(); - __block double reconnectDelay; - id mock = [OCMockObject mockForClass:[NSBlockOperation class]]; - [[[[mock stub] andReturn: mock ] andDo:^(NSInvocation *invocation) { - __unsafe_unretained void (^successCallback)() = nil; - [invocation getArgument: &successCallback atIndex: 2]; - reconnectAfterTimeoutCallback = successCallback; - }] blockOperationWithBlock: [OCMArg any]]; - [[[mock stub] andDo:^(NSInvocation *invocation) { - double reconnectDelayOut = 0; - [invocation getArgument: &reconnectDelayOut atIndex:4]; - reconnectDelay = reconnectDelayOut; - }] performSelector:@selector(start) withObject:nil afterDelay: [[sse reconnectDelay] integerValue]]; - - strongNetConnect.onFailure(strongNetConnect.mock, [[NSError alloc]initWithDomain:@"EXPECTED" code:42 userInfo:nil]); - [mock stopMocking];//dont want to accidentally get other blocks - XCTAssertEqual(2, reconnectDelay, "Unexpected reconnect delay"); - - //we will be calling open again, so lets recapture the data to verify - SSE_NetworkMock* NetReconnect = [[SSE_NetworkMock alloc]init]; - - reconnectAfterTimeoutCallback();//simulating retry timeout - [NetReconnect prepareForOpeningResponse:nil]; - //todo: verify request url was reconnect not connect, but then you have to sovle ARC - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { - if (error){ - NSLog(@"Sub-Timeout Error: %@", error); - } - }]; + } + }]; + + XCTestExpectation *reconnecting = [self expectationWithDescription:@"Retrying callback called"]; + connection.reconnecting = ^(){ + [reconnecting fulfill]; + }; + + XCTestExpectation *reconnected = [self expectationWithDescription:@"Retry callback called"]; + connection.reconnected = ^(){ + [reconnected fulfill]; + }; + + SSE_WaitBlock* reconnectDelay = [[SSE_WaitBlock alloc] init:[[sse reconnectDelay] doubleValue]]; + NetworkMock.onFailure(NetworkMock.mock, [[NSError alloc]initWithDomain:@"EXPECTED" code:42 userInfo:nil]); + [[NetworkMock mock] stopMocking]; + SSE_NetworkMock* NetworkReconnectMock = [[SSE_NetworkMock alloc]init]; + [reconnectDelay.mock stopMocking];//dont want to accidentally get other blocks + [NetworkReconnectMock prepareForOpeningResponse:^{ + reconnectDelay.afterWait(); + }]; + + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { + if (error){ + NSLog(@"Sub-Timeout Error: %@", error); } }]; } @@ -346,9 +322,6 @@ - (void)testLostConnectionAbortsAllConnectionsAndReconnects { XCTestExpectation *initialized = [self expectationWithDescription:@"initialized"]; SSE_NetworkMock* NetworkMock = [[SSE_NetworkMock alloc] init]; SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; - __weak __typeof(&*self)weakSelf = self; - __weak __typeof(&*connection)weakConnection = connection; - __weak __typeof(&*NetworkMock)weakNetConnect = NetworkMock; connection.connectionToken = @"10101010101"; connection.connectionId = @"10101"; connection.disconnectTimeout = @30; @@ -365,58 +338,49 @@ - (void)testLostConnectionAbortsAllConnectionsAndReconnects { [initialized fulfill]; }]; }]; - + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { if (error) { NSLog(@"Timeout Error: %@", error); return; } - __strong __typeof(&*weakSelf)strongSelf = weakSelf; - __strong __typeof(&*weakConnection)strongConnection = weakConnection; - __strong __typeof(&*weakNetConnect)strongNetConnect = weakNetConnect; + }]; + + XCTestExpectation *reconnecting = [self expectationWithDescription:@"Retrying callback called"]; + connection.reconnecting = ^(){ + [reconnecting fulfill]; + }; + + XCTestExpectation *reconnected = [self expectationWithDescription:@"Retry callback called"]; + connection.reconnected = ^(){ + [reconnected fulfill]; + }; - //and then we lose connection with the server - //spoiler: we expect this to reconnect and for connection to communicate that out - XCTestExpectation *expectation = [strongSelf expectationWithDescription:@"Retrying callback called"]; - XCTestExpectation *expectation2 = [strongSelf expectationWithDescription:@"Retry callback called"]; - strongConnection.reconnecting = ^(){ - [expectation fulfill]; - }; - strongConnection.reconnected = ^(){ - [expectation2 fulfill]; - }; - - //do setup to simulate and verify the reconnect delay - SSE_WaitBlock* reconnectDelay = [[SSE_WaitBlock alloc] init:[[sse reconnectDelay] doubleValue]]; - - //loses connection immediately, everything gets cleared out, but we - //do not reconnect till later - [sse lostConnection:connection]; - [queueMock verify];//clears out the queue after the timeout - - //we will be calling open again, so lets recapture the data to verify - SSE_NetworkMock* NetReconnect = [[SSE_NetworkMock alloc]init]; - NSError *cancelledError = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:nil];//when the operation is cancelled, it yields the NSURLErrorCancelled error. From https://github.com/AFNetworking/AFNetworking/blob/c9bbbeb9cae6aeceef5353fd273fc48329009c3f/AFNetworking/AFURLConnectionOperation.m#L502 - strongNetConnect.onFailure(strongNetConnect.mock, cancelledError); - [reconnectDelay.mock stopMocking];//dont want to accidentally get other blocks + //loses connection immediately, everything gets cleared out, but we + //do not reconnect till later + [sse lostConnection:connection]; + [queueMock verify];//clears out the queue after the timeout + + SSE_WaitBlock* reconnectDelay = [[SSE_WaitBlock alloc] init:[[sse reconnectDelay] doubleValue]]; + NSError *cancelledError = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:nil];//when the operation is cancelled, it yields the NSURLErrorCancelled error. From https://github.com/AFNetworking/AFNetworking/blob/c9bbbeb9cae6aeceef5353fd273fc48329009c3f/AFNetworking/AFURLConnectionOperation.m#L502 + NetworkMock.onFailure(NetworkMock.mock, cancelledError); + [[NetworkMock mock] stopMocking]; + SSE_NetworkMock* NetworkReconnectMock = [[SSE_NetworkMock alloc]init]; + [reconnectDelay.mock stopMocking];//dont want to accidentally get other blocks + [NetworkReconnectMock prepareForOpeningResponse:^{ reconnectDelay.afterWait(); - XCTAssertEqual(2, reconnectDelay.waitTime, "Unexpected reconnect delay"); - - [NetReconnect prepareForOpeningResponse:nil]; - [strongSelf waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { - if (error){ - NSLog(@"Sub-Timeout Error: %@", error); - } - }]; + }]; + + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { + if (error) { + NSLog(@"Timeout Error: %@", error); return; + } }]; } - (void)testDisconnectsOnReconnectTimeout { XCTestExpectation *initialized = [self expectationWithDescription:@"initialized"]; - SSE_NetworkMock* NetConnect = [[SSE_NetworkMock alloc] init]; + SSE_NetworkMock* NetworkMock = [[SSE_NetworkMock alloc] init]; SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; - __weak __typeof(&*self)weakSelf = self; - __weak __typeof(&*connection)weakConnection = connection; - __weak __typeof(&*NetConnect)weakNetConnect = NetConnect; connection.connectionToken = connection.connectionId = @"10101"; connection.disconnectTimeout = @30; @@ -432,13 +396,13 @@ - (void)testDisconnectsOnReconnectTimeout { @"ProtocolVersion": @"1.3.0.0", @"TransportConnectTimeout": @10 }; - [SRMockNegotiate mockNegotiateForTransport:sse statusCode:@200 json:json]; + [SRMockClientTransport negotiateForTransport:sse statusCode:@200 json:json]; connection.started = ^{ [initialized fulfill]; }; - [NetConnect prepareForOpeningResponse:@"data: initialized\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}\n\n" then:^{ + [NetworkMock prepareForOpeningResponse:@"data: initialized\n\ndata: {\"M\":[{\"H\":\"hubname\", \"M\":\"message\", \"A\": \"12345\"}]}\n\n" then:^{ [connection start:sse]; }]; @@ -446,57 +410,46 @@ - (void)testDisconnectsOnReconnectTimeout { if (error) { NSLog(@"Timeout Error: %@", error); return; } - __strong __typeof(&*weakSelf)strongSelf = weakSelf; - __strong __typeof(&*weakConnection)strongConnection = weakConnection; - __strong __typeof(&*weakNetConnect)strongNetConnect = weakNetConnect; - - //trigger an error to see - //spoiler: we expect this to reconnect and for connection to communicate that out - XCTestExpectation *expectation = [strongSelf expectationWithDescription:@"Retrying callback called"]; - XCTestExpectation *expectation2 = [strongSelf expectationWithDescription:@"disconnected callback called"]; - strongConnection.reconnecting = ^(){ - [expectation fulfill]; - }; - strongConnection.reconnected = ^(){ - XCTAssert(NO, @"unexpected change!"); - }; - - strongConnection.closed = ^(){ - [expectation2 fulfill]; - }; - - //do setup to simulate and verify the reconnect delay - SSE_WaitBlock* reconnectBlock = [[SSE_WaitBlock alloc]init:[[sse reconnectDelay] doubleValue]]; - NSError *cancelledError = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:nil]; - strongNetConnect.onFailure(strongNetConnect.mock, cancelledError); - [reconnectBlock.mock stopMocking];//dont want to accidentally get other blocks - - //we will be calling open again, so lets recapture the data to verify - SSE_NetworkMock* NetReconnect = [[SSE_NetworkMock alloc]init]; - //prep to catch the reconnect timeout - SSE_WaitBlock* reconnectTimeoutBlock = [[SSE_WaitBlock alloc] init:[connection.disconnectTimeout doubleValue]]; - - //retry has waited now, - reconnectBlock.afterWait(); - XCTAssertEqual([connection.disconnectTimeout doubleValue], reconnectTimeoutBlock.waitTime, @"got timeout value from an unexpected place - check to be sure we are pulling from the connection"); - - //lets track that we clear out the queue when we timeout - id queueMock = [OCMockObject niceMockForClass:[NSOperationQueue class]]; - [[queueMock expect] cancelAllOperations]; - sse.serverSentEventsOperationQueue = queueMock; - - //connection timed out without succeeding - reconnectTimeoutBlock.afterWait(); - - [queueMock verify]; - XCTAssertTrue([sse stop], @"did not stop the transport. this makes the transport unpredictable"); - - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { - if (error){ - NSLog(@"Sub-Timeout Error: %@", error); - } - sse = nil; - }]; + }]; + + XCTestExpectation *reconnecting = [self expectationWithDescription:@"Retrying callback called"]; + connection.reconnecting = ^(){ + [reconnecting fulfill]; + }; + + connection.reconnected = ^(){ + XCTAssert(NO, @"unexpected change!"); + }; + + XCTestExpectation *closed = [self expectationWithDescription:@"closed"]; + connection.closed = ^(){ + [closed fulfill]; + }; + + SSE_WaitBlock* reconnectDelay = [[SSE_WaitBlock alloc] init:[[sse reconnectDelay] doubleValue]]; + NSError *cancelledError = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:nil]; + NetworkMock.onFailure(NetworkMock.mock, cancelledError); + [[NetworkMock mock] stopMocking]; + [reconnectDelay.mock stopMocking];//dont want to accidentally get other blocks + SSE_WaitBlock* reconnectTimeoutBlock = [[SSE_WaitBlock alloc] init:[connection.disconnectTimeout doubleValue]]; + reconnectDelay.afterWait(); + + //lets track that we clear out the queue when we timeout + id queueMock = [OCMockObject niceMockForClass:[NSOperationQueue class]]; + [[queueMock expect] cancelAllOperations]; + sse.serverSentEventsOperationQueue = queueMock; + + //connection timed out without succeeding + reconnectTimeoutBlock.afterWait(); + + [queueMock verify]; + XCTAssertTrue([sse stop], @"did not stop the transport. this makes the transport unpredictable"); + + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { + if (error){ + NSLog(@"Sub-Timeout Error: %@", error); + } + sse = nil; }]; } @@ -580,7 +533,7 @@ - (void)testTransportCanTimeoutWhenItDoesNotReceiveInitializeMessage { @"ProtocolVersion": @"1.3.0.0", @"TransportConnectTimeout": @10 }; - [SRMockNegotiate mockNegotiateForTransport:sse statusCode:@200 json:json]; + [SRMockClientTransport negotiateForTransport:sse statusCode:@200 json:json]; connection.started = ^{ XCTAssert(NO, @"Connection started"); @@ -621,7 +574,7 @@ - (void)testStart_Stop_StartTriggersTheCorrectCallbacks { @"ProtocolVersion": @"1.3.0.0", @"TransportConnectTimeout": @10 }; - [SRMockNegotiate mockNegotiateForTransport:sse statusCode:@200 json:json]; + [SRMockClientTransport negotiateForTransport:sse statusCode:@200 json:json]; __block BOOL firstClosedCalled = NO; __block int startCount = 0; @@ -670,7 +623,7 @@ - (void)xtestPingIntervalStopsTheConnectionOn401s { @"ProtocolVersion": @"1.3.0.0", @"TransportConnectTimeout": @10 }; - [SRMockNegotiate mockNegotiateForTransport:sse statusCode:@200 json:json]; + [SRMockClientTransport negotiateForTransport:sse statusCode:@200 json:json]; connection.error = ^(NSError *error){ [initialized fulfill]; @@ -704,7 +657,7 @@ - (void)xtestPingIntervalStopsTheConnectionOn403s { @"ProtocolVersion": @"1.3.0.0", @"TransportConnectTimeout": @10 }; - [SRMockNegotiate mockNegotiateForTransport:sse statusCode:@200 json:json]; + [SRMockClientTransport negotiateForTransport:sse statusCode:@200 json:json]; connection.error = ^(NSError *error){ [initialized fulfill]; @@ -737,10 +690,8 @@ - (void)testReconnectExceedingTheReconnectWindowResultsInTheConnectionDisconnect XCTestExpectation *initialized = [self expectationWithDescription:@"initialized"]; SSE_NetworkMock* NetworkMock = [[SSE_NetworkMock alloc] init]; SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"]; - __weak __typeof(&*self)weakSelf = self; - __weak __typeof(&*connection)weakConnection = connection; - - __block SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init]; + + SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init]; sse.serverSentEventsOperationQueue = nil;//set to nil to avoid ARC error http://stackoverflow.com/questions/18121902/using-ocmock-on-nsoperation-gives-bad-access id json = @{ @@ -750,7 +701,7 @@ - (void)testReconnectExceedingTheReconnectWindowResultsInTheConnectionDisconnect @"ProtocolVersion": @"1.3.0.0", @"TransportConnectTimeout": @10 }; - [SRMockNegotiate mockNegotiateForTransport:sse statusCode:@200 json:json]; + [SRMockClientTransport negotiateForTransport:sse statusCode:@200 json:json]; connection.started = ^{ [initialized fulfill]; @@ -764,45 +715,33 @@ - (void)testReconnectExceedingTheReconnectWindowResultsInTheConnectionDisconnect if (error) { NSLog(@"Timeout Error: %@", error); return; } - __strong __typeof(&*weakSelf)strongSelf = weakSelf; - __strong __typeof(&*weakConnection)strongConnection = weakConnection; - - //trigger an error to see - //spoiler: we expect this to fail - XCTestExpectation *expectation2 = [strongSelf expectationWithDescription:@"disconnected callback called"]; + }]; + + XCTestExpectation *disconnected = [self expectationWithDescription:@"disconnected"]; + connection.reconnected = ^(){ + XCTAssert(NO, @"unexpected change!"); + }; + connection.closed = ^(){ + [disconnected fulfill]; + }; + + SSE_WaitBlock* reconnectDelay = [[SSE_WaitBlock alloc] init:[[sse reconnectDelay] doubleValue]]; + NSError *cancelledError = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:nil]; + NetworkMock.onFailure(NetworkMock.mock, cancelledError); + [[NetworkMock mock] stopMocking]; + [reconnectDelay.mock stopMocking];//dont want to accidentally get other blocks + SSE_WaitBlock* reconnectTimeoutBlock = [[SSE_WaitBlock alloc] init:[connection.disconnectTimeout doubleValue]]; + //retry has waited now, + reconnectDelay.afterWait(); + XCTAssertEqual([connection.disconnectTimeout doubleValue], reconnectTimeoutBlock.waitTime, @"got timeout value from an unexpected place - check to be sure we are pulling from the connection"); - strongConnection.reconnected = ^(){ - XCTAssert(NO, @"unexpected change!"); - }; - - strongConnection.closed = ^(){ - [expectation2 fulfill]; - }; - - //do setup to simulate and verify the reconnect delay - SSE_WaitBlock* reconnectBlock = [[SSE_WaitBlock alloc]init:[[sse reconnectDelay] doubleValue]]; - NSError *cancelledError = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:nil]; - NetworkMock.onFailure(NetworkMock.mock, cancelledError); - [reconnectBlock.mock stopMocking];//dont want to accidentally get other blocks - - //we will be calling open again, so lets recapture the data to verify - SSE_NetworkMock* NetReconnect = [[SSE_NetworkMock alloc]init]; - //prep to catch the reconnect timeout - SSE_WaitBlock* reconnectTimeoutBlock = [[SSE_WaitBlock alloc]init: [connection.disconnectTimeout doubleValue]]; - - //retry has waited now, - reconnectBlock.afterWait(); - XCTAssertEqual([connection.disconnectTimeout doubleValue], reconnectTimeoutBlock.waitTime, @"got timeout value from an unexpected place - check to be sure we are pulling from the connection"); - - //connection timed out without succeeding - reconnectTimeoutBlock.afterWait(); - - [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { - if (error){ - NSLog(@"Sub-Timeout Error: %@", error); - } - sse = nil; - }]; + //connection timed out without succeeding + reconnectTimeoutBlock.afterWait(); + + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) { + if (error){ + NSLog(@"Sub-Timeout Error: %@", error); + } }]; } @@ -821,7 +760,7 @@ - (void)testConnectionCanBeStoppedDuringTransportStart { @"ProtocolVersion": @"1.3.0.0", @"TransportConnectTimeout": @10 }; - [SRMockNegotiate mockNegotiateForTransport:sse statusCode:@200 json:json]; + [SRMockClientTransport negotiateForTransport:sse statusCode:@200 json:json]; connection.closed = ^{ [initialized fulfill]; @@ -898,25 +837,16 @@ - (void)testTransportCanSendAndReceiveMessagesOnConnect { __block SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init]; sse.serverSentEventsOperationQueue = nil;//set to nil to avoid ARC error - id pmock = [OCMockObject partialMockForObject: sse]; - [[[pmock stub] andDo:^(NSInvocation *invocation) { - __unsafe_unretained void (^ callbackOut)(SRNegotiationResponse * response, NSError *error); - [invocation getArgument: &callbackOut atIndex: 4]; - callbackOut([[SRNegotiationResponse alloc ] - initWithDictionary:@{ - @"ConnectionId": @"10101", - @"ConnectionToken": @"10101010101", - @"DisconnectTimeout": @30, - @"ProtocolVersion": @"1.3.0.0", - @"TransportConnectTimeout": @10 - }], nil); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; - [[[pmock stub] andDo:^(NSInvocation * invocation) { - __unsafe_unretained void (^ callbackOut)(id * response, NSError *error); - [invocation getArgument: &callbackOut atIndex: 5]; - callbackOut(nil, nil);//SSE just falls back to httpbase, just verify we are allowed through - }] send:[OCMArg any] data:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; - + id json = @{ + @"ConnectionId": @"10101", + @"ConnectionToken": @"10101010101", + @"DisconnectTimeout": @30, + @"ProtocolVersion": @"1.3.0.0", + @"TransportConnectTimeout": @10 + }; + id transportMock = [SRMockClientTransport negotiateForTransport:sse statusCode:@200 json:json]; + [SRMockClientTransport sendForMockTransport:transportMock statusCode:@200 json:nil]; + __block NSMutableArray* values = [[NSMutableArray alloc] init]; connection.started = ^(){ @@ -958,25 +888,15 @@ - (void)testTransportThrowsAnErrorIfProtocolVersionIsIncorrect{ SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init]; sse.serverSentEventsOperationQueue = nil;//set to nil to avoid ARC error - id pmock = [OCMockObject partialMockForObject: sse]; - [[[pmock stub] andDo:^(NSInvocation *invocation) { - __unsafe_unretained void (^ callbackOut)(SRNegotiationResponse * response, NSError *error); - [invocation getArgument: &callbackOut atIndex: 4]; - callbackOut([[SRNegotiationResponse alloc ] - initWithDictionary:@{ - @"ConnectionId": @"10101", - @"ConnectionToken": @"10101010101", - @"DisconnectTimeout": @30, - @"ProtocolVersion": @"2.0.0.0", - @"TransportConnectTimeout": @10 - }], nil); - }] negotiate:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; - [[[pmock stub] andDo:^(NSInvocation * invocation) { - __unsafe_unretained void (^ callbackOut)(id * response, NSError *error); - [invocation getArgument: &callbackOut atIndex: 5]; - callbackOut(nil, nil);//SSE just falls back to httpbase, just verify we are allowed through - }] send:[OCMArg any] data:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]]; - + id json = @{ + @"ConnectionId": @"10101", + @"ConnectionToken": @"10101010101", + @"DisconnectTimeout": @30, + @"ProtocolVersion": @"2.0.0.0", + @"TransportConnectTimeout": @10 + }; + [SRMockClientTransport negotiateForTransport:sse statusCode:@200 json:json]; + BOOL failed = NO; @try { diff --git a/SignalR.Client/Transports/SRServerSentEventsTransport.m b/SignalR.Client/Transports/SRServerSentEventsTransport.m index 4e990da4..a92c1516 100644 --- a/SignalR.Client/Transports/SRServerSentEventsTransport.m +++ b/SignalR.Client/Transports/SRServerSentEventsTransport.m @@ -111,7 +111,6 @@ - (void)lostConnection:(id)connection { #pragma mark SSE Transport - (void)open:(id )connection connectionData:(NSString *)connectionData isReconnecting: (BOOL) isReconnecting { - __block SREventSourceStreamReader *eventSource; id parameters = @{ @"transport" : [self name], @"connectionToken" : ([connection connectionToken]) ? [connection connectionToken] : @"", @@ -238,11 +237,11 @@ - (void)open:(id )connection connectionData:(NSString *)c //special case differs from above SRLogServerSentEvents("error: %@", error); [operation cancel];//clean up to avoid duplicates - [eventSource close: error];//clean up -> this should end up in eventSource.closed above + [strongSelf.eventSource close: error];//clean up -> this should end up in eventSource.closed above return;//bail out early as we've taken care of the below } [operation cancel];//clean up to avoid duplicates - [eventSource close];//clean up -> this should end up in eventSource.closed above + [strongSelf.eventSource close];//clean up -> this should end up in eventSource.closed above }]; [self.serverSentEventsOperationQueue addOperation:operation]; }