blob: ca6d38d5e517597f2fc2d0c1f9c0df466e15a69d [file] [log] [blame]
/* Copyright (c) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <XCTest/XCTest.h>
#import "GTMHTTPFetcherTestServer.h"
#import "GTMHTTPFetcherService.h"
@interface GTMHTTPFetcherServiceTest : XCTestCase {
GTMHTTPFetcherTestServer *testServer_;
BOOL isServerRunning_;
}
@end
@implementation GTMHTTPFetcherServiceTest
// file available in Tests folder
static NSString *const kValidFileName = @"gettysburgaddress.txt";
- (NSString *)docRootPath {
// find a test file
NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
XCTAssertNotNil(testBundle);
// use the directory of the test file as the root directory for our server
NSString *docFolder = [testBundle resourcePath];
return docFolder;
}
- (void)setUp {
NSString *docRoot = [self docRootPath];
testServer_ = [[GTMHTTPFetcherTestServer alloc] initWithDocRoot:docRoot];
isServerRunning_ = (testServer_ != nil);
XCTAssertTrue(isServerRunning_,
@">>> http test server failed to launch; skipping"
" service tests\n");
}
- (void)tearDown {
[testServer_ release];
testServer_ = nil;
isServerRunning_ = NO;
}
- (void)testFetcherService {
if (!isServerRunning_) return;
// Utility blocks for counting array entries for a specific host
NSUInteger (^URLsPerHost) (NSArray *, NSString *) = ^(NSArray *URLs,
NSString *host) {
NSUInteger counter = 0;
for (NSURL *url in URLs) {
if ([host isEqual:[url host]]) counter++;
}
return counter;
};
NSUInteger (^FetchersPerHost) (NSArray *, NSString *) = ^(NSArray *fetchers,
NSString *host) {
NSArray *fetcherURLs = [fetchers valueForKeyPath:@"mutableRequest.URL"];
return URLsPerHost(fetcherURLs, host);
};
// Utility block for finding the minimum priority fetcher for a specific host
NSInteger (^PriorityPerHost) (NSArray *, NSString *) = ^(NSArray *fetchers,
NSString *host) {
NSInteger val = NSIntegerMax;
for (GTMHTTPFetcher *fetcher in fetchers) {
if ([host isEqual:[[fetcher.mutableRequest URL] host]]) {
val = MIN(val, fetcher.servicePriority);
}
}
return val;
};
// We'll verify we fetched from the server the same data that is on disk
NSString *gettysburgPath = [testServer_ localPathForFile:kValidFileName];
NSData *gettysburgAddress = [NSData dataWithContentsOfFile:gettysburgPath];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
// We'll create 10 fetchers. Only 2 should run simultaneously.
// 1 should fail; the rest should succeeed.
const NSUInteger kMaxRunningFetchersPerHost = 2;
NSString *const kUserAgent = @"ServiceTest-UA";
const NSTimeInterval kTimeout = 55;
GTMHTTPFetcherService *service = [[[GTMHTTPFetcherService alloc] init] autorelease];
service.maxRunningFetchersPerHost = kMaxRunningFetchersPerHost;
service.fetchHistory.shouldRememberETags = NO;
service.userAgent = kUserAgent;
service.timeout = kTimeout;
service.allowLocalhostRequest = YES;
// Make URLs for a valid fetch, a fetch that returns a status error,
// and a valid fetch with a different host
NSURL *validFileURL = [testServer_ localURLForFile:kValidFileName];
NSString *invalidFile = [kValidFileName stringByAppendingString:@"?status=400"];
NSURL *invalidFileURL = [testServer_ localURLForFile:invalidFile];
NSString *validURLStr = [validFileURL absoluteString];
NSString *altValidURLStr = [validURLStr stringByReplacingOccurrencesOfString:@"localhost"
withString:@"127.0.0.1"];
NSURL *altValidURL = [NSURL URLWithString:altValidURLStr];
XCTAssertEqualObjects([validFileURL host], @"localhost", @"unexpected host");
XCTAssertEqualObjects([invalidFileURL host], @"localhost", @"unexpected host");
XCTAssertEqualObjects([altValidURL host], @"127.0.0.1", @"unexpected host");
// Make an array with the urls from the different hosts, including one
// that will fail with a status 400 error
NSMutableArray *urlArray = [NSMutableArray array];
for (int idx = 1; idx <= 4; idx++) [urlArray addObject:validFileURL];
[urlArray addObject:invalidFileURL];
for (int idx = 1; idx <= 5; idx++) [urlArray addObject:validFileURL];
for (int idx = 1; idx <= 5; idx++) [urlArray addObject:altValidURL];
for (int idx = 1; idx <= 5; idx++) [urlArray addObject:validFileURL];
NSUInteger totalNumberOfFetchers = [urlArray count];
__block NSMutableArray *pending = [NSMutableArray array];
__block NSMutableArray *running = [NSMutableArray array];
__block NSMutableArray *completed = [NSMutableArray array];
NSUInteger priorityVal = 0;
// Create all the fetchers
for (NSURL *fileURL in urlArray) {
GTMHTTPFetcher *fetcher = [service fetcherWithURL:fileURL];
// Fetcher start notification
[nc addObserverForName:kGTMHTTPFetcherStartedNotification
object:fetcher
queue:nil
usingBlock:^(NSNotification *note) {
// Verify that we have at most two fetchers running for this
// fetcher's host
[running addObject:fetcher];
[pending removeObject:fetcher];
NSMutableURLRequest *fetcherReq = [fetcher mutableRequest];
NSURL *fetcherReqURL = [fetcherReq URL];
NSString *host = [fetcherReqURL host];
NSUInteger numberRunning = FetchersPerHost(running, host);
XCTAssertTrue(numberRunning > 0, @"count error");
XCTAssertTrue(numberRunning <= kMaxRunningFetchersPerHost,
@"too many running");
NSInteger pendingPriority = PriorityPerHost(pending, host);
XCTAssertTrue(fetcher.servicePriority <= pendingPriority,
@"a pending fetcher has greater priority");
XCTAssertEqual([service numberOfFetchers],
[running count] + [pending count],
@"fetcher count off");
XCTAssertEqual([service numberOfRunningFetchers],
[running count], @"running off");
XCTAssertEqual([service numberOfDelayedFetchers],
[pending count], @"delayed off");
NSArray *matches =
[service issuedFetchersWithRequestURL:fetcherReqURL];
NSUInteger idx = NSNotFound;
if (matches) {
idx = [matches indexOfObjectIdenticalTo:fetcher];
}
XCTAssertTrue(idx != NSNotFound, @"Missing %@ in %@",
fetcherReqURL, matches);
NSURL *fakeURL =
[NSURL URLWithString:@"http://example.com/bad"];
matches = [service issuedFetchersWithRequestURL:fakeURL];
XCTAssertEqual([matches count], (NSUInteger)0);
NSString *agent = [fetcherReq valueForHTTPHeaderField:@"User-Agent"];
XCTAssertEqualObjects(agent, kUserAgent);
XCTAssertEqual([fetcherReq timeoutInterval], kTimeout);
}];
// Fetcher stopped notification
[nc addObserverForName:kGTMHTTPFetcherStoppedNotification
object:fetcher
queue:nil
usingBlock:^(NSNotification *note) {
// Verify that we only have two fetchers running
[completed addObject:fetcher];
[running removeObject:fetcher];
NSString *host = [[[fetcher mutableRequest] URL] host];
NSUInteger numberRunning = FetchersPerHost(running, host);
NSUInteger numberPending = FetchersPerHost(pending, host);
NSUInteger numberCompleted = FetchersPerHost(completed, host);
XCTAssertTrue(numberRunning <= kMaxRunningFetchersPerHost,
@"too many running");
XCTAssertTrue(numberPending + numberRunning + numberCompleted <= URLsPerHost(urlArray, host),
@"%d issued running (pending:%u running:%u completed:%u)",
(unsigned int)totalNumberOfFetchers, (unsigned int)numberPending,
(unsigned int)numberRunning, (unsigned int)numberCompleted);
XCTAssertEqual([service numberOfFetchers],
[running count] + [pending count] + 1,
@"fetcher count off");
XCTAssertEqual([service numberOfRunningFetchers],
[running count] + 1, @"running off");
XCTAssertEqual([service numberOfDelayedFetchers],
[pending count], @"delayed off");
}];
[pending addObject:fetcher];
// Set the fetch priority to a value that cycles 0, 1, -1, 0, ...
priorityVal++;
if (priorityVal > 1) priorityVal = -1;
fetcher.servicePriority = priorityVal;
// Start this fetcher
[fetcher beginFetchWithCompletionHandler:^(NSData *fetchData, NSError *fetchError) {
// Callback
//
// The query should be empty except for the URL with a status code
NSString *query = [[[fetcher mutableRequest] URL] query];
BOOL isValidRequest = ([query length] == 0);
if (isValidRequest) {
XCTAssertEqualObjects(fetchData, gettysburgAddress,
@"Bad fetch data");
XCTAssertNil(fetchError, @"unexpected %@ %@",
fetchError, [fetchError userInfo]);
} else {
// This is the query with ?status=400
XCTAssertEqual([fetchError code], (NSInteger) 400, @"expected error");
}
}];
}
[service waitForCompletionOfAllFetchersWithTimeout:10];
XCTAssertEqual([pending count], (NSUInteger) 0,
@"still pending: %@", pending);
XCTAssertEqual([running count], (NSUInteger) 0,
@"still running: %@", running);
XCTAssertEqual([completed count], (NSUInteger) totalNumberOfFetchers,
@"incomplete");
XCTAssertEqual([service numberOfFetchers], (NSUInteger) 0,
@"service non-empty");
}
- (void)testStopAllFetchers {
if (!isServerRunning_) return;
GTMHTTPFetcherService *service = [[[GTMHTTPFetcherService alloc] init] autorelease];
service.maxRunningFetchersPerHost = 2;
service.fetchHistory.shouldRememberETags = NO;
service.allowLocalhostRequest = YES;
// Create three fetchers for each of two URLs, so there should be
// two running and one delayed for each
NSURL *validFileURL = [testServer_ localURLForFile:kValidFileName];
NSString *validURLStr = [validFileURL absoluteString];
NSString *altValidURLStr = [validURLStr stringByReplacingOccurrencesOfString:@"localhost"
withString:@"127.0.0.1"];
NSURL *altValidURL = [NSURL URLWithString:altValidURLStr];
// Add three fetches for each URL
NSMutableArray *urlArray = [NSMutableArray array];
[urlArray addObject:validFileURL];
[urlArray addObject:altValidURL];
[urlArray addObject:validFileURL];
[urlArray addObject:altValidURL];
[urlArray addObject:validFileURL];
[urlArray addObject:altValidURL];
// Create and start all the fetchers
for (NSURL *fileURL in urlArray) {
GTMHTTPFetcher *fetcher = [service fetcherWithURL:fileURL];
[fetcher beginFetchWithCompletionHandler:^(NSData *fetchData, NSError *fetchError) {
// We shouldn't reach any of the callbacks
XCTFail(@"Fetcher completed but should have been stopped");
}];
}
// Two hosts
XCTAssertEqual([service.runningHosts count], (NSUInteger)2, @"hosts running");
XCTAssertEqual([service.delayedHosts count], (NSUInteger)2, @"hosts delayed");
// We should see two fetchers running and one delayed for each host
NSArray *localhosts = [service.runningHosts objectForKey:@"localhost"];
XCTAssertEqual([localhosts count], (NSUInteger)2, @"hosts running");
localhosts = [service.delayedHosts objectForKey:@"localhost"];
XCTAssertEqual([localhosts count], (NSUInteger)1, @"hosts delayed");
[service stopAllFetchers];
XCTAssertEqual([service.runningHosts count], (NSUInteger)0, @"hosts running");
XCTAssertEqual([service.delayedHosts count], (NSUInteger)0, @"hosts delayed");
}
@end