15.1.938. tablet_qt/tests/auto/qobjects/int64validator/testint64validator.cpp

/*
    Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
    Created by Rudolf Cardinal (rnc1001@cam.ac.uk).

    This file is part of CamCOPS.

    CamCOPS 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 3 of the License, or
    (at your option) any later version.

    CamCOPS 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 CamCOPS. If not, see <https://www.gnu.org/licenses/>.
*/

#include <QtTest/QtTest>
#include <QValidator>

#include "qobjects/int64validator.h"

#define TESTINT64_INCLUDE_RANDOM  // should generally be defined

class TestInt64Validator : public QObject
{
    Q_OBJECT

#ifdef TESTINT64_INCLUDE_RANDOM

private:
    void validateRandomNumbers(const qint64 lowest, const qint64 highest);
#endif

private slots:
    void testSetRangeSetsTopAndBottom();
    void testSetRangeSignalsWhenTopChanges();
    void testSetRangeSignalsWhenBottomChanges();
    void testSetRangeSignalsWhenNothingChanges();
    void testSetBottomSetsBottom();
    void testSetTopSetsTop();
    void testValidateReturnsIntermediateIfEmpty();
    void testValidateReturnsInvalidIfDecimalPoint();
    void testValidateReturnsIntermediateIfMinusAndNegativeAllowed();
    void testValidateReturnsInvalidIfMinusAndNegativeNotAllowed();
    void testValidateReturnsIntermediateIfPlusAndPositiveAllowed();
    void testValidateReturnsInvalidIfPlusAndPositiveNotAllowed();
    void testValidateReturnsInvalidIfNotAnInt();
    void testValidateReturnsAcceptableIfAnIntWithinRange();
    void testValidateReturnsIntermediateIfNegativeZero();
    void testValidateReturnsInvalidIfTopNegativeAndNoMinus();
    void testValidateReturnsIntermediateIfHasValidStart();
    void testValidateReturnsIntermediateIfHasInvalidStart();
    void testValidateReturnsIntermediateIfZeroAndRangeGreaterThanZero();
#ifdef TESTINT64_INCLUDE_RANDOM
    void testRandomNumbersAndRangesLargeRange();
    void testRandomNumbersAndRangesSmallRange();
#endif
};

void TestInt64Validator::testSetRangeSetsTopAndBottom()
{
    Int64Validator* validator = new Int64Validator();

    validator->setRange(-10, 10);

    QCOMPARE(validator->top(), 10);
    QCOMPARE(validator->bottom(), -10);
}

void TestInt64Validator::testSetRangeSignalsWhenTopChanges()
{
    const qint64 bottom = -10;
    const qint64 old_top = 0;

    Int64Validator* validator = new Int64Validator(bottom, old_top);

    QSignalSpy changed_spy(validator, SIGNAL(changed()));
    QVERIFY(changed_spy.isValid());

    QSignalSpy top_spy(validator, SIGNAL(topChanged(qint64)));
    QVERIFY(top_spy.isValid());

    QSignalSpy bottom_spy(validator, SIGNAL(bottomChanged(qint64)));
    QVERIFY(bottom_spy.isValid());

    const qint64 new_top = 10;
    validator->setRange(bottom, new_top);

    QCOMPARE(changed_spy.count(), 1);
    QCOMPARE(top_spy.count(), 1);
    QList<QVariant> arguments = top_spy.takeFirst();
    QVERIFY(arguments.at(0).toInt() == new_top);

    QCOMPARE(bottom_spy.count(), 0);
}

void TestInt64Validator::testSetRangeSignalsWhenBottomChanges()
{
    const qint64 old_bottom = -10;
    const qint64 top = 10;

    Int64Validator* validator = new Int64Validator(old_bottom, top);

    QSignalSpy changed_spy(validator, SIGNAL(changed()));
    QVERIFY(changed_spy.isValid());

    QSignalSpy top_spy(validator, SIGNAL(topChanged(qint64)));
    QVERIFY(top_spy.isValid());

    QSignalSpy bottom_spy(validator, SIGNAL(bottomChanged(qint64)));
    QVERIFY(bottom_spy.isValid());

    const qint64 new_bottom = 10;
    validator->setRange(new_bottom, top);

    QCOMPARE(changed_spy.count(), 1);
    QCOMPARE(top_spy.count(), 0);

    QCOMPARE(bottom_spy.count(), 1);
    QList<QVariant> arguments = bottom_spy.takeFirst();
    QVERIFY(arguments.at(0).toInt() == new_bottom);
}

void TestInt64Validator::testSetRangeSignalsWhenNothingChanges()
{
    const qint64 bottom = 0;
    const qint64 top = 10;

    Int64Validator* validator = new Int64Validator(bottom, top);

    QSignalSpy changed_spy(validator, SIGNAL(changed()));
    QVERIFY(changed_spy.isValid());

    QSignalSpy top_spy(validator, SIGNAL(topChanged(qint64)));
    QVERIFY(top_spy.isValid());

    QSignalSpy bottom_spy(validator, SIGNAL(bottomChanged(qint64)));
    QVERIFY(bottom_spy.isValid());

    validator->setRange(bottom, top);

    QCOMPARE(changed_spy.count(), 0);
    QCOMPARE(top_spy.count(), 0);
    QCOMPARE(bottom_spy.count(), 0);
}

void TestInt64Validator::testSetBottomSetsBottom()
{
    Int64Validator* validator = new Int64Validator();
    validator->setBottom(-10);

    QCOMPARE(validator->bottom(), -10);
}

void TestInt64Validator::testSetTopSetsTop()
{
    Int64Validator* validator = new Int64Validator();
    validator->setTop(10);

    QCOMPARE(validator->top(), 10);
}

void TestInt64Validator::testValidateReturnsIntermediateIfEmpty()
{
    const qint64 bottom = 0;
    const qint64 top = 10;
    QString text("");
    int pos = 0;

    Int64Validator validator(bottom, top, nullptr);

    QCOMPARE(validator.validate(text, pos), QValidator::Intermediate);
}

void TestInt64Validator::testValidateReturnsInvalidIfDecimalPoint()
{
    const qint64 bottom = 0;
    const qint64 top = 10;
    QString text("3.1416");
    int pos = 0;

    Int64Validator validator(bottom, top, nullptr);

    QCOMPARE(validator.validate(text, pos), QValidator::Invalid);
}

void TestInt64Validator::
    testValidateReturnsIntermediateIfMinusAndNegativeAllowed()
{
    const qint64 bottom = -1;
    const qint64 top = 10;
    QString text("-");
    int pos = 0;

    Int64Validator validator(bottom, top, nullptr);

    QCOMPARE(validator.validate(text, pos), QValidator::Intermediate);
}

void TestInt64Validator::
    testValidateReturnsInvalidIfMinusAndNegativeNotAllowed()
{
    const qint64 bottom = 0;
    const qint64 top = 10;
    QString text("-");
    int pos = 0;

    Int64Validator validator(bottom, top, nullptr);

    QCOMPARE(validator.validate(text, pos), QValidator::Invalid);
}

void TestInt64Validator::
    testValidateReturnsIntermediateIfPlusAndPositiveAllowed()
{
    const qint64 bottom = 0;
    const qint64 top = 10;
    QString text("+");
    int pos = 0;

    Int64Validator validator(bottom, top, nullptr);

    QCOMPARE(validator.validate(text, pos), QValidator::Intermediate);
}

void TestInt64Validator::testValidateReturnsInvalidIfPlusAndPositiveNotAllowed(
)
{
    const qint64 bottom = -100;
    const qint64 top = -1;
    QString text("+");
    int pos = 0;

    Int64Validator validator(bottom, top, nullptr);

    QCOMPARE(validator.validate(text, pos), QValidator::Invalid);
}

void TestInt64Validator::testValidateReturnsInvalidIfNotAnInt()
{
    const qint64 bottom = 0;
    const qint64 top = 10;
    QString text("not an int");
    int pos = 0;

    Int64Validator validator(bottom, top, nullptr);

    QCOMPARE(validator.validate(text, pos), QValidator::Invalid);
}

void TestInt64Validator::testValidateReturnsAcceptableIfAnIntWithinRange()
{
    const qint64 bottom = 0;
    const qint64 top = 10;
    QString text("3");
    int pos = 0;

    Int64Validator validator(bottom, top, nullptr);

    QCOMPARE(validator.validate(text, pos), QValidator::Acceptable);
}

void TestInt64Validator::testValidateReturnsIntermediateIfNegativeZero()
{
    const qint64 bottom = -2;
    const qint64 top = -1;
    QString text("-0");
    int pos = 0;

    Int64Validator validator(bottom, top, nullptr);

    QCOMPARE(validator.validate(text, pos), QValidator::Intermediate);
}

void TestInt64Validator::testValidateReturnsInvalidIfTopNegativeAndNoMinus()
{
    const qint64 bottom = -10;
    const qint64 top = -1;
    QString text("1");
    int pos = 0;

    Int64Validator validator(bottom, top, nullptr);

    QCOMPARE(validator.validate(text, pos), QValidator::Invalid);
}

void TestInt64Validator::testValidateReturnsIntermediateIfHasValidStart()
{
    const qint64 bottom = 10;
    const qint64 top = 20;
    QString text("1");
    int pos = 0;

    Int64Validator validator(bottom, top, nullptr);

    QCOMPARE(validator.validate(text, pos), QValidator::Intermediate);
}

void TestInt64Validator::testValidateReturnsIntermediateIfHasInvalidStart()
{
    const qint64 bottom = 10;
    const qint64 top = 19;
    QString text("2");
    int pos = 0;

    Int64Validator validator(bottom, top, nullptr);

    QCOMPARE(validator.validate(text, pos), QValidator::Intermediate);
}

void TestInt64Validator::
    testValidateReturnsIntermediateIfZeroAndRangeGreaterThanZero()
{
    const qint64 bottom = 1;
    const qint64 top = 5;
    QString text("0");
    int pos = 0;

    Int64Validator validator(bottom, top, nullptr);

    QCOMPARE(validator.validate(text, pos), QValidator::Intermediate);
}

#ifdef TESTINT64_INCLUDE_RANDOM
void TestInt64Validator::testRandomNumbersAndRangesLargeRange()
{
    // QRandomGenerator does not work across the full range but this is
    // good enough for our purposes
    validateRandomNumbers(-1e10, 1e10);
}

void TestInt64Validator::testRandomNumbersAndRangesSmallRange()
{
    validateRandomNumbers(-1000, 1000);
}

void TestInt64Validator::validateRandomNumbers(
    const qint64 lowest, const qint64 highest
)
{
    const int seed = 1234;
    const int num_tests = 1000;

    QRandomGenerator rng(seed);

    for (int test = 0; test < num_tests; ++test) {
        const qint64 limit_1 = rng.bounded(lowest, highest);
        const qint64 limit_2 = rng.bounded(lowest, highest);

        const qint64 bottom = std::min(limit_1, limit_2);
        const qint64 top = std::max(limit_1, limit_2);

        QString str_number;

        if (bottom == top) {
            str_number.setNum(bottom);
        } else {
            const qint64 number = rng.bounded(top - bottom) + bottom;
            str_number.setNum(number);
        }

        int pos = 0;

        Int64Validator validator(bottom, top, nullptr);

        for (int c = 1; c <= str_number.length(); ++c) {
            QString typed = str_number.first(c);

            auto state = validator.validate(typed, pos);

            if (state == QValidator::Invalid) {
                qDebug() << "Validation failed for" << typed << "from"
                         << str_number << "range" << bottom << "to" << top;
                QVERIFY(false);
            }
        }

        QVERIFY(true);
    }
}
#endif

QTEST_MAIN(TestInt64Validator)

#include "testint64validator.moc"