Publish Framework tray enhancements and idle fingerprint retry
This commit is contained in:
@@ -15,6 +15,54 @@ class HardwareTest : public QObject {
|
||||
QFile file(path); QVERIFY(file.open(QIODevice::WriteOnly)); QCOMPARE(file.write(value), value.size());
|
||||
}
|
||||
private Q_SLOTS:
|
||||
void coreGroupFrequencies() {
|
||||
QTemporaryDir root;
|
||||
const QString base = root.path() + "/devices/system/cpu/cpufreq/";
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
const auto policy = base + "policy" + QString::number(i) + "/";
|
||||
put(policy + "related_cpus", QByteArray::number(i));
|
||||
put(policy + "scaling_cur_freq", QByteArray::number((i + 1) * 1000000));
|
||||
}
|
||||
// No core-type metadata: retain the original combined reading.
|
||||
auto sensors = frequencySensors(root.path());
|
||||
QCOMPARE(sensors.size(), 1);
|
||||
QCOMPARE(sensors[0].id, QString("cpu"));
|
||||
QVERIFY(sensors[0].primary);
|
||||
QCOMPARE(sensorValue(sensors[0]), std::optional<double>(2000));
|
||||
put(root.path() + "/bus/event_source/devices/cpu_core/cpus", "2");
|
||||
put(root.path() + "/bus/event_source/devices/cpu_atom/cpus", "0-1");
|
||||
sensors = frequencySensors(root.path());
|
||||
QCOMPARE(sensors.size(), 5);
|
||||
QCOMPARE(sensors[0].id, QString("cpu/p"));
|
||||
QCOMPARE(sensors[1].id, QString("cpu/e"));
|
||||
QCOMPARE(sensors[2].id, QString("cpu"));
|
||||
QVERIFY(sensors[0].primary && sensors[1].primary && !sensors[2].primary);
|
||||
QCOMPARE(sensorValue(sensors[0]), std::optional<double>(3000));
|
||||
QCOMPARE(sensorValue(sensors[1]), std::optional<double>(1500));
|
||||
QCOMPARE(sensorValue(sensors[2]), std::optional<double>(2000));
|
||||
QCOMPARE(sensors[3].id, QString("cpu/p/max"));
|
||||
QCOMPARE(sensors[4].id, QString("cpu/e/max"));
|
||||
QVERIFY(!sensors[3].primary && !sensors[4].primary);
|
||||
QCOMPARE(sensorValue(sensors[3]), std::optional<double>(3000));
|
||||
QCOMPARE(sensorValue(sensors[4]), std::optional<double>(2000));
|
||||
put(base + "policy0/scaling_cur_freq", "4000000");
|
||||
QCOMPARE(sensorValue(sensors[4]), std::optional<double>(4000));
|
||||
put(base + "policy0/scaling_cur_freq", "1000000");
|
||||
QCOMPARE(sensorValue(sensors[4]), std::optional<double>(2000)); // Not a historical peak.
|
||||
// Missing readings aren't zeroes, and membership isn't guessed by speed.
|
||||
put(base + "policy0/scaling_cur_freq", "unavailable");
|
||||
QCOMPARE(sensorValue(sensors[1]), std::optional<double>(2000));
|
||||
QCOMPARE(sensorValue(sensors[4]), std::optional<double>(2000));
|
||||
put(base + "policy1/scaling_cur_freq", "unavailable");
|
||||
QVERIFY(!sensorValue(sensors[1]));
|
||||
QVERIFY(!sensorValue(sensors[4]));
|
||||
QCOMPARE(sensorValue(sensors[2]), std::optional<double>(3000));
|
||||
put(base + "policy2/related_cpus", "1 2");
|
||||
sensors = frequencySensors(root.path());
|
||||
QCOMPARE(sensors.size(), 1); // A policy spanning types cannot be split.
|
||||
QVERIFY(sensors[0].primary);
|
||||
}
|
||||
|
||||
void energyCounters() {
|
||||
EnergyCounter counter;
|
||||
const quint64 range = 262143328850ULL;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <QSlider>
|
||||
#include "graphdata.h"
|
||||
#include "tooltip.h"
|
||||
#include "readingformat.h"
|
||||
#include "processusage.h"
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
@@ -33,6 +34,112 @@
|
||||
class WindowTest : public QObject {
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void readingFormat_data() {
|
||||
QTest::addColumn<double>("value"); QTest::addColumn<QString>("text");
|
||||
QTest::newRow("zero") << 0. << QString("0.0");
|
||||
QTest::newRow("small") << 1.26 << QString("1.3");
|
||||
QTest::newRow("negative") << -1.26 << QString("-1.3");
|
||||
QTest::newRow("negative zero") << -0.01 << QString("0.0");
|
||||
QTest::newRow("whole below ten") << 9. << QString("9.0");
|
||||
QTest::newRow("round to boundary") << 9.96 << QString("10.0");
|
||||
QTest::newRow("ten") << 10. << QString("10");
|
||||
QTest::newRow("minus ten") << -10. << QString("-10");
|
||||
QTest::newRow("round up") << 12.6 << QString("13");
|
||||
QTest::newRow("round down") << 12.4 << QString("12");
|
||||
QTest::newRow("negative round") << -12.5 << QString("-13");
|
||||
QTest::newRow("unavailable") << double(NAN) << QString("—");
|
||||
QTest::newRow("infinite") << double(INFINITY) << QString("—");
|
||||
}
|
||||
void readingFormat() {
|
||||
QFETCH(double, value); QFETCH(QString, text);
|
||||
QCOMPARE(formatReading(value), text);
|
||||
}
|
||||
void trayNumberPrecision() {
|
||||
TrayStyle style; style.border = false;
|
||||
style.backgroundColor = Qt::black; style.lineColor = Qt::white;
|
||||
const auto render = [&](double value, const QString &unit) {
|
||||
return telemetryIcon(false, {{0, value}}, unit, style, 0).pixmap(64, 64).toImage();
|
||||
};
|
||||
QCOMPARE(render(1250, "MHz"), render(1.25, "W")); // GHz in the icon.
|
||||
QCOMPARE(render(1.25, "W"), render(1.25, "°C"));
|
||||
QVERIFY(render(1.2, "W") != render(1.4, "W"));
|
||||
QCOMPARE(render(12.6, "W"), render(13, "%"));
|
||||
QCOMPARE(render(10000, "MHz"), render(10, "%"));
|
||||
QVERIFY(render(-1.2, "W") != render(1.2, "W"));
|
||||
}
|
||||
void stackedTrayNumbers() {
|
||||
TrayStyle style; style.border = false;
|
||||
style.backgroundColor = Qt::black; style.lineColor = Qt::white;
|
||||
const auto render = [&](double first, double second, const QString &unit = "W") {
|
||||
return telemetryIcon(false, {{0, first}}, "W", style, 0, TrayReading{second, unit}).pixmap(64, 64).toImage();
|
||||
};
|
||||
const auto both = render(1.2, 3.4);
|
||||
const auto changedTop = render(5.6, 3.4), changedBottom = render(1.2, 7.8);
|
||||
QVERIFY(both.copy(0, 0, 64, 32) != changedTop.copy(0, 0, 64, 32));
|
||||
QCOMPARE(both.copy(0, 32, 64, 32), changedTop.copy(0, 32, 64, 32));
|
||||
QCOMPARE(both.copy(0, 0, 64, 32), changedBottom.copy(0, 0, 64, 32));
|
||||
QVERIFY(both.copy(0, 32, 64, 32) != changedBottom.copy(0, 32, 64, 32));
|
||||
QCOMPARE(both, render(1.2, 3400, "MHz"));
|
||||
QVERIFY(both != render(1.2, NAN)); // Missing is a dash, not removal of the second row.
|
||||
// Both rows use their available height with small margins.
|
||||
for (int row = 0; row < 2; ++row) {
|
||||
int first = 64, last = -1;
|
||||
for (int y = row * 32; y < (row + 1) * 32; ++y)
|
||||
for (int x = 0; x < 64; ++x)
|
||||
if (qRed(both.pixel(x, y)) > 128) { first = std::min(first, y); last = std::max(last, y); }
|
||||
QVERIFY(first <= row * 32 + 5); QVERIFY(last >= row * 32 + 26);
|
||||
}
|
||||
const auto single = telemetryIcon(false, {{0, 1.2}}, "W", style, 0).pixmap(64, 64).toImage();
|
||||
QCOMPARE(single, telemetryIcon(false, {{0, 1.2}}, "W", style, 0, std::nullopt).pixmap(64, 64).toImage());
|
||||
const QVector<QPointF> history{{0, 1.2}, {1000, 3.4}};
|
||||
QCOMPARE(telemetryIcon(true, history, "W", style, 1000).pixmap(64, 64).toImage(),
|
||||
telemetryIcon(true, history, "W", style, 1000, TrayReading{90, "%"}).pixmap(64, 64).toImage());
|
||||
}
|
||||
void secondTrayReading() {
|
||||
const QVector<Sensor> sensors{{"cpu-usage", "CPU usage", {}, "%"}, {"cpu/p/max", "P-cores maximum", {}, "MHz"}};
|
||||
TrayPage page(QVariantMap{{"tray/mode", "number"}}, sensors);
|
||||
auto *second = page.findChild<QComboBox *>("traySecondMetric");
|
||||
auto *mode = page.findChild<QComboBox *>("trayDisplayMode");
|
||||
QVERIFY(second); QCOMPARE(second->currentText(), QString("None")); QVERIFY(!page.secondMetric());
|
||||
QVERIFY(!second->isHidden());
|
||||
const auto original = page.draft();
|
||||
second->setCurrentIndex(second->findData("cpu/p/max"));
|
||||
QVERIFY(page.secondMetric()); QCOMPARE(page.secondMetric()->id, QString("cpu/p/max"));
|
||||
const auto chosen = page.draft();
|
||||
TrayPage restored(chosen, sensors);
|
||||
QCOMPARE(restored.secondMetric()->id, QString("cpu/p/max"));
|
||||
mode->setCurrentIndex(mode->findData("graph")); QVERIFY(second->isHidden());
|
||||
mode->setCurrentIndex(mode->findData("number")); QVERIFY(!second->isHidden());
|
||||
QCOMPARE(page.secondMetric()->id, QString("cpu/p/max"));
|
||||
page.load(original); QVERIFY(!page.secondMetric());
|
||||
page.load(chosen); QCOMPARE(page.secondMetric()->id, QString("cpu/p/max"));
|
||||
TrayPage missing(chosen, {sensors[0]}); QVERIFY(!missing.secondMetric());
|
||||
}
|
||||
void powerHoverOptions() {
|
||||
const QVector<Sensor> sensors{{"power/rapl/0", "CPU package", {}, "W"},
|
||||
{"power/rapl/0:0", "CPU cores", {}, "W"}, {"battery-rate", "Battery rate", {}, "W"}};
|
||||
QVariantMap settings{{"tray/hover/cpu", false}, {"tray/hover/fan", false}, {"tray/hover/battery", false}};
|
||||
const QVector<Sensor> power{sensors[0], sensors[1]};
|
||||
const QMap<QString, double> values{{power[0].id, 4.25}, {power[1].id, 12.6}};
|
||||
TrayPage page(settings, sensors);
|
||||
auto *package = page.findChild<QCheckBox *>("hover/" + power[0].id);
|
||||
auto *cores = page.findChild<QCheckBox *>("hover/" + power[1].id);
|
||||
QVERIFY(package); QVERIFY(cores);
|
||||
QVERIFY(!package->isChecked()); QVERIFY(!cores->isChecked());
|
||||
QVERIFY(!page.findChild<QCheckBox *>("hover/battery-rate"));
|
||||
QVERIFY(trayTooltip(page.draft(), values, {}, {}, {}, power).isEmpty());
|
||||
const auto saved = page.draft();
|
||||
package->setChecked(true);
|
||||
QCOMPARE(trayTooltip(page.draft(), values, {}, {}, {}, power), QString("CPU package: 4.3 W"));
|
||||
cores->setChecked(true);
|
||||
QCOMPARE(trayTooltip(page.draft(), values, {}, {}, {}, power), QString("Power consumption:\n\u2003CPU package: 4.3 W\n\u2003CPU cores: 13 W"));
|
||||
QCOMPARE(trayTooltip(page.draft(), {}, {}, {}, {}, power), QString("Power consumption:\n\u2003CPU package: — W\n\u2003CPU cores: — W"));
|
||||
const auto chosen = page.draft();
|
||||
page.load(saved); QVERIFY(!package->isChecked()); QVERIFY(!cores->isChecked());
|
||||
page.load(chosen); QVERIFY(package->isChecked()); QVERIFY(cores->isChecked());
|
||||
TrayPage restored(chosen, sensors);
|
||||
QCOMPARE(trayTooltip(restored.draft(), values, {}, {}, {}, power), trayTooltip(chosen, values, {}, {}, {}, power));
|
||||
}
|
||||
void hoverFormatting() {
|
||||
const QVector<Sensor> sensors{{"cros_ec/peci-temp", "CPU", {}, "°C"}, {"spd5118/temp1", "Memory", {}, "°C"},
|
||||
{"nvme/Composite", "NVMe", {}, "°C"}};
|
||||
@@ -52,14 +159,15 @@ private Q_SLOTS:
|
||||
settings["tray/hover/temperature/memory"] = false; settings["tray/hover/temperature/nvme"] = false;
|
||||
QCOMPARE(trayTooltip(settings, readings, temperatures, {}, apps), QString("CPU 47°C"));
|
||||
QCOMPARE(trayTooltip(settings, {}, temperatures, {}, {}), QString("CPU —°C"));
|
||||
QCOMPARE(trayTooltip(settings, {{sensors[0].id, 3.2}}, temperatures, {}, {}), QString("CPU 3.2°C"));
|
||||
settings["tray/hover/temperature/cpu"] = false;
|
||||
QVERIFY(trayTooltip(settings, readings, temperatures, {}, {}).isEmpty());
|
||||
QVariantMap battery{{"state", "Discharging"}, {"capacity", 75}, {"watts", 12.5}, {"remainingMWh", 56250}, {"fullMWh", 75000}};
|
||||
QCOMPARE(batteryTooltip(battery, 80, {{"TimeToEmpty", 5400}}), QString("Battery 75% · 56 250 mWh / 75 000 mWh\n\u2003-12.5 W · 1 h 30 min to 0%"));
|
||||
QCOMPARE(batteryTooltip(battery, 80, {{"TimeToEmpty", 5400}}), QString("Battery 75% · 56 250 mWh / 75 000 mWh\n\u2003-13 W · 1 h 30 min to 0%"));
|
||||
battery["state"] = "Charging";
|
||||
QCOMPARE(batteryTooltip(battery, 100, {{"TimeToFull", 3600}}), QString("Battery 75% · 56 250 mWh / 75 000 mWh\n\u2003+12.5 W · 1 h 0 min to 100%"));
|
||||
QCOMPARE(batteryTooltip(battery, 100, {{"TimeToFull", 3600}}), QString("Battery 75% · 56 250 mWh / 75 000 mWh\n\u2003+13 W · 1 h 0 min to 100%"));
|
||||
battery["charge_full"] = 10000; battery["charge_now"] = 7500; battery["current_now"] = 1000;
|
||||
QCOMPARE(batteryTooltip(battery, 80, {}), QString("Battery 75% · 56 250 mWh / 75 000 mWh\n\u2003+12.5 W · ≈0 h 30 min to 80%"));
|
||||
QCOMPARE(batteryTooltip(battery, 80, {}), QString("Battery 75% · 56 250 mWh / 75 000 mWh\n\u2003+13 W · ≈0 h 30 min to 80%"));
|
||||
battery["current_now"] = 0;
|
||||
QVERIFY(batteryTooltip(battery, 80, {}).contains("Time remaining unavailable"));
|
||||
battery["capacity"] = 80;
|
||||
@@ -296,6 +404,7 @@ private Q_SLOTS:
|
||||
first->findChild<QComboBox *>("trayHistory")->setCurrentIndex(0);
|
||||
first->findChild<QCheckBox *>("border")->setChecked(false);
|
||||
last->findChild<QComboBox *>("trayDisplayMode")->setCurrentIndex(2);
|
||||
last->findChild<QComboBox *>("traySecondMetric")->setCurrentIndex(1);
|
||||
last->findChild<QSpinBox *>("hover/topApps")->setValue(3);
|
||||
const auto firstValues = first->draft(), lastValues = last->draft();
|
||||
const auto original = page.draft();
|
||||
@@ -304,6 +413,8 @@ private Q_SLOTS:
|
||||
QSignalSpy changed(&page, &TrayIconsPage::settingsChanged);
|
||||
cards[2]->findChild<QToolButton *>("moveIconLeft")->click();
|
||||
QCOMPARE(page.configurations()[1].values, lastValues); QCOMPARE(changed.count(), 1);
|
||||
QVERIFY(page.configurations()[1].secondMetric);
|
||||
QCOMPARE(page.configurations()[1].secondMetric->id, QString("cpu-usage"));
|
||||
cards[0]->findChild<QToolButton *>("moveIconRight")->click();
|
||||
QCOMPARE(page.configurations()[0].values, lastValues);
|
||||
QCOMPARE(page.configurations()[1].values, firstValues);
|
||||
@@ -411,7 +522,7 @@ private Q_SLOTS:
|
||||
for (int i = 0; i <= 7200; ++i) chart.sample({{"hidden", double(i)}}, 500, start + i * 500);
|
||||
QCOMPARE(chart.history("hidden").size(), 7201); // Not capped at 600, even when hidden.
|
||||
chart.setSelected("hidden", true);
|
||||
QVERIFY(chart.readingAt(start + 500).contains("hidden: 1 MHz"));
|
||||
QVERIFY(chart.readingAt(start + 500).contains("hidden: 1.0 MHz"));
|
||||
const auto end = double(start + 3600000);
|
||||
chart.setHistoryWindow(historyRetentionMs, true);
|
||||
QCOMPARE(chart.timeRange(), qMakePair(double(start), end));
|
||||
|
||||
Reference in New Issue
Block a user