File indexing completed on 2024-04-14 15:32:41
0001 /******************************************************************* 0002 * duplicatefinderjob.cpp 0003 * SPDX-FileCopyrightText: 2011 Matthias Fuchs <mat69@gmx.net> 0004 * SPDX-FileCopyrightText: 2019-2021 Harald Sitter <sitter@kde.org> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 * 0008 ******************************************************************/ 0009 0010 #include "duplicatefinderjob.h" 0011 0012 #include <QtConcurrent> 0013 0014 #include "backtracegenerator.h" 0015 #include "debuggermanager.h" 0016 #include "drkonqi.h" 0017 #include "drkonqi_debug.h" 0018 #include "parser/backtraceparser.h" 0019 0020 DuplicateFinderJob::DuplicateFinderJob(const QList<Bugzilla::Bug::Ptr> &bugs, BugzillaManager *manager, QObject *parent) 0021 : KJob(parent) 0022 , m_manager(manager) 0023 , m_bugs(bugs) 0024 { 0025 qCDebug(DRKONQI_LOG) << "Possible duplicates:" << m_bugs.size(); 0026 connect(m_manager, &BugzillaManager::bugReportFetched, this, &DuplicateFinderJob::slotBugReportFetched); 0027 connect(m_manager, &BugzillaManager::bugReportError, this, &DuplicateFinderJob::slotError); 0028 0029 connect(m_manager, &BugzillaManager::commentsFetched, this, &DuplicateFinderJob::slotCommentsFetched); 0030 connect(m_manager, &BugzillaManager::commentsError, this, &DuplicateFinderJob::slotError); 0031 } 0032 0033 DuplicateFinderJob::~DuplicateFinderJob() = default; 0034 0035 void DuplicateFinderJob::start() 0036 { 0037 if (qEnvironmentVariableIntValue("DRKONQI_SKIP_DUPES") > 0) { 0038 metaObject()->invokeMethod(this, &DuplicateFinderJob::emitResult); 0039 return; 0040 } 0041 0042 analyzeNextBug(); 0043 } 0044 0045 DuplicateFinderJob::Result DuplicateFinderJob::result() const 0046 { 0047 return m_result; 0048 } 0049 0050 void DuplicateFinderJob::analyzeNextBug() 0051 { 0052 if (m_bugs.isEmpty()) { 0053 emitResult(); 0054 return; 0055 } 0056 0057 m_bug = m_bugs.takeFirst(); 0058 qCDebug(DRKONQI_LOG) << "Fetching:" << m_bug->id(); 0059 m_manager->fetchComments(m_bug, this); 0060 } 0061 0062 void DuplicateFinderJob::fetchBug(int bugId) 0063 { 0064 if (bugId > 0) { 0065 qCDebug(DRKONQI_LOG) << "Fetching:" << bugId; 0066 m_manager->fetchBugReport(bugId, this); 0067 } else { 0068 qCDebug(DRKONQI_LOG) << "Bug id not valid:" << bugId; 0069 analyzeNextBug(); 0070 } 0071 } 0072 0073 void DuplicateFinderJob::slotBugReportFetched(const Bugzilla::Bug::Ptr &bug, QObject *owner) 0074 { 0075 if (this != owner) { 0076 return; 0077 } 0078 0079 m_bug = bug; 0080 qCDebug(DRKONQI_LOG) << "Fetching:" << m_bug->id(); 0081 m_manager->fetchComments(m_bug, this); 0082 } 0083 0084 void DuplicateFinderJob::slotCommentsFetched(const QList<Bugzilla::Comment::Ptr> &comments, QObject *owner) 0085 { 0086 if (this != owner) { 0087 return; 0088 } 0089 0090 // NOTE: we do not hold the comments in our bug object, once they go out 0091 // of scope they are gone again. We have no use for keeping them in memory 0092 // a user might look at 3 out of 20 bugs, and for those we can simply 0093 // request the comments again instead of holding the potentially very large 0094 // comments in memory. 0095 0096 BacktraceGenerator *btGenerator = DrKonqi::debuggerManager()->backtraceGenerator(); 0097 const QList<BacktraceLine> ourTraceLines = btGenerator->parser()->parsedBacktraceLines(); 0098 0099 // QFuture the parsing. We'll not want to block the GUI thread with this nonesense. 0100 auto watcher = new QFutureWatcher<ParseBugBacktraces::DuplicateRating>(this); 0101 connect(watcher, &std::remove_pointer_t<decltype(watcher)>::finished, this, [this, watcher] { 0102 // runs on our thread again 0103 watcher->deleteLater(); 0104 commentsParsed(watcher->result()); 0105 }); 0106 auto future = QtConcurrent::run([comments, ourTraceLines]() -> ParseBugBacktraces::DuplicateRating { 0107 ParseBugBacktraces parse(comments); 0108 parse.parse(); 0109 return parse.findDuplicate(ourTraceLines); 0110 }); 0111 watcher->setFuture(future); 0112 } 0113 0114 void DuplicateFinderJob::commentsParsed(ParseBugBacktraces::DuplicateRating rating) 0115 { 0116 qCDebug(DRKONQI_LOG) << "Duplicate rating:" << rating; 0117 0118 // TODO handle more cases here 0119 if (rating != ParseBugBacktraces::PerfectDuplicate) { 0120 qCDebug(DRKONQI_LOG) << "Bug" << m_bug->id() << "most likely not a duplicate:" << rating; 0121 analyzeNextBug(); 0122 return; 0123 } 0124 0125 bool unknownStatus = (m_bug->status() == Bugzilla::Bug::Status::Unknown); 0126 bool unknownResolution = (m_bug->resolution() == Bugzilla::Bug::Resolution::Unknown); 0127 0128 // The Bug is a duplicate, now find out the status and resolution of the existing report 0129 if (m_bug->resolution() == Bugzilla::Bug::Resolution::DUPLICATE) { 0130 qCDebug(DRKONQI_LOG) << "Found duplicate is a duplicate itself."; 0131 if (!m_result.duplicate) { 0132 m_result.duplicate = m_bug->id(); 0133 } 0134 fetchBug(m_bug->dupe_of()); 0135 } else if (unknownStatus || unknownResolution) { 0136 // A resolution is unknown when the bug is unresolved. 0137 // Status generally is never unknown. 0138 qCDebug(DRKONQI_LOG) << "Either the status or the resolution is unknown."; 0139 qCDebug(DRKONQI_LOG) << "Status \"" << m_bug->status() << "\" known:" << !unknownStatus; 0140 qCDebug(DRKONQI_LOG) << "Resolution \"" << m_bug->resolution() << "\" known:" << !unknownResolution; 0141 analyzeNextBug(); 0142 } else { 0143 if (!m_result.duplicate) { 0144 m_result.duplicate = m_bug->id(); 0145 } 0146 m_result.parentDuplicate = m_bug->id(); 0147 m_result.status = m_bug->status(); 0148 m_result.resolution = m_bug->resolution(); 0149 qCDebug(DRKONQI_LOG) << "Found duplicate information (id/status/resolution):" << m_bug->id() << m_bug->status() << m_bug->resolution(); 0150 emitResult(); 0151 } 0152 } 0153 0154 void DuplicateFinderJob::slotError(const QString &message, QObject *owner) 0155 { 0156 if (this != owner) { 0157 return; 0158 } 0159 qCDebug(DRKONQI_LOG) << "Error fetching bug:" << message; 0160 analyzeNextBug(); 0161 }