|
2 | 2 | // Distributed under the MIT software license, see the accompanying |
3 | 3 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
4 | 4 |
|
| 5 | +#include <QtTest/QtTest> |
| 6 | + |
| 7 | +#include <net_processing.h> |
| 8 | +#include <test/mocks/mocknode.h> |
5 | 9 | #include <qml/initexecutor.h> |
6 | 10 |
|
7 | | -#include <QMetaObject> |
8 | | -#include <QMetaMethod> |
9 | | -#include <QObject> |
10 | | -#include <QTest> |
| 11 | +#include <util/translation.h> |
| 12 | + |
| 13 | +#include <stdexcept> |
11 | 14 |
|
12 | | -#include <type_traits> |
| 15 | +Q_DECLARE_METATYPE(interfaces::BlockAndHeaderTipInfo) |
| 16 | + |
| 17 | +namespace { |
| 18 | +constexpr auto SIGNAL_TIMEOUT{5'000}; |
| 19 | +} |
13 | 20 |
|
14 | 21 | class QmlInitExecutorApiTests : public QObject |
15 | 22 | { |
16 | 23 | Q_OBJECT |
17 | 24 |
|
18 | 25 | private Q_SLOTS: |
19 | | - void exposesExpectedQObjectApi(); |
| 26 | + void initTestCase(); |
| 27 | + void initializeEmitsResultAndRunsOffMainThread(); |
| 28 | + void initializeEmitsRunawayExceptionOnFailure(); |
| 29 | + void shutdownEmitsResultAndRunsOffMainThread(); |
| 30 | + void shutdownEmitsRunawayExceptionOnFailure(); |
20 | 31 | }; |
21 | 32 |
|
22 | | -static bool HasMethodByName(const QMetaObject& meta, const QByteArray& method_name) |
| 33 | +void QmlInitExecutorApiTests::initTestCase() |
23 | 34 | { |
24 | | - for (int i = 0; i < meta.methodCount(); ++i) { |
25 | | - if (meta.method(i).name() == method_name) return true; |
26 | | - } |
27 | | - return false; |
| 35 | + qRegisterMetaType<interfaces::BlockAndHeaderTipInfo>("interfaces::BlockAndHeaderTipInfo"); |
28 | 36 | } |
29 | 37 |
|
30 | | -void QmlInitExecutorApiTests::exposesExpectedQObjectApi() |
| 38 | +void QmlInitExecutorApiTests::initializeEmitsResultAndRunsOffMainThread() |
31 | 39 | { |
32 | | - QVERIFY((std::is_base_of_v<QObject, QmlInitExecutor>)); |
33 | | - QVERIFY((std::is_constructible_v<QmlInitExecutor, interfaces::Node&>)); |
| 40 | + using ::testing::_; |
| 41 | + using ::testing::Invoke; |
| 42 | + using ::testing::StrictMock; |
| 43 | + |
| 44 | + StrictMock<MockNode> node; |
| 45 | + bool ran_off_main_thread{false}; |
| 46 | + |
| 47 | + EXPECT_CALL(node, appInitMain(_)).WillOnce(Invoke([&](interfaces::BlockAndHeaderTipInfo* tip_info) { |
| 48 | + ran_off_main_thread = QThread::currentThread() != QCoreApplication::instance()->thread(); |
| 49 | + tip_info->block_height = 101; |
| 50 | + tip_info->block_time = 1'700'000'001; |
| 51 | + tip_info->header_height = 105; |
| 52 | + tip_info->header_time = 1'700'000'099; |
| 53 | + tip_info->verification_progress = 0.75; |
| 54 | + return true; |
| 55 | + })); |
| 56 | + |
| 57 | + QmlInitExecutor executor{node}; |
| 58 | + QSignalSpy initialize_spy(&executor, &QmlInitExecutor::initializeResult); |
| 59 | + QSignalSpy runaway_spy(&executor, &QmlInitExecutor::runawayException); |
| 60 | + |
| 61 | + executor.initialize(); |
| 62 | + |
| 63 | + QVERIFY(initialize_spy.wait(SIGNAL_TIMEOUT)); |
| 64 | + QCOMPARE(initialize_spy.count(), 1); |
| 65 | + QCOMPARE(runaway_spy.count(), 0); |
| 66 | + QVERIFY(ran_off_main_thread); |
| 67 | + |
| 68 | + const QList<QVariant> arguments = initialize_spy.takeFirst(); |
| 69 | + QCOMPARE(arguments.at(0).toBool(), true); |
| 70 | + |
| 71 | + const auto tip_info = arguments.at(1).value<interfaces::BlockAndHeaderTipInfo>(); |
| 72 | + QCOMPARE(tip_info.block_height, 101); |
| 73 | + QCOMPARE(tip_info.block_time, 1'700'000'001LL); |
| 74 | + QCOMPARE(tip_info.header_height, 105); |
| 75 | + QCOMPARE(tip_info.header_time, 1'700'000'099LL); |
| 76 | + QCOMPARE(tip_info.verification_progress, 0.75); |
| 77 | +} |
| 78 | + |
| 79 | +void QmlInitExecutorApiTests::initializeEmitsRunawayExceptionOnFailure() |
| 80 | +{ |
| 81 | + using ::testing::_; |
| 82 | + using ::testing::Invoke; |
| 83 | + using ::testing::Return; |
| 84 | + using ::testing::StrictMock; |
| 85 | + |
| 86 | + StrictMock<MockNode> node; |
| 87 | + |
| 88 | + EXPECT_CALL(node, appInitMain(_)).WillOnce(Invoke([](interfaces::BlockAndHeaderTipInfo*) -> bool { |
| 89 | + throw std::runtime_error{"init failed"}; |
| 90 | + })); |
| 91 | + EXPECT_CALL(node, getWarnings()).WillOnce(Return(bilingual_str{"Initialization failed", "Translated init failure"})); |
| 92 | + |
| 93 | + QmlInitExecutor executor{node}; |
| 94 | + QSignalSpy initialize_spy(&executor, &QmlInitExecutor::initializeResult); |
| 95 | + QSignalSpy runaway_spy(&executor, &QmlInitExecutor::runawayException); |
| 96 | + |
| 97 | + executor.initialize(); |
| 98 | + |
| 99 | + QVERIFY(runaway_spy.wait(SIGNAL_TIMEOUT)); |
| 100 | + QCOMPARE(runaway_spy.count(), 1); |
| 101 | + QCOMPARE(initialize_spy.count(), 0); |
| 102 | + QCOMPARE(runaway_spy.takeFirst().at(0).toString(), QString{"Translated init failure"}); |
| 103 | +} |
| 104 | + |
| 105 | +void QmlInitExecutorApiTests::shutdownEmitsResultAndRunsOffMainThread() |
| 106 | +{ |
| 107 | + using ::testing::Invoke; |
| 108 | + using ::testing::StrictMock; |
| 109 | + |
| 110 | + StrictMock<MockNode> node; |
| 111 | + bool ran_off_main_thread{false}; |
| 112 | + |
| 113 | + EXPECT_CALL(node, appShutdown()).WillOnce(Invoke([&] { |
| 114 | + ran_off_main_thread = QThread::currentThread() != QCoreApplication::instance()->thread(); |
| 115 | + })); |
| 116 | + |
| 117 | + QmlInitExecutor executor{node}; |
| 118 | + QSignalSpy shutdown_spy(&executor, &QmlInitExecutor::shutdownResult); |
| 119 | + QSignalSpy runaway_spy(&executor, &QmlInitExecutor::runawayException); |
| 120 | + |
| 121 | + executor.shutdown(); |
| 122 | + |
| 123 | + QVERIFY(shutdown_spy.wait(SIGNAL_TIMEOUT)); |
| 124 | + QCOMPARE(shutdown_spy.count(), 1); |
| 125 | + QCOMPARE(runaway_spy.count(), 0); |
| 126 | + QVERIFY(ran_off_main_thread); |
| 127 | +} |
| 128 | + |
| 129 | +void QmlInitExecutorApiTests::shutdownEmitsRunawayExceptionOnFailure() |
| 130 | +{ |
| 131 | + using ::testing::Invoke; |
| 132 | + using ::testing::Return; |
| 133 | + using ::testing::StrictMock; |
| 134 | + |
| 135 | + StrictMock<MockNode> node; |
| 136 | + |
| 137 | + EXPECT_CALL(node, appShutdown()).WillOnce(Invoke([] { |
| 138 | + throw std::runtime_error{"shutdown failed"}; |
| 139 | + })); |
| 140 | + EXPECT_CALL(node, getWarnings()).WillOnce(Return(bilingual_str{"Shutdown failed", "Translated shutdown failure"})); |
| 141 | + |
| 142 | + QmlInitExecutor executor{node}; |
| 143 | + QSignalSpy shutdown_spy(&executor, &QmlInitExecutor::shutdownResult); |
| 144 | + QSignalSpy runaway_spy(&executor, &QmlInitExecutor::runawayException); |
34 | 145 |
|
35 | | - const QMetaObject& meta = QmlInitExecutor::staticMetaObject; |
| 146 | + executor.shutdown(); |
36 | 147 |
|
37 | | - QVERIFY(HasMethodByName(meta, "initialize")); |
38 | | - QVERIFY(HasMethodByName(meta, "shutdown")); |
39 | | - QVERIFY(HasMethodByName(meta, "initializeResult")); |
40 | | - QVERIFY(HasMethodByName(meta, "shutdownResult")); |
41 | | - QVERIFY(HasMethodByName(meta, "runawayException")); |
| 148 | + QVERIFY(runaway_spy.wait(SIGNAL_TIMEOUT)); |
| 149 | + QCOMPARE(runaway_spy.count(), 1); |
| 150 | + QCOMPARE(shutdown_spy.count(), 0); |
| 151 | + QCOMPARE(runaway_spy.takeFirst().at(0).toString(), QString{"Translated shutdown failure"}); |
42 | 152 | } |
43 | 153 |
|
44 | 154 | int RunQmlInitExecutorApiTests(int argc, char* argv[]) |
|
0 commit comments