From 80d926290ca3a6b601e0e8dbe7ff445ca6d28a98 Mon Sep 17 00:00:00 2001 From: Evgeniy Alekseev Date: Thu, 25 Aug 2016 13:33:08 +0300 Subject: [PATCH] finally implement bug reporting (#104) --- .../package/contents/ui/BugReport.qml | 57 +++++++++++++++++-- sources/awesome-widget/plugin/awactions.cpp | 18 ++++++ sources/awesome-widget/plugin/awactions.h | 1 + .../awesome-widget/plugin/awbugreporter.cpp | 54 +++++++++++++----- sources/awesome-widget/plugin/awbugreporter.h | 7 ++- sources/extsysmonsources/batterysource.cpp | 6 +- sources/extsysmonsources/gpuloadsource.cpp | 6 +- sources/extsysmonsources/gputempsource.cpp | 6 +- sources/extsysmonsources/processessource.cpp | 6 +- sources/test/testawbugreporter.cpp | 7 ++- 10 files changed, 137 insertions(+), 31 deletions(-) diff --git a/sources/awesome-widget/package/contents/ui/BugReport.qml b/sources/awesome-widget/package/contents/ui/BugReport.qml index a0bfac4..10f27b6 100644 --- a/sources/awesome-widget/package/contents/ui/BugReport.qml +++ b/sources/awesome-widget/package/contents/ui/BugReport.qml @@ -31,8 +31,8 @@ QtDialogs.Dialog { id: awBugReporter } - width: 640 - height: 480 + width: 480 + height: 640 property bool debug: awActions.isDebugEnabled() @@ -52,7 +52,7 @@ QtDialogs.Dialog { QtControls.GroupBox { width: parent.width - height: parent.height / 3 + height: parent.height / 5 title: i18n("Description") QtControls.TextArea { id: description @@ -63,7 +63,7 @@ QtDialogs.Dialog { } QtControls.GroupBox { width: parent.width - height: parent.height / 3 + height: parent.height / 5 title: i18n("Steps to reproduce") QtControls.TextArea { id: reproduce @@ -74,7 +74,7 @@ QtDialogs.Dialog { } QtControls.GroupBox { width: parent.width - height: parent.height / 3 + height: parent.height / 5 title: i18n("Expected result") QtControls.TextArea { id: expected @@ -83,13 +83,56 @@ QtDialogs.Dialog { textFormat: TextEdit.PlainText } } + QtControls.GroupBox { + width: parent.width + height: parent.height * 2 / 5 + title: i18n("Logs") + Row { + id: debugCmdLabel + width: parent.width + QtControls.Label { + width: parent.width * 2 / 5 + horizontalAlignment: Text.AlignJustify + verticalAlignment: Text.AlignVCenter + wrapMode: Text.WordWrap + text: i18n("Use command") + } + QtControls.TextField { + id: customTime + width: parent.width * 3 / 5 + readOnly: true + text: "QT_LOGGING_RULES=*=true plasmawindowed org.kde.plasma.awesomewidget" + } + } + QtControls.Button { + id: logButton + anchors.top: debugCmdLabel.bottom + width: parent.width + text: i18n("Load log file") + onClicked: logPath.open() + } + QtControls.TextArea { + anchors.top: logButton.bottom + anchors.bottom: parent.bottom + id: logBody + width: parent.width + textFormat: TextEdit.PlainText + } + + QtDialogs.FileDialog { + id: logPath + title: i18n("Open log file") + onAccepted: + logBody.text = awActions.getFileContent(logPath.fileUrl.toString().replace("file://", "")) + } + } } onAccepted: { if (debug) console.debug() var text = awBugReporter.generateText(description.text, reproduce.text, - expected.text) + expected.text, logBody.text) awBugReporter.sendBugReport(title.text, text) } @@ -104,6 +147,8 @@ QtDialogs.Dialog { Component.onCompleted: { if (debug) console.debug() + + awBugReporter.doConnect() } } diff --git a/sources/awesome-widget/plugin/awactions.cpp b/sources/awesome-widget/plugin/awactions.cpp index 31f15bf..a24663f 100644 --- a/sources/awesome-widget/plugin/awactions.cpp +++ b/sources/awesome-widget/plugin/awactions.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -56,6 +57,23 @@ void AWActions::checkUpdates(const bool showAnyway) } +QString AWActions::getFileContent(const QString path) const +{ + qCDebug(LOG_AW) << "Get content from file" << path; + + QFile inputFile(path); + if (!inputFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qCWarning(LOG_AW) << "Could not open file as text" + << inputFile.fileName(); + return QString(); + } + + QString output = inputFile.readAll(); + inputFile.close(); + return output; +} + + // HACK: since QML could not use QLoggingCategory I need this hack bool AWActions::isDebugEnabled() const { diff --git a/sources/awesome-widget/plugin/awactions.h b/sources/awesome-widget/plugin/awactions.h index 7185667..56e2bde 100644 --- a/sources/awesome-widget/plugin/awactions.h +++ b/sources/awesome-widget/plugin/awactions.h @@ -33,6 +33,7 @@ public: explicit AWActions(QObject *parent = nullptr); virtual ~AWActions(); Q_INVOKABLE void checkUpdates(const bool showAnyway = false); + Q_INVOKABLE QString getFileContent(const QString path) const; Q_INVOKABLE bool isDebugEnabled() const; Q_INVOKABLE bool runCmd(const QString cmd = QString("/usr/bin/true")) const; Q_INVOKABLE void showReadme() const; diff --git a/sources/awesome-widget/plugin/awbugreporter.cpp b/sources/awesome-widget/plugin/awbugreporter.cpp index 5620e6a..af3a364 100644 --- a/sources/awesome-widget/plugin/awbugreporter.cpp +++ b/sources/awesome-widget/plugin/awbugreporter.cpp @@ -34,9 +34,6 @@ AWBugReporter::AWBugReporter(QObject *parent) : QObject(parent) { qCDebug(LOG_AW) << __PRETTY_FUNCTION__; - - connect(this, SIGNAL(replyReceived(const int, const QString)), this, - SLOT(showInformation(const int, const QString))); } @@ -46,20 +43,32 @@ AWBugReporter::~AWBugReporter() } +void AWBugReporter::doConnect() +{ + // additional method for testing needs + connect(this, SIGNAL(replyReceived(const int, const QString)), this, + SLOT(showInformation(const int, const QString))); +} + + QString AWBugReporter::generateText(const QString description, const QString reproduce, - const QString expected) + const QString expected, + const QString logs) const { + // do not log logs here, it may have quite large size qCDebug(LOG_AW) << "Generate text with description" << description << "steps" << reproduce << "and expected result" << expected; QString output; - output += QString("**Description**\n\n%1\n").arg(description); - output += QString("**Step to reproduce**\n\n%1\n").arg(reproduce); - output += QString("**Expected result**\n\n%1\n").arg(expected); - output - += QString("**Version**\n\n%1").arg(getBuildData().join(QString("\n"))); + output += QString("**Description**\n\n%1\n\n").arg(description); + output += QString("**Step to reproduce**\n\n%1\n\n").arg(reproduce); + output += QString("**Expected result**\n\n%1\n\n").arg(expected); + output += QString("**Version**\n\n%1\n\n") + .arg(getBuildData().join(QString("\n"))); + // append logs + output += QString("**Logs**\n\n%1").arg(logs); return output; } @@ -122,16 +131,33 @@ void AWBugReporter::showInformation(const int number, const QString url) qCDebug(LOG_AW) << "Created issue with number" << number << "and url" << url; + // cache url first + m_lastBugUrl = url; + QMessageBox *msgBox = new QMessageBox(nullptr); msgBox->setAttribute(Qt::WA_DeleteOnClose); msgBox->setModal(false); msgBox->setWindowTitle(i18n("Issue created")); - msgBox->setText(i18n("Issue %1 has been created")); + msgBox->setText(i18n("Issue %1 has been created", number)); msgBox->setStandardButtons(QMessageBox::Open | QMessageBox::Close); msgBox->setIcon(QMessageBox::Information); - connect(msgBox, &QMessageBox::accepted, - [this, url]() { return QDesktopServices::openUrl(url); }); - - return msgBox->open(); + msgBox->open(this, SLOT(userReplyOnBugReport(QAbstractButton *))); +} + + +void AWBugReporter::userReplyOnBugReport(QAbstractButton *button) +{ + QMessageBox::ButtonRole ret + = static_cast(sender())->buttonRole(button); + qCInfo(LOG_AW) << "User select" << ret; + + switch (ret) { + case QMessageBox::AcceptRole: + QDesktopServices::openUrl(m_lastBugUrl); + break; + case QMessageBox::RejectRole: + default: + break; + } } diff --git a/sources/awesome-widget/plugin/awbugreporter.h b/sources/awesome-widget/plugin/awbugreporter.h index 6fb3e75..a241a2c 100644 --- a/sources/awesome-widget/plugin/awbugreporter.h +++ b/sources/awesome-widget/plugin/awbugreporter.h @@ -22,6 +22,7 @@ #include +class QAbstractButton; class QNetworkReply; class AWBugReporter : public QObject @@ -31,9 +32,11 @@ class AWBugReporter : public QObject public: explicit AWBugReporter(QObject *parent = nullptr); virtual ~AWBugReporter(); + Q_INVOKABLE void doConnect(); Q_INVOKABLE QString generateText(const QString description, const QString reproduce, - const QString expected); + const QString expected, + const QString logs) const; Q_INVOKABLE void sendBugReport(const QString title, const QString body); signals: @@ -42,8 +45,10 @@ signals: private slots: void issueReplyRecieved(QNetworkReply *reply); void showInformation(const int number, const QString url); + void userReplyOnBugReport(QAbstractButton *button); private: + QString m_lastBugUrl; }; diff --git a/sources/extsysmonsources/batterysource.cpp b/sources/extsysmonsources/batterysource.cpp index 1f2e337..22d1fbf 100644 --- a/sources/extsysmonsources/batterysource.cpp +++ b/sources/extsysmonsources/batterysource.cpp @@ -103,7 +103,7 @@ void BatterySource::run() { // adaptor QFile acFile(QString("%1/AC/online").arg(m_acpiPath)); - if (acFile.open(QIODevice::ReadOnly)) + if (acFile.open(QIODevice::ReadOnly | QIODevice::Text)) m_values[QString("battery/ac")] = (QString(acFile.readLine()).trimmed().toInt() == 1); acFile.close(); @@ -116,8 +116,8 @@ void BatterySource::run() QString("%1/BAT%2/energy_now").arg(m_acpiPath).arg(i)); QFile fullLevelFile( QString("%1/BAT%2/energy_full").arg(m_acpiPath).arg(i)); - if ((currentLevelFile.open(QIODevice::ReadOnly)) - && (fullLevelFile.open(QIODevice::ReadOnly))) { + if ((currentLevelFile.open(QIODevice::ReadOnly | QIODevice::Text)) + && (fullLevelFile.open(QIODevice::ReadOnly | QIODevice::Text))) { float batCurrent = QString(currentLevelFile.readLine()).trimmed().toFloat(); float batFull diff --git a/sources/extsysmonsources/gpuloadsource.cpp b/sources/extsysmonsources/gpuloadsource.cpp index 482c468..dcefab8 100644 --- a/sources/extsysmonsources/gpuloadsource.cpp +++ b/sources/extsysmonsources/gpuloadsource.cpp @@ -56,10 +56,14 @@ QString GPULoadSource::autoGpu() { QString gpu = QString("disable"); QFile moduleFile(QString("/proc/modules")); - if (!moduleFile.open(QIODevice::ReadOnly)) + if (!moduleFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qCWarning(LOG_AW) << "Could not open file as text" + << moduleFile.fileName(); return gpu; + } QString output = moduleFile.readAll(); + moduleFile.close(); if (output.contains(QString("fglrx"))) gpu = QString("ati"); else if (output.contains(QString("nvidia"))) diff --git a/sources/extsysmonsources/gputempsource.cpp b/sources/extsysmonsources/gputempsource.cpp index 437c143..17f607e 100644 --- a/sources/extsysmonsources/gputempsource.cpp +++ b/sources/extsysmonsources/gputempsource.cpp @@ -57,10 +57,14 @@ QString GPUTemperatureSource::autoGpu() { QString gpu = QString("disable"); QFile moduleFile(QString("/proc/modules")); - if (!moduleFile.open(QIODevice::ReadOnly)) + if (!moduleFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qCWarning(LOG_AW) << "Could not open file as text" + << moduleFile.fileName(); return gpu; + } QString output = moduleFile.readAll(); + moduleFile.close(); if (output.contains(QString("fglrx"))) gpu = QString("ati"); else if (output.contains(QString("nvidia"))) diff --git a/sources/extsysmonsources/processessource.cpp b/sources/extsysmonsources/processessource.cpp index 7cdcdf3..63fe151 100644 --- a/sources/extsysmonsources/processessource.cpp +++ b/sources/extsysmonsources/processessource.cpp @@ -87,15 +87,17 @@ void ProcessesSource::run() for (auto dir : directories) { QFile statusFile(QString("/proc/%1/status").arg(dir)); - if (!statusFile.open(QIODevice::ReadOnly)) + if (!statusFile.open(QIODevice::ReadOnly | QIODevice::Text)) continue; QFile cmdFile(QString("/proc/%1/cmdline").arg(dir)); - if (!cmdFile.open(QIODevice::ReadOnly)) + if (!cmdFile.open(QIODevice::ReadOnly | QIODevice::Text)) continue; QString output = statusFile.readAll(); if (output.contains(QString("running"))) running.append(cmdFile.readAll()); + statusFile.close(); + cmdFile.close(); } m_values[QString("ps/running/count")] = running.count(); diff --git a/sources/test/testawbugreporter.cpp b/sources/test/testawbugreporter.cpp index a6258f6..a2c116d 100644 --- a/sources/test/testawbugreporter.cpp +++ b/sources/test/testawbugreporter.cpp @@ -38,8 +38,9 @@ void TestAWBugReporter::cleanupTestCase() void TestAWBugReporter::test_generateText() { - data = AWTestLibrary::randomStringList(3); - QString output = plugin->generateText(data.at(0), data.at(1), data.at(2)); + data = AWTestLibrary::randomStringList(4); + QString output + = plugin->generateText(data.at(0), data.at(1), data.at(2), data.at(3)); for (auto string : data) QVERIFY(output.contains(string)); @@ -51,7 +52,7 @@ void TestAWBugReporter::test_sendBugReport() QSignalSpy spy(plugin, SIGNAL(replyReceived(bool, QString))); plugin->sendBugReport( AWTestLibrary::randomString(), - plugin->generateText(data.at(0), data.at(1), data.at(2))); + plugin->generateText(data.at(0), data.at(1), data.at(2), data.at(3))); QVERIFY(spy.wait(5000)); QVariantList arguments = spy.takeFirst();