EMASCurlTests/EMASCurlMetricObserverTest.m (127 lines of code) (raw):
//
// EMASCurlMetricObserverTest.m
// EMASCurlTests
//
// Created by xuyecan on 2024/12/17.
//
#import <Foundation/Foundation.h>
#import <XCTest/XCTest.h>
#import <EMASCurl/EMASCurl.h>
#import "EMASCurlTestConstants.h"
static NSURLSession *session;
@interface EMASCurlMetricsTestBase : XCTestCase
@end
@implementation EMASCurlMetricsTestBase
- (void)setUp {
[super setUp];
}
- (void)downloadDataWithMetrics:(NSString *)endpoint {
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", endpoint, PATH_DOWNLOAD_1MB_DATA_AT_200KBPS_SPEED]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__weak typeof(self) weakSelf = self;
__block NSNumber *totalTimeConsumed = 0;
[EMASCurlProtocol setMetricsObserverBlockForRequest:request metricsObserverBlock:^(NSURLRequest * _Nonnull request, BOOL success, NSError * error, double nameLookUpTimeMS, double connectTimeMs, double appConnectTimeMs, double preTransferTimeMs, double startTransferTimeMs, double totalTimeMs) {
XCTAssertGreaterThan(totalTimeMs, startTransferTimeMs, @"Total time should be after start transfer time");
// Log metrics for debugging
NSLog(@"Network Metrics:\n"
"DNS Lookup: %.2fms\n"
"Connect: %.2fms\n"
"App Connect: %.2fms\n"
"Pre-transfer: %.2fms\n"
"Start Transfer: %.2fms\n"
"Total: %.2fms",
nameLookUpTimeMS, connectTimeMs, appConnectTimeMs,
preTransferTimeMs, startTransferTimeMs, totalTimeMs);
totalTimeConsumed = [NSNumber numberWithDouble:totalTimeMs];
}];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
XCTAssertNil(error, @"Download failed with error: %@", error);
XCTAssertNotNil(response, @"No response received");
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
XCTAssertEqual(httpResponse.statusCode, 200, @"Expected 200 status code");
XCTAssertEqual(1024 * 1024, [data length], @"Expected 1MB of data");
XCTAssertGreaterThan([totalTimeConsumed doubleValue], 0, @"Total time should be recorded");
dispatch_semaphore_signal(semaphore);
}];
[dataTask resume];
XCTAssertEqual(dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC)), 0, @"Download request timed out");
}
@end
@interface EMASCurlMetricsTestHttp11 : EMASCurlMetricsTestBase
@end
@implementation EMASCurlMetricsTestHttp11
- (void)setUp {
[super setUp];
[EMASCurlProtocol setHTTPVersion:HTTP1];
[EMASCurlProtocol setDebugLogEnabled:YES];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
[EMASCurlProtocol installIntoSessionConfiguration:config];
session = [NSURLSession sessionWithConfiguration:config delegate:nil delegateQueue:nil];
}
- (void)testDownloadDataWithMetrics {
[self downloadDataWithMetrics:HTTP11_ENDPOINT];
}
@end
@interface EMASCurlMetricsTestHttp2 : EMASCurlMetricsTestBase
@end
@implementation EMASCurlMetricsTestHttp2
- (void)setUp {
[super setUp];
[EMASCurlProtocol setHTTPVersion:HTTP2];
[EMASCurlProtocol setDebugLogEnabled:YES];
NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
NSString *certPath = [testBundle pathForResource:@"ca" ofType:@"crt"];
XCTAssertNotNil(certPath, @"Certificate file not found in test bundle.");
[EMASCurlProtocol setSelfSignedCAFilePath:certPath];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
[EMASCurlProtocol installIntoSessionConfiguration:config];
session = [NSURLSession sessionWithConfiguration:config delegate:nil delegateQueue:nil];
}
- (void)testDownloadDataWithMetrics {
[self downloadDataWithMetrics:HTTP2_ENDPOINT];
}
@end
@interface MockDNSResolver : NSObject <EMASCurlProtocolDNSResolver>
@end
@implementation MockDNSResolver
+ (NSString *)resolveDomain:(NSString *)domain {
// Simulate DNS resolution by sleeping for a known duration
[NSThread sleepForTimeInterval:2]; // 500ms delay
return @"127.0.0.1";
}
@end
@interface EMASCurlMetricsTestCustomDNS : EMASCurlMetricsTestBase
@end
@implementation EMASCurlMetricsTestCustomDNS
- (void)setUp {
[super setUp];
[EMASCurlProtocol setHTTPVersion:HTTP2];
[EMASCurlProtocol setDebugLogEnabled:YES];
[EMASCurlProtocol setDNSResolver:[MockDNSResolver class]];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
[EMASCurlProtocol installIntoSessionConfiguration:config];
session = [NSURLSession sessionWithConfiguration:config delegate:nil delegateQueue:nil];
}
- (void)tearDown {
[super tearDown];
}
- (void)testCustomDNSResolutionMetrics {
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", HTTP11_ENDPOINT, PATH_DOWNLOAD_1MB_DATA_AT_200KBPS_SPEED]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block BOOL metricsReceived = NO;
double startTime = [[NSDate date] timeIntervalSince1970];
[EMASCurlProtocol setMetricsObserverBlockForRequest:request metricsObserverBlock:^(NSURLRequest * _Nonnull request, BOOL success, NSError *error, double nameLookUpTimeMS, double connectTimeMs, double appConnectTimeMs, double preTransferTimeMs, double startTransferTimeMs, double totalTimeMs) {
// Our mock resolver has a 500ms delay
XCTAssertGreaterThanOrEqual(nameLookUpTimeMS, 2000, @"DNS lookup time should be at least 2000ms with mock resolver");
XCTAssertLessThan(nameLookUpTimeMS, 2100, @"DNS lookup time should not be much more than 2100ms");
metricsReceived = YES;
// Log metrics for debugging
NSLog(@"Custom DNS Resolution Metrics:\n"
"DNS Lookup: %.2fms\n"
"Connect: %.2fms\n"
"Total: %.2fms",
nameLookUpTimeMS, connectTimeMs, totalTimeMs);
}];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
XCTAssertTrue(metricsReceived, @"Metrics callback should have been received");
dispatch_semaphore_signal(semaphore);
}];
[dataTask resume];
// 确保解析dns的阻塞是发生在另一个线程
XCTAssertLessThan([[NSDate date] timeIntervalSince1970] - startTime, 0.1);
XCTAssertEqual(dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC)), 0, @"Request timed out");
}
@end