From c586dc8747882770973b6488c9f5f9e6e3f08d6c Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Thu, 12 Jul 2018 12:03:50 -0700 Subject: Separate Xcode project and tests for GoogleUtilities (#1521) --- .travis.yml | 1 + Example/Core/Tests/FIRAppEnvironmentUtilTest.m | 63 -- Example/Core/Tests/FIRMutableDictionaryTest.m | 87 -- Example/Core/Tests/FIRNetworkTest.m | 993 ----------------- Example/Core/Tests/FIRReachabilityCheckerTest.m | 357 ------ Example/Core/Tests/third_party/GTMHTTPServer.h | 173 --- Example/Core/Tests/third_party/GTMHTTPServer.m | 630 ----------- Example/Firebase.xcodeproj/project.pbxproj | 78 +- .../GoogleUtilities.xcodeproj/project.pbxproj | 1135 ++++++++++++++++++++ .../xcshareddata/xcschemes/Example_iOS.xcscheme | 111 ++ .../xcshareddata/xcschemes/Example_macOS.xcscheme | 101 ++ .../xcshareddata/xcschemes/Example_tvOS.xcscheme | 101 ++ .../xcshareddata/xcschemes/Tests_iOS.xcscheme | 84 ++ .../xcshareddata/xcschemes/Tests_macOS.xcscheme | 90 ++ .../xcshareddata/xcschemes/Tests_tvOS.xcscheme | 81 ++ GoogleUtilities/Example/Podfile | 34 + .../Tests/Environment/GULAppEnvironmentUtilTest.m | 62 ++ .../Example/Tests/Logger/GULLoggerTest.m | 210 ++++ .../Tests/Network/GULMutableDictionaryTest.m | 87 ++ .../Example/Tests/Network/GULNetworkTest.m | 998 +++++++++++++++++ .../Tests/Network/third_party/GTMHTTPServer.h | 173 +++ .../Tests/Network/third_party/GTMHTTPServer.m | 630 +++++++++++ .../Reachability/GULReachabilityCheckerTest.m | 358 ++++++ GoogleUtilities/Example/Tests/Tests-Info.plist | 22 + GoogleUtilities/Example/iOS/AppDelegate.h | 21 + GoogleUtilities/Example/iOS/AppDelegate.m | 55 + .../Example/iOS/Base.lproj/LaunchScreen.storyboard | 27 + .../Example/iOS/Base.lproj/Main.storyboard | 27 + .../Example/iOS/GoogleUtilities-Info.plist | 54 + .../AppIcon.appiconset/Contents.json | 98 ++ GoogleUtilities/Example/iOS/ViewController.h | 19 + GoogleUtilities/Example/iOS/ViewController.m | 33 + GoogleUtilities/Example/iOS/main.m | 22 + GoogleUtilities/Example/macOS/AppDelegate.h | 21 + GoogleUtilities/Example/macOS/AppDelegate.m | 33 + .../Example/macOS/Base.lproj/Main.storyboard | 693 ++++++++++++ GoogleUtilities/Example/macOS/Info.plist | 37 + GoogleUtilities/Example/macOS/ViewController.h | 21 + GoogleUtilities/Example/macOS/ViewController.m | 33 + GoogleUtilities/Example/macOS/main.m | 21 + GoogleUtilities/Example/tvOS/AppDelegate.h | 21 + GoogleUtilities/Example/tvOS/AppDelegate.m | 59 + .../Content.imageset/Contents.json | 11 + .../Back.imagestacklayer/Contents.json | 6 + .../App Icon - App Store.imagestack/Contents.json | 17 + .../Content.imageset/Contents.json | 11 + .../Front.imagestacklayer/Contents.json | 6 + .../Content.imageset/Contents.json | 11 + .../Middle.imagestacklayer/Contents.json | 6 + .../Content.imageset/Contents.json | 16 + .../Back.imagestacklayer/Contents.json | 6 + .../App Icon.imagestack/Contents.json | 17 + .../Content.imageset/Contents.json | 16 + .../Front.imagestacklayer/Contents.json | 6 + .../Content.imageset/Contents.json | 16 + .../Middle.imagestacklayer/Contents.json | 6 + .../Contents.json | 32 + .../Top Shelf Image Wide.imageset/Contents.json | 16 + .../Top Shelf Image.imageset/Contents.json | 16 + .../Example/tvOS/Assets.xcassets/Contents.json | 6 + .../LaunchImage.launchimage/Contents.json | 22 + GoogleUtilities/Example/tvOS/Info.plist | 37 + GoogleUtilities/Example/tvOS/Main.storyboard | 28 + GoogleUtilities/Example/tvOS/ViewController.h | 19 + GoogleUtilities/Example/tvOS/ViewController.m | 33 + GoogleUtilities/Example/tvOS/main.m | 22 + scripts/build.sh | 7 + scripts/if_changed.sh | 2 +- scripts/install_prereqs.sh | 2 + 69 files changed, 5970 insertions(+), 2377 deletions(-) delete mode 100644 Example/Core/Tests/FIRAppEnvironmentUtilTest.m delete mode 100644 Example/Core/Tests/FIRMutableDictionaryTest.m delete mode 100644 Example/Core/Tests/FIRNetworkTest.m delete mode 100644 Example/Core/Tests/FIRReachabilityCheckerTest.m delete mode 100644 Example/Core/Tests/third_party/GTMHTTPServer.h delete mode 100644 Example/Core/Tests/third_party/GTMHTTPServer.m create mode 100644 GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj create mode 100644 GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Example_iOS.xcscheme create mode 100644 GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Example_macOS.xcscheme create mode 100644 GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Example_tvOS.xcscheme create mode 100644 GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Tests_iOS.xcscheme create mode 100644 GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Tests_macOS.xcscheme create mode 100644 GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Tests_tvOS.xcscheme create mode 100644 GoogleUtilities/Example/Podfile create mode 100644 GoogleUtilities/Example/Tests/Environment/GULAppEnvironmentUtilTest.m create mode 100644 GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m create mode 100644 GoogleUtilities/Example/Tests/Network/GULMutableDictionaryTest.m create mode 100644 GoogleUtilities/Example/Tests/Network/GULNetworkTest.m create mode 100644 GoogleUtilities/Example/Tests/Network/third_party/GTMHTTPServer.h create mode 100644 GoogleUtilities/Example/Tests/Network/third_party/GTMHTTPServer.m create mode 100644 GoogleUtilities/Example/Tests/Reachability/GULReachabilityCheckerTest.m create mode 100644 GoogleUtilities/Example/Tests/Tests-Info.plist create mode 100644 GoogleUtilities/Example/iOS/AppDelegate.h create mode 100644 GoogleUtilities/Example/iOS/AppDelegate.m create mode 100644 GoogleUtilities/Example/iOS/Base.lproj/LaunchScreen.storyboard create mode 100644 GoogleUtilities/Example/iOS/Base.lproj/Main.storyboard create mode 100644 GoogleUtilities/Example/iOS/GoogleUtilities-Info.plist create mode 100644 GoogleUtilities/Example/iOS/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 GoogleUtilities/Example/iOS/ViewController.h create mode 100644 GoogleUtilities/Example/iOS/ViewController.m create mode 100644 GoogleUtilities/Example/iOS/main.m create mode 100644 GoogleUtilities/Example/macOS/AppDelegate.h create mode 100644 GoogleUtilities/Example/macOS/AppDelegate.m create mode 100644 GoogleUtilities/Example/macOS/Base.lproj/Main.storyboard create mode 100644 GoogleUtilities/Example/macOS/Info.plist create mode 100644 GoogleUtilities/Example/macOS/ViewController.h create mode 100644 GoogleUtilities/Example/macOS/ViewController.m create mode 100644 GoogleUtilities/Example/macOS/main.m create mode 100644 GoogleUtilities/Example/tvOS/AppDelegate.h create mode 100644 GoogleUtilities/Example/tvOS/AppDelegate.m create mode 100644 GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json create mode 100644 GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json create mode 100644 GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json create mode 100644 GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json create mode 100644 GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json create mode 100644 GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json create mode 100644 GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json create mode 100644 GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json create mode 100644 GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json create mode 100644 GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json create mode 100644 GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json create mode 100644 GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json create mode 100644 GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json create mode 100644 GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json create mode 100644 GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json create mode 100644 GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json create mode 100644 GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json create mode 100644 GoogleUtilities/Example/tvOS/Assets.xcassets/Contents.json create mode 100644 GoogleUtilities/Example/tvOS/Assets.xcassets/LaunchImage.launchimage/Contents.json create mode 100644 GoogleUtilities/Example/tvOS/Info.plist create mode 100644 GoogleUtilities/Example/tvOS/Main.storyboard create mode 100644 GoogleUtilities/Example/tvOS/ViewController.h create mode 100644 GoogleUtilities/Example/tvOS/ViewController.m create mode 100644 GoogleUtilities/Example/tvOS/main.m diff --git a/.travis.yml b/.travis.yml index 934c106..9924b9c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -89,6 +89,7 @@ jobs: before_install: - ./scripts/if_cron.sh ./scripts/install_prereqs.sh script: + - ./scripts/if_cron.sh bundle exec pod lib lint GoogleUtilities.podspec --use-libraries - ./scripts/if_cron.sh bundle exec pod lib lint FirebaseCore.podspec --use-libraries $ALT_SOURCES - ./scripts/if_cron.sh bundle exec pod lib lint FirebaseAuth.podspec --use-libraries $ALT_SOURCES - ./scripts/if_cron.sh bundle exec pod lib lint FirebaseAuthInterop.podspec --use-libraries $ALT_SOURCES diff --git a/Example/Core/Tests/FIRAppEnvironmentUtilTest.m b/Example/Core/Tests/FIRAppEnvironmentUtilTest.m deleted file mode 100644 index 4ac2ef5..0000000 --- a/Example/Core/Tests/FIRAppEnvironmentUtilTest.m +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2018 Google -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import -#import - -#import - -#import "FIRTestCase.h" - -@interface GULAppEnvironmentUtilTest : FIRTestCase - -@property(nonatomic) id processInfoMock; - -@end - -@implementation GULAppEnvironmentUtilTest - -- (void)setUp { - [super setUp]; - - _processInfoMock = OCMPartialMock([NSProcessInfo processInfo]); -} - -- (void)tearDown { - [super tearDown]; - - [_processInfoMock stopMocking]; -} - -- (void)testSystemVersionInfoMajorOnly { - NSOperatingSystemVersion osTen = {.majorVersion = 10, .minorVersion = 0, .patchVersion = 0}; - OCMStub([self.processInfoMock operatingSystemVersion]).andReturn(osTen); - - XCTAssertTrue([[GULAppEnvironmentUtil systemVersion] isEqualToString:@"10.0"]); -} - -- (void)testSystemVersionInfoMajorMinor { - NSOperatingSystemVersion osTenTwo = {.majorVersion = 10, .minorVersion = 2, .patchVersion = 0}; - OCMStub([self.processInfoMock operatingSystemVersion]).andReturn(osTenTwo); - - XCTAssertTrue([[GULAppEnvironmentUtil systemVersion] isEqualToString:@"10.2"]); -} - -- (void)testSystemVersionInfoMajorMinorPatch { - NSOperatingSystemVersion osTenTwoOne = {.majorVersion = 10, .minorVersion = 2, .patchVersion = 1}; - OCMStub([self.processInfoMock operatingSystemVersion]).andReturn(osTenTwoOne); - - XCTAssertTrue([[GULAppEnvironmentUtil systemVersion] isEqualToString:@"10.2.1"]); -} - -@end diff --git a/Example/Core/Tests/FIRMutableDictionaryTest.m b/Example/Core/Tests/FIRMutableDictionaryTest.m deleted file mode 100644 index f6be760..0000000 --- a/Example/Core/Tests/FIRMutableDictionaryTest.m +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2018 Google -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "FIRTestCase.h" - -#import - -const static NSString *const kKey = @"testKey1"; -const static NSString *const kValue = @"testValue1"; -const static NSString *const kKey2 = @"testKey2"; -const static NSString *const kValue2 = @"testValue2"; - -@interface FIRMutableDictionaryTest : FIRTestCase -@property(nonatomic) GULMutableDictionary *dictionary; -@end - -@implementation FIRMutableDictionaryTest - -- (void)setUp { - [super setUp]; - self.dictionary = [[GULMutableDictionary alloc] init]; -} - -- (void)tearDown { - self.dictionary = nil; - [super tearDown]; -} - -- (void)testSetGetAndRemove { - XCTAssertNil([self.dictionary objectForKey:kKey]); - [self.dictionary setObject:kValue forKey:kKey]; - XCTAssertEqual(kValue, [self.dictionary objectForKey:kKey]); - [self.dictionary removeObjectForKey:kKey]; - XCTAssertNil([self.dictionary objectForKey:kKey]); -} - -- (void)testSetGetAndRemoveKeyed { - XCTAssertNil(self.dictionary[kKey]); - self.dictionary[kKey] = kValue; - XCTAssertEqual(kValue, self.dictionary[kKey]); - [self.dictionary removeObjectForKey:kKey]; - XCTAssertNil(self.dictionary[kKey]); -} - -- (void)testRemoveAll { - XCTAssertNil(self.dictionary[kKey]); - XCTAssertNil(self.dictionary[kKey2]); - self.dictionary[kKey] = kValue; - self.dictionary[kKey2] = kValue2; - [self.dictionary removeAllObjects]; - XCTAssertNil(self.dictionary[kKey]); - XCTAssertNil(self.dictionary[kKey2]); -} - -- (void)testCount { - XCTAssertEqual([self.dictionary count], 0); - self.dictionary[kKey] = kValue; - XCTAssertEqual([self.dictionary count], 1); - self.dictionary[kKey2] = kValue2; - XCTAssertEqual([self.dictionary count], 2); - [self.dictionary removeAllObjects]; - XCTAssertEqual([self.dictionary count], 0); -} - -- (void)testUnderlyingDictionary { - XCTAssertEqual([self.dictionary count], 0); - self.dictionary[kKey] = kValue; - self.dictionary[kKey2] = kValue2; - - NSDictionary *dict = self.dictionary.dictionary; - XCTAssertEqual([dict count], 2); - XCTAssertEqual(dict[kKey], kValue); - XCTAssertEqual(dict[kKey2], kValue2); -} - -@end diff --git a/Example/Core/Tests/FIRNetworkTest.m b/Example/Core/Tests/FIRNetworkTest.m deleted file mode 100644 index a1410ee..0000000 --- a/Example/Core/Tests/FIRNetworkTest.m +++ /dev/null @@ -1,993 +0,0 @@ -// Copyright 2018 Google -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "FIRTestCase.h" - -#import "GTMHTTPServer.h" - -#import -#import -#import - -@interface GULNetwork () - -- (void)reachability:(GULReachabilityChecker *)reachability - statusChanged:(GULReachabilityStatus)status; - -@end - -@interface GULNetworkURLSession () - -- (void)maybeRemoveTempFilesAtURL:(NSURL *)tempFile expiringTime:(NSTimeInterval)expiringTime; - -@end - -@interface GULNetworkTest : FIRTestCase -@end - -@implementation GULNetworkTest { - dispatch_queue_t _backgroundQueue; - GULNetwork *_network; - - /// Fake Server. - GTMHTTPServer *_httpServer; - GTMHTTPRequestMessage *_request; - int _statusCode; - - // For network reachability test. - BOOL _fakeNetworkIsReachable; - BOOL _currentNetworkStatus; - GULReachabilityStatus _fakeReachabilityStatus; -} - -#pragma mark - Setup and teardown - -- (void)setUp { - [super setUp]; - - _fakeNetworkIsReachable = YES; - _statusCode = 200; - _request = nil; - - _httpServer = [[GTMHTTPServer alloc] initWithDelegate:self]; - - // Start the server. - NSError *error = nil; - XCTAssertTrue([_httpServer start:&error], @"Failed to start HTTP server: %@", error); - - _network = [[GULNetwork alloc] init]; - _backgroundQueue = dispatch_queue_create("Test queue", DISPATCH_QUEUE_SERIAL); - - _request = nil; -} - -- (void)tearDown { - _network = nil; - _backgroundQueue = nil; - _request = nil; - - [_httpServer stop]; - _httpServer = nil; - - [super tearDown]; -} - -#pragma mark - Test reachability - -- (void)testReachability { - _network.reachabilityDelegate = self; - - id reachability = [_network valueForKey:@"_reachability"]; - XCTAssertNotNil(reachability); - - id reachabilityMock = OCMPartialMock(reachability); - [[[reachabilityMock stub] andCall:@selector(reachabilityStatus) onObject:self] - reachabilityStatus]; - - // Fake scenario with connectivity. - _fakeNetworkIsReachable = YES; - _fakeReachabilityStatus = kGULReachabilityViaWifi; - [_network reachability:reachabilityMock statusChanged:[reachabilityMock reachabilityStatus]]; - XCTAssertTrue([_network isNetworkConnected]); - XCTAssertEqual(_currentNetworkStatus, _fakeNetworkIsReachable); - - // Fake scenario without connectivity. - _fakeNetworkIsReachable = NO; - _fakeReachabilityStatus = kGULReachabilityNotReachable; - [_network reachability:reachabilityMock statusChanged:[reachabilityMock reachabilityStatus]]; - XCTAssertFalse([_network isNetworkConnected]); - XCTAssertEqual(_currentNetworkStatus, _fakeNetworkIsReachable); - - [reachabilityMock stopMocking]; - reachabilityMock = nil; -} - -#pragma mark - Test POST Foreground - -- (void)testSessionNetwork_POST_foreground { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - - NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; - - NSURL *url = - [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/2", _httpServer.port]]; - _statusCode = 200; - - [_network postURL:url - payload:uncompressedData - queue:_backgroundQueue - usingBackgroundSession:NO - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - [self verifyResponse:response error:error]; - [self verifyRequest]; - XCTAssertFalse(_network.hasUploadInProgress, "There must be no pending request"); - [expectation fulfill]; - }]; - XCTAssertTrue(_network.hasUploadInProgress, "There must be a pending request"); - // Wait a little bit so the server has enough time to respond. - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testSessionNetworkShouldReturnError_POST_foreground { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - - NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; - NSURL *url = - [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/3", _httpServer.port]]; - _statusCode = 500; - - [_network postURL:url - payload:uncompressedData - queue:_backgroundQueue - usingBackgroundSession:NO - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - XCTAssertEqual(((NSHTTPURLResponse *)response).statusCode, 500); - XCTAssertFalse(_network.hasUploadInProgress, "There must be no pending request"); - [expectation fulfill]; - }]; - XCTAssertTrue(_network.hasUploadInProgress, "There must be a pending request"); - // Wait a little bit so the server has enough time to respond. - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testNilURLNSURLSession_POST_foreground { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - - NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; - _statusCode = 200; - - [_network postURL:nil - payload:uncompressedData - queue:_backgroundQueue - usingBackgroundSession:NO - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - XCTAssertEqual(error.code, GULErrorCodeNetworkInvalidURL); - XCTAssertFalse(_network.hasUploadInProgress, "There must be no pending request"); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testEmptyURLNSURLSession_POST_foreground { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; - _statusCode = 200; - - [_network postURL:[NSURL URLWithString:@""] - payload:uncompressedData - queue:_backgroundQueue - usingBackgroundSession:NO - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - XCTAssertEqual(error.code, GULErrorCodeNetworkInvalidURL); - XCTAssertFalse(_network.hasUploadInProgress, "There must be no pending request"); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testEmptyPayloadNSURLSession_POST_foreground { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - NSData *uncompressedData = [[NSData alloc] init]; - NSURL *url = - [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/2", _httpServer.port]]; - _statusCode = 200; - - [_network postURL:url - payload:uncompressedData - queue:_backgroundQueue - usingBackgroundSession:NO - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - XCTAssertNil(error); - XCTAssertNotNil(_request); - XCTAssertEqualObjects([_request.URL absoluteString], [url absoluteString]); - XCTAssertFalse(_network.hasUploadInProgress, "There must be no pending request"); - [expectation fulfill]; - }]; - XCTAssertTrue(_network.hasUploadInProgress, "There must be a pending request"); - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testNilQueueNSURLSession_POST_foreground { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - - NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; - NSURL *url = - [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/1", _httpServer.port]]; - _statusCode = 200; - - [_network postURL:url - payload:uncompressedData - queue:nil - usingBackgroundSession:NO - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - [self verifyResponse:response error:error]; - [self verifyRequest]; - [expectation fulfill]; - }]; - - // Wait a little bit so the server has enough time to respond. - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testHasRequestPendingNSURLSession_POST_foreground { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; - NSURL *url = - [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/hasRequestPending", - _httpServer.port]]; - _statusCode = 200; - - [_network postURL:url - payload:uncompressedData - queue:_backgroundQueue - usingBackgroundSession:NO - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - [self verifyResponse:response error:error]; - [self verifyRequest]; - - XCTAssertFalse(_network.hasUploadInProgress, @"hasUploadInProgress must be false"); - [expectation fulfill]; - }]; - - XCTAssertTrue(_network.hasUploadInProgress, @"hasUploadInProgress must be true"); - // Wait a little bit so the server has enough time to respond. - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -#pragma mark - Test POST Background - -- (void)testSessionNetwork_POST_background { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - - NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; - NSURL *url = - [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/2", _httpServer.port]]; - _statusCode = 200; - - [_network postURL:url - payload:uncompressedData - queue:_backgroundQueue - usingBackgroundSession:YES - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - [self verifyResponse:response error:error]; - [self verifyRequest]; - XCTAssertFalse(_network.hasUploadInProgress, "There must be no pending request"); - [expectation fulfill]; - }]; - XCTAssertTrue(_network.hasUploadInProgress, "There must be a pending request"); - // Wait a little bit so the server has enough time to respond. - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testSessionNetworkShouldReturnError_POST_background { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - - NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; - NSURL *url = - [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/3", _httpServer.port]]; - _statusCode = 500; - - [_network postURL:url - payload:uncompressedData - queue:_backgroundQueue - usingBackgroundSession:YES - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - XCTAssertEqual(((NSHTTPURLResponse *)response).statusCode, 500); - XCTAssertFalse(_network.hasUploadInProgress, "There must be no pending request"); - [expectation fulfill]; - }]; - XCTAssertTrue(_network.hasUploadInProgress, "There must be a pending request"); - // Wait a little bit so the server has enough time to respond. - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testNilURLNSURLSession_POST_background { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - - NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; - _statusCode = 200; - - [_network postURL:nil - payload:uncompressedData - queue:_backgroundQueue - usingBackgroundSession:YES - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - XCTAssertEqual(error.code, GULErrorCodeNetworkInvalidURL); - XCTAssertFalse(_network.hasUploadInProgress, "There must be no pending request"); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testEmptyURLNSURLSession_POST_background { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; - _statusCode = 200; - - [_network postURL:[NSURL URLWithString:@""] - payload:uncompressedData - queue:_backgroundQueue - usingBackgroundSession:YES - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - XCTAssertEqual(error.code, GULErrorCodeNetworkInvalidURL); - XCTAssertFalse(_network.hasUploadInProgress, "There must be no pending request"); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testEmptyPayloadNSURLSession_POST_background { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - NSData *uncompressedData = [[NSData alloc] init]; - NSURL *url = - [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/2", _httpServer.port]]; - _statusCode = 200; - - [_network postURL:url - payload:uncompressedData - queue:_backgroundQueue - usingBackgroundSession:YES - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - XCTAssertNil(error); - XCTAssertNotNil(_request); - XCTAssertEqualObjects([_request.URL absoluteString], [url absoluteString]); - XCTAssertFalse(_network.hasUploadInProgress, "There must be no pending request"); - [expectation fulfill]; - }]; - XCTAssertTrue(_network.hasUploadInProgress, "There must be a pending request"); - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testNilQueueNSURLSession_POST_background { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - - NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; - NSURL *url = - [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/1", _httpServer.port]]; - _statusCode = 200; - - [_network postURL:url - payload:uncompressedData - queue:nil - usingBackgroundSession:YES - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - [self verifyResponse:response error:error]; - [self verifyRequest]; - [expectation fulfill]; - }]; - - // Wait a little bit so the server has enough time to respond. - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testHasRequestPendingNSURLSession_POST_background { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; - NSURL *url = - [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/hasRequestPending", - _httpServer.port]]; - _statusCode = 200; - - [_network postURL:url - payload:uncompressedData - queue:_backgroundQueue - usingBackgroundSession:YES - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - [self verifyResponse:response error:error]; - [self verifyRequest]; - - XCTAssertFalse(_network.hasUploadInProgress, @"hasUploadInProgress must be false"); - [expectation fulfill]; - }]; - - XCTAssertTrue(_network.hasUploadInProgress, @"hasUploadInProgress must be true"); - // Wait a little bit so the server has enough time to respond. - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -#pragma mark - GET Methods Foreground - -- (void)testSessionNetworkAsync_GET_foreground { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - - NSURL *url = - [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/2", _httpServer.port]]; - _statusCode = 200; - - [_network getURL:url - headers:nil - queue:_backgroundQueue - usingBackgroundSession:NO - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - XCTAssertNotNil(data); - NSString *responseBody = - [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - XCTAssertEqualObjects(responseBody, @"Hello, World!"); - XCTAssertNil(error); - XCTAssertFalse(_network.hasUploadInProgress, "There must be no pending request"); - [expectation fulfill]; - }]; - XCTAssertTrue(_network.hasUploadInProgress, "There must be a pending request"); - // Wait a little bit so the server has enough time to respond. - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testSessionNetworkShouldReturnError_GET_foreground { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - NSURL *url = - [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/3", _httpServer.port]]; - _statusCode = 500; - - [_network getURL:url - headers:nil - queue:_backgroundQueue - usingBackgroundSession:NO - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - XCTAssertEqual(((NSHTTPURLResponse *)response).statusCode, 500); - XCTAssertFalse(_network.hasUploadInProgress, "There must be no pending request"); - [expectation fulfill]; - }]; - XCTAssertTrue(_network.hasUploadInProgress, "There must be a pending request"); - // Wait a little bit so the server has enough time to respond. - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testNilURLNSURLSession_GET_foreground { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - _statusCode = 200; - - [_network getURL:nil - headers:nil - queue:_backgroundQueue - usingBackgroundSession:NO - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - XCTAssertEqual(error.code, GULErrorCodeNetworkInvalidURL); - XCTAssertFalse(_network.hasUploadInProgress, "There must be no pending request"); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testEmptyURLNSURLSession_GET_foreground { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - _statusCode = 200; - - [_network getURL:[NSURL URLWithString:@""] - headers:nil - queue:_backgroundQueue - usingBackgroundSession:NO - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - XCTAssertEqual(error.code, GULErrorCodeNetworkInvalidURL); - XCTAssertFalse(_network.hasUploadInProgress, "There must be no pending request"); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testNilQueueNSURLSession_GET_foreground { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - - NSURL *url = - [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/1", _httpServer.port]]; - _statusCode = 200; - - [_network getURL:url - headers:nil - queue:nil - usingBackgroundSession:NO - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - XCTAssertNotNil(data); - NSString *responseBody = - [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - XCTAssertEqualObjects(responseBody, @"Hello, World!"); - XCTAssertNil(error); - XCTAssertFalse(_network.hasUploadInProgress, "There must be no pending request"); - [expectation fulfill]; - }]; - XCTAssertTrue(_network.hasUploadInProgress, "There must be a pending request"); - // Wait a little bit so the server has enough time to respond. - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testHasRequestPendingNSURLSession_GET_foreground { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - NSURL *url = - [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/hasRequestPending", - _httpServer.port]]; - _statusCode = 200; - - [_network getURL:url - headers:nil - queue:_backgroundQueue - usingBackgroundSession:NO - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - XCTAssertNotNil(data); - NSString *responseBody = - [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - XCTAssertEqualObjects(responseBody, @"Hello, World!"); - XCTAssertNil(error); - XCTAssertFalse(_network.hasUploadInProgress, @"hasUploadInProgress must be false"); - [expectation fulfill]; - }]; - - XCTAssertTrue(_network.hasUploadInProgress, @"hasUploadInProgress must be true"); - // Wait a little bit so the server has enough time to respond. - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testHeaders_foreground { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - - NSURL *url = - [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/3", _httpServer.port]]; - _statusCode = 200; - - NSDictionary *headers = @{@"Version" : @"123"}; - - [_network getURL:url - headers:headers - queue:_backgroundQueue - usingBackgroundSession:NO - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - XCTAssertNotNil(data); - NSString *responseBody = - [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - XCTAssertEqualObjects(responseBody, @"Hello, World!"); - XCTAssertNil(error); - - NSString *version = [_request.allHeaderFieldValues valueForKey:@"Version"]; - XCTAssertEqualObjects(version, @"123"); - - [expectation fulfill]; - }]; - - // Wait a little bit so the server has enough time to respond. - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -#pragma mark - GET Methods Background - -- (void)testSessionNetworkAsync_GET_background { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - - NSURL *url = - [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/2", _httpServer.port]]; - _statusCode = 200; - - [_network getURL:url - headers:nil - queue:_backgroundQueue - usingBackgroundSession:YES - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - XCTAssertNotNil(data); - NSString *responseBody = - [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - XCTAssertEqualObjects(responseBody, @"Hello, World!"); - XCTAssertNil(error); - XCTAssertFalse(_network.hasUploadInProgress, "There must be no pending request"); - [expectation fulfill]; - }]; - XCTAssertTrue(_network.hasUploadInProgress, "There must be a pending request"); - // Wait a little bit so the server has enough time to respond. - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testSessionNetworkShouldReturnError_GET_background { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - NSURL *url = - [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/3", _httpServer.port]]; - _statusCode = 500; - - [_network getURL:url - headers:nil - queue:_backgroundQueue - usingBackgroundSession:YES - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - XCTAssertEqual(((NSHTTPURLResponse *)response).statusCode, 500); - XCTAssertFalse(_network.hasUploadInProgress, "There must be no pending request"); - [expectation fulfill]; - }]; - XCTAssertTrue(_network.hasUploadInProgress, "There must be a pending request"); - // Wait a little bit so the server has enough time to respond. - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testNilURLNSURLSession_GET_background { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - _statusCode = 200; - - [_network getURL:nil - headers:nil - queue:_backgroundQueue - usingBackgroundSession:YES - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - XCTAssertEqual(error.code, GULErrorCodeNetworkInvalidURL); - XCTAssertFalse(_network.hasUploadInProgress, "There must be no pending request"); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testEmptyURLNSURLSession_GET_background { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - _statusCode = 200; - - [_network getURL:[NSURL URLWithString:@""] - headers:nil - queue:_backgroundQueue - usingBackgroundSession:YES - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - XCTAssertEqual(error.code, GULErrorCodeNetworkInvalidURL); - XCTAssertFalse(_network.hasUploadInProgress, "There must be no pending request"); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testNilQueueNSURLSession_GET_background { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - - NSURL *url = - [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/1", _httpServer.port]]; - _statusCode = 200; - - [_network getURL:url - headers:nil - queue:nil - usingBackgroundSession:YES - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - XCTAssertNotNil(data); - NSString *responseBody = - [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - XCTAssertEqualObjects(responseBody, @"Hello, World!"); - XCTAssertNil(error); - XCTAssertFalse(_network.hasUploadInProgress, "There must be no pending request"); - [expectation fulfill]; - }]; - XCTAssertTrue(_network.hasUploadInProgress, "There must be a pending request"); - // Wait a little bit so the server has enough time to respond. - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testHasRequestPendingNSURLSession_GET_background { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - NSURL *url = - [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/hasRequestPending", - _httpServer.port]]; - _statusCode = 200; - - [_network getURL:url - headers:nil - queue:_backgroundQueue - usingBackgroundSession:YES - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - XCTAssertNotNil(data); - NSString *responseBody = - [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - XCTAssertEqualObjects(responseBody, @"Hello, World!"); - XCTAssertNil(error); - XCTAssertFalse(_network.hasUploadInProgress, @"hasUploadInProgress must be false"); - [expectation fulfill]; - }]; - - XCTAssertTrue(_network.hasUploadInProgress, @"hasUploadInProgress must be true"); - // Wait a little bit so the server has enough time to respond. - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -- (void)testHeaders_background { - XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; - - NSURL *url = - [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/3", _httpServer.port]]; - _statusCode = 200; - - NSDictionary *headers = @{@"Version" : @"123"}; - - [_network getURL:url - headers:headers - queue:_backgroundQueue - usingBackgroundSession:YES - completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - XCTAssertNotNil(data); - NSString *responseBody = - [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - XCTAssertEqualObjects(responseBody, @"Hello, World!"); - XCTAssertNil(error); - - NSString *version = [_request.allHeaderFieldValues valueForKey:@"Version"]; - XCTAssertEqualObjects(version, @"123"); - - [expectation fulfill]; - }]; - - // Wait a little bit so the server has enough time to respond. - [self waitForExpectationsWithTimeout:10 - handler:^(NSError *error) { - if (error) { - XCTFail(@"Timeout Error: %@", error); - } - }]; -} - -#pragma mark - Test clean up files - -- (void)testRemoveExpiredFiles { - NSError *writeError = nil; - NSFileManager *fileManager = [NSFileManager defaultManager]; - - GULNetworkURLSession *session = [[GULNetworkURLSession alloc] - initWithNetworkLoggerDelegate:(id)_network]; - NSArray *paths = - NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); - NSString *applicationSupportDirectory = paths.firstObject; - NSArray *tempPathComponents = @[ - applicationSupportDirectory, kGULNetworkApplicationSupportSubdirectory, - @"GULNetworkTemporaryDirectory" - ]; - NSURL *folderURL = [NSURL fileURLWithPathComponents:tempPathComponents]; - [fileManager createDirectoryAtURL:folderURL - withIntermediateDirectories:YES - attributes:nil - error:&writeError]; - - NSURL *tempFile1 = [folderURL URLByAppendingPathComponent:@"FIRUpload_temp_123"]; - [self createTempFileAtURL:tempFile1]; - NSURL *tempFile2 = [folderURL URLByAppendingPathComponent:@"FIRUpload_temp_456"]; - [self createTempFileAtURL:tempFile2]; - - XCTAssertTrue([fileManager fileExistsAtPath:tempFile1.path]); - XCTAssertTrue([fileManager fileExistsAtPath:tempFile2.path]); - - NSDate *now = - [[NSDate date] dateByAddingTimeInterval:1]; // Start mocking the clock to avoid flakiness. - id mockDate = OCMStrictClassMock([NSDate class]); - [[[mockDate stub] andReturn:now] date]; - - // The file should not be removed since it is not expired yet. - [session maybeRemoveTempFilesAtURL:folderURL expiringTime:20]; - XCTAssertTrue([fileManager fileExistsAtPath:tempFile1.path]); - XCTAssertTrue([fileManager fileExistsAtPath:tempFile2.path]); - - [mockDate stopMocking]; - mockDate = nil; - - now = [[NSDate date] dateByAddingTimeInterval:100]; // Move forward in time 100s. - mockDate = OCMStrictClassMock([NSDate class]); - [[[mockDate stub] andReturn:now] date]; - - [session maybeRemoveTempFilesAtURL:folderURL expiringTime:20]; - XCTAssertFalse([fileManager fileExistsAtPath:tempFile1.path]); - XCTAssertFalse([fileManager fileExistsAtPath:tempFile2.path]); - [mockDate stopMocking]; - mockDate = nil; -} - -#pragma mark - Internal Methods - -- (void)createTempFileAtURL:(NSURL *)fileURL { - // Create a dictionary and write it to file. - NSDictionary *someContent = @{@"object" : @"key"}; - [someContent writeToURL:fileURL atomically:YES]; -} - -- (void)verifyResponse:(NSHTTPURLResponse *)response error:(NSError *)error { - XCTAssertNil(error, @"Error is not expected"); - XCTAssertNotNil(response, @"Error is not expected"); -} - -- (void)verifyRequest { - XCTAssertNotNil(_request, @"Request cannot be nil"); - - // Test whether the request is compressed correctly. - NSData *requestBody = [_request body]; - NSData *decompressedRequestData = [NSData gul_dataByInflatingGzippedData:requestBody error:NULL]; - NSString *requestString = - [[NSString alloc] initWithData:decompressedRequestData encoding:NSUTF8StringEncoding]; - XCTAssertEqualObjects(requestString, @"Google", @"Request is not compressed correctly."); - - // The request has to be a POST. - XCTAssertEqualObjects([_request method], @"POST", @"Request method has to be POST"); - - // Content length has to be set correctly. - NSString *contentLength = [_request.allHeaderFieldValues valueForKey:@"Content-Length"]; - XCTAssertEqualObjects(contentLength, @"26", @"Content Length is incorrect"); - - NSString *contentEncoding = [_request.allHeaderFieldValues valueForKey:@"Content-Encoding"]; - XCTAssertEqualObjects(contentEncoding, @"gzip", @"Content Encoding is incorrect"); -} - -#pragma mark - Helper Methods - -- (GTMHTTPResponseMessage *)httpServer:(GTMHTTPServer *)server - handleRequest:(GTMHTTPRequestMessage *)request { - _request = request; - - NSData *html = - [@"Hello, World!" dataUsingEncoding:NSUTF8StringEncoding]; - return [GTMHTTPResponseMessage responseWithBody:html - contentType:@"text/html; charset=UTF-8" - statusCode:_statusCode]; -} - -- (BOOL)isReachable { - return _fakeNetworkIsReachable; -} - -- (GULReachabilityStatus)reachabilityStatus { - return _fakeReachabilityStatus; -} - -#pragma mark - FIRReachabilityDelegate - -- (void)reachabilityDidChange { - _currentNetworkStatus = _fakeNetworkIsReachable; -} - -@end diff --git a/Example/Core/Tests/FIRReachabilityCheckerTest.m b/Example/Core/Tests/FIRReachabilityCheckerTest.m deleted file mode 100644 index 2f447d3..0000000 --- a/Example/Core/Tests/FIRReachabilityCheckerTest.m +++ /dev/null @@ -1,357 +0,0 @@ -// Copyright 2018 Google -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import "FIRTestCase.h" - -#import -#import "GULReachabilityChecker+Internal.h" - -@interface GULReachabilityCheckerTest : FIRTestCase { - @private - GULReachabilityChecker *checker_; - NSMutableArray *statuses_; - BOOL createFail_; - BOOL setCallbackFail_; - BOOL scheduleUnscheduleFail_; -} - -- (void *)createReachabilityWithAllocator:(CFAllocatorRef)allocator withName:(const char *)hostname; -- (BOOL)reachability:(const void *)reachability - setCallback:(SCNetworkReachabilityCallBack)callback - withContext:(SCNetworkReachabilityContext *)context; -- (BOOL)scheduleReachability:(const void *)reachability - runLoop:(CFRunLoopRef)runLoop - runLoopMode:(CFStringRef)runLoopMode; -- (BOOL)unscheduleReachability:(const void *)reachability - runLoop:(CFRunLoopRef)runLoop - runLoopMode:(CFStringRef)runLoopMode; -- (void)releaseReachability:(const void *)reachability; -@end - -static NSString *const kHostname = @"www.google.com"; -static const void *kFakeReachabilityObject = (const void *)0x8badf00d; - -static GULReachabilityCheckerTest *FakeReachabilityTest = nil; - -static struct { - int callsMade; - int createCall; - int setCallbackCall; - int scheduleCall; - int unscheduleCall; - int releaseCall; - void (*callback)(SCNetworkReachabilityRef, SCNetworkReachabilityFlags, void *info); - void *callbackInfo; -} FakeReachability; - -static SCNetworkReachabilityRef ReachabilityCreateWithName(CFAllocatorRef allocator, - const char *hostname) { - return (SCNetworkReachabilityRef) - [FakeReachabilityTest createReachabilityWithAllocator:allocator withName:hostname]; -} - -static Boolean ReachabilitySetCallback(SCNetworkReachabilityRef reachability, - SCNetworkReachabilityCallBack callback, - SCNetworkReachabilityContext *context) { - return [FakeReachabilityTest reachability:reachability setCallback:callback withContext:context]; -} - -static Boolean ReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef reachability, - CFRunLoopRef runLoop, - CFStringRef runLoopMode) { - return [FakeReachabilityTest scheduleReachability:reachability - runLoop:runLoop - runLoopMode:runLoopMode]; -} - -static Boolean ReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef reachability, - CFRunLoopRef runLoop, - CFStringRef runLoopMode) { - return [FakeReachabilityTest unscheduleReachability:reachability - runLoop:runLoop - runLoopMode:runLoopMode]; -} - -static void ReachabilityRelease(CFTypeRef reachability) { - [FakeReachabilityTest releaseReachability:reachability]; -} - -static const struct GULReachabilityApi kTestReachabilityApi = { - ReachabilityCreateWithName, ReachabilitySetCallback, ReachabilityScheduleWithRunLoop, - ReachabilityUnscheduleFromRunLoop, ReachabilityRelease, -}; - -@implementation GULReachabilityCheckerTest - -- (void)resetFakeReachability { - FakeReachabilityTest = self; - FakeReachability.callsMade = 0; - FakeReachability.createCall = -1; - FakeReachability.setCallbackCall = -1; - FakeReachability.scheduleCall = -1; - FakeReachability.unscheduleCall = -1; - FakeReachability.releaseCall = -1; - FakeReachability.callback = NULL; - FakeReachability.callbackInfo = NULL; -} - -- (void)setUp { - [super setUp]; - - [self resetFakeReachability]; - createFail_ = NO; - setCallbackFail_ = NO; - scheduleUnscheduleFail_ = NO; - - checker_ = [[GULReachabilityChecker alloc] initWithReachabilityDelegate:self withHost:kHostname]; - statuses_ = [[NSMutableArray alloc] init]; -} - -- (void *)createReachabilityWithAllocator:(CFAllocatorRef)allocator - withName:(const char *)hostname { - XCTAssertTrue(allocator == kCFAllocatorDefault, @""); - XCTAssertEqual(strcmp(hostname, [kHostname UTF8String]), 0, @""); - XCTAssertEqual(FakeReachability.callsMade, 0, @"create call must always come first."); - XCTAssertEqual(FakeReachability.createCall, -1, @"create call must only be called once."); - FakeReachability.createCall = ++FakeReachability.callsMade; - return createFail_ ? NULL : (void *)kFakeReachabilityObject; -} - -- (BOOL)reachability:(const void *)reachability - setCallback:(SCNetworkReachabilityCallBack)callback - withContext:(SCNetworkReachabilityContext *)context { - XCTAssertEqual(reachability, kFakeReachabilityObject, @"got bad object"); - XCTAssertEqual((int)context->version, 0, @""); - XCTAssertEqual(context->info, (__bridge void *)checker_, @""); - XCTAssertEqual((void *)context->retain, NULL, @""); - XCTAssertEqual((void *)context->release, NULL, @""); - XCTAssertEqual((void *)context->copyDescription, NULL, @""); - XCTAssertEqual(FakeReachability.setCallbackCall, -1, @"setCallback should only be called once."); - FakeReachability.setCallbackCall = ++FakeReachability.callsMade; - XCTAssertTrue(callback != NULL, @""); - FakeReachability.callback = callback; - XCTAssertTrue(context->info != NULL, @""); - FakeReachability.callbackInfo = context->info; - return setCallbackFail_ ? NO : YES; -} - -- (BOOL)scheduleReachability:(const void *)reachability - runLoop:(CFRunLoopRef)runLoop - runLoopMode:(CFStringRef)runLoopMode { - XCTAssertEqual(reachability, kFakeReachabilityObject, @"got bad object"); - XCTAssertEqual(runLoop, CFRunLoopGetMain(), @"bad run loop"); - XCTAssertEqualObjects((__bridge NSString *)runLoopMode, - (__bridge NSString *)kCFRunLoopCommonModes, @"bad run loop mode"); - XCTAssertEqual(FakeReachability.scheduleCall, -1, - @"scheduleWithRunLoop should only be called once."); - FakeReachability.scheduleCall = ++FakeReachability.callsMade; - return scheduleUnscheduleFail_ ? NO : YES; -} - -- (BOOL)unscheduleReachability:(const void *)reachability - runLoop:(CFRunLoopRef)runLoop - runLoopMode:(CFStringRef)runLoopMode { - XCTAssertEqual(reachability, kFakeReachabilityObject, @"got bad object"); - XCTAssertEqual(runLoop, CFRunLoopGetMain(), @"bad run loop"); - XCTAssertEqualObjects((__bridge NSString *)runLoopMode, - (__bridge NSString *)kCFRunLoopCommonModes, @"bad run loop mode"); - XCTAssertEqual(FakeReachability.unscheduleCall, -1, - @"unscheduleFromRunLoop should only be called once."); - FakeReachability.unscheduleCall = ++FakeReachability.callsMade; - return scheduleUnscheduleFail_ ? NO : YES; -} - -- (void)releaseReachability:(const void *)reachability { - XCTAssertEqual(reachability, kFakeReachabilityObject, @"got bad object"); - XCTAssertEqual(FakeReachability.releaseCall, -1, @"release should only be called once."); - FakeReachability.releaseCall = ++FakeReachability.callsMade; -} - -- (void)reachability:(GULReachabilityChecker *)reachability - statusChanged:(GULReachabilityStatus)status { - [statuses_ addObject:[NSNumber numberWithInt:(int)status]]; -} - -#pragma mark - Test - -- (void)testApiHappyPath { - [checker_ setReachabilityApi:&kTestReachabilityApi]; - XCTAssertEqual([checker_ reachabilityApi], &kTestReachabilityApi, @""); - - XCTAssertFalse(checker_.isActive, @""); - XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); - - XCTAssertTrue([checker_ start], @""); - - XCTAssertTrue(checker_.isActive, @""); - XCTAssertEqual([statuses_ count], (NSUInteger)0, @""); - XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); - - FakeReachability.callback(kFakeReachabilityObject, 0, FakeReachability.callbackInfo); - - XCTAssertEqual([statuses_ count], (NSUInteger)1, @""); - XCTAssertEqual([(NSNumber *)[statuses_ objectAtIndex:0] intValue], - (int)kGULReachabilityNotReachable, @""); - XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityNotReachable, @""); - - FakeReachability.callback(kFakeReachabilityObject, kSCNetworkReachabilityFlagsReachable, - FakeReachability.callbackInfo); - - XCTAssertEqual([statuses_ count], (NSUInteger)2, @""); - XCTAssertEqual([(NSNumber *)[statuses_ objectAtIndex:1] intValue], (int)kGULReachabilityViaWifi, - @""); - XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityViaWifi, @""); - - FakeReachability.callback( - kFakeReachabilityObject, - kSCNetworkReachabilityFlagsReachable | kSCNetworkReachabilityFlagsConnectionRequired, - FakeReachability.callbackInfo); - - XCTAssertEqual([statuses_ count], (NSUInteger)3, @""); - XCTAssertEqual([(NSNumber *)[statuses_ objectAtIndex:2] intValue], - (int)kGULReachabilityNotReachable, @""); - XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityNotReachable, @""); - -#if TARGET_OS_IOS || TARGET_OS_TV - FakeReachability.callback( - kFakeReachabilityObject, - kSCNetworkReachabilityFlagsReachable | kSCNetworkReachabilityFlagsIsWWAN, - FakeReachability.callbackInfo); - - XCTAssertEqual([statuses_ count], (NSUInteger)4, @""); - XCTAssertEqual([(NSNumber *)[statuses_ objectAtIndex:3] intValue], - (int)kGULReachabilityViaCellular, @""); - XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityViaCellular, @""); - - FakeReachability.callback(kFakeReachabilityObject, kSCNetworkReachabilityFlagsIsWWAN, - FakeReachability.callbackInfo); - - XCTAssertEqual([statuses_ count], (NSUInteger)5, @""); - XCTAssertEqual([(NSNumber *)[statuses_ objectAtIndex:4] intValue], - (int)kGULReachabilityNotReachable, @""); - XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityNotReachable, @""); -#endif - - [checker_ stop]; - - XCTAssertFalse(checker_.isActive, @""); - XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); - - XCTAssertEqual(FakeReachability.callsMade, 5, @""); - - XCTAssertEqual(FakeReachability.createCall, 1, @""); - XCTAssertEqual(FakeReachability.setCallbackCall, 2, @""); - XCTAssertEqual(FakeReachability.scheduleCall, 3, @""); - XCTAssertEqual(FakeReachability.unscheduleCall, 4, @""); - XCTAssertEqual(FakeReachability.releaseCall, 5, @""); -} - -- (void)testApiCreateFail { - [checker_ setReachabilityApi:&kTestReachabilityApi]; - XCTAssertEqual([checker_ reachabilityApi], &kTestReachabilityApi, @""); - - createFail_ = YES; - - XCTAssertFalse(checker_.isActive, @""); - XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); - - XCTAssertFalse([checker_ start], @""); - - XCTAssertFalse(checker_.isActive, @""); - XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); - - [checker_ stop]; - - XCTAssertFalse(checker_.isActive, @""); - XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); - - XCTAssertEqual(FakeReachability.callsMade, 1, @""); - - XCTAssertEqual(FakeReachability.createCall, 1, @""); - XCTAssertEqual(FakeReachability.setCallbackCall, -1, @""); - XCTAssertEqual(FakeReachability.scheduleCall, -1, @""); - XCTAssertEqual(FakeReachability.unscheduleCall, -1, @""); - XCTAssertEqual(FakeReachability.releaseCall, -1, @""); - - XCTAssertEqual([statuses_ count], (NSUInteger)0, @""); -} - -- (void)testApiCallbackFail { - [checker_ setReachabilityApi:&kTestReachabilityApi]; - XCTAssertEqual([checker_ reachabilityApi], &kTestReachabilityApi, @""); - - setCallbackFail_ = YES; - - XCTAssertFalse(checker_.isActive, @""); - XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); - - XCTAssertFalse([checker_ start], @""); - - XCTAssertFalse(checker_.isActive, @""); - XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); - - [checker_ stop]; - - XCTAssertFalse(checker_.isActive, @""); - XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); - - XCTAssertEqual(FakeReachability.callsMade, 3, @""); - - XCTAssertEqual(FakeReachability.createCall, 1, @""); - XCTAssertEqual(FakeReachability.setCallbackCall, 2, @""); - XCTAssertEqual(FakeReachability.scheduleCall, -1, @""); - XCTAssertEqual(FakeReachability.unscheduleCall, -1, @""); - XCTAssertEqual(FakeReachability.releaseCall, 3, @""); - - XCTAssertEqual([statuses_ count], (NSUInteger)0, @""); -} - -- (void)testApiScheduleFail { - [checker_ setReachabilityApi:&kTestReachabilityApi]; - XCTAssertEqual([checker_ reachabilityApi], &kTestReachabilityApi, @""); - - scheduleUnscheduleFail_ = YES; - - XCTAssertFalse(checker_.isActive, @""); - XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); - - XCTAssertFalse([checker_ start], @""); - - XCTAssertFalse(checker_.isActive, @""); - XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); - - [checker_ stop]; - - XCTAssertFalse(checker_.isActive, @""); - XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); - - XCTAssertEqual(FakeReachability.callsMade, 4, @""); - - XCTAssertEqual(FakeReachability.createCall, 1, @""); - XCTAssertEqual(FakeReachability.setCallbackCall, 2, @""); - XCTAssertEqual(FakeReachability.scheduleCall, 3, @""); - XCTAssertEqual(FakeReachability.unscheduleCall, -1, @""); - XCTAssertEqual(FakeReachability.releaseCall, 4, @""); - - XCTAssertEqual([statuses_ count], (NSUInteger)0, @""); -} - -- (void)testBadHost { - XCTAssertNil([[GULReachabilityChecker alloc] initWithReachabilityDelegate:self withHost:nil], - @"Creating a checker with nil hostname must fail."); - XCTAssertNil([[GULReachabilityChecker alloc] initWithReachabilityDelegate:self withHost:@""], - @"Creating a checker with empty hostname must fail."); -} - -@end diff --git a/Example/Core/Tests/third_party/GTMHTTPServer.h b/Example/Core/Tests/third_party/GTMHTTPServer.h deleted file mode 100644 index cc51207..0000000 --- a/Example/Core/Tests/third_party/GTMHTTPServer.h +++ /dev/null @@ -1,173 +0,0 @@ -/* Copyright 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// -// GTMHTTPServer.h -// -// This is a *very* *simple* webserver that can be built into something, it is -// not meant to stand up a site, it sends all requests to its delegate for -// processing on the main thread. It does not support pipelining, etc. It's -// great for places where you need a simple webserver to unittest some code -// that hits a server. -// -// NOTE: there are several TODOs left in here as markers for things that could -// be done if one wanted to add more to this class. -// -// Based a little on HTTPServer, part of the CocoaHTTPServer sample code found at -// https://opensource.apple.com/source/HTTPServer/HTTPServer-11/CocoaHTTPServer/ -// License for the CocoaHTTPServer sample code: -// -// Software License Agreement (BSD License) -// -// Copyright (c) 2011, Deusty, LLC -// All rights reserved. -// -// Redistribution and use of this software in source and binary forms, -// with or without modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above -// copyright notice, this list of conditions and the -// following disclaimer. -// -// * Neither the name of Deusty nor the names of its -// contributors may be used to endorse or promote products -// derived from this software without specific prior -// written permission of Deusty, LLC. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR -// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -#import - -#if GTM_IPHONE_SDK -#import -#endif // GTM_IPHONE_SDK - -// Global contants needed for errors from start - -#undef _EXTERN -#undef _INITIALIZE_AS -#ifdef GTMHTTPSERVER_DEFINE_GLOBALS -#define _EXTERN -#define _INITIALIZE_AS(x) = x -#else -#define _EXTERN extern -#define _INITIALIZE_AS(x) -#endif - -_EXTERN NSString *const kGTMHTTPServerErrorDomain - _INITIALIZE_AS(@"com.google.mactoolbox.HTTPServerDomain"); -enum { - kGTMHTTPServerSocketCreateFailedError = -100, - kGTMHTTPServerBindFailedError = -101, - kGTMHTTPServerListenFailedError = -102, - kGTMHTTPServerHandleCreateFailedError = -103, -}; - -@class GTMHTTPRequestMessage, GTMHTTPResponseMessage; - -// ---------------------------------------------------------------------------- - -// See comment at top of file for the intened use of this class. -@interface GTMHTTPServer : NSObject { - @private - id delegate_; // WEAK - uint16_t port_; - BOOL reusePort_; - BOOL localhostOnly_; - NSFileHandle *listenHandle_; - NSMutableArray *connections_; -} - -// The delegate must support the httpServer:handleRequest: method in -// NSObject(GTMHTTPServerDelegateMethods) below. -- (id)initWithDelegate:(id)delegate; - -- (id)delegate; - -// Passing port zero will let one get assigned. -- (uint16_t)port; -- (void)setPort:(uint16_t)port; - -// Controls listening socket behavior: SO_REUSEADDR vs SO_REUSEPORT. -// The default is NO (SO_REUSEADDR) -- (BOOL)reusePort; -- (void)setReusePort:(BOOL)reusePort; - -// Receive connections on the localHost loopback address only or on all -// interfaces for this machine. The default is to only listen on localhost. -- (BOOL)localhostOnly; -- (void)setLocalhostOnly:(BOOL)yesno; - -// Start/Stop the web server. If there is an error starting up the server, |NO| -// is returned, and the specific startup failure can be returned in |error| (see -// above for the error domain and error codes). If the server is started, |YES| -// is returned and the server's delegate is called for any requests that come -// in. -- (BOOL)start:(NSError **)error; -- (void)stop; - -// returns the number of requests currently active in the server (i.e.-being -// read in, sent replies). -- (NSUInteger)activeRequestCount; - -@end - -@interface NSObject (GTMHTTPServerDelegateMethods) -- (GTMHTTPResponseMessage *)httpServer:(GTMHTTPServer *)server - handleRequest:(GTMHTTPRequestMessage *)request; -@end - -// ---------------------------------------------------------------------------- - -// Encapsulates an http request, one of these is sent to the server's delegate -// for each request. -@interface GTMHTTPRequestMessage : NSObject { - @private - CFHTTPMessageRef message_; -} -- (NSString *)version; -- (NSURL *)URL; -- (NSString *)method; -- (NSData *)body; -- (NSDictionary *)allHeaderFieldValues; -@end - -// ---------------------------------------------------------------------------- - -// Encapsulates an http response, the server's delegate should return one for -// each request received. -@interface GTMHTTPResponseMessage : NSObject { - @private - CFHTTPMessageRef message_; -} -+ (instancetype)responseWithString:(NSString *)plainText; -+ (instancetype)responseWithHTMLString:(NSString *)htmlString; -+ (instancetype)responseWithBody:(NSData *)body - contentType:(NSString *)contentType - statusCode:(int)statusCode; -+ (instancetype)emptyResponseWithCode:(int)statusCode; -// TODO: class method for redirections? -// TODO: add helper for expire/no-cache -- (void)setValue:(NSString *)value forHeaderField:(NSString *)headerField; -- (void)setHeaderValuesFromDictionary:(NSDictionary *)dict; -@end diff --git a/Example/Core/Tests/third_party/GTMHTTPServer.m b/Example/Core/Tests/third_party/GTMHTTPServer.m deleted file mode 100644 index 526d8c5..0000000 --- a/Example/Core/Tests/third_party/GTMHTTPServer.m +++ /dev/null @@ -1,630 +0,0 @@ -/* Copyright 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// -// Based a little on HTTPServer, part of the CocoaHTTPServer sample code found at -// https://opensource.apple.com/source/HTTPServer/HTTPServer-11/CocoaHTTPServer/ -// License for the CocoaHTTPServer sample code: -// -// Software License Agreement (BSD License) -// -// Copyright (c) 2011, Deusty, LLC -// All rights reserved. -// -// Redistribution and use of this software in source and binary forms, -// with or without modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above -// copyright notice, this list of conditions and the -// following disclaimer. -// -// * Neither the name of Deusty nor the names of its -// contributors may be used to endorse or promote products -// derived from this software without specific prior -// written permission of Deusty, LLC. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR -// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// - -#import -#import -#import - -#define GTMHTTPSERVER_DEFINE_GLOBALS -#import "GTMHTTPServer.h" - -// avoid some of GTM's promiscuous dependencies -#ifndef _GTMDevLog -#define _GTMDevLog NSLog -#endif - -#ifndef GTM_STATIC_CAST -#define GTM_STATIC_CAST(type, object) ((type *)(object)) -#endif - -#ifndef GTMCFAutorelease -#define GTMCFAutorelease(x) ([(id)x autorelease]) -#endif - -@interface GTMHTTPServer (PrivateMethods) -- (void)acceptedConnectionNotification:(NSNotification *)notification; -- (NSMutableDictionary *)connectionWithFileHandle:(NSFileHandle *)fileHandle; -- (void)dataAvailableNotification:(NSNotification *)notification; -- (NSMutableDictionary *)lookupConnection:(NSFileHandle *)fileHandle; -- (void)closeConnection:(NSMutableDictionary *)connDict; -- (void)sendResponseOnNewThread:(NSMutableDictionary *)connDict; -- (void)sentResponse:(NSMutableDictionary *)connDict; -@end - -// keys for our connection dictionaries -static NSString *kFileHandle = @"FileHandle"; -static NSString *kRequest = @"Request"; -static NSString *kResponse = @"Response"; - -@interface GTMHTTPRequestMessage (PrivateHelpers) -- (BOOL)isHeaderComplete; -- (BOOL)appendData:(NSData *)data; -- (NSString *)headerFieldValueForKey:(NSString *)key; -- (UInt32)contentLength; -- (void)setBody:(NSData *)body; -@end - -@interface GTMHTTPResponseMessage () -- (id)initWithBody:(NSData *)body contentType:(NSString *)contentType statusCode:(int)statusCode; -- (NSData *)serializedData; -@end - -@implementation GTMHTTPServer - -- (id)init { - return [self initWithDelegate:nil]; -} - -- (id)initWithDelegate:(id)delegate { - self = [super init]; - if (self) { - if (!delegate) { - _GTMDevLog(@"missing delegate"); - [self release]; - return nil; - } - delegate_ = delegate; - -#ifndef NS_BLOCK_ASSERTIONS - BOOL isDelegateOK = [delegate_ respondsToSelector:@selector(httpServer:handleRequest:)]; - NSAssert(isDelegateOK, @"GTMHTTPServer delegate lacks handleRequest sel"); -#endif - - localhostOnly_ = YES; - connections_ = [[NSMutableArray alloc] init]; - } - return self; -} - -- (void)dealloc { - [self stop]; - [connections_ release]; - [super dealloc]; -} - -#if !TARGET_OS_IPHONE -- (void)finalize { - [self stop]; - [super finalize]; -} -#endif - -- (id)delegate { - return delegate_; -} - -- (uint16_t)port { - return port_; -} - -- (void)setPort:(uint16_t)port { - port_ = port; -} - -- (BOOL)reusePort { - return reusePort_; -} - -- (void)setReusePort:(BOOL)yesno { - reusePort_ = yesno; -} - -- (BOOL)localhostOnly { - return localhostOnly_; -} - -- (void)setLocalhostOnly:(BOOL)yesno { - localhostOnly_ = yesno; -} - -- (BOOL)start:(NSError **)error { - NSAssert(listenHandle_ == nil, @"start called when we already have a listenHandle_"); - - if (error) *error = NULL; - - NSInteger startFailureCode = 0; - int fd = socket(AF_INET, SOCK_STREAM, 0); - if (fd <= 0) { - // COV_NF_START - we'd need to use up *all* sockets to test this? - startFailureCode = kGTMHTTPServerSocketCreateFailedError; - goto startFailed; - // COV_NF_END - } - - // enable address reuse quicker after we are done w/ our socket - int yes = 1; - int sock_opt = reusePort_ ? SO_REUSEPORT : SO_REUSEADDR; - if (setsockopt(fd, SOL_SOCKET, sock_opt, (void *)&yes, (socklen_t)sizeof(yes)) != 0) { - _GTMDevLog(@"failed to mark the socket as reusable"); // COV_NF_LINE - } - - // bind - struct sockaddr_in addr; - bzero(&addr, sizeof(addr)); - addr.sin_len = sizeof(addr); - addr.sin_family = AF_INET; - addr.sin_port = htons(port_); - if (localhostOnly_) { - addr.sin_addr.s_addr = htonl(0x7F000001); - } else { - // COV_NF_START - testing this could cause a leopard firewall prompt during tests. - addr.sin_addr.s_addr = htonl(INADDR_ANY); - // COV_NF_END - } - if (bind(fd, (struct sockaddr *)(&addr), (socklen_t)sizeof(addr)) != 0) { - startFailureCode = kGTMHTTPServerBindFailedError; - goto startFailed; - } - - // collect the port back out - if (port_ == 0) { - socklen_t len = (socklen_t)sizeof(addr); - if (getsockname(fd, (struct sockaddr *)(&addr), &len) == 0) { - port_ = ntohs(addr.sin_port); - } - } - - // tell it to listen for connections - if (listen(fd, 5) != 0) { - // COV_NF_START - startFailureCode = kGTMHTTPServerListenFailedError; - goto startFailed; - // COV_NF_END - } - - // now use a filehandle to accept connections - listenHandle_ = [[NSFileHandle alloc] initWithFileDescriptor:fd closeOnDealloc:YES]; - if (listenHandle_ == nil) { - // COV_NF_START - we'd need to run out of memory to test this? - startFailureCode = kGTMHTTPServerHandleCreateFailedError; - goto startFailed; - // COV_NF_END - } - - // setup notifications for connects - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - [center addObserver:self - selector:@selector(acceptedConnectionNotification:) - name:NSFileHandleConnectionAcceptedNotification - object:listenHandle_]; - [listenHandle_ acceptConnectionInBackgroundAndNotify]; - - // TODO: maybe hit the delegate incase it wants to register w/ NSNetService, - // or just know we're up and running? - - return YES; - -startFailed: - if (error) { - *error = [[[NSError alloc] initWithDomain:kGTMHTTPServerErrorDomain - code:startFailureCode - userInfo:nil] autorelease]; - } - if (fd > 0) { - close(fd); - } - return NO; -} - -- (void)stop { - if (listenHandle_) { - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - [center removeObserver:self - name:NSFileHandleConnectionAcceptedNotification - object:listenHandle_]; - [listenHandle_ release]; - listenHandle_ = nil; - // TODO: maybe hit the delegate in case it wants to unregister w/ - // NSNetService, or just know we've stopped running? - } - [connections_ removeAllObjects]; -} - -- (NSUInteger)activeRequestCount { - return [connections_ count]; -} - -- (NSString *)description { - NSString *result = - [NSString stringWithFormat:@"%@<%p>{ port=%d localHostOnly=%@ status=%@ }", [self class], - self, port_, (localhostOnly_ ? @"YES" : @"NO"), - (listenHandle_ != nil ? @"Started" : @"Stopped")]; - return result; -} - -@end - -@implementation GTMHTTPServer (PrivateMethods) - -- (void)acceptedConnectionNotification:(NSNotification *)notification { - NSDictionary *userInfo = [notification userInfo]; - NSFileHandle *newConnection = [userInfo objectForKey:NSFileHandleNotificationFileHandleItem]; - NSAssert1(newConnection != nil, @"failed to get the connection in the notification: %@", - notification); - - // make sure we accept more... - [listenHandle_ acceptConnectionInBackgroundAndNotify]; - - // TODO: could let the delegate look at the address, before we start working - // on it. - - NSMutableDictionary *connDict = [self connectionWithFileHandle:newConnection]; - [connections_ addObject:connDict]; -} - -- (NSMutableDictionary *)connectionWithFileHandle:(NSFileHandle *)fileHandle { - NSMutableDictionary *result = [NSMutableDictionary dictionary]; - - [result setObject:fileHandle forKey:kFileHandle]; - - GTMHTTPRequestMessage *request = [[[GTMHTTPRequestMessage alloc] init] autorelease]; - [result setObject:request forKey:kRequest]; - - // setup for data notifications - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - [center addObserver:self - selector:@selector(dataAvailableNotification:) - name:NSFileHandleReadCompletionNotification - object:fileHandle]; - [fileHandle readInBackgroundAndNotify]; - - return result; -} - -- (void)dataAvailableNotification:(NSNotification *)notification { - NSFileHandle *connectionHandle = GTM_STATIC_CAST(NSFileHandle, [notification object]); - NSMutableDictionary *connDict = [self lookupConnection:connectionHandle]; - if (connDict == nil) return; // we are no longer tracking this one - - NSDictionary *userInfo = [notification userInfo]; - NSData *readData = [userInfo objectForKey:NSFileHandleNotificationDataItem]; - if ([readData length] == 0) { - // remote side closed - [self closeConnection:connDict]; - return; - } - - // Use a local pool to keep memory down incase the runloop we're in doesn't - // drain until it gets a UI event. - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - @try { - // Like Apple's sample, we just keep adding data until we get a full header - // and any referenced body. - - GTMHTTPRequestMessage *request = [connDict objectForKey:kRequest]; - [request appendData:readData]; - - // Is the header complete yet? - if (![request isHeaderComplete]) { - // more data... - [connectionHandle readInBackgroundAndNotify]; - } else { - // Do we have all the body? - UInt32 contentLength = [request contentLength]; - NSData *body = [request body]; - NSUInteger bodyLength = [body length]; - if (contentLength > bodyLength) { - // need more data... - [connectionHandle readInBackgroundAndNotify]; - } else { - if (contentLength < bodyLength) { - // We got extra (probably someone trying to pipeline on us), trim - // and let the extra data go... - NSData *newBody = [NSData dataWithBytes:[body bytes] length:contentLength]; - [request setBody:newBody]; - _GTMDevLog(@"Got %lu extra bytes on http request, ignoring them", - (unsigned long)(bodyLength - contentLength)); - } - - GTMHTTPResponseMessage *response = nil; - @try { - // Off to the delegate - response = [delegate_ httpServer:self handleRequest:request]; - } @catch (NSException *e) { - _GTMDevLog(@"Exception trying to handle http request: %@", e); - } // COV_NF_LINE - radar 5851992 only reachable w/ an uncaught exception which isn't - // testable - - if (response) { - // We don't support connection reuse, so we add (force) the header to - // close every connection. - [response setValue:@"close" forHeaderField:@"Connection"]; - - // spawn thread to send reply (since we do a blocking send) - [connDict setObject:response forKey:kResponse]; - [NSThread detachNewThreadSelector:@selector(sendResponseOnNewThread:) - toTarget:self - withObject:connDict]; - } else { - // No response, shut it down - [self closeConnection:connDict]; - } - } - } - } @catch (NSException *e) { // COV_NF_START - _GTMDevLog(@"exception while read data: %@", e); - // exception while dealing with the connection, close it - } // COV_NF_END - @finally { - [pool drain]; - } -} - -- (NSMutableDictionary *)lookupConnection:(NSFileHandle *)fileHandle { - NSMutableDictionary *result = nil; - for (NSMutableDictionary *connDict in connections_) { - if (fileHandle == [connDict objectForKey:kFileHandle]) { - result = connDict; - break; - } - } - return result; -} - -- (void)closeConnection:(NSMutableDictionary *)connDict { - // remove the notification - NSFileHandle *connectionHandle = [connDict objectForKey:kFileHandle]; - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - [center removeObserver:self name:NSFileHandleReadCompletionNotification object:connectionHandle]; - // in a non GC world, we're fine just letting the connect get closed when - // the object is release when it comes out of connections_, but in a GC world - // it won't get cleaned up - [connectionHandle closeFile]; - - // remove it from the list - [connections_ removeObject:connDict]; -} - -- (void)sendResponseOnNewThread:(NSMutableDictionary *)connDict { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - @try { - GTMHTTPResponseMessage *response = [connDict objectForKey:kResponse]; - NSFileHandle *connectionHandle = [connDict objectForKey:kFileHandle]; - NSData *serialized = [response serializedData]; - [connectionHandle writeData:serialized]; - } @catch (NSException *e) { // COV_NF_START - causing an exception here is to hard in a test - // TODO: let the delegate know about the exception (but do it on the main - // thread) - _GTMDevLog(@"exception while sending reply: %@", e); - } // COV_NF_END - - // back to the main thread to close things down - [self performSelectorOnMainThread:@selector(sentResponse:) withObject:connDict waitUntilDone:NO]; - - [pool release]; -} - -- (void)sentResponse:(NSMutableDictionary *)connDict { - // make sure we're still tracking this connection (in case server was stopped) - NSFileHandle *connection = [connDict objectForKey:kFileHandle]; - NSMutableDictionary *connDict2 = [self lookupConnection:connection]; - if (connDict != connDict2) return; - - // TODO: message the delegate that it was sent - - // close it down - [self closeConnection:connDict]; -} - -@end - -#pragma mark - - -@implementation GTMHTTPRequestMessage - -- (id)init { - self = [super init]; - if (self) { - message_ = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, YES); - } - return self; -} - -- (void)dealloc { - if (message_) { - CFRelease(message_); - } - [super dealloc]; -} - -- (NSString *)version { - return GTMCFAutorelease(CFHTTPMessageCopyVersion(message_)); -} - -- (NSURL *)URL { - return GTMCFAutorelease(CFHTTPMessageCopyRequestURL(message_)); -} - -- (NSString *)method { - return GTMCFAutorelease(CFHTTPMessageCopyRequestMethod(message_)); -} - -- (NSData *)body { - return GTMCFAutorelease(CFHTTPMessageCopyBody(message_)); -} - -- (NSDictionary *)allHeaderFieldValues { - return GTMCFAutorelease(CFHTTPMessageCopyAllHeaderFields(message_)); -} - -- (NSString *)description { - CFStringRef desc = CFCopyDescription(message_); - NSString *result = [NSString stringWithFormat:@"%@<%p>{ message=%@ }", [self class], self, desc]; - CFRelease(desc); - return result; -} - -@end - -@implementation GTMHTTPRequestMessage (PrivateHelpers) - -- (BOOL)isHeaderComplete { - return CFHTTPMessageIsHeaderComplete(message_) ? YES : NO; -} - -- (BOOL)appendData:(NSData *)data { - return CFHTTPMessageAppendBytes(message_, [data bytes], (CFIndex)[data length]) ? YES : NO; -} - -- (NSString *)headerFieldValueForKey:(NSString *)key { - CFStringRef value = NULL; - if (key) { - value = CFHTTPMessageCopyHeaderFieldValue(message_, (CFStringRef)key); - } - return GTMCFAutorelease(value); -} - -- (UInt32)contentLength { - return (UInt32)[[self headerFieldValueForKey:@"Content-Length"] intValue]; -} - -- (void)setBody:(NSData *)body { - if (!body) { - body = [NSData data]; // COV_NF_LINE - can only happen in we fail to make the new data object - } - CFHTTPMessageSetBody(message_, (CFDataRef)body); -} - -@end - -#pragma mark - - -@implementation GTMHTTPResponseMessage - -- (id)init { - return [self initWithBody:nil contentType:nil statusCode:0]; -} - -- (id)initWithBody:(NSData *)body contentType:(NSString *)contentType statusCode:(int)statusCode { - self = [super init]; - if (self) { - if ((statusCode < 100) || (statusCode > 599)) { - [self release]; - return nil; - } - message_ = - CFHTTPMessageCreateResponse(kCFAllocatorDefault, statusCode, NULL, kCFHTTPVersion1_0); - if (!message_) { - // COV_NF_START - [self release]; - return nil; - // COV_NF_END - } - NSUInteger bodyLength = 0; - if (body) { - bodyLength = [body length]; - CFHTTPMessageSetBody(message_, (CFDataRef)body); - } - if ([contentType length] == 0) { - contentType = @"text/html"; - } - NSString *bodyLenStr = [NSString stringWithFormat:@"%lu", (unsigned long)bodyLength]; - [self setValue:bodyLenStr forHeaderField:@"Content-Length"]; - [self setValue:contentType forHeaderField:@"Content-Type"]; - } - return self; -} - -- (void)dealloc { - if (message_) { - CFRelease(message_); - } - [super dealloc]; -} - -+ (instancetype)responseWithString:(NSString *)plainText { - NSData *body = [plainText dataUsingEncoding:NSUTF8StringEncoding]; - return [self responseWithBody:body contentType:@"text/plain; charset=UTF-8" statusCode:200]; -} - -+ (instancetype)responseWithHTMLString:(NSString *)htmlString { - return [self responseWithBody:[htmlString dataUsingEncoding:NSUTF8StringEncoding] - contentType:@"text/html; charset=UTF-8" - statusCode:200]; -} - -+ (instancetype)responseWithBody:(NSData *)body - contentType:(NSString *)contentType - statusCode:(int)statusCode { - return [[[[self class] alloc] initWithBody:body contentType:contentType statusCode:statusCode] - autorelease]; -} - -+ (instancetype)emptyResponseWithCode:(int)statusCode { - return - [[[[self class] alloc] initWithBody:nil contentType:nil statusCode:statusCode] autorelease]; -} - -- (void)setValue:(NSString *)value forHeaderField:(NSString *)headerField { - if ([headerField length] == 0) return; - if (value == nil) { - value = @""; - } - CFHTTPMessageSetHeaderFieldValue(message_, (CFStringRef)headerField, (CFStringRef)value); -} - -- (void)setHeaderValuesFromDictionary:(NSDictionary *)dict { - for (id key in dict) { - id value = [dict valueForKey:key]; - [self setValue:value forHeaderField:key]; - } -} - -- (NSString *)description { - CFStringRef desc = CFCopyDescription(message_); - NSString *result = [NSString stringWithFormat:@"%@<%p>{ message=%@ }", [self class], self, desc]; - CFRelease(desc); - return result; -} - -- (NSData *)serializedData { - return GTMCFAutorelease(CFHTTPMessageCopySerializedMessage(message_)); -} - -@end diff --git a/Example/Firebase.xcodeproj/project.pbxproj b/Example/Firebase.xcodeproj/project.pbxproj index f658174..2003bd1 100644 --- a/Example/Firebase.xcodeproj/project.pbxproj +++ b/Example/Firebase.xcodeproj/project.pbxproj @@ -339,9 +339,6 @@ DE47C142207ACAA900B1AEDF /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DE47C13B207ACAA900B1AEDF /* main.m */; }; DE47C143207ACAA900B1AEDF /* FIRAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DE47C13C207ACAA900B1AEDF /* FIRAppDelegate.m */; }; DE47C144207ACAA900B1AEDF /* FIRViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DE47C13D207ACAA900B1AEDF /* FIRViewController.m */; }; - DE4B26E020855F4C0030A38C /* FIRAppEnvironmentUtilTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE4B26DE20855F1F0030A38C /* FIRAppEnvironmentUtilTest.m */; }; - DE4B26E120855F500030A38C /* FIRAppEnvironmentUtilTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE4B26DE20855F1F0030A38C /* FIRAppEnvironmentUtilTest.m */; }; - DE4B26E220855F520030A38C /* FIRAppEnvironmentUtilTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE4B26DE20855F1F0030A38C /* FIRAppEnvironmentUtilTest.m */; }; DE750DBD1EB3DD5B00A75E47 /* FIRAuthAPNSTokenTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE750DB61EB3DD4000A75E47 /* FIRAuthAPNSTokenTests.m */; }; DE750DBE1EB3DD6800A75E47 /* FIRAuthAPNSTokenManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE750DB51EB3DD4000A75E47 /* FIRAuthAPNSTokenManagerTests.m */; }; DE750DBF1EB3DD6C00A75E47 /* FIRAuthAppCredentialManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE750DB71EB3DD4000A75E47 /* FIRAuthAppCredentialManagerTests.m */; }; @@ -577,18 +574,6 @@ ED34CF5420DC16DD000EA5D1 /* FIRComponentTypeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = ED34CF4D20DC16DD000EA5D1 /* FIRComponentTypeTest.m */; }; ED34CF5520DC16DD000EA5D1 /* FIRComponentTypeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = ED34CF4D20DC16DD000EA5D1 /* FIRComponentTypeTest.m */; }; ED34CF5620DC16DD000EA5D1 /* FIRComponentTypeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = ED34CF4D20DC16DD000EA5D1 /* FIRComponentTypeTest.m */; }; - ED8C81002088EFA20093EB8A /* FIRMutableDictionaryTest.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8C80FA2088EFA10093EB8A /* FIRMutableDictionaryTest.m */; }; - ED8C81012088EFA20093EB8A /* FIRMutableDictionaryTest.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8C80FA2088EFA10093EB8A /* FIRMutableDictionaryTest.m */; }; - ED8C81022088EFA20093EB8A /* FIRMutableDictionaryTest.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8C80FA2088EFA10093EB8A /* FIRMutableDictionaryTest.m */; }; - ED8C81032088EFA20093EB8A /* FIRNetworkTest.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8C80FB2088EFA10093EB8A /* FIRNetworkTest.m */; }; - ED8C81042088EFA20093EB8A /* FIRNetworkTest.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8C80FB2088EFA10093EB8A /* FIRNetworkTest.m */; }; - ED8C81052088EFA20093EB8A /* FIRNetworkTest.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8C80FB2088EFA10093EB8A /* FIRNetworkTest.m */; }; - ED8C81062088EFA20093EB8A /* FIRReachabilityCheckerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8C80FC2088EFA20093EB8A /* FIRReachabilityCheckerTest.m */; }; - ED8C81072088EFA20093EB8A /* FIRReachabilityCheckerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8C80FC2088EFA20093EB8A /* FIRReachabilityCheckerTest.m */; }; - ED8C81082088EFA20093EB8A /* FIRReachabilityCheckerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8C80FC2088EFA20093EB8A /* FIRReachabilityCheckerTest.m */; }; - ED8C81092088EFA20093EB8A /* GTMHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8C80FF2088EFA20093EB8A /* GTMHTTPServer.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - ED8C810A2088EFA20093EB8A /* GTMHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8C80FF2088EFA20093EB8A /* GTMHTTPServer.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - ED8C810B2088EFA20093EB8A /* GTMHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8C80FF2088EFA20093EB8A /* GTMHTTPServer.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; EDD43AA420BF7C7B005EBB36 /* FIRAnalyticsConfigurationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = EDD43AA320BF7C7B005EBB36 /* FIRAnalyticsConfigurationTest.m */; }; EDD43AA520BF7C7B005EBB36 /* FIRAnalyticsConfigurationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = EDD43AA320BF7C7B005EBB36 /* FIRAnalyticsConfigurationTest.m */; }; EDD43AA620BF7C7B005EBB36 /* FIRAnalyticsConfigurationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = EDD43AA320BF7C7B005EBB36 /* FIRAnalyticsConfigurationTest.m */; }; @@ -1072,7 +1057,6 @@ DE47C13B207ACAA900B1AEDF /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; DE47C13C207ACAA900B1AEDF /* FIRAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRAppDelegate.m; sourceTree = ""; }; DE47C13D207ACAA900B1AEDF /* FIRViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRViewController.m; sourceTree = ""; }; - DE4B26DE20855F1F0030A38C /* FIRAppEnvironmentUtilTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRAppEnvironmentUtilTest.m; sourceTree = ""; }; DE53893E1FBB62E100199FC2 /* Auth_Tests_tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Auth_Tests_tvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; DE53894C1FBB635400199FC2 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; DE53894D1FBB635400199FC2 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -1268,11 +1252,6 @@ ED34CF4B20DC16DC000EA5D1 /* FIRTestComponents.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRTestComponents.m; sourceTree = ""; }; ED34CF4C20DC16DD000EA5D1 /* FIRTestComponents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FIRTestComponents.h; sourceTree = ""; }; ED34CF4D20DC16DD000EA5D1 /* FIRComponentTypeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRComponentTypeTest.m; sourceTree = ""; }; - ED8C80FA2088EFA10093EB8A /* FIRMutableDictionaryTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRMutableDictionaryTest.m; sourceTree = ""; }; - ED8C80FB2088EFA10093EB8A /* FIRNetworkTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRNetworkTest.m; sourceTree = ""; }; - ED8C80FC2088EFA20093EB8A /* FIRReachabilityCheckerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRReachabilityCheckerTest.m; sourceTree = ""; }; - ED8C80FE2088EFA20093EB8A /* GTMHTTPServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMHTTPServer.h; sourceTree = ""; }; - ED8C80FF2088EFA20093EB8A /* GTMHTTPServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMHTTPServer.m; sourceTree = ""; }; EDD43AA320BF7C7B005EBB36 /* FIRAnalyticsConfigurationTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRAnalyticsConfigurationTest.m; sourceTree = ""; }; /* End PBXFileReference section */ @@ -2280,36 +2259,22 @@ children = ( EDD43AA320BF7C7B005EBB36 /* FIRAnalyticsConfigurationTest.m */, DEE14D751E844677006FA992 /* FIRAppAssociationRegistrationUnitTests.m */, - DE4B26DE20855F1F0030A38C /* FIRAppEnvironmentUtilTest.m */, DEE14D761E844677006FA992 /* FIRAppTest.m */, DEE14D771E844677006FA992 /* FIRBundleUtilTest.m */, ED34CF4A20DC16DC000EA5D1 /* FIRComponentContainerTest.m */, ED34CF4D20DC16DD000EA5D1 /* FIRComponentTypeTest.m */, DEE14D781E844677006FA992 /* FIRConfigurationTest.m */, DEE14D791E844677006FA992 /* FIRLoggerTest.m */, - ED8C80FA2088EFA10093EB8A /* FIRMutableDictionaryTest.m */, - ED8C80FB2088EFA10093EB8A /* FIRNetworkTest.m */, DEE14D7A1E844677006FA992 /* FIROptionsTest.m */, - ED8C80FC2088EFA20093EB8A /* FIRReachabilityCheckerTest.m */, DEE14D7B1E844677006FA992 /* FIRTestCase.h */, DEE14D7C1E844677006FA992 /* FIRTestCase.m */, ED34CF4C20DC16DD000EA5D1 /* FIRTestComponents.h */, ED34CF4B20DC16DC000EA5D1 /* FIRTestComponents.m */, DEE14D7D1E844677006FA992 /* Tests-Info.plist */, - ED8C80FD2088EFA20093EB8A /* third_party */, ); path = Tests; sourceTree = ""; }; - ED8C80FD2088EFA20093EB8A /* third_party */ = { - isa = PBXGroup; - children = ( - ED8C80FE2088EFA20093EB8A /* GTMHTTPServer.h */, - ED8C80FF2088EFA20093EB8A /* GTMHTTPServer.m */, - ); - path = third_party; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -3633,18 +3598,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - ED8C81072088EFA20093EB8A /* FIRReachabilityCheckerTest.m in Sources */, - ED8C81042088EFA20093EB8A /* FIRNetworkTest.m in Sources */, D064E6AF1ED9B31C001956DF /* FIRAppAssociationRegistrationUnitTests.m in Sources */, - ED8C81012088EFA20093EB8A /* FIRMutableDictionaryTest.m in Sources */, D064E6B01ED9B31C001956DF /* FIRAppTest.m in Sources */, ED34CF5220DC16DD000EA5D1 /* FIRTestComponents.m in Sources */, D064E6B11ED9B31C001956DF /* FIRConfigurationTest.m in Sources */, ED34CF5520DC16DD000EA5D1 /* FIRComponentTypeTest.m in Sources */, - DE4B26E120855F500030A38C /* FIRAppEnvironmentUtilTest.m in Sources */, D064E6B21ED9B31C001956DF /* FIRLoggerTest.m in Sources */, D064E6B31ED9B31C001956DF /* FIROptionsTest.m in Sources */, - ED8C810A2088EFA20093EB8A /* GTMHTTPServer.m in Sources */, D064E6B41ED9B31C001956DF /* FIRBundleUtilTest.m in Sources */, ED34CF4F20DC16DD000EA5D1 /* FIRComponentContainerTest.m in Sources */, EDD43AA520BF7C7B005EBB36 /* FIRAnalyticsConfigurationTest.m in Sources */, @@ -4095,18 +4055,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - ED8C81082088EFA20093EB8A /* FIRReachabilityCheckerTest.m in Sources */, - ED8C81052088EFA20093EB8A /* FIRNetworkTest.m in Sources */, DEAAD3DA1FBA34250053BF48 /* FIROptionsTest.m in Sources */, - ED8C81022088EFA20093EB8A /* FIRMutableDictionaryTest.m in Sources */, DEAAD3D51FBA34250053BF48 /* FIRAppAssociationRegistrationUnitTests.m in Sources */, ED34CF5320DC16DD000EA5D1 /* FIRTestComponents.m in Sources */, DEAAD3D91FBA34250053BF48 /* FIRLoggerTest.m in Sources */, ED34CF5620DC16DD000EA5D1 /* FIRComponentTypeTest.m in Sources */, - DE4B26E220855F520030A38C /* FIRAppEnvironmentUtilTest.m in Sources */, DEAAD3D61FBA34250053BF48 /* FIRAppTest.m in Sources */, DEAAD3D81FBA34250053BF48 /* FIRConfigurationTest.m in Sources */, - ED8C810B2088EFA20093EB8A /* GTMHTTPServer.m in Sources */, DEAAD3DB1FBA34250053BF48 /* FIRTestCase.m in Sources */, ED34CF5020DC16DD000EA5D1 /* FIRComponentContainerTest.m in Sources */, EDD43AA620BF7C7B005EBB36 /* FIRAnalyticsConfigurationTest.m in Sources */, @@ -4182,18 +4137,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - ED8C81062088EFA20093EB8A /* FIRReachabilityCheckerTest.m in Sources */, - ED8C81032088EFA20093EB8A /* FIRNetworkTest.m in Sources */, DEE14D8E1E84468D006FA992 /* FIRAppAssociationRegistrationUnitTests.m in Sources */, - ED8C81002088EFA20093EB8A /* FIRMutableDictionaryTest.m in Sources */, DEE14D8F1E84468D006FA992 /* FIRAppTest.m in Sources */, ED34CF5120DC16DD000EA5D1 /* FIRTestComponents.m in Sources */, DEE14D911E84468D006FA992 /* FIRConfigurationTest.m in Sources */, ED34CF5420DC16DD000EA5D1 /* FIRComponentTypeTest.m in Sources */, - DE4B26E020855F4C0030A38C /* FIRAppEnvironmentUtilTest.m in Sources */, DEE14D921E84468D006FA992 /* FIRLoggerTest.m in Sources */, DEE14D931E84468D006FA992 /* FIROptionsTest.m in Sources */, - ED8C81092088EFA20093EB8A /* GTMHTTPServer.m in Sources */, DEE14D901E84468D006FA992 /* FIRBundleUtilTest.m in Sources */, ED34CF4E20DC16DD000EA5D1 /* FIRComponentContainerTest.m in Sources */, EDD43AA420BF7C7B005EBB36 /* FIRAnalyticsConfigurationTest.m in Sources */, @@ -4938,10 +4888,6 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = ""; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - "$(SRCROOT)/../GoogleUtilities/Reachability", - ); INFOPLIST_FILE = "Core/Tests/Tests-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = YES; @@ -4962,10 +4908,6 @@ COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - "$(SRCROOT)/../GoogleUtilities/Reachability", - ); INFOPLIST_FILE = "Core/Tests/Tests-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = NO; @@ -6368,10 +6310,6 @@ DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = EQHXZ8M8AV; GCC_C_LANGUAGE_STANDARD = gnu11; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - "$(SRCROOT)/../GoogleUtilities/Reachability", - ); INFOPLIST_FILE = "Core/Tests/Tests-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = YES; @@ -6405,10 +6343,6 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = EQHXZ8M8AV; GCC_C_LANGUAGE_STANDARD = gnu11; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - "$(SRCROOT)/../GoogleUtilities/Reachability", - ); INFOPLIST_FILE = "Core/Tests/Tests-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = NO; @@ -6697,13 +6631,12 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - DEBUG_INFORMATION_FORMAT = dwarf; - DEVELOPMENT_TEAM = ""; HEADER_SEARCH_PATHS = ( "$(inherited)", "${PODS_ROOT}/Headers/Private", - "$(SRCROOT)/../GoogleUtilities/Reachability", ); + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "Core/Tests/Tests-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 10.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -6720,14 +6653,13 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = ""; HEADER_SEARCH_PATHS = ( "$(inherited)", "${PODS_ROOT}/Headers/Private", - "$(SRCROOT)/../GoogleUtilities/Reachability", ); + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "Core/Tests/Tests-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 10.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; diff --git a/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj new file mode 100644 index 0000000..164eb57 --- /dev/null +++ b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/project.pbxproj @@ -0,0 +1,1135 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; + 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; }; + 6003F592195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; + 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; + 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; + 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; + DE5CF98E20F686310063FFDD /* GULAppEnvironmentUtilTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE5CF98C20F686290063FFDD /* GULAppEnvironmentUtilTest.m */; }; + DEC977D720F68C3300014E20 /* GULReachabilityCheckerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC977D320F68C3300014E20 /* GULReachabilityCheckerTest.m */; }; + DEC977D820F68C3300014E20 /* GULMutableDictionaryTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC977D520F68C3300014E20 /* GULMutableDictionaryTest.m */; }; + DEC977D920F68C3300014E20 /* GULNetworkTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC977D620F68C3300014E20 /* GULNetworkTest.m */; }; + DEC977DD20F68FE100014E20 /* GTMHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC977DC20F68FE100014E20 /* GTMHTTPServer.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + DEC977E120F6A7C100014E20 /* GULLoggerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC977DF20F6A7A700014E20 /* GULLoggerTest.m */; }; + DEC977EE20F6ACDA00014E20 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC977E420F6ACDA00014E20 /* ViewController.m */; }; + DEC977EF20F6ACDA00014E20 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DEC977E520F6ACDA00014E20 /* LaunchScreen.storyboard */; }; + DEC977F020F6ACDA00014E20 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DEC977E720F6ACDA00014E20 /* Main.storyboard */; }; + DEC977F120F6ACDA00014E20 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC977E920F6ACDA00014E20 /* main.m */; }; + DEC977F320F6ACDA00014E20 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DEC977EB20F6ACDA00014E20 /* Images.xcassets */; }; + DEC977F420F6ACDA00014E20 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC977EC20F6ACDA00014E20 /* AppDelegate.m */; }; + DEC9781820F6D37400014E20 /* GULLoggerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC977DF20F6A7A700014E20 /* GULLoggerTest.m */; }; + DEC9781920F6D38500014E20 /* GULAppEnvironmentUtilTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE5CF98C20F686290063FFDD /* GULAppEnvironmentUtilTest.m */; }; + DEC9781A20F6D38800014E20 /* GULReachabilityCheckerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC977D320F68C3300014E20 /* GULReachabilityCheckerTest.m */; }; + DEC9781B20F6D39500014E20 /* GULMutableDictionaryTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC977D520F68C3300014E20 /* GULMutableDictionaryTest.m */; }; + DEC9781C20F6D39500014E20 /* GULNetworkTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC977D620F68C3300014E20 /* GULNetworkTest.m */; }; + DEC9781D20F6D39900014E20 /* GTMHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC977DC20F68FE100014E20 /* GTMHTTPServer.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + DEC9786820F6D65B00014E20 /* GULLoggerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC977DF20F6A7A700014E20 /* GULLoggerTest.m */; }; + DEC9786920F6D66300014E20 /* GTMHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC977DC20F68FE100014E20 /* GTMHTTPServer.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + DEC9786A20F6D66300014E20 /* GULMutableDictionaryTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC977D520F68C3300014E20 /* GULMutableDictionaryTest.m */; }; + DEC9786B20F6D66300014E20 /* GULNetworkTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC977D620F68C3300014E20 /* GULNetworkTest.m */; }; + DEC9786C20F6D66700014E20 /* GULReachabilityCheckerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC977D320F68C3300014E20 /* GULReachabilityCheckerTest.m */; }; + DEC9786D20F6D66B00014E20 /* GULAppEnvironmentUtilTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE5CF98C20F686290063FFDD /* GULAppEnvironmentUtilTest.m */; }; + DEC9787720F6DE7200014E20 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DEC9787120F6DE7200014E20 /* Main.storyboard */; }; + DEC9787920F6DE7200014E20 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC9787420F6DE7200014E20 /* main.m */; }; + DEC9787A20F6DE7200014E20 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC9787520F6DE7200014E20 /* AppDelegate.m */; }; + DEC9787B20F6DE7200014E20 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC9787620F6DE7200014E20 /* ViewController.m */; }; + DEC9788520F6E1E000014E20 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DEC9787E20F6E1DF00014E20 /* Assets.xcassets */; }; + DEC9788620F6E1E000014E20 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC9787F20F6E1DF00014E20 /* ViewController.m */; }; + DEC9788720F6E1E000014E20 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DEC9788020F6E1DF00014E20 /* Main.storyboard */; }; + DEC9788820F6E1E000014E20 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC9788120F6E1DF00014E20 /* main.m */; }; + DEC9788920F6E1E000014E20 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC9788220F6E1DF00014E20 /* AppDelegate.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 6003F5B3195388D20070C39A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6003F582195388D10070C39A /* Project object */; + proxyType = 1; + remoteGlobalIDString = 6003F589195388D20070C39A; + remoteInfo = GoogleUtilities; + }; + DEC9780C20F6D0EA00014E20 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6003F582195388D10070C39A /* Project object */; + proxyType = 1; + remoteGlobalIDString = DEC977F820F6D0E900014E20; + remoteInfo = "GoogleUtilities-macOS"; + }; + DEC9785C20F6D5DA00014E20 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6003F582195388D10070C39A /* Project object */; + proxyType = 1; + remoteGlobalIDString = DEC9784620F6D5D800014E20; + remoteInfo = Example_tvOS; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 6003F58A195388D20070C39A /* Example_iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example_iOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 6003F5AE195388D20070C39A /* Tests_iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests_iOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; + 7BEA793625C8DE7C8EC60006 /* GoogleUtilities.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = GoogleUtilities.podspec; path = ../GoogleUtilities.podspec; sourceTree = ""; }; + DE5CF98C20F686290063FFDD /* GULAppEnvironmentUtilTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GULAppEnvironmentUtilTest.m; sourceTree = ""; }; + DEC977D320F68C3300014E20 /* GULReachabilityCheckerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GULReachabilityCheckerTest.m; sourceTree = ""; }; + DEC977D520F68C3300014E20 /* GULMutableDictionaryTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GULMutableDictionaryTest.m; sourceTree = ""; }; + DEC977D620F68C3300014E20 /* GULNetworkTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GULNetworkTest.m; sourceTree = ""; }; + DEC977DB20F68FE100014E20 /* GTMHTTPServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMHTTPServer.h; sourceTree = ""; }; + DEC977DC20F68FE100014E20 /* GTMHTTPServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMHTTPServer.m; sourceTree = ""; }; + DEC977DF20F6A7A700014E20 /* GULLoggerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GULLoggerTest.m; sourceTree = ""; }; + DEC977E320F6ACDA00014E20 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + DEC977E420F6ACDA00014E20 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + DEC977E620F6ACDA00014E20 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + DEC977E820F6ACDA00014E20 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + DEC977E920F6ACDA00014E20 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + DEC977EA20F6ACDA00014E20 /* GoogleUtilities-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleUtilities-Info.plist"; sourceTree = ""; }; + DEC977EB20F6ACDA00014E20 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + DEC977EC20F6ACDA00014E20 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + DEC977ED20F6ACDA00014E20 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + DEC977F920F6D0E900014E20 /* Example_macOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example_macOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; + DEC9780B20F6D0EA00014E20 /* Tests_macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests_macOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + DEC9784720F6D5D800014E20 /* Example_tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example_tvOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; + DEC9785B20F6D5DA00014E20 /* Tests_tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests_tvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + DEC9786F20F6DE7200014E20 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + DEC9787020F6DE7200014E20 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + DEC9787220F6DE7200014E20 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + DEC9787320F6DE7200014E20 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DEC9787420F6DE7200014E20 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + DEC9787520F6DE7200014E20 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + DEC9787620F6DE7200014E20 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + DEC9787D20F6E1DF00014E20 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + DEC9787E20F6E1DF00014E20 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + DEC9787F20F6E1DF00014E20 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + DEC9788020F6E1DF00014E20 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; + DEC9788120F6E1DF00014E20 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + DEC9788220F6E1DF00014E20 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + DEC9788320F6E1DF00014E20 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DEC9788420F6E1DF00014E20 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + E0A8D570636E99E7C3396DF8 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; + F1F2A7C03C10A3A03F9502B8 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 6003F587195388D20070C39A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */, + 6003F592195388D20070C39A /* UIKit.framework in Frameworks */, + 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6003F5AB195388D20070C39A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */, + 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */, + 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DEC977F620F6D0E900014E20 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DEC9780820F6D0EA00014E20 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DEC9784420F6D5D800014E20 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DEC9785820F6D5DA00014E20 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 6003F581195388D10070C39A = { + isa = PBXGroup; + children = ( + DEC977E220F6ACDA00014E20 /* iOS */, + DEC9786E20F6DE7200014E20 /* macOS */, + DEC9787C20F6E1DF00014E20 /* tvOS */, + 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */, + 6003F5B5195388D20070C39A /* Tests */, + 6003F58C195388D20070C39A /* Frameworks */, + 6003F58B195388D20070C39A /* Products */, + ); + sourceTree = ""; + }; + 6003F58B195388D20070C39A /* Products */ = { + isa = PBXGroup; + children = ( + 6003F58A195388D20070C39A /* Example_iOS.app */, + 6003F5AE195388D20070C39A /* Tests_iOS.xctest */, + DEC977F920F6D0E900014E20 /* Example_macOS.app */, + DEC9780B20F6D0EA00014E20 /* Tests_macOS.xctest */, + DEC9784720F6D5D800014E20 /* Example_tvOS.app */, + DEC9785B20F6D5DA00014E20 /* Tests_tvOS.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 6003F58C195388D20070C39A /* Frameworks */ = { + isa = PBXGroup; + children = ( + 6003F58D195388D20070C39A /* Foundation.framework */, + 6003F58F195388D20070C39A /* CoreGraphics.framework */, + 6003F591195388D20070C39A /* UIKit.framework */, + 6003F5AF195388D20070C39A /* XCTest.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 6003F5B5195388D20070C39A /* Tests */ = { + isa = PBXGroup; + children = ( + DEC977DE20F6A7A700014E20 /* Logger */, + DEC977D420F68C3300014E20 /* Network */, + DEC977D220F68C3300014E20 /* Reachability */, + DE5CF98B20F686290063FFDD /* Environment */, + 6003F5B6195388D20070C39A /* Supporting Files */, + ); + path = Tests; + sourceTree = ""; + }; + 6003F5B6195388D20070C39A /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 6003F5B7195388D20070C39A /* Tests-Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */ = { + isa = PBXGroup; + children = ( + 7BEA793625C8DE7C8EC60006 /* GoogleUtilities.podspec */, + E0A8D570636E99E7C3396DF8 /* README.md */, + F1F2A7C03C10A3A03F9502B8 /* LICENSE */, + ); + name = "Podspec Metadata"; + sourceTree = ""; + }; + DE5CF98B20F686290063FFDD /* Environment */ = { + isa = PBXGroup; + children = ( + DE5CF98C20F686290063FFDD /* GULAppEnvironmentUtilTest.m */, + ); + path = Environment; + sourceTree = ""; + }; + DEC977D220F68C3300014E20 /* Reachability */ = { + isa = PBXGroup; + children = ( + DEC977D320F68C3300014E20 /* GULReachabilityCheckerTest.m */, + ); + path = Reachability; + sourceTree = ""; + }; + DEC977D420F68C3300014E20 /* Network */ = { + isa = PBXGroup; + children = ( + DEC977DA20F68FE100014E20 /* third_party */, + DEC977D520F68C3300014E20 /* GULMutableDictionaryTest.m */, + DEC977D620F68C3300014E20 /* GULNetworkTest.m */, + ); + path = Network; + sourceTree = ""; + }; + DEC977DA20F68FE100014E20 /* third_party */ = { + isa = PBXGroup; + children = ( + DEC977DB20F68FE100014E20 /* GTMHTTPServer.h */, + DEC977DC20F68FE100014E20 /* GTMHTTPServer.m */, + ); + path = third_party; + sourceTree = ""; + }; + DEC977DE20F6A7A700014E20 /* Logger */ = { + isa = PBXGroup; + children = ( + DEC977DF20F6A7A700014E20 /* GULLoggerTest.m */, + ); + path = Logger; + sourceTree = ""; + }; + DEC977E220F6ACDA00014E20 /* iOS */ = { + isa = PBXGroup; + children = ( + DEC977E320F6ACDA00014E20 /* AppDelegate.h */, + DEC977E420F6ACDA00014E20 /* ViewController.m */, + DEC977E520F6ACDA00014E20 /* LaunchScreen.storyboard */, + DEC977E720F6ACDA00014E20 /* Main.storyboard */, + DEC977E920F6ACDA00014E20 /* main.m */, + DEC977EA20F6ACDA00014E20 /* GoogleUtilities-Info.plist */, + DEC977EB20F6ACDA00014E20 /* Images.xcassets */, + DEC977EC20F6ACDA00014E20 /* AppDelegate.m */, + DEC977ED20F6ACDA00014E20 /* ViewController.h */, + ); + path = iOS; + sourceTree = ""; + }; + DEC9786E20F6DE7200014E20 /* macOS */ = { + isa = PBXGroup; + children = ( + DEC9786F20F6DE7200014E20 /* AppDelegate.h */, + DEC9787020F6DE7200014E20 /* ViewController.h */, + DEC9787120F6DE7200014E20 /* Main.storyboard */, + DEC9787320F6DE7200014E20 /* Info.plist */, + DEC9787420F6DE7200014E20 /* main.m */, + DEC9787520F6DE7200014E20 /* AppDelegate.m */, + DEC9787620F6DE7200014E20 /* ViewController.m */, + ); + path = macOS; + sourceTree = ""; + }; + DEC9787C20F6E1DF00014E20 /* tvOS */ = { + isa = PBXGroup; + children = ( + DEC9787D20F6E1DF00014E20 /* AppDelegate.h */, + DEC9787E20F6E1DF00014E20 /* Assets.xcassets */, + DEC9787F20F6E1DF00014E20 /* ViewController.m */, + DEC9788020F6E1DF00014E20 /* Main.storyboard */, + DEC9788120F6E1DF00014E20 /* main.m */, + DEC9788220F6E1DF00014E20 /* AppDelegate.m */, + DEC9788320F6E1DF00014E20 /* Info.plist */, + DEC9788420F6E1DF00014E20 /* ViewController.h */, + ); + path = tvOS; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 6003F589195388D20070C39A /* Example_iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "Example_iOS" */; + buildPhases = ( + 6003F586195388D20070C39A /* Sources */, + 6003F587195388D20070C39A /* Frameworks */, + 6003F588195388D20070C39A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Example_iOS; + productName = GoogleUtilities; + productReference = 6003F58A195388D20070C39A /* Example_iOS.app */; + productType = "com.apple.product-type.application"; + }; + 6003F5AD195388D20070C39A /* Tests_iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "Tests_iOS" */; + buildPhases = ( + 6003F5AA195388D20070C39A /* Sources */, + 6003F5AB195388D20070C39A /* Frameworks */, + 6003F5AC195388D20070C39A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 6003F5B4195388D20070C39A /* PBXTargetDependency */, + ); + name = Tests_iOS; + productName = GoogleUtilitiesTests; + productReference = 6003F5AE195388D20070C39A /* Tests_iOS.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + DEC977F820F6D0E900014E20 /* Example_macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = DEC9781620F6D0EB00014E20 /* Build configuration list for PBXNativeTarget "Example_macOS" */; + buildPhases = ( + DEC977F520F6D0E900014E20 /* Sources */, + DEC977F620F6D0E900014E20 /* Frameworks */, + DEC977F720F6D0E900014E20 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Example_macOS; + productName = "GoogleUtilities-macOS"; + productReference = DEC977F920F6D0E900014E20 /* Example_macOS.app */; + productType = "com.apple.product-type.application"; + }; + DEC9780A20F6D0EA00014E20 /* Tests_macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = DEC9781720F6D0EB00014E20 /* Build configuration list for PBXNativeTarget "Tests_macOS" */; + buildPhases = ( + DEC9780720F6D0EA00014E20 /* Sources */, + DEC9780820F6D0EA00014E20 /* Frameworks */, + DEC9780920F6D0EA00014E20 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + DEC9780D20F6D0EA00014E20 /* PBXTargetDependency */, + ); + name = Tests_macOS; + productName = "GoogleUtilities-macOSTests"; + productReference = DEC9780B20F6D0EA00014E20 /* Tests_macOS.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + DEC9784620F6D5D800014E20 /* Example_tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = DEC9786620F6D5DA00014E20 /* Build configuration list for PBXNativeTarget "Example_tvOS" */; + buildPhases = ( + DEC9784320F6D5D800014E20 /* Sources */, + DEC9784420F6D5D800014E20 /* Frameworks */, + DEC9784520F6D5D800014E20 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Example_tvOS; + productName = Example_tvOS; + productReference = DEC9784720F6D5D800014E20 /* Example_tvOS.app */; + productType = "com.apple.product-type.application"; + }; + DEC9785A20F6D5DA00014E20 /* Tests_tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = DEC9786720F6D5DA00014E20 /* Build configuration list for PBXNativeTarget "Tests_tvOS" */; + buildPhases = ( + DEC9785720F6D5DA00014E20 /* Sources */, + DEC9785820F6D5DA00014E20 /* Frameworks */, + DEC9785920F6D5DA00014E20 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + DEC9785D20F6D5DA00014E20 /* PBXTargetDependency */, + ); + name = Tests_tvOS; + productName = Example_tvOSTests; + productReference = DEC9785B20F6D5DA00014E20 /* Tests_tvOS.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 6003F582195388D10070C39A /* Project object */ = { + isa = PBXProject; + attributes = { + CLASSPREFIX = FIR; + LastUpgradeCheck = 0940; + ORGANIZATIONNAME = "Google, Inc."; + TargetAttributes = { + 6003F5AD195388D20070C39A = { + TestTargetID = 6003F589195388D20070C39A; + }; + DEC977F820F6D0E900014E20 = { + CreatedOnToolsVersion = 9.4; + ProvisioningStyle = Automatic; + }; + DEC9780A20F6D0EA00014E20 = { + CreatedOnToolsVersion = 9.4; + ProvisioningStyle = Automatic; + TestTargetID = DEC977F820F6D0E900014E20; + }; + DEC9784620F6D5D800014E20 = { + CreatedOnToolsVersion = 9.4; + ProvisioningStyle = Automatic; + }; + DEC9785A20F6D5DA00014E20 = { + CreatedOnToolsVersion = 9.4; + ProvisioningStyle = Automatic; + TestTargetID = DEC9784620F6D5D800014E20; + }; + }; + }; + buildConfigurationList = 6003F585195388D10070C39A /* Build configuration list for PBXProject "GoogleUtilities" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 6003F581195388D10070C39A; + productRefGroup = 6003F58B195388D20070C39A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 6003F589195388D20070C39A /* Example_iOS */, + 6003F5AD195388D20070C39A /* Tests_iOS */, + DEC977F820F6D0E900014E20 /* Example_macOS */, + DEC9780A20F6D0EA00014E20 /* Tests_macOS */, + DEC9784620F6D5D800014E20 /* Example_tvOS */, + DEC9785A20F6D5DA00014E20 /* Tests_tvOS */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 6003F588195388D20070C39A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DEC977EF20F6ACDA00014E20 /* LaunchScreen.storyboard in Resources */, + DEC977F320F6ACDA00014E20 /* Images.xcassets in Resources */, + DEC977F020F6ACDA00014E20 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6003F5AC195388D20070C39A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DEC977F720F6D0E900014E20 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DEC9787720F6DE7200014E20 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DEC9780920F6D0EA00014E20 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DEC9784520F6D5D800014E20 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DEC9788520F6E1E000014E20 /* Assets.xcassets in Resources */, + DEC9788720F6E1E000014E20 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DEC9785920F6D5DA00014E20 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 6003F586195388D20070C39A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DEC977F120F6ACDA00014E20 /* main.m in Sources */, + DEC977EE20F6ACDA00014E20 /* ViewController.m in Sources */, + DEC977F420F6ACDA00014E20 /* AppDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6003F5AA195388D20070C39A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DEC977DD20F68FE100014E20 /* GTMHTTPServer.m in Sources */, + DEC977D820F68C3300014E20 /* GULMutableDictionaryTest.m in Sources */, + DEC977E120F6A7C100014E20 /* GULLoggerTest.m in Sources */, + DEC977D920F68C3300014E20 /* GULNetworkTest.m in Sources */, + DE5CF98E20F686310063FFDD /* GULAppEnvironmentUtilTest.m in Sources */, + DEC977D720F68C3300014E20 /* GULReachabilityCheckerTest.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DEC977F520F6D0E900014E20 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DEC9787A20F6DE7200014E20 /* AppDelegate.m in Sources */, + DEC9787920F6DE7200014E20 /* main.m in Sources */, + DEC9787B20F6DE7200014E20 /* ViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DEC9780720F6D0EA00014E20 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DEC9781920F6D38500014E20 /* GULAppEnvironmentUtilTest.m in Sources */, + DEC9781A20F6D38800014E20 /* GULReachabilityCheckerTest.m in Sources */, + DEC9781820F6D37400014E20 /* GULLoggerTest.m in Sources */, + DEC9781D20F6D39900014E20 /* GTMHTTPServer.m in Sources */, + DEC9781B20F6D39500014E20 /* GULMutableDictionaryTest.m in Sources */, + DEC9781C20F6D39500014E20 /* GULNetworkTest.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DEC9784320F6D5D800014E20 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DEC9788820F6E1E000014E20 /* main.m in Sources */, + DEC9788620F6E1E000014E20 /* ViewController.m in Sources */, + DEC9788920F6E1E000014E20 /* AppDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DEC9785720F6D5DA00014E20 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DEC9786920F6D66300014E20 /* GTMHTTPServer.m in Sources */, + DEC9786B20F6D66300014E20 /* GULNetworkTest.m in Sources */, + DEC9786A20F6D66300014E20 /* GULMutableDictionaryTest.m in Sources */, + DEC9786C20F6D66700014E20 /* GULReachabilityCheckerTest.m in Sources */, + DEC9786820F6D65B00014E20 /* GULLoggerTest.m in Sources */, + DEC9786D20F6D66B00014E20 /* GULAppEnvironmentUtilTest.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 6003F5B4195388D20070C39A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 6003F589195388D20070C39A /* Example_iOS */; + targetProxy = 6003F5B3195388D20070C39A /* PBXContainerItemProxy */; + }; + DEC9780D20F6D0EA00014E20 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DEC977F820F6D0E900014E20 /* Example_macOS */; + targetProxy = DEC9780C20F6D0EA00014E20 /* PBXContainerItemProxy */; + }; + DEC9785D20F6D5DA00014E20 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DEC9784620F6D5D800014E20 /* Example_tvOS */; + targetProxy = DEC9785C20F6D5DA00014E20 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + DEC977E520F6ACDA00014E20 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + DEC977E620F6ACDA00014E20 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; + DEC977E720F6ACDA00014E20 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + DEC977E820F6ACDA00014E20 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + DEC9787120F6DE7200014E20 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + DEC9787220F6DE7200014E20 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 6003F5BD195388D20070C39A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + 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; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.3; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 6003F5BE195388D20070C39A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.3; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 6003F5C0195388D20070C39A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + INFOPLIST_FILE = "iOS/GoogleUtilities-Info.plist"; + MODULE_NAME = ExampleApp; + OTHER_LDFLAGS = ( + "$(inherited)", + "-all_load", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 6003F5C1195388D20070C39A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + INFOPLIST_FILE = "iOS/GoogleUtilities-Info.plist"; + MODULE_NAME = ExampleApp; + OTHER_LDFLAGS = ( + "$(inherited)", + "-all_load", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; + 6003F5C3195388D20070C39A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../Reachability", + ); + INFOPLIST_FILE = "Tests/Tests-Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example_iOS.app/Example_iOS"; + WRAPPER_EXTENSION = xctest; + }; + name = Debug; + }; + 6003F5C4195388D20070C39A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = NO; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../Reachability", + ); + INFOPLIST_FILE = "Tests/Tests-Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example_iOS.app/Example_iOS"; + WRAPPER_EXTENSION = xctest; + }; + name = Release; + }; + DEC9781220F6D0EB00014E20 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = macOS/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.google.GoogleUtilities-macOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Debug; + }; + DEC9781320F6D0EB00014E20 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = macOS/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = "com.google.GoogleUtilities-macOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Release; + }; + DEC9781420F6D0EB00014E20 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = gnu11; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../Reachability", + ); + INFOPLIST_FILE = "Tests/Tests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.google.GoogleUtilities-macOSTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example_macOS.app/Contents/MacOS/Example_macOS"; + }; + name = Debug; + }; + DEC9781520F6D0EB00014E20 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = gnu11; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../Reachability", + ); + INFOPLIST_FILE = "Tests/Tests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = "com.google.GoogleUtilities-macOSTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example_macOS.app/Contents/MacOS/Example_macOS"; + }; + name = Release; + }; + DEC9786220F6D5DA00014E20 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = tvOS/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.google.Example-tvOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 11.1; + }; + name = Debug; + }; + DEC9786320F6D5DA00014E20 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = tvOS/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = "com.google.Example-tvOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 11.1; + }; + name = Release; + }; + DEC9786420F6D5DA00014E20 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = gnu11; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../Reachability", + ); + INFOPLIST_FILE = "Tests/Tests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.google.Example-tvOSTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example_tvOS.app/Example_tvOS"; + TVOS_DEPLOYMENT_TARGET = 11.1; + }; + name = Debug; + }; + DEC9786520F6D5DA00014E20 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = gnu11; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../Reachability", + ); + INFOPLIST_FILE = "Tests/Tests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = "com.google.Example-tvOSTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example_tvOS.app/Example_tvOS"; + TVOS_DEPLOYMENT_TARGET = 11.1; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 6003F585195388D10070C39A /* Build configuration list for PBXProject "GoogleUtilities" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6003F5BD195388D20070C39A /* Debug */, + 6003F5BE195388D20070C39A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "Example_iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6003F5C0195388D20070C39A /* Debug */, + 6003F5C1195388D20070C39A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "Tests_iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6003F5C3195388D20070C39A /* Debug */, + 6003F5C4195388D20070C39A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DEC9781620F6D0EB00014E20 /* Build configuration list for PBXNativeTarget "Example_macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DEC9781220F6D0EB00014E20 /* Debug */, + DEC9781320F6D0EB00014E20 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DEC9781720F6D0EB00014E20 /* Build configuration list for PBXNativeTarget "Tests_macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DEC9781420F6D0EB00014E20 /* Debug */, + DEC9781520F6D0EB00014E20 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DEC9786620F6D5DA00014E20 /* Build configuration list for PBXNativeTarget "Example_tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DEC9786220F6D5DA00014E20 /* Debug */, + DEC9786320F6D5DA00014E20 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DEC9786720F6D5DA00014E20 /* Build configuration list for PBXNativeTarget "Tests_tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DEC9786420F6D5DA00014E20 /* Debug */, + DEC9786520F6D5DA00014E20 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 6003F582195388D10070C39A /* Project object */; +} diff --git a/GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Example_iOS.xcscheme b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Example_iOS.xcscheme new file mode 100644 index 0000000..f4ef277 --- /dev/null +++ b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Example_iOS.xcscheme @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Example_macOS.xcscheme b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Example_macOS.xcscheme new file mode 100644 index 0000000..550c22d --- /dev/null +++ b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Example_macOS.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Example_tvOS.xcscheme b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Example_tvOS.xcscheme new file mode 100644 index 0000000..7b087de --- /dev/null +++ b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Example_tvOS.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Tests_iOS.xcscheme b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Tests_iOS.xcscheme new file mode 100644 index 0000000..91ef539 --- /dev/null +++ b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Tests_iOS.xcscheme @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Tests_macOS.xcscheme b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Tests_macOS.xcscheme new file mode 100644 index 0000000..016d313 --- /dev/null +++ b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Tests_macOS.xcscheme @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Tests_tvOS.xcscheme b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Tests_tvOS.xcscheme new file mode 100644 index 0000000..d9f14c0 --- /dev/null +++ b/GoogleUtilities/Example/GoogleUtilities.xcodeproj/xcshareddata/xcschemes/Tests_tvOS.xcscheme @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GoogleUtilities/Example/Podfile b/GoogleUtilities/Example/Podfile new file mode 100644 index 0000000..814e93a --- /dev/null +++ b/GoogleUtilities/Example/Podfile @@ -0,0 +1,34 @@ +use_frameworks! + +target 'Example_iOS' do + platform :ios, '8.0' + + pod 'GoogleUtilities', :path => '../../' + + target 'Tests_iOS' do + inherit! :search_paths + pod 'OCMock' + end +end + +target 'Example_macOS' do + platform :osx, '10.10' + + pod 'GoogleUtilities', :path => '../../' + + target 'Tests_macOS' do + inherit! :search_paths + pod 'OCMock' + end +end + +target 'Example_tvOS' do + platform :tvos, '10.0' + + pod 'GoogleUtilities', :path => '../../' + + target 'Tests_tvOS' do + inherit! :search_paths + pod 'OCMock' + end +end diff --git a/GoogleUtilities/Example/Tests/Environment/GULAppEnvironmentUtilTest.m b/GoogleUtilities/Example/Tests/Environment/GULAppEnvironmentUtilTest.m new file mode 100644 index 0000000..62a7bf8 --- /dev/null +++ b/GoogleUtilities/Example/Tests/Environment/GULAppEnvironmentUtilTest.m @@ -0,0 +1,62 @@ +// Copyright 2018 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import +#import + +#import + +@interface GULAppEnvironmentUtilTest : XCTestCase + +@property(nonatomic) id processInfoMock; + +@end + +@implementation GULAppEnvironmentUtilTest + +- (void)setUp { + [super setUp]; + + _processInfoMock = OCMPartialMock([NSProcessInfo processInfo]); +} + +- (void)tearDown { + [super tearDown]; + + [_processInfoMock stopMocking]; +} + +- (void)testSystemVersionInfoMajorOnly { + NSOperatingSystemVersion osTen = {.majorVersion = 10, .minorVersion = 0, .patchVersion = 0}; + OCMStub([self.processInfoMock operatingSystemVersion]).andReturn(osTen); + + XCTAssertTrue([[GULAppEnvironmentUtil systemVersion] isEqualToString:@"10.0"]); +} + +- (void)testSystemVersionInfoMajorMinor { + NSOperatingSystemVersion osTenTwo = {.majorVersion = 10, .minorVersion = 2, .patchVersion = 0}; + OCMStub([self.processInfoMock operatingSystemVersion]).andReturn(osTenTwo); + + XCTAssertTrue([[GULAppEnvironmentUtil systemVersion] isEqualToString:@"10.2"]); +} + +- (void)testSystemVersionInfoMajorMinorPatch { + NSOperatingSystemVersion osTenTwoOne = {.majorVersion = 10, .minorVersion = 2, .patchVersion = 1}; + OCMStub([self.processInfoMock operatingSystemVersion]).andReturn(osTenTwoOne); + + XCTAssertTrue([[GULAppEnvironmentUtil systemVersion] isEqualToString:@"10.2.1"]); +} + +@end diff --git a/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m b/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m new file mode 100644 index 0000000..f65c06b --- /dev/null +++ b/GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m @@ -0,0 +1,210 @@ +// Copyright 2018 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifdef DEBUG +// The tests depend upon library methods only built with #ifdef DEBUG + +#import +#import + +#import + +#import + +extern NSString *const kGULPersistedDebugModeKey; + +extern const char *kGULLoggerASLClientFacilityName; + +extern void GULResetLogger(void); + +extern aslclient getGULLoggerClient(void); + +extern dispatch_queue_t getGULClientQueue(void); + +extern BOOL getGULLoggerDebugMode(void); + +static NSString *const kMessageCode = @"I-COR000001"; + +@interface GULLoggerTest : XCTestCase + +@property(nonatomic) NSString *randomLogString; + +@property(nonatomic, strong) NSUserDefaults *defaults; + +@end + +@implementation GULLoggerTest + +- (void)setUp { + [super setUp]; + GULResetLogger(); + + // Stub NSUserDefaults for cleaner testing. + _defaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.google.logger_test"]; +} + +- (void)tearDown { + [super tearDown]; + + _defaults = nil; +} + +- (void)testInitializeASLForDebugModeWithUserDefaults { + // Stub. + NSNumber *debugMode = @YES; + [self.defaults setBool:debugMode.boolValue forKey:kGULPersistedDebugModeKey]; + + // Test. + GULLogError(@"my service", NO, kMessageCode, @"Some error."); + + // Assert. + debugMode = [self.defaults objectForKey:kGULPersistedDebugModeKey]; + XCTAssertTrue(debugMode.boolValue); +} + +- (void)testMessageCodeFormat { + // Valid case. + XCTAssertNoThrow(GULLogError(@"my service", NO, @"I-APP000001", @"Message.")); + + // An extra dash or missing dash should fail. + XCTAssertThrows(GULLogError(@"my service", NO, @"I-APP-000001", @"Message.")); + XCTAssertThrows(GULLogError(@"my service", NO, @"IAPP000001", @"Message.")); + + // Wrong number of digits should fail. + XCTAssertThrows(GULLogError(@"my service", NO, @"I-APP00001", @"Message.")); + XCTAssertThrows(GULLogError(@"my service", NO, @"I-APP0000001", @"Message.")); + + // Lowercase should fail. + XCTAssertThrows(GULLogError(@"my service", NO, @"I-app000001", @"Message.")); + +// nil or empty message code should fail. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" + XCTAssertThrows(GULLogError(@"my service", NO, nil, @"Message.")); +#pragma clang diagnostic pop + + XCTAssertThrows(GULLogError(@"my service", NO, @"", @"Message.")); + + // Android message code should fail. + XCTAssertThrows(GULLogError(@"my service", NO, @"A-APP000001", @"Message.")); +} + +- (void)testLoggerInterface { + XCTAssertNoThrow(GULLogError(@"my service", NO, kMessageCode, @"Message.")); + XCTAssertNoThrow(GULLogError(@"my service", NO, kMessageCode, @"Configure %@.", @"blah")); + + XCTAssertNoThrow(GULLogWarning(@"my service", NO, kMessageCode, @"Message.")); + XCTAssertNoThrow(GULLogWarning(@"my service", NO, kMessageCode, @"Configure %@.", @"blah")); + + XCTAssertNoThrow(GULLogNotice(@"my service", NO, kMessageCode, @"Message.")); + XCTAssertNoThrow(GULLogNotice(@"my service", NO, kMessageCode, @"Configure %@.", @"blah")); + + XCTAssertNoThrow(GULLogInfo(@"my service", NO, kMessageCode, @"Message.")); + XCTAssertNoThrow(GULLogInfo(@"my service", NO, kMessageCode, @"Configure %@.", @"blah")); + + XCTAssertNoThrow(GULLogDebug(@"my service", NO, kMessageCode, @"Message.")); + XCTAssertNoThrow(GULLogDebug(@"my service", NO, kMessageCode, @"Configure %@.", @"blah")); +} + +// asl_set_filter does not perform as expected in unit test environment with simulator. The +// following test only checks whether the logs have been sent to system with the default settings in +// the unit test environment. +- (void)testSystemLogWithDefaultStatus { +#if !(BUG128) // Disable until https://github.com/firebase/firebase-ios-sdk/issues/128 is fixed + // Test fails on device and iOS 9 simulators - b/38130372 + return; +#else + // Sets the time interval that we need to wait in order to fetch all the logs. + NSTimeInterval timeInterval = 0.1f; + // Generates a random string each time and check whether it has been logged. + // Log messages with Notice level and below should be logged to system/device by default. + self.randomLogString = [NSUUID UUID].UUIDString; + GULLogError(@"my service", NO, kMessageCode, @"%@", self.randomLogString); + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeInterval]]; + XCTAssertTrue([self logExists]); + + self.randomLogString = [NSUUID UUID].UUIDString; + GULLogWarning(@"my service", kMessageCode, @"%@", self.randomLogString); + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeInterval]]; + XCTAssertTrue([self logExists]); + + self.randomLogString = [NSUUID UUID].UUIDString; + GULLogNotice(@"my service", kMessageCode, @"%@", self.randomLogString); + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeInterval]]; + XCTAssertTrue([self logExists]); + + // Log messages with Info level and above should NOT be logged to system/device by default. + self.randomLogString = [NSUUID UUID].UUIDString; + GULLogInfo(@"my service", kMessageCode, @"%@", self.randomLogString); + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeInterval]]; + XCTAssertFalse([self logExists]); + + self.randomLogString = [NSUUID UUID].UUIDString; + GULLogDebug(@"my service", kMessageCode, @"%@", self.randomLogString); + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeInterval]]; + XCTAssertFalse([self logExists]); +#endif +} + +// The GULLoggerLevel enum must match the ASL_LEVEL_* constants, but we manually redefine +// them in GULLoggerLevel.h since we cannot include (see b/34976089 for more details). +// This test ensures the constants match. +- (void)testGULLoggerLevelValues { + XCTAssertEqual(GULLoggerLevelError, ASL_LEVEL_ERR); + XCTAssertEqual(GULLoggerLevelWarning, ASL_LEVEL_WARNING); + XCTAssertEqual(GULLoggerLevelNotice, ASL_LEVEL_NOTICE); + XCTAssertEqual(GULLoggerLevelInfo, ASL_LEVEL_INFO); + XCTAssertEqual(GULLoggerLevelDebug, ASL_LEVEL_DEBUG); +} + +// Helper functions. +- (BOOL)logExists { + [self drainGULClientQueue]; + NSString *correctMsg = + [NSString stringWithFormat:@"%@[%@] %@", @"my service", kMessageCode, self.randomLogString]; + return [self messageWasLogged:correctMsg]; +} + +- (void)drainGULClientQueue { + dispatch_semaphore_t workerSemaphore = dispatch_semaphore_create(0); + dispatch_async(getGULClientQueue(), ^{ + dispatch_semaphore_signal(workerSemaphore); + }); + dispatch_semaphore_wait(workerSemaphore, DISPATCH_TIME_FOREVER); +} + +- (BOOL)messageWasLogged:(NSString *)message { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + aslmsg query = asl_new(ASL_TYPE_QUERY); + asl_set_query(query, ASL_KEY_FACILITY, kGULLoggerASLClientFacilityName, ASL_QUERY_OP_EQUAL); + aslresponse r = asl_search(getGULLoggerClient(), query); + asl_free(query); + aslmsg m; + const char *val; + NSMutableArray *allMsg = [[NSMutableArray alloc] init]; + while ((m = asl_next(r)) != NULL) { + val = asl_get(m, ASL_KEY_MSG); + if (val) { + [allMsg addObject:[NSString stringWithUTF8String:val]]; + } + } + asl_free(m); + asl_release(r); + return [allMsg containsObject:message]; +#pragma clang pop +} + +@end +#endif diff --git a/GoogleUtilities/Example/Tests/Network/GULMutableDictionaryTest.m b/GoogleUtilities/Example/Tests/Network/GULMutableDictionaryTest.m new file mode 100644 index 0000000..6378618 --- /dev/null +++ b/GoogleUtilities/Example/Tests/Network/GULMutableDictionaryTest.m @@ -0,0 +1,87 @@ +// Copyright 2018 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import + +const static NSString *const kKey = @"testKey1"; +const static NSString *const kValue = @"testValue1"; +const static NSString *const kKey2 = @"testKey2"; +const static NSString *const kValue2 = @"testValue2"; + +@interface GULMutableDictionaryTest : XCTestCase +@property(nonatomic) GULMutableDictionary *dictionary; +@end + +@implementation GULMutableDictionaryTest + +- (void)setUp { + [super setUp]; + self.dictionary = [[GULMutableDictionary alloc] init]; +} + +- (void)tearDown { + self.dictionary = nil; + [super tearDown]; +} + +- (void)testSetGetAndRemove { + XCTAssertNil([self.dictionary objectForKey:kKey]); + [self.dictionary setObject:kValue forKey:kKey]; + XCTAssertEqual(kValue, [self.dictionary objectForKey:kKey]); + [self.dictionary removeObjectForKey:kKey]; + XCTAssertNil([self.dictionary objectForKey:kKey]); +} + +- (void)testSetGetAndRemoveKeyed { + XCTAssertNil(self.dictionary[kKey]); + self.dictionary[kKey] = kValue; + XCTAssertEqual(kValue, self.dictionary[kKey]); + [self.dictionary removeObjectForKey:kKey]; + XCTAssertNil(self.dictionary[kKey]); +} + +- (void)testRemoveAll { + XCTAssertNil(self.dictionary[kKey]); + XCTAssertNil(self.dictionary[kKey2]); + self.dictionary[kKey] = kValue; + self.dictionary[kKey2] = kValue2; + [self.dictionary removeAllObjects]; + XCTAssertNil(self.dictionary[kKey]); + XCTAssertNil(self.dictionary[kKey2]); +} + +- (void)testCount { + XCTAssertEqual([self.dictionary count], 0); + self.dictionary[kKey] = kValue; + XCTAssertEqual([self.dictionary count], 1); + self.dictionary[kKey2] = kValue2; + XCTAssertEqual([self.dictionary count], 2); + [self.dictionary removeAllObjects]; + XCTAssertEqual([self.dictionary count], 0); +} + +- (void)testUnderlyingDictionary { + XCTAssertEqual([self.dictionary count], 0); + self.dictionary[kKey] = kValue; + self.dictionary[kKey2] = kValue2; + + NSDictionary *dict = self.dictionary.dictionary; + XCTAssertEqual([dict count], 2); + XCTAssertEqual(dict[kKey], kValue); + XCTAssertEqual(dict[kKey2], kValue2); +} + +@end diff --git a/GoogleUtilities/Example/Tests/Network/GULNetworkTest.m b/GoogleUtilities/Example/Tests/Network/GULNetworkTest.m new file mode 100644 index 0000000..4d31503 --- /dev/null +++ b/GoogleUtilities/Example/Tests/Network/GULNetworkTest.m @@ -0,0 +1,998 @@ +// Copyright 2018 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "GTMHTTPServer.h" + +#import +#import + +#import +#import +#import + +@interface GULNetwork () + +- (void)reachability:(GULReachabilityChecker *)reachability + statusChanged:(GULReachabilityStatus)status; + +@end + +@interface GULNetworkURLSession () + +- (void)maybeRemoveTempFilesAtURL:(NSURL *)tempFile expiringTime:(NSTimeInterval)expiringTime; + +@end + +@interface GULNetworkTest : XCTestCase +@end + +@implementation GULNetworkTest { + dispatch_queue_t _backgroundQueue; + GULNetwork *_network; + + /// Fake Server. + GTMHTTPServer *_httpServer; + GTMHTTPRequestMessage *_request; + int _statusCode; + + // For network reachability test. + BOOL _fakeNetworkIsReachable; + BOOL _currentNetworkStatus; + GULReachabilityStatus _fakeReachabilityStatus; +} + +#pragma mark - Setup and teardown + +- (void)setUp { + [super setUp]; + + _fakeNetworkIsReachable = YES; + _statusCode = 200; + _request = nil; + + _httpServer = [[GTMHTTPServer alloc] initWithDelegate:self]; + + // Start the server. + NSError *error = nil; + XCTAssertTrue([_httpServer start:&error], @"Failed to start HTTP server: %@", error); + + _network = [[GULNetwork alloc] init]; + _backgroundQueue = dispatch_queue_create("Test queue", DISPATCH_QUEUE_SERIAL); + + _request = nil; +} + +- (void)tearDown { + _network = nil; + _backgroundQueue = nil; + _request = nil; + + [_httpServer stop]; + _httpServer = nil; + + [super tearDown]; +} + +#pragma mark - Test reachability + +- (void)testReachability { + _network.reachabilityDelegate = self; + + id reachability = [_network valueForKey:@"_reachability"]; + XCTAssertNotNil(reachability); + + id reachabilityMock = OCMPartialMock(reachability); + [[[reachabilityMock stub] andCall:@selector(reachabilityStatus) onObject:self] + reachabilityStatus]; + + // Fake scenario with connectivity. + _fakeNetworkIsReachable = YES; + _fakeReachabilityStatus = kGULReachabilityViaWifi; + [_network reachability:reachabilityMock statusChanged:[reachabilityMock reachabilityStatus]]; + XCTAssertTrue([_network isNetworkConnected]); + XCTAssertEqual(_currentNetworkStatus, _fakeNetworkIsReachable); + + // Fake scenario without connectivity. + _fakeNetworkIsReachable = NO; + _fakeReachabilityStatus = kGULReachabilityNotReachable; + [_network reachability:reachabilityMock statusChanged:[reachabilityMock reachabilityStatus]]; + XCTAssertFalse([_network isNetworkConnected]); + XCTAssertEqual(_currentNetworkStatus, _fakeNetworkIsReachable); + + [reachabilityMock stopMocking]; + reachabilityMock = nil; +} + +#pragma mark - Test POST Foreground + +- (void)testSessionNetwork_POST_foreground { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + + NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; + + NSURL *url = + [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/2", _httpServer.port]]; + _statusCode = 200; + + [_network postURL:url + payload:uncompressedData + queue:_backgroundQueue + usingBackgroundSession:NO + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + [self verifyResponse:response error:error]; + [self verifyRequest]; + XCTAssertFalse(self->_network.hasUploadInProgress, "There must be no pending request"); + [expectation fulfill]; + }]; + XCTAssertTrue(self->_network.hasUploadInProgress, "There must be a pending request"); + // Wait a little bit so the server has enough time to respond. + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testSessionNetworkShouldReturnError_POST_foreground { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + + NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; + NSURL *url = + [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/3", _httpServer.port]]; + _statusCode = 500; + + [_network postURL:url + payload:uncompressedData + queue:_backgroundQueue + usingBackgroundSession:NO + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + XCTAssertEqual(((NSHTTPURLResponse *)response).statusCode, 500); + XCTAssertFalse(self->_network.hasUploadInProgress, "There must be no pending request"); + [expectation fulfill]; + }]; + XCTAssertTrue(self->_network.hasUploadInProgress, "There must be a pending request"); + // Wait a little bit so the server has enough time to respond. + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testNilURLNSURLSession_POST_foreground { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + + NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; + _statusCode = 200; + + [_network postURL:nil + payload:uncompressedData + queue:_backgroundQueue + usingBackgroundSession:NO + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + XCTAssertEqual(error.code, GULErrorCodeNetworkInvalidURL); + XCTAssertFalse(self->_network.hasUploadInProgress, "There must be no pending request"); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testEmptyURLNSURLSession_POST_foreground { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; + _statusCode = 200; + + [_network postURL:[NSURL URLWithString:@""] + payload:uncompressedData + queue:_backgroundQueue + usingBackgroundSession:NO + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + XCTAssertEqual(error.code, GULErrorCodeNetworkInvalidURL); + XCTAssertFalse(self->_network.hasUploadInProgress, "There must be no pending request"); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testEmptyPayloadNSURLSession_POST_foreground { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + NSData *uncompressedData = [[NSData alloc] init]; + NSURL *url = + [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/2", _httpServer.port]]; + _statusCode = 200; + + [_network postURL:url + payload:uncompressedData + queue:_backgroundQueue + usingBackgroundSession:NO + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + XCTAssertNil(error); + XCTAssertNotNil(self->_request); + XCTAssertEqualObjects([self->_request.URL absoluteString], [url absoluteString]); + XCTAssertFalse(self->_network.hasUploadInProgress, "There must be no pending request"); + [expectation fulfill]; + }]; + XCTAssertTrue(self->_network.hasUploadInProgress, "There must be a pending request"); + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testNilQueueNSURLSession_POST_foreground { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + + NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; + NSURL *url = + [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/1", _httpServer.port]]; + _statusCode = 200; + + [_network postURL:url + payload:uncompressedData + queue:nil + usingBackgroundSession:NO + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + [self verifyResponse:response error:error]; + [self verifyRequest]; + [expectation fulfill]; + }]; + + // Wait a little bit so the server has enough time to respond. + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testHasRequestPendingNSURLSession_POST_foreground { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; + NSURL *url = + [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/hasRequestPending", + _httpServer.port]]; + _statusCode = 200; + + [_network postURL:url + payload:uncompressedData + queue:_backgroundQueue + usingBackgroundSession:NO + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + [self verifyResponse:response error:error]; + [self verifyRequest]; + + XCTAssertFalse(self->_network.hasUploadInProgress, + @"hasUploadInProgress must be false"); + [expectation fulfill]; + }]; + + XCTAssertTrue(self->_network.hasUploadInProgress, @"hasUploadInProgress must be true"); + // Wait a little bit so the server has enough time to respond. + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +#pragma mark - Test POST Background + +- (void)testSessionNetwork_POST_background { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + + NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; + NSURL *url = + [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/2", _httpServer.port]]; + _statusCode = 200; + + [_network postURL:url + payload:uncompressedData + queue:_backgroundQueue + usingBackgroundSession:YES + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + [self verifyResponse:response error:error]; + [self verifyRequest]; + XCTAssertFalse(self->_network.hasUploadInProgress, "There must be no pending request"); + [expectation fulfill]; + }]; + XCTAssertTrue(self->_network.hasUploadInProgress, "There must be a pending request"); + // Wait a little bit so the server has enough time to respond. + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testSessionNetworkShouldReturnError_POST_background { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + + NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; + NSURL *url = + [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/3", _httpServer.port]]; + _statusCode = 500; + + [_network postURL:url + payload:uncompressedData + queue:_backgroundQueue + usingBackgroundSession:YES + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + XCTAssertEqual(((NSHTTPURLResponse *)response).statusCode, 500); + XCTAssertFalse(self->_network.hasUploadInProgress, "There must be no pending request"); + [expectation fulfill]; + }]; + XCTAssertTrue(self->_network.hasUploadInProgress, "There must be a pending request"); + // Wait a little bit so the server has enough time to respond. + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testNilURLNSURLSession_POST_background { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + + NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; + _statusCode = 200; + + [_network postURL:nil + payload:uncompressedData + queue:_backgroundQueue + usingBackgroundSession:YES + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + XCTAssertEqual(error.code, GULErrorCodeNetworkInvalidURL); + XCTAssertFalse(self->_network.hasUploadInProgress, "There must be no pending request"); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testEmptyURLNSURLSession_POST_background { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; + _statusCode = 200; + + [_network postURL:[NSURL URLWithString:@""] + payload:uncompressedData + queue:_backgroundQueue + usingBackgroundSession:YES + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + XCTAssertEqual(error.code, GULErrorCodeNetworkInvalidURL); + XCTAssertFalse(self->_network.hasUploadInProgress, "There must be no pending request"); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testEmptyPayloadNSURLSession_POST_background { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + NSData *uncompressedData = [[NSData alloc] init]; + NSURL *url = + [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/2", _httpServer.port]]; + _statusCode = 200; + + [_network postURL:url + payload:uncompressedData + queue:_backgroundQueue + usingBackgroundSession:YES + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + XCTAssertNil(error); + XCTAssertNotNil(self->_request); + XCTAssertEqualObjects([self->_request.URL absoluteString], [url absoluteString]); + XCTAssertFalse(self->_network.hasUploadInProgress, "There must be no pending request"); + [expectation fulfill]; + }]; + XCTAssertTrue(self->_network.hasUploadInProgress, "There must be a pending request"); + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testNilQueueNSURLSession_POST_background { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + + NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; + NSURL *url = + [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/1", _httpServer.port]]; + _statusCode = 200; + + [_network postURL:url + payload:uncompressedData + queue:nil + usingBackgroundSession:YES + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + [self verifyResponse:response error:error]; + [self verifyRequest]; + [expectation fulfill]; + }]; + + // Wait a little bit so the server has enough time to respond. + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testHasRequestPendingNSURLSession_POST_background { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + NSData *uncompressedData = [@"Google" dataUsingEncoding:NSUTF8StringEncoding]; + NSURL *url = + [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/hasRequestPending", + _httpServer.port]]; + _statusCode = 200; + + [_network postURL:url + payload:uncompressedData + queue:_backgroundQueue + usingBackgroundSession:YES + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + [self verifyResponse:response error:error]; + [self verifyRequest]; + + XCTAssertFalse(self->_network.hasUploadInProgress, + @"hasUploadInProgress must be false"); + [expectation fulfill]; + }]; + + XCTAssertTrue(self->_network.hasUploadInProgress, @"hasUploadInProgress must be true"); + // Wait a little bit so the server has enough time to respond. + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +#pragma mark - GET Methods Foreground + +- (void)testSessionNetworkAsync_GET_foreground { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + + NSURL *url = + [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/2", _httpServer.port]]; + _statusCode = 200; + + [_network getURL:url + headers:nil + queue:_backgroundQueue + usingBackgroundSession:NO + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + XCTAssertNotNil(data); + NSString *responseBody = + [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + XCTAssertEqualObjects(responseBody, @"Hello, World!"); + XCTAssertNil(error); + XCTAssertFalse(self->_network.hasUploadInProgress, "There must be no pending request"); + [expectation fulfill]; + }]; + XCTAssertTrue(self->_network.hasUploadInProgress, "There must be a pending request"); + // Wait a little bit so the server has enough time to respond. + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testSessionNetworkShouldReturnError_GET_foreground { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + NSURL *url = + [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/3", _httpServer.port]]; + _statusCode = 500; + + [_network getURL:url + headers:nil + queue:_backgroundQueue + usingBackgroundSession:NO + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + XCTAssertEqual(((NSHTTPURLResponse *)response).statusCode, 500); + XCTAssertFalse(self->_network.hasUploadInProgress, "There must be no pending request"); + [expectation fulfill]; + }]; + XCTAssertTrue(self->_network.hasUploadInProgress, "There must be a pending request"); + // Wait a little bit so the server has enough time to respond. + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testNilURLNSURLSession_GET_foreground { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + _statusCode = 200; + + [_network getURL:nil + headers:nil + queue:_backgroundQueue + usingBackgroundSession:NO + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + XCTAssertEqual(error.code, GULErrorCodeNetworkInvalidURL); + XCTAssertFalse(self->_network.hasUploadInProgress, "There must be no pending request"); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testEmptyURLNSURLSession_GET_foreground { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + _statusCode = 200; + + [_network getURL:[NSURL URLWithString:@""] + headers:nil + queue:_backgroundQueue + usingBackgroundSession:NO + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + XCTAssertEqual(error.code, GULErrorCodeNetworkInvalidURL); + XCTAssertFalse(self->_network.hasUploadInProgress, "There must be no pending request"); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testNilQueueNSURLSession_GET_foreground { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + + NSURL *url = + [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/1", _httpServer.port]]; + _statusCode = 200; + + [_network getURL:url + headers:nil + queue:nil + usingBackgroundSession:NO + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + XCTAssertNotNil(data); + NSString *responseBody = + [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + XCTAssertEqualObjects(responseBody, @"Hello, World!"); + XCTAssertNil(error); + XCTAssertFalse(self->_network.hasUploadInProgress, "There must be no pending request"); + [expectation fulfill]; + }]; + XCTAssertTrue(self->_network.hasUploadInProgress, "There must be a pending request"); + // Wait a little bit so the server has enough time to respond. + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testHasRequestPendingNSURLSession_GET_foreground { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + NSURL *url = + [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/hasRequestPending", + _httpServer.port]]; + _statusCode = 200; + + [_network getURL:url + headers:nil + queue:_backgroundQueue + usingBackgroundSession:NO + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + XCTAssertNotNil(data); + NSString *responseBody = + [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + XCTAssertEqualObjects(responseBody, @"Hello, World!"); + XCTAssertNil(error); + XCTAssertFalse(self->_network.hasUploadInProgress, + @"hasUploadInProgress must be false"); + [expectation fulfill]; + }]; + + XCTAssertTrue(self->_network.hasUploadInProgress, @"hasUploadInProgress must be true"); + // Wait a little bit so the server has enough time to respond. + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testHeaders_foreground { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + + NSURL *url = + [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/3", _httpServer.port]]; + _statusCode = 200; + + NSDictionary *headers = @{@"Version" : @"123"}; + + [_network getURL:url + headers:headers + queue:_backgroundQueue + usingBackgroundSession:NO + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + XCTAssertNotNil(data); + NSString *responseBody = + [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + XCTAssertEqualObjects(responseBody, @"Hello, World!"); + XCTAssertNil(error); + + NSString *version = [self->_request.allHeaderFieldValues valueForKey:@"Version"]; + XCTAssertEqualObjects(version, @"123"); + + [expectation fulfill]; + }]; + + // Wait a little bit so the server has enough time to respond. + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +#pragma mark - GET Methods Background + +- (void)testSessionNetworkAsync_GET_background { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + + NSURL *url = + [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/2", _httpServer.port]]; + _statusCode = 200; + + [_network getURL:url + headers:nil + queue:_backgroundQueue + usingBackgroundSession:YES + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + XCTAssertNotNil(data); + NSString *responseBody = + [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + XCTAssertEqualObjects(responseBody, @"Hello, World!"); + XCTAssertNil(error); + XCTAssertFalse(self->_network.hasUploadInProgress, "There must be no pending request"); + [expectation fulfill]; + }]; + XCTAssertTrue(self->_network.hasUploadInProgress, "There must be a pending request"); + // Wait a little bit so the server has enough time to respond. + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testSessionNetworkShouldReturnError_GET_background { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + NSURL *url = + [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/3", _httpServer.port]]; + _statusCode = 500; + + [_network getURL:url + headers:nil + queue:_backgroundQueue + usingBackgroundSession:YES + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + XCTAssertEqual(((NSHTTPURLResponse *)response).statusCode, 500); + XCTAssertFalse(self->_network.hasUploadInProgress, "There must be no pending request"); + [expectation fulfill]; + }]; + XCTAssertTrue(self->_network.hasUploadInProgress, "There must be a pending request"); + // Wait a little bit so the server has enough time to respond. + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testNilURLNSURLSession_GET_background { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + _statusCode = 200; + + [_network getURL:nil + headers:nil + queue:_backgroundQueue + usingBackgroundSession:YES + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + XCTAssertEqual(error.code, GULErrorCodeNetworkInvalidURL); + XCTAssertFalse(self->_network.hasUploadInProgress, "There must be no pending request"); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testEmptyURLNSURLSession_GET_background { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + _statusCode = 200; + + [_network getURL:[NSURL URLWithString:@""] + headers:nil + queue:_backgroundQueue + usingBackgroundSession:YES + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + XCTAssertEqual(error.code, GULErrorCodeNetworkInvalidURL); + XCTAssertFalse(self->_network.hasUploadInProgress, "There must be no pending request"); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testNilQueueNSURLSession_GET_background { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + + NSURL *url = + [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/1", _httpServer.port]]; + _statusCode = 200; + + [_network getURL:url + headers:nil + queue:nil + usingBackgroundSession:YES + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + XCTAssertNotNil(data); + NSString *responseBody = + [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + XCTAssertEqualObjects(responseBody, @"Hello, World!"); + XCTAssertNil(error); + XCTAssertFalse(self->_network.hasUploadInProgress, "There must be no pending request"); + [expectation fulfill]; + }]; + XCTAssertTrue(self->_network.hasUploadInProgress, "There must be a pending request"); + // Wait a little bit so the server has enough time to respond. + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testHasRequestPendingNSURLSession_GET_background { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + NSURL *url = + [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/hasRequestPending", + _httpServer.port]]; + _statusCode = 200; + + [_network getURL:url + headers:nil + queue:_backgroundQueue + usingBackgroundSession:YES + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + XCTAssertNotNil(data); + NSString *responseBody = + [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + XCTAssertEqualObjects(responseBody, @"Hello, World!"); + XCTAssertNil(error); + XCTAssertFalse(self->_network.hasUploadInProgress, + @"hasUploadInProgress must be false"); + [expectation fulfill]; + }]; + + XCTAssertTrue(self->_network.hasUploadInProgress, @"hasUploadInProgress must be true"); + // Wait a little bit so the server has enough time to respond. + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +- (void)testHeaders_background { + XCTestExpectation *expectation = [self expectationWithDescription:@"Expect block is called"]; + + NSURL *url = + [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%d/3", _httpServer.port]]; + _statusCode = 200; + + NSDictionary *headers = @{@"Version" : @"123"}; + + [_network getURL:url + headers:headers + queue:_backgroundQueue + usingBackgroundSession:YES + completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + XCTAssertNotNil(data); + NSString *responseBody = + [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + XCTAssertEqualObjects(responseBody, @"Hello, World!"); + XCTAssertNil(error); + + NSString *version = [self->_request.allHeaderFieldValues valueForKey:@"Version"]; + XCTAssertEqualObjects(version, @"123"); + + [expectation fulfill]; + }]; + + // Wait a little bit so the server has enough time to respond. + [self waitForExpectationsWithTimeout:10 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Timeout Error: %@", error); + } + }]; +} + +#pragma mark - Test clean up files + +- (void)testRemoveExpiredFiles { + NSError *writeError = nil; + NSFileManager *fileManager = [NSFileManager defaultManager]; + + GULNetworkURLSession *session = [[GULNetworkURLSession alloc] + initWithNetworkLoggerDelegate:(id)_network]; + NSArray *paths = + NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); + NSString *applicationSupportDirectory = paths.firstObject; + NSArray *tempPathComponents = @[ + applicationSupportDirectory, kGULNetworkApplicationSupportSubdirectory, + @"GULNetworkTemporaryDirectory" + ]; + NSURL *folderURL = [NSURL fileURLWithPathComponents:tempPathComponents]; + [fileManager createDirectoryAtURL:folderURL + withIntermediateDirectories:YES + attributes:nil + error:&writeError]; + + NSURL *tempFile1 = [folderURL URLByAppendingPathComponent:@"FIRUpload_temp_123"]; + [self createTempFileAtURL:tempFile1]; + NSURL *tempFile2 = [folderURL URLByAppendingPathComponent:@"FIRUpload_temp_456"]; + [self createTempFileAtURL:tempFile2]; + + XCTAssertTrue([fileManager fileExistsAtPath:tempFile1.path]); + XCTAssertTrue([fileManager fileExistsAtPath:tempFile2.path]); + + NSDate *now = + [[NSDate date] dateByAddingTimeInterval:1]; // Start mocking the clock to avoid flakiness. + id mockDate = OCMStrictClassMock([NSDate class]); + [[[mockDate stub] andReturn:now] date]; + + // The file should not be removed since it is not expired yet. + [session maybeRemoveTempFilesAtURL:folderURL expiringTime:20]; + XCTAssertTrue([fileManager fileExistsAtPath:tempFile1.path]); + XCTAssertTrue([fileManager fileExistsAtPath:tempFile2.path]); + + [mockDate stopMocking]; + mockDate = nil; + + now = [[NSDate date] dateByAddingTimeInterval:100]; // Move forward in time 100s. + mockDate = OCMStrictClassMock([NSDate class]); + [[[mockDate stub] andReturn:now] date]; + + [session maybeRemoveTempFilesAtURL:folderURL expiringTime:20]; + XCTAssertFalse([fileManager fileExistsAtPath:tempFile1.path]); + XCTAssertFalse([fileManager fileExistsAtPath:tempFile2.path]); + [mockDate stopMocking]; + mockDate = nil; +} + +#pragma mark - Internal Methods + +- (void)createTempFileAtURL:(NSURL *)fileURL { + // Create a dictionary and write it to file. + NSDictionary *someContent = @{@"object" : @"key"}; + [someContent writeToURL:fileURL atomically:YES]; +} + +- (void)verifyResponse:(NSHTTPURLResponse *)response error:(NSError *)error { + XCTAssertNil(error, @"Error is not expected"); + XCTAssertNotNil(response, @"Error is not expected"); +} + +- (void)verifyRequest { + XCTAssertNotNil(_request, @"Request cannot be nil"); + + // Test whether the request is compressed correctly. + NSData *requestBody = [_request body]; + NSData *decompressedRequestData = [NSData gul_dataByInflatingGzippedData:requestBody error:NULL]; + NSString *requestString = + [[NSString alloc] initWithData:decompressedRequestData encoding:NSUTF8StringEncoding]; + XCTAssertEqualObjects(requestString, @"Google", @"Request is not compressed correctly."); + + // The request has to be a POST. + XCTAssertEqualObjects([_request method], @"POST", @"Request method has to be POST"); + + // Content length has to be set correctly. + NSString *contentLength = [_request.allHeaderFieldValues valueForKey:@"Content-Length"]; + XCTAssertEqualObjects(contentLength, @"26", @"Content Length is incorrect"); + + NSString *contentEncoding = [_request.allHeaderFieldValues valueForKey:@"Content-Encoding"]; + XCTAssertEqualObjects(contentEncoding, @"gzip", @"Content Encoding is incorrect"); +} + +#pragma mark - Helper Methods + +- (GTMHTTPResponseMessage *)httpServer:(GTMHTTPServer *)server + handleRequest:(GTMHTTPRequestMessage *)request { + _request = request; + + NSData *html = + [@"Hello, World!" dataUsingEncoding:NSUTF8StringEncoding]; + return [GTMHTTPResponseMessage responseWithBody:html + contentType:@"text/html; charset=UTF-8" + statusCode:_statusCode]; +} + +- (BOOL)isReachable { + return _fakeNetworkIsReachable; +} + +- (GULReachabilityStatus)reachabilityStatus { + return _fakeReachabilityStatus; +} + +#pragma mark - FIRReachabilityDelegate + +- (void)reachabilityDidChange { + _currentNetworkStatus = _fakeNetworkIsReachable; +} + +@end diff --git a/GoogleUtilities/Example/Tests/Network/third_party/GTMHTTPServer.h b/GoogleUtilities/Example/Tests/Network/third_party/GTMHTTPServer.h new file mode 100644 index 0000000..cc51207 --- /dev/null +++ b/GoogleUtilities/Example/Tests/Network/third_party/GTMHTTPServer.h @@ -0,0 +1,173 @@ +/* Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTMHTTPServer.h +// +// This is a *very* *simple* webserver that can be built into something, it is +// not meant to stand up a site, it sends all requests to its delegate for +// processing on the main thread. It does not support pipelining, etc. It's +// great for places where you need a simple webserver to unittest some code +// that hits a server. +// +// NOTE: there are several TODOs left in here as markers for things that could +// be done if one wanted to add more to this class. +// +// Based a little on HTTPServer, part of the CocoaHTTPServer sample code found at +// https://opensource.apple.com/source/HTTPServer/HTTPServer-11/CocoaHTTPServer/ +// License for the CocoaHTTPServer sample code: +// +// Software License Agreement (BSD License) +// +// Copyright (c) 2011, Deusty, LLC +// All rights reserved. +// +// Redistribution and use of this software in source and binary forms, +// with or without modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above +// copyright notice, this list of conditions and the +// following disclaimer. +// +// * Neither the name of Deusty nor the names of its +// contributors may be used to endorse or promote products +// derived from this software without specific prior +// written permission of Deusty, LLC. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#import + +#if GTM_IPHONE_SDK +#import +#endif // GTM_IPHONE_SDK + +// Global contants needed for errors from start + +#undef _EXTERN +#undef _INITIALIZE_AS +#ifdef GTMHTTPSERVER_DEFINE_GLOBALS +#define _EXTERN +#define _INITIALIZE_AS(x) = x +#else +#define _EXTERN extern +#define _INITIALIZE_AS(x) +#endif + +_EXTERN NSString *const kGTMHTTPServerErrorDomain + _INITIALIZE_AS(@"com.google.mactoolbox.HTTPServerDomain"); +enum { + kGTMHTTPServerSocketCreateFailedError = -100, + kGTMHTTPServerBindFailedError = -101, + kGTMHTTPServerListenFailedError = -102, + kGTMHTTPServerHandleCreateFailedError = -103, +}; + +@class GTMHTTPRequestMessage, GTMHTTPResponseMessage; + +// ---------------------------------------------------------------------------- + +// See comment at top of file for the intened use of this class. +@interface GTMHTTPServer : NSObject { + @private + id delegate_; // WEAK + uint16_t port_; + BOOL reusePort_; + BOOL localhostOnly_; + NSFileHandle *listenHandle_; + NSMutableArray *connections_; +} + +// The delegate must support the httpServer:handleRequest: method in +// NSObject(GTMHTTPServerDelegateMethods) below. +- (id)initWithDelegate:(id)delegate; + +- (id)delegate; + +// Passing port zero will let one get assigned. +- (uint16_t)port; +- (void)setPort:(uint16_t)port; + +// Controls listening socket behavior: SO_REUSEADDR vs SO_REUSEPORT. +// The default is NO (SO_REUSEADDR) +- (BOOL)reusePort; +- (void)setReusePort:(BOOL)reusePort; + +// Receive connections on the localHost loopback address only or on all +// interfaces for this machine. The default is to only listen on localhost. +- (BOOL)localhostOnly; +- (void)setLocalhostOnly:(BOOL)yesno; + +// Start/Stop the web server. If there is an error starting up the server, |NO| +// is returned, and the specific startup failure can be returned in |error| (see +// above for the error domain and error codes). If the server is started, |YES| +// is returned and the server's delegate is called for any requests that come +// in. +- (BOOL)start:(NSError **)error; +- (void)stop; + +// returns the number of requests currently active in the server (i.e.-being +// read in, sent replies). +- (NSUInteger)activeRequestCount; + +@end + +@interface NSObject (GTMHTTPServerDelegateMethods) +- (GTMHTTPResponseMessage *)httpServer:(GTMHTTPServer *)server + handleRequest:(GTMHTTPRequestMessage *)request; +@end + +// ---------------------------------------------------------------------------- + +// Encapsulates an http request, one of these is sent to the server's delegate +// for each request. +@interface GTMHTTPRequestMessage : NSObject { + @private + CFHTTPMessageRef message_; +} +- (NSString *)version; +- (NSURL *)URL; +- (NSString *)method; +- (NSData *)body; +- (NSDictionary *)allHeaderFieldValues; +@end + +// ---------------------------------------------------------------------------- + +// Encapsulates an http response, the server's delegate should return one for +// each request received. +@interface GTMHTTPResponseMessage : NSObject { + @private + CFHTTPMessageRef message_; +} ++ (instancetype)responseWithString:(NSString *)plainText; ++ (instancetype)responseWithHTMLString:(NSString *)htmlString; ++ (instancetype)responseWithBody:(NSData *)body + contentType:(NSString *)contentType + statusCode:(int)statusCode; ++ (instancetype)emptyResponseWithCode:(int)statusCode; +// TODO: class method for redirections? +// TODO: add helper for expire/no-cache +- (void)setValue:(NSString *)value forHeaderField:(NSString *)headerField; +- (void)setHeaderValuesFromDictionary:(NSDictionary *)dict; +@end diff --git a/GoogleUtilities/Example/Tests/Network/third_party/GTMHTTPServer.m b/GoogleUtilities/Example/Tests/Network/third_party/GTMHTTPServer.m new file mode 100644 index 0000000..526d8c5 --- /dev/null +++ b/GoogleUtilities/Example/Tests/Network/third_party/GTMHTTPServer.m @@ -0,0 +1,630 @@ +/* Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// Based a little on HTTPServer, part of the CocoaHTTPServer sample code found at +// https://opensource.apple.com/source/HTTPServer/HTTPServer-11/CocoaHTTPServer/ +// License for the CocoaHTTPServer sample code: +// +// Software License Agreement (BSD License) +// +// Copyright (c) 2011, Deusty, LLC +// All rights reserved. +// +// Redistribution and use of this software in source and binary forms, +// with or without modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above +// copyright notice, this list of conditions and the +// following disclaimer. +// +// * Neither the name of Deusty nor the names of its +// contributors may be used to endorse or promote products +// derived from this software without specific prior +// written permission of Deusty, LLC. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#import +#import +#import + +#define GTMHTTPSERVER_DEFINE_GLOBALS +#import "GTMHTTPServer.h" + +// avoid some of GTM's promiscuous dependencies +#ifndef _GTMDevLog +#define _GTMDevLog NSLog +#endif + +#ifndef GTM_STATIC_CAST +#define GTM_STATIC_CAST(type, object) ((type *)(object)) +#endif + +#ifndef GTMCFAutorelease +#define GTMCFAutorelease(x) ([(id)x autorelease]) +#endif + +@interface GTMHTTPServer (PrivateMethods) +- (void)acceptedConnectionNotification:(NSNotification *)notification; +- (NSMutableDictionary *)connectionWithFileHandle:(NSFileHandle *)fileHandle; +- (void)dataAvailableNotification:(NSNotification *)notification; +- (NSMutableDictionary *)lookupConnection:(NSFileHandle *)fileHandle; +- (void)closeConnection:(NSMutableDictionary *)connDict; +- (void)sendResponseOnNewThread:(NSMutableDictionary *)connDict; +- (void)sentResponse:(NSMutableDictionary *)connDict; +@end + +// keys for our connection dictionaries +static NSString *kFileHandle = @"FileHandle"; +static NSString *kRequest = @"Request"; +static NSString *kResponse = @"Response"; + +@interface GTMHTTPRequestMessage (PrivateHelpers) +- (BOOL)isHeaderComplete; +- (BOOL)appendData:(NSData *)data; +- (NSString *)headerFieldValueForKey:(NSString *)key; +- (UInt32)contentLength; +- (void)setBody:(NSData *)body; +@end + +@interface GTMHTTPResponseMessage () +- (id)initWithBody:(NSData *)body contentType:(NSString *)contentType statusCode:(int)statusCode; +- (NSData *)serializedData; +@end + +@implementation GTMHTTPServer + +- (id)init { + return [self initWithDelegate:nil]; +} + +- (id)initWithDelegate:(id)delegate { + self = [super init]; + if (self) { + if (!delegate) { + _GTMDevLog(@"missing delegate"); + [self release]; + return nil; + } + delegate_ = delegate; + +#ifndef NS_BLOCK_ASSERTIONS + BOOL isDelegateOK = [delegate_ respondsToSelector:@selector(httpServer:handleRequest:)]; + NSAssert(isDelegateOK, @"GTMHTTPServer delegate lacks handleRequest sel"); +#endif + + localhostOnly_ = YES; + connections_ = [[NSMutableArray alloc] init]; + } + return self; +} + +- (void)dealloc { + [self stop]; + [connections_ release]; + [super dealloc]; +} + +#if !TARGET_OS_IPHONE +- (void)finalize { + [self stop]; + [super finalize]; +} +#endif + +- (id)delegate { + return delegate_; +} + +- (uint16_t)port { + return port_; +} + +- (void)setPort:(uint16_t)port { + port_ = port; +} + +- (BOOL)reusePort { + return reusePort_; +} + +- (void)setReusePort:(BOOL)yesno { + reusePort_ = yesno; +} + +- (BOOL)localhostOnly { + return localhostOnly_; +} + +- (void)setLocalhostOnly:(BOOL)yesno { + localhostOnly_ = yesno; +} + +- (BOOL)start:(NSError **)error { + NSAssert(listenHandle_ == nil, @"start called when we already have a listenHandle_"); + + if (error) *error = NULL; + + NSInteger startFailureCode = 0; + int fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd <= 0) { + // COV_NF_START - we'd need to use up *all* sockets to test this? + startFailureCode = kGTMHTTPServerSocketCreateFailedError; + goto startFailed; + // COV_NF_END + } + + // enable address reuse quicker after we are done w/ our socket + int yes = 1; + int sock_opt = reusePort_ ? SO_REUSEPORT : SO_REUSEADDR; + if (setsockopt(fd, SOL_SOCKET, sock_opt, (void *)&yes, (socklen_t)sizeof(yes)) != 0) { + _GTMDevLog(@"failed to mark the socket as reusable"); // COV_NF_LINE + } + + // bind + struct sockaddr_in addr; + bzero(&addr, sizeof(addr)); + addr.sin_len = sizeof(addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(port_); + if (localhostOnly_) { + addr.sin_addr.s_addr = htonl(0x7F000001); + } else { + // COV_NF_START - testing this could cause a leopard firewall prompt during tests. + addr.sin_addr.s_addr = htonl(INADDR_ANY); + // COV_NF_END + } + if (bind(fd, (struct sockaddr *)(&addr), (socklen_t)sizeof(addr)) != 0) { + startFailureCode = kGTMHTTPServerBindFailedError; + goto startFailed; + } + + // collect the port back out + if (port_ == 0) { + socklen_t len = (socklen_t)sizeof(addr); + if (getsockname(fd, (struct sockaddr *)(&addr), &len) == 0) { + port_ = ntohs(addr.sin_port); + } + } + + // tell it to listen for connections + if (listen(fd, 5) != 0) { + // COV_NF_START + startFailureCode = kGTMHTTPServerListenFailedError; + goto startFailed; + // COV_NF_END + } + + // now use a filehandle to accept connections + listenHandle_ = [[NSFileHandle alloc] initWithFileDescriptor:fd closeOnDealloc:YES]; + if (listenHandle_ == nil) { + // COV_NF_START - we'd need to run out of memory to test this? + startFailureCode = kGTMHTTPServerHandleCreateFailedError; + goto startFailed; + // COV_NF_END + } + + // setup notifications for connects + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + [center addObserver:self + selector:@selector(acceptedConnectionNotification:) + name:NSFileHandleConnectionAcceptedNotification + object:listenHandle_]; + [listenHandle_ acceptConnectionInBackgroundAndNotify]; + + // TODO: maybe hit the delegate incase it wants to register w/ NSNetService, + // or just know we're up and running? + + return YES; + +startFailed: + if (error) { + *error = [[[NSError alloc] initWithDomain:kGTMHTTPServerErrorDomain + code:startFailureCode + userInfo:nil] autorelease]; + } + if (fd > 0) { + close(fd); + } + return NO; +} + +- (void)stop { + if (listenHandle_) { + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + [center removeObserver:self + name:NSFileHandleConnectionAcceptedNotification + object:listenHandle_]; + [listenHandle_ release]; + listenHandle_ = nil; + // TODO: maybe hit the delegate in case it wants to unregister w/ + // NSNetService, or just know we've stopped running? + } + [connections_ removeAllObjects]; +} + +- (NSUInteger)activeRequestCount { + return [connections_ count]; +} + +- (NSString *)description { + NSString *result = + [NSString stringWithFormat:@"%@<%p>{ port=%d localHostOnly=%@ status=%@ }", [self class], + self, port_, (localhostOnly_ ? @"YES" : @"NO"), + (listenHandle_ != nil ? @"Started" : @"Stopped")]; + return result; +} + +@end + +@implementation GTMHTTPServer (PrivateMethods) + +- (void)acceptedConnectionNotification:(NSNotification *)notification { + NSDictionary *userInfo = [notification userInfo]; + NSFileHandle *newConnection = [userInfo objectForKey:NSFileHandleNotificationFileHandleItem]; + NSAssert1(newConnection != nil, @"failed to get the connection in the notification: %@", + notification); + + // make sure we accept more... + [listenHandle_ acceptConnectionInBackgroundAndNotify]; + + // TODO: could let the delegate look at the address, before we start working + // on it. + + NSMutableDictionary *connDict = [self connectionWithFileHandle:newConnection]; + [connections_ addObject:connDict]; +} + +- (NSMutableDictionary *)connectionWithFileHandle:(NSFileHandle *)fileHandle { + NSMutableDictionary *result = [NSMutableDictionary dictionary]; + + [result setObject:fileHandle forKey:kFileHandle]; + + GTMHTTPRequestMessage *request = [[[GTMHTTPRequestMessage alloc] init] autorelease]; + [result setObject:request forKey:kRequest]; + + // setup for data notifications + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + [center addObserver:self + selector:@selector(dataAvailableNotification:) + name:NSFileHandleReadCompletionNotification + object:fileHandle]; + [fileHandle readInBackgroundAndNotify]; + + return result; +} + +- (void)dataAvailableNotification:(NSNotification *)notification { + NSFileHandle *connectionHandle = GTM_STATIC_CAST(NSFileHandle, [notification object]); + NSMutableDictionary *connDict = [self lookupConnection:connectionHandle]; + if (connDict == nil) return; // we are no longer tracking this one + + NSDictionary *userInfo = [notification userInfo]; + NSData *readData = [userInfo objectForKey:NSFileHandleNotificationDataItem]; + if ([readData length] == 0) { + // remote side closed + [self closeConnection:connDict]; + return; + } + + // Use a local pool to keep memory down incase the runloop we're in doesn't + // drain until it gets a UI event. + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + @try { + // Like Apple's sample, we just keep adding data until we get a full header + // and any referenced body. + + GTMHTTPRequestMessage *request = [connDict objectForKey:kRequest]; + [request appendData:readData]; + + // Is the header complete yet? + if (![request isHeaderComplete]) { + // more data... + [connectionHandle readInBackgroundAndNotify]; + } else { + // Do we have all the body? + UInt32 contentLength = [request contentLength]; + NSData *body = [request body]; + NSUInteger bodyLength = [body length]; + if (contentLength > bodyLength) { + // need more data... + [connectionHandle readInBackgroundAndNotify]; + } else { + if (contentLength < bodyLength) { + // We got extra (probably someone trying to pipeline on us), trim + // and let the extra data go... + NSData *newBody = [NSData dataWithBytes:[body bytes] length:contentLength]; + [request setBody:newBody]; + _GTMDevLog(@"Got %lu extra bytes on http request, ignoring them", + (unsigned long)(bodyLength - contentLength)); + } + + GTMHTTPResponseMessage *response = nil; + @try { + // Off to the delegate + response = [delegate_ httpServer:self handleRequest:request]; + } @catch (NSException *e) { + _GTMDevLog(@"Exception trying to handle http request: %@", e); + } // COV_NF_LINE - radar 5851992 only reachable w/ an uncaught exception which isn't + // testable + + if (response) { + // We don't support connection reuse, so we add (force) the header to + // close every connection. + [response setValue:@"close" forHeaderField:@"Connection"]; + + // spawn thread to send reply (since we do a blocking send) + [connDict setObject:response forKey:kResponse]; + [NSThread detachNewThreadSelector:@selector(sendResponseOnNewThread:) + toTarget:self + withObject:connDict]; + } else { + // No response, shut it down + [self closeConnection:connDict]; + } + } + } + } @catch (NSException *e) { // COV_NF_START + _GTMDevLog(@"exception while read data: %@", e); + // exception while dealing with the connection, close it + } // COV_NF_END + @finally { + [pool drain]; + } +} + +- (NSMutableDictionary *)lookupConnection:(NSFileHandle *)fileHandle { + NSMutableDictionary *result = nil; + for (NSMutableDictionary *connDict in connections_) { + if (fileHandle == [connDict objectForKey:kFileHandle]) { + result = connDict; + break; + } + } + return result; +} + +- (void)closeConnection:(NSMutableDictionary *)connDict { + // remove the notification + NSFileHandle *connectionHandle = [connDict objectForKey:kFileHandle]; + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + [center removeObserver:self name:NSFileHandleReadCompletionNotification object:connectionHandle]; + // in a non GC world, we're fine just letting the connect get closed when + // the object is release when it comes out of connections_, but in a GC world + // it won't get cleaned up + [connectionHandle closeFile]; + + // remove it from the list + [connections_ removeObject:connDict]; +} + +- (void)sendResponseOnNewThread:(NSMutableDictionary *)connDict { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + @try { + GTMHTTPResponseMessage *response = [connDict objectForKey:kResponse]; + NSFileHandle *connectionHandle = [connDict objectForKey:kFileHandle]; + NSData *serialized = [response serializedData]; + [connectionHandle writeData:serialized]; + } @catch (NSException *e) { // COV_NF_START - causing an exception here is to hard in a test + // TODO: let the delegate know about the exception (but do it on the main + // thread) + _GTMDevLog(@"exception while sending reply: %@", e); + } // COV_NF_END + + // back to the main thread to close things down + [self performSelectorOnMainThread:@selector(sentResponse:) withObject:connDict waitUntilDone:NO]; + + [pool release]; +} + +- (void)sentResponse:(NSMutableDictionary *)connDict { + // make sure we're still tracking this connection (in case server was stopped) + NSFileHandle *connection = [connDict objectForKey:kFileHandle]; + NSMutableDictionary *connDict2 = [self lookupConnection:connection]; + if (connDict != connDict2) return; + + // TODO: message the delegate that it was sent + + // close it down + [self closeConnection:connDict]; +} + +@end + +#pragma mark - + +@implementation GTMHTTPRequestMessage + +- (id)init { + self = [super init]; + if (self) { + message_ = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, YES); + } + return self; +} + +- (void)dealloc { + if (message_) { + CFRelease(message_); + } + [super dealloc]; +} + +- (NSString *)version { + return GTMCFAutorelease(CFHTTPMessageCopyVersion(message_)); +} + +- (NSURL *)URL { + return GTMCFAutorelease(CFHTTPMessageCopyRequestURL(message_)); +} + +- (NSString *)method { + return GTMCFAutorelease(CFHTTPMessageCopyRequestMethod(message_)); +} + +- (NSData *)body { + return GTMCFAutorelease(CFHTTPMessageCopyBody(message_)); +} + +- (NSDictionary *)allHeaderFieldValues { + return GTMCFAutorelease(CFHTTPMessageCopyAllHeaderFields(message_)); +} + +- (NSString *)description { + CFStringRef desc = CFCopyDescription(message_); + NSString *result = [NSString stringWithFormat:@"%@<%p>{ message=%@ }", [self class], self, desc]; + CFRelease(desc); + return result; +} + +@end + +@implementation GTMHTTPRequestMessage (PrivateHelpers) + +- (BOOL)isHeaderComplete { + return CFHTTPMessageIsHeaderComplete(message_) ? YES : NO; +} + +- (BOOL)appendData:(NSData *)data { + return CFHTTPMessageAppendBytes(message_, [data bytes], (CFIndex)[data length]) ? YES : NO; +} + +- (NSString *)headerFieldValueForKey:(NSString *)key { + CFStringRef value = NULL; + if (key) { + value = CFHTTPMessageCopyHeaderFieldValue(message_, (CFStringRef)key); + } + return GTMCFAutorelease(value); +} + +- (UInt32)contentLength { + return (UInt32)[[self headerFieldValueForKey:@"Content-Length"] intValue]; +} + +- (void)setBody:(NSData *)body { + if (!body) { + body = [NSData data]; // COV_NF_LINE - can only happen in we fail to make the new data object + } + CFHTTPMessageSetBody(message_, (CFDataRef)body); +} + +@end + +#pragma mark - + +@implementation GTMHTTPResponseMessage + +- (id)init { + return [self initWithBody:nil contentType:nil statusCode:0]; +} + +- (id)initWithBody:(NSData *)body contentType:(NSString *)contentType statusCode:(int)statusCode { + self = [super init]; + if (self) { + if ((statusCode < 100) || (statusCode > 599)) { + [self release]; + return nil; + } + message_ = + CFHTTPMessageCreateResponse(kCFAllocatorDefault, statusCode, NULL, kCFHTTPVersion1_0); + if (!message_) { + // COV_NF_START + [self release]; + return nil; + // COV_NF_END + } + NSUInteger bodyLength = 0; + if (body) { + bodyLength = [body length]; + CFHTTPMessageSetBody(message_, (CFDataRef)body); + } + if ([contentType length] == 0) { + contentType = @"text/html"; + } + NSString *bodyLenStr = [NSString stringWithFormat:@"%lu", (unsigned long)bodyLength]; + [self setValue:bodyLenStr forHeaderField:@"Content-Length"]; + [self setValue:contentType forHeaderField:@"Content-Type"]; + } + return self; +} + +- (void)dealloc { + if (message_) { + CFRelease(message_); + } + [super dealloc]; +} + ++ (instancetype)responseWithString:(NSString *)plainText { + NSData *body = [plainText dataUsingEncoding:NSUTF8StringEncoding]; + return [self responseWithBody:body contentType:@"text/plain; charset=UTF-8" statusCode:200]; +} + ++ (instancetype)responseWithHTMLString:(NSString *)htmlString { + return [self responseWithBody:[htmlString dataUsingEncoding:NSUTF8StringEncoding] + contentType:@"text/html; charset=UTF-8" + statusCode:200]; +} + ++ (instancetype)responseWithBody:(NSData *)body + contentType:(NSString *)contentType + statusCode:(int)statusCode { + return [[[[self class] alloc] initWithBody:body contentType:contentType statusCode:statusCode] + autorelease]; +} + ++ (instancetype)emptyResponseWithCode:(int)statusCode { + return + [[[[self class] alloc] initWithBody:nil contentType:nil statusCode:statusCode] autorelease]; +} + +- (void)setValue:(NSString *)value forHeaderField:(NSString *)headerField { + if ([headerField length] == 0) return; + if (value == nil) { + value = @""; + } + CFHTTPMessageSetHeaderFieldValue(message_, (CFStringRef)headerField, (CFStringRef)value); +} + +- (void)setHeaderValuesFromDictionary:(NSDictionary *)dict { + for (id key in dict) { + id value = [dict valueForKey:key]; + [self setValue:value forHeaderField:key]; + } +} + +- (NSString *)description { + CFStringRef desc = CFCopyDescription(message_); + NSString *result = [NSString stringWithFormat:@"%@<%p>{ message=%@ }", [self class], self, desc]; + CFRelease(desc); + return result; +} + +- (NSData *)serializedData { + return GTMCFAutorelease(CFHTTPMessageCopySerializedMessage(message_)); +} + +@end diff --git a/GoogleUtilities/Example/Tests/Reachability/GULReachabilityCheckerTest.m b/GoogleUtilities/Example/Tests/Reachability/GULReachabilityCheckerTest.m new file mode 100644 index 0000000..da9fbf1 --- /dev/null +++ b/GoogleUtilities/Example/Tests/Reachability/GULReachabilityCheckerTest.m @@ -0,0 +1,358 @@ +// Copyright 2018 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import + +#import "GULReachabilityChecker+Internal.h" + +@interface GULReachabilityCheckerTest : XCTestCase { + @private + GULReachabilityChecker *checker_; + NSMutableArray *statuses_; + BOOL createFail_; + BOOL setCallbackFail_; + BOOL scheduleUnscheduleFail_; +} + +- (void *)createReachabilityWithAllocator:(CFAllocatorRef)allocator withName:(const char *)hostname; +- (BOOL)reachability:(const void *)reachability + setCallback:(SCNetworkReachabilityCallBack)callback + withContext:(SCNetworkReachabilityContext *)context; +- (BOOL)scheduleReachability:(const void *)reachability + runLoop:(CFRunLoopRef)runLoop + runLoopMode:(CFStringRef)runLoopMode; +- (BOOL)unscheduleReachability:(const void *)reachability + runLoop:(CFRunLoopRef)runLoop + runLoopMode:(CFStringRef)runLoopMode; +- (void)releaseReachability:(const void *)reachability; +@end + +static NSString *const kHostname = @"www.google.com"; +static const void *kFakeReachabilityObject = (const void *)0x8badf00d; + +static GULReachabilityCheckerTest *FakeReachabilityTest = nil; + +static struct { + int callsMade; + int createCall; + int setCallbackCall; + int scheduleCall; + int unscheduleCall; + int releaseCall; + void (*callback)(SCNetworkReachabilityRef, SCNetworkReachabilityFlags, void *info); + void *callbackInfo; +} FakeReachability; + +static SCNetworkReachabilityRef ReachabilityCreateWithName(CFAllocatorRef allocator, + const char *hostname) { + return (SCNetworkReachabilityRef) + [FakeReachabilityTest createReachabilityWithAllocator:allocator withName:hostname]; +} + +static Boolean ReachabilitySetCallback(SCNetworkReachabilityRef reachability, + SCNetworkReachabilityCallBack callback, + SCNetworkReachabilityContext *context) { + return [FakeReachabilityTest reachability:reachability setCallback:callback withContext:context]; +} + +static Boolean ReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef reachability, + CFRunLoopRef runLoop, + CFStringRef runLoopMode) { + return [FakeReachabilityTest scheduleReachability:reachability + runLoop:runLoop + runLoopMode:runLoopMode]; +} + +static Boolean ReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef reachability, + CFRunLoopRef runLoop, + CFStringRef runLoopMode) { + return [FakeReachabilityTest unscheduleReachability:reachability + runLoop:runLoop + runLoopMode:runLoopMode]; +} + +static void ReachabilityRelease(CFTypeRef reachability) { + [FakeReachabilityTest releaseReachability:reachability]; +} + +static const struct GULReachabilityApi kTestReachabilityApi = { + ReachabilityCreateWithName, ReachabilitySetCallback, ReachabilityScheduleWithRunLoop, + ReachabilityUnscheduleFromRunLoop, ReachabilityRelease, +}; + +@implementation GULReachabilityCheckerTest + +- (void)resetFakeReachability { + FakeReachabilityTest = self; + FakeReachability.callsMade = 0; + FakeReachability.createCall = -1; + FakeReachability.setCallbackCall = -1; + FakeReachability.scheduleCall = -1; + FakeReachability.unscheduleCall = -1; + FakeReachability.releaseCall = -1; + FakeReachability.callback = NULL; + FakeReachability.callbackInfo = NULL; +} + +- (void)setUp { + [super setUp]; + + [self resetFakeReachability]; + createFail_ = NO; + setCallbackFail_ = NO; + scheduleUnscheduleFail_ = NO; + + checker_ = [[GULReachabilityChecker alloc] initWithReachabilityDelegate:self withHost:kHostname]; + statuses_ = [[NSMutableArray alloc] init]; +} + +- (void *)createReachabilityWithAllocator:(CFAllocatorRef)allocator + withName:(const char *)hostname { + XCTAssertTrue(allocator == kCFAllocatorDefault, @""); + XCTAssertEqual(strcmp(hostname, [kHostname UTF8String]), 0, @""); + XCTAssertEqual(FakeReachability.callsMade, 0, @"create call must always come first."); + XCTAssertEqual(FakeReachability.createCall, -1, @"create call must only be called once."); + FakeReachability.createCall = ++FakeReachability.callsMade; + return createFail_ ? NULL : (void *)kFakeReachabilityObject; +} + +- (BOOL)reachability:(const void *)reachability + setCallback:(SCNetworkReachabilityCallBack)callback + withContext:(SCNetworkReachabilityContext *)context { + XCTAssertEqual(reachability, kFakeReachabilityObject, @"got bad object"); + XCTAssertEqual((int)context->version, 0, @""); + XCTAssertEqual(context->info, (__bridge void *)checker_, @""); + XCTAssertEqual((void *)context->retain, NULL, @""); + XCTAssertEqual((void *)context->release, NULL, @""); + XCTAssertEqual((void *)context->copyDescription, NULL, @""); + XCTAssertEqual(FakeReachability.setCallbackCall, -1, @"setCallback should only be called once."); + FakeReachability.setCallbackCall = ++FakeReachability.callsMade; + XCTAssertTrue(callback != NULL, @""); + FakeReachability.callback = callback; + XCTAssertTrue(context->info != NULL, @""); + FakeReachability.callbackInfo = context->info; + return setCallbackFail_ ? NO : YES; +} + +- (BOOL)scheduleReachability:(const void *)reachability + runLoop:(CFRunLoopRef)runLoop + runLoopMode:(CFStringRef)runLoopMode { + XCTAssertEqual(reachability, kFakeReachabilityObject, @"got bad object"); + XCTAssertEqual(runLoop, CFRunLoopGetMain(), @"bad run loop"); + XCTAssertEqualObjects((__bridge NSString *)runLoopMode, + (__bridge NSString *)kCFRunLoopCommonModes, @"bad run loop mode"); + XCTAssertEqual(FakeReachability.scheduleCall, -1, + @"scheduleWithRunLoop should only be called once."); + FakeReachability.scheduleCall = ++FakeReachability.callsMade; + return scheduleUnscheduleFail_ ? NO : YES; +} + +- (BOOL)unscheduleReachability:(const void *)reachability + runLoop:(CFRunLoopRef)runLoop + runLoopMode:(CFStringRef)runLoopMode { + XCTAssertEqual(reachability, kFakeReachabilityObject, @"got bad object"); + XCTAssertEqual(runLoop, CFRunLoopGetMain(), @"bad run loop"); + XCTAssertEqualObjects((__bridge NSString *)runLoopMode, + (__bridge NSString *)kCFRunLoopCommonModes, @"bad run loop mode"); + XCTAssertEqual(FakeReachability.unscheduleCall, -1, + @"unscheduleFromRunLoop should only be called once."); + FakeReachability.unscheduleCall = ++FakeReachability.callsMade; + return scheduleUnscheduleFail_ ? NO : YES; +} + +- (void)releaseReachability:(const void *)reachability { + XCTAssertEqual(reachability, kFakeReachabilityObject, @"got bad object"); + XCTAssertEqual(FakeReachability.releaseCall, -1, @"release should only be called once."); + FakeReachability.releaseCall = ++FakeReachability.callsMade; +} + +- (void)reachability:(GULReachabilityChecker *)reachability + statusChanged:(GULReachabilityStatus)status { + [statuses_ addObject:[NSNumber numberWithInt:(int)status]]; +} + +#pragma mark - Test + +- (void)testApiHappyPath { + [checker_ setReachabilityApi:&kTestReachabilityApi]; + XCTAssertEqual([checker_ reachabilityApi], &kTestReachabilityApi, @""); + + XCTAssertFalse(checker_.isActive, @""); + XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); + + XCTAssertTrue([checker_ start], @""); + + XCTAssertTrue(checker_.isActive, @""); + XCTAssertEqual([statuses_ count], (NSUInteger)0, @""); + XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); + + FakeReachability.callback(kFakeReachabilityObject, 0, FakeReachability.callbackInfo); + + XCTAssertEqual([statuses_ count], (NSUInteger)1, @""); + XCTAssertEqual([(NSNumber *)[statuses_ objectAtIndex:0] intValue], + (int)kGULReachabilityNotReachable, @""); + XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityNotReachable, @""); + + FakeReachability.callback(kFakeReachabilityObject, kSCNetworkReachabilityFlagsReachable, + FakeReachability.callbackInfo); + + XCTAssertEqual([statuses_ count], (NSUInteger)2, @""); + XCTAssertEqual([(NSNumber *)[statuses_ objectAtIndex:1] intValue], (int)kGULReachabilityViaWifi, + @""); + XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityViaWifi, @""); + + FakeReachability.callback( + kFakeReachabilityObject, + kSCNetworkReachabilityFlagsReachable | kSCNetworkReachabilityFlagsConnectionRequired, + FakeReachability.callbackInfo); + + XCTAssertEqual([statuses_ count], (NSUInteger)3, @""); + XCTAssertEqual([(NSNumber *)[statuses_ objectAtIndex:2] intValue], + (int)kGULReachabilityNotReachable, @""); + XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityNotReachable, @""); + +#if TARGET_OS_IOS || TARGET_OS_TV + FakeReachability.callback( + kFakeReachabilityObject, + kSCNetworkReachabilityFlagsReachable | kSCNetworkReachabilityFlagsIsWWAN, + FakeReachability.callbackInfo); + + XCTAssertEqual([statuses_ count], (NSUInteger)4, @""); + XCTAssertEqual([(NSNumber *)[statuses_ objectAtIndex:3] intValue], + (int)kGULReachabilityViaCellular, @""); + XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityViaCellular, @""); + + FakeReachability.callback(kFakeReachabilityObject, kSCNetworkReachabilityFlagsIsWWAN, + FakeReachability.callbackInfo); + + XCTAssertEqual([statuses_ count], (NSUInteger)5, @""); + XCTAssertEqual([(NSNumber *)[statuses_ objectAtIndex:4] intValue], + (int)kGULReachabilityNotReachable, @""); + XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityNotReachable, @""); +#endif + + [checker_ stop]; + + XCTAssertFalse(checker_.isActive, @""); + XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); + + XCTAssertEqual(FakeReachability.callsMade, 5, @""); + + XCTAssertEqual(FakeReachability.createCall, 1, @""); + XCTAssertEqual(FakeReachability.setCallbackCall, 2, @""); + XCTAssertEqual(FakeReachability.scheduleCall, 3, @""); + XCTAssertEqual(FakeReachability.unscheduleCall, 4, @""); + XCTAssertEqual(FakeReachability.releaseCall, 5, @""); +} + +- (void)testApiCreateFail { + [checker_ setReachabilityApi:&kTestReachabilityApi]; + XCTAssertEqual([checker_ reachabilityApi], &kTestReachabilityApi, @""); + + createFail_ = YES; + + XCTAssertFalse(checker_.isActive, @""); + XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); + + XCTAssertFalse([checker_ start], @""); + + XCTAssertFalse(checker_.isActive, @""); + XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); + + [checker_ stop]; + + XCTAssertFalse(checker_.isActive, @""); + XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); + + XCTAssertEqual(FakeReachability.callsMade, 1, @""); + + XCTAssertEqual(FakeReachability.createCall, 1, @""); + XCTAssertEqual(FakeReachability.setCallbackCall, -1, @""); + XCTAssertEqual(FakeReachability.scheduleCall, -1, @""); + XCTAssertEqual(FakeReachability.unscheduleCall, -1, @""); + XCTAssertEqual(FakeReachability.releaseCall, -1, @""); + + XCTAssertEqual([statuses_ count], (NSUInteger)0, @""); +} + +- (void)testApiCallbackFail { + [checker_ setReachabilityApi:&kTestReachabilityApi]; + XCTAssertEqual([checker_ reachabilityApi], &kTestReachabilityApi, @""); + + setCallbackFail_ = YES; + + XCTAssertFalse(checker_.isActive, @""); + XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); + + XCTAssertFalse([checker_ start], @""); + + XCTAssertFalse(checker_.isActive, @""); + XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); + + [checker_ stop]; + + XCTAssertFalse(checker_.isActive, @""); + XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); + + XCTAssertEqual(FakeReachability.callsMade, 3, @""); + + XCTAssertEqual(FakeReachability.createCall, 1, @""); + XCTAssertEqual(FakeReachability.setCallbackCall, 2, @""); + XCTAssertEqual(FakeReachability.scheduleCall, -1, @""); + XCTAssertEqual(FakeReachability.unscheduleCall, -1, @""); + XCTAssertEqual(FakeReachability.releaseCall, 3, @""); + + XCTAssertEqual([statuses_ count], (NSUInteger)0, @""); +} + +- (void)testApiScheduleFail { + [checker_ setReachabilityApi:&kTestReachabilityApi]; + XCTAssertEqual([checker_ reachabilityApi], &kTestReachabilityApi, @""); + + scheduleUnscheduleFail_ = YES; + + XCTAssertFalse(checker_.isActive, @""); + XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); + + XCTAssertFalse([checker_ start], @""); + + XCTAssertFalse(checker_.isActive, @""); + XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); + + [checker_ stop]; + + XCTAssertFalse(checker_.isActive, @""); + XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @""); + + XCTAssertEqual(FakeReachability.callsMade, 4, @""); + + XCTAssertEqual(FakeReachability.createCall, 1, @""); + XCTAssertEqual(FakeReachability.setCallbackCall, 2, @""); + XCTAssertEqual(FakeReachability.scheduleCall, 3, @""); + XCTAssertEqual(FakeReachability.unscheduleCall, -1, @""); + XCTAssertEqual(FakeReachability.releaseCall, 4, @""); + + XCTAssertEqual([statuses_ count], (NSUInteger)0, @""); +} + +- (void)testBadHost { + XCTAssertNil([[GULReachabilityChecker alloc] initWithReachabilityDelegate:self withHost:nil], + @"Creating a checker with nil hostname must fail."); + XCTAssertNil([[GULReachabilityChecker alloc] initWithReachabilityDelegate:self withHost:@""], + @"Creating a checker with empty hostname must fail."); +} + +@end diff --git a/GoogleUtilities/Example/Tests/Tests-Info.plist b/GoogleUtilities/Example/Tests/Tests-Info.plist new file mode 100644 index 0000000..169b6f7 --- /dev/null +++ b/GoogleUtilities/Example/Tests/Tests-Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/GoogleUtilities/Example/iOS/AppDelegate.h b/GoogleUtilities/Example/iOS/AppDelegate.h new file mode 100644 index 0000000..aaabd8f --- /dev/null +++ b/GoogleUtilities/Example/iOS/AppDelegate.h @@ -0,0 +1,21 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +@import UIKit; + +@interface AppDelegate : UIResponder + +@property(strong, nonatomic) UIWindow *window; + +@end diff --git a/GoogleUtilities/Example/iOS/AppDelegate.m b/GoogleUtilities/Example/iOS/AppDelegate.m new file mode 100644 index 0000000..395e235 --- /dev/null +++ b/GoogleUtilities/Example/iOS/AppDelegate.m @@ -0,0 +1,55 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "AppDelegate.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application { + // Sent when the application is about to move from active to inactive state. This can occur for + // certain types of temporary interruptions (such as an incoming phone call or SMS message) or + // when the user quits the application and it begins the transition to the background state. Use + // this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. + // Games should use this method to pause the game. +} + +- (void)applicationDidEnterBackground:(UIApplication *)application { + // Use this method to release shared resources, save user data, invalidate timers, and store + // enough application state information to restore your application to its current state in case + // it is terminated later. If your application supports background execution, this method is + // called instead of applicationWillTerminate: when the user quits. +} + +- (void)applicationWillEnterForeground:(UIApplication *)application { + // Called as part of the transition from the background to the inactive state; here you can undo + // many of the changes made on entering the background. +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If + // the application was previously in the background, optionally refresh the user interface. +} + +- (void)applicationWillTerminate:(UIApplication *)application { + // Called when the application is about to terminate. Save data if appropriate. See also + // applicationDidEnterBackground:. +} + +@end diff --git a/GoogleUtilities/Example/iOS/Base.lproj/LaunchScreen.storyboard b/GoogleUtilities/Example/iOS/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..66a7681 --- /dev/null +++ b/GoogleUtilities/Example/iOS/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GoogleUtilities/Example/iOS/Base.lproj/Main.storyboard b/GoogleUtilities/Example/iOS/Base.lproj/Main.storyboard new file mode 100644 index 0000000..d164a23 --- /dev/null +++ b/GoogleUtilities/Example/iOS/Base.lproj/Main.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GoogleUtilities/Example/iOS/GoogleUtilities-Info.plist b/GoogleUtilities/Example/iOS/GoogleUtilities-Info.plist new file mode 100644 index 0000000..fc26896 --- /dev/null +++ b/GoogleUtilities/Example/iOS/GoogleUtilities-Info.plist @@ -0,0 +1,54 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/GoogleUtilities/Example/iOS/Images.xcassets/AppIcon.appiconset/Contents.json b/GoogleUtilities/Example/iOS/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d8db8d6 --- /dev/null +++ b/GoogleUtilities/Example/iOS/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/GoogleUtilities/Example/iOS/ViewController.h b/GoogleUtilities/Example/iOS/ViewController.h new file mode 100644 index 0000000..5db7982 --- /dev/null +++ b/GoogleUtilities/Example/iOS/ViewController.h @@ -0,0 +1,19 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +@import UIKit; + +@interface ViewController : UIViewController + +@end diff --git a/GoogleUtilities/Example/iOS/ViewController.m b/GoogleUtilities/Example/iOS/ViewController.m new file mode 100644 index 0000000..6d4676b --- /dev/null +++ b/GoogleUtilities/Example/iOS/ViewController.m @@ -0,0 +1,33 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "ViewController.h" + +@interface ViewController () + +@end + +@implementation ViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view, typically from a nib. +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +@end diff --git a/GoogleUtilities/Example/iOS/main.m b/GoogleUtilities/Example/iOS/main.m new file mode 100644 index 0000000..266ff2d --- /dev/null +++ b/GoogleUtilities/Example/iOS/main.m @@ -0,0 +1,22 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +@import UIKit; +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/GoogleUtilities/Example/macOS/AppDelegate.h b/GoogleUtilities/Example/macOS/AppDelegate.h new file mode 100644 index 0000000..bb974b0 --- /dev/null +++ b/GoogleUtilities/Example/macOS/AppDelegate.h @@ -0,0 +1,21 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +@interface AppDelegate : NSObject + +@end diff --git a/GoogleUtilities/Example/macOS/AppDelegate.m b/GoogleUtilities/Example/macOS/AppDelegate.m new file mode 100644 index 0000000..da2832c --- /dev/null +++ b/GoogleUtilities/Example/macOS/AppDelegate.m @@ -0,0 +1,33 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + // Insert code here to initialize your application +} + +- (void)applicationWillTerminate:(NSNotification *)aNotification { + // Insert code here to tear down your application +} + +@end diff --git a/GoogleUtilities/Example/macOS/Base.lproj/Main.storyboard b/GoogleUtilities/Example/macOS/Base.lproj/Main.storyboard new file mode 100644 index 0000000..1cd523a --- /dev/null +++ b/GoogleUtilities/Example/macOS/Base.lproj/Main.storyboard @@ -0,0 +1,693 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GoogleUtilities/Example/macOS/Info.plist b/GoogleUtilities/Example/macOS/Info.plist new file mode 100644 index 0000000..f5a2636 --- /dev/null +++ b/GoogleUtilities/Example/macOS/Info.plist @@ -0,0 +1,37 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1.0 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + NSHumanReadableCopyright + Copyright © 2017 Google. All rights reserved. + NSMainStoryboardFile + Main + NSPrincipalClass + NSApplication + + diff --git a/GoogleUtilities/Example/macOS/ViewController.h b/GoogleUtilities/Example/macOS/ViewController.h new file mode 100644 index 0000000..18a57af --- /dev/null +++ b/GoogleUtilities/Example/macOS/ViewController.h @@ -0,0 +1,21 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +@interface ViewController : NSViewController + +@end diff --git a/GoogleUtilities/Example/macOS/ViewController.m b/GoogleUtilities/Example/macOS/ViewController.m new file mode 100644 index 0000000..b94574f --- /dev/null +++ b/GoogleUtilities/Example/macOS/ViewController.m @@ -0,0 +1,33 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "ViewController.h" + +@implementation ViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Do any additional setup after loading the view. +} + +- (void)setRepresentedObject:(id)representedObject { + [super setRepresentedObject:representedObject]; + + // Update the view, if already loaded. +} + +@end diff --git a/GoogleUtilities/Example/macOS/main.m b/GoogleUtilities/Example/macOS/main.m new file mode 100644 index 0000000..45ed70f --- /dev/null +++ b/GoogleUtilities/Example/macOS/main.m @@ -0,0 +1,21 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +int main(int argc, const char* argv[]) { + return NSApplicationMain(argc, argv); +} diff --git a/GoogleUtilities/Example/tvOS/AppDelegate.h b/GoogleUtilities/Example/tvOS/AppDelegate.h new file mode 100644 index 0000000..013891c --- /dev/null +++ b/GoogleUtilities/Example/tvOS/AppDelegate.h @@ -0,0 +1,21 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@interface AppDelegate : UIResponder + +@property(strong, nonatomic) UIWindow *window; + +@end diff --git a/GoogleUtilities/Example/tvOS/AppDelegate.m b/GoogleUtilities/Example/tvOS/AppDelegate.m new file mode 100644 index 0000000..2e4e32f --- /dev/null +++ b/GoogleUtilities/Example/tvOS/AppDelegate.m @@ -0,0 +1,59 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application { + // Sent when the application is about to move from active to inactive state. This can occur for + // certain types of temporary interruptions (such as an incoming phone call or SMS message) or + // when the user quits the application and it begins the transition to the background state. Use + // this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. + // Games should use this method to pause the game. +} + +- (void)applicationDidEnterBackground:(UIApplication *)application { + // Use this method to release shared resources, save user data, invalidate timers, and store + // enough application state information to restore your application to its current state in case + // it is terminated later. If your application supports background execution, this method is + // called instead of applicationWillTerminate: when the user quits. +} + +- (void)applicationWillEnterForeground:(UIApplication *)application { + // Called as part of the transition from the background to the active state; here you can undo + // many of the changes made on entering the background. +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If + // the application was previously in the background, optionally refresh the user interface. +} + +- (void)applicationWillTerminate:(UIApplication *)application { + // Called when the application is about to terminate. Save data if appropriate. See also + // applicationDidEnterBackground:. +} + +@end diff --git a/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..7f06667 --- /dev/null +++ b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,11 @@ +{ + "images" : [ + { + "idiom" : "tv" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json new file mode 100644 index 0000000..d29f024 --- /dev/null +++ b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "layers" : [ + { + "filename" : "Front.imagestacklayer" + }, + { + "filename" : "Middle.imagestacklayer" + }, + { + "filename" : "Back.imagestacklayer" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..7f06667 --- /dev/null +++ b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,11 @@ +{ + "images" : [ + { + "idiom" : "tv" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..7f06667 --- /dev/null +++ b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,11 @@ +{ + "images" : [ + { + "idiom" : "tv" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..16a370d --- /dev/null +++ b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json new file mode 100644 index 0000000..d29f024 --- /dev/null +++ b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "layers" : [ + { + "filename" : "Front.imagestacklayer" + }, + { + "filename" : "Middle.imagestacklayer" + }, + { + "filename" : "Back.imagestacklayer" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..16a370d --- /dev/null +++ b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..16a370d --- /dev/null +++ b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json new file mode 100644 index 0000000..b03ded1 --- /dev/null +++ b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json @@ -0,0 +1,32 @@ +{ + "assets" : [ + { + "size" : "1280x768", + "idiom" : "tv", + "filename" : "App Icon - App Store.imagestack", + "role" : "primary-app-icon" + }, + { + "size" : "400x240", + "idiom" : "tv", + "filename" : "App Icon.imagestack", + "role" : "primary-app-icon" + }, + { + "size" : "2320x720", + "idiom" : "tv", + "filename" : "Top Shelf Image Wide.imageset", + "role" : "top-shelf-image-wide" + }, + { + "size" : "1920x720", + "idiom" : "tv", + "filename" : "Top Shelf Image.imageset", + "role" : "top-shelf-image" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json new file mode 100644 index 0000000..16a370d --- /dev/null +++ b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json new file mode 100644 index 0000000..16a370d --- /dev/null +++ b/GoogleUtilities/Example/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/GoogleUtilities/Example/tvOS/Assets.xcassets/Contents.json b/GoogleUtilities/Example/tvOS/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/GoogleUtilities/Example/tvOS/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/GoogleUtilities/Example/tvOS/Assets.xcassets/LaunchImage.launchimage/Contents.json b/GoogleUtilities/Example/tvOS/Assets.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000..d746a60 --- /dev/null +++ b/GoogleUtilities/Example/tvOS/Assets.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "orientation" : "landscape", + "idiom" : "tv", + "extent" : "full-screen", + "minimum-system-version" : "11.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "tv", + "extent" : "full-screen", + "minimum-system-version" : "9.0", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/GoogleUtilities/Example/tvOS/Info.plist b/GoogleUtilities/Example/tvOS/Info.plist new file mode 100644 index 0000000..d76f80a --- /dev/null +++ b/GoogleUtilities/Example/tvOS/Info.plist @@ -0,0 +1,37 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + UIUserInterfaceStyle + Automatic + + diff --git a/GoogleUtilities/Example/tvOS/Main.storyboard b/GoogleUtilities/Example/tvOS/Main.storyboard new file mode 100644 index 0000000..72d5e22 --- /dev/null +++ b/GoogleUtilities/Example/tvOS/Main.storyboard @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GoogleUtilities/Example/tvOS/ViewController.h b/GoogleUtilities/Example/tvOS/ViewController.h new file mode 100644 index 0000000..b6115b8 --- /dev/null +++ b/GoogleUtilities/Example/tvOS/ViewController.h @@ -0,0 +1,19 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@interface ViewController : UIViewController + +@end diff --git a/GoogleUtilities/Example/tvOS/ViewController.m b/GoogleUtilities/Example/tvOS/ViewController.m new file mode 100644 index 0000000..6d4676b --- /dev/null +++ b/GoogleUtilities/Example/tvOS/ViewController.m @@ -0,0 +1,33 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "ViewController.h" + +@interface ViewController () + +@end + +@implementation ViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view, typically from a nib. +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +@end diff --git a/GoogleUtilities/Example/tvOS/main.m b/GoogleUtilities/Example/tvOS/main.m new file mode 100644 index 0000000..d9e6654 --- /dev/null +++ b/GoogleUtilities/Example/tvOS/main.m @@ -0,0 +1,22 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/scripts/build.sh b/scripts/build.sh index 57d4010..ce3e58e 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -168,6 +168,13 @@ case "$product-$method-$platform" in build \ test + RunXcodebuild \ + -workspace 'GoogleUtilities/Example/GoogleUtilities.xcworkspace' \ + -scheme "Example_$platform" \ + "${xcb_flags[@]}" \ + build \ + test + if [[ $platform == 'iOS' ]]; then RunXcodebuild \ -workspace 'Functions/Example/FirebaseFunctions.xcworkspace' \ diff --git a/scripts/if_changed.sh b/scripts/if_changed.sh index 51217d6..e0e2a18 100755 --- a/scripts/if_changed.sh +++ b/scripts/if_changed.sh @@ -46,7 +46,7 @@ elif [[ -z "$TRAVIS_COMMIT_RANGE" ]]; then else case "$PROJECT-$METHOD" in Firebase-*) - check_changes '^(Firebase|Functions|Example)' + check_changes '^(Firebase|Functions|GoogleUtilities|Example)' ;; Firestore-xcodebuild|Firestore-pod-lib-lint) diff --git a/scripts/install_prereqs.sh b/scripts/install_prereqs.sh index c943369..9124299 100755 --- a/scripts/install_prereqs.sh +++ b/scripts/install_prereqs.sh @@ -26,11 +26,13 @@ case "$PROJECT-$PLATFORM-$METHOD" in gem install xcpretty bundle exec pod install --project-directory=Example --repo-update bundle exec pod install --project-directory=Functions/Example + bundle exec pod install --project-directory=GoogleUtilities/Example ;; Firebase-*-xcodebuild) gem install xcpretty bundle exec pod install --project-directory=Example --repo-update + bundle exec pod install --project-directory=GoogleUtilities/Example ;; Firestore-*-xcodebuild) -- cgit v1.2.3