1 // Copyright (c) 2018-2020 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5 #include <qt/test/apptests.h>
6
7 #include <chainparams.h>
8 #include <key.h>
9 #include <qt/bitcoin.h>
10 #include <qt/bitcoingui.h>
11 #include <qt/networkstyle.h>
12 #include <qt/rpcconsole.h>
13 #include <shutdown.h>
14 #include <test/util/setup_common.h>
15 #include <univalue.h>
16 #include <validation.h>
17
18 #if defined(HAVE_CONFIG_H)
19 #include <config/bitcoin-config.h>
20 #endif
21
22 #include <QAction>
23 #include <QLineEdit>
24 #include <QScopedPointer>
25 #include <QSignalSpy>
26 #include <QTest>
27 #include <QTextEdit>
28 #include <QtGlobal>
29 #include <QtTest/QtTestWidgets>
30 #include <QtTest/QtTestGui>
31
32 namespace {
33 //! Call getblockchaininfo RPC and check first field of JSON output.
TestRpcCommand(RPCConsole * console)34 void TestRpcCommand(RPCConsole* console)
35 {
36 QTextEdit* messagesWidget = console->findChild<QTextEdit*>("messagesWidget");
37 QLineEdit* lineEdit = console->findChild<QLineEdit*>("lineEdit");
38 QSignalSpy mw_spy(messagesWidget, &QTextEdit::textChanged);
39 QVERIFY(mw_spy.isValid());
40 QTest::keyClicks(lineEdit, "getblockchaininfo");
41 QTest::keyClick(lineEdit, Qt::Key_Return);
42 QVERIFY(mw_spy.wait(1000));
43 QCOMPARE(mw_spy.count(), 4);
44 QString output = messagesWidget->toPlainText();
45 UniValue value;
46 value.read(output.right(output.size() - output.lastIndexOf(QChar::ObjectReplacementCharacter) - 1).toStdString());
47 QCOMPARE(value["chain"].get_str(), std::string("regtest"));
48 }
49 } // namespace
50
51 //! Entry point for BitcoinApplication tests.
appTests()52 void AppTests::appTests()
53 {
54 #ifdef Q_OS_MAC
55 if (QApplication::platformName() == "minimal") {
56 // Disable for mac on "minimal" platform to avoid crashes inside the Qt
57 // framework when it tries to look up unimplemented cocoa functions,
58 // and fails to handle returned nulls
59 // (https://bugreports.qt.io/browse/QTBUG-49686).
60 QWARN("Skipping AppTests on mac build with 'minimal' platform set due to Qt bugs. To run AppTests, invoke "
61 "with 'QT_QPA_PLATFORM=cocoa test_bitcoin-qt' on mac, or else use a linux or windows build.");
62 return;
63 }
64 #endif
65
66 fs::create_directories([] {
67 BasicTestingSetup test{CBaseChainParams::REGTEST}; // Create a temp data directory to backup the gui settings to
68 return gArgs.GetDataDirNet() / "blocks";
69 }());
70
71 qRegisterMetaType<interfaces::BlockAndHeaderTipInfo>("interfaces::BlockAndHeaderTipInfo");
72 m_app.parameterSetup();
73 m_app.createOptionsModel(true /* reset settings */);
74 QScopedPointer<const NetworkStyle> style(NetworkStyle::instantiate(Params().NetworkIDString()));
75 m_app.setupPlatformStyle();
76 m_app.createWindow(style.data());
77 connect(&m_app, &BitcoinApplication::windowShown, this, &AppTests::guiTests);
78 expectCallback("guiTests");
79 m_app.baseInitialize();
80 m_app.requestInitialize();
81 m_app.exec();
82 m_app.requestShutdown();
83 m_app.exec();
84
85 // Reset global state to avoid interfering with later tests.
86 LogInstance().DisconnectTestLogger();
87 AbortShutdown();
88 }
89
90 //! Entry point for BitcoinGUI tests.
guiTests(BitcoinGUI * window)91 void AppTests::guiTests(BitcoinGUI* window)
92 {
93 HandleCallback callback{"guiTests", *this};
94 connect(window, &BitcoinGUI::consoleShown, this, &AppTests::consoleTests);
95 expectCallback("consoleTests");
96 QAction* action = window->findChild<QAction*>("openRPCConsoleAction");
97 action->activate(QAction::Trigger);
98 }
99
100 //! Entry point for RPCConsole tests.
consoleTests(RPCConsole * console)101 void AppTests::consoleTests(RPCConsole* console)
102 {
103 HandleCallback callback{"consoleTests", *this};
104 TestRpcCommand(console);
105 }
106
107 //! Destructor to shut down after the last expected callback completes.
~HandleCallback()108 AppTests::HandleCallback::~HandleCallback()
109 {
110 auto& callbacks = m_app_tests.m_callbacks;
111 auto it = callbacks.find(m_callback);
112 assert(it != callbacks.end());
113 callbacks.erase(it);
114 if (callbacks.empty()) {
115 m_app_tests.m_app.quit();
116 }
117 }
118