<commit_before>#include "progressdialog.h"
#include "ui_progressdialog.h"
#include <QThread>
#include <QtConcurrentRun.h>
#include <algorithm>

ProgressDialog::ProgressDialog(QWidget *parent, Operations Operation, std::vector<std::string> Paths, std::string OutPath, std::vector<Drive*>& Drives) :
    QDialog(parent),
    ui(new Ui::ProgressDialog)
{
    ui->setupUi(this);
    scene = 0;
    PathCount = Paths.size();
    FilesTotal = PathCount;
    FilesCompleted = 0;
    WorkerThread = QtConcurrent::run(this, &ProgressDialog::PerformOperation, Operation, Paths, OutPath, Drives);
    operation = Operation;
}

ProgressDialog::~ProgressDialog()
{
    delete ui;
}

void ProgressDialog::PerformOperation(Operations Operation, std::vector<std::string> Paths, std::string OutPath, std::vector<Drive*>& Drives)
{
    switch(Operation)
    {
    case OperationCopyToDisk:
        CopyFileToLocalDisk(Paths, OutPath, Drives);
        break;
    }
}

void ProgressDialog::CopyFileToLocalDisk(std::vector<std::string> Paths, std::string OutPath, std::vector<Drive*>& Drives)
{
    qRegisterMetaType<Progress>();
    std::vector<Drive*> ConnectedDrives;
    // For each top-level path
    for (int i = 0; i < Paths.size(); i++)
    {
        // Split the path so that we can get the disk name
        std::vector<std::string> Split;
        Helpers::split(Paths.at(i), '/', Split);
        Drive *d;
        // For each drive
        for (int j = 0; j < Drives.size(); j++)
        {
            // Set d, break when we've found the proper drive.
            // POSSIBLE BUG: we'll go ahead and trust that everything is handled properly in MainForm though
            d = Drives.at(j);
            if (QString::fromWCharArray(d->FriendlyName.c_str()) == Helpers::QStringFromStdString(Split.at(0)))
                break;
        }

        // Find the drive in our ConnectedDrives vector
        vector<Drive*>::iterator it = std::find(ConnectedDrives.begin(), ConnectedDrives.end(), d);
        if (it == ConnectedDrives.end())
        {
            // If the drive was not connected, connect it
            connect(d, SIGNAL(FileProgressChanged(const Progress&)), this, SLOT(OnFileProgressChanged(const Progress&)), Qt::QueuedConnection);
            // Add the drive to our vector of connected drives
            ConnectedDrives.push_back(d);
        }
        // We were passed a folder path
        if (PathCount > 1)
        {
            try
            {
                // Get the file
                File *f = d->FileFromPath(Paths.at(i));
                // Write the files to folder path + file name
                d->CopyFileToLocalDisk(f, (OutPath + "/") + Split.at(Split.size() - 1));
            }
            // Exception was thrown, we're dealing with a folder path
            catch(...)
            {
                // Add folder handling
            }
        }
        else
        {
            // See if this is a file...
            try
            {
                std::string path(Paths.at(i));
                File *f = d->FileFromPath(path);
                // Succeeded, write the file to the destined folder
                d->CopyFileToLocalDisk(f, OutPath);
            }
            catch (...)
            {
                // Add folder handling here
            }
        }
        FilesCompleted++;
    }
}

void ProgressDialog::OnFileProgressChanged(const Progress& p)
{
    // If the maximums don't match
    if (ui->progressCurrent->maximum() != p.Maximum)
        // Set the progress bar maximum value
        ui->progressCurrent->setMaximum(p.Maximum);

    // Set the progress bar's current value
    ui->progressCurrent->setValue(p.Current);

    // If we're dealing with one file and the path count is one
    if (FilesTotal == 1 && PathCount == 1)
    {
        // Set the total progress bar's valuesto that of the current progress bar
        ui->progressTotal->setValue(p.Current);
        if (ui->progressTotal->maximum() != p.Maximum)
            ui->progressTotal->setMaximum(p.Maximum);
    }

    // Set the title of the total groupbox to reflect the current file out of x
    ui->groupBoxTotal->setTitle(QString("Total - File %1 out of %2").arg(FilesCompleted + 1).arg(FilesTotal));

    string Title = Helpers::QStringToStdString(ui->groupBoxCurrent->title());
    // If we've just moved on to a new file
    if (Title.substr(Title.find_first_of('/') + 1) != p.FilePath)
    {
        ui->groupBoxCurrent->setTitle(QString::fromWCharArray((p.Device->FriendlyName + L"/").c_str()) + Helpers::QStringFromStdString(p.FilePath));

        // If the file is an STFS package
        if (p.IsStfsPackage)
        {
            // Delete the current scene for the graphics view
            if (scene)
            {
                delete scene;
            }
            // Get the package image
            QImage image(p.PackageImage);

            // Create a new scene with the dimensions of the image
            scene = new QGraphicsScene(ui->graphicsView);
            scene->setSceneRect(0,0,image.width(), image.height());

            // Set the graphics view size
            ui->graphicsView->setFixedSize(64, 64);

            // Set the image that the scene will display
            scene->addPixmap(QPixmap::fromImage(image));

            // Set the scene and display the image
            ui->graphicsView->setScene(scene);
            ui->graphicsView->show();

            // Makes the graphics view scale the image down so there's no scrolling
            ui->graphicsView->fitInView(ui->graphicsView->rect(), Qt::KeepAspectRatio);

            // Get the package name (real)
            ui->labelFileName->setText(p.PackageName);
        }
        // Not an STFS package -- use the default file image
        else
        {
            // Delete the current scene if there is one
            if (scene)
                delete scene;

            ui->labelFileName->setText(Helpers::QStringFromStdString(p.FileName));

            // Get the file icon image
            QImage image(":/File System Icons/iFile");

            // Get the file icon pixmap
            QPixmap pixmap = QPixmap::fromImage(image);

            // Create a new scene
            scene = new QGraphicsScene(ui->graphicsView);
            scene->setSceneRect(0,0,64, 64);

            // Set the graphics view's fixed size
            ui->graphicsView->setFixedSize(64, 64);

            // Set the scene image
            scene->addPixmap(pixmap.scaled(QSize(64, 64), Qt::KeepAspectRatio));

            // Set the graphics view's scene, display the image
            ui->graphicsView->setScene(scene);
            ui->graphicsView->show();

            // Makes the image fit within the bounds of the graphics view without scrolling
            ui->graphicsView->fitInView(ui->graphicsView->rect(), Qt::KeepAspectRatio);
        }
    }
    if (FilesCompleted == FilesTotal)
        this->close();
}
<commit_msg>ProgressDialog: Added handling for multiple files (progress bar)<commit_after>#include "progressdialog.h"
#include "ui_progressdialog.h"
#include <QThread>
#include <QtConcurrentRun.h>
#include <algorithm>

ProgressDialog::ProgressDialog(QWidget *parent, Operations Operation, std::vector<std::string> Paths, std::string OutPath, std::vector<Drive*>& Drives) :
    QDialog(parent),
    ui(new Ui::ProgressDialog)
{
    ui->setupUi(this);
    scene = 0;
    PathCount = Paths.size();
    FilesTotal = PathCount;
    FilesCompleted = 0;
    WorkerThread = QtConcurrent::run(this, &ProgressDialog::PerformOperation, Operation, Paths, OutPath, Drives);
    operation = Operation;
}

ProgressDialog::~ProgressDialog()
{
    delete ui;
}

void ProgressDialog::PerformOperation(Operations Operation, std::vector<std::string> Paths, std::string OutPath, std::vector<Drive*>& Drives)
{
    switch(Operation)
    {
    case OperationCopyToDisk:
        CopyFileToLocalDisk(Paths, OutPath, Drives);
        break;
    }
}

void ProgressDialog::CopyFileToLocalDisk(std::vector<std::string> Paths, std::string OutPath, std::vector<Drive*>& Drives)
{
    qRegisterMetaType<Progress>();
    std::vector<Drive*> ConnectedDrives;
    // For each top-level path
    for (int i = 0; i < Paths.size(); i++)
    {
        // Split the path so that we can get the disk name
        std::vector<std::string> Split;
        Helpers::split(Paths.at(i), '/', Split);
        Drive *d;
        // For each drive
        for (int j = 0; j < Drives.size(); j++)
        {
            // Set d, break when we've found the proper drive.
            // POSSIBLE BUG: we'll go ahead and trust that everything is handled properly in MainForm though
            d = Drives.at(j);
            if (QString::fromWCharArray(d->FriendlyName.c_str()) == Helpers::QStringFromStdString(Split.at(0)))
                break;
        }

        // Find the drive in our ConnectedDrives vector
        vector<Drive*>::iterator it = std::find(ConnectedDrives.begin(), ConnectedDrives.end(), d);
        if (it == ConnectedDrives.end())
        {
            // If the drive was not connected, connect it
            connect(d, SIGNAL(FileProgressChanged(const Progress&)), this, SLOT(OnFileProgressChanged(const Progress&)), Qt::QueuedConnection);
            // Add the drive to our vector of connected drives
            ConnectedDrives.push_back(d);
        }
        // We were passed a folder path
        if (PathCount > 1)
        {
            try
            {
                // Get the file
                File *f = d->FileFromPath(Paths.at(i));
                // Write the files to folder path + file name
                d->CopyFileToLocalDisk(f, (OutPath + "/") + Split.at(Split.size() - 1));
            }
            // Exception was thrown, we're dealing with a folder path
            catch(...)
            {
                // Add folder handling
            }
        }
        else
        {
            // See if this is a file...
            try
            {
                std::string path(Paths.at(i));
                File *f = d->FileFromPath(path);
                // Succeeded, write the file to the destined folder
                d->CopyFileToLocalDisk(f, OutPath);
            }
            catch (...)
            {
                // Add folder handling here
            }
        }
    }
}

void ProgressDialog::OnFileProgressChanged(const Progress& p)
{
    // If the maximums don't match
    if (ui->progressCurrent->maximum() != p.Maximum)
        // Set the progress bar maximum value
        ui->progressCurrent->setMaximum(p.Maximum);

    // Set the progress bar's current value
    ui->progressCurrent->setValue(p.Current);

    // If we're dealing with one file and the path count is one
    if (FilesTotal == 1 && PathCount == 1)
    {
        // Set the total progress bar's values to that of the current progress bar
        if (ui->progressTotal->maximum() != p.Maximum)
            ui->progressTotal->setMaximum(p.Maximum);
        ui->progressTotal->setValue(p.Current);
    }
    // If we're dealing with multiple files
    else
    {
        // Set the value of the total progressbar to the number of files we're doing
        ui->progressTotal->setMaximum(FilesTotal);
        ui->progressTotal->setValue(FilesCompleted + 1);
    }

    // Set the title of the total groupbox to reflect the current file out of x
    ui->groupBoxTotal->setTitle(QString("Total - File %1 out of %2").arg(FilesCompleted + 1).arg(FilesTotal));

    string Title = Helpers::QStringToStdString(ui->groupBoxCurrent->title());
    // If we've just moved on to a new file
    if (Title.substr(Title.find_first_of('/') + 1) != p.FilePath)
    {
        ui->groupBoxCurrent->setTitle(QString::fromWCharArray((p.Device->FriendlyName + L"/").c_str()) + Helpers::QStringFromStdString(p.FilePath));

        // If the file is an STFS package
        if (p.IsStfsPackage)
        {
            // Delete the current scene for the graphics view
            if (scene)
            {
                delete scene;
            }
            // Get the package image
            QImage image(p.PackageImage);

            // Create a new scene with the dimensions of the image
            scene = new QGraphicsScene(ui->graphicsView);
            scene->setSceneRect(0,0,image.width(), image.height());

            // Set the graphics view size
            ui->graphicsView->setFixedSize(64, 64);

            // Set the image that the scene will display
            scene->addPixmap(QPixmap::fromImage(image));

            // Set the scene and display the image
            ui->graphicsView->setScene(scene);
            ui->graphicsView->show();

            // Makes the graphics view scale the image down so there's no scrolling
            ui->graphicsView->fitInView(ui->graphicsView->rect(), Qt::KeepAspectRatio);

            // Get the package name (real)
            ui->labelFileName->setText(p.PackageName);
        }
        // Not an STFS package -- use the default file image
        else
        {
            // Delete the current scene if there is one
            if (scene)
                delete scene;

            ui->labelFileName->setText(Helpers::QStringFromStdString(p.FileName));

            // Get the file icon image
            QImage image(":/File System Icons/iFile");

            // Get the file icon pixmap
            QPixmap pixmap = QPixmap::fromImage(image);

            // Create a new scene
            scene = new QGraphicsScene(ui->graphicsView);
            scene->setSceneRect(0,0,64, 64);

            // Set the graphics view's fixed size
            ui->graphicsView->setFixedSize(64, 64);

            // Set the scene image
            scene->addPixmap(pixmap.scaled(QSize(64, 64), Qt::KeepAspectRatio));

            // Set the graphics view's scene, display the image
            ui->graphicsView->setScene(scene);
            ui->graphicsView->show();

            // Makes the image fit within the bounds of the graphics view without scrolling
            ui->graphicsView->fitInView(ui->graphicsView->rect(), Qt::KeepAspectRatio);
        }
    }
    if (p.Done)
        FilesCompleted++;
    if (FilesCompleted == FilesTotal)
        this->close();
}
<|endoftext|>


<commit_before>/****************************************************************************
**
** 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  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://qt.nokia.com/contact.
** $QT_END_LICENSE$
**
****************************************************************************/

// When using WinSock2 on Windows, it's the first thing that can be included
// (except qglobal.h), or else you'll get tons of compile errors
#include <qglobal.h>

// MinGW doesn't provide getaddrinfo(), so we test for Q_OS_WIN
// and Q_CC_GNU, which indirectly tells us whether we're using MinGW.
#if defined(Q_OS_WIN) && defined(Q_CC_GNU)
# define QT_NO_GETADDRINFO
#endif

#if defined(Q_OS_WIN) && !defined(QT_NO_GETADDRINFO)
# include <winsock2.h>
# include <ws2tcpip.h>
#endif

#include <QtTest/QtTest>
#include <qcoreapplication.h>
#include <QDebug>
#include <QTcpSocket>
#include <private/qthread_p.h>
#include <QTcpServer>

#include <time.h>
#include <qlibrary.h>
#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
#include <windows.h>
#else
#include <unistd.h>
#include <signal.h>
#endif

#include <qhostinfo.h>

#if !defined(QT_NO_GETADDRINFO)
# if !defined(Q_OS_WINCE)
#  include <sys/types.h>
# else
#  include <types.h>
# endif
# if defined(Q_OS_UNIX)
#  include <sys/socket.h>
# endif
# if !defined(Q_OS_WIN)
#  include <netdb.h>
# endif
#endif

#include "../network-settings.h"

//TESTED_CLASS=
//TESTED_FILES=

const char * const lupinellaIp = "10.3.4.6";

class tst_QHostInfo : public QObject
{
    Q_OBJECT

public:
    tst_QHostInfo();
    virtual ~tst_QHostInfo();

public slots:
    void init();
    void cleanup();
private slots:
    void getSetCheck();
    void staticInformation();
    void initTestCase();
    void lookupIPv4_data();
    void lookupIPv4();
    void lookupIPv6_data();
    void lookupIPv6();
    void reverseLookup_data();
    void reverseLookup();

    void blockingLookup_data();
    void blockingLookup();

    void raceCondition();
    void threadSafety();

protected slots:
    void resultsReady(const QHostInfo &);

private:
    bool ipv6LookupsAvailable;
    bool ipv6Available;
    bool lookupDone;
    QHostInfo lookupResults;
};

// Testing get/set functions
void tst_QHostInfo::getSetCheck()
{
    QHostInfo obj1;
    // HostInfoError QHostInfo::error()
    // void QHostInfo::setError(HostInfoError)
    obj1.setError(QHostInfo::HostInfoError(0));
    QCOMPARE(QHostInfo::HostInfoError(0), obj1.error());
    obj1.setError(QHostInfo::HostInfoError(1));
    QCOMPARE(QHostInfo::HostInfoError(1), obj1.error());

    // int QHostInfo::lookupId()
    // void QHostInfo::setLookupId(int)
    obj1.setLookupId(0);
    QCOMPARE(0, obj1.lookupId());
    obj1.setLookupId(INT_MIN);
    QCOMPARE(INT_MIN, obj1.lookupId());
    obj1.setLookupId(INT_MAX);
    QCOMPARE(INT_MAX, obj1.lookupId());
}

void tst_QHostInfo::staticInformation()
{
    qDebug() << "Hostname:" << QHostInfo::localHostName();
    qDebug() << "Domain name:" << QHostInfo::localDomainName();
}

tst_QHostInfo::tst_QHostInfo()
{
    Q_SET_DEFAULT_IAP
}

tst_QHostInfo::~tst_QHostInfo()
{
}

void tst_QHostInfo::initTestCase()
{
    ipv6Available = false;
    ipv6LookupsAvailable = false;
#if !defined(QT_NO_GETADDRINFO)
    // check if the system getaddrinfo can do IPv6 lookups
    struct addrinfo hint, *result = 0;
    memset(&hint, 0, sizeof hint);
    hint.ai_family = AF_UNSPEC;
# ifdef AI_ADDRCONFIG
    hint.ai_flags = AI_ADDRCONFIG;
# endif

    int res = getaddrinfo("::1", "80", &hint, &result);
    if (res == 0) {
        // this test worked
        freeaddrinfo(result);
        res = getaddrinfo("ipv6-test.dev.troll.no", "80", &hint, &result);
        if (res == 0 && result != 0 && result->ai_family != AF_INET) {
            freeaddrinfo(result);
            ipv6LookupsAvailable = true;
        }
    }
#endif

    QTcpServer server;
    if (server.listen(QHostAddress("::1"))) {
        // We have IPv6 support
        ipv6Available = true;
    }
}

void tst_QHostInfo::init()
{
}

void tst_QHostInfo::cleanup()
{
}

void tst_QHostInfo::lookupIPv4_data()
{
    QTest::addColumn<QString>("hostname");
    QTest::addColumn<QString>("addresses");
    QTest::addColumn<int>("err");

#ifdef Q_OS_SYMBIAN
    // Test server lookup
    QTest::newRow("lookup_01") << QtNetworkSettings::serverName() << QtNetworkSettings::serverIP().toString() << int(QHostInfo::NoError);
    QTest::newRow("literal_ip4") << QtNetworkSettings::serverIP().toString() << QtNetworkSettings::serverIP().toString() << int(QHostInfo::NoError);
    QTest::newRow("multiple_ip4") << "multi.dev.troll.no" << "1.2.3.4 1.2.3.5 10.3.3.31" << int(QHostInfo::NoError);
#else
    QTest::newRow("empty") << "" << "" << int(QHostInfo::HostNotFound);

    QTest::newRow("single_ip4") << "lupinella.troll.no" << lupinellaIp << int(QHostInfo::NoError);
    QTest::newRow("multiple_ip4") << "multi.dev.troll.no" << "1.2.3.4 1.2.3.5 10.3.3.31" << int(QHostInfo::NoError);
    QTest::newRow("literal_ip4") << lupinellaIp << lupinellaIp << int(QHostInfo::NoError);
#endif
    QTest::newRow("notfound") << "this-name-does-not-exist-hopefully." << "" << int(QHostInfo::HostNotFound);

    QTest::newRow("idn-ace") << "xn--alqualond-34a.troll.no" << "10.3.3.55" << int(QHostInfo::NoError);
    QTest::newRow("idn-unicode") << QString::fromLatin1("alqualond\353.troll.no") << "10.3.3.55" << int(QHostInfo::NoError);
}

void tst_QHostInfo::lookupIPv4()
{
    QFETCH(QString, hostname);
    QFETCH(int, err);
    QFETCH(QString, addresses);

    lookupDone = false;
    QHostInfo::lookupHost(hostname, this, SLOT(resultsReady(const QHostInfo&)));

    QTestEventLoop::instance().enterLoop(3);
    QVERIFY(!QTestEventLoop::instance().timeout());
    QVERIFY(lookupDone);

    QCOMPARE((int)lookupResults.error(), (int)err);

    QStringList tmp;
    for (int i = 0; i < lookupResults.addresses().count(); ++i)
        tmp.append(lookupResults.addresses().at(i).toString());
    tmp.sort();

    QStringList expected = addresses.split(' ');
    expected.sort();

    QCOMPARE(tmp.join(" "), expected.join(" "));
}

void tst_QHostInfo::lookupIPv6_data()
{
    QTest::addColumn<QString>("hostname");
    QTest::addColumn<QString>("addresses");
    QTest::addColumn<int>("err");

    QTest::newRow("ip6") << "www.ipv6-net.org" << "62.93.217.177 2001:618:1401:0:0:0:0:4" << int(QHostInfo::NoError);

    // avoid using real IPv6 addresses here because this will do a DNS query
    // real addresses are between 2000:: and 3fff:ffff:ffff:ffff:ffff:ffff:ffff
    QTest::newRow("literal_ip6") << "f001:6b0:1:ea:202:a5ff:fecd:13a6" << "f001:6b0:1:ea:202:a5ff:fecd:13a6" << int(QHostInfo::NoError);
    QTest::newRow("literal_shortip6") << "f001:618:1401::4" << "f001:618:1401:0:0:0:0:4" << int(QHostInfo::NoError);
}

void tst_QHostInfo::lookupIPv6()
{
    QFETCH(QString, hostname);
    QFETCH(int, err);
    QFETCH(QString, addresses);

    if (!ipv6LookupsAvailable)
        QSKIP("This platform does not support IPv6 lookups", SkipAll);

    lookupDone = false;
    QHostInfo::lookupHost(hostname, this, SLOT(resultsReady(const QHostInfo&)));

    QTestEventLoop::instance().enterLoop(3);
    QVERIFY(!QTestEventLoop::instance().timeout());
    QVERIFY(lookupDone);

    QCOMPARE((int)lookupResults.error(), (int)err);

    QStringList tmp;
    for (int i = 0; i < lookupResults.addresses().count(); ++i)
        tmp.append(lookupResults.addresses().at(i).toString());
    tmp.sort();

    QStringList expected = addresses.split(' ');
    expected.sort();

    QCOMPARE(tmp.join(" ").toLower(), expected.join(" ").toLower());
}

void tst_QHostInfo::reverseLookup_data()
{
    QTest::addColumn<QString>("address");
    QTest::addColumn<QStringList>("hostNames");
    QTest::addColumn<int>("err");

    QTest::newRow("trolltech.com") << QString("62.70.27.69") << QStringList(QString("diverse.troll.no")) << 0;

    // ### Use internal DNS instead. Discussed with Andreas.
    //QTest::newRow("classical.hexago.com") << QString("2001:5c0:0:2::24") << QStringList(QString("classical.hexago.com")) << 0;
    QTest::newRow("origin.cisco.com") << QString("12.159.148.94") << QStringList(QString("origin.cisco.com")) << 0;
    QTest::newRow("bogusexample.doenstexist.org") << QString("1::2::3::4") << QStringList() << 1;
}

void tst_QHostInfo::reverseLookup()
{
    QFETCH(QString, address);
    QFETCH(QStringList, hostNames);
    QFETCH(int, err);

    if (!ipv6LookupsAvailable && hostNames.contains("classical.hexago.com")) {
        QSKIP("IPv6 lookups are not supported on this platform", SkipSingle);
    }
#if defined(Q_OS_HPUX) && defined(__ia64)
    if (hostNames.contains("classical.hexago.com"))
        QSKIP("HP-UX 11i does not support IPv6 reverse lookups.", SkipSingle);
#endif

    QHostInfo info = QHostInfo::fromName(address);

    if (err == 0) {
        QVERIFY(hostNames.contains(info.hostName()));
        QCOMPARE(info.addresses().first(), QHostAddress(address));
    } else {
        QCOMPARE(info.hostName(), address);
        QCOMPARE(info.error(), QHostInfo::HostNotFound);
    }

}

void tst_QHostInfo::blockingLookup_data()
{
    lookupIPv4_data();
    if (ipv6LookupsAvailable)
        lookupIPv6_data();
}

void tst_QHostInfo::blockingLookup()
{
    QFETCH(QString, hostname);
    QFETCH(int, err);
    QFETCH(QString, addresses);

    QHostInfo hostInfo = QHostInfo::fromName(hostname);
    QStringList tmp;
    for (int i = 0; i < hostInfo.addresses().count(); ++i)
        tmp.append(hostInfo.addresses().at(i).toString());
    tmp.sort();

    QCOMPARE((int)hostInfo.error(), (int)err);

    QStringList expected = addresses.split(' ');
    expected.sort();

    QCOMPARE(tmp.join(" ").toUpper(), expected.join(" ").toUpper());
}

void tst_QHostInfo::raceCondition()
{
    for (int i = 0; i < 1000; ++i) {
        QTcpSocket socket;
        socket.connectToHost("notavalidname.troll.no", 80);
    }
}

class LookupThread : public QThread
{
protected:
    inline void run()
    {
         QHostInfo info = QHostInfo::fromName("qt.nokia.com");
         QCOMPARE(info.addresses().at(0).toString(), QString("87.238.50.178"));
    }
};

void tst_QHostInfo::threadSafety()
{
    const int nattempts = 5;
#if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN)
    const int runs = 10;
#else
    const int runs = 100;
#endif
    LookupThread thr[nattempts];
    for (int j = 0; j < runs; ++j) {
        for (int i = 0; i < nattempts; ++i)
            thr[i].start();
        for (int k = nattempts - 1; k >= 0; --k)
            thr[k].wait();
    }
}

void tst_QHostInfo::resultsReady(const QHostInfo &hi)
{
    lookupDone = true;
    lookupResults = hi;

    QTestEventLoop::instance().exitLoop();
}

QTEST_MAIN(tst_QHostInfo)
#include "tst_qhostinfo.moc"
<commit_msg>We can use runs = 100 in the test case. On Symbian platform it will take longer time.<commit_after>/****************************************************************************
**
** 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  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://qt.nokia.com/contact.
** $QT_END_LICENSE$
**
****************************************************************************/

// When using WinSock2 on Windows, it's the first thing that can be included
// (except qglobal.h), or else you'll get tons of compile errors
#include <qglobal.h>

// MinGW doesn't provide getaddrinfo(), so we test for Q_OS_WIN
// and Q_CC_GNU, which indirectly tells us whether we're using MinGW.
#if defined(Q_OS_WIN) && defined(Q_CC_GNU)
# define QT_NO_GETADDRINFO
#endif

#if defined(Q_OS_WIN) && !defined(QT_NO_GETADDRINFO)
# include <winsock2.h>
# include <ws2tcpip.h>
#endif

#include <QtTest/QtTest>
#include <qcoreapplication.h>
#include <QDebug>
#include <QTcpSocket>
#include <private/qthread_p.h>
#include <QTcpServer>

#include <time.h>
#include <qlibrary.h>
#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
#include <windows.h>
#else
#include <unistd.h>
#include <signal.h>
#endif

#include <qhostinfo.h>

#if !defined(QT_NO_GETADDRINFO)
# if !defined(Q_OS_WINCE)
#  include <sys/types.h>
# else
#  include <types.h>
# endif
# if defined(Q_OS_UNIX)
#  include <sys/socket.h>
# endif
# if !defined(Q_OS_WIN)
#  include <netdb.h>
# endif
#endif

#include "../network-settings.h"

//TESTED_CLASS=
//TESTED_FILES=

const char * const lupinellaIp = "10.3.4.6";

class tst_QHostInfo : public QObject
{
    Q_OBJECT

public:
    tst_QHostInfo();
    virtual ~tst_QHostInfo();

public slots:
    void init();
    void cleanup();
private slots:
    void getSetCheck();
    void staticInformation();
    void initTestCase();
    void lookupIPv4_data();
    void lookupIPv4();
    void lookupIPv6_data();
    void lookupIPv6();
    void reverseLookup_data();
    void reverseLookup();

    void blockingLookup_data();
    void blockingLookup();

    void raceCondition();
    void threadSafety();

protected slots:
    void resultsReady(const QHostInfo &);

private:
    bool ipv6LookupsAvailable;
    bool ipv6Available;
    bool lookupDone;
    QHostInfo lookupResults;
};

// Testing get/set functions
void tst_QHostInfo::getSetCheck()
{
    QHostInfo obj1;
    // HostInfoError QHostInfo::error()
    // void QHostInfo::setError(HostInfoError)
    obj1.setError(QHostInfo::HostInfoError(0));
    QCOMPARE(QHostInfo::HostInfoError(0), obj1.error());
    obj1.setError(QHostInfo::HostInfoError(1));
    QCOMPARE(QHostInfo::HostInfoError(1), obj1.error());

    // int QHostInfo::lookupId()
    // void QHostInfo::setLookupId(int)
    obj1.setLookupId(0);
    QCOMPARE(0, obj1.lookupId());
    obj1.setLookupId(INT_MIN);
    QCOMPARE(INT_MIN, obj1.lookupId());
    obj1.setLookupId(INT_MAX);
    QCOMPARE(INT_MAX, obj1.lookupId());
}

void tst_QHostInfo::staticInformation()
{
    qDebug() << "Hostname:" << QHostInfo::localHostName();
    qDebug() << "Domain name:" << QHostInfo::localDomainName();
}

tst_QHostInfo::tst_QHostInfo()
{
    Q_SET_DEFAULT_IAP
}

tst_QHostInfo::~tst_QHostInfo()
{
}

void tst_QHostInfo::initTestCase()
{
    ipv6Available = false;
    ipv6LookupsAvailable = false;
#if !defined(QT_NO_GETADDRINFO)
    // check if the system getaddrinfo can do IPv6 lookups
    struct addrinfo hint, *result = 0;
    memset(&hint, 0, sizeof hint);
    hint.ai_family = AF_UNSPEC;
# ifdef AI_ADDRCONFIG
    hint.ai_flags = AI_ADDRCONFIG;
# endif

    int res = getaddrinfo("::1", "80", &hint, &result);
    if (res == 0) {
        // this test worked
        freeaddrinfo(result);
        res = getaddrinfo("ipv6-test.dev.troll.no", "80", &hint, &result);
        if (res == 0 && result != 0 && result->ai_family != AF_INET) {
            freeaddrinfo(result);
            ipv6LookupsAvailable = true;
        }
    }
#endif

    QTcpServer server;
    if (server.listen(QHostAddress("::1"))) {
        // We have IPv6 support
        ipv6Available = true;
    }
}

void tst_QHostInfo::init()
{
}

void tst_QHostInfo::cleanup()
{
}

void tst_QHostInfo::lookupIPv4_data()
{
    QTest::addColumn<QString>("hostname");
    QTest::addColumn<QString>("addresses");
    QTest::addColumn<int>("err");

#ifdef Q_OS_SYMBIAN
    // Test server lookup
    QTest::newRow("lookup_01") << QtNetworkSettings::serverName() << QtNetworkSettings::serverIP().toString() << int(QHostInfo::NoError);
    QTest::newRow("literal_ip4") << QtNetworkSettings::serverIP().toString() << QtNetworkSettings::serverIP().toString() << int(QHostInfo::NoError);
    QTest::newRow("multiple_ip4") << "multi.dev.troll.no" << "1.2.3.4 1.2.3.5 10.3.3.31" << int(QHostInfo::NoError);
#else
    QTest::newRow("empty") << "" << "" << int(QHostInfo::HostNotFound);

    QTest::newRow("single_ip4") << "lupinella.troll.no" << lupinellaIp << int(QHostInfo::NoError);
    QTest::newRow("multiple_ip4") << "multi.dev.troll.no" << "1.2.3.4 1.2.3.5 10.3.3.31" << int(QHostInfo::NoError);
    QTest::newRow("literal_ip4") << lupinellaIp << lupinellaIp << int(QHostInfo::NoError);
#endif
    QTest::newRow("notfound") << "this-name-does-not-exist-hopefully." << "" << int(QHostInfo::HostNotFound);

    QTest::newRow("idn-ace") << "xn--alqualond-34a.troll.no" << "10.3.3.55" << int(QHostInfo::NoError);
    QTest::newRow("idn-unicode") << QString::fromLatin1("alqualond\353.troll.no") << "10.3.3.55" << int(QHostInfo::NoError);
}

void tst_QHostInfo::lookupIPv4()
{
    QFETCH(QString, hostname);
    QFETCH(int, err);
    QFETCH(QString, addresses);

    lookupDone = false;
    QHostInfo::lookupHost(hostname, this, SLOT(resultsReady(const QHostInfo&)));

    QTestEventLoop::instance().enterLoop(3);
    QVERIFY(!QTestEventLoop::instance().timeout());
    QVERIFY(lookupDone);

    QCOMPARE((int)lookupResults.error(), (int)err);

    QStringList tmp;
    for (int i = 0; i < lookupResults.addresses().count(); ++i)
        tmp.append(lookupResults.addresses().at(i).toString());
    tmp.sort();

    QStringList expected = addresses.split(' ');
    expected.sort();

    QCOMPARE(tmp.join(" "), expected.join(" "));
}

void tst_QHostInfo::lookupIPv6_data()
{
    QTest::addColumn<QString>("hostname");
    QTest::addColumn<QString>("addresses");
    QTest::addColumn<int>("err");

    QTest::newRow("ip6") << "www.ipv6-net.org" << "62.93.217.177 2001:618:1401:0:0:0:0:4" << int(QHostInfo::NoError);

    // avoid using real IPv6 addresses here because this will do a DNS query
    // real addresses are between 2000:: and 3fff:ffff:ffff:ffff:ffff:ffff:ffff
    QTest::newRow("literal_ip6") << "f001:6b0:1:ea:202:a5ff:fecd:13a6" << "f001:6b0:1:ea:202:a5ff:fecd:13a6" << int(QHostInfo::NoError);
    QTest::newRow("literal_shortip6") << "f001:618:1401::4" << "f001:618:1401:0:0:0:0:4" << int(QHostInfo::NoError);
}

void tst_QHostInfo::lookupIPv6()
{
    QFETCH(QString, hostname);
    QFETCH(int, err);
    QFETCH(QString, addresses);

    if (!ipv6LookupsAvailable)
        QSKIP("This platform does not support IPv6 lookups", SkipAll);

    lookupDone = false;
    QHostInfo::lookupHost(hostname, this, SLOT(resultsReady(const QHostInfo&)));

    QTestEventLoop::instance().enterLoop(3);
    QVERIFY(!QTestEventLoop::instance().timeout());
    QVERIFY(lookupDone);

    QCOMPARE((int)lookupResults.error(), (int)err);

    QStringList tmp;
    for (int i = 0; i < lookupResults.addresses().count(); ++i)
        tmp.append(lookupResults.addresses().at(i).toString());
    tmp.sort();

    QStringList expected = addresses.split(' ');
    expected.sort();

    QCOMPARE(tmp.join(" ").toLower(), expected.join(" ").toLower());
}

void tst_QHostInfo::reverseLookup_data()
{
    QTest::addColumn<QString>("address");
    QTest::addColumn<QStringList>("hostNames");
    QTest::addColumn<int>("err");

    QTest::newRow("trolltech.com") << QString("62.70.27.69") << QStringList(QString("diverse.troll.no")) << 0;

    // ### Use internal DNS instead. Discussed with Andreas.
    //QTest::newRow("classical.hexago.com") << QString("2001:5c0:0:2::24") << QStringList(QString("classical.hexago.com")) << 0;
    QTest::newRow("origin.cisco.com") << QString("12.159.148.94") << QStringList(QString("origin.cisco.com")) << 0;
    QTest::newRow("bogusexample.doenstexist.org") << QString("1::2::3::4") << QStringList() << 1;
}

void tst_QHostInfo::reverseLookup()
{
    QFETCH(QString, address);
    QFETCH(QStringList, hostNames);
    QFETCH(int, err);

    if (!ipv6LookupsAvailable && hostNames.contains("classical.hexago.com")) {
        QSKIP("IPv6 lookups are not supported on this platform", SkipSingle);
    }
#if defined(Q_OS_HPUX) && defined(__ia64)
    if (hostNames.contains("classical.hexago.com"))
        QSKIP("HP-UX 11i does not support IPv6 reverse lookups.", SkipSingle);
#endif

    QHostInfo info = QHostInfo::fromName(address);

    if (err == 0) {
        QVERIFY(hostNames.contains(info.hostName()));
        QCOMPARE(info.addresses().first(), QHostAddress(address));
    } else {
        QCOMPARE(info.hostName(), address);
        QCOMPARE(info.error(), QHostInfo::HostNotFound);
    }

}

void tst_QHostInfo::blockingLookup_data()
{
    lookupIPv4_data();
    if (ipv6LookupsAvailable)
        lookupIPv6_data();
}

void tst_QHostInfo::blockingLookup()
{
    QFETCH(QString, hostname);
    QFETCH(int, err);
    QFETCH(QString, addresses);

    QHostInfo hostInfo = QHostInfo::fromName(hostname);
    QStringList tmp;
    for (int i = 0; i < hostInfo.addresses().count(); ++i)
        tmp.append(hostInfo.addresses().at(i).toString());
    tmp.sort();

    QCOMPARE((int)hostInfo.error(), (int)err);

    QStringList expected = addresses.split(' ');
    expected.sort();

    QCOMPARE(tmp.join(" ").toUpper(), expected.join(" ").toUpper());
}

void tst_QHostInfo::raceCondition()
{
    for (int i = 0; i < 1000; ++i) {
        QTcpSocket socket;
        socket.connectToHost("notavalidname.troll.no", 80);
    }
}

class LookupThread : public QThread
{
protected:
    inline void run()
    {
         QHostInfo info = QHostInfo::fromName("qt.nokia.com");
         QCOMPARE(info.addresses().at(0).toString(), QString("87.238.50.178"));
    }
};

void tst_QHostInfo::threadSafety()
{
    const int nattempts = 5;
#if defined(Q_OS_WINCE)
    const int runs = 10;
#else
    const int runs = 100;
#endif
    LookupThread thr[nattempts];
    for (int j = 0; j < runs; ++j) {
        for (int i = 0; i < nattempts; ++i)
            thr[i].start();
        for (int k = nattempts - 1; k >= 0; --k)
            thr[k].wait();
    }
}

void tst_QHostInfo::resultsReady(const QHostInfo &hi)
{
    lookupDone = true;
    lookupResults = hi;

    QTestEventLoop::instance().exitLoop();
}

QTEST_MAIN(tst_QHostInfo)
#include "tst_qhostinfo.moc"
<|endoftext|>


<commit_before>// Ignore unused parameter warnings coming from cppunit headers
#include <libmesh/ignore_warnings.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestCase.h>
#include <libmesh/restore_warnings.h>

#include <libmesh/equation_systems.h>
#include <libmesh/replicated_mesh.h>
#include <libmesh/numeric_vector.h>
#include <libmesh/dof_map.h>
#include <libmesh/elem.h>
#include <libmesh/face_quad4.h>
#include <libmesh/ghosting_functor.h>
#include <libmesh/coupling_matrix.h>
#include <libmesh/quadrature_gauss.h>
#include <libmesh/linear_implicit_system.h>
#include <libmesh/mesh_refinement.h>
#include <libmesh/mesh_modification.h>

#include "test_comm.h"

// THE CPPUNIT_TEST_SUITE_END macro expands to code that involves
// std::auto_ptr, which in turn produces -Wdeprecated-declarations
// warnings.  These can be ignored in GCC as long as we wrap the
// offending code in appropriate pragmas.  We can't get away with a
// single ignore_warnings.h inclusion at the beginning of this file,
// since the libmesh headers pull in a restore_warnings.h at some
// point.  We also don't bother restoring warnings at the end of this
// file since it's not a header.
#include <libmesh/ignore_warnings.h>

using namespace libMesh;

// This coupling functor is attempting to mimic in a basic way
// a particular use case. The idea is that the mesh has overlapping domains
// (we assume only two in this test) and that the elements are not
// topologically connected between the overlapping domains, but when the user
// solves their problem, some dofs on are coupled between the overlapping
// elements. So we want to exercise the GhostingFunctor for this scenario,
// in particular both algebraic ghosting (dof ghosting) and coupling
// ghosting (sparsity pattern). In the use case, the coupling happens at
// quadrature points, so we look for the overlap based on the quadrature points.
//
// Additionally, the use case also has an issue that the coupling functor
// cannot be added until after the System is initialized because the overlap
// depends on the solution. So we will exercise that case as well.
class OverlappingCouplingFunctor : public GhostingFunctor
{
  public:

  OverlappingCouplingFunctor( System & system )
    : GhostingFunctor(),
      _system(system),
      _mesh(system.get_mesh()),
      _point_locator(system.get_mesh().sub_point_locator()),
      _coupling_matrix(nullptr)
  {
    // This is a highly specalized coupling functor where we are assuming
    // only two subdomains that are overlapping so make sure there's only two.
    CPPUNIT_ASSERT_EQUAL(2, (int)_mesh.n_subdomains());

    // We're assuming a specific set of subdomain ids
    std::set<subdomain_id_type> ids;
    _mesh.subdomain_ids(ids);
    CPPUNIT_ASSERT( ids.find(1) != ids.end() );
    CPPUNIT_ASSERT( ids.find(2) != ids.end() );

    _point_locator->enable_out_of_mesh_mode();
  }

  virtual ~OverlappingCouplingFunctor(){};

  void set_coupling_matrix (std::unique_ptr<CouplingMatrix> & coupling_matrix)
  { _coupling_matrix = std::move(coupling_matrix); }

  virtual void operator()
  (const MeshBase::const_element_iterator & range_begin,
   const MeshBase::const_element_iterator & range_end,
   processor_id_type p,
   std::unordered_map<const Elem *,const CouplingMatrix*> & coupled_elements) override
  {

    for( const auto & elem : as_range(range_begin,range_end) )
      {
        std::set<subdomain_id_type> allowed_subdomains;
        unsigned int var = 0;
        if( elem->subdomain_id() == 1 )
          {
            var = _system.variable_number("V");
            allowed_subdomains.insert(2);
          }
        else if( elem->subdomain_id() == 2 )
          {
            var = _system.variable_number("U");
            allowed_subdomains.insert(1);
          }
        else
          CPPUNIT_FAIL("Something bad happpend");

        unsigned int dim = 2;
        FEType fe_type = _system.variable_type(var);
        std::unique_ptr<FEBase> fe (FEBase::build(dim, fe_type));
        QGauss qrule (dim, fe_type.default_quadrature_order());
        fe->attach_quadrature_rule (&qrule);

        const std::vector<libMesh::Point> & qpoints = fe->get_xyz();

        fe->reinit(elem);

        for ( const auto & qp : qpoints )
          {
            const Elem * overlapping_elem = (*_point_locator)( qp, &allowed_subdomains );

            if( overlapping_elem && overlapping_elem->processor_id() != p )
              coupled_elements.insert( std::make_pair(overlapping_elem,_coupling_matrix.get()) );
          }
      }
  }

private:

  System & _system;

  const MeshBase & _mesh;

  std::unique_ptr<PointLocatorBase> _point_locator;

  std::unique_ptr<CouplingMatrix> _coupling_matrix;
};

// Common functionality for all the different tests we'll be running
// We're creating two elements that will live in subdomain 1 and one
// element that is lives in subdomain 2 that overlaps both elements in
// subdomain 1, but is not topologically connected.
class OverlappingTestBase
{
protected:

  std::unique_ptr<MeshBase> _mesh;

  std::unique_ptr<EquationSystems> _es;

  void build_quad_mesh(unsigned int n_refinements = 0)
  {
    // We are making assumptions in various places about the presence
    // of the elements on the current processor so we're restricting to
    // ReplicatedMesh for now.
    _mesh.reset(new ReplicatedMesh(*TestCommWorld));

    _mesh->set_mesh_dimension(2);

    _mesh->add_point( Point(0.0,0.0),0 );
    _mesh->add_point( Point(1.0,0.0),1 );
    _mesh->add_point( Point(1.0,1.0),2 );
    _mesh->add_point( Point(0.0,1.0),3 );

    {
      Elem* elem = _mesh->add_elem( new Quad4 );
      elem->set_id(0);
      elem->subdomain_id() = 1;

      for (unsigned int n=0; n<4; n++)
        elem->set_node(n) = _mesh->node_ptr(n);
    }

    _mesh->add_point( Point(1.0,2.0),4 );
    _mesh->add_point( Point(0.0,2.0),5 );

    {
      Elem* elem = _mesh->add_elem( new Quad4 );
      elem->set_id(1);
      elem->subdomain_id() = 1;

      elem->set_node(0) = _mesh->node_ptr(3);
      elem->set_node(1) = _mesh->node_ptr(2);
      elem->set_node(2) = _mesh->node_ptr(4);
      elem->set_node(3) = _mesh->node_ptr(5);
    }

    _mesh->add_point( Point(0.0,0.0),6 );
    _mesh->add_point( Point(1.0,0.0),7 );
    _mesh->add_point( Point(1.0,2.0),8 );
    _mesh->add_point( Point(0.0,2.0),9 );

    {
      Elem* elem = _mesh->add_elem( new Quad4 );
      elem->set_id(2);
      elem->subdomain_id() = 2;

      elem->set_node(0) = _mesh->node_ptr(6);
      elem->set_node(1) = _mesh->node_ptr(7);
      elem->set_node(2) = _mesh->node_ptr(8);
      elem->set_node(3) = _mesh->node_ptr(9);
    }

    _mesh->prepare_for_use();

    if (n_refinements > 0)
      {
        MeshRefinement refine(*_mesh);
        refine.uniformly_refine(n_refinements);
      }
  }

  void init(MeshBase & mesh)
  {
    _es.reset( new EquationSystems(*_mesh) );
    LinearImplicitSystem & sys = _es->add_system<LinearImplicitSystem> ("SimpleSystem");

    std::set<subdomain_id_type> sub_one;
    sub_one.insert(1);

    std::set<subdomain_id_type> sub_two;
    sub_two.insert(2);

    sys.add_variable("U", FIRST, LAGRANGE, &sub_two);
    sys.add_variable("L", FIRST, LAGRANGE, &sub_two);

    sys.add_variable("V", FIRST, LAGRANGE, &sub_one);
    sys.add_variable("p", FIRST, LAGRANGE, &sub_one);

    _es->init();
  }

  void clear()
  {
    _es.reset();
    _mesh.reset();
  }

  void setup_coupling_matrix( std::unique_ptr<CouplingMatrix> & coupling )
  {
    System & system = _es->get_system("SimpleSystem");

    coupling.reset( new CouplingMatrix(system.n_vars()) );

    const unsigned int u_var = system.variable_number("U");
    const unsigned int l_var = system.variable_number("L");
    const unsigned int v_var = system.variable_number("V");
    const unsigned int p_var = system.variable_number("p");

    // Only adding the overlapping couplings since the primary ones should
    // be there by default.
    (*coupling)(u_var,v_var) = true;
    (*coupling)(l_var,v_var) = true;
    (*coupling)(l_var,p_var) = true;
    (*coupling)(v_var,u_var) = true;
    (*coupling)(v_var,l_var) = true;
  }

};

// Mainly just to sanity check the mesh construction and
// the assumptions made in the OverlappingCouplingFunctor
class OverlappingFunctorTest : public CppUnit::TestCase,
                               public OverlappingTestBase
{
public:

  CPPUNIT_TEST_SUITE( OverlappingFunctorTest );

  CPPUNIT_TEST( checkCouplingFunctorQuad );
  CPPUNIT_TEST( checkCouplingFunctorQuadUnifRef );
  CPPUNIT_TEST( checkCouplingFunctorTri );
  CPPUNIT_TEST( checkCouplingFunctorTriUnifRef );

  CPPUNIT_TEST_SUITE_END();

public:

  public:
  void setUp()
  {
    this->build_quad_mesh();
    this->init(*_mesh);
  }

  void tearDown()
  { this->clear(); }

  void checkCouplingFunctorQuad()
  { this->run_coupling_functor_test(0); }

  void checkCouplingFunctorQuadUnifRef()
  { this->run_coupling_functor_test(1); }

  void checkCouplingFunctorTri()
  {
    MeshTools::Modification::all_tri(*_mesh);
    _es->reinit();
    this->run_coupling_functor_test(0);
  }

  void checkCouplingFunctorTriUnifRef()
  {
    MeshTools::Modification::all_tri(*_mesh);
    _es->reinit();
    this->run_coupling_functor_test(1);
  }

private:

  // This is basically to sanity check the coupling functor
  // with the supplied mesh to make sure all the assumptions
  // are kosher.
  void run_coupling_functor_test(unsigned int n_refinements)
  {
    if( n_refinements > 0 )
      {
        MeshRefinement refine(*_mesh);
        refine.uniformly_refine(n_refinements);
        _es->reinit();
      }

    System & system = _es->get_system("SimpleSystem");

    OverlappingCouplingFunctor coupling_functor(system);

    std::unordered_map<const Elem *,const CouplingMatrix*> subdomain_one_couplings;
    std::unordered_map<const Elem *,const CouplingMatrix*> subdomain_two_couplings;

    coupling_functor( _mesh->active_subdomain_elements_begin(1),
                      _mesh->active_subdomain_elements_end(1),
                      DofObject::invalid_processor_id,
                      subdomain_one_couplings );

    coupling_functor( _mesh->active_subdomain_elements_begin(2),
                      _mesh->active_subdomain_elements_end(2),
                      DofObject::invalid_processor_id,
                      subdomain_two_couplings );

    dof_id_type n_elems_subdomain_two = std::distance( _mesh->active_subdomain_elements_begin(2),
                                                       _mesh->active_subdomain_elements_end(2) );

    CPPUNIT_ASSERT_EQUAL( n_elems_subdomain_two, (dof_id_type) subdomain_one_couplings.size() );
    CPPUNIT_ASSERT_EQUAL( 2*n_elems_subdomain_two, (dof_id_type) subdomain_two_couplings.size() );
  }

};

CPPUNIT_TEST_SUITE_REGISTRATION( OverlappingFunctorTest );
<commit_msg>Add OverlappingTestPartitioner<commit_after>// Ignore unused parameter warnings coming from cppunit headers
#include <libmesh/ignore_warnings.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestCase.h>
#include <libmesh/restore_warnings.h>

#include <libmesh/equation_systems.h>
#include <libmesh/replicated_mesh.h>
#include <libmesh/numeric_vector.h>
#include <libmesh/dof_map.h>
#include <libmesh/elem.h>
#include <libmesh/face_quad4.h>
#include <libmesh/ghosting_functor.h>
#include <libmesh/coupling_matrix.h>
#include <libmesh/quadrature_gauss.h>
#include <libmesh/linear_implicit_system.h>
#include <libmesh/mesh_refinement.h>
#include <libmesh/mesh_modification.h>
#include <libmesh/partitioner.h>

#include "test_comm.h"

// THE CPPUNIT_TEST_SUITE_END macro expands to code that involves
// std::auto_ptr, which in turn produces -Wdeprecated-declarations
// warnings.  These can be ignored in GCC as long as we wrap the
// offending code in appropriate pragmas.  We can't get away with a
// single ignore_warnings.h inclusion at the beginning of this file,
// since the libmesh headers pull in a restore_warnings.h at some
// point.  We also don't bother restoring warnings at the end of this
// file since it's not a header.
#include <libmesh/ignore_warnings.h>

using namespace libMesh;

// This coupling functor is attempting to mimic in a basic way
// a particular use case. The idea is that the mesh has overlapping domains
// (we assume only two in this test) and that the elements are not
// topologically connected between the overlapping domains, but when the user
// solves their problem, some dofs on are coupled between the overlapping
// elements. So we want to exercise the GhostingFunctor for this scenario,
// in particular both algebraic ghosting (dof ghosting) and coupling
// ghosting (sparsity pattern). In the use case, the coupling happens at
// quadrature points, so we look for the overlap based on the quadrature points.
//
// Additionally, the use case also has an issue that the coupling functor
// cannot be added until after the System is initialized because the overlap
// depends on the solution. So we will exercise that case as well.
class OverlappingCouplingFunctor : public GhostingFunctor
{
  public:

  OverlappingCouplingFunctor( System & system )
    : GhostingFunctor(),
      _system(system),
      _mesh(system.get_mesh()),
      _point_locator(system.get_mesh().sub_point_locator()),
      _coupling_matrix(nullptr)
  {
    // This is a highly specalized coupling functor where we are assuming
    // only two subdomains that are overlapping so make sure there's only two.
    CPPUNIT_ASSERT_EQUAL(2, (int)_mesh.n_subdomains());

    // We're assuming a specific set of subdomain ids
    std::set<subdomain_id_type> ids;
    _mesh.subdomain_ids(ids);
    CPPUNIT_ASSERT( ids.find(1) != ids.end() );
    CPPUNIT_ASSERT( ids.find(2) != ids.end() );

    _point_locator->enable_out_of_mesh_mode();
  }

  virtual ~OverlappingCouplingFunctor(){};

  void set_coupling_matrix (std::unique_ptr<CouplingMatrix> & coupling_matrix)
  { _coupling_matrix = std::move(coupling_matrix); }

  virtual void operator()
  (const MeshBase::const_element_iterator & range_begin,
   const MeshBase::const_element_iterator & range_end,
   processor_id_type p,
   std::unordered_map<const Elem *,const CouplingMatrix*> & coupled_elements) override
  {

    for( const auto & elem : as_range(range_begin,range_end) )
      {
        std::set<subdomain_id_type> allowed_subdomains;
        unsigned int var = 0;
        if( elem->subdomain_id() == 1 )
          {
            var = _system.variable_number("V");
            allowed_subdomains.insert(2);
          }
        else if( elem->subdomain_id() == 2 )
          {
            var = _system.variable_number("U");
            allowed_subdomains.insert(1);
          }
        else
          CPPUNIT_FAIL("Something bad happpend");

        unsigned int dim = 2;
        FEType fe_type = _system.variable_type(var);
        std::unique_ptr<FEBase> fe (FEBase::build(dim, fe_type));
        QGauss qrule (dim, fe_type.default_quadrature_order());
        fe->attach_quadrature_rule (&qrule);

        const std::vector<libMesh::Point> & qpoints = fe->get_xyz();

        fe->reinit(elem);

        for ( const auto & qp : qpoints )
          {
            const Elem * overlapping_elem = (*_point_locator)( qp, &allowed_subdomains );

            if( overlapping_elem && overlapping_elem->processor_id() != p )
              coupled_elements.insert( std::make_pair(overlapping_elem,_coupling_matrix.get()) );
          }
      }
  }

private:

  System & _system;

  const MeshBase & _mesh;

  std::unique_ptr<PointLocatorBase> _point_locator;

  std::unique_ptr<CouplingMatrix> _coupling_matrix;
};

// We use a custom partioner for this test to help ensure we're
// testing the cases we want. In particular, we're going to
// ensure that elements in subdomain one are on different processors
// from elements subdomain two so we know ghosting will be required
// for algebraic and coupling ghosting between the two subdomains.
class OverlappingTestPartitioner : public Partitioner
{
public:

  OverlappingTestPartitioner () = default;
  OverlappingTestPartitioner (const OverlappingTestPartitioner &) = default;
  OverlappingTestPartitioner (OverlappingTestPartitioner &&) = default;
  OverlappingTestPartitioner & operator= (const OverlappingTestPartitioner &) = default;
  OverlappingTestPartitioner & operator= (OverlappingTestPartitioner &&) = default;
  virtual ~OverlappingTestPartitioner() = default;

  /**
   * \returns A copy of this partitioner wrapped in a smart pointer.
   */
  virtual std::unique_ptr<Partitioner> clone () const override
  {
    return libmesh_make_unique<OverlappingTestPartitioner>(*this);
  }

protected:

  /**
   * Partition the \p MeshBase into \p n subdomains.
   */
  virtual void _do_partition (MeshBase & mesh,
                              const unsigned int n) override
  {
    // If we're on one partition, then everyone gets to be on that partition
    if (n == 1)
      this->single_partition_range (mesh.active_elements_begin(), mesh.active_elements_end());
    else
      {
        libmesh_assert_greater (n, 0);

        // If there are more partitions than elements, then just
        // assign one element to each partition
        if (mesh.n_active_elem() <= n)
          {
            processor_id_type e = 0;
            for (auto & elem : mesh.active_element_ptr_range() )
              {
                elem->processor_id() = e;
                e++;
              }
          }
        // Otherwise, we'll split up the partitions into two groups
        // and then assign the elements of each subdomain into each of
        // the two groups
        else
          {
            unsigned int n_sub_two_elems = std::distance( mesh.active_subdomain_elements_begin(2),
                                                          mesh.active_subdomain_elements_end(2) );

            unsigned int n_parts_sub_two = 0;
            if( n_sub_two_elems < n/2 )
              n_parts_sub_two = n_sub_two_elems;
            else
                n_parts_sub_two = n/2;

            const unsigned int n_parts_sub_one = n - n_parts_sub_two;

            const dof_id_type sub_two_blk_size = cast_int<dof_id_type>
              (std::distance( mesh.active_subdomain_elements_begin(2),
                              mesh.active_subdomain_elements_end(2) )/n_parts_sub_two );

            this->assign_proc_id_subdomain( mesh, 2, sub_two_blk_size, n_parts_sub_two, 0 );

            const dof_id_type sub_one_blk_size = cast_int<dof_id_type>
              (std::distance( mesh.active_subdomain_elements_begin(1),
                              mesh.active_subdomain_elements_end(1) )/n_parts_sub_one );

            this->assign_proc_id_subdomain( mesh, 1, sub_one_blk_size, n_parts_sub_one, n_parts_sub_two );
          }
      }
  }

  void assign_proc_id_subdomain( const MeshBase & mesh,
                                 const subdomain_id_type sid,
                                 const dof_id_type blksize,
                                 const unsigned int n_parts,
                                 const processor_id_type offset )
  {
    dof_id_type e = 0;
    for (auto & elem : mesh.active_subdomain_elements_ptr_range(sid))
      {
        if ((e/blksize) < n_parts)
          elem->processor_id() = offset + cast_int<processor_id_type>(e/blksize);
        else
          elem->processor_id() = offset;

        e++;
      }
  }

};

// Common functionality for all the different tests we'll be running
// We're creating two elements that will live in subdomain 1 and one
// element that is lives in subdomain 2 that overlaps both elements in
// subdomain 1, but is not topologically connected.
class OverlappingTestBase
{
protected:

  std::unique_ptr<MeshBase> _mesh;

  std::unique_ptr<EquationSystems> _es;

  void build_quad_mesh(unsigned int n_refinements = 0)
  {
    // We are making assumptions in various places about the presence
    // of the elements on the current processor so we're restricting to
    // ReplicatedMesh for now.
    _mesh.reset(new ReplicatedMesh(*TestCommWorld));

    _mesh->set_mesh_dimension(2);

    _mesh->add_point( Point(0.0,0.0),0 );
    _mesh->add_point( Point(1.0,0.0),1 );
    _mesh->add_point( Point(1.0,1.0),2 );
    _mesh->add_point( Point(0.0,1.0),3 );

    {
      Elem* elem = _mesh->add_elem( new Quad4 );
      elem->set_id(0);
      elem->subdomain_id() = 1;

      for (unsigned int n=0; n<4; n++)
        elem->set_node(n) = _mesh->node_ptr(n);
    }

    _mesh->add_point( Point(1.0,2.0),4 );
    _mesh->add_point( Point(0.0,2.0),5 );

    {
      Elem* elem = _mesh->add_elem( new Quad4 );
      elem->set_id(1);
      elem->subdomain_id() = 1;

      elem->set_node(0) = _mesh->node_ptr(3);
      elem->set_node(1) = _mesh->node_ptr(2);
      elem->set_node(2) = _mesh->node_ptr(4);
      elem->set_node(3) = _mesh->node_ptr(5);
    }

    _mesh->add_point( Point(0.0,0.0),6 );
    _mesh->add_point( Point(1.0,0.0),7 );
    _mesh->add_point( Point(1.0,2.0),8 );
    _mesh->add_point( Point(0.0,2.0),9 );

    {
      Elem* elem = _mesh->add_elem( new Quad4 );
      elem->set_id(2);
      elem->subdomain_id() = 2;

      elem->set_node(0) = _mesh->node_ptr(6);
      elem->set_node(1) = _mesh->node_ptr(7);
      elem->set_node(2) = _mesh->node_ptr(8);
      elem->set_node(3) = _mesh->node_ptr(9);
    }

    _mesh->prepare_for_use();

    if (n_refinements > 0)
      {
        MeshRefinement refine(*_mesh);
        refine.uniformly_refine(n_refinements);
      }
  }

  void init(MeshBase & mesh)
  {
    _es.reset( new EquationSystems(*_mesh) );
    LinearImplicitSystem & sys = _es->add_system<LinearImplicitSystem> ("SimpleSystem");

    std::set<subdomain_id_type> sub_one;
    sub_one.insert(1);

    std::set<subdomain_id_type> sub_two;
    sub_two.insert(2);

    sys.add_variable("U", FIRST, LAGRANGE, &sub_two);
    sys.add_variable("L", FIRST, LAGRANGE, &sub_two);

    sys.add_variable("V", FIRST, LAGRANGE, &sub_one);
    sys.add_variable("p", FIRST, LAGRANGE, &sub_one);

    _es->init();
  }

  void clear()
  {
    _es.reset();
    _mesh.reset();
  }

  void setup_coupling_matrix( std::unique_ptr<CouplingMatrix> & coupling )
  {
    System & system = _es->get_system("SimpleSystem");

    coupling.reset( new CouplingMatrix(system.n_vars()) );

    const unsigned int u_var = system.variable_number("U");
    const unsigned int l_var = system.variable_number("L");
    const unsigned int v_var = system.variable_number("V");
    const unsigned int p_var = system.variable_number("p");

    // Only adding the overlapping couplings since the primary ones should
    // be there by default.
    (*coupling)(u_var,v_var) = true;
    (*coupling)(l_var,v_var) = true;
    (*coupling)(l_var,p_var) = true;
    (*coupling)(v_var,u_var) = true;
    (*coupling)(v_var,l_var) = true;
  }

};

// Mainly just to sanity check the mesh construction and
// the assumptions made in the OverlappingCouplingFunctor
class OverlappingFunctorTest : public CppUnit::TestCase,
                               public OverlappingTestBase
{
public:

  CPPUNIT_TEST_SUITE( OverlappingFunctorTest );

  CPPUNIT_TEST( checkCouplingFunctorQuad );
  CPPUNIT_TEST( checkCouplingFunctorQuadUnifRef );
  CPPUNIT_TEST( checkCouplingFunctorTri );
  CPPUNIT_TEST( checkCouplingFunctorTriUnifRef );

  CPPUNIT_TEST_SUITE_END();

public:

  public:
  void setUp()
  {
    this->build_quad_mesh();
    this->init(*_mesh);
  }

  void tearDown()
  { this->clear(); }

  void checkCouplingFunctorQuad()
  { this->run_coupling_functor_test(0); }

  void checkCouplingFunctorQuadUnifRef()
  { this->run_coupling_functor_test(1); }

  void checkCouplingFunctorTri()
  {
    MeshTools::Modification::all_tri(*_mesh);
    _es->reinit();
    this->run_coupling_functor_test(0);
  }

  void checkCouplingFunctorTriUnifRef()
  {
    MeshTools::Modification::all_tri(*_mesh);
    _es->reinit();
    this->run_coupling_functor_test(1);
  }

private:

  // This is basically to sanity check the coupling functor
  // with the supplied mesh to make sure all the assumptions
  // are kosher.
  void run_coupling_functor_test(unsigned int n_refinements)
  {
    if( n_refinements > 0 )
      {
        MeshRefinement refine(*_mesh);
        refine.uniformly_refine(n_refinements);
        _es->reinit();
      }

    System & system = _es->get_system("SimpleSystem");

    OverlappingCouplingFunctor coupling_functor(system);

    std::unordered_map<const Elem *,const CouplingMatrix*> subdomain_one_couplings;
    std::unordered_map<const Elem *,const CouplingMatrix*> subdomain_two_couplings;

    coupling_functor( _mesh->active_subdomain_elements_begin(1),
                      _mesh->active_subdomain_elements_end(1),
                      DofObject::invalid_processor_id,
                      subdomain_one_couplings );

    coupling_functor( _mesh->active_subdomain_elements_begin(2),
                      _mesh->active_subdomain_elements_end(2),
                      DofObject::invalid_processor_id,
                      subdomain_two_couplings );

    dof_id_type n_elems_subdomain_two = std::distance( _mesh->active_subdomain_elements_begin(2),
                                                       _mesh->active_subdomain_elements_end(2) );

    CPPUNIT_ASSERT_EQUAL( n_elems_subdomain_two, (dof_id_type) subdomain_one_couplings.size() );
    CPPUNIT_ASSERT_EQUAL( 2*n_elems_subdomain_two, (dof_id_type) subdomain_two_couplings.size() );
  }

};

CPPUNIT_TEST_SUITE_REGISTRATION( OverlappingFunctorTest );
<|endoftext|>


<commit_before>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// transaction_test.cpp
//
// Identification: tests/concurrency/transaction_test.cpp
//
// Copyright (c) 2015, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "harness.h"
#include "concurrency/transaction_tests_util.h"

namespace peloton {

namespace test {

//===--------------------------------------------------------------------===//
// Transaction Tests
//===--------------------------------------------------------------------===//

class TransactionTests : public PelotonTest {};

// Execute a schedule, the result for any read will be recorded in
// schedule->results
void ExecuteSchedule(concurrency::TransactionManager *txn_manager,
                     storage::DataTable *table, TransactionSchedule *schedule) {
  // Sleep for the first operation, this gives the opportunity to schedule
  // the start time of a txn
  int last_time = schedule->times[0];
  std::this_thread::sleep_for(std::chrono::milliseconds(last_time));

  auto transaction = txn_manager->BeginTransaction();
  for (int i = 0; i < (int)schedule->operations.size(); i++) {
    // Sleep milliseconds
    std::this_thread::sleep_for(
        std::chrono::milliseconds(schedule->times[i] - last_time));
    last_time = schedule->times[i];

    // Prepare data for operation
    txn_op_t op = schedule->operations[i].op;
    int id = schedule->operations[i].id;
    int value = schedule->operations[i].value;

    // Execute the operation
    switch (op) {
      case TXN_OP_INSERT: {
        LOG_TRACE("Execute Insert");
        TransactionTestsUtil::ExecuteInsert(transaction, table, id, value);
        break;
      }
      case TXN_OP_READ: {
        LOG_TRACE("Execute Read");
        int result = TransactionTestsUtil::ExecuteRead(transaction, table, id);
        schedule->results.push_back(result);
        break;
      }
      case TXN_OP_DELETE: {
        LOG_TRACE("Execute Delete");
        TransactionTestsUtil::ExecuteDelete(transaction, table, id);
        break;
      }
      case TXN_OP_UPDATE: {
        LOG_TRACE("Execute Update");
        TransactionTestsUtil::ExecuteUpdate(transaction, table, id, value);
        break;
      }
      case TXN_OP_SCAN: {
        LOG_TRACE("Execute Scan");
        TransactionTestsUtil::ExecuteScan(transaction, schedule->results, table,
                                          id);
        break;
      }
      case TXN_OP_ABORT: {
        LOG_TRACE("Abort");
        // Assert last operation
        assert(i == (int)schedule->operations.size() - 1);
        break;
      }
      case TXN_OP_NOTHING: {
        LOG_TRACE("Do nothing");
        // Do nothing
        break;
      }
    }
  }

  if (schedule->operations.rbegin()->op == TXN_OP_ABORT)
    schedule->txn_result = txn_manager->AbortTransaction();
  else {
    schedule->txn_result = txn_manager->CommitTransaction();
  }

  LOG_TRACE("Txn finished");
}

void ThreadExecuteSchedule(concurrency::TransactionManager *txn_manager,
                           storage::DataTable *table,
                           TransactionSchedules *schedules) {
  int next_schedule = schedules->next_sched.fetch_add(1);
  auto schedule = schedules->schedules[next_schedule];
  ExecuteSchedule(txn_manager, table, schedule);
}

void TransactionTest(concurrency::TransactionManager *txn_manager) {
  uint64_t thread_id = TestingHarness::GetInstance().GetThreadId();

  for (oid_t txn_itr = 1; txn_itr <= 50; txn_itr++) {
    txn_manager->BeginTransaction();
    if (thread_id % 2 == 0) {
      std::chrono::microseconds sleep_time(1);
      std::this_thread::sleep_for(sleep_time);
    }

    if (txn_itr % 25 != 0) {
      txn_manager->CommitTransaction();
    } else {
      txn_manager->AbortTransaction();
    }
  }
}

void DirtyWriteTest() {
  auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance();
  std::unique_ptr<storage::DataTable> table(
      TransactionTestsUtil::CreateTable());
  TransactionSchedules schedules;

  // 0 ms       T1 updates (0, ?) to (0, 1)
  // 500 ms     T2 updates (0, ?) to (0, 2), and commits
  // 1000 ms    T1 commits
  // 1500 ms    T3 reads (0, ?) and commits

  TransactionSchedule schedule1;
  TransactionSchedule schedule2;
  TransactionSchedule schedule3;
  schedule1.AddUpdate(0, 1, 0);
  schedule2.AddUpdate(0, 2, 500);
  schedule1.AddDoNothing(1000);
  schedule3.AddRead(0, 1500);

  schedules.AddSchedule(&schedule1);
  schedules.AddSchedule(&schedule2);
  schedules.AddSchedule(&schedule3);

  LaunchParallelTest(3, ThreadExecuteSchedule, &txn_manager, table.get(),
                     &schedules);

  // T1 and T2 can't both succeed
  EXPECT_FALSE(schedule1.txn_result == RESULT_SUCCESS && schedule2.txn_result == RESULT_SUCCESS);
  // T3 should succeed
  EXPECT_EQ(RESULT_SUCCESS, schedule3.txn_result);
  // For MVCC, actually one and only one T should succeed?
  EXPECT_TRUE((schedule1.txn_result == RESULT_SUCCESS && schedule2.txn_result == RESULT_ABORTED) ||
                (schedule1.txn_result == RESULT_ABORTED && schedule2.txn_result == RESULT_SUCCESS));
}

void DirtyReadTest() {
  auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance();
  std::unique_ptr<storage::DataTable> table(
      TransactionTestsUtil::CreateTable());

  TransactionSchedules schedules;

  // 0 ms         T1 updates (0, ?) to (0, 1)
  // 500 ms       T2 reads (0, ?)
  // 1000 ms      T1 commits
  // Insert (1, 1) at 500 ms, commit at 1500 ms
  TransactionSchedule schedule1;
  schedule1.AddInsert(10, 1, 500);
  schedule1.AddDoNothing(1500);
  // Start transaction at 0 ms, but read at 2000 ms, should read nothing
  TransactionSchedule schedule2;
  schedule2.AddDoNothing(0);
  schedule2.AddRead(10, 2000);
  // Read 1 at 1000 ms, should not read the uncommitted version
  TransactionSchedule schedule3;
  schedule3.AddRead(10, 1000);
  // Read 1 at 2000 ms, should read the committed version
  TransactionSchedule schedule4;
  schedule4.AddRead(10, 2000);

  schedules.AddSchedule(&schedule1);
  schedules.AddSchedule(&schedule2);
  schedules.AddSchedule(&schedule3);
  schedules.AddSchedule(&schedule4);

  LaunchParallelTest(4, ThreadExecuteSchedule, &txn_manager, table.get(),
                     &schedules);

  EXPECT_EQ(-1, schedule2.results[0]);
  EXPECT_EQ(-1, schedule3.results[0]);
  EXPECT_EQ(1, schedule4.results[0]);
}

void FuzzyReadTest() {
  auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance();
  std::unique_ptr<storage::DataTable> table(
      TransactionTestsUtil::CreateTable());

  TransactionSchedules schedules;

  // T1 Read at 0 ms, T2 update at 500 ms, T1 commit at 1000 ms, T1 should fail
  TransactionSchedule schedule1;
  schedule1.AddRead(0, 0);
  schedule1.AddDoNothing(1000);
  TransactionSchedule schedule2;
  schedule2.AddUpdate(0, 1, 500);

  // T1 Read at 0 ms, T2 delete at 500 ms, T1 commit at 1000 ms, T1 should fail
  TransactionSchedule schedule3;
  schedule3.AddRead(1, 0);
  schedule3.AddDoNothing(1000);
  TransactionSchedule schedule4;
  schedule4.AddDelete(1, 500);

  schedules.AddSchedule(&schedule1);
  schedules.AddSchedule(&schedule2);
  schedules.AddSchedule(&schedule3);
  schedules.AddSchedule(&schedule4);

  LaunchParallelTest(4, ThreadExecuteSchedule, &txn_manager, table.get(),
                     &schedules);

  EXPECT_EQ(RESULT_SUCCESS, schedule2.txn_result);
  EXPECT_EQ(RESULT_SUCCESS, schedule4.txn_result);
  EXPECT_EQ(RESULT_ABORTED, schedule1.txn_result);
  EXPECT_EQ(RESULT_ABORTED, schedule3.txn_result);
}

void PhantomTest() {
  auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance();
  std::unique_ptr<storage::DataTable> table(
      TransactionTestsUtil::CreateTable());

  TransactionSchedules schedules;
  // Begin Scan for id>=0 at 0 ms, Insert (5, 0) at 500 ms, Commit at 1000 ms,
  // should fail
  TransactionSchedule schedule1;
  schedule1.AddScan(0, 0);
  schedule1.AddDoNothing(1000);
  TransactionSchedule schedule2;
  schedule2.AddInsert(5, 0, 500);
  // Another txn scan again, should see the new val
  TransactionSchedule schedule3;
  schedule3.AddScan(0, 1500);
  TransactionSchedule schedule4;
  schedule4.AddScan(0, 0);

  // Begin scan for id>=0 at 1500 ms, Delete (5, 0) at 2000 ms, Commit at 2500 ms.
  // Should have missing value.
  TransactionSchedule schedule5;
  TransactionSchedule schedule6;
  schedule5.AddScan(0, 1500);
  schedule6.AddDelete(5, 2000);
  schedule5.AddDoNothing(2500);

  schedules.AddSchedule(&schedule1);
  schedules.AddSchedule(&schedule2);
  schedules.AddSchedule(&schedule3);
  schedules.AddSchedule(&schedule4);
  schedules.AddSchedule(&schedule5);
  schedules.AddSchedule(&schedule6);

  LaunchParallelTest(6, ThreadExecuteSchedule, &txn_manager, table.get(),
                     &schedules);

  EXPECT_EQ(RESULT_ABORTED, schedule1.txn_result);
  EXPECT_EQ(RESULT_SUCCESS, schedule2.txn_result);
  EXPECT_EQ(RESULT_SUCCESS, schedule3.txn_result);
  EXPECT_EQ(RESULT_SUCCESS, schedule4.txn_result);
  EXPECT_EQ(RESULT_ABORTED, schedule5.txn_result);
  EXPECT_EQ(RESULT_SUCCESS, schedule6.txn_result);
  EXPECT_EQ(11, schedule3.results.size());
  EXPECT_EQ(10, schedule4.results.size());
}

TEST_F(TransactionTests, TransactionTest) {
  auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance();

  LaunchParallelTest(8, TransactionTest, &txn_manager);

  std::cout << "next Commit Id :: " << txn_manager.GetNextCommitId() << "\n";
}

TEST_F(TransactionTests, AbortTest) {
  auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance();
  std::unique_ptr<storage::DataTable> table(
      TransactionTestsUtil::CreateTable());
  TransactionSchedules schedules;
  TransactionSchedule schedule1;
  TransactionSchedule schedule2;
  // Schedule one thread to update and abort, another thread to read afterwards
  schedule1.AddUpdate(0, 100, 0);
  schedule1.AddAbort(50);
  schedule2.AddRead(0, 100);
  schedules.AddSchedule(&schedule1);
  schedules.AddSchedule(&schedule2);
  LaunchParallelTest(2, ThreadExecuteSchedule, &txn_manager, table.get(),
                     &schedules);

  EXPECT_EQ(schedule2.results[0], 0);

}

TEST_F(TransactionTests, SerializableTest) {
  DirtyWriteTest();
  DirtyReadTest();
  FuzzyReadTest();
//  PhantomTes();
}

}  // End test namespace
}  // End peloton namespace
<commit_msg>add another commit order to txn DirtyWrite<commit_after>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// transaction_test.cpp
//
// Identification: tests/concurrency/transaction_test.cpp
//
// Copyright (c) 2015, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "harness.h"
#include "concurrency/transaction_tests_util.h"

namespace peloton {

namespace test {

//===--------------------------------------------------------------------===//
// Transaction Tests
//===--------------------------------------------------------------------===//

class TransactionTests : public PelotonTest {};

// Execute a schedule, the result for any read will be recorded in
// schedule->results
void ExecuteSchedule(concurrency::TransactionManager *txn_manager,
                     storage::DataTable *table, TransactionSchedule *schedule) {
  // Sleep for the first operation, this gives the opportunity to schedule
  // the start time of a txn
  int last_time = schedule->times[0];
  std::this_thread::sleep_for(std::chrono::milliseconds(last_time));

  auto transaction = txn_manager->BeginTransaction();
  for (int i = 0; i < (int)schedule->operations.size(); i++) {
    // Sleep milliseconds
    std::this_thread::sleep_for(
        std::chrono::milliseconds(schedule->times[i] - last_time));
    last_time = schedule->times[i];

    // Prepare data for operation
    txn_op_t op = schedule->operations[i].op;
    int id = schedule->operations[i].id;
    int value = schedule->operations[i].value;

    // Execute the operation
    switch (op) {
      case TXN_OP_INSERT: {
        LOG_TRACE("Execute Insert");
        TransactionTestsUtil::ExecuteInsert(transaction, table, id, value);
        break;
      }
      case TXN_OP_READ: {
        LOG_TRACE("Execute Read");
        int result = TransactionTestsUtil::ExecuteRead(transaction, table, id);
        schedule->results.push_back(result);
        break;
      }
      case TXN_OP_DELETE: {
        LOG_TRACE("Execute Delete");
        TransactionTestsUtil::ExecuteDelete(transaction, table, id);
        break;
      }
      case TXN_OP_UPDATE: {
        LOG_TRACE("Execute Update");
        TransactionTestsUtil::ExecuteUpdate(transaction, table, id, value);
        break;
      }
      case TXN_OP_SCAN: {
        LOG_TRACE("Execute Scan");
        TransactionTestsUtil::ExecuteScan(transaction, schedule->results, table,
                                          id);
        break;
      }
      case TXN_OP_ABORT: {
        LOG_TRACE("Abort");
        // Assert last operation
        assert(i == (int)schedule->operations.size() - 1);
        break;
      }
      case TXN_OP_NOTHING: {
        LOG_TRACE("Do nothing");
        // Do nothing
        break;
      }
    }
  }

  if (schedule->operations.rbegin()->op == TXN_OP_ABORT)
    schedule->txn_result = txn_manager->AbortTransaction();
  else {
    schedule->txn_result = txn_manager->CommitTransaction();
  }

  LOG_TRACE("Txn finished");
}

void ThreadExecuteSchedule(concurrency::TransactionManager *txn_manager,
                           storage::DataTable *table,
                           TransactionSchedules *schedules) {
  int next_schedule = schedules->next_sched.fetch_add(1);
  auto schedule = schedules->schedules[next_schedule];
  ExecuteSchedule(txn_manager, table, schedule);
}

void TransactionTest(concurrency::TransactionManager *txn_manager) {
  uint64_t thread_id = TestingHarness::GetInstance().GetThreadId();

  for (oid_t txn_itr = 1; txn_itr <= 50; txn_itr++) {
    txn_manager->BeginTransaction();
    if (thread_id % 2 == 0) {
      std::chrono::microseconds sleep_time(1);
      std::this_thread::sleep_for(sleep_time);
    }

    if (txn_itr % 25 != 0) {
      txn_manager->CommitTransaction();
    } else {
      txn_manager->AbortTransaction();
    }
  }
}

void DirtyWriteTest() {
  auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance();
  std::unique_ptr<storage::DataTable> table(
      TransactionTestsUtil::CreateTable());
  TransactionSchedules schedules;

  {
    // 0 ms       T1 updates (0, ?) to (0, 1)
    // 500 ms     T2 updates (0, ?) to (0, 2), and commits
    // 1000 ms    T1 commits

    TransactionSchedule schedule1;
    TransactionSchedule schedule2;
    schedule1.AddUpdate(0, 1, 0);
    schedule2.AddUpdate(0, 2, 500);
    schedule1.AddDoNothing(1000);

    schedules.AddSchedule(&schedule1);
    schedules.AddSchedule(&schedule2);

    LaunchParallelTest(2, ThreadExecuteSchedule, &txn_manager, table.get(),
                       &schedules);

    // T1 and T2 can't both succeed
    EXPECT_FALSE(schedule1.txn_result == RESULT_SUCCESS && schedule2.txn_result == RESULT_SUCCESS);
    // For MVCC, actually one and only one T should succeed?
    EXPECT_TRUE((schedule1.txn_result == RESULT_SUCCESS && schedule2.txn_result == RESULT_ABORTED) ||
                (schedule1.txn_result == RESULT_ABORTED && schedule2.txn_result == RESULT_SUCCESS));
    schedules.clear();
  }

  {
    // 0 ms       T1 updates (0, ?) to (0, 1)
    // 500 ms     T2 updates (0, ?) to (0, 2)
    // 1000 ms    T1 commits
    // 1500 ms    T2 commits

    TransactionSchedule schedule1;
    TransactionSchedule schedule2;
    schedule1.AddUpdate(0, 1, 0);
    schedule2.AddUpdate(0, 2, 500);
    schedule1.AddDoNothing(1000);
    schedule2.AddDoNothing(1500);

    schedules.AddSchedule(&schedule1);
    schedules.AddSchedule(&schedule2);

    LaunchParallelTest(2, ThreadExecuteSchedule, &txn_manager, table.get(),
                       &schedules);

    // T1 and T2 can't both succeed
    EXPECT_FALSE(schedule1.txn_result == RESULT_SUCCESS && schedule2.txn_result == RESULT_SUCCESS);
    // For MVCC, actually one and only one T should succeed?
    EXPECT_TRUE((schedule1.txn_result == RESULT_SUCCESS && schedule2.txn_result == RESULT_ABORTED) ||
                (schedule1.txn_result == RESULT_ABORTED && schedule2.txn_result == RESULT_SUCCESS));
    schedules.clear();
  }

}

void DirtyReadTest() {
  auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance();
  std::unique_ptr<storage::DataTable> table(
      TransactionTestsUtil::CreateTable());

  TransactionSchedules schedules;

  // 0 ms         T1 updates (0, ?) to (0, 1)
  // 500 ms       T2 reads (0, ?)
  // 1000 ms      T1 commits
  // Insert (1, 1) at 500 ms, commit at 1500 ms
  TransactionSchedule schedule1;
  schedule1.AddInsert(10, 1, 500);
  schedule1.AddDoNothing(1500);
  // Start transaction at 0 ms, but read at 2000 ms, should read nothing
  TransactionSchedule schedule2;
  schedule2.AddDoNothing(0);
  schedule2.AddRead(10, 2000);
  // Read 1 at 1000 ms, should not read the uncommitted version
  TransactionSchedule schedule3;
  schedule3.AddRead(10, 1000);
  // Read 1 at 2000 ms, should read the committed version
  TransactionSchedule schedule4;
  schedule4.AddRead(10, 2000);

  schedules.AddSchedule(&schedule1);
  schedules.AddSchedule(&schedule2);
  schedules.AddSchedule(&schedule3);
  schedules.AddSchedule(&schedule4);

  LaunchParallelTest(4, ThreadExecuteSchedule, &txn_manager, table.get(),
                     &schedules);

  EXPECT_EQ(-1, schedule2.results[0]);
  EXPECT_EQ(-1, schedule3.results[0]);
  EXPECT_EQ(1, schedule4.results[0]);
}

void FuzzyReadTest() {
  auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance();
  std::unique_ptr<storage::DataTable> table(
      TransactionTestsUtil::CreateTable());

  TransactionSchedules schedules;

  // T1 Read at 0 ms, T2 update at 500 ms, T1 commit at 1000 ms, T1 should fail
  TransactionSchedule schedule1;
  schedule1.AddRead(0, 0);
  schedule1.AddDoNothing(1000);
  TransactionSchedule schedule2;
  schedule2.AddUpdate(0, 1, 500);

  // T1 Read at 0 ms, T2 delete at 500 ms, T1 commit at 1000 ms, T1 should fail
  TransactionSchedule schedule3;
  schedule3.AddRead(1, 0);
  schedule3.AddDoNothing(1000);
  TransactionSchedule schedule4;
  schedule4.AddDelete(1, 500);

  schedules.AddSchedule(&schedule1);
  schedules.AddSchedule(&schedule2);
  schedules.AddSchedule(&schedule3);
  schedules.AddSchedule(&schedule4);

  LaunchParallelTest(4, ThreadExecuteSchedule, &txn_manager, table.get(),
                     &schedules);

  EXPECT_EQ(RESULT_SUCCESS, schedule2.txn_result);
  EXPECT_EQ(RESULT_SUCCESS, schedule4.txn_result);
  EXPECT_EQ(RESULT_ABORTED, schedule1.txn_result);
  EXPECT_EQ(RESULT_ABORTED, schedule3.txn_result);
}

void PhantomTest() {
  auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance();
  std::unique_ptr<storage::DataTable> table(
      TransactionTestsUtil::CreateTable());

  TransactionSchedules schedules;
  // Begin Scan for id>=0 at 0 ms, Insert (5, 0) at 500 ms, Commit at 1000 ms,
  // should fail
  TransactionSchedule schedule1;
  schedule1.AddScan(0, 0);
  schedule1.AddDoNothing(1000);
  TransactionSchedule schedule2;
  schedule2.AddInsert(5, 0, 500);
  // Another txn scan again, should see the new val
  TransactionSchedule schedule3;
  schedule3.AddScan(0, 1500);
  TransactionSchedule schedule4;
  schedule4.AddScan(0, 0);

  // Begin scan for id>=0 at 1500 ms, Delete (5, 0) at 2000 ms, Commit at 2500 ms.
  // Should have missing value.
  TransactionSchedule schedule5;
  TransactionSchedule schedule6;
  schedule5.AddScan(0, 1500);
  schedule6.AddDelete(5, 2000);
  schedule5.AddDoNothing(2500);

  schedules.AddSchedule(&schedule1);
  schedules.AddSchedule(&schedule2);
  schedules.AddSchedule(&schedule3);
  schedules.AddSchedule(&schedule4);
  schedules.AddSchedule(&schedule5);
  schedules.AddSchedule(&schedule6);

  LaunchParallelTest(6, ThreadExecuteSchedule, &txn_manager, table.get(),
                     &schedules);

  EXPECT_EQ(RESULT_ABORTED, schedule1.txn_result);
  EXPECT_EQ(RESULT_SUCCESS, schedule2.txn_result);
  EXPECT_EQ(RESULT_SUCCESS, schedule3.txn_result);
  EXPECT_EQ(RESULT_SUCCESS, schedule4.txn_result);
  EXPECT_EQ(RESULT_ABORTED, schedule5.txn_result);
  EXPECT_EQ(RESULT_SUCCESS, schedule6.txn_result);
  EXPECT_EQ(11, schedule3.results.size());
  EXPECT_EQ(10, schedule4.results.size());
}

TEST_F(TransactionTests, TransactionTest) {
  auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance();

  LaunchParallelTest(8, TransactionTest, &txn_manager);

  std::cout << "next Commit Id :: " << txn_manager.GetNextCommitId() << "\n";
}

TEST_F(TransactionTests, AbortTest) {
  auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance();
  std::unique_ptr<storage::DataTable> table(
      TransactionTestsUtil::CreateTable());
  TransactionSchedules schedules;
  TransactionSchedule schedule1;
  TransactionSchedule schedule2;
  // Schedule one thread to update and abort, another thread to read afterwards
  schedule1.AddUpdate(0, 100, 0);
  schedule1.AddAbort(50);
  schedule2.AddRead(0, 100);
  schedules.AddSchedule(&schedule1);
  schedules.AddSchedule(&schedule2);
  LaunchParallelTest(2, ThreadExecuteSchedule, &txn_manager, table.get(),
                     &schedules);

  EXPECT_EQ(schedule2.results[0], 0);

}

TEST_F(TransactionTests, SerializableTest) {
  DirtyWriteTest();
  DirtyReadTest();
  FuzzyReadTest();
//  PhantomTes();
}

}  // End test namespace
}  // End peloton namespace
<|endoftext|>


<commit_before>#ifndef MAPBOX_UTIL_OPTIONAL_HPP
#define MAPBOX_UTIL_OPTIONAL_HPP

#include <type_traits>

#include "variant.hpp"

namespace mapbox { namespace util {

    namespace detail {
        struct none_helper{};
    }
    typedef int detail::none_helper::*none_t ;
    none_t const none = (static_cast<none_t>(0)) ;

    template<typename T>
    class optional {
        static_assert(!std::is_reference<T>::value, "optional doesn't support references");

        variant<none_t, T> variant_;

      public:
        optional() = default;

        explicit optional(optional const& rhs) {
            variant_ = rhs.variant_;
        }

        explicit optional(T const& v) {
            variant_ = v;
        }

        bool operator!() const {
            return variant_.template is<none_t>();
        }

        T const& get() const {
            return variant_.template get<T>();
        }

        T&       get() {
            return variant_.template get<T>();
        }

        T const& operator *() const { return this->get() ; }
        T        operator *()       { return this->get() ; }

        optional& operator = ( T const& v ) {
            variant_ = v;
            return *this;
        }

        optional& operator = ( optional const& rhs ) {
            variant_ = rhs.variant_;
            return *this;
        }

        template<typename... Args>
        void emplace(Args&&... args) {
            variant_ = T{std::forward<Args>(args)...};
        }

        void reset() {
            variant_ = none;
        }
    };
}
}

#endif
<commit_msg>explicit operator bool() const noexcept<commit_after>#ifndef MAPBOX_UTIL_OPTIONAL_HPP
#define MAPBOX_UTIL_OPTIONAL_HPP

#include <type_traits>

#include "variant.hpp"

namespace mapbox { namespace util {

    namespace detail {
        struct none_helper{};
    }
    typedef int detail::none_helper::*none_t ;
    none_t const none = (static_cast<none_t>(0)) ;

    template<typename T>
    class optional {
        static_assert(!std::is_reference<T>::value, "optional doesn't support references");

        variant<none_t, T> variant_;

      public:
        optional() = default;

        explicit optional(optional const& rhs) {
            variant_ = rhs.variant_;
        }

        explicit optional(T const& v) {
            variant_ = v;
        }

        bool operator!() const noexcept{
            return variant_.template is<none_t>();
        }

        explicit operator bool() const noexcept {
            return variant_.template is<T>();
        }

        T const& get() const {
            return variant_.template get<T>();
        }

        T&       get() {
            return variant_.template get<T>();
        }

        T const& operator *() const { return this->get() ; }
        T        operator *()       { return this->get() ; }

        optional& operator = ( T const& v ) {
            variant_ = v;
            return *this;
        }

        optional& operator = ( optional const& rhs ) {
            variant_ = rhs.variant_;
            return *this;
        }

        template<typename... Args>
        void emplace(Args&&... args) {
            variant_ = T{std::forward<Args>(args)...};
        }

        void reset() {
            variant_ = none;
        }
    };
}
}

#endif
<|endoftext|>


<commit_before><commit_msg>Hack to get mus working in non-ash world<commit_after><|endoftext|>


<commit_before>// Copyright 2018 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <gtest/gtest.h>

#include <chrono>
#include <string>
#include <memory>

#include "rclcpp/exceptions.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/executors.hpp"

#include "rcl_interfaces/msg/intra_process_message.hpp"

using namespace std::chrono_literals;

using rcl_interfaces::msg::IntraProcessMessage;

class TestMultiThreadedExecutor : public ::testing::Test
{
protected:
  static void SetUpTestCase()
  {
    rclcpp::init(0, nullptr);
  }
};

/*
   Test that timers are not taken multiple times when using reentrant callback groups.
 */
TEST_F(TestMultiThreadedExecutor, timer_over_take) {
#ifdef __linux__
  // This seems to be the most effective way to force the bug to happen on Linux.
  // This is unnecessary on MacOS, since the default scheduler causes it.
  struct sched_param param;
  param.sched_priority = 0;
  if (sched_setscheduler(0, SCHED_BATCH, &param) != 0) {
    perror("sched_setscheduler");
  }
#endif

  bool yield_before_execute = true;

  rclcpp::executors::MultiThreadedExecutor executor(
    rclcpp::executor::create_default_executor_arguments(), 2u, yield_before_execute);

  ASSERT_GT(executor.get_number_of_threads(), 1u);

  std::shared_ptr<rclcpp::Node> node =
    std::make_shared<rclcpp::Node>("test_multi_threaded_executor_timer_over_take");

  auto cbg = node->create_callback_group(rclcpp::callback_group::CallbackGroupType::Reentrant);

  rclcpp::Clock system_clock(RCL_STEADY_TIME);
  std::mutex last_mutex;
  auto last = system_clock.now();

  std::atomic_int timer_count {0};

  auto timer_callback = [&timer_count, &executor, &system_clock, &last_mutex, &last]() {
      // While this tolerance is a little wide, if the bug occurs, the next step will
      // happen almost instantly. The purpose of this test is not to measure the jitter
      // in timers, just assert that a reasonable amount of time has passed.
      const double PERIOD = 0.1f;
      const double TOLERANCE = 0.025f;

      rclcpp::Time now = system_clock.now();
      timer_count++;

      if (timer_count > 5) {
        executor.cancel();
      }

      {
        std::lock_guard<std::mutex> lock(last_mutex);
        double diff = std::abs((now - last).nanoseconds()) / 1.0e9;
        last = now;

        if (diff < PERIOD - TOLERANCE || diff > PERIOD + TOLERANCE) {
          executor.cancel();
          ASSERT_GT(diff, PERIOD - TOLERANCE);
          ASSERT_LT(diff, PERIOD + TOLERANCE);
        }
      }
    };

  auto timer = node->create_wall_timer(100ms, timer_callback, cbg);
  executor.add_node(node);
  executor.spin();
}
<commit_msg>Relax multithreaded test constraint (#907)<commit_after>// Copyright 2018 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <gtest/gtest.h>

#include <chrono>
#include <string>
#include <memory>

#include "rclcpp/exceptions.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/executors.hpp"

#include "rcl_interfaces/msg/intra_process_message.hpp"

using namespace std::chrono_literals;

using rcl_interfaces::msg::IntraProcessMessage;

class TestMultiThreadedExecutor : public ::testing::Test
{
protected:
  static void SetUpTestCase()
  {
    rclcpp::init(0, nullptr);
  }
};

/*
   Test that timers are not taken multiple times when using reentrant callback groups.
 */
TEST_F(TestMultiThreadedExecutor, timer_over_take) {
#ifdef __linux__
  // This seems to be the most effective way to force the bug to happen on Linux.
  // This is unnecessary on MacOS, since the default scheduler causes it.
  struct sched_param param;
  param.sched_priority = 0;
  if (sched_setscheduler(0, SCHED_BATCH, &param) != 0) {
    perror("sched_setscheduler");
  }
#endif

  bool yield_before_execute = true;

  rclcpp::executors::MultiThreadedExecutor executor(
    rclcpp::executor::create_default_executor_arguments(), 2u, yield_before_execute);

  ASSERT_GT(executor.get_number_of_threads(), 1u);

  std::shared_ptr<rclcpp::Node> node =
    std::make_shared<rclcpp::Node>("test_multi_threaded_executor_timer_over_take");

  auto cbg = node->create_callback_group(rclcpp::callback_group::CallbackGroupType::Reentrant);

  rclcpp::Clock system_clock(RCL_STEADY_TIME);
  std::mutex last_mutex;
  auto last = system_clock.now();

  std::atomic_int timer_count {0};

  auto timer_callback = [&timer_count, &executor, &system_clock, &last_mutex, &last]() {
      // While this tolerance is a little wide, if the bug occurs, the next step will
      // happen almost instantly. The purpose of this test is not to measure the jitter
      // in timers, just assert that a reasonable amount of time has passed.
      const double PERIOD = 0.1f;
      const double TOLERANCE = 0.025f;

      rclcpp::Time now = system_clock.now();
      timer_count++;

      if (timer_count > 5) {
        executor.cancel();
      }

      {
        std::lock_guard<std::mutex> lock(last_mutex);
        double diff = std::abs((now - last).nanoseconds()) / 1.0e9;
        last = now;

        if (diff < PERIOD - TOLERANCE) {
          executor.cancel();
          ASSERT_GT(diff, PERIOD - TOLERANCE);
        }
      }
    };

  auto timer = node->create_wall_timer(100ms, timer_callback, cbg);
  executor.add_node(node);
  executor.spin();
}
<|endoftext|>