Skip to content

Commit 7489c96

Browse files
committed
test: add init executor coverage with MockNode
1 parent 507666a commit 7489c96

3 files changed

Lines changed: 144 additions & 20 deletions

File tree

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ target_include_directories(bitcoinqml_unit_tests
3737
target_link_libraries(bitcoinqml_unit_tests
3838
PRIVATE
3939
bitcoin_common
40+
GTest::gmock
4041
bitcoin_util
4142
GTest::gmock
4243
Qt6::Core

test/mocks/mocknode.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,22 @@
55
#ifndef BITCOIN_QML_TEST_MOCKS_MOCKNODE_H
66
#define BITCOIN_QML_TEST_MOCKS_MOCKNODE_H
77

8+
#ifdef Assert
9+
#pragma push_macro("Assert")
10+
#undef Assert
11+
#define BITCOIN_QML_RESTORE_ASSERT_MACRO
12+
#endif
13+
814
#include <gmock/gmock.h>
15+
16+
#ifdef BITCOIN_QML_RESTORE_ASSERT_MACRO
17+
#pragma pop_macro("Assert")
18+
#undef BITCOIN_QML_RESTORE_ASSERT_MACRO
19+
#endif
20+
921
#include <interfaces/handler.h>
1022
#include <interfaces/node.h>
23+
#include <net_processing.h>
1124

1225
#include <coins.h>
1326
#include <node/types.h>

test/test_qmlinitexecutor_api.cpp

Lines changed: 130 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,153 @@
22
// Distributed under the MIT software license, see the accompanying
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

5+
#include <QtTest/QtTest>
6+
7+
#include <net_processing.h>
8+
#include <test/mocks/mocknode.h>
59
#include <qml/initexecutor.h>
610

7-
#include <QMetaObject>
8-
#include <QMetaMethod>
9-
#include <QObject>
10-
#include <QTest>
11+
#include <util/translation.h>
12+
13+
#include <stdexcept>
1114

12-
#include <type_traits>
15+
Q_DECLARE_METATYPE(interfaces::BlockAndHeaderTipInfo)
16+
17+
namespace {
18+
constexpr auto SIGNAL_TIMEOUT{5'000};
19+
}
1320

1421
class QmlInitExecutorApiTests : public QObject
1522
{
1623
Q_OBJECT
1724

1825
private Q_SLOTS:
19-
void exposesExpectedQObjectApi();
26+
void initTestCase();
27+
void initializeEmitsResultAndRunsOffMainThread();
28+
void initializeEmitsRunawayExceptionOnFailure();
29+
void shutdownEmitsResultAndRunsOffMainThread();
30+
void shutdownEmitsRunawayExceptionOnFailure();
2031
};
2132

22-
static bool HasMethodByName(const QMetaObject& meta, const QByteArray& method_name)
33+
void QmlInitExecutorApiTests::initTestCase()
2334
{
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");
2836
}
2937

30-
void QmlInitExecutorApiTests::exposesExpectedQObjectApi()
38+
void QmlInitExecutorApiTests::initializeEmitsResultAndRunsOffMainThread()
3139
{
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);
34145

35-
const QMetaObject& meta = QmlInitExecutor::staticMetaObject;
146+
executor.shutdown();
36147

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"});
42152
}
43153

44154
int RunQmlInitExecutorApiTests(int argc, char* argv[])

0 commit comments

Comments
 (0)