Files
rustdesk/flutter/test/input_modifier_utils_test.dart
T
RustDeskandClaude Opus 5.5 9997b7a3b8 test: remove tests that do not pin user-visible behavior (#16539)
* test: remove tests that do not pin user-visible behavior

Apply the AGENTS.md test rules to the existing Rust and Flutter suites.
A test is kept when it is the only coverage of user- or API-visible
behavior, the regression test of a reported or fixed bug, a security
boundary (one test per rejection class), or a check against an external
reference. Tests that assert private state or the order of internal
steps, pin review-round corner cases, or repeat what a kept test already
covers are removed.

Rust: 679 -> 298 tests. Dart: 118 -> 45 tests.

Test-only helpers left without callers go with them
(applyLastOnlineStateForTesting, terminalKeyboardRowWidth,
debugBufferedInputCount, tokio's test-util dev feature), and cfg gates
and visibility that existed only for the deleted tests are narrowed.
Runtime behavior is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WS1HTjp6UQuG43KEcQPq1R

* test: keep tests that pin security and user-visible behavior

Restore six tests that review showed are the only coverage of their
behavior:

- update metadata rejects an artifact file name with path components;
- ICE candidates from another session are dropped;
- KCP delivers every multi-segment frame queued before a close;
- set_files rejects an empty name in a multi-file transfer;
- a lopsided elongated cursor keeps its centre hotspot (#16242);
- toggling Show hidden during a pending same-path read issues a new read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WS1HTjp6UQuG43KEcQPq1R

* test: keep offline update and key-exchange downgrade rejections

Restore three tests that are the only coverage of their rejection class:

- offline update metadata rejects a signed artifact with an http or
  credential-bearing URL;
- offline verification rejects a renamed local file and a version that
  does not match the release;
- a key-exchange pick lowered in transit leaves both sides unable to
  decrypt, so a v0 fallback after a failed v1 decrypt cannot pass
  unnoticed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WS1HTjp6UQuG43KEcQPq1R

* test: keep the decomposed-grapheme and reconnect-cancel tests

Each catches a plausible wrong change that passes every kept test:
mapping the first scalar under a locked Ctrl turns é into \x05,
and a reconnect that does not fail pending reads leaves them hanging
until the 30 s timeout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WS1HTjp6UQuG43KEcQPq1R

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 14:45:22 +08:00

237 lines
6.2 KiB
Dart

import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_hbb/models/input_modifier_utils.dart';
void main() {
group('shouldReleaseStaleMobileShift', () {
test('releases one-shot mobile shift after a text key', () {
expect(
shouldReleaseStaleMobileShift(
isMobile: true,
cachedShiftPressed: true,
actualShiftPressed: false,
logicalKey: LogicalKeyboardKey.keyD,
hasTrackedShiftKeyDown: true,
),
isTrue,
);
});
test('does not release when shift is still physically pressed', () {
expect(
shouldReleaseStaleMobileShift(
isMobile: true,
cachedShiftPressed: true,
actualShiftPressed: true,
logicalKey: LogicalKeyboardKey.keyD,
hasTrackedShiftKeyDown: true,
),
isFalse,
);
});
});
group('shouldApplyTerminalInputModifiers', () {
test('rejects terminal control bytes and multi-character sequences', () {
for (final input in ['\x00', '\x03', '\t', '\n', '\r', '\x1B', '\x7F']) {
expect(
shouldApplyTerminalInputModifiers(input),
isFalse,
reason: '${input.codeUnits} must not consume a one-shot modifier',
);
}
expect(shouldApplyTerminalInputModifiers('\x1B[A'), isFalse);
});
});
group('applyTerminalInputModifiers', () {
test('maps the complete Ctrl symbol range', () {
const mappings = {
'[': '\x1B',
r'\': '\x1C',
']': '\x1D',
'^': '\x1E',
'_': '\x1F',
'/': '\x1F',
};
for (final entry in mappings.entries) {
expect(
applyTerminalInputModifiers(
entry.key,
ctrlLocked: true,
altLocked: false,
),
entry.value,
reason: 'Ctrl+${entry.key} should map to ${entry.value.codeUnits}',
);
}
});
test('applies Ctrl before Alt for combined modifiers', () {
expect(
applyTerminalInputModifiers(
'b',
ctrlLocked: true,
altLocked: true,
),
'\x1B\x02',
);
});
});
group('prepareTerminalInputPayload', () {
test('normalizes a mobile keyboard Enter to carriage return', () {
expect(
prepareTerminalInputPayload(
'\n',
source: TerminalInputSource.keyboard,
isMobileOrWebMobile: true,
bracketedPasteMode: false,
ctrlLocked: false,
altLocked: false,
),
'\r',
);
});
test('keeps Ctrl+J as line feed on mobile', () {
expect(
prepareTerminalInputPayload(
'j',
source: TerminalInputSource.keyboard,
isMobileOrWebMobile: true,
bracketedPasteMode: false,
ctrlLocked: true,
altLocked: false,
),
'\n',
);
});
test('does not apply Alt to a terminal control byte', () {
expect(
prepareTerminalInputPayload(
'\x1B',
source: TerminalInputSource.keyboard,
isMobileOrWebMobile: true,
bracketedPasteMode: false,
ctrlLocked: false,
altLocked: true,
),
'\x1B',
);
});
test('keeps decomposed graphemes intact with locked keyboard modifiers',
() {
const decomposedEAcute = 'é';
expect(
prepareTerminalInputPayload(
decomposedEAcute,
source: TerminalInputSource.keyboard,
isMobileOrWebMobile: true,
bracketedPasteMode: false,
ctrlLocked: true,
altLocked: false,
),
decomposedEAcute,
);
});
test('wraps paste without applying locked modifiers', () {
expect(
prepareTerminalInputPayload(
'd',
source: TerminalInputSource.paste,
isMobileOrWebMobile: true,
bracketedPasteMode: true,
ctrlLocked: true,
altLocked: true,
),
'\x1B[200~d\x1B[201~',
);
});
});
group('shouldHandleTerminalPasteShortcut', () {
test('handles only Ctrl+Shift+V on Linux with a virtual lock', () {
expect(
shouldHandleTerminalPasteShortcut(
platform: TargetPlatform.linux,
logicalKey: LogicalKeyboardKey.keyV,
isKeyDown: true,
isKeyRepeat: false,
controlPressed: true,
metaPressed: false,
altPressed: false,
shiftPressed: true,
modifierLockActive: true,
),
isTrue,
);
expect(
shouldHandleTerminalPasteShortcut(
platform: TargetPlatform.linux,
logicalKey: LogicalKeyboardKey.keyV,
isKeyDown: true,
isKeyRepeat: false,
controlPressed: true,
metaPressed: false,
altPressed: false,
shiftPressed: false,
modifierLockActive: true,
),
isFalse,
);
});
test('handles Ctrl+V and Meta+V when a virtual modifier lock is active',
() {
expect(
shouldHandleTerminalPasteShortcut(
platform: TargetPlatform.windows,
logicalKey: LogicalKeyboardKey.keyV,
isKeyDown: true,
isKeyRepeat: false,
controlPressed: true,
metaPressed: false,
altPressed: false,
shiftPressed: false,
modifierLockActive: true,
),
isTrue,
);
expect(
shouldHandleTerminalPasteShortcut(
platform: TargetPlatform.macOS,
logicalKey: LogicalKeyboardKey.keyV,
isKeyDown: true,
isKeyRepeat: false,
controlPressed: false,
metaPressed: true,
altPressed: false,
shiftPressed: false,
modifierLockActive: true,
),
isTrue,
);
});
});
group('shouldClearTerminalModifiersWhenRow3Collapses', () {
test('clears visible modifier state when expanded row is collapsed', () {
expect(
shouldClearTerminalModifiersWhenRow3Collapses(
wasExpanded: true,
willExpand: false,
ctrlLocked: true,
altLocked: false,
),
isTrue,
);
});
});
}