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