From 89893576179000aab90d683a43fabf2b2fb2c3b2 Mon Sep 17 00:00:00 2001 From: jasplin Date: Tue, 11 May 2010 14:42:17 +0200 Subject: Added result history details to ASF Stats page. --- src/bm/asfstats.cpp | 33 ++-- src/bm/asfstats.h | 21 +-- src/bm/bmrequest.cpp | 508 +++++++++++++++++++++++++++++++++----------------- src/bm/bmrequest.h | 6 +- src/bmclient/main.cpp | 19 +- 5 files changed, 388 insertions(+), 199 deletions(-) diff --git a/src/bm/asfstats.cpp b/src/bm/asfstats.cpp index 960e1c2..4074a77 100644 --- a/src/bm/asfstats.cpp +++ b/src/bm/asfstats.cpp @@ -124,9 +124,7 @@ Step 2: Sample the stable and smoothed RHs at fromTimestamp and toTimestamp and // ### 2 B DOCUMENTED! -void ASFStats::compute( - const QList &rhInfos, UnstableStats *usStats, int *regressed, - int *unchanged, int *improved) +void ASFStats::compute(const QList &rhInfos, StatsInfo *statsInfo) { Q_ASSERT((fromTimestamp >= 0) && (fromTimestamp <= toTimestamp)); Q_ASSERT((diffTolerance >= 0.0) && (diffTolerance <= 100.0)); @@ -134,8 +132,15 @@ void ASFStats::compute( Q_ASSERT((sfTolerance >= 0.0) && (sfTolerance <= 100.0)); Q_ASSERT((lfTolerance >= 0.0) && (lfTolerance <= 100.0)); - usStats->unstable.fill(false, rhInfos.size()); - *regressed = *unchanged = *improved = 0; + statsInfo->regressed.fill(false, rhInfos.size()); + statsInfo->unchanged.fill(false, rhInfos.size()); + statsInfo->improved.fill(false, rhInfos.size()); + statsInfo->unstable.fill(false, rhInfos.size()); + statsInfo->usZero.fill(false, rhInfos.size()); + statsInfo->usLowFromPos.fill(false, rhInfos.size()); + statsInfo->usLowSF.fill(false, rhInfos.size()); + statsInfo->usLowLF.fill(false, rhInfos.size()); + statsInfo->usHighMaxLD.fill(false, rhInfos.size()); for (int i = 0; i < rhInfos.size(); ++i) { bool zerosFound = false; @@ -162,29 +167,29 @@ void ASFStats::compute( bool unstable = false; if (zerosFound) { unstable = true; - ++(usStats->zeroCount); + statsInfo->usZero.setBit(i); } if (fromPos == -1) { unstable = true; - ++(usStats->lowFromPosCount); + statsInfo->usLowFromPos.setBit(i); } if (sf < sfTolerance) { unstable = true; - ++(usStats->lowSFCount); + statsInfo->usLowSF.setBit(i); } if (lf < lfTolerance) { unstable = true; - ++(usStats->lowLFCount); + statsInfo->usLowLF.setBit(i); } if (maxLD > maxLDTolerance) { unstable = true; - ++(usStats->highMaxLDCount); + statsInfo->usHighMaxLD.setBit(i); } if (unstable) { // RH is unstable, so mark it as such ... - usStats->unstable.setBit(i); + statsInfo->unstable.setBit(i); } else { // RH is stable, so sample the smoothed values at fromTimestamp and toTimestamp and @@ -197,14 +202,14 @@ void ASFStats::compute( const qreal v1 = rhInfos.at(i)->value(fromPos); const qreal v2 = rhInfos.at(i)->value(toPos); if ((100 * (qMax(v1, v2) / qMin(v1, v2) - 1)) <= diffTolerance) { - ++(*unchanged); + statsInfo->unchanged.setBit(i); } else { const qreal diff = BMMisc::lowerIsBetter(rhInfos.at(i)->metric()) ? (v1 - v2) : (v2 - v1); if (diff < 0) - ++(*regressed); + statsInfo->regressed.setBit(i); else - ++(*improved); + statsInfo->improved.setBit(i); } } } diff --git a/src/bm/asfstats.h b/src/bm/asfstats.h index c153376..83bbc65 100644 --- a/src/bm/asfstats.h +++ b/src/bm/asfstats.h @@ -43,20 +43,19 @@ public: , toTimestamp(toTimestamp) {} - struct UnstableStats { + struct StatsInfo { + QBitArray regressed; + QBitArray unchanged; + QBitArray improved; QBitArray unstable; - int zeroCount; - int lowFromPosCount; - int lowSFCount; - int lowLFCount; - int highMaxLDCount; - UnstableStats() - : zeroCount(0), lowFromPosCount(), lowSFCount(0), lowLFCount(0), highMaxLDCount(0) {} + QBitArray usZero; + QBitArray usLowFromPos; + QBitArray usLowSF; + QBitArray usLowLF; + QBitArray usHighMaxLD; }; - void compute( - const QList &rhInfos, UnstableStats *usStats, int *regressed, - int *unchanged, int *improved); + void compute(const QList &rhInfos, StatsInfo *statsInfo); private: // Tolerances: diff --git a/src/bm/bmrequest.cpp b/src/bm/bmrequest.cpp index 46539b8..33769a5 100644 --- a/src/bm/bmrequest.cpp +++ b/src/bm/bmrequest.cpp @@ -1686,6 +1686,123 @@ struct BenchmarkStats2 { }; }; +// ### 2 B DOCUMENTED! +static bool extractBMAndContextFromRH( + int bmcontextId, const QString &reqName, QSqlDatabase *database, + QString *testCase, QString *testFunction, QString *dataTag, QString *metric, + QString *platform, QString *host, QString *gitRepo, QString *gitBranch, QString *error) +{ + QSqlQuery query(*database); + + // Get benchmark ID and context ID ... + if (!query.exec( + QString("SELECT benchmarkId, contextId FROM bmcontext WHERE id=%1;") + .arg(bmcontextId))) { + *error = errorReply( + query, reqName, QString("failed to get IDs for benchmark and context")); + return false; + } + int benchmarkId; + int contextId; + if (query.next()) { + benchmarkId = query.value(0).toInt(); + contextId = query.value(1).toInt(); + } else { + *error = errorReply( + query, reqName, QString("no such benchmark and context (programming error?)")); + return false; + } + + // Get benchmark ... + if (!query.exec( + QString("SELECT testCase, testFunction, dataTag FROM benchmark WHERE id=%1;") + .arg(benchmarkId))) { + *error = errorReply(query, reqName, QString("failed to get benchmark")); + return false; + } + if (query.next()) { + *testCase = query.value(0).toString(); + *testFunction = query.value(1).toString(); + *dataTag = query.value(2).toString(); + } else { + *error = errorReply(query, reqName, QString("no such benchmark (programming error?)")); + return false; + } + + // Get metric ID, platform ID, host ID, and branch ID ... + if (!query.exec( + QString("SELECT metricId, platformId, hostId, branchId FROM context WHERE id=%1;") + .arg(contextId))) { + *error = errorReply( + query, reqName, QString("failed to get IDs for metrid, platform, host, and branch")); + return false; + } + int metricId; + int platformId; + int hostId; + int branchId; + if (query.next()) { + metricId = query.value(0).toInt(); + platformId = query.value(1).toInt(); + hostId = query.value(2).toInt(); + branchId = query.value(3).toInt(); + } else { + *error = errorReply(query, reqName, QString("no such context (programming error?)")); + return false; + } + + // Get metric ... + if (!query.exec(QString("SELECT name FROM metric WHERE id=%1;").arg(metricId))) { + *error = errorReply(query, reqName, QString("failed to get metric")); + return false; + } + if (query.next()) { + *metric = query.value(0).toString(); + } else { + *error = errorReply(query, reqName, QString("no such metric (programming error?)")); + return false; + } + + // Get platform ... + if (!query.exec(QString("SELECT name FROM platform WHERE id=%1;").arg(platformId))) { + *error = errorReply(query, reqName, QString("failed to get platform")); + return false; + } + if (query.next()) { + *platform = query.value(0).toString(); + } else { + *error = errorReply(query, reqName, QString("no such platform (programming error?)")); + return false; + } + + // Get host ... + if (!query.exec(QString("SELECT name FROM host WHERE id=%1;").arg(hostId))) { + *error = errorReply(query, reqName, QString("failed to get host")); + return false; + } + if (query.next()) { + *host = query.value(0).toString(); + } else { + *error = errorReply(query, reqName, QString("no such host (programming error?)")); + return false; + } + + // Get branch ... + if (!query.exec(QString("SELECT gitRepo, gitBranch FROM branch WHERE id=%1;").arg(branchId))) { + *error = errorReply(query, reqName, QString("failed to get branch")); + return false; + } + if (query.next()) { + *gitRepo = query.value(0).toString(); + *gitBranch = query.value(1).toString(); + } else { + *error = errorReply(query, reqName, QString("no such branch (programming error?)")); + return false; + } + + return true; +} + // --- GetHistory --- QByteArray BMRequest_GetHistory::toRequestBuffer(QString *) { @@ -1694,10 +1811,10 @@ QByteArray BMRequest_GetHistory::toRequestBuffer(QString *) "testCase=\"%2\" testFunction=\"%3\" dataTag=\"%4\" metric=\"%5\" platform=\"%6\" " "host=\"%7\" gitRepo=\"%8\" gitBranch=\"%9\" timestamp1=\"%10\" " "timestamp2=\"%11\" diffTolerance=\"%12\" stabTolerance=\"%13\" " - "maxSize=\"%14\" />") + "maxSize=\"%14\" explicitBMContextId=\"%15\" />") .arg(name()).arg(testCase).arg(testFunction).arg(dataTag).arg(metric).arg(platform) .arg(host).arg(gitRepo).arg(gitBranch).arg(timestamp1).arg(timestamp2) - .arg(diffTolerance).arg(stabTolerance).arg(maxSize); + .arg(diffTolerance).arg(stabTolerance).arg(maxSize).arg(explicitBMContextId); return xmlConvert(request); } @@ -1721,22 +1838,44 @@ QByteArray BMRequest_GetHistory::toReplyBuffer() Q_ASSERT(ok); maxSize = contextElem.attributeNode("maxSize").value().toInt(&ok); Q_ASSERT(ok); + explicitBMContextId = contextElem.attributeNode("explicitBMContextId").value().toInt(&ok); + Q_ASSERT(ok); - QString reply = QString("").arg(name()); + if (explicitBMContextId >= 0) { - int benchmarkId; - if (!getBenchmarkId(&reply, &benchmarkId, testCase, testFunction, dataTag)) - return xmlConvert(reply); + QString error; + if (!extractBMAndContextFromRH( + explicitBMContextId, name(), database, &testCase, &testFunction, &dataTag, + &metric, &platform, &host, &gitRepo, &gitBranch, &error)) { + return xmlConvert(error); + } + } + + QString reply = QString("").arg(name()); int bmcontextId; - if (!getBMContextId( - &reply, &bmcontextId, benchmarkId, metric, platform, host, gitRepo, gitBranch)) - return xmlConvert(reply); + if (explicitBMContextId >= 0) { + bmcontextId = explicitBMContextId; + } else { + int benchmarkId; + if (!getBenchmarkId(&reply, &benchmarkId, testCase, testFunction, dataTag)) + return xmlConvert(reply); + if (!getBMContextId( + &reply, &bmcontextId, benchmarkId, metric, platform, host, gitRepo, gitBranch)) + return xmlConvert(reply); + } if (!appendBranchHistoryToReply( &reply, bmcontextId, "", timestamp1, timestamp2, diffTolerance, stabTolerance, maxSize)) return xmlConvert(reply); + reply.append( + QString( + "") + .arg(testCase).arg(testFunction).arg(dataTag).arg(metric).arg(platform) + .arg(host).arg(gitRepo).arg(gitBranch)); + reply.append(""); return xmlConvert(reply); @@ -1881,16 +2020,19 @@ void BMRequest_GetHistory::handleReply_HTML(const QStringList &args) const offset = args.indexOf("get"); Q_ASSERT(offset >= 0); - Q_ASSERT(args.size() == offset + 18); + Q_ASSERT(args.size() >= offset + 18); const QString type = args.at(offset + 1); - testCase = args.at(offset + 2); - testFunction = args.at(offset + 3); - dataTag = args.at(offset + 4); - metric = args.at(offset + 5); - platform = args.at(offset + 6); - host = args.at(offset + 7); - gitRepo = args.at(offset + 8); - gitBranch = args.at(offset + 9); + + QDomNodeList bmContextNodes = doc.elementsByTagName("bmcontext"); + QDomElement bmContextElem = bmContextNodes.at(0).toElement(); + testCase = bmContextElem.attributeNode("testCase").value(); + testFunction = bmContextElem.attributeNode("testFunction").value(); + dataTag = bmContextElem.attributeNode("dataTag").value(); + metric = bmContextElem.attributeNode("metric").value(); + platform = bmContextElem.attributeNode("platform").value(); + host = bmContextElem.attributeNode("host").value(); + gitRepo = bmContextElem.attributeNode("gitRepo").value(); + gitBranch = bmContextElem.attributeNode("gitBranch").value(); bool ok; ok = BMMisc::getTimestamp(args, offset + 11, ×tamp1); @@ -4534,145 +4676,23 @@ QByteArray BMRequest_GetResult::toReplyBuffer() } deleteQuery(query); - // Get benchmark ID and context ID ... - query = createQuery(); - if (!query->exec( - QString("SELECT benchmarkId, contextId FROM bmcontext WHERE id=%1;") - .arg(bmcontextId))) { - reply = errorReply(*query, name(), QString("failed to get result (exec() failed (3))")); - deleteQuery(query); - return xmlConvert(reply); - } - int benchmarkId; - int contextId; - if (query->next()) { - benchmarkId = query->value(0).toInt(); - contextId = query->value(1).toInt(); - } else { - reply = errorReply( - *query, name(), QString("no such benchmark and context (programming error?)")); - deleteQuery(query); - return xmlConvert(reply); - } - deleteQuery(query); - // Get benchmark ... - query = createQuery(); - if (!query->exec( - QString("SELECT testCase, testFunction, dataTag FROM benchmark WHERE id=%1;") - .arg(benchmarkId))) { - reply = errorReply(*query, name(), QString("failed to get result (exec() failed (4))")); - deleteQuery(query); - return xmlConvert(reply); - } + QString error; + QString testCase; QString testFunction; QString dataTag; - if (query->next()) { - testCase = query->value(0).toString(); - testFunction = query->value(1).toString(); - dataTag = query->value(2).toString(); - } else { - reply = errorReply(*query, name(), QString("no such benchmark (programming error?)")); - deleteQuery(query); - return xmlConvert(reply); - } - deleteQuery(query); - - // Get metric ID, platform ID, host ID, and branch ID ... - query = createQuery(); - if (!query->exec( - QString("SELECT metricId, platformId, hostId, branchId FROM context WHERE id=%1;") - .arg(contextId))) { - reply = errorReply(*query, name(), QString("failed to get result (exec() failed (5))")); - deleteQuery(query); - return xmlConvert(reply); - } - int metricId; - int platformId; - int hostId; - int branchId; - if (query->next()) { - metricId = query->value(0).toInt(); - platformId = query->value(1).toInt(); - hostId = query->value(2).toInt(); - branchId = query->value(3).toInt(); - } else { - reply = errorReply(*query, name(), QString("no such context (programming error?)")); - deleteQuery(query); - return xmlConvert(reply); - } - deleteQuery(query); - - // Get metric ... - query = createQuery(); - if (!query->exec(QString("SELECT name FROM metric WHERE id=%1;").arg(metricId))) { - reply = errorReply(*query, name(), QString("failed to get result (exec() failed (6))")); - deleteQuery(query); - return xmlConvert(reply); - } QString metric; - if (query->next()) { - metric = query->value(0).toString(); - } else { - reply = errorReply(*query, name(), QString("no such metric (programming error?)")); - deleteQuery(query); - return xmlConvert(reply); - } - deleteQuery(query); - - // Get platform ... - query = createQuery(); - if (!query->exec(QString("SELECT name FROM platform WHERE id=%1;").arg(platformId))) { - reply = errorReply(*query, name(), QString("failed to get result (exec() failed (7))")); - deleteQuery(query); - return xmlConvert(reply); - } QString platform; - if (query->next()) { - platform = query->value(0).toString(); - } else { - reply = errorReply(*query, name(), QString("no such platform (programming error?)")); - deleteQuery(query); - return xmlConvert(reply); - } - deleteQuery(query); - - // Get host ... - query = createQuery(); - if (!query->exec(QString("SELECT name FROM host WHERE id=%1;").arg(hostId))) { - reply = errorReply(*query, name(), QString("failed to get result (exec() failed (8))")); - deleteQuery(query); - return xmlConvert(reply); - } QString host; - if (query->next()) { - host = query->value(0).toString(); - } else { - reply = errorReply(*query, name(), QString("no such host (programming error?)")); - deleteQuery(query); - return xmlConvert(reply); - } - deleteQuery(query); - - // Get branch ... - query = createQuery(); - if (!query->exec(QString("SELECT gitRepo, gitBranch FROM branch WHERE id=%1;").arg(branchId))) { - reply = errorReply(*query, name(), QString("failed to get result (exec() failed (9))")); - deleteQuery(query); - return xmlConvert(reply); - } QString gitRepo; QString gitBranch; - if (query->next()) { - gitRepo = query->value(0).toString(); - gitBranch = query->value(1).toString(); - } else { - reply = errorReply(*query, name(), QString("no such branch (programming error?)")); - deleteQuery(query); - return xmlConvert(reply); + if (!extractBMAndContextFromRH( + bmcontextId, name(), database, &testCase, &testFunction, &dataTag, &metric, &platform, + &host, &gitRepo, &gitBranch, &error)) { + return xmlConvert(error); } - deleteQuery(query); + // Get values ... query = createQuery(); @@ -7356,31 +7376,39 @@ QByteArray BMRequest_ASFStatsGetValues::toReplyBuffer() reply = QString("").arg(name()); ASFStats asfStats(diffTol, stabTol, sfTol, lfTol, maxLDTol, fromTimestamp, toTimestamp); - ASFStats::UnstableStats usStats; - int regressed = 0; - int unchanged = 0; - int improved = 0; - asfStats.compute(rhInfos, &usStats, ®ressed, &unchanged, &improved); + ASFStats::StatsInfo statsInfo; + asfStats.compute(rhInfos, &statsInfo); // Add stats to reply ... reply += QString( "") - .arg(regressed).arg(unchanged).arg(improved) - .arg(usStats.unstable.count(true)) - .arg(usStats.zeroCount) - .arg(usStats.lowFromPosCount) - .arg(usStats.lowSFCount) - .arg(usStats.lowLFCount) - .arg(usStats.highMaxLDCount); - - // Add IDs of unstable result histories to reply ... - reply += ""; - for (int i = 0; i < usStats.unstable.size(); ++i) - if (usStats.unstable.testBit(i)) - reply += QString("%1 ").arg(rhInfos.at(i)->bmcontextId()); - reply += ""; + .arg(statsInfo.regressed.count(true)) + .arg(statsInfo.unchanged.count(true)) + .arg(statsInfo.improved.count(true)) + .arg(statsInfo.unstable.count(true)) + .arg(statsInfo.usZero.count(true)) + .arg(statsInfo.usLowFromPos.count(true)) + .arg(statsInfo.usLowSF.count(true)) + .arg(statsInfo.usLowLF.count(true)) + .arg(statsInfo.usHighMaxLD.count(true)); + + // Add result history stats to reply ... + for (int i = 0; i < rhInfos.size(); ++i) { + reply += QString( + "") + .arg(rhInfos.at(i)->bmcontextId()) + .arg(statsInfo.regressed.testBit(i)) + .arg(statsInfo.unchanged.testBit(i)) + .arg(statsInfo.improved.testBit(i)) + .arg(statsInfo.usZero.testBit(i)) + .arg(statsInfo.usLowFromPos.testBit(i)) + .arg(statsInfo.usLowSF.testBit(i)) + .arg(statsInfo.usLowLF.testBit(i)) + .arg(statsInfo.usHighMaxLD.testBit(i)); + } //---------------------------------------------------------------------------------------- @@ -7416,6 +7444,75 @@ QByteArray BMRequest_ASFStatsGetValues::toReplyBuffer() return xmlConvert(reply); } +// ### 2 B DOCUMENTED! +struct RHStats { + int id; // BM context ID + bool rg; // regressed + bool uc; // unchanged + bool im; // improved + + // Conditions for being unstable: + bool zr; // zero existence + bool fp; // 'from' position to low (i.e. no data before the 'from' time) + bool sf; // stability fraction too low + bool lf; // unique level fraction too low + bool ld; // max level difference to high + + RHStats( + const int id, const bool rg, const bool uc, const bool im, const bool zr, const bool fp, + const bool sf, const bool lf, const bool ld) + : id(id), rg(rg), uc(uc), im(im), zr(zr), fp(fp), sf(sf), lf(lf), ld(ld) {} + + struct LessThan { + LessThan() {} + bool operator()(const RHStats *s1, const RHStats *s2) { + + // Ranking priority: + // 1: Regressed (ranked first) + // 2: Unchanged + // 3: Improved + // 4: All others (ranked last) + + if (s1->rg && !s2->rg) + return true; + if (!s1->rg && s2->rg) + return false; + + if (s1->uc && !s2->uc) + return true; + if (!s1->uc && s2->uc) + return false; + + if (s1->im && !s2->im) + return true; + if (!s1->im && s2->im) + return false; + + return false; + } + }; +}; + +// ### 2 B DOCUMENTED! +static QString createRHDetailsLink( + const QString &webServer, const QString &server, const QString &styleSheet, int bmcontextId) +{ + QString url; + url = QString("%1/cgi-bin/bmclientwrapper").arg(webServer); + url += QString("?command=-server %1").arg(server); + url += + " get detailspage dummyTestCase dummyTestFunction dummyDataTag dummyMetric dummyPlatform" + " dummyHost dummyGitRepo dummyGitBranch"; // ### this is a bit silly, so fix it by making + // the bmclient independent of argument positions + QDateTime dateTime = QDateTime::currentDateTime(); + url += QString(" -timerange first last 0.00 0 -1 %1 %2 -bmcontextid %3") + .arg(styleSheet) + .arg(dateTime.toTime_t()) + .arg(bmcontextId); + + return QString("details").arg(url); +} + void BMRequest_ASFStatsGetValues::handleReply_HTML(const QStringList &args) const { QString error; @@ -7447,6 +7544,14 @@ void BMRequest_ASFStatsGetValues::handleReply_HTML(const QStringList &args) cons return; } + // Extract the web server from the style sheet ... + QRegExp rx("^(\\S+:\\d+)\\D+$"); + if (rx.indexIn(styleSheet) == -1) { + BMMisc::printHTMLErrorPage(QString("failed to extract web server from style sheet")); + return; + } + const QString webServer = rx.cap(1); + // *** Header *** QString reply = QString("\n\n"); reply += QString("\n").arg(styleSheet); @@ -7628,6 +7733,77 @@ void BMRequest_ASFStatsGetValues::handleReply_HTML(const QStringList &args) cons reply += "\n"; reply += "\n"; + + // Result history details table ... + + reply += "

"; + + reply += "
\n"; + reply += ""; + reply += ""; + reply += ""; + reply += ""; + reply += "" + ""; + reply += ""; + reply += "" + ""; + reply += "" + ""; + reply += "
RG:Regressed
UC:Unchanged
IM:Improved
ZR:Zero existence
FP:'From' position to low (i.e. no data before the 'from' time)
SF:Stability fraction too low
LF:Unique level fraction too high
LD:Max level difference to high
\n"; + + reply += "
\n"; + + QList rhStats; + QDomNodeList rhNodes = doc.elementsByTagName("rh"); + for (int i = 0; i < rhNodes.size(); ++i) { + QDomElement rhElem = rhNodes.at(i).toElement(); + rhStats.append( + new RHStats( + rhElem.attributeNode("id").value().toInt(), + rhElem.attributeNode("rg").value().toInt(), + rhElem.attributeNode("uc").value().toInt(), + rhElem.attributeNode("im").value().toInt(), + rhElem.attributeNode("zr").value().toInt(), + rhElem.attributeNode("fp").value().toInt(), + rhElem.attributeNode("sf").value().toInt(), + rhElem.attributeNode("lf").value().toInt(), + rhElem.attributeNode("ld").value().toInt() + ) + ); + } + + qSort(rhStats.begin(), rhStats.end(), RHStats::LessThan()); + + for (int i = 0; i < rhStats.size(); ++i) { + reply += QString( + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "\n") + .arg(i) + .arg(createRHDetailsLink(webServer, server, styleSheet, rhStats.at(i)->id)) + .arg(rhStats.at(i)->rg ? "RG" : "") + .arg(rhStats.at(i)->uc ? "UC" : "") + .arg(rhStats.at(i)->im ? "IM" : "") + .arg(rhStats.at(i)->zr ? "ZR" : "") + .arg(rhStats.at(i)->fp ? "FP" : "") + .arg(rhStats.at(i)->sf ? "SF" : "") + .arg(rhStats.at(i)->lf ? "LF" : "") + .arg(rhStats.at(i)->ld ? "LD" : ""); + } + + reply += "
%1%2%3%4%5%6%7%8%9%10
"; + + reply += ""; BMMisc::printHTMLOutput(reply); diff --git a/src/bm/bmrequest.h b/src/bm/bmrequest.h index 8d90d88..12d61a1 100644 --- a/src/bm/bmrequest.h +++ b/src/bm/bmrequest.h @@ -308,12 +308,13 @@ public: const QString &gitRepo, const QString &gitBranch, const QString ×tamp1, const QString ×tamp2, const qreal diffTolerance, const int stabTolerance, const int maxSize, - const QString &styleSheet, const int currTimestamp) + const QString &styleSheet, const int currTimestamp, const int explicitBMContextId = -1) : testCase(testCase), testFunction(testFunction), dataTag(dataTag), metric(metric) , platform(platform), host(host), gitRepo(gitRepo), gitBranch(gitBranch) , timestamp1(timestamp1), timestamp2(timestamp2) , diffTolerance(diffTolerance), stabTolerance(stabTolerance) - , maxSize(maxSize), styleSheet(styleSheet), currTimestamp(currTimestamp) {} + , maxSize(maxSize), styleSheet(styleSheet), currTimestamp(currTimestamp) + , explicitBMContextId(explicitBMContextId) {} BMRequest_GetHistory(const QDomDocument &doc) : BMRequest(doc) {} private: mutable QString testCase; @@ -331,6 +332,7 @@ private: mutable int maxSize; mutable QString styleSheet; mutable int currTimestamp; + mutable int explicitBMContextId; QString name() const { return "GetHistory"; } QByteArray toRequestBuffer(QString *error); diff --git a/src/bmclient/main.cpp b/src/bmclient/main.cpp index ca890d5..0823d20 100644 --- a/src/bmclient/main.cpp +++ b/src/bmclient/main.cpp @@ -911,18 +911,25 @@ BMRequest * Executor::createGetHistoryRequest(const QStringList &args, QString * // Extract current timestamp ... int currTimestamp = args.at(17).toInt(&ok); - if (!ok) { - *error = QString( - "failed to extract current timestamp as an integer for 'get history' command"); - return 0; - } else if (currTimestamp < 0) { + if ((!ok) || (currTimestamp < 0)) { currTimestamp = -1; } + // Extract BM context ID (which overrides other values if specified) ... + int explicitBMContextId = -1; + if (BMMisc::getOption(args, "-bmcontextid", &values, 1, 0, error)) { + bool ok; + explicitBMContextId = values.at(0).toInt(&ok); + if ((!ok) || (explicitBMContextId < 0)) { + *error = "failed to extract stability tolerance as non-negative integer"; + return 0; + } + } + return new BMRequest_GetHistory( args.at(2), args.at(3), args.at(4), args.at(5), args.at(6), args.at(7), args.at(8), args.at(9), timeRange.first, timeRange.second, diffTolerance, - stabTolerance, maxSize, args.at(16), currTimestamp); + stabTolerance, maxSize, args.at(16), currTimestamp, explicitBMContextId); } BMRequest * Executor::createGetHistory2Request(const QStringList &args, QString *error) const -- cgit v1.2.3