//
// $Id: InternetSocketTest.m,v 1.8 2005/07/09 19:01:57 will_mason Exp $
//
// vi: set ft=objc:

/*
 * ObjectiveLib - a library of containers and algorithms for Objective-C
 *
 * Copyright (c) 2004
 * Will Mason
 *
 * Portions:
 *
 * Copyright (c) 1994
 * Hewlett-Packard Company
 *
 * Copyright (c) 1996,1997
 * Silicon Graphics Computer Systems, Inc.
 *
 * Copyright (c) 1997
 * Moscow Center for SPARC Technology
 *
 * Copyright (c) 1999 
 * Boris Fomitchev
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * You may contact the author at will_mason@users.sourceforge.net.
 */

#import "InternetSocketTest.h"
#import <ObjectiveLib/Socket.h>
#import <ObjectiveLib/OutStream.h>
#import <ObjectiveLib/InStream.h>
#import <Foundation/NSString.h>
#import <Foundation/NSException.h>
#import <Foundation/NSThread.h>
#import <Foundation/NSDate.h>
#import <Foundation/NSAutoreleasePool.h>
#import <limits.h>
#import <stdlib.h>
#import <string.h>

#define BOOL_OPT_STR(msg) ( msg ? "YES" : "NO" )

@implementation InternetSocketTest

- (void) runServerThread: (id)arg
{
    OLSocket* accpt;
    char* myData = NULL;
    unsigned i;
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

    NS_DURING

        [self logMessage: "Starting server thread"];
        myData = malloc(1000000);
        accpt = [arg acceptConnection];
        [self logMessage: "Server accepted connection from %s",
            [[[accpt remoteAddress] description] cString]];
        i = 0;
        while (i < 1000000)
            i += [[accpt inStream] readBytes: myData + i count: 1000000 - i];
        if (i != 1000000)
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Only wrote %u of 1000000 bytes", i];
        }
        if (memcmp(myData, communicatedData, 1000000) != 0)
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "The memory regions should be equal"];
        }
        [self logMessage: "Server comparison done"];
        for (i = 0; i < 1000000; i++)
            communicatedData[i] = lrand48() % 256;
        i = 0;
        while (i < 1000000)
            i += [[accpt outStream] writeBytes: communicatedData + i count: 1000000 - i];
        if (i != 1000000)
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Only wrote %u of 1000000 bytes", i];
        }
        [self logMessage: "Server data written"];

    NS_HANDLER

        [self errInFile: __FILE__ line: __LINE__
            format: "%s: %s",
            [[localException name] cString], [[localException reason] cString]];

    NS_ENDHANDLER

    free(myData);
    [arg release];
    [pool release];
    [self logMessage: "Leaving server thread"];
}

- (void) testBind
{
    OLInternetServerSocket* server;
    OLInternetAddress* addr;

    server = [[OLInternetServerSocket alloc] init];

    NS_DURING

        addr = [OLInternetAddress addressWithCurrentHostAndPort: 5381];
        [server bindToAddress: addr];
        if (![[server localAddress] isEqual: addr])
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "\"%s\" should be equal to \"%s\"",
                [[addr description] cString], [[[server localAddress] description] cString]];
        }

    NS_HANDLER

        [self errInFile: __FILE__ line: __LINE__
            format: "%s: %s",
            [[localException name] cString], [[localException reason] cString]];

    NS_ENDHANDLER

    [server release];
}

- (void) testCommunicate
{
    OLInternetServerSocket* server;
    OLInternetClientSocket* client = nil;
    OLInternetAddress* addr;
    char* myData = NULL;
    int i;

    NS_DURING

        communicatedData = malloc(1000000);
        for (i = 0; i < 1000000; i++)
            communicatedData[i] = lrand48() % 256;
        addr = [OLInternetAddress addressWithCurrentHostAndPort: 19567];
        server = [[OLInternetServerSocket alloc] initWithLocalAddress: addr];
        [NSThread detachNewThreadSelector: @selector(runServerThread:)
            toTarget: self withObject: server];
        [NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 1]];
        [self logMessage: "Client connecting"];
        client = [[OLInternetClientSocket alloc] initWithRemoteAddress: addr];
        [self logMessage: "Client connected to %s",
            [[[client remoteAddress] description] cString]];
        i = 0;
        while (i < 1000000)
            i += [[client outStream] writeBytes: communicatedData + i count: 1000000 - i];
        if (i != 1000000)
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Only wrote %i of 1000000 bytes", i];
        }
        [self logMessage: "Client data written"];
        myData = malloc(1000000);
        i = 0;
        while (i < 1000000)
            i += [[client inStream] readBytes: myData + i count: 1000000 - i];
        if (i != 1000000)
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Only read %i of 1000000 bytes", i];
        }
        if (memcmp(myData, communicatedData, 1000000) != 0)
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "The memory regions should be equal"];
        }
        [self logMessage: "Client comparison done"];
        [NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 1]];

    NS_HANDLER

        [self errInFile: __FILE__ line: __LINE__
            format: "%s: %s",
            [[localException name] cString], [[localException reason] cString]];

    NS_ENDHANDLER

    free(myData);
    free(communicatedData);
    [client release];
}

- (void) testConvenienceAllocators
{
    OLInternetServerSocket* server;
    OLInternetClientSocket* client;
    unsigned qLen;

    server = [OLInternetServerSocket socket];
    qLen = [server queueLength];
    [self logMessage: "The default queue length is %u", qLen];

    NS_DURING

        if (![(OLInternetAddress*)[server localAddress] isLocalWildcard])
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "The server is not bound to a local address"];
        }

    NS_HANDLER
    NS_ENDHANDLER

    server = [OLInternetServerSocket socketWithLocalInternetAddress:
        [OLInternetAddress addressWithCurrentHostAndPort: 19568]];
    if ([server queueLength] != qLen)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected %u, but got %u", qLen, [server queueLength]];
    }

    NS_DURING

        if ([(OLInternetAddress*)[server localAddress] isLocalWildcard])
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "The server should be bound"];
        }

    NS_HANDLER

        [self errInFile: __FILE__ line: __LINE__
            format: "The server should be bound"];

    NS_ENDHANDLER

    server = [OLInternetServerSocket socketWithQueueLength: 5];
    if ([server queueLength] != 5)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 5, but got %u", [server queueLength]];
    }

    NS_DURING

        if (![(OLInternetAddress*)[server localAddress] isLocalWildcard])
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "The server is not bound to a local address"];
        }

    NS_HANDLER
    NS_ENDHANDLER

    server = [OLInternetServerSocket socketWithLocalInternetAddress:
        [OLInternetAddress addressWithCurrentHostAndPort: 19569]
        queueLength: 5];
    if ([server queueLength] != 5)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 5, but got %u", [server queueLength]];
    }

    NS_DURING

        if ([(OLInternetAddress*)[server localAddress] isLocalWildcard])
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "The server should be bound"];
        }

    NS_HANDLER

        [self errInFile: __FILE__ line: __LINE__
            format: "The server should be bound"];

    NS_ENDHANDLER

    client = [OLInternetClientSocket socket];

    NS_DURING

        [client remoteAddress];
        [self errInFile: __FILE__ line: __LINE__
            format: "The client is not connected to a server"];

    NS_HANDLER
    NS_ENDHANDLER

    client = [OLInternetClientSocket socketWithRemoteInternetAddress:
        [OLInternetAddress addressWithCurrentHostAndPort: 19569]];

    NS_DURING

        [client remoteAddress];

    NS_HANDLER

        [self errInFile: __FILE__ line: __LINE__
            format: "The client is connected"];

    NS_ENDHANDLER
}

- (void) testSocketOptions
{
    OLInternetServerSocket* server;
    unsigned scratch;
    BOOL flipped;

    server = [[OLInternetServerSocket alloc] init];
    [self logMessage: "Internet Server Socket Options:"];

    NS_DURING

        [self logMessage: " allows broadcast         : %s",
            BOOL_OPT_STR([server allowsBroadcast])];

    NS_HANDLER

        [self logMessage: " allows broadcast         : %s",
            [[localException reason] cString]];

    NS_ENDHANDLER

    [self logMessage: " don't route              : %s",
        BOOL_OPT_STR([server dontRoute])];
    [self logMessage: " keep alive               : %s",
        BOOL_OPT_STR([server keepAlive])];
    scratch = [server linger];
    if (scratch != UINT_MAX)
        [self logMessage: " linger                   : %u", scratch];
    else
        [self logMessage: " linger                   : off"];
    [self logMessage: " out-of-band inline       : %s",
        BOOL_OPT_STR([server outOfBandInline])];
    [self logMessage: " receive buffer size      : %u",
        [server receiveBufferSize]];

    NS_DURING
    
        scratch = [server receiveLowWaterMark];
        [self logMessage: " receive low water mark   : %u", scratch];

    NS_HANDLER

        [self logMessage: " receive low water mark   : %s",
            [[localException reason] cString]];

    NS_ENDHANDLER
        
    NS_DURING
    
        scratch = [server receiveTimeOut];
        [self logMessage: " receive time out         : %u", scratch];

    NS_HANDLER

        [self logMessage: " receive time out         : %s",
            [[localException reason] cString]];

    NS_ENDHANDLER

    [self logMessage: " reuse address            : %s",
        BOOL_OPT_STR([server reuseAddress])];
    [self logMessage: " reuse port               : %s",
        BOOL_OPT_STR([server reusePort])];
    [self logMessage: " send buffer size         : %u",
        [server sendBufferSize]];

    NS_DURING
    
        scratch = [server sendLowWaterMark];
        [self logMessage: " send low water mark      : %u", scratch];

    NS_HANDLER

        [self logMessage: " send low water mark      : %s",
            [[localException reason] cString]];

    NS_ENDHANDLER

    NS_DURING
    
        scratch = [server sendTimeOut];
        [self logMessage: " send time out            : %u", scratch];

    NS_HANDLER

        [self logMessage: " send time out            : %s",
            [[localException reason] cString]];

    NS_ENDHANDLER

    [self logMessage: " socket type              : %u",
        [server socketType]];
    [self logMessage: " queue length             : %u",
        [server queueLength]];

    NS_DURING

        flipped = [server allowsBroadcast] ? NO : YES;
        [server setAllowsBroadcast: flipped];
        if ([server allowsBroadcast] != flipped)
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Expected %s, but got %s",
                flipped ? "YES" : "NO", [server allowsBroadcast] ? "YES" : "NO"];
        }

    NS_HANDLER

        [self logMessage: "%s: %s",
            [[localException name] cString], [[localException reason] cString]];

    NS_ENDHANDLER

    flipped = [server dontRoute] ? NO : YES;
    [server setDontRoute: flipped];
    if ([server dontRoute] != flipped)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected %s, but got %s",
            flipped ? "YES" : "NO", [server dontRoute] ? "YES" : "NO"];
    }
    flipped = [server keepAlive] ? NO : YES;
    [server setKeepAlive: flipped];
    if ([server keepAlive] != flipped)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected %s, but got %s",
            flipped ? "YES" : "NO", [server keepAlive] ? "YES" : "NO"];
    }
    [server setLinger: 99];
    if ([server linger] != 99)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 99, but got %u", [server linger]];
    }
    flipped = [server outOfBandInline] ? NO : YES;
    [server setOutOfBandInline: flipped];
    if ([server outOfBandInline] != flipped)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected %s, but got %s",
            flipped ? "YES" : "NO", [server outOfBandInline] ? "YES" : "NO"];
    }
    [server setReceiveBufferSize: 711];
    if ([server receiveBufferSize] < 711)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 711, but got %u", [server receiveBufferSize]];
    }

    NS_DURING

        [server setReceiveLowWaterMark: 711];
        if ([server receiveLowWaterMark] != 711)
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Expected 711, but got %u", [server receiveLowWaterMark]];
        }
        [server setReceiveTimeOut: 700];
        if ([server receiveTimeOut] != 700)
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Expected 700, but got %u", [server receiveTimeOut]];
        }

    NS_HANDLER

        [self logMessage: "%s: %s",
            [[localException name] cString], [[localException reason] cString]];

    NS_ENDHANDLER

    flipped = [server reuseAddress] ? NO : YES;
    [server setReuseAddress: flipped];
    if ([server reuseAddress] != flipped)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected %s, but got %s",
            flipped ? "YES" : "NO", [server reuseAddress] ? "YES" : "NO"];
    }
    if ([server hasReusePortSupport])
    {
        flipped = [server reusePort] ? NO : YES;
        [server setReusePort: flipped];
        if ([server reusePort] != flipped)
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Expected %s, but got %s",
                flipped ? "YES" : "NO", [server reusePort] ? "YES" : "NO"];
        }
    }
    [server setSendBufferSize: 711];
    if ([server sendBufferSize] < 711)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 711, but got %u", [server sendBufferSize]];
    }

    NS_DURING

        [server setSendLowWaterMark: 711];
        if ([server sendLowWaterMark] != 711)
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Expected 711, but got %u", [server sendLowWaterMark]];
        }
        [server setSendTimeOut: 700];
        if ([server sendTimeOut] != 700)
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Expected 700, but got %u", [server sendTimeOut]];
        }

    NS_HANDLER

        [self logMessage: "%s: %s",
            [[localException name] cString], [[localException reason] cString]];

    NS_ENDHANDLER

    [server release];
}

@end
