Commit graph

8 commits

Author SHA1 Message Date
Vadim Peretokin
a382a61878
fix: non-English game text no longer garbles after a stray "&" (#9489)
#### Brief overview of the PR

When a game sends a bare `&` that runs straight into non-English
(multi-byte) text - which is not a valid MXP entity - Mudlet was
re-encoding the recovered bytes, mangling the following characters into
mojibake. For example `&Ф` rendered as `&Ð�`, and `Käse&Brötchen` lost
its umlaut. This restores the raw-byte passthrough for these
malformed/unknown entities while keeping the correct session-encoding
handling for custom `<!ENTITY>` values. Non-English text now displays
intact.

#### Motivation for adding to Mudlet

#9439 fixed custom MXP entities that carry non-Latin1 values, but the
same code path also handles malformed and unknown entities, whose value
is raw, still-undecoded bytes (one Latin1 code unit per byte). Encoding
those bytes with the session charset double-encoded them and orphaned
the UTF-8 continuation bytes still sitting in the buffer, so any
non-ASCII text immediately after a stray `&` turned to garbage. Players
on games that use Cyrillic, umlauts, CJK and other non-ASCII text were
the ones affected.

The fix distinguishes the two cases: a custom entity value is decoded
text, so it is re-encoded with the session encoding; a malformed/unknown
entity value is raw bytes, so it is passed through unchanged with
`toLatin1()`.

#### Other info

Follow-up to #9439.

**Test case:**

Two functional tests were added (using an ephemeral stub port so
parallel test runs cannot collide):

- `test_MalformedEntityKeepsNonAsciiBytes` - sends `Käse&Brötchen and &Ф
too` and expects it back byte-for-byte. This fails on the pre-fix code
(the tail garbles to mojibake) and passes with the fix.
- `test_CustomEntityKeepsNonAsciiValue` - sends `<!ENTITY storm
"Гроза">The &storm; rages` and expects `The Гроза rages`, guarding the
#9439 behaviour so the new branching does not regress it.

Manual check: connect to a UTF-8 game that emits a bare `&` followed by
non-English text (e.g. `&Ф`). Before this fix the following characters
render as garbage; after it they render correctly.

Assisted-by: Claude:claude-fable-5
Assisted-by: Claude:claude-opus-4-8
2026-07-27 19:48:40 +02:00
Vadim Peretokin
cb402cc3e6
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
Vadim Peretokin
094022f47c
infrastructure: test suites no longer clash when run at the same time (#9483)
#### Brief overview of PR changes/additions
- TelnetServerStub now binds an OS-assigned (ephemeral) port; its log
reports the actual bound port
- All 11 functional tests that hardcoded listen ports (three shared port
4000, two pairs shared 4003/4004) now read the real port back via
serverPort()

#### Motivation for adding to Mudlet
Concurrent test runs (parallel CI jobs, multiple checkouts on one
machine) collided on the fixed ports, causing flaky bind failures and
tests connecting to the wrong run's server.

#### Other info (issues closed, discussion etc)
Follows the pattern GMCPCharLoginTest already used. Verified by running
two copies of TelnetTextDisplayedTest simultaneously - both passed on
distinct ports (37503/42303), impossible before.

**Test case:** Run the functional suite twice in parallel (two build
dirs or ctest -j2 repeated); no "address already in use" failures.

Assisted-by: Claude:claude-opus-4-8
2026-07-20 09:01:51 +02:00
Stephen Lyons
113218930f
Infrastructure: Swap out QtConcurrent module header for sub-module ones (#9246)
#### Brief overview of PR changes/additions
The Qt documentation for `QtConcurrent` points out:
> If you include the `<QtConcurrent>` header, the entire Qt Concurrent
module with the entire Qt Core module will be included, which may
increase compilation times and binary sizes. To use individual functions
from the QtConcurrent namespace, you can include more specific headers.
>
> The table below lists the functions in the QtConcurrent namespace and
their corresponding headers:

|Function|Header|
|--------|------|
|`QtConcurrent::run()`|`<QtConcurrentRun>`|
|`QtConcurrent::task()`| `<QtConcurrentTask>`|

|`QtConcurrent::filter()`,<br>`QtConcurrent::filtered()`,<br>`QtConcurrent::filteredReduced()`|`<QtConcurrentFilter>`|
|`QtConcurrent::map()`,<br>`QtConcurrent::mapped()`,<br>`QtConcurrent::mappedReduced()`|`<QtConcurrentMap>`|

#### Motivation for adding to Mudlet
To speed up the build a little by removing stuff that isn't needed.

#### Other info (issues closed, discussion etc)
In doing this I happened to start cleaning up a couple of header files
`T2DMap.h` and then `mudlet.h`, I then got into converting some
`#include`s into forward declarations in a "include-what-you-use" move.
This then rippled through into a (more than 10!) number of files but
should "improve" things.

Note that the ordering of `#include` in many files seems to be rather
haphazard and is due for some serious overhaul - I suggest that we
should actually declare an "official" style for this project so that
everyone knows what it is.

**During the CI/CB process I discovered that Linux and then MacOS builds
were failing because the file referred to by the `#include
<QtConcurrentTask>` header file was missing, yet was present on my local
PC when I was using the Qt framework from the On-line installer.
Initially I suspected a Debian (and then Devuan - as the packaged
version on my own machine also had this defect AND Ubuntu) package
problem; however it now seems to be an upstream Qt issue as the various
Qt versions & OS combinations suggest that Qt themselves fixed it for Qt
6.10:**
| OS     | QtVersion             | Missing header |
|--------|-----------------------|----------------|
| Windows| 6.11.0 package        |       No       |
| Devuan | 6.8.2 package         |      Yes       |
| Devuan | 6.10.0 online install |       No       |
| Ubuntu | 6.9.0 package         |      Yes       |
| Debian | 6.8.2 package         |      Yes       |
| MacOS  | 6.9.0 package         |      Yes       |

**To fix this I reverted to an `#include <qtconcurrenttask.h>` for Linux
and MacOS builds - although it would probably have been better to make
it conditional on the Qt Version instead...**

*I have reported this upstream to Debian - see:
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1135197*

---------

Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
2026-04-29 13:35:29 +01:00
Vadim Peretokin
3d7c7ffedf
infrastructure: fix flaky TelnetTextDisplayedTest in CI (#9193)
#### Brief overview of PR changes/additions
Replace `getCurrentLine()` assertion with a `waitForTextInBuffer()`
helper that polls the entire console buffer via `qWaitFor` with a 5s
timeout.

#### Motivation for adding to Mudlet
The test fails intermittently in CI due to two issues: in PTB
(scheduled) builds the cursor lands on the PTB banner line instead of
the server's message, and in regular builds data sometimes hasn't
arrived within the 200ms signal wait.

#### Other info (issues closed, discussion etc)
Failures seen in:
- https://github.com/Mudlet/Mudlet/actions/runs/24436883460 (PTB, macOS
x86_64 - cursor on PTB banner)
- https://github.com/Mudlet/Mudlet/actions/runs/24310433046 (push,
ubuntu/clang - cursor on connection info line)
- https://github.com/Mudlet/Mudlet/actions/runs/24310465007 (PR,
ubuntu/clang - same race condition)

---------

Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
2026-04-17 18:39:33 +02:00
Vadim Peretokin
ce0f69949f
Fix: increase TelnetTextDisplayedTest timeouts for CI (#9184)
#### Brief overview of PR changes/additions
Increased test timeouts in TelnetTextDisplayedTest - profile load from
1s to 5s, telnet connect from 500ms to 2s.

#### Motivation for adding to Mudlet
CI runners are slower than local machines, causing the test to
consistently time out and segfault (the QPointer access after timeout
triggers a crash). This was blocking PR #9169 and likely other PRs
touching the same CI jobs.

#### Other info (issues closed, discussion etc)
Fixes flaky TelnetTextDisplayedTest failures seen across all CI
platforms (Linux, macOS, Windows).

Ideally the root cause (crash on timeout) would be fixed, but since the
cost here is just a few extra seconds of wait time, increasing the
timeouts is a proportionate fix.
2026-04-11 19:55:30 +02:00
Nicolas KEITA
f8a7b93f3d
Infrastructure: ignore macOS keychain for telnet functional test (#8592)
<!-- Keep the title short & concise so anyone non-technical can
understand it,
     the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
The functional test no longer uses the macOS Keychain.
#### Motivation for adding to Mudlet
Fix CI failing
https://github.com/Mudlet/Mudlet/pull/8572#issuecomment-3587918831
#### Other info (issues closed, discussion etc)
2025-11-28 15:26:07 +01:00
Nicolas KEITA
d36d484ea5
Add: functional GUI tests using QTest (#8572)
<!-- Keep the title short & concise so anyone non-technical can
understand it,
     the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Adds functional GUI tests simulating a user logging into a MUD via
Mudlet. A mock Telnet server sends messages then we verify the GUI
correctly receives and displays them.

For example, the test checks that a welcome message like:

```
QString messageFromTheMud("\x1B[1z<B>Greetings < hunters & sorcerers</B>\x1B[7z");
QString messageToExpect("Greetings < hunters & sorcerers");
```


is correctly parsed and displayed in the client.


#### Motivation for adding to Mudlet
Allows testing GUI behavior and Telnet messages, including malformed MXP
messages (https://github.com/Mudlet/Mudlet/issues/7896).

#### Other info (issues closed, discussion etc)

This also improves test coverage.

Command that I use:
`ctest --test-dir build/test -L functional --output-on-failure`
2025-11-28 04:44:18 +01:00