summaryrefslogtreecommitdiffstats
path: root/Include/classobject.h
Commit message (Expand)AuthorAgeFilesLines
* The real suport for augmented assignment: new opcodes, new PyNumber andThomas Wouters2000-08-241-0/+4
* ANSI-fication and Py_PROTO extermination.Fred Drake2000-07-091-32/+33
* Change copyright notice - 2nd try.Guido van Rossum2000-06-301-6/+0
* Change copyright notice.Guido van Rossum2000-06-301-22/+7
* Add DL_IMPORT(returntype) for all officially exported functions.Guido van Rossum1998-12-041-8/+8
* Move the definition of PyMethodObject to classobject.h, so it can defineGuido van Rossum1998-07-101-0/+16
* Remove redundant references to thread stuff -- long, long ago, thereGuido van Rossum1997-03-141-6/+0
* New permission notice, includes CNRI.Guido van Rossum1996-10-251-13/+20
* changes for keyword args to built-in functions and classesGuido van Rossum1995-07-261-1/+1
* make the type a parameter of the DL_IMPORT macro, for Borland CGuido van Rossum1995-02-271-1/+1
* corrected two unconverted namesGuido van Rossum1995-01-201-1/+1
* The great renaming, phase two: all header files have been updated toGuido van Rossum1995-01-121-25/+27
* add 5th arg to instancebinopGuido van Rossum1995-01-071-1/+2
* Added 1995 copyright.Guido van Rossum1995-01-041-2/+2
* ceval.h: added Py_MakePendingCalls()Guido van Rossum1994-09-281-0/+2
* Define cl_{get,set,del}attr members in classobjectGuido van Rossum1994-09-061-6/+4
* Changes for dynamic linking under NTGuido van Rossum1994-08-181-1/+1
* * Objects/classobject.c, Include/classobject.h: added __getattr__Guido van Rossum1994-08-121-0/+12
* Merge alpha100 branch back to main trunkGuido van Rossum1994-08-011-1/+1
* * timemodule.c: Add hack for Solaris 2.Guido van Rossum1993-11-231-2/+0
* * Added support for X11 modules.Guido van Rossum1993-07-281-0/+11
* * Lots of small changes related to access.Guido van Rossum1993-05-211-5/+7
* Access checks now work, at least for instance data (not for methodsGuido van Rossum1993-05-201-1/+11
* Several changes in one:Guido van Rossum1993-05-191-1/+1
* * Changed all copyright messages to include 1993.Guido van Rossum1993-03-291-2/+2
* * classobject.[ch], {float,long,int}object.c, bltinmodule.c:Guido van Rossum1992-08-141-1/+0
* Changes so that user-defined classes can implement operations invokedGuido van Rossum1992-08-121-0/+3
* Copyright for 1992 addedGuido van Rossum1992-04-051-1/+1
* newclassobject gets a third parameter (optional class name)Guido van Rossum1991-10-201-1/+1
* Renamed class members to instances and class methods to instance methodsGuido van Rossum1991-05-051-6/+6
* Changed 'class member' to 'instance'.Guido van Rossum1991-04-041-3/+3
* Added copyright notice.Guido van Rossum1991-02-191-0/+24
* "Compiling" versionGuido van Rossum1990-12-201-1/+1
* Initial revisionGuido van Rossum1990-10-141-0/+20
n442'>442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935
/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the either Technology Preview License Agreement or the
** Beta Release License Agreement.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain
** additional rights. These rights are described in the Nokia Qt LGPL
** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
** package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://www.qtsoftware.com/contact.
** $QT_END_LICENSE$
**
****************************************************************************/


#include <QtTest/QtTest>

#include <qtextstream.h>
#include <QtNetwork/qlocalsocket.h>
#include <QtNetwork/qlocalserver.h>
#include "../../shared/util.h"

//TESTED_CLASS=QLocalServer, QLocalSocket
//TESTED_FILES=network/socket/qlocalserver.cpp network/socket/qlocalsocket.cpp

Q_DECLARE_METATYPE(QLocalSocket::LocalSocketError)
Q_DECLARE_METATYPE(QLocalSocket::LocalSocketState)

class tst_QLocalSocket : public QObject
{
    Q_OBJECT

public:
    tst_QLocalSocket();
    virtual ~tst_QLocalSocket();

public Q_SLOTS:
    void init();
    void cleanup();

private slots:
    // basics
    void server_basic();
    void server_connectionsCount();
    void socket_basic();

    void listen_data();
    void listen();

    void listenAndConnect_data();
    void listenAndConnect();

    void sendData_data();
    void sendData();

    void readBufferOverflow();

    void fullPath();

    void hitMaximumConnections_data();
    void hitMaximumConnections();

    void setSocketDescriptor();

    void threadedConnection_data();
    void threadedConnection();

    void processConnection_data();
    void processConnection();

    void longPath();
    void waitForDisconnect();
    void waitForDisconnectByServer();

    void removeServer();

    void recycleServer();

    void multiConnect();
    void writeOnlySocket();

    void debug();

};

tst_QLocalSocket::tst_QLocalSocket()
{
    if (!QFile::exists("lackey/lackey"
#ifdef Q_OS_WIN
    ".exe"
#endif
                ))
        qWarning() << "lackey executable doesn't exists!";
}

tst_QLocalSocket::~tst_QLocalSocket()
{
}

void tst_QLocalSocket::init()
{
    qRegisterMetaType<QLocalSocket::LocalSocketState>("QLocalSocket::LocalSocketState");
    qRegisterMetaType<QLocalSocket::LocalSocketError>("QLocalSocket::LocalSocketError");
}

void tst_QLocalSocket::cleanup()
{
}

class LocalServer : public QLocalServer
{
    Q_OBJECT

public:
    LocalServer() : QLocalServer()
    {
        connect(this, SIGNAL(newConnection()), this, SLOT(slotNewConnection()));
    }

    bool listen(const QString &name)
    {
        removeServer(name);
        return QLocalServer::listen(name);
    }

    QList<int> hits;

protected:
    void incomingConnection(quintptr socketDescriptor)
    {
        hits.append(socketDescriptor);
        QLocalServer::incomingConnection(socketDescriptor);
    }

private slots:
    void slotNewConnection() {
        QVERIFY(!hits.isEmpty());
        QVERIFY(hasPendingConnections());
    }
};

class LocalSocket : public QLocalSocket
{
    Q_OBJECT

public:
    LocalSocket(QObject *parent = 0) : QLocalSocket(parent)
    {
        connect(this, SIGNAL(connected()),
                this, SLOT(slotConnected()));
        connect(this, SIGNAL(disconnected()),
                this, SLOT(slotDisconnected()));
        connect(this, SIGNAL(error(QLocalSocket::LocalSocketError)),
                this, SLOT(slotError(QLocalSocket::LocalSocketError)));
        connect(this, SIGNAL(stateChanged(QLocalSocket::LocalSocketState)),
                this, SLOT(slotStateChanged(QLocalSocket::LocalSocketState)));
        connect(this, SIGNAL(readyRead()),
                this, SLOT(slotReadyRead()));
    }

private slots:
    void slotConnected()
    {
        QCOMPARE(state(), QLocalSocket::ConnectedState);
    }
    void slotDisconnected()
    {
        QCOMPARE(state(), QLocalSocket::UnconnectedState);
    }
    void slotError(QLocalSocket::LocalSocketError newError)
    {
        QVERIFY(errorString() != "Unknown error");
        QCOMPARE(error(), newError);
    }
    void slotStateChanged(QLocalSocket::LocalSocketState newState)
    {
        QCOMPARE(state(), newState);
    }
    void slotReadyRead()
    {
        QVERIFY(bytesAvailable() > 0);
    }
};

// basic test make sure no segfaults and check default values
void tst_QLocalSocket::server_basic()
{
    LocalServer server;
    QSignalSpy spyNewConnection(&server, SIGNAL(newConnection()));
    server.close();
    QCOMPARE(server.errorString(), QString());
    QCOMPARE(server.hasPendingConnections(), false);
    QCOMPARE(server.isListening(), false);
    QCOMPARE(server.maxPendingConnections(), 30);
    QCOMPARE(server.nextPendingConnection(), (QLocalSocket*)0);
    QCOMPARE(server.serverName(), QString());
    QCOMPARE(server.fullServerName(), QString());
    QCOMPARE(server.serverError(), QAbstractSocket::UnknownSocketError);
    server.setMaxPendingConnections(20);
    bool timedOut = true;
    QCOMPARE(server.waitForNewConnection(3000, &timedOut), false);
    QVERIFY(!timedOut);
    QCOMPARE(server.listen(QString()), false);

    QCOMPARE(server.hits.count(), 0);
    QCOMPARE(spyNewConnection.count(), 0);
}

void tst_QLocalSocket::server_connectionsCount()
{
    LocalServer server;
    server.setMaxPendingConnections(10);
    QCOMPARE(server.maxPendingConnections(), 10);
}

// basic test make sure no segfaults and check default values
void tst_QLocalSocket::socket_basic()
{
    LocalSocket socket;
    QSignalSpy spyConnected(&socket, SIGNAL(connected()));
    QSignalSpy spyDisconnected(&socket, SIGNAL(disconnected()));
    QSignalSpy spyError(&socket, SIGNAL(error(QLocalSocket::LocalSocketError)));
    QSignalSpy spyStateChanged(&socket, SIGNAL(stateChanged(QLocalSocket::LocalSocketState)));
    QSignalSpy spyReadyRead(&socket, SIGNAL(readyRead()));

    QCOMPARE(socket.serverName(), QString());
    QCOMPARE(socket.fullServerName(), QString());
    socket.abort();
    QVERIFY(socket.bytesAvailable() == 0);
    QVERIFY(socket.bytesToWrite() == 0);
    QCOMPARE(socket.canReadLine(), false);
    socket.close();
    socket.disconnectFromServer();
    QCOMPARE(QLocalSocket::UnknownSocketError, socket.error());
    QVERIFY(socket.errorString() != QString());
    QCOMPARE(socket.flush(), false);
    QCOMPARE(socket.isValid(), false);
    QVERIFY(socket.readBufferSize() == 0);
    socket.setReadBufferSize(0);
    //QCOMPARE(socket.socketDescriptor(), -1);
    QCOMPARE(socket.state(), QLocalSocket::UnconnectedState);
    QCOMPARE(socket.waitForConnected(0), false);
    QCOMPARE(socket.waitForDisconnected(0), false);
    QCOMPARE(socket.waitForReadyRead(0), false);

    QCOMPARE(spyConnected.count(), 0);
    QCOMPARE(spyDisconnected.count(), 0);
    QCOMPARE(spyError.count(), 0);
    QCOMPARE(spyStateChanged.count(), 0);
    QCOMPARE(spyReadyRead.count(), 0);
}

void tst_QLocalSocket::listen_data()
{
    QTest::addColumn<QString>("name");
    QTest::addColumn<bool>("canListen");
    QTest::addColumn<bool>("close");
    QTest::newRow("null") << QString() << false << false;
    QTest::newRow("tst_localsocket") << "tst_localsocket" << true << true;
    QTest::newRow("tst_localsocket") << "tst_localsocket" << true << false;
}

// start a server that listens, but don't connect a socket, make sure everything is in order
void tst_QLocalSocket::listen()
{
    LocalServer server;
    QSignalSpy spyNewConnection(&server, SIGNAL(newConnection()));

    QFETCH(QString, name);
    QFETCH(bool, canListen);
    QFETCH(bool, close);
    QVERIFY2((server.listen(name) == canListen), server.errorString().toLatin1().constData());

    // test listening
    QCOMPARE(server.serverName(), name);
    QVERIFY(server.fullServerName().contains(name));
    QCOMPARE(server.isListening(), canListen);
    QCOMPARE(server.hasPendingConnections(), false);
    QCOMPARE(server.nextPendingConnection(), (QLocalSocket*)0);
    QCOMPARE(server.hits.count(), 0);
    QCOMPARE(spyNewConnection.count(), 0);
    if (canListen) {
        QVERIFY(server.errorString() == QString());
        QCOMPARE(server.serverError(), QAbstractSocket::UnknownSocketError);
        // already isListening
        QVERIFY(!server.listen(name));
    } else {
        QVERIFY(server.errorString() != QString());
        QCOMPARE(server.serverError(), QAbstractSocket::HostNotFoundError);
    }
    QCOMPARE(server.maxPendingConnections(), 30);
    bool timedOut = false;
    QCOMPARE(server.waitForNewConnection(3000, &timedOut), false);
    QCOMPARE(timedOut, canListen);
    if (close)
        server.close();
}

void tst_QLocalSocket::listenAndConnect_data()
{
    QTest::addColumn<QString>("name");
    QTest::addColumn<bool>("canListen");
    QTest::addColumn<int>("connections");
    for (int i = 0; i < 3; ++i) {
        int connections = i;
        if (i == 2)
            connections = 5;
        QTest::newRow(QString("null %1").arg(i).toLatin1()) << QString() << false << connections;
        QTest::newRow(QString("tst_localsocket %1").arg(i).toLatin1()) << "tst_localsocket" << true << connections;
    }
}

void tst_QLocalSocket::listenAndConnect()
{
    LocalServer server;
    QSignalSpy spyNewConnection(&server, SIGNAL(newConnection()));

    QFETCH(QString, name);
    QFETCH(bool, canListen);

    QCOMPARE(server.listen(name), canListen);
    QTest::qWait(1000);
    //QVERIFY(!server.errorString().isEmpty());
    QCOMPARE(server.serverError(),
            canListen ? QAbstractSocket::UnknownSocketError : QAbstractSocket::HostNotFoundError);

    // test creating connection(s)
    QFETCH(int, connections);
    QList<QLocalSocket*> sockets;
    for (int i = 0; i < connections; ++i) {
        LocalSocket *socket = new LocalSocket;

        QSignalSpy spyConnected(socket, SIGNAL(connected()));
        QSignalSpy spyDisconnected(socket, SIGNAL(disconnected()));
        QSignalSpy spyError(socket, SIGNAL(error(QLocalSocket::LocalSocketError)));
        QSignalSpy spyStateChanged(socket, SIGNAL(stateChanged(QLocalSocket::LocalSocketState)));
        QSignalSpy spyReadyRead(socket, SIGNAL(readyRead()));

        socket->connectToServer(name);
#ifdef QT_LOCALSOCKET_TCP
        QTest::qWait(250);
#endif

        QCOMPARE(socket->serverName(), name);
        QVERIFY(socket->fullServerName().contains(name));
        sockets.append(socket);
        if (canListen) {
            QCOMPARE(socket->errorString(), QString("Unknown error"));
            QCOMPARE(socket->error(), QLocalSocket::UnknownSocketError);
            QCOMPARE(socket->state(), QLocalSocket::ConnectedState);
            //QVERIFY(socket->socketDescriptor() != -1);
            QCOMPARE(spyError.count(), 0);
        } else {
            QVERIFY(socket->errorString() != QString());
            QVERIFY(socket->error() != QLocalSocket::UnknownSocketError);
            QCOMPARE(socket->state(), QLocalSocket::UnconnectedState);
            //QVERIFY(socket->socketDescriptor() == -1);
            QCOMPARE(qVariantValue<QLocalSocket::LocalSocketError>(spyError.first()[0]),
                     QLocalSocket::ServerNotFoundError);
        }

        QVERIFY(socket->bytesAvailable() == 0);
        QVERIFY(socket->bytesToWrite() == 0);
        QCOMPARE(socket->canReadLine(), false);
        QCOMPARE(socket->flush(), false);
        QCOMPARE(socket->isValid(), canListen);
        QCOMPARE(socket->readBufferSize(), (qint64)0);
        QCOMPARE(socket->waitForConnected(0), canListen);
        QCOMPARE(socket->waitForReadyRead(0), false);

        QTRY_COMPARE(spyConnected.count(), canListen ? 1 : 0);
        QCOMPARE(spyDisconnected.count(), 0);

        // error signals
        QVERIFY(spyError.count() >= 0);
        if (canListen) {
            if (spyError.count() > 0)
                QCOMPARE(qVariantValue<QLocalSocket::LocalSocketError>(spyError.first()[0]),
                         QLocalSocket::SocketTimeoutError);
        } else {
            QCOMPARE(qVariantValue<QLocalSocket::LocalSocketError>(spyError.first()[0]),
                     QLocalSocket::ServerNotFoundError);
        }

        // Check first and last state
        QCOMPARE(qVariantValue<QLocalSocket::LocalSocketState>(spyStateChanged.first()[0]),
                 QLocalSocket::ConnectingState);
#if 0
        for (int j = 0; j < spyStateChanged.count(); ++j) {
            QLocalSocket::LocalSocketState s;
            s = qVariantValue<QLocalSocket::LocalSocketState>(spyStateChanged.at(j).at(0));
	    qDebug() << s;
        }
#endif
        if (canListen)
            QCOMPARE(qVariantValue<QLocalSocket::LocalSocketState>(spyStateChanged.last()[0]),
                     QLocalSocket::ConnectedState);
        QCOMPARE(spyStateChanged.count(), 2);
        QCOMPARE(spyReadyRead.count(), 0);

        bool timedOut = true;
        QCOMPARE(server.waitForNewConnection(3000, &timedOut), canListen);
        QVERIFY(!timedOut);
        QCOMPARE(server.hasPendingConnections(), canListen);
        QCOMPARE(server.isListening(), canListen);
        // NOTE: socket disconnecting is not tested here

        // server checks post connection
        if (canListen) {
            QCOMPARE(server.serverName(), name);
            QVERIFY(server.fullServerName().contains(name));
            QVERIFY(server.nextPendingConnection() != (QLocalSocket*)0);
            QTRY_COMPARE(server.hits.count(), i + 1);
            QCOMPARE(spyNewConnection.count(), i + 1);
            QVERIFY(server.errorString() == QString());
            QCOMPARE(server.serverError(), QAbstractSocket::UnknownSocketError);
        } else {
            QVERIFY(server.serverName().isEmpty());
            QVERIFY(server.fullServerName().isEmpty());
            QVERIFY(server.nextPendingConnection() == (QLocalSocket*)0);
            QCOMPARE(spyNewConnection.count(), 0);
            QCOMPARE(server.hits.count(), 0);
            QVERIFY(server.errorString() != QString());
            QCOMPARE(server.serverError(), QAbstractSocket::HostNotFoundError);
        }
    }
    qDeleteAll(sockets.begin(), sockets.end());

    server.close();

    QCOMPARE(server.hits.count(), (canListen ? connections : 0));
    QCOMPARE(spyNewConnection.count(), (canListen ? connections : 0));
}

void tst_QLocalSocket::sendData_data()
{
    listenAndConnect_data();
}

void tst_QLocalSocket::sendData()
{
    QFETCH(QString, name);
    QFETCH(bool, canListen);

    LocalServer server;
    QSignalSpy spy(&server, SIGNAL(newConnection()));

    QCOMPARE(server.listen(name), canListen);

    LocalSocket socket;
    QSignalSpy spyConnected(&socket, SIGNAL(connected()));
    QSignalSpy spyDisconnected(&socket, SIGNAL(disconnected()));
    QSignalSpy spyError(&socket, SIGNAL(error(QLocalSocket::LocalSocketError)));
    QSignalSpy spyStateChanged(&socket, SIGNAL(stateChanged(QLocalSocket::LocalSocketState)));
    QSignalSpy spyReadyRead(&socket, SIGNAL(readyRead()));

    // test creating a connection
    socket.connectToServer(name);
    bool timedOut = true;
    QCOMPARE(server.waitForNewConnection(3000, &timedOut), canListen);
#ifdef QT_LOCALSOCKET_TCP
    QTest::qWait(250);
#endif
    QVERIFY(!timedOut);
    QCOMPARE(spyConnected.count(), canListen ? 1 : 0);
    QCOMPARE(socket.state(), canListen ? QLocalSocket::ConnectedState : QLocalSocket::UnconnectedState);

    // test sending/receiving data
    if (server.hasPendingConnections()) {
        QString testLine = "test";
	for (int i = 0; i < 50000; ++i)
		testLine += "a";
        QLocalSocket *serverSocket = server.nextPendingConnection();
        QVERIFY(serverSocket);
        QCOMPARE(serverSocket->state(), QLocalSocket::ConnectedState);
        QTextStream out(serverSocket);
        QTextStream in(&socket);
        out << testLine << endl;
        bool wrote = serverSocket->waitForBytesWritten(3000);

        if (!socket.canReadLine())
            QVERIFY(socket.waitForReadyRead());

        QVERIFY(socket.bytesAvailable() >= 0);
        QCOMPARE(socket.bytesToWrite(), (qint64)0);
        QCOMPARE(socket.flush(), false);
        QCOMPARE(socket.isValid(), canListen);
        QCOMPARE(socket.readBufferSize(), (qint64)0);
        QCOMPARE(spyReadyRead.count(), 1);

        QVERIFY(testLine.startsWith(in.readLine()));
        QVERIFY(wrote || serverSocket->waitForBytesWritten(1000));
        QCOMPARE(serverSocket->errorString(), QString("Unknown error"));
        QCOMPARE(socket.errorString(), QString("Unknown error"));
    }

    socket.disconnectFromServer();
    QCOMPARE(spyConnected.count(), canListen ? 1 : 0);
    QCOMPARE(spyDisconnected.count(), canListen ? 1 : 0);
    QCOMPARE(spyError.count(), canListen ? 0 : 1);
    QCOMPARE(spyStateChanged.count(), canListen ? 4 : 2);
    QCOMPARE(spyReadyRead.count(), canListen ? 1 : 0);

    server.close();

    QCOMPARE(server.hits.count(), (canListen ? 1 : 0));
    QCOMPARE(spy.count(), (canListen ? 1 : 0));
}

void tst_QLocalSocket::readBufferOverflow()
{
    const int readBufferSize = 128;
    const int dataBufferSize = readBufferSize * 2;
    const QString serverName = QLatin1String("myPreciousTestServer");
    LocalServer server;
    server.listen(serverName);
    QVERIFY(server.isListening());

    LocalSocket client;
    client.setReadBufferSize(readBufferSize);
    client.connectToServer(serverName);

    bool timedOut = true;
    QVERIFY(server.waitForNewConnection(3000, &timedOut));
    QVERIFY(!timedOut);

    QCOMPARE(client.state(), QLocalSocket::ConnectedState);
    QVERIFY(server.hasPendingConnections());

    QLocalSocket* serverSocket = server.nextPendingConnection();
    char* buffer = (char*)qMalloc(dataBufferSize);
    memset(buffer, 0, dataBufferSize);
    serverSocket->write(buffer, dataBufferSize);
    serverSocket->flush();
    qFree(buffer);

    QVERIFY(client.waitForReadyRead());
    QCOMPARE(client.readAll().size(), dataBufferSize);
}

// QLocalSocket/Server can take a name or path, check that it works as expected
void tst_QLocalSocket::fullPath()
{
    LocalServer server;
    QString name = "qlocalsocket_pathtest";
#if defined(QT_LOCALSOCKET_TCP)
    QString path = "QLocalServer";
#elif defined(Q_OS_WIN)
    QString path = "\\\\.\\pipe\\";
#else
    QString path = "/tmp";
#endif
    QString serverName = path + '/' + name;
    QVERIFY2(server.listen(serverName), server.errorString().toLatin1().constData());
    QCOMPARE(server.serverName(), serverName);
    QCOMPARE(server.fullServerName(), serverName);

    LocalSocket socket;
    socket.connectToServer(serverName);
    QCOMPARE(socket.serverName(), serverName);
    QCOMPARE(socket.fullServerName(), serverName);
    socket.disconnectFromServer();
#ifdef QT_LOCALSOCKET_TCP
    QTest::qWait(250);
#endif
    QCOMPARE(socket.serverName(), QString());
    QCOMPARE(socket.fullServerName(), QString());
}

void tst_QLocalSocket::hitMaximumConnections_data()
{
    QTest::addColumn<int>("max");
    QTest::newRow("none") << 0;
    QTest::newRow("1") << 1;
    QTest::newRow("3") << 3;
}

void tst_QLocalSocket::hitMaximumConnections()
{
    QFETCH(int, max);
    LocalServer server;
    QString name = "tst_localsocket";
    server.setMaxPendingConnections(max);
    QVERIFY2(server.listen(name), server.errorString().toLatin1().constData());
    int connections = server.maxPendingConnections() + 1;
    QList<QLocalSocket*> sockets;
    for (int i = 0; i < connections; ++i) {
        LocalSocket *socket = new LocalSocket;
        sockets.append(socket);
        socket->connectToServer(name);
    }
   bool timedOut = true;
   QVERIFY(server.waitForNewConnection(3000, &timedOut));
   QVERIFY(!timedOut);
   QVERIFY(server.hits.count() > 0);
   qDeleteAll(sockets.begin(), sockets.end());
}

// check that state and mode are kept
void tst_QLocalSocket::setSocketDescriptor()
{
    LocalSocket socket;
    quintptr minusOne = -1;
    socket.setSocketDescriptor(minusOne, QLocalSocket::ConnectingState, QIODevice::Append);
    QCOMPARE(socket.socketDescriptor(), minusOne);
    QCOMPARE(socket.state(), QLocalSocket::ConnectingState);
    QVERIFY((socket.openMode() & QIODevice::Append) != 0);
}

class Client : public QThread
{

public:
    void run()
    {
        QString testLine = "test";
        LocalSocket socket;
        QSignalSpy spyReadyRead(&socket, SIGNAL(readyRead()));
        int tries = 0;
        do {
            socket.connectToServer("qlocalsocket_threadtest");
            if (socket.error() != QLocalSocket::ServerNotFoundError
                && socket.error() != QLocalSocket::ConnectionRefusedError)
                break;
            QTest::qWait(100);
            ++tries;
        } while ((socket.error() == QLocalSocket::ServerNotFoundError
                  || socket.error() == QLocalSocket::ConnectionRefusedError)
		 && tries < 1000);
        if (tries == 0 && socket.state() != QLocalSocket::ConnectedState) {
            QVERIFY(socket.waitForConnected(3000));
            QVERIFY(socket.state() == QLocalSocket::ConnectedState);
        }

        // We should *not* have this signal yet!
        if (tries == 0)
            QCOMPARE(spyReadyRead.count(), 0);
        socket.waitForReadyRead();
        QCOMPARE(spyReadyRead.count(), 1);
        QTextStream in(&socket);
        QCOMPARE(in.readLine(), testLine);
        socket.close();
    }
};

class Server : public QThread
{

public:
    int clients;
    void run()
    {
        QString testLine = "test";
        LocalServer server;
        server.setMaxPendingConnections(10);
        QVERIFY2(server.listen("qlocalsocket_threadtest"),
                 server.errorString().toLatin1().constData());
        int done = clients;
        while (done > 0) {
            bool timedOut = true;
            QVERIFY(server.waitForNewConnection(3000, &timedOut));
            QVERIFY(!timedOut);
            QLocalSocket *serverSocket = server.nextPendingConnection();
            QVERIFY(serverSocket);
            QTextStream out(serverSocket);
            out << testLine << endl;
            QCOMPARE(serverSocket->state(), QLocalSocket::ConnectedState);
            QVERIFY2(serverSocket->waitForBytesWritten(), serverSocket->errorString().toLatin1().constData());
            QCOMPARE(serverSocket->errorString(), QString("Unknown error"));
            --done;
            delete serverSocket;
        }
        QCOMPARE(server.hits.count(), clients);
    }
};

void tst_QLocalSocket::threadedConnection_data()
{
    QTest::addColumn<int>("threads");
    QTest::newRow("1 client") << 1;
    QTest::newRow("2 clients") << 2;
#ifdef Q_OS_WINCE
    QTest::newRow("4 clients") << 4;
#endif
#ifndef Q_OS_WIN
    QTest::newRow("5 clients") << 5;
    QTest::newRow("10 clients") << 10;
#endif
#ifndef Q_OS_WINCE
    QTest::newRow("20 clients") << 20;
#endif
}

void tst_QLocalSocket::threadedConnection()
{
    QFETCH(int, threads);
    Server server;
    server.clients = threads;
    server.start();

    QList<Client*> clients;
    for (int i = 0; i < threads; ++i) {
        clients.append(new Client());
        clients.last()->start();
    }

    server.wait();
    while (!clients.isEmpty()) {
        QVERIFY(clients.first()->wait(30000));
        Client *client =clients.takeFirst();
	client->terminate();
	delete client;
    }
}

void tst_QLocalSocket::processConnection_data()
{
    QTest::addColumn<int>("processes");
    QTest::newRow("1 client") << 1;
#ifndef Q_OS_WIN
    QTest::newRow("2 clients") << 2;
    QTest::newRow("5 clients") << 5;
#endif
    QTest::newRow("30 clients") << 30;
}

/*!
    Create external processes that produce and consume.
 */
void tst_QLocalSocket::processConnection()
{
#if defined(QT_NO_PROCESS)
    QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll);
#else
    QFETCH(int, processes);
    QStringList serverArguments = QStringList() << SRCDIR "lackey/scripts/server.js" << QString::number(processes);
    QProcess producer;
    producer.setProcessChannelMode(QProcess::ForwardedChannels);
#ifdef Q_WS_QWS
    serverArguments << "-qws";
#endif
    QList<QProcess*> consumers;
    producer.start("lackey/lackey", serverArguments);
    QVERIFY(producer.waitForStarted(-1));
    QTest::qWait(2000);
    for (int i = 0; i < processes; ++i) {
       QStringList arguments = QStringList() << SRCDIR "lackey/scripts/client.js";
#ifdef Q_WS_QWS
       arguments << "-qws";
#endif
        QProcess *p = new QProcess;
        p->setProcessChannelMode(QProcess::ForwardedChannels);
        consumers.append(p);
        p->start("lackey/lackey", arguments);
    }

    while (!consumers.isEmpty()) {
        consumers.first()->waitForFinished(20000);
        QCOMPARE(consumers.first()->exitStatus(), QProcess::NormalExit);
        QCOMPARE(consumers.first()->exitCode(), 0);
        QProcess *consumer = consumers.takeFirst();
        consumer->terminate();
        delete consumer;
    }
    producer.waitForFinished(15000);
#endif
}

void tst_QLocalSocket::longPath()
{
#ifndef Q_OS_WIN
    QString name;
    for (int i = 0; i < 256; ++i)
        name += 'a';
    LocalServer server;
    QVERIFY(!server.listen(name));

    LocalSocket socket;
    socket.connectToServer(name);
    QCOMPARE(socket.state(), QLocalSocket::UnconnectedState);
#endif
}

void tst_QLocalSocket::waitForDisconnect()
{
    QString name = "tst_localsocket";
    LocalServer server;
    QVERIFY(server.listen(name));
    LocalSocket socket;
    socket.connectToServer(name);
    QVERIFY(socket.waitForConnected(3000));
    QVERIFY(server.waitForNewConnection(3000));
    QLocalSocket *serverSocket = server.nextPendingConnection();
    QVERIFY(serverSocket);
    socket.disconnectFromServer();
    QTime timer;
    timer.start();
    QVERIFY(serverSocket->waitForDisconnected(3000));
    QVERIFY(timer.elapsed() < 2000);
}

void tst_QLocalSocket::waitForDisconnectByServer()
{
    QString name = "tst_localsocket";
    LocalServer server;
    QVERIFY(server.listen(name));
    LocalSocket socket;
    QSignalSpy spy(&socket, SIGNAL(disconnected()));
    QVERIFY(spy.isValid());
    socket.connectToServer(name);
    QVERIFY(socket.waitForConnected(3000));
    QVERIFY(server.waitForNewConnection(3000));
    QLocalSocket *serverSocket = server.nextPendingConnection();
    QVERIFY(serverSocket);
    serverSocket->close();
    QVERIFY(serverSocket->state() == QLocalSocket::UnconnectedState);
    QVERIFY(socket.waitForDisconnected(3000));
    QCOMPARE(spy.count(), 1);
}

void tst_QLocalSocket::removeServer()
{
    // this is a hostile takeover, but recovering from a crash results in the same
    QLocalServer server, server2;
    QVERIFY(QLocalServer::removeServer("cleanuptest"));
    QVERIFY(server.listen("cleanuptest"));
#ifndef Q_OS_WIN
    // on Windows, there can be several sockets listening on the same pipe
    // on Unix, there can only be one socket instance
    QVERIFY(! server2.listen("cleanuptest"));
#endif
    QVERIFY(QLocalServer::removeServer("cleanuptest"));
    QVERIFY(server2.listen("cleanuptest"));
}

void tst_QLocalSocket::recycleServer()
{
    LocalServer server;
    QLocalSocket client;

    QVERIFY(server.listen("recycletest1"));
    client.connectToServer("recycletest1");
    QVERIFY(client.waitForConnected(201));
    QVERIFY(server.waitForNewConnection(201));
    QVERIFY(server.nextPendingConnection() != 0);

    server.close();
    client.disconnectFromServer();
    qApp->processEvents();

    QVERIFY(server.listen("recycletest2"));
    client.connectToServer("recycletest2");
    QVERIFY(client.waitForConnected(202));
    QVERIFY(server.waitForNewConnection(202));
    QVERIFY(server.nextPendingConnection() != 0);
}

void tst_QLocalSocket::multiConnect()
{
    QLocalServer server;
    QLocalSocket client1;
    QLocalSocket client2;
    QLocalSocket client3;

    QVERIFY(server.listen("multiconnect"));

    client1.connectToServer("multiconnect");
    client2.connectToServer("multiconnect");
    client3.connectToServer("multiconnect");

    QVERIFY(client1.waitForConnected(201));
    QVERIFY(client2.waitForConnected(202));
    QVERIFY(client3.waitForConnected(203));

    QVERIFY(server.waitForNewConnection(201));
    QVERIFY(server.nextPendingConnection() != 0);
    QVERIFY(server.waitForNewConnection(202));
    QVERIFY(server.nextPendingConnection() != 0);
    QVERIFY(server.waitForNewConnection(203));
    QVERIFY(server.nextPendingConnection() != 0);
}

void tst_QLocalSocket::writeOnlySocket()
{
    QLocalServer server;
    QVERIFY(server.listen("writeOnlySocket"));

    QLocalSocket client;
    client.connectToServer("writeOnlySocket", QIODevice::WriteOnly);
    QVERIFY(client.waitForConnected());

    QVERIFY(server.waitForNewConnection());
    QLocalSocket* serverSocket = server.nextPendingConnection();
    QVERIFY(serverSocket);

    QCOMPARE(client.bytesAvailable(), qint64(0));
    QCOMPARE(client.state(), QLocalSocket::ConnectedState);
}

void tst_QLocalSocket::debug()
{
    // Make sure this compiles
    qDebug() << QLocalSocket::ConnectionRefusedError << QLocalSocket::UnconnectedState;
}

QTEST_MAIN(tst_QLocalSocket)
#include "tst_qlocalsocket.moc"