Publish tool cleanup and Framework monitoring refinements
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
#include "hardware.h"
|
||||
#include "fan.h"
|
||||
#include "cpu.h"
|
||||
#include "power.h"
|
||||
#include <QTest>
|
||||
#include <QTemporaryDir>
|
||||
#include <QDir>
|
||||
@@ -14,6 +15,76 @@ class HardwareTest : public QObject {
|
||||
QFile file(path); QVERIFY(file.open(QIODevice::WriteOnly)); QCOMPARE(file.write(value), value.size());
|
||||
}
|
||||
private Q_SLOTS:
|
||||
void energyCounters() {
|
||||
EnergyCounter counter;
|
||||
const quint64 range = 262143328850ULL;
|
||||
QVERIFY(!counter.sample(1000000, range, 0, 3000));
|
||||
QCOMPARE(counter.sample(6000000, range, 1000, 3000), std::optional<double>(5));
|
||||
QCOMPARE(counter.sample(16000000, range, 3000, 3000), std::optional<double>(5));
|
||||
QVERIFY(!counter.sample(100, range, 4000, 3000)); // Reset, not a wrap.
|
||||
QCOMPARE(counter.sample(100, range, 5000, 3000), std::optional<double>(0));
|
||||
QVERIFY(!counter.sample(200, range, 10000, 3000)); // Long gaps establish a new baseline.
|
||||
QCOMPARE(counter.sample(1000200, range, 11000, 3000), std::optional<double>(1));
|
||||
EnergyCounter wrapped;
|
||||
QVERIFY(!wrapped.sample(range - 3000000, range, 0, 3000));
|
||||
QCOMPARE(wrapped.sample(2000000, range, 1000, 3000), std::optional<double>(5));
|
||||
QVERIFY(!wrapped.sample(100, 100000000, 2000, 3000)); // Changed range.
|
||||
QVERIFY(!wrapped.sample(100, 0, 3000, 3000));
|
||||
QVERIFY(!wrapped.sample(range + 1, range, 4000, 3000));
|
||||
EnergyCounter large;
|
||||
const quint64 base = 1ULL << 60;
|
||||
QVERIFY(!large.sample(base, base * 2, 0, 3000));
|
||||
QCOMPARE(large.sample(base + 1000, base * 2, 1000, 3000), std::optional<double>(.001));
|
||||
}
|
||||
void powerDiscoveryAndSampling() {
|
||||
QTemporaryDir root;
|
||||
const QString rapl = root.path() + "/class/powercap/intel-rapl:0/";
|
||||
const QString mmio = root.path() + "/class/powercap/intel-rapl-mmio:0/";
|
||||
for (const auto &path : {rapl, mmio}) {
|
||||
put(path + "name", "package-0"); put(path + "energy_uj", "1000000");
|
||||
put(path + "max_energy_range_uj", "262143328850");
|
||||
}
|
||||
const QString fan = root.path() + "/class/hwmon/hwmon9/";
|
||||
put(fan + "name", "power_meter"); put(fan + "power1_input", "250000"); put(fan + "power1_average", "500000");
|
||||
const QString acpiFan = root.path() + "/class/hwmon/hwmon10/";
|
||||
put(acpiFan + "name", "acpi_fan"); put(acpiFan + "power1_input", "0");
|
||||
PowerMonitor monitor(root.path()); QCOMPARE(monitor.sensors().size(), 2);
|
||||
QCOMPARE(monitor.sensors()[0].name, QString("CPU package"));
|
||||
QCOMPARE(monitor.sensors()[0].path, rapl + "energy_uj");
|
||||
const QString cpu = monitor.sensors()[0].id, fanId = monitor.sensors()[1].id;
|
||||
auto values = monitor.sample(1000, 0);
|
||||
QVERIFY(!values.contains(cpu)); QCOMPARE(values.value(fanId), .25);
|
||||
put(rapl + "energy_uj", "6000000");
|
||||
values = monitor.sample(1000, 1000); QCOMPARE(values.value(cpu), 5.);
|
||||
put(rapl + "energy_uj", "unavailable"); QVERIFY(!monitor.sample(1000, 2000).contains(cpu));
|
||||
put(rapl + "energy_uj", "11000000"); QVERIFY(!monitor.sample(1000, 3000).contains(cpu));
|
||||
put(rapl + "energy_uj", "16000000"); QCOMPARE(monitor.sample(1000, 4000).value(cpu), 5.);
|
||||
monitor.reset(); QVERIFY(!monitor.sample(1000, 5000).contains(cpu));
|
||||
put(fan + "power1_input", "0"); QCOMPARE(monitor.sample(1000, 6000).value(fanId), 0.);
|
||||
QTemporaryDir fallback;
|
||||
const QString fallbackZone = fallback.path() + "/class/powercap/intel-rapl-mmio:0/";
|
||||
put(fallbackZone + "name", "package-0"); put(fallbackZone + "energy_uj", "0");
|
||||
PowerMonitor mmioOnly(fallback.path()); QCOMPARE(mmioOnly.sensors().size(), 1);
|
||||
QCOMPARE(mmioOnly.sensors()[0].id, cpu);
|
||||
QVERIFY(mmioOnly.sample(1000, 0).isEmpty()); // Missing range is not a zero reading.
|
||||
}
|
||||
void platformPowerIsExcluded() {
|
||||
for (const auto &prefix : {"intel-rapl:", "intel-rapl-mmio:"}) {
|
||||
QTemporaryDir root;
|
||||
const QStringList names{"package-0", "core", "uncore", "dram", "psys", "psys-0"};
|
||||
for (int i = 0; i < names.size(); ++i) {
|
||||
const QString path = root.path() + "/class/powercap/" + prefix + QString::number(i) + "/";
|
||||
put(path + "name", names[i].toUtf8()); put(path + "energy_uj", "0");
|
||||
put(path + "max_energy_range_uj", "1000000");
|
||||
}
|
||||
PowerMonitor monitor(root.path());
|
||||
QStringList discovered;
|
||||
for (const auto &sensor : monitor.sensors()) discovered.append(sensor.name);
|
||||
QCOMPARE(discovered, QStringList({"CPU package", "CPU cores", "Uncore", "Memory (RAPL)"}));
|
||||
QVERIFY(monitor.sample(1000, 0).isEmpty());
|
||||
QCOMPARE(monitor.sample(1000, 1000).size(), 4);
|
||||
}
|
||||
}
|
||||
void modelGate() {
|
||||
QTemporaryDir root;
|
||||
QVERIFY(!compatibilityError(root.path()).isEmpty());
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#include "window.h"
|
||||
#include <QMenu>
|
||||
#include <QPainter>
|
||||
#include <QMouseEvent>
|
||||
#include <QTest>
|
||||
#include <QTemporaryDir>
|
||||
#include <QTabWidget>
|
||||
@@ -21,6 +24,11 @@
|
||||
#include "processusage.h"
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QGroupBox>
|
||||
#include <QScrollArea>
|
||||
#include <QScrollBar>
|
||||
#include "widgets.h"
|
||||
#include <QColorDialog>
|
||||
|
||||
class WindowTest : public QObject {
|
||||
Q_OBJECT
|
||||
@@ -99,9 +107,24 @@ private Q_SLOTS:
|
||||
auto *outside = page.findChild<QComboBox *>("trayOutside");
|
||||
QVERIFY(graph->isHidden());
|
||||
mode->setCurrentIndex(mode->findData("graph")); QVERIFY(graph->isVisible()); QVERIFY(overflow->isVisible());
|
||||
auto *border = page.findChild<QCheckBox *>("border");
|
||||
auto *borderColour = page.findChild<ColorButton *>("borderColor");
|
||||
auto *colours = qobject_cast<QFormLayout *>(borderColour->parentWidget()->layout());
|
||||
auto *borderLabel = colours->labelForField(borderColour);
|
||||
const auto bordered = page.draft();
|
||||
border->setChecked(false);
|
||||
QVERIFY(borderColour->isHidden()); QVERIFY(borderLabel->isHidden());
|
||||
QCOMPARE(page.draft().value("tray/borderColor"), bordered.value("tray/borderColor"));
|
||||
page.load(bordered); QVERIFY(borderColour->isVisible()); QVERIFY(borderLabel->isVisible());
|
||||
outside->setCurrentIndex(outside->findData("hide")); QVERIFY(overflow->isHidden());
|
||||
outside->setCurrentIndex(outside->findData("clamp")); QVERIFY(overflow->isVisible());
|
||||
auto *cpu = page.findChild<QCheckBox *>("hover/cpu");
|
||||
auto *hoverTitle = page.findChild<QLabel *>("trayHoverTitle");
|
||||
auto *hoverSettings = page.findChild<QWidget *>("trayHoverSettings");
|
||||
QVERIFY(hoverTitle); QVERIFY(hoverSettings);
|
||||
QVERIFY(!qobject_cast<QGroupBox *>(hoverSettings));
|
||||
QCOMPARE(hoverSettings->layout()->contentsMargins().left(), 24);
|
||||
QVERIFY(cpu->mapTo(&page, QPoint()).x() > hoverTitle->mapTo(&page, QPoint()).x());
|
||||
auto *app = page.findChild<QSpinBox *>("hover/topApps");
|
||||
QCOMPARE(app->minimum(), 0); QCOMPARE(app->maximum(), 3);
|
||||
const auto saved = page.draft();
|
||||
@@ -117,6 +140,31 @@ private Q_SLOTS:
|
||||
QVERIFY(!migrated.draft().contains("tray/hover/topApp"));
|
||||
QCOMPARE(tooltipAppCount({{"tray/hover/topApps", 3}, {"tray/hover/topApp", false}}), 3);
|
||||
}
|
||||
void backgroundOpacity() {
|
||||
const QVector<Sensor> sensors{{"cpu-usage", "CPU", {}, "%"}};
|
||||
TrayPage page(QVariantMap{{"tray/transparent", true}, {"tray/backgroundColor", "#ff123456"}, {"tray/mode", "number"}}, sensors);
|
||||
QVERIFY(!page.findChild<QCheckBox *>("transparent"));
|
||||
QVERIFY(!page.draft().contains("tray/transparent"));
|
||||
QCOMPARE(page.iconStyle().backgroundColor, QColor(0x12, 0x34, 0x56, 0));
|
||||
const auto transparent = page.draft(); page.show();
|
||||
bool alphaAvailable = false;
|
||||
QTimer::singleShot(0, &page, [&] {
|
||||
auto *dialog = qobject_cast<QColorDialog *>(QApplication::activeModalWidget());
|
||||
if (!dialog) return;
|
||||
alphaAvailable = dialog->testOption(QColorDialog::ShowAlphaChannel);
|
||||
dialog->setCurrentColor(QColor(20, 40, 60, 128)); dialog->accept();
|
||||
});
|
||||
page.findChild<ColorButton *>("backgroundColor")->click(); QVERIFY(alphaAvailable);
|
||||
QCOMPARE(page.iconStyle().backgroundColor, QColor(20, 40, 60, 128));
|
||||
const auto half = page.draft();
|
||||
page.load(transparent); QCOMPARE(page.iconStyle().backgroundColor.alpha(), 0);
|
||||
page.load(half); QCOMPARE(page.iconStyle().backgroundColor.alpha(), 128);
|
||||
auto style = page.iconStyle(); style.border = false; style.fill = false;
|
||||
const auto image = telemetryIcon(true, {}, "%", style).pixmap(64, 64).toImage();
|
||||
QCOMPARE(image.pixelColor(1, 1).alpha(), 128);
|
||||
TrayPage oldOpaque(QVariantMap{{"tray/transparent", false}, {"tray/backgroundColor", "#ff123456"}}, sensors);
|
||||
QCOMPARE(oldOpaque.iconStyle().backgroundColor.alpha(), 255);
|
||||
}
|
||||
void liveProcessSampling() {
|
||||
CpuUsage cpu; ProcessUsage processes;
|
||||
cpu.sample(readText("/proc/stat"));
|
||||
@@ -131,12 +179,152 @@ private Q_SLOTS:
|
||||
previous = app.percent;
|
||||
}
|
||||
}
|
||||
void multipleTrayDraftsAndLayout() {
|
||||
QTemporaryDir root; QSettings settings(root.filePath("tray.ini"), QSettings::IniFormat);
|
||||
settings.setValue("tray/mode", "number"); settings.setValue("tray/hover/topApp", true);
|
||||
const QVector<Sensor> sensors{{"cpu-usage", "CPU usage", {}, "%"}};
|
||||
TrayIconsPage page(settings, sensors); page.show();
|
||||
auto *count = page.findChild<QSlider *>("trayIconCount");
|
||||
QCOMPARE(count->minimum(), 1); QCOMPARE(count->maximum(), 10);
|
||||
QCOMPARE(page.configurations().first().mode, QString("number"));
|
||||
QCOMPARE(tooltipAppCount(page.configurations().first().values), 1);
|
||||
const auto original = page.draft();
|
||||
QVERIFY(!original.contains("tray/mode"));
|
||||
QCOMPARE(original.value("tray/icons/1/mode").toString(), QString("number"));
|
||||
count->setValue(3);
|
||||
auto editors = page.findChildren<TrayPage *>(); QCOMPARE(editors.size(), 3);
|
||||
QCOMPARE(page.configurations()[1].mode, QString("icon"));
|
||||
auto *secondMode = editors[1]->findChild<QComboBox *>("trayDisplayMode");
|
||||
secondMode->setCurrentIndex(secondMode->findData("graph"));
|
||||
editors[1]->findChild<QComboBox *>("trayHistory")->setCurrentIndex(0);
|
||||
count->setValue(1);
|
||||
QCOMPARE(page.draft(), original); // Hidden cards do not get serialised.
|
||||
count->setValue(3);
|
||||
QCOMPARE(page.configurations()[1].mode, QString("graph"));
|
||||
QCOMPARE(page.configurations()[1].style.historyMs, qint64(10000));
|
||||
const auto three = page.draft();
|
||||
page.load(original); count->setValue(3);
|
||||
QCOMPARE(page.configurations()[1].mode, QString("icon")); // Undo discards unsaved additions.
|
||||
page.load(three);
|
||||
count->setValue(1); page.discardHidden(); count->setValue(3);
|
||||
QCOMPARE(page.configurations()[1].mode, QString("icon")); // Successful save forgets removed cards.
|
||||
page.load(three);
|
||||
const auto cards = page.findChildren<QGroupBox *>(QString(), Qt::FindDirectChildrenOnly);
|
||||
QCOMPARE(cards.size(), 3);
|
||||
const int width = cards[0]->minimumWidth();
|
||||
page.resize(width * 3 + 100, 1600);
|
||||
QTRY_COMPARE(cards[0]->y(), cards[1]->y());
|
||||
QCOMPARE(cards[1]->y(), cards[2]->y()); QVERIFY(cards[1]->x() > cards[0]->x());
|
||||
page.resize(width + 40, 3000);
|
||||
QTRY_VERIFY(cards[1]->y() > cards[0]->y()); QCOMPARE(cards[1]->x(), cards[0]->x());
|
||||
count->setValue(10); QCOMPARE(page.configurations().size(), 10);
|
||||
QCOMPARE(page.draft().value("tray/icons/10/mode").toString(), QString("icon"));
|
||||
for (const auto &key : settings.allKeys()) settings.remove(key);
|
||||
const auto saved = page.draft();
|
||||
for (auto it = saved.cbegin(); it != saved.cend(); ++it) settings.setValue(it.key(), it.value());
|
||||
TrayIconsPage restored(settings, sensors);
|
||||
QCOMPARE(restored.draft(), saved); QCOMPARE(restored.configurations().size(), 10);
|
||||
const QString shot = qEnvironmentVariable("FRAMEWORK_TRAY_MULTI_SCREENSHOT");
|
||||
if (!shot.isEmpty()) {
|
||||
page.load(three); page.resize(width * 2 + 70, 1500); QTest::qWait(50);
|
||||
QVERIFY(page.grab().save(shot));
|
||||
}
|
||||
}
|
||||
void multipleLiveTrayIcons() {
|
||||
QTemporaryDir root; qputenv("XDG_CONFIG_HOME", root.path().toUtf8());
|
||||
QSettings::setPath(QSettings::NativeFormat, QSettings::UserScope, root.path());
|
||||
QSettings settings("fedora-tools", "framework-laptop-tools");
|
||||
settings.setValue("tray/mode", "number");
|
||||
Window window; window.show();
|
||||
auto *page = window.findChild<TrayIconsPage *>();
|
||||
auto *count = window.findChild<QSlider *>("trayIconCount");
|
||||
auto *save = window.findChild<QPushButton *>("saveAll");
|
||||
auto *undo = window.findChild<QPushButton *>("undoAll");
|
||||
auto icons = window.findChildren<QSystemTrayIcon *>(); QCOMPARE(icons.size(), 1);
|
||||
auto *first = icons[0];
|
||||
count->setValue(2); QCOMPARE(window.findChildren<QSystemTrayIcon *>().size(), 1);
|
||||
auto *tabs = window.findChild<QTabWidget *>(); tabs->setCurrentIndex(5);
|
||||
const auto cards = page->findChildren<QGroupBox *>(QString(), Qt::FindDirectChildrenOnly);
|
||||
const int cardWidth = cards[0]->minimumWidth();
|
||||
window.resize(cardWidth * 2 + 150, 830);
|
||||
QTRY_COMPARE(cards[0]->y(), cards[1]->y());
|
||||
window.resize(cardWidth + 150, 830);
|
||||
QTRY_VERIFY(cards[1]->y() > cards[0]->y());
|
||||
auto *scroll = qobject_cast<QScrollArea *>(tabs->widget(5));
|
||||
QTRY_COMPARE(scroll->horizontalScrollBar()->maximum(), 0);
|
||||
QVERIFY(scroll->verticalScrollBar()->maximum() > 0);
|
||||
auto *second = page->findChildren<TrayPage *>()[1];
|
||||
second->findChild<QCheckBox *>("hover/cpu")->setChecked(false);
|
||||
QTRY_VERIFY(save->isEnabled()); save->click();
|
||||
icons = window.findChildren<QSystemTrayIcon *>(); QCOMPARE(icons.size(), 2); QCOMPARE(icons[0], first);
|
||||
QVERIFY(icons[0]->toolTip().startsWith("CPU usage:"));
|
||||
QVERIFY(!icons[1]->toolTip().startsWith("CPU usage:"));
|
||||
QVERIFY(icons[0]->contextMenu() != icons[1]->contextMenu());
|
||||
QCOMPARE(icons[0]->contextMenu()->actions().size(), icons[1]->contextMenu()->actions().size());
|
||||
QCOMPARE(settings.value("tray/count").toInt(), 2);
|
||||
QVERIFY(!settings.contains("tray/mode"));
|
||||
const auto ordered = page->draft();
|
||||
cards[0]->findChild<QToolButton *>("moveIconRight")->click();
|
||||
QVERIFY(icons[0]->toolTip().startsWith("CPU usage:")); // Move is still only a draft.
|
||||
QTRY_VERIFY(save->isEnabled()); save->click();
|
||||
QVERIFY(!icons[0]->toolTip().startsWith("CPU usage:"));
|
||||
QVERIFY(icons[1]->toolTip().startsWith("CPU usage:"));
|
||||
QVERIFY(!settings.value("tray/icons/1/hover/cpu").toBool());
|
||||
cards[0]->findChild<QToolButton *>("moveIconRight")->click();
|
||||
QTRY_VERIFY(undo->isEnabled()); undo->click();
|
||||
QVERIFY(!page->configurations()[0].values.value("tray/hover/cpu").toBool());
|
||||
cards[0]->findChild<QToolButton *>("moveIconRight")->click();
|
||||
QCOMPARE(page->draft(), ordered);
|
||||
QTRY_VERIFY(save->isEnabled()); save->click();
|
||||
count->setValue(1); QTRY_VERIFY(undo->isEnabled()); undo->click(); QCOMPARE(count->value(), 2);
|
||||
QVERIFY(!page->findChildren<TrayPage *>()[1]->findChild<QCheckBox *>("hover/cpu")->isChecked());
|
||||
count->setValue(1); QTRY_VERIFY(save->isEnabled()); save->click();
|
||||
QCOMPARE(window.findChildren<QSystemTrayIcon *>().size(), 1);
|
||||
for (const auto &key : settings.allKeys()) QVERIFY(!key.startsWith("tray/icons/2/"));
|
||||
count->setValue(2);
|
||||
QVERIFY(page->findChildren<TrayPage *>()[1]->findChild<QCheckBox *>("hover/cpu")->isChecked());
|
||||
QCOMPARE(page->configurations()[1].mode, QString("icon"));
|
||||
}
|
||||
void moveTrayConfigurations() {
|
||||
QTemporaryDir root; QSettings settings(root.filePath("tray.ini"), QSettings::IniFormat);
|
||||
TrayIconsPage page(settings, {{"cpu-usage", "CPU", {}, "%"}});
|
||||
auto *count = page.findChild<QSlider *>("trayIconCount"); count->setValue(3);
|
||||
const auto cards = page.findChildren<QGroupBox *>(QString(), Qt::FindDirectChildrenOnly);
|
||||
auto *first = cards[0]->findChild<TrayPage *>();
|
||||
auto *last = cards[2]->findChild<TrayPage *>();
|
||||
first->findChild<QComboBox *>("trayDisplayMode")->setCurrentIndex(1);
|
||||
first->findChild<QComboBox *>("trayHistory")->setCurrentIndex(0);
|
||||
first->findChild<QCheckBox *>("border")->setChecked(false);
|
||||
last->findChild<QComboBox *>("trayDisplayMode")->setCurrentIndex(2);
|
||||
last->findChild<QSpinBox *>("hover/topApps")->setValue(3);
|
||||
const auto firstValues = first->draft(), lastValues = last->draft();
|
||||
const auto original = page.draft();
|
||||
QVERIFY(!cards[0]->findChild<QToolButton *>("moveIconLeft")->isEnabled());
|
||||
QVERIFY(!cards[2]->findChild<QToolButton *>("moveIconRight")->isEnabled());
|
||||
QSignalSpy changed(&page, &TrayIconsPage::settingsChanged);
|
||||
cards[2]->findChild<QToolButton *>("moveIconLeft")->click();
|
||||
QCOMPARE(page.configurations()[1].values, lastValues); QCOMPARE(changed.count(), 1);
|
||||
cards[0]->findChild<QToolButton *>("moveIconRight")->click();
|
||||
QCOMPARE(page.configurations()[0].values, lastValues);
|
||||
QCOMPARE(page.configurations()[1].values, firstValues);
|
||||
// Crossing a grid row still exchanges adjacent logical positions.
|
||||
cards[1]->findChild<QToolButton *>("moveIconRight")->click();
|
||||
QCOMPARE(page.configurations()[2].values, firstValues);
|
||||
count->setValue(2);
|
||||
QVERIFY(!cards[1]->findChild<QToolButton *>("moveIconRight")->isEnabled());
|
||||
count->setValue(3); QCOMPARE(page.configurations()[2].values, firstValues);
|
||||
page.load(original); QCOMPARE(page.draft(), original);
|
||||
count->setValue(1);
|
||||
QVERIFY(!cards[0]->findChild<QToolButton *>("moveIconRight")->isEnabled());
|
||||
QVERIFY(!cards[0]->findChild<QToolButton *>("moveIconLeft")->isEnabled());
|
||||
}
|
||||
void timeWeightedColumns() {
|
||||
const auto connected = [](const QPointF &, const QPointF &) { return true; };
|
||||
const QVector<QPointF> points{{0, 0}, {2, 10}, {10, 10}};
|
||||
auto columns = timeAverages(points.begin(), points.end(), 0, 10, 2, connected);
|
||||
QCOMPARE(columns.size(), 2);
|
||||
QCOMPARE(columns[0].mean, 8.); QCOMPARE(columns[0].maximum, 10.);
|
||||
QCOMPARE(columns[0].lower(), columns[0].mean); QVERIFY(columns[0].compressed);
|
||||
QCOMPARE(columns[1].mean, 10.);
|
||||
columns = timeAverages(points.begin(), points.end(), 1, 3, 1, connected);
|
||||
QCOMPARE(columns[0].mean, 8.75); // Segment clipped and weighted at both pixel edges.
|
||||
@@ -153,17 +341,70 @@ private Q_SLOTS:
|
||||
for (const auto &column : columns) {
|
||||
QVERIFY(std::abs(column.mean - (column.begin + column.end - 10)) < 1e-10);
|
||||
QVERIFY(std::abs(column.maximum - (column.end * 2 - 10)) < 1e-10);
|
||||
QVERIFY(!column.compressed);
|
||||
QCOMPARE(column.lower(), column.minimum);
|
||||
QVERIFY(std::abs(column.minimum - (column.begin * 2 - 10)) < 1e-10);
|
||||
}
|
||||
const QVector<QPointF> gaps{{0, 0}, {2, 10}, {3, NAN}, {4, 10}, {6, 0}};
|
||||
columns = timeAverages(gaps.begin(), gaps.end(), 0, 6, 1, connected);
|
||||
QCOMPARE(columns.size(), 2); QVERIFY(columns[1].startsRun);
|
||||
QCOMPARE(columns.size(), 2);
|
||||
QCOMPARE(columns[0].mean, 5.); QCOMPARE(columns[1].mean, 5.);
|
||||
const auto line = averageLine(columns);
|
||||
QVERIFY(std::isnan(line[3].y())); // Never bridge a gap, even inside one pixel.
|
||||
QCOMPARE(columns[0].end, 2.); QCOMPARE(columns[1].begin, 4.); // Gaps stay separate even in one pixel.
|
||||
columns = timeAverages(points.begin(), points.end(), 0, 10, 1,
|
||||
[](const QPointF &, const QPointF &b) { return b.x() < 10; });
|
||||
QCOMPARE(columns.size(), 1); QCOMPARE(columns[0].end, 2.); // Explicit suspend break.
|
||||
}
|
||||
void columnThicknessIsVerticalOnly() {
|
||||
QCOMPARE(columnBand(5, 6, 10, 10), QRectF(5, 9, 1, 2));
|
||||
QCOMPARE(columnBand(5, 6, 10, 11, 2, 2), QRectF(5, 9.5, 1, 2));
|
||||
QCOMPARE(columnBand(5, 6, 10, 50), QRectF(5, 10, 1, 40));
|
||||
for (double ratio : {1., 1.25, 1.5, 2.}) for (double bottom : {10., 11., 20.}) {
|
||||
QImage image(qRound(32 * ratio), qRound(32 * ratio), QImage::Format_ARGB32_Premultiplied);
|
||||
image.setDevicePixelRatio(ratio); image.fill(Qt::transparent);
|
||||
{
|
||||
QPainter painter(&image);
|
||||
painter.fillRect(columnBand(5 / ratio, 6 / ratio, 10, bottom, 2, ratio), Qt::green);
|
||||
}
|
||||
int pixels = 0;
|
||||
for (int y = 0; y < image.height(); ++y) for (int x = 0; x < image.width(); ++x) {
|
||||
if (!image.pixelColor(x, y).alpha()) continue;
|
||||
QCOMPARE(x, 5); ++pixels; // Thickness never leaks into either neighbour.
|
||||
}
|
||||
QVERIFY(pixels >= std::ceil(2 * ratio));
|
||||
}
|
||||
}
|
||||
void sparseSlopeStaysInItsColumns() {
|
||||
TrayStyle style; style.border = false; style.fill = false;
|
||||
style.backgroundColor = Qt::black; style.lineColor = Qt::green; style.historyMs = 6200;
|
||||
// Two samples span exactly two raster columns and most of the graph height.
|
||||
const auto image = telemetryIcon(true, {{1000, 10}, {1200, 90}}, "%", style, 6200).pixmap(64, 64).toImage();
|
||||
int pixels = 0;
|
||||
for (int y = 0; y < image.height(); ++y) for (int x = 0; x < image.width(); ++x) {
|
||||
if (image.pixelColor(x, y) != QColor(Qt::green)) continue;
|
||||
QVERIFY(x == 11 || x == 12); ++pixels;
|
||||
}
|
||||
QVERIFY(pixels > 40);
|
||||
}
|
||||
void peakHeightSurvivesPixelPhase() {
|
||||
QVector<QPointF> points;
|
||||
for (int i = 0; i <= 1000; ++i) points.append({double(i), i == 500 ? 100. : 10.});
|
||||
for (int phase = 0; phase <= 32; ++phase) {
|
||||
const double first = 200 + phase / 32. * 7.5;
|
||||
const auto columns = timeAverages(points.cbegin(), points.cend(), first, first + 600, 80,
|
||||
[](const QPointF &, const QPointF &) { return true; });
|
||||
QImage image(80, 120, QImage::Format_ARGB32_Premultiplied); image.fill(Qt::transparent);
|
||||
{
|
||||
QPainter painter(&image);
|
||||
for (const auto &column : columns)
|
||||
painter.fillRect(columnBand(column.index, column.index + 1, 110 - column.maximum,
|
||||
110 - column.lower()), Qt::green);
|
||||
}
|
||||
int top = image.height();
|
||||
for (int y = 0; y < image.height(); ++y) for (int x = 0; x < image.width(); ++x)
|
||||
if (image.pixelColor(x, y).alpha()) top = std::min(top, y);
|
||||
QCOMPARE(top, 10); // The peak's rasterised height, not merely its numeric maximum.
|
||||
}
|
||||
}
|
||||
void dayHistoryAndStretch() {
|
||||
Chart chart("MHz"); chart.addSeries("hidden");
|
||||
const qint64 start = 100000000;
|
||||
@@ -187,6 +428,7 @@ private Q_SLOTS:
|
||||
}
|
||||
void monitorHistorySettings() {
|
||||
QTemporaryDir config; qputenv("XDG_CONFIG_HOME", config.path().toUtf8());
|
||||
QSettings::setPath(QSettings::NativeFormat, QSettings::UserScope, config.path());
|
||||
{
|
||||
Window window;
|
||||
auto *slider = window.findChild<QSlider *>("monitorHistory"); QVERIFY(slider);
|
||||
@@ -208,10 +450,10 @@ private Q_SLOTS:
|
||||
TrayPage tray(settings, {{"cpu-usage", "CPU usage", {}, "%"}});
|
||||
QCOMPARE(tray.iconStyle().historyMs, qint64(60000)); // Retired choices use the new default.
|
||||
}
|
||||
void denseHistorySoftensPeaks() {
|
||||
void denseHistoryShowsPeakColumns() {
|
||||
Chart chart("%"); chart.resize(600, 200); chart.addSeries("sensor"); chart.setSelected("sensor", true);
|
||||
const qint64 start = 100000000;
|
||||
// A half-second spike is faint, not an opaque excursion of the mean line.
|
||||
// A half-second spike fills its column up to the maximum.
|
||||
for (int i = 0; i <= 172800; ++i)
|
||||
chart.sample({{"sensor", i == 43200 ? 100. : 10.}}, 500, start + i * 500);
|
||||
QCOMPARE(chart.history("sensor").size(), 172801);
|
||||
@@ -222,8 +464,7 @@ private Q_SLOTS:
|
||||
bool peak = false;
|
||||
for (int y = 15; y < 60; ++y) for (int x = 100; x < 550; ++x) {
|
||||
const auto colour = image.pixelColor(x, y);
|
||||
QVERIFY(colour != chart.color("sensor"));
|
||||
peak |= colour.blue() > colour.red() + 10 && colour.green() > colour.red() + 5;
|
||||
peak |= colour == chart.color("sensor");
|
||||
}
|
||||
QVERIFY(peak);
|
||||
chart.setHistoryWindow(300000, true);
|
||||
@@ -236,12 +477,19 @@ private Q_SLOTS:
|
||||
QCOMPARE(freq.minimum, 0.); QCOMPARE(freq.maximum, 6000.); QCOMPARE(freq.step, 2000.);
|
||||
const auto rate = AxisTicks::covering(-13, 42, 4);
|
||||
QCOMPARE(rate.minimum, -20.); QCOMPARE(rate.maximum, 60.); QCOMPARE(rate.step, 20.);
|
||||
QCOMPARE(timeTickStep(3600000, 6), 600000.);
|
||||
QCOMPARE(timeTickStep(3600000, 6), 720000.); // 0.2 h, not a repeating decimal.
|
||||
QCOMPARE(timeTickStep(3600000, 3), 1800000.);
|
||||
QCOMPARE(ageLabel(7200000), QString("2h"));
|
||||
QCOMPARE(ageLabel(1200000), QString("20min"));
|
||||
QCOMPARE(ageLabel(5000), QString("5s"));
|
||||
QCOMPARE(ageLabel(0), QString("Now"));
|
||||
QCOMPARE(timeTickStep(240000, 8), 30000.);
|
||||
QCOMPARE(timeTickStep(60000, 8), 12000.); // 0.2 min.
|
||||
QCOMPARE(ageLabel(7200000, 7200000), QString("2h"));
|
||||
QCOMPARE(ageLabel(1800000, 7200000), QString("0.5h"));
|
||||
QCOMPARE(ageLabel(1200000, 1200000), QString("20min"));
|
||||
QCOMPARE(ageLabel(210000, 240000), QString("3.5min"));
|
||||
QCOMPARE(ageLabel(150000, 240000), QString("2.5min"));
|
||||
QCOMPARE(ageLabel(90000, 240000), QString("1.5min"));
|
||||
QCOMPARE(ageLabel(30000, 240000), QString("0.5min"));
|
||||
QCOMPARE(ageLabel(5000, 10000), QString("5s"));
|
||||
QCOMPARE(ageLabel(0, 240000), QString("Now"));
|
||||
}
|
||||
void historyHoverAndSleep() {
|
||||
Chart chart("%"); chart.addSeries("battery", "Charge level"); chart.setSelected("battery", true);
|
||||
@@ -266,6 +514,64 @@ private Q_SLOTS:
|
||||
chart.sample({}, 30000, historyRetentionMs + 230000);
|
||||
QVERIFY(!chart.readingAt(106000).contains("Asleep")); // Annotations expire with samples.
|
||||
}
|
||||
void samplingIntervalChanges_data() {
|
||||
QTest::addColumn<int>("before"); QTest::addColumn<int>("after");
|
||||
QTest::newRow("battery-faster") << 60000 << 15000;
|
||||
QTest::newRow("battery-slower") << 15000 << 60000;
|
||||
QTest::newRow("fast-sensors-faster") << 2000 << 500;
|
||||
QTest::newRow("fast-sensors-slower") << 500 << 2000;
|
||||
}
|
||||
void samplingIntervalChanges() {
|
||||
QFETCH(int, before); QFETCH(int, after);
|
||||
Chart changed("%"), reference("%");
|
||||
for (auto *chart : {&changed, &reference}) {
|
||||
chart->resize(850, 240); chart->addSeries("battery", "Battery");
|
||||
chart->setSelected("battery", true); chart->setHistoryWindow(300000, false);
|
||||
for (int i = 0; i < 4; ++i) chart->sample({{"battery", 50}}, before, i * before);
|
||||
}
|
||||
const qint64 now = 3 * before + after;
|
||||
changed.sample({{"battery", 50}}, after, now);
|
||||
reference.sample({{"battery", 50}}, std::max(before, after), now);
|
||||
const auto history = changed.history("battery");
|
||||
QCOMPARE(history.size(), 5);
|
||||
for (const auto &point : history) QVERIFY(std::isfinite(point.y()));
|
||||
QVERIFY(changed.readingAt(before / 2).contains("Battery: 50 %"));
|
||||
QCOMPARE(changed.grab().toImage(), reference.grab().toImage());
|
||||
TrayStyle style; style.historyMs = 300000;
|
||||
QCOMPARE(telemetryIcon(true, history, "%", style, now).pixmap(64, 64).toImage(),
|
||||
telemetryIcon(true, reference.history("battery"), "%", style, now).pixmap(64, 64).toImage());
|
||||
}
|
||||
void intervalChangesPreserveGaps() {
|
||||
Chart chart("%"); chart.addSeries("battery", "Battery"); chart.setSelected("battery", true);
|
||||
for (qint64 time : {0, 15000, 90000}) chart.sample({{"battery", 50}}, 15000, time);
|
||||
chart.sample({}, 15000, 105000);
|
||||
chart.sample({{"battery", 50}}, 15000, 120000);
|
||||
chart.setPowerState(true, {}, 125000); chart.setPowerState(false, false, 130000);
|
||||
chart.sample({{"battery", 50}}, 15000, 135000);
|
||||
chart.sample({{"battery", 50}}, 60000, 195000);
|
||||
const auto history = chart.history("battery");
|
||||
for (double gap : {90000., 105000., 135000.})
|
||||
QVERIFY(std::any_of(history.cbegin(), history.cend(), [gap](const QPointF &p) {
|
||||
return p.x() == gap && !std::isfinite(p.y());
|
||||
}));
|
||||
QVERIFY(chart.readingAt(45000).contains("Battery: —"));
|
||||
QVERIFY(chart.readingAt(105000).contains("Battery: —"));
|
||||
QVERIFY(chart.readingAt(127000).contains("Battery: —"));
|
||||
QVERIFY(chart.readingAt(120000).contains("Battery: 50 %"));
|
||||
}
|
||||
void intervalHistoryRetainsBoundary() {
|
||||
Chart chart("%"); chart.addSeries("battery", "Battery"); chart.setSelected("battery", true);
|
||||
chart.sample({{"battery", 50}}, 60000, 0);
|
||||
chart.sample({{"battery", 50}}, 60000, 60000);
|
||||
chart.sample({{"battery", 50}}, 15000, 120000);
|
||||
chart.sample({{"battery", 50}}, 15000, historyRetentionMs + 61000);
|
||||
const auto history = chart.history("battery");
|
||||
QCOMPARE(history.first().x(), 60000.);
|
||||
QCOMPARE(history[1], QPointF(120000, 50)); // No artificial gap at the cadence transition.
|
||||
QVERIFY(chart.readingAt(90000).contains("Battery: 50 %"));
|
||||
chart.sample({{"battery", 50}}, 15000, historyRetentionMs + 121000);
|
||||
QVERIFY(chart.readingAt(30000).contains("Battery: —")); // The old boundary has expired.
|
||||
}
|
||||
void clickableLegend() {
|
||||
Chart chart("MHz"); chart.addSeries("cpu"); chart.setSelected("cpu", true);
|
||||
Legend legend(&chart, {{"cpu", "CPU average", {}, "MHz"}});
|
||||
@@ -307,7 +613,7 @@ private Q_SLOTS:
|
||||
QCOMPARE(image.pixelColor(32, 3), QColor(Qt::red));
|
||||
QCOMPARE(image.pixelColor(32, 32), QColor(Qt::black));
|
||||
style.clamp = false; image = render(); QCOMPARE(image.pixelColor(32, 3), QColor(Qt::black));
|
||||
style.border = false; style.transparent = true; image = render(); QCOMPARE(image.pixelColor(32, 1).alpha(), 0);
|
||||
style.border = false; style.backgroundColor = Qt::transparent; image = render(); QCOMPARE(image.pixelColor(32, 1).alpha(), 0);
|
||||
style.clamp = true; style.overflowColor = false; image = render(); QCOMPARE(image.pixelColor(32, 1), QColor(Qt::green));
|
||||
QVERIFY(trayPlotRect(true).width() < trayPlotRect(false).width());
|
||||
style.fill = true;
|
||||
@@ -340,7 +646,7 @@ private Q_SLOTS:
|
||||
QCOMPARE(telemetryIcon(false, points, "%", style, 120000).pixmap(64, 64).toImage(), number);
|
||||
QCOMPARE(image.pixelColor(60, 32), QColor(Qt::green));
|
||||
}
|
||||
void traySoftensPeaks() {
|
||||
void trayShowsPeakColumns() {
|
||||
TrayStyle style; style.border = false; style.backgroundColor = Qt::black;
|
||||
style.lineColor = Qt::green; style.fill = false; style.historyMs = 300000;
|
||||
QVector<QPointF> points;
|
||||
@@ -351,8 +657,7 @@ private Q_SLOTS:
|
||||
bool band = false;
|
||||
for (int y = 4; y < 30; ++y) for (int x = 20; x < 45; ++x) {
|
||||
const auto colour = image.pixelColor(x, y);
|
||||
QVERIFY(colour != QColor(Qt::green));
|
||||
band |= colour.green() > 0 && colour.green() < 100;
|
||||
band |= colour == QColor(Qt::green);
|
||||
}
|
||||
QVERIFY(band);
|
||||
bool mean = false;
|
||||
@@ -446,6 +751,55 @@ private Q_SLOTS:
|
||||
chart.sample({{"cpu", 2500}}, 1000, now + 1000);
|
||||
QVERIFY(chart.grab().toImage() != before);
|
||||
}
|
||||
void chartRefreshesAfterDisplayChanges() {
|
||||
Chart chart("MHz"); chart.resize(650, 240);
|
||||
chart.addSeries("cpu"); chart.setSelected("cpu", true);
|
||||
chart.sample({{"cpu", 1000}}, 1000, 100000);
|
||||
chart.sample({{"cpu", 2000}}, 1000, 101000);
|
||||
chart.show(); QVERIFY(QTest::qWaitForWindowExposed(&chart));
|
||||
QTest::mouseMove(&chart, QPoint(1, 1));
|
||||
const auto original = chart.grab().toImage();
|
||||
QCOMPARE(chart.grab().toImage(), original);
|
||||
chart.setSelected("cpu", false);
|
||||
QVERIFY(chart.grab().toImage() != original);
|
||||
chart.setSelected("cpu", true);
|
||||
QCOMPARE(chart.grab().toImage(), original);
|
||||
chart.setHistoryWindow(300000, false);
|
||||
const auto unstretched = chart.grab().toImage();
|
||||
QVERIFY(unstretched != original);
|
||||
auto palette = chart.palette(); palette.setColor(QPalette::Text, Qt::red);
|
||||
chart.setPalette(palette);
|
||||
const auto recoloured = chart.grab().toImage();
|
||||
QVERIFY(recoloured != unstretched);
|
||||
auto font = chart.font(); font.setPointSizeF(font.pointSizeF() + 2); chart.setFont(font);
|
||||
QVERIFY(chart.grab().toImage() != recoloured);
|
||||
chart.resize(800, 240);
|
||||
QCOMPARE(chart.grab().size(), QSize(800, 240) * chart.devicePixelRatioF());
|
||||
}
|
||||
void stationaryHoverFollowsSamples() {
|
||||
Chart chart("W"); chart.resize(850, 240);
|
||||
chart.addSeries("power"); chart.setSelected("power", true);
|
||||
chart.setHistoryWindow(300000, false);
|
||||
const qint64 now = QDateTime::currentMSecsSinceEpoch();
|
||||
chart.sample({{"power", 1}}, 1000, now);
|
||||
chart.show(); QVERIFY(QTest::qWaitForWindowExposed(&chart));
|
||||
// Wayland does not support QTest's global cursor warp. Send a local move.
|
||||
const QPoint position(400, 100);
|
||||
QMouseEvent move(QEvent::MouseMove, position, chart.mapToGlobal(position),
|
||||
Qt::NoButton, Qt::NoButton, Qt::NoModifier);
|
||||
QApplication::sendEvent(&chart, &move);
|
||||
auto *popup = chart.findChild<QLabel *>("chartReadingPopup");
|
||||
QVERIFY(popup); QVERIFY(popup->isVisible());
|
||||
const auto time = chart.hoverTime(); QVERIFY(time);
|
||||
const QString text = popup->text();
|
||||
chart.sample({{"power", 2}}, 1000, now + 1000);
|
||||
QCOMPARE(chart.hoverTime(), std::optional<qint64>(*time + 1000));
|
||||
QVERIFY(popup->text() != text);
|
||||
QCOMPARE(popup->text(), chart.readingAt(*chart.hoverTime()));
|
||||
QTest::qWait(11000); // Past the usual tooltip inactivity timeout, without moving.
|
||||
QVERIFY(popup->isVisible());
|
||||
chart.hide(); QVERIFY(!popup->isVisible()); QVERIFY(!chart.hoverTime());
|
||||
}
|
||||
void sliderAndNumberStaySynchronized() {
|
||||
ValueControl control;
|
||||
control.setRange(400, 4800); control.setSuffix(" MHz"); control.setSingleStep(100);
|
||||
@@ -469,9 +823,33 @@ private Q_SLOTS:
|
||||
slider->setValue(0);
|
||||
QCOMPARE(number->text(), QString("Firmware default"));
|
||||
}
|
||||
void slidersJumpToClick() {
|
||||
JumpSlider slider(Qt::Horizontal); slider.setRange(0, 100); slider.setPageStep(10);
|
||||
slider.resize(400, 40); slider.show();
|
||||
const QPoint target(300, 20);
|
||||
for (bool inverted : {false, true}) {
|
||||
slider.setInvertedAppearance(inverted); slider.setValue(50);
|
||||
QTest::mousePress(&slider, Qt::LeftButton, Qt::NoModifier, target);
|
||||
QVERIFY(slider.isSliderDown());
|
||||
QVERIFY(inverted ? slider.value() < 30 : slider.value() > 70);
|
||||
const int pressed = slider.value();
|
||||
QTest::qWait(550); QCOMPARE(slider.value(), pressed); // No page-step auto-repeat.
|
||||
QTest::mouseMove(&slider, QPoint(200, 20));
|
||||
QVERIFY(std::abs(slider.value() - 50) <= 1);
|
||||
QTest::mouseRelease(&slider, Qt::LeftButton, Qt::NoModifier, QPoint(200, 20));
|
||||
QVERIFY(!slider.isSliderDown());
|
||||
}
|
||||
slider.setInvertedAppearance(false); slider.setLayoutDirection(Qt::RightToLeft); slider.setValue(50);
|
||||
QTest::mouseClick(&slider, Qt::LeftButton, Qt::NoModifier, target); QVERIFY(slider.value() < 30);
|
||||
slider.setLayoutDirection(Qt::LeftToRight); slider.setValue(50);
|
||||
QTest::keyClick(&slider, Qt::Key_Right); QCOMPARE(slider.value(), 51);
|
||||
slider.setEnabled(false); QTest::mouseClick(&slider, Qt::LeftButton, Qt::NoModifier, target);
|
||||
QCOMPARE(slider.value(), 51);
|
||||
}
|
||||
void pagesAndReadOnlyStartup() {
|
||||
QTemporaryDir config;
|
||||
qputenv("XDG_CONFIG_HOME", config.path().toUtf8());
|
||||
QSettings::setPath(QSettings::NativeFormat, QSettings::UserScope, config.path());
|
||||
QSettings settings("fedora-tools", "framework-laptop-tools");
|
||||
settings.setValue("sampling/fast", 0);
|
||||
settings.setValue("sampling/battery", -5);
|
||||
@@ -484,7 +862,31 @@ private Q_SLOTS:
|
||||
QCOMPARE(tabs->tabText(3), QString("Battery"));
|
||||
QCOMPARE(tabs->tabText(4), QString("CPU"));
|
||||
QCOMPARE(tabs->tabText(5), QString("Tray icon"));
|
||||
QCOMPARE(window.findChildren<Chart *>().size(), 4);
|
||||
QCOMPARE(window.findChildren<Chart *>().size(), 5);
|
||||
auto *powerChart = window.findChild<Chart *>("powerChart"); QVERIFY(powerChart);
|
||||
for (QWidget *ancestor = powerChart; ancestor; ancestor = ancestor->parentWidget())
|
||||
QVERIFY(ancestor->toolTip().isEmpty()); // No inherited help tooltip obscures graph readings.
|
||||
const qreal titleSize = window.findChild<QLabel *>("chartTitle")->font().pointSizeF();
|
||||
for (auto *group : window.findChildren<QGroupBox *>("settingsSection")) {
|
||||
group->ensurePolished(); QCOMPARE(group->font().pointSizeF(), titleSize);
|
||||
}
|
||||
for (const auto *name : {"batteryCpuProfile", "acCpuProfile"}) {
|
||||
auto *group = window.findChild<QGroupBox *>(name); QVERIFY(group);
|
||||
group->ensurePolished(); QCOMPARE(group->font().pointSizeF(), titleSize);
|
||||
for (auto *combo : group->findChildren<QComboBox *>())
|
||||
QCOMPARE(combo->font().pointSizeF(), titleSize - 2);
|
||||
}
|
||||
QCOMPARE(window.findChild<QLabel *>("trayHoverTitle")->font().pointSizeF(), titleSize - 2);
|
||||
QCOMPARE(window.findChild<QLabel *>("trayIconTitle")->font().pointSizeF(), titleSize);
|
||||
QCOMPARE(window.findChild<QCheckBox *>("hover/cpu")->font().pointSizeF(), titleSize - 2);
|
||||
for (auto *slider : window.findChildren<QSlider *>()) {
|
||||
QVERIFY(slider->style()->styleHint(QStyle::SH_Slider_AbsoluteSetButtons, nullptr, slider) & Qt::LeftButton);
|
||||
QVERIFY(!(slider->style()->styleHint(QStyle::SH_Slider_PageSetButtons, nullptr, slider) & Qt::LeftButton));
|
||||
}
|
||||
for (auto *title : window.findChildren<QLabel *>("chartTitle")) {
|
||||
if (title->text() == "Fan speed" || title->text() == "Battery") continue;
|
||||
QVERIFY(std::abs(title->geometry().center().x() - title->parentWidget()->rect().center().x()) <= 2);
|
||||
}
|
||||
for (auto *toggle : window.findChildren<QToolButton *>("extraTemperatureSensors")) QVERIFY(!toggle->isChecked());
|
||||
const QString monitor = qEnvironmentVariable("FRAMEWORK_TOOLS_MONITOR_SCREENSHOT");
|
||||
if (!monitor.isEmpty()) QVERIFY(window.grab().save(monitor));
|
||||
@@ -568,6 +970,7 @@ private Q_SLOTS:
|
||||
}
|
||||
void sharedSaveAndUndo() {
|
||||
QTemporaryDir config; qputenv("XDG_CONFIG_HOME", config.path().toUtf8());
|
||||
QSettings::setPath(QSettings::NativeFormat, QSettings::UserScope, config.path());
|
||||
QSettings settings("fedora-tools", "framework-laptop-tools");
|
||||
Window window; window.show();
|
||||
auto *bar = window.findChild<QWidget *>("pendingChanges"); QVERIFY(bar);
|
||||
@@ -606,10 +1009,10 @@ private Q_SLOTS:
|
||||
hoverApps->setValue(3);
|
||||
QTRY_VERIFY(save->isEnabled()); save->click();
|
||||
QVERIFY(bar->isHidden()); QCOMPARE(settings.value("sampling/fast").toInt(), 2000);
|
||||
QCOMPARE(settings.value("tray/mode").toString(), QString("number")); QVERIFY(QFile::exists(path));
|
||||
QCOMPARE(settings.value("tray/historyMs").toInt(), 60000);
|
||||
QVERIFY(!settings.value("tray/hover/cpu").toBool());
|
||||
QCOMPARE(settings.value("tray/hover/topApps").toInt(), 3);
|
||||
QCOMPARE(settings.value("tray/icons/1/mode").toString(), QString("number")); QVERIFY(QFile::exists(path));
|
||||
QCOMPARE(settings.value("tray/icons/1/historyMs").toInt(), 60000);
|
||||
QVERIFY(!settings.value("tray/icons/1/hover/cpu").toBool());
|
||||
QCOMPARE(settings.value("tray/icons/1/hover/topApps").toInt(), 3);
|
||||
QVERIFY(!trayIcon->toolTip().startsWith("CPU "));
|
||||
hoverCpu->setChecked(true);
|
||||
hoverApps->setValue(1);
|
||||
@@ -621,7 +1024,7 @@ private Q_SLOTS:
|
||||
QVERIFY(!hoverCpu->isChecked());
|
||||
QCOMPARE(hoverApps->value(), 3);
|
||||
// Hover is a timestamp shared by every graph, not the same screen coordinate.
|
||||
const auto charts = window.findChildren<Chart *>(); QCOMPARE(charts.size(), 4);
|
||||
const auto charts = window.findChildren<Chart *>(); QCOMPARE(charts.size(), 5);
|
||||
const qint64 time = QDateTime::currentMSecsSinceEpoch() - 1000;
|
||||
charts.first()->hovered(time);
|
||||
for (auto *chart : charts) QCOMPARE(chart->hoverTime(), std::optional<qint64>(time));
|
||||
@@ -630,6 +1033,7 @@ private Q_SLOTS:
|
||||
}
|
||||
void lowFanConfirmationCanBeCancelled() {
|
||||
QTemporaryDir config; qputenv("XDG_CONFIG_HOME", config.path().toUtf8());
|
||||
QSettings::setPath(QSettings::NativeFormat, QSettings::UserScope, config.path());
|
||||
Window window; window.show();
|
||||
auto *save = window.findChild<QPushButton *>("saveAll");
|
||||
QTRY_VERIFY(save->isEnabled());
|
||||
|
||||
Reference in New Issue
Block a user