Merge pull request #579 from tcballard/add/task-manager-0.0.3
Add Task Manager for Omarchy 0.0.3 (edge preview)
This commit is contained in:
4 files changed
+208
No files matched your search
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"source": "local",
|
||||
"channels": ["edge"],
|
||||
"upstream": {
|
||||
"watch": {
|
||||
"github": "tcballard/omarchy-task-manager",
|
||||
"pattern": "v(?P<version>[0-9]+\\.[0-9]+\\.[0-9]+)",
|
||||
"allow_prerelease": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
# Canonical recipe. scripts/package-source.sh fills the release source checksum.
|
||||
# Maintainer: Tom Ballard (tcballard)
|
||||
pkgname=omarchy-task-manager
|
||||
pkgver=0.0.3
|
||||
pkgrel=3
|
||||
url='https://github.com/tcballard/omarchy-task-manager'
|
||||
pkgdesc='Floating native task manager for Omarchy (preview)'
|
||||
arch=('x86_64')
|
||||
license=('MIT')
|
||||
depends=('qt6-base' 'qt6-declarative' 'qt6-wayland' 'qt6-svg' 'hicolor-icon-theme' 'gcc-libs' 'glibc' 'glib2' 'systemd' 'coreutils')
|
||||
optdepends=('gdb: live process core dumps' 'nvidia-utils: NVIDIA device telemetry')
|
||||
makedepends=('cmake' 'ninja' 'rust' 'cargo')
|
||||
checkdepends=('python' 'desktop-file-utils')
|
||||
source=("$url/releases/download/v$pkgver/$pkgname-$pkgver.tar.gz"
|
||||
"pause-resume-test.patch"
|
||||
"worker-shutdown.patch")
|
||||
sha256sums=('cc3e24a0bb8fad2b7ce0d4fb780aa774d32b53b8de924b488b55674bdb936c85'
|
||||
'ce191fc8dc7e4f7018193aa4982d58fd463f66b37e3129b3acce46b7a1c9a89e'
|
||||
'1142aaedf8739bf2ed1cb823a03ed7e406d7ff59d7dfb72748815cc81cfda96b')
|
||||
|
||||
# Backport upstream PR #11 (sample observation) and PR #12 (worker shutdown).
|
||||
prepare() {
|
||||
cd "$srcdir/$pkgname-$pkgver"
|
||||
patch -Np1 -i "$srcdir/pause-resume-test.patch"
|
||||
patch -Np1 -i "$srcdir/worker-shutdown.patch"
|
||||
}
|
||||
|
||||
build() {
|
||||
cmake -S "$srcdir/$pkgname-$pkgver" -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_INSTALL_LIBDIR=lib
|
||||
cmake --build build
|
||||
}
|
||||
|
||||
check() {
|
||||
cd "$srcdir/$pkgname-$pkgver"
|
||||
cargo test --locked
|
||||
ctest --test-dir "$srcdir/build" --output-on-failure
|
||||
desktop-file-validate packaging/io.github.tcballard.TaskManager.desktop
|
||||
python tests/protocol.py "$srcdir/build/omarchy-task-manager-core"
|
||||
}
|
||||
|
||||
package() {
|
||||
DESTDIR="$pkgdir" cmake --install build
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
diff --git a/tests/bridge_test.cpp b/tests/bridge_test.cpp
|
||||
index bab79f2..9bed7be 100644
|
||||
--- a/tests/bridge_test.cpp
|
||||
+++ b/tests/bridge_test.cpp
|
||||
@@ -79,37 +79,53 @@ private slots:
|
||||
const auto before = bridge.history().size();
|
||||
QTest::qWait(1100);
|
||||
QCOMPARE(bridge.history().size(), before);
|
||||
- QSignalSpy samples(&bridge, &Bridge::snapshotChanged);
|
||||
+ // Capture at emission: QTRY processes events, so another timer sample can
|
||||
+ // replace bridge.snapshot() before the waiting assertion runs.
|
||||
+ QVariantList samples;
|
||||
+ QList<int> historySizes;
|
||||
+ QObject sampleObserver; // Disconnect before the captured lists are destroyed.
|
||||
+ connect(&bridge, &Bridge::snapshotChanged, &sampleObserver, [&] {
|
||||
+ samples.append(bridge.snapshot());
|
||||
+ historySizes.append(bridge.history().size());
|
||||
+ });
|
||||
bridge.setPaused(false);
|
||||
QTRY_VERIFY_WITH_TIMEOUT(samples.count() > 0, 8000);
|
||||
- QVERIFY(!bridge.snapshot()
|
||||
+ QVERIFY(!samples.first().toMap()
|
||||
.value("system")
|
||||
.toMap()
|
||||
.value("continuous")
|
||||
.toBool());
|
||||
- QCOMPARE(bridge.history().size(), 1);
|
||||
+ QCOMPARE(historySizes.first(), 1);
|
||||
bridge.active(false);
|
||||
QTest::qWait(600);
|
||||
samples.clear();
|
||||
+ historySizes.clear();
|
||||
bridge.active(true);
|
||||
QTRY_VERIFY_WITH_TIMEOUT(samples.count() > 0, 8000);
|
||||
- QVERIFY(!bridge.snapshot()
|
||||
+ QVERIFY(!samples.first().toMap()
|
||||
.value("system")
|
||||
.toMap()
|
||||
.value("continuous")
|
||||
.toBool());
|
||||
QTRY_VERIFY_WITH_TIMEOUT(!bridge.busy(), 8000);
|
||||
samples.clear();
|
||||
+ historySizes.clear();
|
||||
bridge.refresh();
|
||||
QVERIFY(bridge.busy());
|
||||
bridge.setPaused(true);
|
||||
bridge.setPaused(false);
|
||||
QTRY_VERIFY_WITH_TIMEOUT(samples.count() > 0, 8000);
|
||||
- QVERIFY(!bridge.snapshot()
|
||||
+ QVERIFY(!samples.first().toMap()
|
||||
.value("system")
|
||||
.toMap()
|
||||
.value("continuous")
|
||||
.toBool());
|
||||
+ QCOMPARE(historySizes.first(), 1);
|
||||
+ // Keep automatic refresh enabled. The baseline must be followed by a
|
||||
+ // continuous sample; inspecting only the latest snapshot misses this order.
|
||||
+ QTRY_VERIFY_WITH_TIMEOUT(samples.count() >= 2, 8000);
|
||||
+ QVERIFY(samples.at(1).toMap().value("system").toMap()
|
||||
+ .value("continuous").toBool());
|
||||
QTRY_VERIFY_WITH_TIMEOUT(!bridge.busy(), 8000);
|
||||
QVERIFY(bridge.prepareManagement({{"category", "startup"}}).isEmpty());
|
||||
QVERIFY(
|
||||
@@ -0,0 +1,90 @@
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index c940e5e..d1cd40f 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -29,7 +29,8 @@ if(BUILD_TESTING)
|
||||
target_include_directories(bridge-test PRIVATE ui)
|
||||
target_link_libraries(bridge-test PRIVATE Qt6::Core Qt6::Gui Qt6::Test)
|
||||
add_dependencies(bridge-test core)
|
||||
- add_test(NAME bridge COMMAND bridge-test)
|
||||
+ add_test(NAME bridge COMMAND bridge-test -nocrashhandler)
|
||||
+ set_tests_properties(bridge PROPERTIES TIMEOUT 60)
|
||||
endif()
|
||||
if(BUILD_TESTING)
|
||||
qt_add_executable(ui-test tests/ui_test.cpp ui/bridge.cpp ui/bridge.h)
|
||||
@@ -37,5 +38,6 @@ if(BUILD_TESTING)
|
||||
qt_add_resources(ui-test test_qml PREFIX "/" FILES ${TASK_MANAGER_QML})
|
||||
target_link_libraries(ui-test PRIVATE Qt6::Core Qt6::Gui Qt6::Quick Qt6::Qml Qt6::QuickControls2 Qt6::Test)
|
||||
add_dependencies(ui-test core)
|
||||
- add_test(NAME ui COMMAND ui-test)
|
||||
+ add_test(NAME ui COMMAND ui-test -nocrashhandler)
|
||||
+ set_tests_properties(ui PROPERTIES TIMEOUT 60)
|
||||
endif()
|
||||
diff --git a/tests/bridge_test.cpp b/tests/bridge_test.cpp
|
||||
index 9bed7be..d6737a4 100644
|
||||
--- a/tests/bridge_test.cpp
|
||||
+++ b/tests/bridge_test.cpp
|
||||
@@ -3,9 +3,46 @@
|
||||
#include <QSignalSpy>
|
||||
#include <QTemporaryDir>
|
||||
#include <QTest>
|
||||
+#include <memory>
|
||||
+#include <signal.h>
|
||||
class BridgeTest : public QObject {
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
+ void shutdownDoesNotPublish_data() {
|
||||
+ QTest::addColumn<bool>("inFlight");
|
||||
+ QTest::addColumn<bool>("stopped");
|
||||
+ QTest::newRow("idle") << false << false;
|
||||
+ QTest::newRow("sample-in-flight") << true << false;
|
||||
+ QTest::newRow("stopped-worker") << true << true;
|
||||
+ }
|
||||
+ void shutdownDoesNotPublish() {
|
||||
+ QFETCH(bool, inFlight);
|
||||
+ QFETCH(bool, stopped);
|
||||
+ auto bridge = std::make_unique<Bridge>();
|
||||
+ QTRY_VERIFY_WITH_TIMEOUT(!bridge->snapshot().isEmpty(), 8000);
|
||||
+ bridge->m_timer.stop();
|
||||
+ QTRY_VERIFY_WITH_TIMEOUT(!bridge->busy(), 8000);
|
||||
+ // Stop only the disposable worker owned by this bridge, forcing shutdown
|
||||
+ // through terminate/kill rather than the normal stdin-EOF exit.
|
||||
+ if (stopped) {
|
||||
+ const auto workerPid = bridge->m_worker.processId();
|
||||
+ QVERIFY(workerPid > 0);
|
||||
+ QCOMPARE(::kill(workerPid, SIGSTOP), 0);
|
||||
+ }
|
||||
+ if (inFlight) {
|
||||
+ bridge->refresh();
|
||||
+ QVERIFY(bridge->busy());
|
||||
+ }
|
||||
+ QSignalSpy snapshots(bridge.get(), &Bridge::snapshotChanged);
|
||||
+ QSignalSpy statuses(bridge.get(), &Bridge::statusChanged);
|
||||
+ QSignalSpy busy(bridge.get(), &Bridge::busyChanged);
|
||||
+ // Closing the app must not publish late samples or worker-exit errors while
|
||||
+ // its bridge and UI are being destroyed. Spies outlive the bridge on purpose.
|
||||
+ bridge.reset();
|
||||
+ QCOMPARE(snapshots.count(), 0);
|
||||
+ QCOMPARE(statuses.count(), 0);
|
||||
+ QCOMPARE(busy.count(), 0);
|
||||
+ }
|
||||
void inspectionErrorsAndDismissal() {
|
||||
Bridge bridge;
|
||||
QTRY_VERIFY_WITH_TIMEOUT(!bridge.snapshot().isEmpty(), 8000);
|
||||
diff --git a/ui/bridge.cpp b/ui/bridge.cpp
|
||||
index 86b5b47..b1fb303 100644
|
||||
--- a/ui/bridge.cpp
|
||||
+++ b/ui/bridge.cpp
|
||||
@@ -82,7 +82,12 @@ Bridge::Bridge(QObject *p) : QObject(p), m_rows(this) {
|
||||
m_clock.start();
|
||||
}
|
||||
Bridge::~Bridge() {
|
||||
+ // waitForFinished() can emit readyRead/error/finished synchronously. Quiesce
|
||||
+ // callbacks before waiting, including if QProcess outlives our final wait and
|
||||
+ // emits from its destructor after the other Bridge members are gone.
|
||||
m_timer.stop();
|
||||
+ m_timeout.stop();
|
||||
+ m_worker.disconnect(this);
|
||||
m_worker.closeWriteChannel();
|
||||
if (!m_worker.waitForFinished(600)) {
|
||||
m_worker.terminate();
|
||||
Reference in new issue
Block a user