fix: enable/disable only affecting some same-named items on Windows (#9366)
#### Brief overview of PR changes/additions
Fixes a Windows-only bug where `enableAlias`/`disableAlias` and the
trigger/timer/key equivalents (plus `setTriggerStayOpen`) only toggled
some items when several shared a name, e.g. two groups both named "Druid
Aliases". The cause was `mLookupTable.constFind(name)` + forward
iteration, which could start mid-run in the `QMultiMap` and skip
duplicates depending on the platform's iteration order - fine on
Linux/macOS, broken on Windows; it now uses `equal_range()`, which is
correct everywhere.
#### Motivation for adding to Mudlet
Windows users with items sharing a name (commonly alias/trigger groups)
could only partly enable/disable them, silently leaving the rest in the
wrong state.
#### Other info (issues closed, discussion etc)
Adds a busted spec and a C++ functional test covering aliases, triggers,
timers, keys and scripts. The functional test failed on the Windows CI
runner and passed on Linux/macOS, pinning it to the platform-dependent
`QMultiMap` iteration. Scripts were already correct (they iterate all
matches).
**Test case:** on Windows, two alias groups both named "Druid Aliases" →
`disableAlias("Druid Aliases")` stops both, `enableAlias("Druid
Aliases")` re-enables both. Covered by `EnableDisableByNameTest` and
`Alias_spec.lua`.
---------
Signed-off-by: Vadim Peretokin <vperetokin@hey.com>
2026-07-03 14:17:58 +02:00
|
|
|
/***************************************************************************
|
|
|
|
|
* Copyright (C) 2026 by Mudlet Developers *
|
|
|
|
|
* *
|
|
|
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
|
|
|
* it under the terms of the GNU General Public License as published by *
|
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
|
|
|
* (at your option) any later version. *
|
|
|
|
|
* *
|
|
|
|
|
* This program is distributed in the hope that it will be useful, *
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
|
|
|
* GNU General Public License for more details. *
|
|
|
|
|
* *
|
|
|
|
|
* You should have received a copy of the GNU General Public License *
|
|
|
|
|
* along with this program; if not, write to the *
|
|
|
|
|
* Free Software Foundation, Inc., *
|
|
|
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* enableX()/disableX() look an item up by name and must toggle EVERY item
|
|
|
|
|
* sharing that name, not just the first one found (e.g. two groups both named
|
|
|
|
|
* "Druid Aliases"). This covers all scriptable item types - aliases, triggers,
|
|
|
|
|
* timers, keys and scripts - by creating two same-named items plus a
|
|
|
|
|
* differently-named control, toggling them through the real Lua functions, and
|
|
|
|
|
* checking each item's active state directly.
|
|
|
|
|
*
|
|
|
|
|
* Run with: ctest -R EnableDisableByNameTest -V
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <QtTest/QtTest>
|
infrastructure: use std::chrono literals for time durations (#9493)
#### Brief overview of PR changes/additions
Convert raw millisecond integer literals at time-duration call sites to
`std::chrono` literals, and add `#include <chrono>` to each touched
translation unit. Examples:
- `QTimer::singleShot(0, ...)` → `QTimer::singleShot(0ms, ...)`
- `mpTimerReplay->setInterval(1000)` → `setInterval(1s)`
- `mPendingTimer.start(60000)` → `start(1min)`
- `QObject::startTimer(50)` → `startTimer(50ms)`
- `QTest::qWait(100)` → `QTest::qWait(100ms)`
- `QThread::msleep(10)` → `QThread::sleep(10ms)`
This is a semantics-preserving refactor - every duration is kept exactly
equal to before (e.g. `1000` ms becomes `1s`, `60000` ms becomes
`1min`). No behavioural change.
#### Motivation for adding to Mudlet
Chrono literals make time durations self-documenting and type-safe. `1s`
/ `100ms` read unambiguously where a bare `1000` / `100` forces the
reader to remember each API's unit, and the compiler now rejects unit
mismatches. Only genuine duration arguments were converted - loop
counts, scroll-line counts, sizes, ports and the like were deliberately
left as plain integers.
All targeted APIs provide `std::chrono` overloads in the minimum
supported Qt (6.8.2): `QTimer::singleShot`/`start`/`setInterval` (5.8),
`QObject::startTimer` (5.9), `QThread::sleep(std::chrono::nanoseconds)`
(6.6) and `QTest::qWait(std::chrono::milliseconds)` (6.7).
#### Other info (issues closed, discussion etc)
Test case: the full application builds cleanly and the entire functional
`ctest` suite passes. The only failing test is the known, pre-existing
`PasswordMigrationTest` LSan exit-leak (GTK3/fontconfig noise), which is
unrelated to this change.
Assisted-by: Claude:claude-opus-4-8
2026-07-25 20:24:31 +02:00
|
|
|
#include <chrono>
|
fix: enable/disable only affecting some same-named items on Windows (#9366)
#### Brief overview of PR changes/additions
Fixes a Windows-only bug where `enableAlias`/`disableAlias` and the
trigger/timer/key equivalents (plus `setTriggerStayOpen`) only toggled
some items when several shared a name, e.g. two groups both named "Druid
Aliases". The cause was `mLookupTable.constFind(name)` + forward
iteration, which could start mid-run in the `QMultiMap` and skip
duplicates depending on the platform's iteration order - fine on
Linux/macOS, broken on Windows; it now uses `equal_range()`, which is
correct everywhere.
#### Motivation for adding to Mudlet
Windows users with items sharing a name (commonly alias/trigger groups)
could only partly enable/disable them, silently leaving the rest in the
wrong state.
#### Other info (issues closed, discussion etc)
Adds a busted spec and a C++ functional test covering aliases, triggers,
timers, keys and scripts. The functional test failed on the Windows CI
runner and passed on Linux/macOS, pinning it to the platform-dependent
`QMultiMap` iteration. Scripts were already correct (they iterate all
matches).
**Test case:** on Windows, two alias groups both named "Druid Aliases" →
`disableAlias("Druid Aliases")` stops both, `enableAlias("Druid
Aliases")` re-enables both. Covered by `EnableDisableByNameTest` and
`Alias_spec.lua`.
---------
Signed-off-by: Vadim Peretokin <vperetokin@hey.com>
2026-07-03 14:17:58 +02:00
|
|
|
|
|
|
|
|
#include <functional>
|
|
|
|
|
|
|
|
|
|
#include "AliasUnit.h"
|
|
|
|
|
#include "Host.h"
|
|
|
|
|
#include "KeyUnit.h"
|
|
|
|
|
#include "MudletInstanceCoordinator.h"
|
|
|
|
|
#include "ScriptUnit.h"
|
|
|
|
|
#include "TAlias.h"
|
|
|
|
|
#include "TKey.h"
|
|
|
|
|
#include "TLuaInterpreter.h"
|
|
|
|
|
#include "TScript.h"
|
|
|
|
|
#include "TTimer.h"
|
|
|
|
|
#include "TTrigger.h"
|
|
|
|
|
#include "TimerUnit.h"
|
|
|
|
|
#include "TriggerUnit.h"
|
|
|
|
|
#include "TelnetServerStub.h"
|
|
|
|
|
#include "ctelnet.h"
|
|
|
|
|
#include "dlgConnectionProfiles.h"
|
|
|
|
|
#include "mudlet.h"
|
|
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
#if defined(INCLUDE_VERSIONED_LUA_HEADERS)
|
|
|
|
|
#include <lua5.1/lauxlib.h>
|
|
|
|
|
#include <lua5.1/lua.h>
|
|
|
|
|
#include <lua5.1/lualib.h>
|
|
|
|
|
#else
|
|
|
|
|
#include <lauxlib.h>
|
|
|
|
|
#include <lua.h>
|
|
|
|
|
#include <lualib.h>
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
infrastructure: use std::chrono literals for time durations (#9493)
#### Brief overview of PR changes/additions
Convert raw millisecond integer literals at time-duration call sites to
`std::chrono` literals, and add `#include <chrono>` to each touched
translation unit. Examples:
- `QTimer::singleShot(0, ...)` → `QTimer::singleShot(0ms, ...)`
- `mpTimerReplay->setInterval(1000)` → `setInterval(1s)`
- `mPendingTimer.start(60000)` → `start(1min)`
- `QObject::startTimer(50)` → `startTimer(50ms)`
- `QTest::qWait(100)` → `QTest::qWait(100ms)`
- `QThread::msleep(10)` → `QThread::sleep(10ms)`
This is a semantics-preserving refactor - every duration is kept exactly
equal to before (e.g. `1000` ms becomes `1s`, `60000` ms becomes
`1min`). No behavioural change.
#### Motivation for adding to Mudlet
Chrono literals make time durations self-documenting and type-safe. `1s`
/ `100ms` read unambiguously where a bare `1000` / `100` forces the
reader to remember each API's unit, and the compiler now rejects unit
mismatches. Only genuine duration arguments were converted - loop
counts, scroll-line counts, sizes, ports and the like were deliberately
left as plain integers.
All targeted APIs provide `std::chrono` overloads in the minimum
supported Qt (6.8.2): `QTimer::singleShot`/`start`/`setInterval` (5.8),
`QObject::startTimer` (5.9), `QThread::sleep(std::chrono::nanoseconds)`
(6.6) and `QTest::qWait(std::chrono::milliseconds)` (6.7).
#### Other info (issues closed, discussion etc)
Test case: the full application builds cleanly and the entire functional
`ctest` suite passes. The only failing test is the known, pre-existing
`PasswordMigrationTest` LSan exit-leak (GTK3/fontconfig noise), which is
unrelated to this change.
Assisted-by: Claude:claude-opus-4-8
2026-07-25 20:24:31 +02:00
|
|
|
using namespace std::chrono_literals;
|
|
|
|
|
|
fix: enable/disable only affecting some same-named items on Windows (#9366)
#### Brief overview of PR changes/additions
Fixes a Windows-only bug where `enableAlias`/`disableAlias` and the
trigger/timer/key equivalents (plus `setTriggerStayOpen`) only toggled
some items when several shared a name, e.g. two groups both named "Druid
Aliases". The cause was `mLookupTable.constFind(name)` + forward
iteration, which could start mid-run in the `QMultiMap` and skip
duplicates depending on the platform's iteration order - fine on
Linux/macOS, broken on Windows; it now uses `equal_range()`, which is
correct everywhere.
#### Motivation for adding to Mudlet
Windows users with items sharing a name (commonly alias/trigger groups)
could only partly enable/disable them, silently leaving the rest in the
wrong state.
#### Other info (issues closed, discussion etc)
Adds a busted spec and a C++ functional test covering aliases, triggers,
timers, keys and scripts. The functional test failed on the Windows CI
runner and passed on Linux/macOS, pinning it to the platform-dependent
`QMultiMap` iteration. Scripts were already correct (they iterate all
matches).
**Test case:** on Windows, two alias groups both named "Druid Aliases" →
`disableAlias("Druid Aliases")` stops both, `enableAlias("Druid
Aliases")` re-enables both. Covered by `EnableDisableByNameTest` and
`Alias_spec.lua`.
---------
Signed-off-by: Vadim Peretokin <vperetokin@hey.com>
2026-07-03 14:17:58 +02:00
|
|
|
extern void qInitResources_mudlet();
|
|
|
|
|
extern void qInitResources_qm();
|
|
|
|
|
extern void qInitResources_additional_splash_screens();
|
|
|
|
|
extern void qInitResources_mudlet_fonts_common();
|
|
|
|
|
extern void qInitResources_mudlet_fonts_posix();
|
|
|
|
|
void initializeQRCResourcesForEnableDisableByNameTest();
|
|
|
|
|
|
|
|
|
|
class EnableDisableByNameTest : public QObject
|
|
|
|
|
{
|
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
TelnetServerStub* mpServer = nullptr;
|
|
|
|
|
Host* mpHost = nullptr;
|
|
|
|
|
const QString mHostname = "EnableDisableByName-Test";
|
2026-07-20 09:01:51 +02:00
|
|
|
QString mPort; // assigned the stub's actual ephemeral port in initTestCase()
|
fix: enable/disable only affecting some same-named items on Windows (#9366)
#### Brief overview of PR changes/additions
Fixes a Windows-only bug where `enableAlias`/`disableAlias` and the
trigger/timer/key equivalents (plus `setTriggerStayOpen`) only toggled
some items when several shared a name, e.g. two groups both named "Druid
Aliases". The cause was `mLookupTable.constFind(name)` + forward
iteration, which could start mid-run in the `QMultiMap` and skip
duplicates depending on the platform's iteration order - fine on
Linux/macOS, broken on Windows; it now uses `equal_range()`, which is
correct everywhere.
#### Motivation for adding to Mudlet
Windows users with items sharing a name (commonly alias/trigger groups)
could only partly enable/disable them, silently leaving the rest in the
wrong state.
#### Other info (issues closed, discussion etc)
Adds a busted spec and a C++ functional test covering aliases, triggers,
timers, keys and scripts. The functional test failed on the Windows CI
runner and passed on Linux/macOS, pinning it to the platform-dependent
`QMultiMap` iteration. Scripts were already correct (they iterate all
matches).
**Test case:** on Windows, two alias groups both named "Druid Aliases" →
`disableAlias("Druid Aliases")` stops both, `enableAlias("Druid
Aliases")` re-enables both. Covered by `EnableDisableByNameTest` and
`Alias_spec.lua`.
---------
Signed-off-by: Vadim Peretokin <vperetokin@hey.com>
2026-07-03 14:17:58 +02:00
|
|
|
const QString mLocalhost = "localhost";
|
|
|
|
|
|
|
|
|
|
void runLua(const QString& code)
|
|
|
|
|
{
|
|
|
|
|
lua_State* L = mpHost->mLuaInterpreter.getLuaGlobalState();
|
Add: sign in to supported games using your browser (e.g. Google, Discord, or the game's own account) (#9378)
#### Brief overview of PR changes/additions
Adds client-side support for the GMCP `Char.Login` v2 sign-in flow. When
a game offers it, Mudlet automates sign-in *around the game's own login
screen* — it renders no sign-in UI of its own:
- Hands off to the game's interactive screen with an empty
`Char.Login.Credentials {}` when nothing is stored, so the player picks
a provider (Google, Discord, GitHub, the game's own account, …) as text
on the game's own screen.
- Opens the sign-in URL the server pushes (`Char.Login.URL`) in the
system browser — but only after the player has acted on this connection,
never unprompted.
- Autofills a stored character name + password when the profile has
them.
- Persists and replays the server's reconnect token (`Char.Login.Token`
/ `Char.Login.Reconnect`) for instant, password-less reconnects, with a
"forget saved sign-in" control in Preferences → Connection.
- Resumes the *remembered* provider's browser sign-in without a menu
when a saved token has expired or been revoked (`Char.Login.Credentials
{account, provider}`), falling back to the interactive hand-off only
when no provider is remembered.
- Handles token rotation and multiple devices safely: overwrites the
saved token when the server rotates it, and if another running Mudlet
instance sharing the profile's keychain rotates the token mid-flight,
replays the fresh token instead of discarding it.
- For a game that is its own OpenID Provider over TLS, optionally runs
the client-driven PKCE flow end to end (`Char.Login.AuthCode`).
#### Motivation for adding to Mudlet
Modern games are moving to browser-based single sign-on; this lets
Mudlet players use those accounts directly, and reconnect without
re-entering anything — while classic character-name/password logins keep
working unchanged.
#### Other info (issues closed, discussion etc)
- Reworks the approach to resolve @vadi2's UX feedback
(https://github.com/Mudlet/Mudlet/pull/9373#issuecomment-4865669499):
there is **no client pop-up and no in-client chooser**. The game owns
the sign-in screen and offers the choice as text; Mudlet only automates
the mechanical hooks around it (autofill, open URL, save token, replay
token).
- Supersedes and closes #9373.
- Implements the revised v2 draft spec, inspired by #9354.
- Covered by new tests: `OAuthClientFlowTest` (OIDC discovery, PKCE,
loopback capture) and the `GMCPCharLoginTest` functional suite driving a
GMCP server stub through the full client flow.
- Try it out on StickMUD.
https://github.com/user-attachments/assets/566947f6-4f43-4bff-b98c-9328b7a40a2d
---------
Signed-off-by: Michael Conley <sousesider@gmail.com>
2026-07-14 08:20:02 -04:00
|
|
|
if (luaL_dostring(L, code.toUtf8().constData()) != 0) {
|
|
|
|
|
const QString error = QString::fromUtf8(lua_tostring(L, -1));
|
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
QFAIL(qPrintable(qsl("Lua error running test script: %1").arg(error)));
|
|
|
|
|
}
|
fix: enable/disable only affecting some same-named items on Windows (#9366)
#### Brief overview of PR changes/additions
Fixes a Windows-only bug where `enableAlias`/`disableAlias` and the
trigger/timer/key equivalents (plus `setTriggerStayOpen`) only toggled
some items when several shared a name, e.g. two groups both named "Druid
Aliases". The cause was `mLookupTable.constFind(name)` + forward
iteration, which could start mid-run in the `QMultiMap` and skip
duplicates depending on the platform's iteration order - fine on
Linux/macOS, broken on Windows; it now uses `equal_range()`, which is
correct everywhere.
#### Motivation for adding to Mudlet
Windows users with items sharing a name (commonly alias/trigger groups)
could only partly enable/disable them, silently leaving the rest in the
wrong state.
#### Other info (issues closed, discussion etc)
Adds a busted spec and a C++ functional test covering aliases, triggers,
timers, keys and scripts. The functional test failed on the Windows CI
runner and passed on Linux/macOS, pinning it to the platform-dependent
`QMultiMap` iteration. Scripts were already correct (they iterate all
matches).
**Test case:** on Windows, two alias groups both named "Druid Aliases" →
`disableAlias("Druid Aliases")` stops both, `enableAlias("Druid
Aliases")` re-enables both. Covered by `EnableDisableByNameTest` and
`Alias_spec.lua`.
---------
Signed-off-by: Vadim Peretokin <vperetokin@hey.com>
2026-07-03 14:17:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Drives one item type through the shared expectation: enableX(dupName)
|
|
|
|
|
// activates BOTH same-named items, disableX(dupName) deactivates BOTH while
|
|
|
|
|
// leaving the differently-named control untouched, and enableX(dupName)
|
|
|
|
|
// brings both back. `active` reads an item's live state by id.
|
|
|
|
|
void checkToggleAffectsAllMatches(const QString& typeName,
|
|
|
|
|
const QString& dupName,
|
|
|
|
|
const QString& soloName,
|
|
|
|
|
int id1,
|
|
|
|
|
int id2,
|
|
|
|
|
int idControl,
|
|
|
|
|
const QString& enableFn,
|
|
|
|
|
const QString& disableFn,
|
|
|
|
|
std::function<bool(int)> active)
|
|
|
|
|
{
|
|
|
|
|
runLua(qsl("%1('%2')").arg(enableFn, dupName));
|
|
|
|
|
runLua(qsl("%1('%2')").arg(enableFn, soloName));
|
|
|
|
|
QVERIFY2(active(id1) && active(id2), qPrintable(qsl("both same-named %1s should start enabled").arg(typeName)));
|
|
|
|
|
QVERIFY2(active(idControl), qPrintable(qsl("control %1 should start enabled").arg(typeName)));
|
|
|
|
|
|
|
|
|
|
runLua(qsl("%1('%2')").arg(disableFn, dupName));
|
|
|
|
|
QVERIFY2(!active(id1), qPrintable(qsl("first %1 should be disabled").arg(typeName)));
|
|
|
|
|
QVERIFY2(!active(id2), qPrintable(qsl("second same-named %1 should ALSO be disabled").arg(typeName)));
|
|
|
|
|
QVERIFY2(active(idControl), qPrintable(qsl("differently-named %1 must stay enabled").arg(typeName)));
|
|
|
|
|
|
|
|
|
|
runLua(qsl("%1('%2')").arg(enableFn, dupName));
|
|
|
|
|
QVERIFY2(active(id1), qPrintable(qsl("first %1 should be re-enabled").arg(typeName)));
|
|
|
|
|
QVERIFY2(active(id2), qPrintable(qsl("second same-named %1 should ALSO be re-enabled").arg(typeName)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private slots:
|
|
|
|
|
void initTestCase()
|
|
|
|
|
{
|
|
|
|
|
initializeQRCResourcesForEnableDisableByNameTest();
|
|
|
|
|
|
|
|
|
|
mpServer = new TelnetServerStub(qApp);
|
2026-07-20 09:01:51 +02:00
|
|
|
mpServer->start(mLocalhost, 0); // ephemeral OS-assigned port avoids collisions across concurrent test runs
|
|
|
|
|
mPort = QString::number(mpServer->serverPort());
|
fix: enable/disable only affecting some same-named items on Windows (#9366)
#### Brief overview of PR changes/additions
Fixes a Windows-only bug where `enableAlias`/`disableAlias` and the
trigger/timer/key equivalents (plus `setTriggerStayOpen`) only toggled
some items when several shared a name, e.g. two groups both named "Druid
Aliases". The cause was `mLookupTable.constFind(name)` + forward
iteration, which could start mid-run in the `QMultiMap` and skip
duplicates depending on the platform's iteration order - fine on
Linux/macOS, broken on Windows; it now uses `equal_range()`, which is
correct everywhere.
#### Motivation for adding to Mudlet
Windows users with items sharing a name (commonly alias/trigger groups)
could only partly enable/disable them, silently leaving the rest in the
wrong state.
#### Other info (issues closed, discussion etc)
Adds a busted spec and a C++ functional test covering aliases, triggers,
timers, keys and scripts. The functional test failed on the Windows CI
runner and passed on Linux/macOS, pinning it to the platform-dependent
`QMultiMap` iteration. Scripts were already correct (they iterate all
matches).
**Test case:** on Windows, two alias groups both named "Druid Aliases" →
`disableAlias("Druid Aliases")` stops both, `enableAlias("Druid
Aliases")` re-enables both. Covered by `EnableDisableByNameTest` and
`Alias_spec.lua`.
---------
Signed-off-by: Vadim Peretokin <vperetokin@hey.com>
2026-07-03 14:17:58 +02:00
|
|
|
mudlet::start();
|
|
|
|
|
mudlet::self()->setupConfig();
|
|
|
|
|
mudlet::self()->takeOwnershipOfInstanceCoordinator(std::make_unique<MudletInstanceCoordinator>("MudletInstanceCoordinator"));
|
|
|
|
|
mudlet::self()->init();
|
|
|
|
|
mudlet::self()->setStorePasswordsSecurely(false);
|
|
|
|
|
deleteProfileDirectory(mHostname);
|
|
|
|
|
|
|
|
|
|
startProfile(mHostname, mLocalhost, mPort);
|
|
|
|
|
mpHost = mudlet::self()->getActiveHost();
|
|
|
|
|
QVERIFY2(mpHost, "No active host after profile creation");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void cleanupTestCase()
|
|
|
|
|
{
|
|
|
|
|
mpHost = nullptr;
|
|
|
|
|
delete mpServer;
|
|
|
|
|
mpServer = nullptr;
|
|
|
|
|
deleteProfileDirectory(mHostname);
|
|
|
|
|
delete mudlet::self();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void test_aliasEnableDisableAffectsAllMatches()
|
|
|
|
|
{
|
|
|
|
|
auto [id1, m1] = mpHost->mLuaInterpreter.startPermAlias(qsl("Dup Aliases"), qsl(""), qsl("^dup_alias_1$"), qsl(""));
|
|
|
|
|
auto [id2, m2] = mpHost->mLuaInterpreter.startPermAlias(qsl("Dup Aliases"), qsl(""), qsl("^dup_alias_2$"), qsl(""));
|
|
|
|
|
auto [id3, m3] = mpHost->mLuaInterpreter.startPermAlias(qsl("Solo Alias"), qsl(""), qsl("^dup_alias_3$"), qsl(""));
|
|
|
|
|
QVERIFY2(id1 > 0, qPrintable(m1));
|
|
|
|
|
QVERIFY2(id2 > 0, qPrintable(m2));
|
|
|
|
|
QVERIFY2(id3 > 0, qPrintable(m3));
|
|
|
|
|
|
|
|
|
|
auto* unit = mpHost->getAliasUnit();
|
|
|
|
|
checkToggleAffectsAllMatches(qsl("alias"), qsl("Dup Aliases"), qsl("Solo Alias"), id1, id2, id3, qsl("enableAlias"), qsl("disableAlias"), [unit](int id) {
|
|
|
|
|
auto* p = unit->getAlias(id);
|
|
|
|
|
return p && p->isActive();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void test_triggerEnableDisableAffectsAllMatches()
|
|
|
|
|
{
|
|
|
|
|
QStringList p1{qsl("dup_trig_1")};
|
|
|
|
|
QStringList p2{qsl("dup_trig_2")};
|
|
|
|
|
QStringList p3{qsl("dup_trig_3")};
|
|
|
|
|
auto [id1, m1] = mpHost->mLuaInterpreter.startPermSubstringTrigger(qsl("Dup Triggers"), qsl(""), p1, qsl(""));
|
|
|
|
|
auto [id2, m2] = mpHost->mLuaInterpreter.startPermSubstringTrigger(qsl("Dup Triggers"), qsl(""), p2, qsl(""));
|
|
|
|
|
auto [id3, m3] = mpHost->mLuaInterpreter.startPermSubstringTrigger(qsl("Solo Trigger"), qsl(""), p3, qsl(""));
|
|
|
|
|
QVERIFY2(id1 > 0, qPrintable(m1));
|
|
|
|
|
QVERIFY2(id2 > 0, qPrintable(m2));
|
|
|
|
|
QVERIFY2(id3 > 0, qPrintable(m3));
|
|
|
|
|
|
|
|
|
|
auto* unit = mpHost->getTriggerUnit();
|
|
|
|
|
checkToggleAffectsAllMatches(qsl("trigger"), qsl("Dup Triggers"), qsl("Solo Trigger"), id1, id2, id3, qsl("enableTrigger"), qsl("disableTrigger"), [unit](int id) {
|
|
|
|
|
auto* p = unit->getTrigger(id);
|
|
|
|
|
return p && p->isActive();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// setTriggerStayOpen() looks triggers up by name through the same path as the
|
|
|
|
|
// enable/disable functions, so it must update EVERY same-named trigger too.
|
|
|
|
|
void test_setTriggerStayOpenAffectsAllMatches()
|
|
|
|
|
{
|
|
|
|
|
QStringList p1{qsl("stayopen_trig_1")};
|
|
|
|
|
QStringList p2{qsl("stayopen_trig_2")};
|
|
|
|
|
QStringList p3{qsl("stayopen_trig_3")};
|
|
|
|
|
auto [id1, m1] = mpHost->mLuaInterpreter.startPermSubstringTrigger(qsl("StayOpen Triggers"), qsl(""), p1, qsl(""));
|
|
|
|
|
auto [id2, m2] = mpHost->mLuaInterpreter.startPermSubstringTrigger(qsl("StayOpen Triggers"), qsl(""), p2, qsl(""));
|
|
|
|
|
auto [id3, m3] = mpHost->mLuaInterpreter.startPermSubstringTrigger(qsl("Solo StayOpen"), qsl(""), p3, qsl(""));
|
|
|
|
|
QVERIFY2(id1 > 0, qPrintable(m1));
|
|
|
|
|
QVERIFY2(id2 > 0, qPrintable(m2));
|
|
|
|
|
QVERIFY2(id3 > 0, qPrintable(m3));
|
|
|
|
|
|
|
|
|
|
auto* unit = mpHost->getTriggerUnit();
|
|
|
|
|
auto* t1 = unit->getTrigger(id1);
|
|
|
|
|
auto* t2 = unit->getTrigger(id2);
|
|
|
|
|
auto* t3 = unit->getTrigger(id3);
|
|
|
|
|
QVERIFY(t1 && t2 && t3);
|
|
|
|
|
|
|
|
|
|
runLua(qsl("setTriggerStayOpen('StayOpen Triggers', 5)"));
|
|
|
|
|
QVERIFY2(t1->mKeepFiring == 5, "first trigger should be set to stay open");
|
|
|
|
|
QVERIFY2(t2->mKeepFiring == 5, "second same-named trigger should ALSO be set to stay open");
|
|
|
|
|
QVERIFY2(t3->mKeepFiring == 0, "differently-named trigger must stay untouched");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void test_timerEnableDisableAffectsAllMatches()
|
|
|
|
|
{
|
|
|
|
|
auto [id1, m1] = mpHost->mLuaInterpreter.startPermTimer(qsl("Dup Timers"), qsl(""), 60.0, qsl(""));
|
|
|
|
|
auto [id2, m2] = mpHost->mLuaInterpreter.startPermTimer(qsl("Dup Timers"), qsl(""), 60.0, qsl(""));
|
|
|
|
|
auto [id3, m3] = mpHost->mLuaInterpreter.startPermTimer(qsl("Solo Timer"), qsl(""), 60.0, qsl(""));
|
|
|
|
|
QVERIFY2(id1 > 0, qPrintable(m1));
|
|
|
|
|
QVERIFY2(id2 > 0, qPrintable(m2));
|
|
|
|
|
QVERIFY2(id3 > 0, qPrintable(m3));
|
|
|
|
|
|
|
|
|
|
auto* unit = mpHost->getTimerUnit();
|
|
|
|
|
checkToggleAffectsAllMatches(qsl("timer"), qsl("Dup Timers"), qsl("Solo Timer"), id1, id2, id3, qsl("enableTimer"), qsl("disableTimer"), [unit](int id) {
|
|
|
|
|
auto* p = unit->getTimer(id);
|
|
|
|
|
return p && p->isActive();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void test_keyEnableDisableAffectsAllMatches()
|
|
|
|
|
{
|
|
|
|
|
QString n1 = qsl("Dup Keys");
|
|
|
|
|
QString n2 = qsl("Dup Keys");
|
|
|
|
|
QString n3 = qsl("Solo Key");
|
|
|
|
|
QString parent;
|
|
|
|
|
QString func;
|
|
|
|
|
int mod = Qt::NoModifier;
|
|
|
|
|
int kc1 = Qt::Key_F7;
|
|
|
|
|
int kc2 = Qt::Key_F8;
|
|
|
|
|
int kc3 = Qt::Key_F9;
|
|
|
|
|
auto [id1, m1] = mpHost->mLuaInterpreter.startPermKey(n1, parent, kc1, mod, func);
|
|
|
|
|
auto [id2, m2] = mpHost->mLuaInterpreter.startPermKey(n2, parent, kc2, mod, func);
|
|
|
|
|
auto [id3, m3] = mpHost->mLuaInterpreter.startPermKey(n3, parent, kc3, mod, func);
|
|
|
|
|
QVERIFY2(id1 > 0, qPrintable(m1));
|
|
|
|
|
QVERIFY2(id2 > 0, qPrintable(m2));
|
|
|
|
|
QVERIFY2(id3 > 0, qPrintable(m3));
|
|
|
|
|
|
|
|
|
|
auto* unit = mpHost->getKeyUnit();
|
|
|
|
|
checkToggleAffectsAllMatches(qsl("key"), qsl("Dup Keys"), qsl("Solo Key"), id1, id2, id3, qsl("enableKey"), qsl("disableKey"), [unit](int id) {
|
|
|
|
|
auto* p = unit->getKey(id);
|
|
|
|
|
return p && p->isActive();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void test_scriptEnableDisableAffectsAllMatches()
|
|
|
|
|
{
|
|
|
|
|
auto makeScript = [this](const QString& name) {
|
|
|
|
|
auto* pScript = new TScript(name, mpHost);
|
|
|
|
|
pScript->setScript(qsl(""));
|
|
|
|
|
mpHost->getScriptUnit()->registerScript(pScript);
|
|
|
|
|
pScript->setIsActive(true);
|
|
|
|
|
pScript->compile();
|
|
|
|
|
return pScript->getID();
|
|
|
|
|
};
|
|
|
|
|
int id1 = makeScript(qsl("Dup Scripts"));
|
|
|
|
|
int id2 = makeScript(qsl("Dup Scripts"));
|
|
|
|
|
int id3 = makeScript(qsl("Solo Script"));
|
|
|
|
|
QVERIFY(id1 > 0);
|
|
|
|
|
QVERIFY(id2 > 0);
|
|
|
|
|
QVERIFY(id3 > 0);
|
|
|
|
|
|
|
|
|
|
auto* unit = mpHost->getScriptUnit();
|
|
|
|
|
checkToggleAffectsAllMatches(qsl("script"), qsl("Dup Scripts"), qsl("Solo Script"), id1, id2, id3, qsl("enableScript"), qsl("disableScript"), [unit](int id) {
|
|
|
|
|
auto* p = unit->getScript(id);
|
|
|
|
|
return p && p->isActive();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Helpers (reused from ResetProfileTest pattern)
|
|
|
|
|
|
|
|
|
|
void startProfile(const QString& hostname, const QString& address, const QString& port)
|
|
|
|
|
{
|
infrastructure: use std::chrono literals for time durations (#9493)
#### Brief overview of PR changes/additions
Convert raw millisecond integer literals at time-duration call sites to
`std::chrono` literals, and add `#include <chrono>` to each touched
translation unit. Examples:
- `QTimer::singleShot(0, ...)` → `QTimer::singleShot(0ms, ...)`
- `mpTimerReplay->setInterval(1000)` → `setInterval(1s)`
- `mPendingTimer.start(60000)` → `start(1min)`
- `QObject::startTimer(50)` → `startTimer(50ms)`
- `QTest::qWait(100)` → `QTest::qWait(100ms)`
- `QThread::msleep(10)` → `QThread::sleep(10ms)`
This is a semantics-preserving refactor - every duration is kept exactly
equal to before (e.g. `1000` ms becomes `1s`, `60000` ms becomes
`1min`). No behavioural change.
#### Motivation for adding to Mudlet
Chrono literals make time durations self-documenting and type-safe. `1s`
/ `100ms` read unambiguously where a bare `1000` / `100` forces the
reader to remember each API's unit, and the compiler now rejects unit
mismatches. Only genuine duration arguments were converted - loop
counts, scroll-line counts, sizes, ports and the like were deliberately
left as plain integers.
All targeted APIs provide `std::chrono` overloads in the minimum
supported Qt (6.8.2): `QTimer::singleShot`/`start`/`setInterval` (5.8),
`QObject::startTimer` (5.9), `QThread::sleep(std::chrono::nanoseconds)`
(6.6) and `QTest::qWait(std::chrono::milliseconds)` (6.7).
#### Other info (issues closed, discussion etc)
Test case: the full application builds cleanly and the entire functional
`ctest` suite passes. The only failing test is the known, pre-existing
`PasswordMigrationTest` LSan exit-leak (GTK3/fontconfig noise), which is
unrelated to this change.
Assisted-by: Claude:claude-opus-4-8
2026-07-25 20:24:31 +02:00
|
|
|
QTimer::singleShot(0ms, qApp, [hostname, address, port]() {
|
fix: enable/disable only affecting some same-named items on Windows (#9366)
#### Brief overview of PR changes/additions
Fixes a Windows-only bug where `enableAlias`/`disableAlias` and the
trigger/timer/key equivalents (plus `setTriggerStayOpen`) only toggled
some items when several shared a name, e.g. two groups both named "Druid
Aliases". The cause was `mLookupTable.constFind(name)` + forward
iteration, which could start mid-run in the `QMultiMap` and skip
duplicates depending on the platform's iteration order - fine on
Linux/macOS, broken on Windows; it now uses `equal_range()`, which is
correct everywhere.
#### Motivation for adding to Mudlet
Windows users with items sharing a name (commonly alias/trigger groups)
could only partly enable/disable them, silently leaving the rest in the
wrong state.
#### Other info (issues closed, discussion etc)
Adds a busted spec and a C++ functional test covering aliases, triggers,
timers, keys and scripts. The functional test failed on the Windows CI
runner and passed on Linux/macOS, pinning it to the platform-dependent
`QMultiMap` iteration. Scripts were already correct (they iterate all
matches).
**Test case:** on Windows, two alias groups both named "Druid Aliases" →
`disableAlias("Druid Aliases")` stops both, `enableAlias("Druid
Aliases")` re-enables both. Covered by `EnableDisableByNameTest` and
`Alias_spec.lua`.
---------
Signed-off-by: Vadim Peretokin <vperetokin@hey.com>
2026-07-03 14:17:58 +02:00
|
|
|
mudlet::self()->startAutoLogin({});
|
infrastructure: use std::chrono literals for time durations (#9493)
#### Brief overview of PR changes/additions
Convert raw millisecond integer literals at time-duration call sites to
`std::chrono` literals, and add `#include <chrono>` to each touched
translation unit. Examples:
- `QTimer::singleShot(0, ...)` → `QTimer::singleShot(0ms, ...)`
- `mpTimerReplay->setInterval(1000)` → `setInterval(1s)`
- `mPendingTimer.start(60000)` → `start(1min)`
- `QObject::startTimer(50)` → `startTimer(50ms)`
- `QTest::qWait(100)` → `QTest::qWait(100ms)`
- `QThread::msleep(10)` → `QThread::sleep(10ms)`
This is a semantics-preserving refactor - every duration is kept exactly
equal to before (e.g. `1000` ms becomes `1s`, `60000` ms becomes
`1min`). No behavioural change.
#### Motivation for adding to Mudlet
Chrono literals make time durations self-documenting and type-safe. `1s`
/ `100ms` read unambiguously where a bare `1000` / `100` forces the
reader to remember each API's unit, and the compiler now rejects unit
mismatches. Only genuine duration arguments were converted - loop
counts, scroll-line counts, sizes, ports and the like were deliberately
left as plain integers.
All targeted APIs provide `std::chrono` overloads in the minimum
supported Qt (6.8.2): `QTimer::singleShot`/`start`/`setInterval` (5.8),
`QObject::startTimer` (5.9), `QThread::sleep(std::chrono::nanoseconds)`
(6.6) and `QTest::qWait(std::chrono::milliseconds)` (6.7).
#### Other info (issues closed, discussion etc)
Test case: the full application builds cleanly and the entire functional
`ctest` suite passes. The only failing test is the known, pre-existing
`PasswordMigrationTest` LSan exit-leak (GTK3/fontconfig noise), which is
unrelated to this change.
Assisted-by: Claude:claude-opus-4-8
2026-07-25 20:24:31 +02:00
|
|
|
QTest::qWait(100ms);
|
fix: enable/disable only affecting some same-named items on Windows (#9366)
#### Brief overview of PR changes/additions
Fixes a Windows-only bug where `enableAlias`/`disableAlias` and the
trigger/timer/key equivalents (plus `setTriggerStayOpen`) only toggled
some items when several shared a name, e.g. two groups both named "Druid
Aliases". The cause was `mLookupTable.constFind(name)` + forward
iteration, which could start mid-run in the `QMultiMap` and skip
duplicates depending on the platform's iteration order - fine on
Linux/macOS, broken on Windows; it now uses `equal_range()`, which is
correct everywhere.
#### Motivation for adding to Mudlet
Windows users with items sharing a name (commonly alias/trigger groups)
could only partly enable/disable them, silently leaving the rest in the
wrong state.
#### Other info (issues closed, discussion etc)
Adds a busted spec and a C++ functional test covering aliases, triggers,
timers, keys and scripts. The functional test failed on the Windows CI
runner and passed on Linux/macOS, pinning it to the platform-dependent
`QMultiMap` iteration. Scripts were already correct (they iterate all
matches).
**Test case:** on Windows, two alias groups both named "Druid Aliases" →
`disableAlias("Druid Aliases")` stops both, `enableAlias("Druid
Aliases")` re-enables both. Covered by `EnableDisableByNameTest` and
`Alias_spec.lua`.
---------
Signed-off-by: Vadim Peretokin <vperetokin@hey.com>
2026-07-03 14:17:58 +02:00
|
|
|
QTest::mouseClick(mudlet::self()->mpConnectionDialog->new_profile_button, Qt::LeftButton);
|
infrastructure: use std::chrono literals for time durations (#9493)
#### Brief overview of PR changes/additions
Convert raw millisecond integer literals at time-duration call sites to
`std::chrono` literals, and add `#include <chrono>` to each touched
translation unit. Examples:
- `QTimer::singleShot(0, ...)` → `QTimer::singleShot(0ms, ...)`
- `mpTimerReplay->setInterval(1000)` → `setInterval(1s)`
- `mPendingTimer.start(60000)` → `start(1min)`
- `QObject::startTimer(50)` → `startTimer(50ms)`
- `QTest::qWait(100)` → `QTest::qWait(100ms)`
- `QThread::msleep(10)` → `QThread::sleep(10ms)`
This is a semantics-preserving refactor - every duration is kept exactly
equal to before (e.g. `1000` ms becomes `1s`, `60000` ms becomes
`1min`). No behavioural change.
#### Motivation for adding to Mudlet
Chrono literals make time durations self-documenting and type-safe. `1s`
/ `100ms` read unambiguously where a bare `1000` / `100` forces the
reader to remember each API's unit, and the compiler now rejects unit
mismatches. Only genuine duration arguments were converted - loop
counts, scroll-line counts, sizes, ports and the like were deliberately
left as plain integers.
All targeted APIs provide `std::chrono` overloads in the minimum
supported Qt (6.8.2): `QTimer::singleShot`/`start`/`setInterval` (5.8),
`QObject::startTimer` (5.9), `QThread::sleep(std::chrono::nanoseconds)`
(6.6) and `QTest::qWait(std::chrono::milliseconds)` (6.7).
#### Other info (issues closed, discussion etc)
Test case: the full application builds cleanly and the entire functional
`ctest` suite passes. The only failing test is the known, pre-existing
`PasswordMigrationTest` LSan exit-leak (GTK3/fontconfig noise), which is
unrelated to this change.
Assisted-by: Claude:claude-opus-4-8
2026-07-25 20:24:31 +02:00
|
|
|
QTest::qWait(100ms);
|
fix: enable/disable only affecting some same-named items on Windows (#9366)
#### Brief overview of PR changes/additions
Fixes a Windows-only bug where `enableAlias`/`disableAlias` and the
trigger/timer/key equivalents (plus `setTriggerStayOpen`) only toggled
some items when several shared a name, e.g. two groups both named "Druid
Aliases". The cause was `mLookupTable.constFind(name)` + forward
iteration, which could start mid-run in the `QMultiMap` and skip
duplicates depending on the platform's iteration order - fine on
Linux/macOS, broken on Windows; it now uses `equal_range()`, which is
correct everywhere.
#### Motivation for adding to Mudlet
Windows users with items sharing a name (commonly alias/trigger groups)
could only partly enable/disable them, silently leaving the rest in the
wrong state.
#### Other info (issues closed, discussion etc)
Adds a busted spec and a C++ functional test covering aliases, triggers,
timers, keys and scripts. The functional test failed on the Windows CI
runner and passed on Linux/macOS, pinning it to the platform-dependent
`QMultiMap` iteration. Scripts were already correct (they iterate all
matches).
**Test case:** on Windows, two alias groups both named "Druid Aliases" →
`disableAlias("Druid Aliases")` stops both, `enableAlias("Druid
Aliases")` re-enables both. Covered by `EnableDisableByNameTest` and
`Alias_spec.lua`.
---------
Signed-off-by: Vadim Peretokin <vperetokin@hey.com>
2026-07-03 14:17:58 +02:00
|
|
|
QTest::keyClicks(QApplication::focusWidget(), hostname);
|
infrastructure: use std::chrono literals for time durations (#9493)
#### Brief overview of PR changes/additions
Convert raw millisecond integer literals at time-duration call sites to
`std::chrono` literals, and add `#include <chrono>` to each touched
translation unit. Examples:
- `QTimer::singleShot(0, ...)` → `QTimer::singleShot(0ms, ...)`
- `mpTimerReplay->setInterval(1000)` → `setInterval(1s)`
- `mPendingTimer.start(60000)` → `start(1min)`
- `QObject::startTimer(50)` → `startTimer(50ms)`
- `QTest::qWait(100)` → `QTest::qWait(100ms)`
- `QThread::msleep(10)` → `QThread::sleep(10ms)`
This is a semantics-preserving refactor - every duration is kept exactly
equal to before (e.g. `1000` ms becomes `1s`, `60000` ms becomes
`1min`). No behavioural change.
#### Motivation for adding to Mudlet
Chrono literals make time durations self-documenting and type-safe. `1s`
/ `100ms` read unambiguously where a bare `1000` / `100` forces the
reader to remember each API's unit, and the compiler now rejects unit
mismatches. Only genuine duration arguments were converted - loop
counts, scroll-line counts, sizes, ports and the like were deliberately
left as plain integers.
All targeted APIs provide `std::chrono` overloads in the minimum
supported Qt (6.8.2): `QTimer::singleShot`/`start`/`setInterval` (5.8),
`QObject::startTimer` (5.9), `QThread::sleep(std::chrono::nanoseconds)`
(6.6) and `QTest::qWait(std::chrono::milliseconds)` (6.7).
#### Other info (issues closed, discussion etc)
Test case: the full application builds cleanly and the entire functional
`ctest` suite passes. The only failing test is the known, pre-existing
`PasswordMigrationTest` LSan exit-leak (GTK3/fontconfig noise), which is
unrelated to this change.
Assisted-by: Claude:claude-opus-4-8
2026-07-25 20:24:31 +02:00
|
|
|
QTest::qWait(100ms);
|
fix: enable/disable only affecting some same-named items on Windows (#9366)
#### Brief overview of PR changes/additions
Fixes a Windows-only bug where `enableAlias`/`disableAlias` and the
trigger/timer/key equivalents (plus `setTriggerStayOpen`) only toggled
some items when several shared a name, e.g. two groups both named "Druid
Aliases". The cause was `mLookupTable.constFind(name)` + forward
iteration, which could start mid-run in the `QMultiMap` and skip
duplicates depending on the platform's iteration order - fine on
Linux/macOS, broken on Windows; it now uses `equal_range()`, which is
correct everywhere.
#### Motivation for adding to Mudlet
Windows users with items sharing a name (commonly alias/trigger groups)
could only partly enable/disable them, silently leaving the rest in the
wrong state.
#### Other info (issues closed, discussion etc)
Adds a busted spec and a C++ functional test covering aliases, triggers,
timers, keys and scripts. The functional test failed on the Windows CI
runner and passed on Linux/macOS, pinning it to the platform-dependent
`QMultiMap` iteration. Scripts were already correct (they iterate all
matches).
**Test case:** on Windows, two alias groups both named "Druid Aliases" →
`disableAlias("Druid Aliases")` stops both, `enableAlias("Druid
Aliases")` re-enables both. Covered by `EnableDisableByNameTest` and
`Alias_spec.lua`.
---------
Signed-off-by: Vadim Peretokin <vperetokin@hey.com>
2026-07-03 14:17:58 +02:00
|
|
|
QTest::keyClick(QApplication::focusWidget(), Qt::Key_Tab);
|
infrastructure: use std::chrono literals for time durations (#9493)
#### Brief overview of PR changes/additions
Convert raw millisecond integer literals at time-duration call sites to
`std::chrono` literals, and add `#include <chrono>` to each touched
translation unit. Examples:
- `QTimer::singleShot(0, ...)` → `QTimer::singleShot(0ms, ...)`
- `mpTimerReplay->setInterval(1000)` → `setInterval(1s)`
- `mPendingTimer.start(60000)` → `start(1min)`
- `QObject::startTimer(50)` → `startTimer(50ms)`
- `QTest::qWait(100)` → `QTest::qWait(100ms)`
- `QThread::msleep(10)` → `QThread::sleep(10ms)`
This is a semantics-preserving refactor - every duration is kept exactly
equal to before (e.g. `1000` ms becomes `1s`, `60000` ms becomes
`1min`). No behavioural change.
#### Motivation for adding to Mudlet
Chrono literals make time durations self-documenting and type-safe. `1s`
/ `100ms` read unambiguously where a bare `1000` / `100` forces the
reader to remember each API's unit, and the compiler now rejects unit
mismatches. Only genuine duration arguments were converted - loop
counts, scroll-line counts, sizes, ports and the like were deliberately
left as plain integers.
All targeted APIs provide `std::chrono` overloads in the minimum
supported Qt (6.8.2): `QTimer::singleShot`/`start`/`setInterval` (5.8),
`QObject::startTimer` (5.9), `QThread::sleep(std::chrono::nanoseconds)`
(6.6) and `QTest::qWait(std::chrono::milliseconds)` (6.7).
#### Other info (issues closed, discussion etc)
Test case: the full application builds cleanly and the entire functional
`ctest` suite passes. The only failing test is the known, pre-existing
`PasswordMigrationTest` LSan exit-leak (GTK3/fontconfig noise), which is
unrelated to this change.
Assisted-by: Claude:claude-opus-4-8
2026-07-25 20:24:31 +02:00
|
|
|
QTest::qWait(100ms);
|
fix: enable/disable only affecting some same-named items on Windows (#9366)
#### Brief overview of PR changes/additions
Fixes a Windows-only bug where `enableAlias`/`disableAlias` and the
trigger/timer/key equivalents (plus `setTriggerStayOpen`) only toggled
some items when several shared a name, e.g. two groups both named "Druid
Aliases". The cause was `mLookupTable.constFind(name)` + forward
iteration, which could start mid-run in the `QMultiMap` and skip
duplicates depending on the platform's iteration order - fine on
Linux/macOS, broken on Windows; it now uses `equal_range()`, which is
correct everywhere.
#### Motivation for adding to Mudlet
Windows users with items sharing a name (commonly alias/trigger groups)
could only partly enable/disable them, silently leaving the rest in the
wrong state.
#### Other info (issues closed, discussion etc)
Adds a busted spec and a C++ functional test covering aliases, triggers,
timers, keys and scripts. The functional test failed on the Windows CI
runner and passed on Linux/macOS, pinning it to the platform-dependent
`QMultiMap` iteration. Scripts were already correct (they iterate all
matches).
**Test case:** on Windows, two alias groups both named "Druid Aliases" →
`disableAlias("Druid Aliases")` stops both, `enableAlias("Druid
Aliases")` re-enables both. Covered by `EnableDisableByNameTest` and
`Alias_spec.lua`.
---------
Signed-off-by: Vadim Peretokin <vperetokin@hey.com>
2026-07-03 14:17:58 +02:00
|
|
|
QTest::keyClicks(QApplication::focusWidget(), address);
|
infrastructure: use std::chrono literals for time durations (#9493)
#### Brief overview of PR changes/additions
Convert raw millisecond integer literals at time-duration call sites to
`std::chrono` literals, and add `#include <chrono>` to each touched
translation unit. Examples:
- `QTimer::singleShot(0, ...)` → `QTimer::singleShot(0ms, ...)`
- `mpTimerReplay->setInterval(1000)` → `setInterval(1s)`
- `mPendingTimer.start(60000)` → `start(1min)`
- `QObject::startTimer(50)` → `startTimer(50ms)`
- `QTest::qWait(100)` → `QTest::qWait(100ms)`
- `QThread::msleep(10)` → `QThread::sleep(10ms)`
This is a semantics-preserving refactor - every duration is kept exactly
equal to before (e.g. `1000` ms becomes `1s`, `60000` ms becomes
`1min`). No behavioural change.
#### Motivation for adding to Mudlet
Chrono literals make time durations self-documenting and type-safe. `1s`
/ `100ms` read unambiguously where a bare `1000` / `100` forces the
reader to remember each API's unit, and the compiler now rejects unit
mismatches. Only genuine duration arguments were converted - loop
counts, scroll-line counts, sizes, ports and the like were deliberately
left as plain integers.
All targeted APIs provide `std::chrono` overloads in the minimum
supported Qt (6.8.2): `QTimer::singleShot`/`start`/`setInterval` (5.8),
`QObject::startTimer` (5.9), `QThread::sleep(std::chrono::nanoseconds)`
(6.6) and `QTest::qWait(std::chrono::milliseconds)` (6.7).
#### Other info (issues closed, discussion etc)
Test case: the full application builds cleanly and the entire functional
`ctest` suite passes. The only failing test is the known, pre-existing
`PasswordMigrationTest` LSan exit-leak (GTK3/fontconfig noise), which is
unrelated to this change.
Assisted-by: Claude:claude-opus-4-8
2026-07-25 20:24:31 +02:00
|
|
|
QTest::qWait(100ms);
|
fix: enable/disable only affecting some same-named items on Windows (#9366)
#### Brief overview of PR changes/additions
Fixes a Windows-only bug where `enableAlias`/`disableAlias` and the
trigger/timer/key equivalents (plus `setTriggerStayOpen`) only toggled
some items when several shared a name, e.g. two groups both named "Druid
Aliases". The cause was `mLookupTable.constFind(name)` + forward
iteration, which could start mid-run in the `QMultiMap` and skip
duplicates depending on the platform's iteration order - fine on
Linux/macOS, broken on Windows; it now uses `equal_range()`, which is
correct everywhere.
#### Motivation for adding to Mudlet
Windows users with items sharing a name (commonly alias/trigger groups)
could only partly enable/disable them, silently leaving the rest in the
wrong state.
#### Other info (issues closed, discussion etc)
Adds a busted spec and a C++ functional test covering aliases, triggers,
timers, keys and scripts. The functional test failed on the Windows CI
runner and passed on Linux/macOS, pinning it to the platform-dependent
`QMultiMap` iteration. Scripts were already correct (they iterate all
matches).
**Test case:** on Windows, two alias groups both named "Druid Aliases" →
`disableAlias("Druid Aliases")` stops both, `enableAlias("Druid
Aliases")` re-enables both. Covered by `EnableDisableByNameTest` and
`Alias_spec.lua`.
---------
Signed-off-by: Vadim Peretokin <vperetokin@hey.com>
2026-07-03 14:17:58 +02:00
|
|
|
QTest::keyClick(QApplication::focusWidget(), Qt::Key_Tab);
|
infrastructure: use std::chrono literals for time durations (#9493)
#### Brief overview of PR changes/additions
Convert raw millisecond integer literals at time-duration call sites to
`std::chrono` literals, and add `#include <chrono>` to each touched
translation unit. Examples:
- `QTimer::singleShot(0, ...)` → `QTimer::singleShot(0ms, ...)`
- `mpTimerReplay->setInterval(1000)` → `setInterval(1s)`
- `mPendingTimer.start(60000)` → `start(1min)`
- `QObject::startTimer(50)` → `startTimer(50ms)`
- `QTest::qWait(100)` → `QTest::qWait(100ms)`
- `QThread::msleep(10)` → `QThread::sleep(10ms)`
This is a semantics-preserving refactor - every duration is kept exactly
equal to before (e.g. `1000` ms becomes `1s`, `60000` ms becomes
`1min`). No behavioural change.
#### Motivation for adding to Mudlet
Chrono literals make time durations self-documenting and type-safe. `1s`
/ `100ms` read unambiguously where a bare `1000` / `100` forces the
reader to remember each API's unit, and the compiler now rejects unit
mismatches. Only genuine duration arguments were converted - loop
counts, scroll-line counts, sizes, ports and the like were deliberately
left as plain integers.
All targeted APIs provide `std::chrono` overloads in the minimum
supported Qt (6.8.2): `QTimer::singleShot`/`start`/`setInterval` (5.8),
`QObject::startTimer` (5.9), `QThread::sleep(std::chrono::nanoseconds)`
(6.6) and `QTest::qWait(std::chrono::milliseconds)` (6.7).
#### Other info (issues closed, discussion etc)
Test case: the full application builds cleanly and the entire functional
`ctest` suite passes. The only failing test is the known, pre-existing
`PasswordMigrationTest` LSan exit-leak (GTK3/fontconfig noise), which is
unrelated to this change.
Assisted-by: Claude:claude-opus-4-8
2026-07-25 20:24:31 +02:00
|
|
|
QTest::qWait(100ms);
|
fix: enable/disable only affecting some same-named items on Windows (#9366)
#### Brief overview of PR changes/additions
Fixes a Windows-only bug where `enableAlias`/`disableAlias` and the
trigger/timer/key equivalents (plus `setTriggerStayOpen`) only toggled
some items when several shared a name, e.g. two groups both named "Druid
Aliases". The cause was `mLookupTable.constFind(name)` + forward
iteration, which could start mid-run in the `QMultiMap` and skip
duplicates depending on the platform's iteration order - fine on
Linux/macOS, broken on Windows; it now uses `equal_range()`, which is
correct everywhere.
#### Motivation for adding to Mudlet
Windows users with items sharing a name (commonly alias/trigger groups)
could only partly enable/disable them, silently leaving the rest in the
wrong state.
#### Other info (issues closed, discussion etc)
Adds a busted spec and a C++ functional test covering aliases, triggers,
timers, keys and scripts. The functional test failed on the Windows CI
runner and passed on Linux/macOS, pinning it to the platform-dependent
`QMultiMap` iteration. Scripts were already correct (they iterate all
matches).
**Test case:** on Windows, two alias groups both named "Druid Aliases" →
`disableAlias("Druid Aliases")` stops both, `enableAlias("Druid
Aliases")` re-enables both. Covered by `EnableDisableByNameTest` and
`Alias_spec.lua`.
---------
Signed-off-by: Vadim Peretokin <vperetokin@hey.com>
2026-07-03 14:17:58 +02:00
|
|
|
QTest::keyClicks(QApplication::focusWidget(), port);
|
infrastructure: use std::chrono literals for time durations (#9493)
#### Brief overview of PR changes/additions
Convert raw millisecond integer literals at time-duration call sites to
`std::chrono` literals, and add `#include <chrono>` to each touched
translation unit. Examples:
- `QTimer::singleShot(0, ...)` → `QTimer::singleShot(0ms, ...)`
- `mpTimerReplay->setInterval(1000)` → `setInterval(1s)`
- `mPendingTimer.start(60000)` → `start(1min)`
- `QObject::startTimer(50)` → `startTimer(50ms)`
- `QTest::qWait(100)` → `QTest::qWait(100ms)`
- `QThread::msleep(10)` → `QThread::sleep(10ms)`
This is a semantics-preserving refactor - every duration is kept exactly
equal to before (e.g. `1000` ms becomes `1s`, `60000` ms becomes
`1min`). No behavioural change.
#### Motivation for adding to Mudlet
Chrono literals make time durations self-documenting and type-safe. `1s`
/ `100ms` read unambiguously where a bare `1000` / `100` forces the
reader to remember each API's unit, and the compiler now rejects unit
mismatches. Only genuine duration arguments were converted - loop
counts, scroll-line counts, sizes, ports and the like were deliberately
left as plain integers.
All targeted APIs provide `std::chrono` overloads in the minimum
supported Qt (6.8.2): `QTimer::singleShot`/`start`/`setInterval` (5.8),
`QObject::startTimer` (5.9), `QThread::sleep(std::chrono::nanoseconds)`
(6.6) and `QTest::qWait(std::chrono::milliseconds)` (6.7).
#### Other info (issues closed, discussion etc)
Test case: the full application builds cleanly and the entire functional
`ctest` suite passes. The only failing test is the known, pre-existing
`PasswordMigrationTest` LSan exit-leak (GTK3/fontconfig noise), which is
unrelated to this change.
Assisted-by: Claude:claude-opus-4-8
2026-07-25 20:24:31 +02:00
|
|
|
QTest::qWait(100ms);
|
fix: enable/disable only affecting some same-named items on Windows (#9366)
#### Brief overview of PR changes/additions
Fixes a Windows-only bug where `enableAlias`/`disableAlias` and the
trigger/timer/key equivalents (plus `setTriggerStayOpen`) only toggled
some items when several shared a name, e.g. two groups both named "Druid
Aliases". The cause was `mLookupTable.constFind(name)` + forward
iteration, which could start mid-run in the `QMultiMap` and skip
duplicates depending on the platform's iteration order - fine on
Linux/macOS, broken on Windows; it now uses `equal_range()`, which is
correct everywhere.
#### Motivation for adding to Mudlet
Windows users with items sharing a name (commonly alias/trigger groups)
could only partly enable/disable them, silently leaving the rest in the
wrong state.
#### Other info (issues closed, discussion etc)
Adds a busted spec and a C++ functional test covering aliases, triggers,
timers, keys and scripts. The functional test failed on the Windows CI
runner and passed on Linux/macOS, pinning it to the platform-dependent
`QMultiMap` iteration. Scripts were already correct (they iterate all
matches).
**Test case:** on Windows, two alias groups both named "Druid Aliases" →
`disableAlias("Druid Aliases")` stops both, `enableAlias("Druid
Aliases")` re-enables both. Covered by `EnableDisableByNameTest` and
`Alias_spec.lua`.
---------
Signed-off-by: Vadim Peretokin <vperetokin@hey.com>
2026-07-03 14:17:58 +02:00
|
|
|
QTest::keyClick(QApplication::focusWidget(), Qt::Key_Return);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
QSignalSpy spy(mudlet::self(), &mudlet::signal_profileLoaded);
|
|
|
|
|
if (!spy.wait(1000)) {
|
|
|
|
|
QFAIL("Profile took too long to load.");
|
|
|
|
|
}
|
|
|
|
|
auto host = mudlet::self()->getActiveHost();
|
|
|
|
|
if (!host) {
|
|
|
|
|
QFAIL("No active host available for the test.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QSignalSpy spy2(&(host->mTelnet), &cTelnet::signal_connected);
|
|
|
|
|
if (!spy2.wait(500)) {
|
|
|
|
|
QFAIL("Could not connect with the host.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void deleteProfileDirectory(const QString& profileName)
|
|
|
|
|
{
|
|
|
|
|
const QString path = mudlet::getMudletPath(enums::profileHomePath, profileName);
|
|
|
|
|
QDir dir(path);
|
|
|
|
|
|
|
|
|
|
if (!dir.exists()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
dir.removeRecursively();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void initializeQRCResourcesForEnableDisableByNameTest()
|
|
|
|
|
{
|
|
|
|
|
#ifdef INCLUDE_VARIABLE_SPLASH_SCREEN
|
|
|
|
|
qInitResources_additional_splash_screens();
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef INCLUDE_FONTS
|
|
|
|
|
qInitResources_mudlet_fonts_common();
|
|
|
|
|
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
|
|
|
|
qInitResources_mudlet_fonts_posix();
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
qInitResources_mudlet();
|
|
|
|
|
qInitResources_qm();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#include "EnableDisableByNameTest.moc"
|
|
|
|
|
QTEST_MAIN(EnableDisableByNameTest)
|