@@ -16,72 +16,37 @@ #include "config.h" #include #include -#import "TestsAppDelegate.h" +#import "ObjFW.h" +#import "ObjFWTest.h" -static OFString *const module = @"OFHTTPClient"; -static OFCondition *condition; -static OFHTTPResponse *response = nil; - -@interface TestsAppDelegate (HTTPClientTests) +@interface OFHTTPClientTests: OTTestCase +{ + OFHTTPResponse *_response; +} @end @interface HTTPClientTestsServer: OFThread { -@public + OFCondition *_condition; uint16_t _port; } -@end - -@implementation HTTPClientTestsServer -- (id)main -{ - OFTCPSocket *listener, *client; - OFSocketAddress address; - char buffer[5]; - - [condition lock]; - - listener = [OFTCPSocket socket]; - address = [listener bindToHost: @"127.0.0.1" port: 0]; - _port = OFSocketAddressIPPort(&address); - [listener listen]; - - [condition signal]; - [condition unlock]; - - client = [listener accept]; - - OFEnsure([[client readLine] isEqual: @"GET /foo HTTP/1.1"]); - OFEnsure([[client readLine] hasPrefix: @"User-Agent:"]); - OFEnsure([[client readLine] isEqual: @"Content-Length: 5"]); - OFEnsure([[client readLine] isEqual: - @"Content-Type: application/x-www-form-urlencoded; charset=UTF-8"]); - - if (![[client readLine] isEqual: - [OFString stringWithFormat: @"Host: 127.0.0.1:%" @PRIu16, _port]]) - OFEnsure(0); - - OFEnsure([[client readLine] isEqual: @""]); - - [client readIntoBuffer: buffer exactLength: 5]; - OFEnsure(memcmp(buffer, "Hello", 5) == 0); - - [client writeString: @"HTTP/1.0 200 OK\r\n" - @"cONTeNT-lENgTH: 7\r\n" - @"\r\n" - @"foo\n" - @"bar"]; - [client close]; - - return nil; -} -@end - -@implementation TestsAppDelegate (OFHTTPClientTests) + +@property (readonly, nonatomic) OFCondition *condition; +@property (readonly) uint16_t port; +@end + +@implementation OFHTTPClientTests +- (void)dealloc +{ + [_response release]; + + [super dealloc]; +} + - (void)client: (OFHTTPClient *)client wantsRequestBody: (OFStream *)body request: (OFHTTPRequest *)request { [body writeString: @"Hello"]; @@ -90,61 +55,146 @@ - (void)client: (OFHTTPClient *)client didPerformRequest: (OFHTTPRequest *)request response: (OFHTTPResponse *)response_ exception: (id)exception { - OFEnsure(exception == nil); + OTAssertNil(exception); - response = [response_ retain]; + [_response release]; + _response = [response_ retain]; [[OFRunLoop mainRunLoop] stop]; } -- (void)HTTPClientTests +- (void)testClient { - void *pool = objc_autoreleasePoolPush(); HTTPClientTestsServer *server; OFIRI *IRI; - OFHTTPClient *client; OFHTTPRequest *request; + OFHTTPClient *client; OFData *data; - condition = [OFCondition condition]; - [condition lock]; - server = [[[HTTPClientTestsServer alloc] init] autorelease]; server.supportsSockets = true; + + [server.condition lock]; + [server start]; - [condition wait]; - [condition unlock]; + [server.condition wait]; + [server.condition unlock]; IRI = [OFIRI IRIWithString: [OFString stringWithFormat: @"http://127.0.0.1:%" @PRIu16 "/foo", - server->_port]]; - - TEST(@"-[asyncPerformRequest:]", - (client = [OFHTTPClient client]) && (client.delegate = self) && - (request = [OFHTTPRequest requestWithIRI: IRI]) && - (request.headers = - [OFDictionary dictionaryWithObject: @"5" - forKey: @"Content-Length"]) && - R([client asyncPerformRequest: request])) + server.port]]; + + request = [OFHTTPRequest requestWithIRI: IRI]; + request.headers = [OFDictionary + dictionaryWithObject: @"5" + forKey: @"Content-Length"]; + + client = [OFHTTPClient client]; + client.delegate = self; + [client asyncPerformRequest: request]; [[OFRunLoop mainRunLoop] runUntilDate: [OFDate dateWithTimeIntervalSinceNow: 2]]; - [response autorelease]; - - TEST(@"Asynchronous handling of requests", response != nil) - - TEST(@"Normalization of server header keys", - [response.headers objectForKey: @"Content-Length"] != nil) - - TEST(@"Correct parsing of data", - (data = [response readDataUntilEndOfStream]) && - data.count == 7 && memcmp(data.items, "foo\nbar", 7) == 0) - - [server join]; - - objc_autoreleasePoolPop(pool); + + OTAssertNotNil(_response); + OTAssertNotNil([_response.headers objectForKey: @"Content-Length"]); + + data = [_response readDataUntilEndOfStream]; + OTAssertEqual(data.count, 7); + OTAssertEqual(data.itemSize, 1); + OTAssertEqual(memcmp(data.items, "foo\nbar", 7), 0); + + OTAssertNil([server join]); +} +@end + +@implementation HTTPClientTestsServer +@synthesize condition = _condition, port = _port; + +- (instancetype)init +{ + self = [super init]; + + @try { + _condition = [[OFCondition alloc] init]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_condition release]; + + [super dealloc]; +} + +- (id)main +{ + OFTCPSocket *listener, *client; + OFSocketAddress address; + bool sawHost = false, sawContentLength = false, sawContentType = false; + bool sawUserAgent = false; + char buffer[5]; + + [_condition lock]; + + listener = [OFTCPSocket socket]; + address = [listener bindToHost: @"127.0.0.1" port: 0]; + _port = OFSocketAddressIPPort(&address); + [listener listen]; + + [_condition signal]; + [_condition unlock]; + client = [listener accept]; + + if (![[client readLine] isEqual: @"GET /foo HTTP/1.1"]) + return @"Wrong request"; + + for (size_t i = 0; i < 4; i++) { + OFString *line = [client readLine]; + + if ([line isEqual: [OFString stringWithFormat: + @"Host: 127.0.0.1:%" @PRIu16, _port]]) + sawHost = true; + else if ([line isEqual: @"Content-Length: 5"]) + sawContentLength = true; + if ([line isEqual: @"Content-Type: application/" + @"x-www-form-urlencoded; charset=UTF-8"]) + sawContentType = true; + else if ([line hasPrefix: @"User-Agent:"]) + sawUserAgent = true; + } + + if (!sawHost) + return @"Missing host"; + if (!sawContentLength) + return @"Missing content length"; + if (!sawContentType) + return @"Missing content type"; + if (!sawUserAgent) + return @"Missing user agent"; + + if (![[client readLine] isEqual: @""]) + return @"Missing empty line"; + + [client readIntoBuffer: buffer exactLength: 5]; + if (memcmp(buffer, "Hello", 5) != 0) + return @"Missing body"; + + [client writeString: @"HTTP/1.0 200 OK\r\n" + @"cONTeNT-lENgTH: 7\r\n" + @"\r\n" + @"foo\n" + @"bar"]; + [client close]; + + return nil; } @end