15.1.942. tablet_qt/tests/auto/qobjects/uint64validator/testuint64validator.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/uint64validator.h"

#define TESTUINT64_INCLUDE_RANDOM  // should generally be defined

class TestUInt64Validator : public QObject
{
    Q_OBJECT

#ifdef TESTUINT64_INCLUDE_RANDOM

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

private slots:
    void testSetRangeSetsTopAndBottom();
    void testSetRangeSignalsWhenTopChanges();
    void testSetRangeSignalsWhenBottomChanges();
    void testSetRangeSignalsWhenNothingChanges();
    void testSetBottomSetsBottom();
    void testSetTopSetsTop();
    void testValidateReturnsIntermediateIfEmpty();
    void testValidateReturnsInvalidIfDecimalPoint();
    void testValidateReturnsInvalidIfMinus();
    void testValidateReturnsIntermediateIfPlusAndPositiveAllowed();
    void testValidateReturnsInvalidIfNotAnInt();
    void testValidateReturnsAcceptableIfAnIntWithinRange();
    void testValidateReturnsInvalidIfMinusZero();
    void testValidateReturnsIntermediateIfHasValidStart();
    void testValidateReturnsIntermediateIfHasInvalidStart();
    void testValidateReturnsIntermediateIfZeroAndRangeGreaterThanZero();
#ifdef TESTUINT64_INCLUDE_RANDOM
    void testRandomNumbersAndRangesLargeRange();
    void testRandomNumbersAndRangesSmallRange();
#endif
};

void TestUInt64Validator::testSetRangeSetsTopAndBottom()
{
    const quint64 bottom = 0;
    const quint64 top = 10;

    UInt64Validator* validator = new UInt64Validator();

    validator->setRange(bottom, top);

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

void TestUInt64Validator::testSetRangeSignalsWhenTopChanges()
{
    const quint64 bottom = 0;
    const quint64 old_top = 10;

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

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

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

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

    const quint64 new_top = 20;
    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 TestUInt64Validator::testSetRangeSignalsWhenBottomChanges()
{
    const quint64 old_bottom = 0;
    const quint64 top = 10;

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

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

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

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

    const quint64 new_bottom = 5;
    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 TestUInt64Validator::testSetRangeSignalsWhenNothingChanges()
{
    const quint64 bottom = 0;
    const quint64 top = 10;

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

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

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

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

    validator->setRange(bottom, top);

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

void TestUInt64Validator::testSetBottomSetsBottom()
{
    const quint64 bottom = 10;

    UInt64Validator* validator = new UInt64Validator();
    validator->setBottom(bottom);

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

void TestUInt64Validator::testSetTopSetsTop()
{
    const quint64 top = 10;

    UInt64Validator* validator = new UInt64Validator();
    validator->setTop(top);

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

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

    UInt64Validator validator(bottom, top, nullptr);

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

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

    UInt64Validator validator(bottom, top, nullptr);

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

void TestUInt64Validator::testValidateReturnsInvalidIfMinus()
{
    const quint64 bottom = 0;
    const quint64 top = 10;
    QString text("-");
    int pos = 0;

    UInt64Validator validator(bottom, top, nullptr);

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

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

    UInt64Validator validator(bottom, top, nullptr);

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

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

    UInt64Validator validator(bottom, top, nullptr);

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

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

    UInt64Validator validator(bottom, top, nullptr);

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

void TestUInt64Validator::testValidateReturnsInvalidIfMinusZero()
{
    const quint64 bottom = -2;
    const quint64 top = -1;
    QString text("-0");
    int pos = 0;

    UInt64Validator validator(bottom, top, nullptr);

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

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

    UInt64Validator validator(bottom, top, nullptr);

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

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

    UInt64Validator validator(bottom, top, nullptr);

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

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

    UInt64Validator validator(bottom, top, nullptr);

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

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

void TestUInt64Validator::testRandomNumbersAndRangesSmallRange()
{
    validateRandomNumbers(0, 1000);
}

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

    QRandomGenerator rng(seed);

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

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

        QString str_number;

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

        int pos = 0;

        UInt64Validator 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(TestUInt64Validator)

#include "testuint64validator.moc"