#import #import #import "IpAddress.h" #import "TestUtil.h" #import "ThreadManager.h" #import "UdpSocket.h" #import "Util.h" @interface UdpSocketTest : XCTestCase @end @implementation UdpSocketTest -(void) testSpecifiedPortLocally { TOCCancelTokenSource* receiverLife = [TOCCancelTokenSource new]; TOCCancelTokenSource* senderLife = [TOCCancelTokenSource new]; __block NSData* received = nil; __block bool senderReceivedData = false; NSData* r1 = [@[@2,@3,@5] ows_toUint8Data]; NSData* r2 = [@[@7,@11,@13] ows_toUint8Data]; NSData* r3 = [@[@17,@19,@23] ows_toUint8Data]; in_port_t port1 = (in_port_t)(arc4random_uniform(40000) + 10000); in_port_t port2 = port1 + (in_port_t)1; UdpSocket* receiver = [UdpSocket udpSocketFromLocalPort:port1 toRemoteEndPoint:[IpEndPoint ipEndPointAtAddress:IpAddress.localhost onPort:port2]]; UdpSocket* sender = [UdpSocket udpSocketFromLocalPort:port2 toRemoteEndPoint:[IpEndPoint ipEndPointAtAddress:IpAddress.localhost onPort:port1]]; [receiver startWithHandler:[PacketHandler packetHandler:^(id packet) { received = packet; } withErrorHandler:^(id error, id relatedInfo, bool causedTermination) { test(false); }] untilCancelled:receiverLife.token]; __block bool failed = false; [sender startWithHandler:[PacketHandler packetHandler:^(NSData* packet) { // there's a length check here because when the destination is unreachable the sender sometimes gets a superfluous empty data callback... no idea why. senderReceivedData |= packet.length > 0; } withErrorHandler:^(id error, id relatedInfo, bool causedTermination) { failed = true; }] untilCancelled:senderLife.token]; test(receiver.isLocalPortKnown); test(receiver.localPort == port1); test(sender.isLocalPortKnown); test(sender.localPort == port2); testChurnAndConditionMustStayTrue(received == nil, 0.1); [sender send:r1]; testChurnUntil([received isEqualToData:r1], 1.0); test([received isEqualToData:r1]); [sender send:r2]; testChurnUntil([received isEqualToData:r2], 1.0); [receiverLife cancel]; test(!failed); [sender send:r3]; testChurnUntil(failed, 1.0); test([received isEqualToData:r2]); [senderLife cancel]; test(!senderReceivedData); } -(void) testArbitraryPortLocally { TOCCancelTokenSource* receiverLife = [TOCCancelTokenSource new]; TOCCancelTokenSource* senderLife = [TOCCancelTokenSource new]; __block NSData* received = nil; __block bool senderReceivedData = false; NSData* r1 = [@[@2,@3,@5] ows_toUint8Data]; NSData* r2 = [@[@7,@11,@13] ows_toUint8Data]; NSData* r3 = [@[@17,@19,@23] ows_toUint8Data]; in_port_t unusedPort = (in_port_t)(arc4random_uniform(40000) + 10000); UdpSocket* receiver = [UdpSocket udpSocketTo:[IpEndPoint ipEndPointAtAddress:IpAddress.localhost onPort:unusedPort]]; [receiver startWithHandler:[PacketHandler packetHandler:^(id packet) { @synchronized (churnLock()) { received = packet; } } withErrorHandler:^(id error, id relatedInfo, bool causedTermination) { test(false); }] untilCancelled:receiverLife.token]; __block bool failed = false; UdpSocket* sender = [UdpSocket udpSocketFromLocalPort:unusedPort toRemoteEndPoint:[IpEndPoint ipEndPointAtAddress:IpAddress.localhost onPort:receiver.localPort]]; [sender startWithHandler:[PacketHandler packetHandler:^(NSData* packet) { // there's a length check here because when the destination is unreachable the sender sometimes gets a superfluous empty data callback... no idea why. senderReceivedData |= packet.length > 0; } withErrorHandler:^(id error, id relatedInfo, bool causedTermination) { failed = true; }] untilCancelled:senderLife.token]; testChurnAndConditionMustStayTrue(received == nil, 0.1); [sender send:r1]; testChurnUntil([received isEqualToData:r1], 1.0); [sender send:r2]; testChurnUntil([received isEqualToData:r2], 1.0); [receiverLife cancel]; test(!failed); [sender send:r3]; testChurnAndConditionMustStayTrue([received isEqualToData:r2], 0.1); test([received isEqualToData:r2]); [senderLife cancel]; test(!senderReceivedData); } -(void) testUdpListen { TOCCancelTokenSource* receiverLife = [TOCCancelTokenSource new]; TOCCancelTokenSource* senderLife = [TOCCancelTokenSource new]; __block NSUInteger listenerReceiveCount = 0; __block NSUInteger listenerReceiveLength = 0; __block NSData* listenerReceivedLast = nil; __block NSUInteger clientReceiveCount = 0; __block NSUInteger clientReceiveLength = 0; __block NSData* clientReceivedLast = nil; in_port_t port = (in_port_t)(arc4random_uniform(40000) + 10000); UdpSocket* listener = [UdpSocket udpSocketToFirstSenderOnLocalPort:port]; [listener startWithHandler:[PacketHandler packetHandler:^(NSData* packet) { listenerReceiveCount += 1; listenerReceiveLength += packet.length; listenerReceivedLast = packet; } withErrorHandler:^(id error, id relatedInfo, bool causedTermination) { test(false); }] untilCancelled:receiverLife.token]; IpEndPoint* e = [IpEndPoint ipEndPointAtAddress:IpAddress.localhost onPort:port]; UdpSocket* client = [UdpSocket udpSocketTo:e]; [client startWithHandler:[PacketHandler packetHandler:^(NSData* packet) { clientReceiveCount += 1; clientReceiveLength += packet.length; clientReceivedLast = packet; } withErrorHandler:^(id error, id relatedInfo, bool causedTermination) { test(false); }] untilCancelled:senderLife.token]; test(!listener.isRemoteEndPointKnown); testThrows([listener remoteEndPoint]); test(client.isRemoteEndPointKnown); test([client remoteEndPoint] == e); test(listenerReceiveCount == 0); test(clientReceiveCount == 0); [client send:increasingData(10)]; testChurnUntil(listenerReceiveCount > 0, 1.0); test(clientReceiveCount == 0); test(listener.isRemoteEndPointKnown); test([[[[listener remoteEndPoint] address] description] isEqualToString:@"127.0.0.1"]); test(listenerReceiveLength == 10); test([listenerReceivedLast isEqualToData:increasingData(10)]); [listener send:increasingData(20)]; testChurnUntil(clientReceiveCount > 0, 1.0); test(listenerReceiveCount == 1); test(clientReceiveCount == 1); test(clientReceiveLength == 20); test([clientReceivedLast isEqualToData:increasingData(20)]); [receiverLife cancel]; [senderLife cancel]; } -(void) testUdpFail { TOCCancelTokenSource* life = [TOCCancelTokenSource new]; in_port_t unusedPort = 10000 + (in_port_t)arc4random_uniform(30000); UdpSocket* udp = [UdpSocket udpSocketTo:[IpEndPoint ipEndPointAtAddress:IpAddress.localhost onPort:unusedPort]]; __block bool failed = false; [udp startWithHandler:[PacketHandler packetHandler:^(id packet) { test(false); } withErrorHandler:^(id error, id relatedInfo, bool causedTermination) { failed = true; }] untilCancelled:life.token]; [udp send:increasingData(20)]; testChurnUntil(failed, 1.0); [life cancel]; } @end