<commit_before>/*

Copyright (c) 2008, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/session.hpp"
#include "libtorrent/session_settings.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/alert_types.hpp"
#include <boost/thread.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/filesystem/operations.hpp>

#include "test.hpp"
#include "setup_transfer.hpp"

using boost::filesystem::remove_all;
using boost::filesystem::exists;

void test_swarm()
{
	using namespace libtorrent;

	session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48000, 49000));
	session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49000, 50000));
	session ses3(fingerprint("LT", 0, 1, 0, 0), std::make_pair(50000, 51000));

	ses1.set_alert_mask(alert::all_categories);
	ses2.set_alert_mask(alert::all_categories);
	ses3.set_alert_mask(alert::all_categories);

	// this is to avoid everything finish from a single peer
	// immediately. To make the swarm actually connect all
	// three peers before finishing.
	float rate_limit = 100000;
	ses1.set_upload_rate_limit(int(rate_limit));
	ses2.set_download_rate_limit(int(rate_limit));
	ses3.set_download_rate_limit(int(rate_limit));
	ses2.set_upload_rate_limit(int(rate_limit / 2));
	ses3.set_upload_rate_limit(int(rate_limit / 2));

	session_settings settings;
	settings.allow_multiple_connections_per_ip = true;
	settings.ignore_limits_on_local_network = false;
	ses1.set_settings(settings);
	ses2.set_settings(settings);
	ses3.set_settings(settings);

#ifndef TORRENT_DISABLE_ENCRYPTION
	pe_settings pes;
	pes.out_enc_policy = pe_settings::forced;
	pes.in_enc_policy = pe_settings::forced;
	ses1.set_pe_settings(pes);
	ses2.set_pe_settings(pes);
	ses3.set_pe_settings(pes);
#endif

	torrent_handle tor1;
	torrent_handle tor2;
	torrent_handle tor3;

	// test using piece sizes smaller than 16kB
	boost::tie(tor1, tor2, tor3) = setup_transfer(&ses1, &ses2, &ses3, true, false, true, "_swarm", 8 * 1024);

	float sum_dl_rate2 = 0.f;
	float sum_dl_rate3 = 0.f;
	int count_dl_rates2 = 0;
	int count_dl_rates3 = 0;

	for (int i = 0; i < 30; ++i)
	{
		print_alerts(ses1, "ses1");
		print_alerts(ses2, "ses2");
		print_alerts(ses3, "ses3");

		torrent_status st1 = tor1.status();
		torrent_status st2 = tor2.status();
		torrent_status st3 = tor3.status();

		if (st2.progress < 1.f && st2.progress > 0.5f)
		{
			sum_dl_rate2 += st2.download_payload_rate;
			++count_dl_rates2;
		}
		if (st3.progress < 1.f && st3.progress > 0.5f)
		{
			sum_dl_rate3 += st3.download_rate;
			++count_dl_rates3;
		}

		std::cerr
			<< "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s "
			<< st1.num_peers << ": "
			<< "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st2.progress * 100) << "% "
			<< st2.num_peers << " - "
			<< "\033[32m" << int(st3.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[31m" << int(st3.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st3.progress * 100) << "% "
			<< st3.num_peers
			<< std::endl;

		if (tor2.is_seed() && tor3.is_seed()) break;
		test_sleep(1000);
	}

	TEST_CHECK(tor2.is_seed());
	TEST_CHECK(tor3.is_seed());

	float average2 = sum_dl_rate2 / float(count_dl_rates2);
	float average3 = sum_dl_rate3 / float(count_dl_rates3);

	std::cerr << average2 << std::endl;
	std::cerr << "average rate: " << (average2 / 1000.f) << "kB/s - "
		<< (average3 / 1000.f) << "kB/s" << std::endl;

	TEST_CHECK(std::fabs(average2 - float(rate_limit)) < rate_limit / 11.f);
	TEST_CHECK(std::fabs(average3 - float(rate_limit)) < rate_limit / 11.f);
	if (tor2.is_seed() && tor3.is_seed()) std::cerr << "done\n";

	// make sure the files are deleted
	ses1.remove_torrent(tor1, session::delete_files);
	ses2.remove_torrent(tor2, session::delete_files);
	ses3.remove_torrent(tor3, session::delete_files);

	std::auto_ptr<alert> a = ses1.pop_alert();
	ptime end = time_now() + seconds(20);
	while (a.get() == 0 || dynamic_cast<torrent_deleted_alert*>(a.get()) == 0)
	{
		if (ses1.wait_for_alert(end - time_now()) == 0)
		{
			std::cerr << "wait_for_alert() expired" << std::endl;
			break;
		}
		a = ses1.pop_alert();
		assert(a.get());
		std::cerr << a->message() << std::endl;
	}

	TEST_CHECK(dynamic_cast<torrent_deleted_alert*>(a.get()) != 0);

	// there shouldn't be any alerts generated from now on
	// make sure that the timer in wait_for_alert() works
	// this should time out (ret == 0) and it should take
	// about 2 seconds
	ptime start = time_now();
	alert const* ret = ses1.wait_for_alert(seconds(2));
	TEST_CHECK(ret == 0);
	if (ret != 0) std::cerr << ret->message() << std::endl;
	TEST_CHECK(time_now() - start < seconds(3));
	TEST_CHECK(time_now() - start > seconds(2));
}

int test_main()
{
	using namespace libtorrent;
	using namespace boost::filesystem;

	// in case the previous run was terminated
	try { remove_all("./tmp1_swarm"); } catch (std::exception&) {}
	try { remove_all("./tmp2_swarm"); } catch (std::exception&) {}
	try { remove_all("./tmp3_swarm"); } catch (std::exception&) {}

	test_swarm();

	test_sleep(2000);
	TEST_CHECK(!exists("./tmp1_swarm/temporary"));
	TEST_CHECK(!exists("./tmp2_swarm/temporary"));
	TEST_CHECK(!exists("./tmp3_swarm/temporary"));

	remove_all("./tmp1_swarm");
	remove_all("./tmp2_swarm");
	remove_all("./tmp3_swarm");

	return 0;
}

<commit_msg>test_swarm fixes<commit_after>/*

Copyright (c) 2008, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/session.hpp"
#include "libtorrent/session_settings.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/alert_types.hpp"
#include <boost/thread.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/filesystem/operations.hpp>

#include "test.hpp"
#include "setup_transfer.hpp"

using boost::filesystem::remove_all;
using boost::filesystem::exists;

void test_swarm()
{
	using namespace libtorrent;

	session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48000, 49000));
	session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49000, 50000));
	session ses3(fingerprint("LT", 0, 1, 0, 0), std::make_pair(50000, 51000));

	// this is to avoid everything finish from a single peer
	// immediately. To make the swarm actually connect all
	// three peers before finishing.
	float rate_limit = 100000;
	ses1.set_upload_rate_limit(int(rate_limit));
	ses2.set_download_rate_limit(int(rate_limit));
	ses3.set_download_rate_limit(int(rate_limit));
	ses2.set_upload_rate_limit(int(rate_limit / 2));
	ses3.set_upload_rate_limit(int(rate_limit / 2));

	session_settings settings;
	settings.allow_multiple_connections_per_ip = true;
	settings.ignore_limits_on_local_network = false;
	ses1.set_settings(settings);
	ses2.set_settings(settings);
	ses3.set_settings(settings);

#ifndef TORRENT_DISABLE_ENCRYPTION
	pe_settings pes;
	pes.out_enc_policy = pe_settings::forced;
	pes.in_enc_policy = pe_settings::forced;
	ses1.set_pe_settings(pes);
	ses2.set_pe_settings(pes);
	ses3.set_pe_settings(pes);
#endif

	torrent_handle tor1;
	torrent_handle tor2;
	torrent_handle tor3;

	// test using piece sizes smaller than 16kB
	boost::tie(tor1, tor2, tor3) = setup_transfer(&ses1, &ses2, &ses3, true, false, true, "_swarm", 8 * 1024);

	ses1.set_alert_mask(alert::all_categories & ~alert::progress_notification);
	ses2.set_alert_mask(alert::all_categories & ~alert::progress_notification);
	ses3.set_alert_mask(alert::all_categories & ~alert::progress_notification);

	float sum_dl_rate2 = 0.f;
	float sum_dl_rate3 = 0.f;
	int count_dl_rates2 = 0;
	int count_dl_rates3 = 0;

	for (int i = 0; i < 30; ++i)
	{
		print_alerts(ses1, "ses1");
		print_alerts(ses2, "ses2");
		print_alerts(ses3, "ses3");

		torrent_status st1 = tor1.status();
		torrent_status st2 = tor2.status();
		torrent_status st3 = tor3.status();

		if (st2.progress < 1.f && st2.progress > 0.5f)
		{
			sum_dl_rate2 += st2.download_payload_rate;
			++count_dl_rates2;
		}
		if (st3.progress < 1.f && st3.progress > 0.5f)
		{
			sum_dl_rate3 += st3.download_rate;
			++count_dl_rates3;
		}

		std::cerr
			<< "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s "
			<< st1.num_peers << ": "
			<< "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st2.progress * 100) << "% "
			<< st2.num_peers << " - "
			<< "\033[32m" << int(st3.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[31m" << int(st3.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st3.progress * 100) << "% "
			<< st3.num_peers
			<< std::endl;

		if (tor2.is_seed() && tor3.is_seed()) break;
		test_sleep(1000);
	}

	TEST_CHECK(tor2.is_seed());
	TEST_CHECK(tor3.is_seed());

	float average2 = sum_dl_rate2 / float(count_dl_rates2);
	float average3 = sum_dl_rate3 / float(count_dl_rates3);

	std::cerr << average2 << std::endl;
	std::cerr << "average rate: " << (average2 / 1000.f) << "kB/s - "
		<< (average3 / 1000.f) << "kB/s" << std::endl;

	TEST_CHECK(std::fabs(average2 - float(rate_limit)) < rate_limit / 11.f);
	TEST_CHECK(std::fabs(average3 - float(rate_limit)) < rate_limit / 11.f);
	if (tor2.is_seed() && tor3.is_seed()) std::cerr << "done\n";

	// make sure the files are deleted
	ses1.remove_torrent(tor1, session::delete_files);
	ses2.remove_torrent(tor2, session::delete_files);
	ses3.remove_torrent(tor3, session::delete_files);

	std::auto_ptr<alert> a = ses1.pop_alert();
	ptime end = time_now() + seconds(20);
	while (a.get() == 0 || dynamic_cast<torrent_deleted_alert*>(a.get()) == 0)
	{
		if (ses1.wait_for_alert(end - time_now()) == 0)
		{
			std::cerr << "wait_for_alert() expired" << std::endl;
			break;
		}
		a = ses1.pop_alert();
		assert(a.get());
		std::cerr << a->message() << std::endl;
	}

	TEST_CHECK(dynamic_cast<torrent_deleted_alert*>(a.get()) != 0);

	// there shouldn't be any alerts generated from now on
	// make sure that the timer in wait_for_alert() works
	// this should time out (ret == 0) and it should take
	// about 2 seconds
	ptime start = time_now();
	alert const* ret = ses1.wait_for_alert(seconds(2));
	TEST_CHECK(ret == 0);
	if (ret != 0) std::cerr << ret->message() << std::endl;
	TEST_CHECK(time_now() - start < seconds(3));
	TEST_CHECK(time_now() - start > seconds(2));
}

int test_main()
{
	using namespace libtorrent;
	using namespace boost::filesystem;

	// in case the previous run was terminated
	try { remove_all("./tmp1_swarm"); } catch (std::exception&) {}
	try { remove_all("./tmp2_swarm"); } catch (std::exception&) {}
	try { remove_all("./tmp3_swarm"); } catch (std::exception&) {}

	test_swarm();

	test_sleep(2000);
	TEST_CHECK(!exists("./tmp1_swarm/temporary"));
	TEST_CHECK(!exists("./tmp2_swarm/temporary"));
	TEST_CHECK(!exists("./tmp3_swarm/temporary"));

	remove_all("./tmp1_swarm");
	remove_all("./tmp2_swarm");
	remove_all("./tmp3_swarm");

	return 0;
}

<|endoftext|>


<commit_before>#include "ChannelFilterEditorDialog.hpp"

#include "controllers/filters/parser/FilterParser.hpp"

namespace chatterino {

namespace {
    const QStringList friendlyBinaryOps = {
        "and", "or",       "+",           "-",         "*",        "/",
        "%",   "equals",   "not equals",  "<",         ">",        "<=",
        ">=",  "contains", "starts with", "ends with", "(nothing)"};
    const QStringList realBinaryOps = {
        "&&", "||",       "+",          "-",        "*", "/",
        "%",  "==",       "!=",         "<",        ">", "<=",
        ">=", "contains", "startswith", "endswith", ""};
}  // namespace

ChannelFilterEditorDialog::ChannelFilterEditorDialog(QWidget *parent)
    : QDialog(parent)
{
    auto vbox = new QVBoxLayout(this);
    auto filterVbox = new QVBoxLayout;
    auto buttonBox = new QHBoxLayout;
    auto okButton = new QPushButton("OK");
    auto cancelButton = new QPushButton("Cancel");

    okButton->setDefault(true);
    cancelButton->setDefault(false);

    auto helpLabel =
        new QLabel(QString("variable help")
                       .arg("https://wiki.chatterino.com/Filters/#variables"));
    helpLabel->setOpenExternalLinks(true);

    buttonBox->addWidget(helpLabel);
    buttonBox->addStretch(1);
    buttonBox->addWidget(cancelButton);
    buttonBox->addWidget(okButton);

    QObject::connect(okButton, &QAbstractButton::clicked, [this] {
        this->accept();
        this->close();
    });
    QObject::connect(cancelButton, &QAbstractButton::clicked, [this] {
        this->reject();
        this->close();
    });

    this->setWindowFlags(
        (this->windowFlags() & ~(Qt::WindowContextHelpButtonHint)) |
        Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint);
    this->setWindowTitle("Channel Filter Creator");

    auto titleInput = new QLineEdit;
    titleInput->setPlaceholderText("Filter name");
    titleInput->setText("My filter");

    this->titleInput_ = titleInput;
    filterVbox->addWidget(titleInput);

    auto left = new ChannelFilterEditorDialog::ValueSpecifier;
    auto right = new ChannelFilterEditorDialog::ValueSpecifier;
    auto exp =
        new ChannelFilterEditorDialog::BinaryOperationSpecifier(left, right);

    this->expressionSpecifier_ = exp;
    filterVbox->addLayout(exp->layout());
    vbox->addLayout(filterVbox);
    vbox->addLayout(buttonBox);

    // setup default values
    left->setType("Variable");
    left->setValue("message.content");
    exp->setOperation("contains");
    right->setType("Text");
    right->setValue("hello");
}

const QString ChannelFilterEditorDialog::getFilter() const
{
    return this->expressionSpecifier_->expressionText();
}

const QString ChannelFilterEditorDialog::getTitle() const
{
    return this->titleInput_->text();
}

ChannelFilterEditorDialog::ValueSpecifier::ValueSpecifier()
{
    this->typeCombo_ = new QComboBox;
    this->varCombo_ = new QComboBox;
    this->valueInput_ = new QLineEdit;
    this->layout_ = new QHBoxLayout;

    this->typeCombo_->insertItems(0, {"Text", "Number", "Variable"});
    this->varCombo_->insertItems(0, filterparser::validIdentifiersMap.values());

    this->layout_->addWidget(this->typeCombo_);
    this->layout_->addWidget(this->varCombo_, 1);
    this->layout_->addWidget(this->valueInput_, 1);
    this->layout_->setContentsMargins(5, 5, 5, 5);

    QObject::connect(  //
        this->typeCombo_, QOverload<int>::of(&QComboBox::currentIndexChanged),
        [this](int index) {
            const auto isNumber = (index == 1);
            const auto isVariable = (index == 2);

            this->valueInput_->setVisible(!isVariable);
            this->varCombo_->setVisible(isVariable);
            this->valueInput_->setValidator(
                isNumber ? (new QIntValidator(0, INT_MAX)) : nullptr);

            this->valueInput_->clear();
        });

    this->varCombo_->hide();
    this->typeCombo_->setCurrentIndex(0);
}

void ChannelFilterEditorDialog::ValueSpecifier::setEnabled(bool enabled)
{
    this->typeCombo_->setEnabled(enabled);
    this->varCombo_->setEnabled(enabled);
    this->valueInput_->setEnabled(enabled);
}

void ChannelFilterEditorDialog::ValueSpecifier::setType(const QString &type)
{
    this->typeCombo_->setCurrentText(type);
}

void ChannelFilterEditorDialog::ValueSpecifier::setValue(const QString &value)
{
    if (this->typeCombo_->currentIndex() == 2)
    {
        this->varCombo_->setCurrentText(
            filterparser::validIdentifiersMap.value(value));
    }
    else
    {
        this->valueInput_->setText(value);
    }
}

QLayout *ChannelFilterEditorDialog::ValueSpecifier::layout() const
{
    return this->layout_;
}

QString ChannelFilterEditorDialog::ValueSpecifier::expressionText()
{
    switch (this->typeCombo_->currentIndex())
    {
        case 0:  // text
            return QString("\"%1\"").arg(
                this->valueInput_->text().replace("\"", "\\\""));
        case 1:  // number
            return this->valueInput_->text();
        case 2:  // variable
            return filterparser::validIdentifiersMap.key(
                this->varCombo_->currentText());
        default:
            return "";
    }
}

ChannelFilterEditorDialog::BinaryOperationSpecifier::BinaryOperationSpecifier(
    ExpressionSpecifier *left, ExpressionSpecifier *right)
    : left_(left)
    , right_(right)
{
    this->opCombo_ = new QComboBox;
    this->layout_ = new QVBoxLayout;

    this->opCombo_->insertItems(0, friendlyBinaryOps);

    this->layout_->addLayout(this->left_->layout());
    this->layout_->addWidget(this->opCombo_);
    this->layout_->addLayout(this->right_->layout());
    this->layout_->setContentsMargins(5, 5, 5, 5);

    QObject::connect(  //
        this->opCombo_, QOverload<int>::of(&QComboBox::currentIndexChanged),
        [this](int index) {
            // disable if set to "(nothing)"
            this->right_->setEnabled(!realBinaryOps.at(index).isEmpty());
        });
}

void ChannelFilterEditorDialog::BinaryOperationSpecifier::setEnabled(
    bool enabled)
{
    this->opCombo_->setEnabled(enabled);
    this->left_->setEnabled(enabled);
    this->right_->setEnabled(enabled);
}

void ChannelFilterEditorDialog::BinaryOperationSpecifier::setOperation(
    const QString &op)
{
    this->opCombo_->setCurrentText(op);
}

QLayout *ChannelFilterEditorDialog::BinaryOperationSpecifier::layout() const
{
    return this->layout_;
}

QString ChannelFilterEditorDialog::BinaryOperationSpecifier::expressionText()
{
    QString opText = realBinaryOps.at(this->opCombo_->currentIndex());
    if (opText.isEmpty())
    {
        return this->left_->expressionText();
    }

    return QString("(%1) %2 (%3)")
        .arg(this->left_->expressionText())
        .arg(opText)
        .arg(this->right_->expressionText());
}

}  // namespace chatterino
<commit_msg>renamed filter dropdown items<commit_after>#include "ChannelFilterEditorDialog.hpp"

#include "controllers/filters/parser/FilterParser.hpp"

namespace chatterino {

namespace {
    const QStringList friendlyBinaryOps = {
        "and", "or",       "+",           "-",         "*",        "/",
        "%",   "equals",   "not equals",  "<",         ">",        "<=",
        ">=",  "contains", "starts with", "ends with", "(nothing)"};
    const QStringList realBinaryOps = {
        "&&", "||",       "+",          "-",        "*", "/",
        "%",  "==",       "!=",         "<",        ">", "<=",
        ">=", "contains", "startswith", "endswith", ""};
}  // namespace

ChannelFilterEditorDialog::ChannelFilterEditorDialog(QWidget *parent)
    : QDialog(parent)
{
    auto vbox = new QVBoxLayout(this);
    auto filterVbox = new QVBoxLayout;
    auto buttonBox = new QHBoxLayout;
    auto okButton = new QPushButton("OK");
    auto cancelButton = new QPushButton("Cancel");

    okButton->setDefault(true);
    cancelButton->setDefault(false);

    auto helpLabel =
        new QLabel(QString("variable help")
                       .arg("https://wiki.chatterino.com/Filters/#variables"));
    helpLabel->setOpenExternalLinks(true);

    buttonBox->addWidget(helpLabel);
    buttonBox->addStretch(1);
    buttonBox->addWidget(cancelButton);
    buttonBox->addWidget(okButton);

    QObject::connect(okButton, &QAbstractButton::clicked, [this] {
        this->accept();
        this->close();
    });
    QObject::connect(cancelButton, &QAbstractButton::clicked, [this] {
        this->reject();
        this->close();
    });

    this->setWindowFlags(
        (this->windowFlags() & ~(Qt::WindowContextHelpButtonHint)) |
        Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint);
    this->setWindowTitle("Channel Filter Creator");

    auto titleInput = new QLineEdit;
    titleInput->setPlaceholderText("Filter name");
    titleInput->setText("My filter");

    this->titleInput_ = titleInput;
    filterVbox->addWidget(titleInput);

    auto left = new ChannelFilterEditorDialog::ValueSpecifier;
    auto right = new ChannelFilterEditorDialog::ValueSpecifier;
    auto exp =
        new ChannelFilterEditorDialog::BinaryOperationSpecifier(left, right);

    this->expressionSpecifier_ = exp;
    filterVbox->addLayout(exp->layout());
    vbox->addLayout(filterVbox);
    vbox->addLayout(buttonBox);

    // setup default values
    left->setType("Variable");
    left->setValue("message.content");
    exp->setOperation("contains");
    right->setType("Text");
    right->setValue("hello");
}

const QString ChannelFilterEditorDialog::getFilter() const
{
    return this->expressionSpecifier_->expressionText();
}

const QString ChannelFilterEditorDialog::getTitle() const
{
    return this->titleInput_->text();
}

ChannelFilterEditorDialog::ValueSpecifier::ValueSpecifier()
{
    this->typeCombo_ = new QComboBox;
    this->varCombo_ = new QComboBox;
    this->valueInput_ = new QLineEdit;
    this->layout_ = new QHBoxLayout;

    this->typeCombo_->insertItems(
        0, {"Variable", "Constant Text", "Constant Number"});
    this->varCombo_->insertItems(0, filterparser::validIdentifiersMap.values());

    this->layout_->addWidget(this->typeCombo_);
    this->layout_->addWidget(this->varCombo_, 1);
    this->layout_->addWidget(this->valueInput_, 1);
    this->layout_->setContentsMargins(5, 5, 5, 5);

    QObject::connect(  //
        this->typeCombo_, QOverload<int>::of(&QComboBox::currentIndexChanged),
        [this](int index) {
            const auto isNumber = (index == 1);
            const auto isVariable = (index == 2);

            this->valueInput_->setVisible(!isVariable);
            this->varCombo_->setVisible(isVariable);
            this->valueInput_->setValidator(
                isNumber ? (new QIntValidator(0, INT_MAX)) : nullptr);

            this->valueInput_->clear();
        });

    this->varCombo_->hide();
    this->typeCombo_->setCurrentIndex(0);
}

void ChannelFilterEditorDialog::ValueSpecifier::setEnabled(bool enabled)
{
    this->typeCombo_->setEnabled(enabled);
    this->varCombo_->setEnabled(enabled);
    this->valueInput_->setEnabled(enabled);
}

void ChannelFilterEditorDialog::ValueSpecifier::setType(const QString &type)
{
    this->typeCombo_->setCurrentText(type);
}

void ChannelFilterEditorDialog::ValueSpecifier::setValue(const QString &value)
{
    if (this->typeCombo_->currentIndex() == 2)
    {
        this->varCombo_->setCurrentText(
            filterparser::validIdentifiersMap.value(value));
    }
    else
    {
        this->valueInput_->setText(value);
    }
}

QLayout *ChannelFilterEditorDialog::ValueSpecifier::layout() const
{
    return this->layout_;
}

QString ChannelFilterEditorDialog::ValueSpecifier::expressionText()
{
    switch (this->typeCombo_->currentIndex())
    {
        case 0:  // text
            return QString("\"%1\"").arg(
                this->valueInput_->text().replace("\"", "\\\""));
        case 1:  // number
            return this->valueInput_->text();
        case 2:  // variable
            return filterparser::validIdentifiersMap.key(
                this->varCombo_->currentText());
        default:
            return "";
    }
}

ChannelFilterEditorDialog::BinaryOperationSpecifier::BinaryOperationSpecifier(
    ExpressionSpecifier *left, ExpressionSpecifier *right)
    : left_(left)
    , right_(right)
{
    this->opCombo_ = new QComboBox;
    this->layout_ = new QVBoxLayout;

    this->opCombo_->insertItems(0, friendlyBinaryOps);

    this->layout_->addLayout(this->left_->layout());
    this->layout_->addWidget(this->opCombo_);
    this->layout_->addLayout(this->right_->layout());
    this->layout_->setContentsMargins(5, 5, 5, 5);

    QObject::connect(  //
        this->opCombo_, QOverload<int>::of(&QComboBox::currentIndexChanged),
        [this](int index) {
            // disable if set to "(nothing)"
            this->right_->setEnabled(!realBinaryOps.at(index).isEmpty());
        });
}

void ChannelFilterEditorDialog::BinaryOperationSpecifier::setEnabled(
    bool enabled)
{
    this->opCombo_->setEnabled(enabled);
    this->left_->setEnabled(enabled);
    this->right_->setEnabled(enabled);
}

void ChannelFilterEditorDialog::BinaryOperationSpecifier::setOperation(
    const QString &op)
{
    this->opCombo_->setCurrentText(op);
}

QLayout *ChannelFilterEditorDialog::BinaryOperationSpecifier::layout() const
{
    return this->layout_;
}

QString ChannelFilterEditorDialog::BinaryOperationSpecifier::expressionText()
{
    QString opText = realBinaryOps.at(this->opCombo_->currentIndex());
    if (opText.isEmpty())
    {
        return this->left_->expressionText();
    }

    return QString("(%1) %2 (%3)")
        .arg(this->left_->expressionText())
        .arg(opText)
        .arg(this->right_->expressionText());
}

}  // namespace chatterino
<|endoftext|>


<commit_before>#ifndef VIENNAGRID_STORAGE_HANDLE_HPP
#define VIENNAGRID_STORAGE_HANDLE_HPP

/* =======================================================================
   Copyright (c) 2011-2013, Institute for Microelectronics,
                            Institute for Analysis and Scientific Computing,
                            TU Wien.

                            -----------------
                     ViennaGrid - The Vienna Grid Library
                            -----------------

   License:      MIT (X11), see file LICENSE in the base directory
======================================================================= */

#include <iterator>
#include <vector>
#include <map>
#include "viennagrid/meta/typemap.hpp"
#include "viennagrid/storage/id.hpp"
#include "viennagrid/storage/forwards.hpp"

namespace viennagrid
{
    namespace storage
    {

        namespace handle
        {

            namespace result_of
            {

                template<typename base_container_type, typename handle_tag>
                struct handle_type
                {};

                template<typename base_container_type>
                struct handle_type<base_container_type, no_handle_tag>
                {
                    typedef viennagrid::meta::null_type type;
                };

                template<typename base_container_type>
                struct handle_type<base_container_type, pointer_handle_tag>
                {
                    typedef typename base_container_type::pointer type;
                };

                template<typename base_container_type>
                struct handle_type<base_container_type, iterator_handle_tag>
                {
                    typedef typename base_container_type::iterator type;
                };

                template<typename base_container_type>
                struct handle_type<base_container_type, id_handle_tag>
                {
                    typedef typename base_container_type::value_type::id_type type;
                };

                template<typename base_container_type, typename handle_tag>
                struct const_handle_type
                {};

                template<typename base_container_type>
                struct const_handle_type<base_container_type, no_handle_tag>
                {
                    typedef viennagrid::meta::null_type type;
                };

                template<typename base_container_type>
                struct const_handle_type<base_container_type, pointer_handle_tag>
                {
                    typedef typename base_container_type::const_pointer type;
                };

                template<typename base_container_type>
                struct const_handle_type<base_container_type, iterator_handle_tag>
                {
                    typedef typename base_container_type::const_iterator type;
                };

                template<typename base_container_type>
                struct const_handle_type<base_container_type, id_handle_tag>
                {
                    typedef typename base_container_type::value_type::const_id_type type;
                };

                // default = iterator
                template<typename handle_type>
                struct value_type
                {
                    typedef typename viennagrid::meta::IF<
                        viennagrid::meta::is_const_iterator<handle_type>::value,
                        const typename handle_type::value_type,
                        typename handle_type::value_type
                    >::type type;
                };

                // pointer
                template<typename value_type_>
                struct value_type< value_type_ * >
                {
                    typedef value_type_ type;
                };

                template<typename value_type_>
                struct value_type< const value_type_ * >
                {
                    typedef const value_type_ type;
                };

                // id
                template<typename value_type_, typename base_id_type_>
                struct value_type< smart_id_t<value_type_, base_id_type_> >
                {
                    typedef value_type_ type;
                };

                template<typename value_type_, typename base_id_type_>
                struct value_type< smart_id_t<const value_type_, base_id_type_> >
                {
                    typedef const value_type_ type;
                };

                // default = iterator
                template<typename handle_type>
                struct handle_tag
                {
                    typedef iterator_handle_tag type;
                };

                // no handle
                template<>
                struct handle_tag<viennagrid::meta::null_type>
                {
                    typedef no_handle_tag type;
                };

                // pointer
                template<typename value_type>
                struct handle_tag<value_type *>
                {
                    typedef pointer_handle_tag type;
                };

                // id
                template<typename value_type_, typename base_id_type_>
                struct handle_tag< smart_id_t<value_type_, base_id_type_> >
                {
                    typedef id_handle_tag type;
                };

            }

            template<typename container_type, typename handle_type>
            void set_handle_invalid( container_type const &, handle_type & handle, pointer_handle_tag )
            { handle = NULL; }

            template<typename container_type, typename handle_type>
            void set_handle_invalid( container_type & container, handle_type & handle, iterator_handle_tag )
            { handle = container.end(); }

            template<typename container_type, typename handle_type>
            void set_handle_invalid( container_type const & container, handle_type & handle, iterator_handle_tag )
            { handle = container.end(); }

            template<typename container_type, typename handle_type>
            void set_handle_invalid( container_type & container, handle_type & handle, id_handle_tag )
            { handle = handle_type(); }

            template<typename container_type, typename handle_type>
            void set_handle_invalid( container_type const & container, handle_type & handle, id_handle_tag )
            { handle = handle_type(); }

            template<typename container_type, typename handle_type>
            void set_handle_invalid( container_type const & container, handle_type & handle )
            { set_handle_invalid(container, handle, typename result_of::handle_tag<handle_type>::type()); }

            template<typename container_type, typename handle_type>
            void set_handle_invalid( container_type & container, handle_type & handle )
            { set_handle_invalid(container, handle, typename result_of::handle_tag<handle_type>::type()); }

            template<typename container_type, typename handle_type>
            bool is_handle_invalid( container_type const & container, handle_type handle )
            {
              handle_type tmp;
              set_handle_invalid(container, tmp);
              return handle == tmp;
            }

            template<typename container_type, typename handle_type>
            bool is_handle_invalid( container_type & container, handle_type handle )
            {
              handle_type tmp;
              set_handle_invalid(container, tmp);
              return handle == tmp;
            }

            // Pointer handle
            template<typename container_type, typename handle_type>
            typename result_of::value_type<handle_type>::type & dereference_handle( container_type &, handle_type handle, pointer_handle_tag )
            { return *handle; }

            template<typename container_type, typename handle_type>
            typename result_of::value_type<handle_type>::type const & dereference_handle( container_type const &, handle_type handle, pointer_handle_tag )
            { return *handle; }

            // Iterator handle
            template<typename container_type, typename handle_type>
            typename result_of::value_type<handle_type>::type & dereference_handle( container_type &, handle_type handle, iterator_handle_tag )
            { return *handle; }

            template<typename container_type, typename handle_type>
            typename result_of::value_type<handle_type>::type const & dereference_handle( container_type const &, handle_type handle, iterator_handle_tag )
            { return *handle; }

            // ID handle
            template<typename container_type, typename handle_type>
            typename result_of::value_type<handle_type>::type & dereference_handle( container_type & container, handle_type handle, id_handle_tag )
            {
                typedef typename result_of::value_type<handle_type>::type value_type;
                typedef typename storage::result_of::id<value_type>::type id_type;

                return *std::find_if(
                    container.begin(),
                    container.end(),
                    id_compare<id_type>(handle)
                );
            }

            template<typename container_type, typename handle_type>
            typename result_of::value_type<handle_type>::type const & dereference_handle( container_type const & container, handle_type handle, id_handle_tag )
            {
                typedef typename result_of::value_type<handle_type>::type value_type;
                typedef typename storage::result_of::id<value_type>::type id_type;

                return *std::find_if(
                    container.begin(),
                    container.end(),
                    id_compare<id_type>(handle)
                );
            }

            template<typename container_type, typename value_type, typename handle_tag>
            struct handle_helper;

            template<typename container_type, typename value_type>
            struct handle_helper<container_type, value_type, no_handle_tag>
            {
                static typename result_of::handle_type<container_type, no_handle_tag>::type handle( container_type &, value_type & )
                { return typename result_of::handle_type<container_type, no_handle_tag>::type(); }

                static typename result_of::const_handle_type<container_type, no_handle_tag>::type handle( container_type const &, value_type const & )
                { return typename result_of::handle_type<container_type, no_handle_tag>::type(); }
            };

            template<typename container_type, typename value_type>
            struct handle_helper<container_type, value_type, iterator_handle_tag>
            {
                static typename result_of::handle_type<container_type, iterator_handle_tag>::type handle( container_type & container, value_type & value )
                {
                    for (typename container_type::iterator it = container.begin(); it != container.end(); ++it)
                        if ( &(*it) == &value ) return it;
                    return container.end();
                }

                static typename result_of::const_handle_type<container_type, iterator_handle_tag>::type handle( container_type const & container, value_type const & value )
                {
                    for (typename container_type::const_iterator it = container.begin(); it != container.end(); ++it)
                        if ( &(*it) == &value ) return it;
                    return container.end();
                }
            };

            template<typename container_type, typename value_type>
            struct handle_helper<container_type, value_type, pointer_handle_tag>
            {
                static typename result_of::handle_type<container_type, pointer_handle_tag>::type handle( container_type &, value_type & value )
                { return &value; }

                static typename result_of::const_handle_type<container_type, pointer_handle_tag>::type handle( container_type const &, value_type const & value )
                { return &value; }
            };

            template<typename container_type, typename value_type>
            struct handle_helper<container_type, value_type, id_handle_tag>
            {
                static typename result_of::handle_type<container_type, id_handle_tag>::type handle( container_type &, value_type & value )
                { return value.id(); }

                static typename result_of::const_handle_type<container_type, id_handle_tag>::type handle( container_type const &, value_type const & value )
                { return value.id(); }
            };

            template<typename container_type, typename value_type, typename handle_tag>
            typename result_of::handle_type<container_type, handle_tag>::type handle( container_type & container, value_type & value, handle_tag )
            { return handle_helper<container_type, value_type, handle_tag>::handle( container,value ); }

            template<typename container_type, typename value_type, typename handle_tag>
            typename result_of::const_handle_type<container_type, handle_tag>::type handle( container_type const & container, value_type const & value, handle_tag )
            { return handle_helper<container_type, value_type, handle_tag>::handle( container,value ); }

        }

    }
}

#endif
<commit_msg>Fixed warnings<commit_after>#ifndef VIENNAGRID_STORAGE_HANDLE_HPP
#define VIENNAGRID_STORAGE_HANDLE_HPP

/* =======================================================================
   Copyright (c) 2011-2013, Institute for Microelectronics,
                            Institute for Analysis and Scientific Computing,
                            TU Wien.

                            -----------------
                     ViennaGrid - The Vienna Grid Library
                            -----------------

   License:      MIT (X11), see file LICENSE in the base directory
======================================================================= */

#include <iterator>
#include <vector>
#include <map>
#include "viennagrid/meta/typemap.hpp"
#include "viennagrid/storage/id.hpp"
#include "viennagrid/storage/forwards.hpp"

namespace viennagrid
{
    namespace storage
    {

        namespace handle
        {

            namespace result_of
            {

                template<typename base_container_type, typename handle_tag>
                struct handle_type
                {};

                template<typename base_container_type>
                struct handle_type<base_container_type, no_handle_tag>
                {
                    typedef viennagrid::meta::null_type type;
                };

                template<typename base_container_type>
                struct handle_type<base_container_type, pointer_handle_tag>
                {
                    typedef typename base_container_type::pointer type;
                };

                template<typename base_container_type>
                struct handle_type<base_container_type, iterator_handle_tag>
                {
                    typedef typename base_container_type::iterator type;
                };

                template<typename base_container_type>
                struct handle_type<base_container_type, id_handle_tag>
                {
                    typedef typename base_container_type::value_type::id_type type;
                };

                template<typename base_container_type, typename handle_tag>
                struct const_handle_type
                {};

                template<typename base_container_type>
                struct const_handle_type<base_container_type, no_handle_tag>
                {
                    typedef viennagrid::meta::null_type type;
                };

                template<typename base_container_type>
                struct const_handle_type<base_container_type, pointer_handle_tag>
                {
                    typedef typename base_container_type::const_pointer type;
                };

                template<typename base_container_type>
                struct const_handle_type<base_container_type, iterator_handle_tag>
                {
                    typedef typename base_container_type::const_iterator type;
                };

                template<typename base_container_type>
                struct const_handle_type<base_container_type, id_handle_tag>
                {
                    typedef typename base_container_type::value_type::const_id_type type;
                };

                // default = iterator
                template<typename handle_type>
                struct value_type
                {
                    typedef typename viennagrid::meta::IF<
                        viennagrid::meta::is_const_iterator<handle_type>::value,
                        const typename handle_type::value_type,
                        typename handle_type::value_type
                    >::type type;
                };

                // pointer
                template<typename value_type_>
                struct value_type< value_type_ * >
                {
                    typedef value_type_ type;
                };

                template<typename value_type_>
                struct value_type< const value_type_ * >
                {
                    typedef const value_type_ type;
                };

                // id
                template<typename value_type_, typename base_id_type_>
                struct value_type< smart_id_t<value_type_, base_id_type_> >
                {
                    typedef value_type_ type;
                };

                template<typename value_type_, typename base_id_type_>
                struct value_type< smart_id_t<const value_type_, base_id_type_> >
                {
                    typedef const value_type_ type;
                };

                // default = iterator
                template<typename handle_type>
                struct handle_tag
                {
                    typedef iterator_handle_tag type;
                };

                // no handle
                template<>
                struct handle_tag<viennagrid::meta::null_type>
                {
                    typedef no_handle_tag type;
                };

                // pointer
                template<typename value_type>
                struct handle_tag<value_type *>
                {
                    typedef pointer_handle_tag type;
                };

                // id
                template<typename value_type_, typename base_id_type_>
                struct handle_tag< smart_id_t<value_type_, base_id_type_> >
                {
                    typedef id_handle_tag type;
                };

            }

            template<typename container_type, typename handle_type>
            void set_handle_invalid( container_type const &, handle_type & handle, pointer_handle_tag )
            { handle = NULL; }

            template<typename container_type, typename handle_type>
            void set_handle_invalid( container_type & container, handle_type & handle, iterator_handle_tag )
            { handle = container.end(); }

            template<typename container_type, typename handle_type>
            void set_handle_invalid( container_type const & container, handle_type & handle, iterator_handle_tag )
            { handle = container.end(); }

            template<typename container_type, typename handle_type>
            void set_handle_invalid( container_type & /*container*/, handle_type & handle, id_handle_tag )
            { handle = handle_type(); }

            template<typename container_type, typename handle_type>
            void set_handle_invalid( container_type const & /*container*/, handle_type & handle, id_handle_tag )
            { handle = handle_type(); }

            template<typename container_type, typename handle_type>
            void set_handle_invalid( container_type const & container, handle_type & handle )
            { set_handle_invalid(container, handle, typename result_of::handle_tag<handle_type>::type()); }

            template<typename container_type, typename handle_type>
            void set_handle_invalid( container_type & container, handle_type & handle )
            { set_handle_invalid(container, handle, typename result_of::handle_tag<handle_type>::type()); }

            template<typename container_type, typename handle_type>
            bool is_handle_invalid( container_type const & container, handle_type handle )
            {
              handle_type tmp;
              set_handle_invalid(container, tmp);
              return handle == tmp;
            }

            template<typename container_type, typename handle_type>
            bool is_handle_invalid( container_type & container, handle_type handle )
            {
              handle_type tmp;
              set_handle_invalid(container, tmp);
              return handle == tmp;
            }

            // Pointer handle
            template<typename container_type, typename handle_type>
            typename result_of::value_type<handle_type>::type & dereference_handle( container_type &, handle_type handle, pointer_handle_tag )
            { return *handle; }

            template<typename container_type, typename handle_type>
            typename result_of::value_type<handle_type>::type const & dereference_handle( container_type const &, handle_type handle, pointer_handle_tag )
            { return *handle; }

            // Iterator handle
            template<typename container_type, typename handle_type>
            typename result_of::value_type<handle_type>::type & dereference_handle( container_type &, handle_type handle, iterator_handle_tag )
            { return *handle; }

            template<typename container_type, typename handle_type>
            typename result_of::value_type<handle_type>::type const & dereference_handle( container_type const &, handle_type handle, iterator_handle_tag )
            { return *handle; }

            // ID handle
            template<typename container_type, typename handle_type>
            typename result_of::value_type<handle_type>::type & dereference_handle( container_type & container, handle_type handle, id_handle_tag )
            {
                typedef typename result_of::value_type<handle_type>::type value_type;
                typedef typename storage::result_of::id<value_type>::type id_type;

                return *std::find_if(
                    container.begin(),
                    container.end(),
                    id_compare<id_type>(handle)
                );
            }

            template<typename container_type, typename handle_type>
            typename result_of::value_type<handle_type>::type const & dereference_handle( container_type const & container, handle_type handle, id_handle_tag )
            {
                typedef typename result_of::value_type<handle_type>::type value_type;
                typedef typename storage::result_of::id<value_type>::type id_type;

                return *std::find_if(
                    container.begin(),
                    container.end(),
                    id_compare<id_type>(handle)
                );
            }

            template<typename container_type, typename value_type, typename handle_tag>
            struct handle_helper;

            template<typename container_type, typename value_type>
            struct handle_helper<container_type, value_type, no_handle_tag>
            {
                static typename result_of::handle_type<container_type, no_handle_tag>::type handle( container_type &, value_type & )
                { return typename result_of::handle_type<container_type, no_handle_tag>::type(); }

                static typename result_of::const_handle_type<container_type, no_handle_tag>::type handle( container_type const &, value_type const & )
                { return typename result_of::handle_type<container_type, no_handle_tag>::type(); }
            };

            template<typename container_type, typename value_type>
            struct handle_helper<container_type, value_type, iterator_handle_tag>
            {
                static typename result_of::handle_type<container_type, iterator_handle_tag>::type handle( container_type & container, value_type & value )
                {
                    for (typename container_type::iterator it = container.begin(); it != container.end(); ++it)
                        if ( &(*it) == &value ) return it;
                    return container.end();
                }

                static typename result_of::const_handle_type<container_type, iterator_handle_tag>::type handle( container_type const & container, value_type const & value )
                {
                    for (typename container_type::const_iterator it = container.begin(); it != container.end(); ++it)
                        if ( &(*it) == &value ) return it;
                    return container.end();
                }
            };

            template<typename container_type, typename value_type>
            struct handle_helper<container_type, value_type, pointer_handle_tag>
            {
                static typename result_of::handle_type<container_type, pointer_handle_tag>::type handle( container_type &, value_type & value )
                { return &value; }

                static typename result_of::const_handle_type<container_type, pointer_handle_tag>::type handle( container_type const &, value_type const & value )
                { return &value; }
            };

            template<typename container_type, typename value_type>
            struct handle_helper<container_type, value_type, id_handle_tag>
            {
                static typename result_of::handle_type<container_type, id_handle_tag>::type handle( container_type &, value_type & value )
                { return value.id(); }

                static typename result_of::const_handle_type<container_type, id_handle_tag>::type handle( container_type const &, value_type const & value )
                { return value.id(); }
            };

            template<typename container_type, typename value_type, typename handle_tag>
            typename result_of::handle_type<container_type, handle_tag>::type handle( container_type & container, value_type & value, handle_tag )
            { return handle_helper<container_type, value_type, handle_tag>::handle( container,value ); }

            template<typename container_type, typename value_type, typename handle_tag>
            typename result_of::const_handle_type<container_type, handle_tag>::type handle( container_type const & container, value_type const & value, handle_tag )
            { return handle_helper<container_type, value_type, handle_tag>::handle( container,value ); }

        }

    }
}

#endif
<|endoftext|>


<commit_before>// Copyright 2010-2012 RethinkDB, all rights reserved.
#ifndef CONCURRENCY_PROMISE_HPP_
#define CONCURRENCY_PROMISE_HPP_

#include "concurrency/cond_var.hpp"

#include "containers/object_buffer.hpp"

/* A promise_t is a condition variable combined with a "return value", of sorts, that
is transmitted to the thing waiting on the condition variable. */

template <class val_t>
class promise_t : public home_thread_mixin_debug_only_t {
public:
    promise_t() { }

    void pulse(const val_t &v) {
        assert_thread();
        value.create(v);
        cond.pulse();
    }

    void pulse_if_not_already_pulsed(const val_t &v) {
        if (!is_pulsed()) {
            pulse(v);
        }
    }

    const val_t &wait() const {
        assert_thread();
        cond.wait_lazily_unordered();
        return *value.get();
    }

    const signal_t *get_ready_signal() const {
        return &cond;
    }

    /* Note that `assert_get_value()` can be called on any thread. */
    val_t assert_get_value() const {
        cond.guarantee_pulsed();
        return *value.get();
    }

    MUST_USE bool try_get_value(val_t *out) const {
        if (is_pulsed()) {
            *out = *value.get();
            return true;
        } else {
            return false;
        }
    }

    bool is_pulsed() const {
        return cond.is_pulsed();
    }

private:
    cond_t cond;
    object_buffer_t<val_t> value;

    DISABLE_COPYING(promise_t);
};

#endif /* CONCURRENCY_PROMISE_HPP_ */
<commit_msg>Avoid template param name clash with val_t.<commit_after>// Copyright 2010-2012 RethinkDB, all rights reserved.
#ifndef CONCURRENCY_PROMISE_HPP_
#define CONCURRENCY_PROMISE_HPP_

#include "concurrency/cond_var.hpp"

#include "containers/object_buffer.hpp"

/* A promise_t is a condition variable combined with a "return value", of sorts, that
is transmitted to the thing waiting on the condition variable. */

template <class value_type>
class promise_t : public home_thread_mixin_debug_only_t {
public:
    promise_t() { }

    void pulse(const value_type &v) {
        assert_thread();
        value.create(v);
        cond.pulse();
    }

    void pulse_if_not_already_pulsed(const value_type &v) {
        if (!is_pulsed()) {
            pulse(v);
        }
    }

    const value_type &wait() const {
        assert_thread();
        cond.wait_lazily_unordered();
        return *value.get();
    }

    const signal_t *get_ready_signal() const {
        return &cond;
    }

    /* Note that `assert_get_value()` can be called on any thread. */
    value_type assert_get_value() const {
        cond.guarantee_pulsed();
        return *value.get();
    }

    MUST_USE bool try_get_value(value_type *out) const {
        if (is_pulsed()) {
            *out = *value.get();
            return true;
        } else {
            return false;
        }
    }

    bool is_pulsed() const {
        return cond.is_pulsed();
    }

private:
    cond_t cond;
    object_buffer_t<value_type> value;

    DISABLE_COPYING(promise_t);
};

#endif /* CONCURRENCY_PROMISE_HPP_ */
<|endoftext|>


<commit_before>/***************************Copyright-DO-NOT-REMOVE-THIS-LINE**
 * CONDOR Copyright Notice
 *
 * See LICENSE.TXT for additional notices and disclaimers.
 *
 * Copyright (c)1990-1998 CONDOR Team, Computer Sciences Department,
 * University of Wisconsin-Madison, Madison, WI.  All Rights Reserved.
 * No use of the CONDOR Software Program Source Code is authorized
 * without the express consent of the CONDOR Team.  For more information
 * contact: CONDOR Team, Attention: Professor Miron Livny,
 * 7367 Computer Sciences, 1210 W. Dayton St., Madison, WI 53706-1685,
 * (608) 262-0856 or miron@cs.wisc.edu.
 *
 * U.S. Government Rights Restrictions: Use, duplication, or disclosure
 * by the U.S. Government is subject to restrictions as set forth in
 * subparagraph (c)(1)(ii) of The Rights in Technical Data and Computer
 * Software clause at DFARS 252.227-7013 or subparagraphs (c)(1) and
 * (2) of Commercial Computer Software-Restricted Rights at 48 CFR
 * 52.227-19, as applicable, CONDOR Team, Attention: Professor Miron
 * Livny, 7367 Computer Sciences, 1210 W. Dayton St., Madison,
 * WI 53706-1685, (608) 262-0856 or miron@cs.wisc.edu.
****************************Copyright-DO-NOT-REMOVE-THIS-LINE**/

#include "image.h"
#include "condor_debug.h"

/*
  Switch to a temporary stack area in the DATA segment, then execute the
  given function.  Note: we save the address of the function in a
  global data location - referencing a value on the stack after the SP
  is moved would be an error.
*/

static void (*SaveFunc)();
static jmp_buf Env;

// The stack is to be aligned on an 8-byte boundary on some
// machines. This is required so that double's can be pushed
// on the stack without any alignment problems.

static const int KILO=1024;
static const int TmpStackSize = sizeof(char)*512*KILO/sizeof(double);

#ifdef COMPRESS_CKPT
static double	*TmpStack;
#else
static double	TmpStack[TmpStackSize];
#endif

/*
Suppose we use up all the stack space while in ExecuteOnTmpStk.
This causes all sorts of crazy errors in other parts of the program.
So, we will put a marker at the end of the stack to catch this case.

After some experimentation, it seems that this doesn't catch all
stack overruns.  Still, it can't hurt...
*/

static const long OverrunFlag = 0xdeadbeef;

/* This function returns the address at which the marker should go. */

static long *GetOverrunPos()
{
	if( StackGrowsDown() ) {
		return (long*)TmpStack;
	} else {
		return (long*)(TmpStack+TmpStackSize-2);
	}
}

void ExecuteOnTmpStk( void (*func)() )
{
	jmp_buf	env;
	SaveFunc = func;

	/*
	condor_malloc gives us memory that is not saved across
	checkpoint/restart -- the mechanism is always present,
	but only properly initialized when compression is enabled.
	*/

	#ifdef COMPRESS_CKPT
	TmpStack = (double *)condor_malloc(TmpStackSize*sizeof(double));
	if(!TmpStack) EXCEPT("Unable to allocate temporary stack!");
	#endif

	*(GetOverrunPos()) = OverrunFlag;

	if( SETJMP(env) == 0 ) {
			// First time through - move SP
		if( StackGrowsDown() ) {
			// when StackGrowsDown, set the stack pointer inside the
			// TmpStack and give some padding -- previously values
			// were getting overwritten above the stack -- Jim B. 2/7/96
			JMP_BUF_SP(env) = (long)(TmpStack + TmpStackSize - 2);
		} else {
			JMP_BUF_SP(env) = (long)TmpStack;
		}
		LONGJMP( env, 1 );
	} else {
			// Second time through - call the function
		SaveFunc();
	}

	// Will we ever get here?

	#ifdef COMPRESS_CKPT
	condor_free(TmpStack);
	#endif

	if( *(GetOverrunPos()) != OverrunFlag ) {
		EXCEPT("Stack overrun in ExecuteOnTmpStack.");
	}
}
<commit_msg>Fixed some type cast problem in assigning the tmpstack to the jmp buf element, but ONLY under IRIX as I need to study the ramifications of those casts on other platforms.<commit_after>/***************************Copyright-DO-NOT-REMOVE-THIS-LINE**
 * CONDOR Copyright Notice
 *
 * See LICENSE.TXT for additional notices and disclaimers.
 *
 * Copyright (c)1990-1998 CONDOR Team, Computer Sciences Department,
 * University of Wisconsin-Madison, Madison, WI.  All Rights Reserved.
 * No use of the CONDOR Software Program Source Code is authorized
 * without the express consent of the CONDOR Team.  For more information
 * contact: CONDOR Team, Attention: Professor Miron Livny,
 * 7367 Computer Sciences, 1210 W. Dayton St., Madison, WI 53706-1685,
 * (608) 262-0856 or miron@cs.wisc.edu.
 *
 * U.S. Government Rights Restrictions: Use, duplication, or disclosure
 * by the U.S. Government is subject to restrictions as set forth in
 * subparagraph (c)(1)(ii) of The Rights in Technical Data and Computer
 * Software clause at DFARS 252.227-7013 or subparagraphs (c)(1) and
 * (2) of Commercial Computer Software-Restricted Rights at 48 CFR
 * 52.227-19, as applicable, CONDOR Team, Attention: Professor Miron
 * Livny, 7367 Computer Sciences, 1210 W. Dayton St., Madison,
 * WI 53706-1685, (608) 262-0856 or miron@cs.wisc.edu.
****************************Copyright-DO-NOT-REMOVE-THIS-LINE**/

#include "image.h"
#include "condor_debug.h"

/*
  Switch to a temporary stack area in the DATA segment, then execute the
  given function.  Note: we save the address of the function in a
  global data location - referencing a value on the stack after the SP
  is moved would be an error.
*/

static void (*SaveFunc)();
static jmp_buf Env;

// The stack is to be aligned on an 8-byte boundary on some
// machines. This is required so that double's can be pushed
// on the stack without any alignment problems.

static const int KILO=1024;
static const int TmpStackSize = sizeof(char)*512*KILO/sizeof(double);

#ifdef COMPRESS_CKPT
static double	*TmpStack;
#else
static double	TmpStack[TmpStackSize];
#endif

/*
Suppose we use up all the stack space while in ExecuteOnTmpStk.
This causes all sorts of crazy errors in other parts of the program.
So, we will put a marker at the end of the stack to catch this case.

After some experimentation, it seems that this doesn't catch all
stack overruns.  Still, it can't hurt...
*/

static const long OverrunFlag = 0xdeadbeef;

/* This function returns the address at which the marker should go. */

static long *GetOverrunPos()
{
	if( StackGrowsDown() ) {
		return (long*)TmpStack;
	} else {
		return (long*)(TmpStack+TmpStackSize-2);
	}
}

void ExecuteOnTmpStk( void (*func)() )
{
	jmp_buf	env;
	SaveFunc = func;

	/*
	condor_malloc gives us memory that is not saved across
	checkpoint/restart -- the mechanism is always present,
	but only properly initialized when compression is enabled.
	*/

	#ifdef COMPRESS_CKPT
	TmpStack = (double *)condor_malloc(TmpStackSize*sizeof(double));
	if(!TmpStack) EXCEPT("Unable to allocate temporary stack!");
	#endif

	*(GetOverrunPos()) = OverrunFlag;

	if( SETJMP(env) == 0 ) {
			// First time through - move SP
		if( StackGrowsDown() ) {
			// when StackGrowsDown, set the stack pointer inside the
			// TmpStack and give some padding -- previously values
			// were getting overwritten above the stack -- Jim B. 2/7/96

			/* XXX must explore the ramifications of that long cast on other
				platforms, it breaks under IRIX because pointers are 64 bit.
				-pete 01/19/00 */
#if defined(IRIX)
			JMP_BUF_SP(env) = (TmpStack + TmpStackSize - 2);
#else
			JMP_BUF_SP(env) = (long)(TmpStack + TmpStackSize - 2);
#endif
		} else {
#if defined(IRIX)
			JMP_BUF_SP(env) = TmpStack;
#else
			JMP_BUF_SP(env) = (long)TmpStack;
#endif
		}
		dprintf(D_CKPT, "About to execute on tmpstack.\n");
		LONGJMP( env, 1 );
		dprintf(D_CKPT, "How did I get here after LONGJMP()!?!\n");
	} else {
			// Second time through - call the function
		dprintf(D_CKPT, "Beginning Execution on TmpStack.\n");
		SaveFunc();
	}

	// Will we ever get here?

	#ifdef COMPRESS_CKPT
	condor_free(TmpStack);
	#endif

	if( *(GetOverrunPos()) != OverrunFlag ) {
		EXCEPT("Stack overrun in ExecuteOnTmpStack.");
	}
}
<|endoftext|>


<commit_before>#include <UnitTest++.h>
#include "tightdb.hpp"
#include "tightdb/group_shared.hpp"

// Does not work for windows yet
#ifndef _MSC_VER

using namespace tightdb;

namespace {

TIGHTDB_TABLE_4(TestTableShared,
                first,  Int,
                second, Int,
                third,  Bool,
                fourth, String)

TEST(Shared_Initial)
{
    // Delete old files if there
    remove("test_shared.tdb");
    remove("test_shared.tdb.lock"); // also the info file

    {
        // Create a new shared db
        SharedGroup shared("test_shared.tdb");
        CHECK(shared.is_valid());

        // Verify that new group is empty
        {
            const Group& g1 = shared.begin_read();
            CHECK(g1.is_valid());
            CHECK(g1.is_empty());
            shared.end_read();
        }

#ifdef _DEBUG
        // Also do a basic ringbuffer test
        shared.test_ringbuf();
#endif
    }

    // Verify that lock file was deleted after use
    const int rc = access("test_shared.tdb.lock", F_OK);
    CHECK_EQUAL(-1, rc);
}

} // anonymous namespace

TEST(Shared1)
{
    // Delete old files if there
    remove("test_shared.tdb");
    remove("test_shared.tdb.lock"); // also the info file

    {
        // Create a new shared db
        SharedGroup shared("test_shared.tdb");
        CHECK(shared.is_valid());

        // Create first table in group
        {
            Group& g1 = shared.begin_write();
            TestTableShared::Ref t1 = g1.get_table<TestTableShared>("test");
            t1->add(1, 2, false, "test");
            shared.commit();
        }

        // Open same db again
        SharedGroup shared2("test_shared.tdb");
        CHECK(shared2.is_valid());
        {
            const Group& g2 = shared2.begin_read();

            // Verify that last set of changes are commited
            TestTableShared::ConstRef t2 = g2.get_table<TestTableShared>("test");
            CHECK(t2->size() == 1);
            CHECK_EQUAL(1, t2[0].first);
            CHECK_EQUAL(2, t2[0].second);
            CHECK_EQUAL(false, t2[0].third);
            CHECK_EQUAL("test", (const char*)t2[0].fourth);
            // don't end_read yet

            // Do a new change while stil having current read transaction open
            {
                Group& g1 = shared.begin_write();
                TestTableShared::Ref t1 = g1.get_table<TestTableShared>("test");
                t1->add(2, 3, true, "more test");
                shared.commit();
            }

            // Verify that that the read transaction does not see
            // the change yet (is isolated)
            CHECK(t2->size() == 1);
            CHECK_EQUAL(1, t2[0].first);
            CHECK_EQUAL(2, t2[0].second);
            CHECK_EQUAL(false, t2[0].third);
            CHECK_EQUAL("test", (const char*)t2[0].fourth);

            // Do one more new change while stil having current read transaction open
            // so we know that it does not overwrite data held by
            {
                Group& g1 = shared.begin_write();
                TestTableShared::Ref t1 = g1.get_table<TestTableShared>("test");
                t1->add(0, 1, false, "even more test");
                shared.commit();
            }

            // Verify that that the read transaction does still not see
            // the change yet (is isolated)
            CHECK(t2->size() == 1);
            CHECK_EQUAL(1, t2[0].first);
            CHECK_EQUAL(2, t2[0].second);
            CHECK_EQUAL(false, t2[0].third);
            CHECK_EQUAL("test", (const char*)t2[0].fourth);

            // Close read transaction
            shared2.end_read();
        }

        // Start a new read transaction and verify that it can now see the changes
        {
            const Group& g3 = shared2.begin_read();
            TestTableShared::ConstRef t3 = g3.get_table<TestTableShared>("test");

            CHECK(t3->size() == 3);
            CHECK_EQUAL(1, t3[0].first);
            CHECK_EQUAL(2, t3[0].second);
            CHECK_EQUAL(false, t3[0].third);
            CHECK_EQUAL("test", (const char*)t3[0].fourth);
            CHECK_EQUAL(2, t3[1].first);
            CHECK_EQUAL(3, t3[1].second);
            CHECK_EQUAL(true, t3[1].third);
            CHECK_EQUAL("more test", (const char*)t3[1].fourth);
            CHECK_EQUAL(0, t3[2].first);
            CHECK_EQUAL(1, t3[2].second);
            CHECK_EQUAL(false, t3[2].third);
            CHECK_EQUAL("even more test", (const char*)t3[2].fourth);

            shared2.end_read();
        }
    }

    // Verify that lock file was deleted after use
    const int rc = access("test_shared.tdb.lock", F_OK);
    CHECK_EQUAL(-1, rc);
}

TEST(Shared_rollback)
{
    // Delete old files if there
    remove("test_shared.tdb");
    remove("test_shared.tdb.lock"); // also the info file

    {
        // Create a new shared db
        SharedGroup shared("test_shared.tdb");
        CHECK(shared.is_valid());

        // Create first table in group (but rollback)
        {
            Group& g1 = shared.begin_write();
            TestTableShared::Ref t1 = g1.get_table<TestTableShared>("test");
            t1->add(1, 2, false, "test");
            shared.rollback();
        }

        // Verify that no changes were made
        {
            const Group& g1 = shared.begin_read();
            CHECK_EQUAL(false, g1.has_table("test"));
            shared.end_read();
        }

        // Really create first table in group
        {
            Group& g1 = shared.begin_write();
            TestTableShared::Ref t1 = g1.get_table<TestTableShared>("test");
            t1->add(1, 2, false, "test");
            shared.commit();
        }

        // Verify that the changes were made
        {
            const Group& g1 = shared.begin_read();
            TestTableShared::ConstRef t = g1.get_table<TestTableShared>("test");
            CHECK(t->size() == 1);
            CHECK_EQUAL(1, t[0].first);
            CHECK_EQUAL(2, t[0].second);
            CHECK_EQUAL(false, t[0].third);
            CHECK_EQUAL("test", (const char*)t[0].fourth);
            shared.end_read();
        }

        // Greate more changes (but rollback)
        {
            Group& g1 = shared.begin_write();
            TestTableShared::Ref t1 = g1.get_table<TestTableShared>("test");
            t1->add(0, 0, true, "more test");
            shared.rollback();
        }

        // Verify that no changes were made
        {
            const Group& g1 = shared.begin_read();
            TestTableShared::ConstRef t = g1.get_table<TestTableShared>("test");
            CHECK(t->size() == 1);
            CHECK_EQUAL(1, t[0].first);
            CHECK_EQUAL(2, t[0].second);
            CHECK_EQUAL(false, t[0].third);
            CHECK_EQUAL("test", (const char*)t[0].fourth);
            shared.end_read();
        }
    }

    // Verify that lock file was deleted after use
    const int rc = access("test_shared.tdb.lock", F_OK);
    CHECK_EQUAL(-1, rc);
}

TEST(Shared_Writes)
{
    // Delete old files if there
    remove("test_shared.tdb");
    remove("test_shared.tdb.lock"); // also the info file

    {
        // Create a new shared db
        SharedGroup shared("test_shared.tdb");
        CHECK(shared.is_valid());

        // Create first table in group
        {
            Group& g1 = shared.begin_write();
            TestTableShared::Ref t1 = g1.get_table<TestTableShared>("test");
            t1->add(0, 2, false, "test");
            shared.commit();
        }

        // Do a lot of repeated write transactions
        for (size_t i = 0; i < 100; ++i) {
            Group& g1 = shared.begin_write();
            TestTableShared::Ref t1 = g1.get_table<TestTableShared>("test");
            t1[0].first += 1;
            shared.commit();
        }

        // Verify that the changes were made
        {
            const Group& g1 = shared.begin_read();
            TestTableShared::ConstRef t = g1.get_table<TestTableShared>("test");
            const int64_t v = t[0].first;
            CHECK_EQUAL(100, v);
            shared.end_read();
        }
    }

    // Verify that lock file was deleted after use
    const int rc = access("test_shared.tdb.lock", F_OK);
    CHECK_EQUAL(-1, rc);
}

namespace {

TIGHTDB_TABLE_1(MyTable_SpecialOrder, first,  Int)

} // anonymous namespace

TEST(Shared_Writes_SpecialOrder)
{
    remove("test.tightdb");
    remove("test.tightdb.lock");

    SharedGroup db("test.tightdb");
    CHECK(db.is_valid());

    const int num_rows = 5; // FIXME: Should be strictly greater than MAX_LIST_SIZE, but that takes a loooooong time!
    const int num_reps = 25;

    {
        Group& group = db.begin_write();
        MyTable_SpecialOrder::Ref table = group.get_table<MyTable_SpecialOrder>("test");
        for (int i=0; i<num_rows; ++i) {
            table->add(0);
        }
    }
    db.commit();

    for (int i=0; i<num_rows; ++i) {
        for (int j=0; j<num_reps; ++j) {
            {
                Group& group = db.begin_write();
                MyTable_SpecialOrder::Ref table = group.get_table<MyTable_SpecialOrder>("test");
                CHECK_EQUAL(j, table[i].first);
                ++table[i].first;
            }
            db.commit();
        }
    }

    {
        const Group& group = db.begin_read();
        MyTable_SpecialOrder::ConstRef table = group.get_table<MyTable_SpecialOrder>("test");
        for (int i=0; i<num_rows; ++i) {
            CHECK_EQUAL(num_reps, table[i].first);
        }
    }
    db.end_read();
}

namespace  {

void* IncrementEntry(void* arg);

void* IncrementEntry(void* arg )
{
    const size_t row_id = (size_t)arg;

    // Open shared db
    SharedGroup shared("test_shared.tdb");
    CHECK(shared.is_valid());

    for (size_t i = 0; i < 100; ++i) {
        // Increment cell
        {
            Group& g1 = shared.begin_write();
            TestTableShared::Ref t1 = g1.get_table<TestTableShared>("test");
            if (t1->size() < row_id+1) {
                for (size_t i = t1->size(); i < row_id+1; ++i) {
                    t1->add(0, 2, false, "test");
                }
            }
            t1[row_id].first += 1;
            shared.commit();
        }

        // Verify in new transaction so that we interleave
        // read and write transactions
        {
            const Group& g1 = shared.begin_read();
            TestTableShared::ConstRef t = g1.get_table<TestTableShared>("test");

            const int64_t v = t[row_id].first;
            const int64_t expected = i+1;
            CHECK_EQUAL(expected, v);

            shared.end_read();
        }
    }
    return NULL;
}

} // anonymous namespace

TEST(Shared_WriterThreads)
{
    // Delete old files if there
    remove("test_shared.tdb");
    remove("test_shared.tdb.lock"); // also the info file

    {
        // Create a new shared db
        SharedGroup shared("test_shared.tdb");
        CHECK(shared.is_valid());

        const size_t thread_count = 10;

        // Create first table in group
        {
            Group& g1 = shared.begin_write();
            TestTableShared::Ref t1 = g1.get_table<TestTableShared>("test");
            for (size_t i = 0; i < thread_count; ++i) {
                t1->add(0, 2, false, "test");
            }
            shared.commit();
        }

        pthread_t threads[thread_count];

        // Create all threads
        for (size_t i = 0; i < thread_count; ++i) {
            const int rc = pthread_create(&threads[i], NULL, IncrementEntry, (void*)i);
            CHECK_EQUAL(0, rc);
        }

        // Wait for all threads to complete
        for (size_t i = 0; i < thread_count; ++i) {
            const int rc = pthread_join(threads[i], NULL);
            CHECK_EQUAL(0, rc);
        }

        // Verify that the changes were made
        {
            const Group& g1 = shared.begin_read();
            TestTableShared::ConstRef t = g1.get_table<TestTableShared>("test");

            for (size_t i = 0; i < thread_count; ++i) {
                const int64_t v = t[i].first;
                CHECK_EQUAL(100, v);
            }
            shared.end_read();
        }
    }

    // Verify that lock file was deleted after use
    const int rc = access("test_shared.tdb.lock", F_OK);
    CHECK_EQUAL(-1, rc);
}

#endif //_MSV_VER
<commit_msg>Failing unit test reenabled. Alexander is working on a solution.<commit_after>#include <UnitTest++.h>
#include "tightdb.hpp"
#include "tightdb/group_shared.hpp"

// Does not work for windows yet
#ifndef _MSC_VER

using namespace tightdb;

namespace {

TIGHTDB_TABLE_4(TestTableShared,
                first,  Int,
                second, Int,
                third,  Bool,
                fourth, String)

TEST(Shared_Initial)
{
    // Delete old files if there
    remove("test_shared.tdb");
    remove("test_shared.tdb.lock"); // also the info file

    {
        // Create a new shared db
        SharedGroup shared("test_shared.tdb");
        CHECK(shared.is_valid());

        // Verify that new group is empty
        {
            const Group& g1 = shared.begin_read();
            CHECK(g1.is_valid());
            CHECK(g1.is_empty());
            shared.end_read();
        }

#ifdef _DEBUG
        // Also do a basic ringbuffer test
        shared.test_ringbuf();
#endif
    }

    // Verify that lock file was deleted after use
    const int rc = access("test_shared.tdb.lock", F_OK);
    CHECK_EQUAL(-1, rc);
}

} // anonymous namespace

TEST(Shared1)
{
    // Delete old files if there
    remove("test_shared.tdb");
    remove("test_shared.tdb.lock"); // also the info file

    {
        // Create a new shared db
        SharedGroup shared("test_shared.tdb");
        CHECK(shared.is_valid());

        // Create first table in group
        {
            Group& g1 = shared.begin_write();
            TestTableShared::Ref t1 = g1.get_table<TestTableShared>("test");
            t1->add(1, 2, false, "test");
            shared.commit();
        }

        // Open same db again
        SharedGroup shared2("test_shared.tdb");
        CHECK(shared2.is_valid());
        {
            const Group& g2 = shared2.begin_read();

            // Verify that last set of changes are commited
            TestTableShared::ConstRef t2 = g2.get_table<TestTableShared>("test");
            CHECK(t2->size() == 1);
            CHECK_EQUAL(1, t2[0].first);
            CHECK_EQUAL(2, t2[0].second);
            CHECK_EQUAL(false, t2[0].third);
            CHECK_EQUAL("test", (const char*)t2[0].fourth);
            // don't end_read yet

            // Do a new change while stil having current read transaction open
            {
                Group& g1 = shared.begin_write();
                TestTableShared::Ref t1 = g1.get_table<TestTableShared>("test");
                t1->add(2, 3, true, "more test");
                shared.commit();
            }

            // Verify that that the read transaction does not see
            // the change yet (is isolated)
            CHECK(t2->size() == 1);
            CHECK_EQUAL(1, t2[0].first);
            CHECK_EQUAL(2, t2[0].second);
            CHECK_EQUAL(false, t2[0].third);
            CHECK_EQUAL("test", (const char*)t2[0].fourth);

            // Do one more new change while stil having current read transaction open
            // so we know that it does not overwrite data held by
            {
                Group& g1 = shared.begin_write();
                TestTableShared::Ref t1 = g1.get_table<TestTableShared>("test");
                t1->add(0, 1, false, "even more test");
                shared.commit();
            }

            // Verify that that the read transaction does still not see
            // the change yet (is isolated)
            CHECK(t2->size() == 1);
            CHECK_EQUAL(1, t2[0].first);
            CHECK_EQUAL(2, t2[0].second);
            CHECK_EQUAL(false, t2[0].third);
            CHECK_EQUAL("test", (const char*)t2[0].fourth);

            // Close read transaction
            shared2.end_read();
        }

        // Start a new read transaction and verify that it can now see the changes
        {
            const Group& g3 = shared2.begin_read();
            TestTableShared::ConstRef t3 = g3.get_table<TestTableShared>("test");

            CHECK(t3->size() == 3);
            CHECK_EQUAL(1, t3[0].first);
            CHECK_EQUAL(2, t3[0].second);
            CHECK_EQUAL(false, t3[0].third);
            CHECK_EQUAL("test", (const char*)t3[0].fourth);
            CHECK_EQUAL(2, t3[1].first);
            CHECK_EQUAL(3, t3[1].second);
            CHECK_EQUAL(true, t3[1].third);
            CHECK_EQUAL("more test", (const char*)t3[1].fourth);
            CHECK_EQUAL(0, t3[2].first);
            CHECK_EQUAL(1, t3[2].second);
            CHECK_EQUAL(false, t3[2].third);
            CHECK_EQUAL("even more test", (const char*)t3[2].fourth);

            shared2.end_read();
        }
    }

    // Verify that lock file was deleted after use
    const int rc = access("test_shared.tdb.lock", F_OK);
    CHECK_EQUAL(-1, rc);
}

TEST(Shared_rollback)
{
    // Delete old files if there
    remove("test_shared.tdb");
    remove("test_shared.tdb.lock"); // also the info file

    {
        // Create a new shared db
        SharedGroup shared("test_shared.tdb");
        CHECK(shared.is_valid());

        // Create first table in group (but rollback)
        {
            Group& g1 = shared.begin_write();
            TestTableShared::Ref t1 = g1.get_table<TestTableShared>("test");
            t1->add(1, 2, false, "test");
            shared.rollback();
        }

        // Verify that no changes were made
        {
            const Group& g1 = shared.begin_read();
            CHECK_EQUAL(false, g1.has_table("test"));
            shared.end_read();
        }

        // Really create first table in group
        {
            Group& g1 = shared.begin_write();
            TestTableShared::Ref t1 = g1.get_table<TestTableShared>("test");
            t1->add(1, 2, false, "test");
            shared.commit();
        }

        // Verify that the changes were made
        {
            const Group& g1 = shared.begin_read();
            TestTableShared::ConstRef t = g1.get_table<TestTableShared>("test");
            CHECK(t->size() == 1);
            CHECK_EQUAL(1, t[0].first);
            CHECK_EQUAL(2, t[0].second);
            CHECK_EQUAL(false, t[0].third);
            CHECK_EQUAL("test", (const char*)t[0].fourth);
            shared.end_read();
        }

        // Greate more changes (but rollback)
        {
            Group& g1 = shared.begin_write();
            TestTableShared::Ref t1 = g1.get_table<TestTableShared>("test");
            t1->add(0, 0, true, "more test");
            shared.rollback();
        }

        // Verify that no changes were made
        {
            const Group& g1 = shared.begin_read();
            TestTableShared::ConstRef t = g1.get_table<TestTableShared>("test");
            CHECK(t->size() == 1);
            CHECK_EQUAL(1, t[0].first);
            CHECK_EQUAL(2, t[0].second);
            CHECK_EQUAL(false, t[0].third);
            CHECK_EQUAL("test", (const char*)t[0].fourth);
            shared.end_read();
        }
    }

    // Verify that lock file was deleted after use
    const int rc = access("test_shared.tdb.lock", F_OK);
    CHECK_EQUAL(-1, rc);
}

TEST(Shared_Writes)
{
    // Delete old files if there
    remove("test_shared.tdb");
    remove("test_shared.tdb.lock"); // also the info file

    {
        // Create a new shared db
        SharedGroup shared("test_shared.tdb");
        CHECK(shared.is_valid());

        // Create first table in group
        {
            Group& g1 = shared.begin_write();
            TestTableShared::Ref t1 = g1.get_table<TestTableShared>("test");
            t1->add(0, 2, false, "test");
            shared.commit();
        }

        // Do a lot of repeated write transactions
        for (size_t i = 0; i < 100; ++i) {
            Group& g1 = shared.begin_write();
            TestTableShared::Ref t1 = g1.get_table<TestTableShared>("test");
            t1[0].first += 1;
            shared.commit();
        }

        // Verify that the changes were made
        {
            const Group& g1 = shared.begin_read();
            TestTableShared::ConstRef t = g1.get_table<TestTableShared>("test");
            const int64_t v = t[0].first;
            CHECK_EQUAL(100, v);
            shared.end_read();
        }
    }

    // Verify that lock file was deleted after use
    const int rc = access("test_shared.tdb.lock", F_OK);
    CHECK_EQUAL(-1, rc);
}

namespace {

TIGHTDB_TABLE_1(MyTable_SpecialOrder, first,  Int)

} // anonymous namespace

TEST(Shared_Writes_SpecialOrder)
{
    remove("test.tightdb");
    remove("test.tightdb.lock");

    SharedGroup db("test.tightdb");
    CHECK(db.is_valid());

    const int num_rows = 5; // FIXME: Should be strictly greater than MAX_LIST_SIZE, but that takes a loooooong time!
    const int num_reps = 25;

    {
        Group& group = db.begin_write();
        MyTable_SpecialOrder::Ref table = group.get_table<MyTable_SpecialOrder>("test");
        for (int i=0; i<num_rows; ++i) {
            table->add(0);
        }
    }
    db.commit();

    for (int i=0; i<num_rows; ++i) {
        for (int j=0; j<num_reps; ++j) {
            {
                Group& group = db.begin_write();
                MyTable_SpecialOrder::Ref table = group.get_table<MyTable_SpecialOrder>("test");
                CHECK_EQUAL(j, table[i].first);
                ++table[i].first;
            }
            db.commit();
        }
    }

    {
        const Group& group = db.begin_read();
        MyTable_SpecialOrder::ConstRef table = group.get_table<MyTable_SpecialOrder>("test");
        for (int i=0; i<num_rows; ++i) {
            CHECK_EQUAL(num_reps, table[i].first);
        }
    }
    db.end_read();
}

namespace  {

void* IncrementEntry(void* arg);

void* IncrementEntry(void* arg )
{
    const size_t row_id = (size_t)arg;

    // Open shared db
    SharedGroup shared("test_shared.tdb");
    CHECK(shared.is_valid());

    for (size_t i = 0; i < 100; ++i) {
        // Increment cell
        {
            Group& g1 = shared.begin_write();
            TestTableShared::Ref t1 = g1.get_table<TestTableShared>("test");
            if (t1->size() < row_id+1) {
                for (size_t i = t1->size(); i < row_id+1; ++i) {
                    t1->add(0, 2, false, "test");
                }
            }
            t1[row_id].first += 1;
            shared.commit();
        }

        // Verify in new transaction so that we interleave
        // read and write transactions
        {
            const Group& g1 = shared.begin_read();
            TestTableShared::ConstRef t = g1.get_table<TestTableShared>("test");

            const int64_t v = t[row_id].first;
            const int64_t expected = i+1;
            CHECK_EQUAL(expected, v);

            shared.end_read();
        }
    }
    return NULL;
}

} // anonymous namespace

TEST(Shared_WriterThreads)
{
    // Delete old files if there
    remove("test_shared.tdb");
    remove("test_shared.tdb.lock"); // also the info file

    {
        // Create a new shared db
        SharedGroup shared("test_shared.tdb");
        CHECK(shared.is_valid());

        const size_t thread_count = 10;

        // Create first table in group
/*
        {
            Group& g1 = shared.begin_write();
            TestTableShared::Ref t1 = g1.get_table<TestTableShared>("test");
            for (size_t i = 0; i < thread_count; ++i) {
                t1->add(0, 2, false, "test");
            }
            shared.commit();
        }
*/

        pthread_t threads[thread_count];

        // Create all threads
        for (size_t i = 0; i < thread_count; ++i) {
            const int rc = pthread_create(&threads[i], NULL, IncrementEntry, (void*)i);
            CHECK_EQUAL(0, rc);
        }

        // Wait for all threads to complete
        for (size_t i = 0; i < thread_count; ++i) {
            const int rc = pthread_join(threads[i], NULL);
            CHECK_EQUAL(0, rc);
        }

        // Verify that the changes were made
        {
            const Group& g1 = shared.begin_read();
            TestTableShared::ConstRef t = g1.get_table<TestTableShared>("test");

            for (size_t i = 0; i < thread_count; ++i) {
                const int64_t v = t[i].first;
                CHECK_EQUAL(100, v);
            }
            shared.end_read();
        }
    }

    // Verify that lock file was deleted after use
    const int rc = access("test_shared.tdb.lock", F_OK);
    CHECK_EQUAL(-1, rc);
}

#endif //_MSV_VER
<|endoftext|>


<commit_before>/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, development version     *
*                (c) 2006-2017 INRIA, USTL, UJF, CNRS, MGH                    *
*                                                                             *
* This program is free software; you can redistribute it and/or modify it     *
* under the terms of the GNU General Public License as published by the Free  *
* Software Foundation; either version 2 of the License, or (at your option)   *
* any later version.                                                          *
*                                                                             *
* This program is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    *
* more details.                                                               *
*                                                                             *
* You should have received a copy of the GNU General Public License along     *
* with this program. If not, see <http://www.gnu.org/licenses/>.              *
*******************************************************************************
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/

#include <sofa/helper/system/PluginManager.h>
using sofa::helper::system::PluginManager ;

#include <sofa/helper/system/FileRepository.h>
using sofa::helper::system::PluginRepository ;

#include <sofa/helper/system/FileSystem.h>
using sofa::helper::system::PluginRepository;
using sofa::helper::system::DataRepository;
using sofa::helper::system::FileSystem;

#include <sofa/helper/Utils.h>
using sofa::helper::Utils;

#include <sofa/helper/BackTrace.h>
using sofa::helper::BackTrace;

#include <sofa/helper/system/console.h>
using sofa::helper::Console ;

#include <sofa/helper/testing/TestMessageHandler.h>
using sofa::helper::logging::MessageDispatcher ;
using sofa::helper::logging::MainGtestMessageHandler ;

#include <sofa/helper/random.h>

#include "BaseTest.h"

namespace sofa {
namespace helper {
namespace testing {

void initializeOnce()
{
    static bool initialized = false ;
    if(!initialized){
        Console::setColorsStatus(Console::ColorsDisabled) ;

        MessageDispatcher::addHandler( MainGtestMessageHandler::getInstance() ) ;
        BackTrace::autodump() ;

        const std::string pluginDir = Utils::getPluginDirectory() ;
        PluginRepository.addFirstPath(pluginDir);
        initialized=true ;
    }
}

int BaseTest::seed = (unsigned int)time(NULL);

BaseTest::BaseTest() :
   m_fatal(sofa::helper::logging::Message::Fatal, __FILE__, __LINE__ ),
   m_error(sofa::helper::logging::Message::Error, __FILE__, __LINE__ )
{
    initializeOnce() ;

    seed = ::testing::UnitTest::GetInstance()->random_seed() ;

    ///if you want to generate the same sequence of pseudo-random numbers than a specific test suites
    ///use the same seed (the seed value is indicated at the 2nd line of test results)
    ///and pass the seed in command argument line ex: SofaTest_test.exe seed 32
    helper::srand(seed);

    ///gtest already use color so we remove the color from the sofa message to make the distinction
    ///clean and avoid ambiguity.
    Console::setColorsStatus(Console::ColorsDisabled) ;

    ///Repeating this for each class is harmless because addHandler test if the handler is already installed and
    ///if so it don't install it again.
    MessageDispatcher::addHandler( MainGtestMessageHandler::getInstance() ) ;
}

BaseTest::~BaseTest() {}

void BaseTest::SetUp()
{
    onSetUp();
}

void BaseTest::TearDown()
{
    onTearDown();
}

} /// testing
} /// helper
} /// sofa

<commit_msg>[SofaKernel] Remove useless code.<commit_after>/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, development version     *
*                (c) 2006-2017 INRIA, USTL, UJF, CNRS, MGH                    *
*                                                                             *
* This program is free software; you can redistribute it and/or modify it     *
* under the terms of the GNU General Public License as published by the Free  *
* Software Foundation; either version 2 of the License, or (at your option)   *
* any later version.                                                          *
*                                                                             *
* This program is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    *
* more details.                                                               *
*                                                                             *
* You should have received a copy of the GNU General Public License along     *
* with this program. If not, see <http://www.gnu.org/licenses/>.              *
*******************************************************************************
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/

#include <sofa/helper/system/PluginManager.h>
using sofa::helper::system::PluginManager ;

#include <sofa/helper/system/FileRepository.h>
using sofa::helper::system::PluginRepository ;

#include <sofa/helper/system/FileSystem.h>
using sofa::helper::system::PluginRepository;
using sofa::helper::system::DataRepository;
using sofa::helper::system::FileSystem;

#include <sofa/helper/Utils.h>
using sofa::helper::Utils;

#include <sofa/helper/BackTrace.h>
using sofa::helper::BackTrace;

#include <sofa/helper/system/console.h>
using sofa::helper::Console ;

#include <sofa/helper/testing/TestMessageHandler.h>
using sofa::helper::logging::MessageDispatcher ;
using sofa::helper::logging::MainGtestMessageHandler ;

#include <sofa/helper/random.h>

#include "BaseTest.h"

namespace sofa {
namespace helper {
namespace testing {

void initializeOnce()
{
    static bool initialized = false ;
    if(!initialized){
        Console::setColorsStatus(Console::ColorsDisabled) ;

        MessageDispatcher::addHandler( MainGtestMessageHandler::getInstance() ) ;
        BackTrace::autodump() ;

        initialized=true ;
    }
}

int BaseTest::seed = (unsigned int)time(NULL);

BaseTest::BaseTest() :
   m_fatal(sofa::helper::logging::Message::Fatal, __FILE__, __LINE__ ),
   m_error(sofa::helper::logging::Message::Error, __FILE__, __LINE__ )
{
    initializeOnce() ;

    seed = ::testing::UnitTest::GetInstance()->random_seed() ;

    ///if you want to generate the same sequence of pseudo-random numbers than a specific test suites
    ///use the same seed (the seed value is indicated at the 2nd line of test results)
    ///and pass the seed in command argument line ex: SofaTest_test.exe seed 32
    helper::srand(seed);

    ///gtest already use color so we remove the color from the sofa message to make the distinction
    ///clean and avoid ambiguity.
    Console::setColorsStatus(Console::ColorsDisabled) ;

    ///Repeating this for each class is harmless because addHandler test if the handler is already installed and
    ///if so it don't install it again.
    MessageDispatcher::addHandler( MainGtestMessageHandler::getInstance() ) ;
}

BaseTest::~BaseTest() {}

void BaseTest::SetUp()
{
    onSetUp();
}

void BaseTest::TearDown()
{
    onTearDown();
}

} /// testing
} /// helper
} /// sofa

<|endoftext|>