Files
waf-platform/EdgeHttpDNS/sdk/ios/AlicloudHttpDNSTests/Network/HttpdnsNWHTTPClient_StateMachineTests.m
2026-02-28 18:55:33 +08:00

592 lines
26 KiB
Objective-C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// HttpdnsNWHTTPClient_StateMachineTests.m
// TrustHttpDNSTests
//
// @author Created by Claude Code on 2025-11-01
// Copyright © 2025 trustapp.com. All rights reserved.
//
// 状æ€<C3A6>机æµè¯• - 包å<E280A6>«çжæ€<C3A6>机与å¼å¸¸åœºæ™?(Q) 测试ç»?
// 测试总数�7 个(Q:17�
//
#import "HttpdnsNWHTTPClientTestBase.h"
@interface HttpdnsNWHTTPClient_StateMachineTests : HttpdnsNWHTTPClientTestBase
@end
@implementation HttpdnsNWHTTPClient_StateMachineTests
#pragma mark - Q. 状æ€<C3A6>机与å¼å¸¸åœºæ™¯æµè¯?
// Q1.1 池溢出时LRU移除ç­ç•¥éªŒè¯<C3A8>
- (void)testStateMachine_PoolOverflowLRU_RemovesOldestByLastUsedDate {
[self.client resetPoolStatistics];
NSString *poolKey = @"127.0.0.1:11080:tcp";
// 需è¦<C3A8>å¹¶å<C2B6>åˆå»?个连接(串行请æ±ä¼šå¤<C3A5>用)
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// å¹¶å<C2B6>å<E28098>èµ·5个请æ±?
for (NSInteger i = 0; i < 5; i++) {
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Request %ld", (long)i]];
[expectations addObject:expectation];
dispatch_async(queue, ^{
NSError *error = nil;
// 使用 /delay/2 ç¡®ä¿<C3A4>所有请æ±å<E2809A>Œæ—¶åœ¨é£žè¡Œä¸­ï¼Œå¼ºåˆ¶åˆå»ºå¤šä¸ªè¿žæŽ¥
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/delay/2"
userAgent:[NSString stringWithFormat:@"Request%ld", (long)i]
timeout:15.0
error:&error];
[expectation fulfill];
});
[NSThread sleepForTimeInterval:0.05]; // å°<C3A5>é—´éš”é<E2809D>¿å…<C3A5>完全å<C2A8>Œæ—¶å<C2B6>¯åŠ?
}
[self waitForExpectations:expectations timeout:20.0];
// 等待所有连接归�
[NSThread sleepForTimeInterval:1.0];
// 验è¯<C3A8>:池大å°<C3A5> â‰?4(LRU移除溢出部分ï¼?
NSUInteger poolCount = [self.client connectionPoolCountForKey:poolKey];
XCTAssertLessThanOrEqual(poolCount, 4,
@"Pool should enforce max 4 connections (LRU)");
// 验è¯<C3A8>:åˆå»ºäº†å¤šä¸ªè¿žæŽ¥
XCTAssertGreaterThanOrEqual(self.client.connectionCreationCount, 3,
@"Should create multiple concurrent connections");
}
// Q2.1 快速连续请æ±ä¸<C3A4>产生é‡<C3A9>å¤<C3A5>连接(间接验è¯<C3A8>å<EFBFBD>Œé‡<C3A9>å½è¿˜é˜²æŠ¤ï¼‰
- (void)testAbnormal_RapidSequentialRequests_NoDuplicates {
[self.client resetPoolStatistics];
NSString *poolKey = @"127.0.0.1:11080:tcp";
// 快速连续å<C2AD>èµ?0个请æ±ï¼ˆæµè¯•连接å½è¿˜çš„å¹ç­‰æ€§ï¼‰
for (NSInteger i = 0; i < 10; i++) {
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"RapidTest"
timeout:15.0
error:&error];
XCTAssertNotNil(response);
}
// 等待连接归还
[NSThread sleepForTimeInterval:1.0];
// 验è¯<C3A8>:池中最å¤?个连接(å ä¸ºä¸²è¡Œè¯·æ±å¤<C3A5>用å<C2A8>Œä¸€è¿žæŽ¥ï¼?
NSUInteger poolCount = [self.client connectionPoolCountForKey:poolKey];
XCTAssertLessThanOrEqual(poolCount, 1,
@"Pool should have at most 1 connection (rapid sequential reuse)");
// 验è¯<C3A8>:åˆå»ºæ¬¡æ•°åº”该是1(所有请æ±å¤<C3A5>用å<C2A8>Œä¸€è¿žæŽ¥ï¼?
XCTAssertEqual(self.client.connectionCreationCount, 1,
@"Should create only 1 connection for sequential requests");
}
// Q2.2 ä¸<C3A4>å<EFBFBD>Œç«¯å<C2AF>£è¯·æ±ä¸<C3A4>äºç¸æ±¡æŸ“æ± 
- (void)testAbnormal_DifferentPorts_IsolatedPools {
[self.client resetPoolStatistics];
NSString *poolKey11080 = @"127.0.0.1:11080:tcp";
NSString *poolKey11443 = @"127.0.0.1:11443:tls";
// å<>端å<C2AF>?1080å<30>起请æ±
NSError *error1 = nil;
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"Port11080"
timeout:15.0
error:&error1];
XCTAssertNotNil(response1);
// å<>端å<C2AF>?1443å<33>起请æ±
NSError *error2 = nil;
HttpdnsNWHTTPClientResponse *response2 = [self.client performRequestWithURLString:@"https://127.0.0.1:11443/get"
userAgent:@"Port11443"
timeout:15.0
error:&error2];
XCTAssertNotNil(response2);
// 等待连接归还
[NSThread sleepForTimeInterval:0.5];
// 验è¯<C3A8>:两个池å<C2A0>„自æœ?个连æŽ?
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey11080], 1,
@"Port 11080 pool should have 1 connection");
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey11443], 1,
@"Port 11443 pool should have 1 connection");
// 验è¯<C3A8>:总共2个连接(池完全隔离)
XCTAssertEqual([self.client totalConnectionCount], 2,
@"Total should be 2 (one per pool)");
}
// Q3.1 池大å°<C3A5>ä¸<C3A4>å<EFBFBD>˜å¼<C3A5>:任何时候池大å°<C3A5>都ä¸<C3A4>超过é™<C3A9>åˆ
- (void)testInvariant_PoolSize_NeverExceedsLimit {
[self.client resetPoolStatistics];
// 快速连续å<C2AD>èµ?0个请æ±åˆ°å<C2B0>Œä¸€ç«¯ç¹
for (NSInteger i = 0; i < 20; i++) {
NSError *error = nil;
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"InvariantTest"
timeout:15.0
error:&error];
}
// 等待所有连接归�
[NSThread sleepForTimeInterval:1.5];
// 验è¯<C3A8>:æ¯<C3A6>个池的大å°<C3A5>ä¸<C3A4>超过4
NSArray<NSString *> *allKeys = [self.client allConnectionPoolKeys];
for (NSString *key in allKeys) {
NSUInteger poolCount = [self.client connectionPoolCountForKey:key];
XCTAssertLessThanOrEqual(poolCount, 4,
@"Pool %@ size should never exceed 4 (actual: %lu)",
key, (unsigned long)poolCount);
}
// 验è¯<C3A8>:总连接数也ä¸<C3A4>超过4(å ä¸ºå<C2BA>ªæœ‰ä¸€ä¸ªæ± ï¼?
XCTAssertLessThanOrEqual([self.client totalConnectionCount], 4,
@"Total connections should not exceed 4");
}
// Q3.3 æ— é‡<C3A9>å¤<C3A5>连接ä¸<C3A4>å<EFBFBD>˜å¼<C3A5>:并å<C2B6>请æ±ä¸<C3A4>产生é‡<C3A9>å¤<C3A5>
- (void)testInvariant_NoDuplicates_ConcurrentRequests {
[self.client resetPoolStatistics];
NSString *poolKey = @"127.0.0.1:11080:tcp";
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// å¹¶å<C2B6>å<E28098>èµ·15个请æ±ï¼ˆå<CB86>¯èƒ½å¤<C3A5>用连接ï¼?
for (NSInteger i = 0; i < 15; i++) {
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Request %ld", (long)i]];
[expectations addObject:expectation];
dispatch_async(queue, ^{
NSError *error = nil;
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"ConcurrentTest"
timeout:15.0
error:&error];
[expectation fulfill];
});
}
[self waitForExpectations:expectations timeout:30.0];
// 等待连接归还
[NSThread sleepForTimeInterval:1.0];
// 验è¯<C3A8>:池大å°<C3A5> â‰?4(ä¸<C3A4>å<EFBFBD>˜å¼<C3A5>ï¼?
NSUInteger poolCount = [self.client connectionPoolCountForKey:poolKey];
XCTAssertLessThanOrEqual(poolCount, 4,
@"Pool should not have duplicates (max 4 connections)");
// 验è¯<C3A8>:åˆå»ºçš„连接数å<C2B0>ˆç<CB86>†ï¼ˆâ‰?5,å ä¸ºå<C2BA>¯èƒ½æœ‰å¤<C3A5>用ï¼?
XCTAssertLessThanOrEqual(self.client.connectionCreationCount, 15,
@"Should not create excessive connections");
}
// Q4.1 边界æ<C592>¡ä»¶ï¼šæ<C5A1>°å¥?0ç§å<E28099>Žè¿žæŽ¥è¿‡æœŸ
- (void)testBoundary_Exactly30Seconds_ConnectionExpired {
if (getenv("SKIP_SLOW_TESTS")) {
return;
}
[self.client resetPoolStatistics];
// 第一个请�
NSError *error1 = nil;
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"InitialRequest"
timeout:15.0
error:&error1];
XCTAssertNotNil(response1);
// 等待æ<E280A6>°å¥½30.5ç§ï¼ˆè¶…过30ç§è¿‡æœŸæ—¶é—´ï¼‰
[NSThread sleepForTimeInterval:30.5];
// 第二个请求:应该创建新连接(旧连接已过期�
NSError *error2 = nil;
HttpdnsNWHTTPClientResponse *response2 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"AfterExpiry"
timeout:15.0
error:&error2];
XCTAssertNotNil(response2);
// 验è¯<C3A8>:åˆå»ºäº†2个连接(旧连接过期,无法å¤<C3A5>用ï¼?
XCTAssertEqual(self.client.connectionCreationCount, 2,
@"Should create 2 connections (first expired after 30s)");
XCTAssertEqual(self.client.connectionReuseCount, 0,
@"Should not reuse expired connection");
}
// Q4.2 边界æ<C592>¡ä»¶ï¼?9ç§å†…连接未过æœ?
- (void)testBoundary_Under30Seconds_ConnectionNotExpired {
[self.client resetPoolStatistics];
// 第一个请�
NSError *error1 = nil;
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"InitialRequest"
timeout:15.0
error:&error1];
XCTAssertNotNil(response1);
// 等待29ç§ï¼ˆæœªåˆ°30ç§è¿‡æœŸæ—¶é—´ï¼‰
[NSThread sleepForTimeInterval:29.0];
// 第二个请æ±ï¼šåº”该å¤<C3A5>用连接
NSError *error2 = nil;
HttpdnsNWHTTPClientResponse *response2 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"BeforeExpiry"
timeout:15.0
error:&error2];
XCTAssertNotNil(response2);
// 验è¯<C3A8>:å<C5A1>ªåˆå»ºäº?个连接(å¤<C3A5>用了)
XCTAssertEqual(self.client.connectionCreationCount, 1,
@"Should create only 1 connection (reused within 30s)");
XCTAssertEqual(self.client.connectionReuseCount, 1,
@"Should reuse connection within 30s");
}
// Q4.3 边界æ<C592>¡ä»¶ï¼šæ<C5A1>°å¥?个连接全部ä¿<C3A4>ç•?
- (void)testBoundary_ExactlyFourConnections_AllKept {
[self.client resetPoolStatistics];
NSString *poolKey = @"127.0.0.1:11080:tcp";
// å¹¶å<C2B6>å<E28098>èµ·4个请æ±ï¼ˆä½¿ç”¨å»¶è¿Ÿç¡®ä¿<C3A4>å<EFBFBD>Œæ—¶åœ¨é£žè¡Œä¸­ï¼Œåˆå»?个独立连接)
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (NSInteger i = 0; i < 4; i++) {
XCTestExpectation *expectation = [self expectationWithDescription:
[NSString stringWithFormat:@"Request %ld", (long)i]];
[expectations addObject:expectation];
dispatch_async(queue, ^{
NSError *error = nil;
// 使用 /delay/2 ç¡®ä¿<C3A4>所有请æ±å<E2809A>Œæ—¶åœ¨é£žè¡Œä¸?
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/delay/2"
userAgent:[NSString stringWithFormat:@"Request%ld", (long)i]
timeout:15.0
error:&error];
[expectation fulfill];
});
[NSThread sleepForTimeInterval:0.05]; // å°<C3A5>é—´éš”é<E2809D>¿å…<C3A5>完全å<C2A8>Œæ—¶å<C2B6>¯åŠ?
}
[self waitForExpectations:expectations timeout:20.0];
// 等待连接归还
[NSThread sleepForTimeInterval:1.0];
// 验è¯<C3A8>:池æ<C2A0>°å¥½æœ?个连接(全部ä¿<C3A4>ç•™ï¼?
NSUInteger poolCount = [self.client connectionPoolCountForKey:poolKey];
XCTAssertEqual(poolCount, 4,
@"Pool should keep all 4 connections (not exceeding limit)");
// 验è¯<C3A8>:æ<C5A1>°å¥½åˆå»?个连æŽ?
XCTAssertEqual(self.client.connectionCreationCount, 4,
@"Should create exactly 4 connections");
}
// Q1.2 正常状æ€<C3A6>åº<C3A5>列验è¯?
- (void)testStateMachine_NormalSequence_StateTransitionsCorrect {
[self.client resetPoolStatistics];
NSString *poolKey = @"127.0.0.1:11080:tcp";
// �步:创建并使用连�(CREATING �IN_USE �IDLE)
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"StateTest"
timeout:15.0
error:nil];
XCTAssertNotNil(response1, @"First request should succeed");
[NSThread sleepForTimeInterval:1.0]; // 等待归还
// 验è¯<C3A8>:池中有1个连æŽ?
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey], 1,
@"Connection should be in pool");
// ç¬?步:å¤<C3A5>用连接 (IDLE â†?IN_USE â†?IDLE)
HttpdnsNWHTTPClientResponse *response2 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"StateTest"
timeout:15.0
error:nil];
XCTAssertNotNil(response2, @"Second request should reuse connection");
// 验è¯<C3A8>:å¤<C3A5>用计数增åŠ?
XCTAssertEqual(self.client.connectionReuseCount, 1,
@"Should have reused connection once");
}
// Q1.3 inUse 标志维护验è¯<C3A8>
- (void)testStateMachine_InUseFlag_CorrectlyMaintained {
[self.client resetPoolStatistics];
NSString *poolKey = @"127.0.0.1:11080:tcp";
// å<>起请æ±å¹¶å½è¿?
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"InUseTest"
timeout:15.0
error:nil];
[NSThread sleepForTimeInterval:1.0]; // 等待归还
// 获å<C2B7>池中连接
NSArray<HttpdnsNWReusableConnection *> *connections = [self.client connectionsInPoolForKey:poolKey];
XCTAssertEqual(connections.count, 1, @"Should have 1 connection in pool");
// 验è¯<C3A8>:池中连接的 inUse 应为 NO
for (HttpdnsNWReusableConnection *conn in connections) {
XCTAssertFalse(conn.inUse, @"Connection in pool should not be marked as inUse");
}
}
// Q2.3 Nil lastUsedDate 处ç<E2809E>†éªŒè¯<C3A8>
- (void)testAbnormal_NilLastUsedDate_HandledGracefully {
[self.client resetPoolStatistics];
NSString *poolKey = @"127.0.0.1:11080:tcp";
// å<>起请æ±åˆå»ºè¿žæŽ¥
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"NilDateTest"
timeout:15.0
error:nil];
[NSThread sleepForTimeInterval:1.0];
// 获å<C2B7>连接并设ç½?lastUsedDate ä¸?nil
NSArray<HttpdnsNWReusableConnection *> *connections = [self.client connectionsInPoolForKey:poolKey];
XCTAssertEqual(connections.count, 1, @"Should have connection");
HttpdnsNWReusableConnection *conn = connections.firstObject;
[conn debugSetLastUsedDate:nil];
// å<>èµ·æ°è¯·æ±è§¦å<C2A6>?prune(内部应使用 distantPast 处ç<E2809E>† nilï¼?
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"NilDateTest"
timeout:15.0
error:nil];
// 验è¯<C3A8>:ä¸<C3A4>崩溃,正常工ä½?
XCTAssertNotNil(response, @"Should handle nil lastUsedDate gracefully");
}
// Q3.2 池中无失效连接ä¸<C3A4>å<EFBFBD>˜å¼<C3A5>
- (void)testInvariant_NoInvalidatedInPool {
[self.client resetPoolStatistics];
NSString *poolKey = @"127.0.0.1:11080:tcp";
// å<>起多个请æ±ï¼ˆåŒ…æ¬æˆ<C3A6>功åŒè¶…æ—¶ï¼?
for (NSInteger i = 0; i < 3; i++) {
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"InvariantTest"
timeout:15.0
error:nil];
}
// å<>èµ·1个超时请æ±?
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/delay/10"
userAgent:@"TimeoutTest"
timeout:0.5
error:nil];
[NSThread sleepForTimeInterval:2.0];
// 获å<C2B7>池中所有连æŽ?
NSArray<HttpdnsNWReusableConnection *> *connections = [self.client connectionsInPoolForKey:poolKey];
// 验è¯<C3A8>:池中无失效连接
for (HttpdnsNWReusableConnection *conn in connections) {
XCTAssertFalse(conn.isInvalidated, @"Pool should not contain invalidated connections");
}
}
// Q3.4 lastUsedDate å<>•调性验è¯?
- (void)testInvariant_LastUsedDate_Monotonic {
[self.client resetPoolStatistics];
NSString *poolKey = @"127.0.0.1:11080:tcp";
// �次使�
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"MonotonicTest"
timeout:15.0
error:nil];
[NSThread sleepForTimeInterval:1.0];
NSArray<HttpdnsNWReusableConnection *> *connections1 = [self.client connectionsInPoolForKey:poolKey];
XCTAssertEqual(connections1.count, 1, @"Should have connection");
NSDate *date1 = connections1.firstObject.lastUsedDate;
XCTAssertNotNil(date1, @"lastUsedDate should be set");
// 等待1ç§ç¡®ä¿<C3A4>时间推è¿?
[NSThread sleepForTimeInterval:1.0];
// ç¬?次使用å<C2A8>Œä¸€è¿žæŽ¥
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"MonotonicTest"
timeout:15.0
error:nil];
[NSThread sleepForTimeInterval:1.0];
NSArray<HttpdnsNWReusableConnection *> *connections2 = [self.client connectionsInPoolForKey:poolKey];
XCTAssertEqual(connections2.count, 1, @"Should still have 1 connection");
NSDate *date2 = connections2.firstObject.lastUsedDate;
// 验è¯<C3A8>:lastUsedDate 递增
XCTAssertTrue([date2 timeIntervalSinceDate:date1] > 0,
@"lastUsedDate should increase after reuse");
}
// Q5.1 è¶…æ—¶+池溢出å¤<C3A5>å<EFBFBD>ˆåœºæ™?
- (void)testCompound_TimeoutDuringPoolOverflow_Handled {
[self.client resetPoolStatistics];
NSString *poolKey = @"127.0.0.1:11080:tcp";
// 先填满池ï¼?个æˆ<C3A6>功连接)
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (NSInteger i = 0; i < 4; i++) {
XCTestExpectation *expectation = [self expectationWithDescription:
[NSString stringWithFormat:@"Fill pool %ld", (long)i]];
[expectations addObject:expectation];
dispatch_async(queue, ^{
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/delay/2"
userAgent:@"CompoundTest"
timeout:15.0
error:nil];
[expectation fulfill];
});
[NSThread sleepForTimeInterval:0.05];
}
[self waitForExpectations:expectations timeout:20.0];
[NSThread sleepForTimeInterval:1.0];
NSUInteger poolCountBefore = [self.client connectionPoolCountForKey:poolKey];
XCTAssertLessThanOrEqual(poolCountBefore, 4, @"Pool should have � connections");
// �个请求超�
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/delay/10"
userAgent:@"TimeoutRequest"
timeout:0.5
error:&error];
XCTAssertNil(response, @"Timeout request should return nil");
XCTAssertNotNil(error, @"Should have error");
[NSThread sleepForTimeInterval:1.0];
// 验è¯<C3A8>:超时连接未加入æ±?
NSUInteger poolCountAfter = [self.client connectionPoolCountForKey:poolKey];
XCTAssertLessThanOrEqual(poolCountAfter, 4, @"Timed-out connection should not be added to pool");
}
// Q2.4 打开失败ä¸<C3A4>加入池
- (void)testAbnormal_OpenFailure_NotAddedToPool {
[self.client resetPoolStatistics];
// å°<C3A5>试连接无效端å<C2AF>£ï¼ˆè¿žæŽ¥æç»<C3A7>)
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:99999/get"
userAgent:@"FailureTest"
timeout:2.0
error:&error];
// 验è¯<C3A8>:请æ±å¤±è´?
XCTAssertNil(response, @"Should fail to connect to invalid port");
// 验è¯<C3A8>:无连接加入æ±?
XCTAssertEqual([self.client totalConnectionCount], 0,
@"Failed connection should not be added to pool");
}
// Q2.5 多次 invalidate 幂等�
- (void)testAbnormal_MultipleInvalidate_Idempotent {
[self.client resetPoolStatistics];
NSString *poolKey = @"127.0.0.1:11080:tcp";
// 创建连接
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"InvalidateTest"
timeout:15.0
error:nil];
[NSThread sleepForTimeInterval:1.0];
NSArray<HttpdnsNWReusableConnection *> *connections = [self.client connectionsInPoolForKey:poolKey];
XCTAssertEqual(connections.count, 1, @"Should have connection");
HttpdnsNWReusableConnection *conn = connections.firstObject;
// 多次 invalidate
[conn debugInvalidate];
[conn debugInvalidate];
[conn debugInvalidate];
// 验è¯<C3A8>:ä¸<C3A4>崩溃
XCTAssertTrue(conn.isInvalidated, @"Connection should be invalidated");
}
// Q5.2 å¹¶å<C2B6> dequeue 竞æ€<C3A6>æµè¯?
- (void)testCompound_ConcurrentDequeueDuringPrune_Safe {
[self.client resetPoolStatistics];
// 在两个端å<C2AF>£åˆå»ºè¿žæŽ?
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"RaceTest"
timeout:15.0
error:nil];
[self.client performRequestWithURLString:@"http://127.0.0.1:11443/get"
userAgent:@"RaceTest"
timeout:15.0
error:nil];
[NSThread sleepForTimeInterval:1.0];
// 等待30ç§è®©è¿žæŽ¥è¿‡æœŸ
[NSThread sleepForTimeInterval:30.5];
// å¹¶å<C2B6>触å<C2A6>两个端å<C2AF>£çš?dequeue(会触å<C2A6> pruneï¼?
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"Race1"
timeout:15.0
error:nil];
});
dispatch_group_async(group, queue, ^{
[self.client performRequestWithURLString:@"http://127.0.0.1:11443/get"
userAgent:@"Race2"
timeout:15.0
error:nil];
});
// 等待完æˆ<C3A6>
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 20 * NSEC_PER_SEC));
// 验è¯<C3A8>:无崩溃,连接池正常工作
NSUInteger totalCount = [self.client totalConnectionCount];
XCTAssertLessThanOrEqual(totalCount, 4, @"Pool should remain stable after concurrent prune");
}
@end