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>
This commit is contained in:
RustDeskandClaude Opus 5.5 authored and GitHub committed 2026-10-14 14:45:22 +08:00
1 parent 76ab280ae9
commit 9997b7a3b8
94 files changed
+66 -9405

No files matched your search

-17
View File
@@ -199,23 +199,6 @@ mod webrtc {
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::os::raw::c_uint;
#[test]
fn tile_log2_uses_c_uint_and_rounds_up() {
let one_thread: c_uint = tile_log2(1);
let three_threads: c_uint = tile_log2(3);
let max_threads: c_uint = tile_log2(64);
assert_eq!(one_thread, 0);
assert_eq!(three_threads, 2);
assert_eq!(max_threads, 6);
}
}
}
impl EncoderApi for AomEncoder {
+1 -187
View File
@@ -795,47 +795,7 @@ impl Drop for DrmReader {
#[cfg(test)]
mod hotspot_provenance_tests {
use super::{cursor_id, hot_measured_from, provenance_code, provenance_is_news};
/// The log this makes observable is one line per CHANGE, not one per cursor read. A cursor is
/// read far more often than its provenance changes, and a per-sample line here would be the
/// same defect libdrmtap already had once.
#[test]
fn provenance_is_logged_once_and_then_only_on_a_change() {
let (none, guessed, measured) = (
provenance_code(None),
provenance_code(Some(false)),
provenance_code(Some(true)),
);
assert_ne!(none, guessed);
assert_ne!(guessed, measured);
assert_ne!(none, measured);
// First sample of a run: -1 is the unset sentinel, so anything is news.
assert!(provenance_is_news(-1, measured));
// The same state again is not.
assert!(!provenance_is_news(measured, measured));
assert!(!provenance_is_news(guessed, guessed));
// A real transition is.
assert!(provenance_is_news(guessed, measured));
assert!(provenance_is_news(measured, none));
}
/// The dedup key has to move when the provenance does, or the consumer is never told that the
/// same picture now means something different. This is the transition that produces it: the
/// properties become readable, the driver's answer is the corner, and the bitmap is unchanged.
#[test]
fn provenance_changes_the_cursor_identity() {
let guessed = cursor_id(0xabc, 24, 24, 0, 0, false);
let measured = cursor_id(0xabc, 24, 24, 0, 0, true);
assert_ne!(
guessed, measured,
"same pixels, same (0, 0), different meaning: the producer must not dedupe this away"
);
// And the pre-existing parts still count, so this did not trade one blind spot for another.
assert_ne!(cursor_id(0xabc, 24, 24, 0, 0, true), cursor_id(0xabc, 32, 24, 0, 0, true));
assert_ne!(cursor_id(0xabc, 24, 24, 0, 0, true), cursor_id(0xabc, 24, 24, 1, 0, true));
assert_ne!(cursor_id(0xabc, 24, 24, 0, 0, true), cursor_id(0xdef, 24, 24, 0, 0, true));
}
use super::hot_measured_from;
/// The case the whole entry point exists for, and the one the old heuristic got backwards: a
/// para-virtualized driver that really publishes the hotspot at the image's top-left corner.
@@ -845,25 +805,6 @@ mod hotspot_provenance_tests {
fn a_measured_zero_hotspot_is_measured() {
assert!(hot_measured_from(Some(true), 0, 0));
}
/// And the mirror: when the library says it was NOT measured, that is the answer even if the
/// coordinates happen to be non-zero. The old test would have trusted them.
#[test]
fn the_librarys_no_beats_the_coordinates() {
assert!(!hot_measured_from(Some(false), 12, 11));
assert!(!hot_measured_from(Some(false), 0, 0));
}
/// No answer available: either the symbol is absent (a library older than 0.5.6) or nothing
/// recorded one for this sample (a cursor read through an older privileged helper). Neither
/// is "it was a guess", so the pre-existing behaviour is kept rather than resolved either way
/// -- an older deployed .so must keep behaving as it used to.
#[test]
fn without_an_answer_the_old_heuristic_stands() {
assert!(!hot_measured_from(None, 0, 0));
assert!(hot_measured_from(None, 12, 11));
assert!(hot_measured_from(None, 0, 5));
}
}
#[cfg(test)]
@@ -924,45 +865,6 @@ mod hotspot_guess_tests {
assert_eq!(infer_hotspot(&px, w, h), (4, 4));
}
/// Each axis is decided by its OWN mirror. This shape is symmetric left-to-right and not
/// top-to-bottom, which is the single-direction resize family: the click point is centred
/// across the axis that mirrors and stays at the edge on the axis that does not.
#[test]
fn one_symmetric_axis_centres_that_axis_only() {
let (px, w, h) = sprite(&[
"....#....",
"...###...",
"..#####..",
".#######.",
"....#....",
"....#....",
"....#....",
"....#....",
"....#....",
]);
assert_eq!(infer_hotspot(&px, w, h), (4, 0));
}
/// A two-headed DIAGONAL arrow mirrors onto the other diagonal about either axis alone, so
/// neither axis reads as symmetric - and it is still a shape with no tip. The half turn is
/// what catches it.
#[test]
fn a_diagonal_two_headed_arrow_is_held_by_its_middle_too() {
let (px, w, h) = sprite(&[
"###......",
"##.......",
"#.#......",
"...#.....",
"....#....",
".....#...",
"......#.#",
".......##",
"......###",
]);
let (hx, hy) = infer_hotspot(&px, w, h);
assert_eq!((hx, hy), (4, 4), "a half turn maps this shape onto itself");
}
/// A single-direction resize cursor is an arrow plus the bar of the edge it resizes, and the
/// theme puts the click point on the BAR. The bar is the heavy end, and it is the only thing
/// that separates `e-resize` from `w-resize`: they are the same picture reflected, so a rule
@@ -992,25 +894,6 @@ mod hotspot_guess_tests {
assert_eq!(infer_hotspot(&px, w, h), (0, 3));
}
/// A corner resize cursor is a bracket hugging one corner, and it mirrors onto itself about a
/// DIAGONAL rather than about either axis, so neither per-axis test sees it. The click point
/// is the inner corner of the bracket, which is the heavy end on both axes.
///
/// A real one's box is usually not square, and a diagonal reflection only lines up inside a
/// square, so which corner of that square the box is placed against decides the answer:
/// `ne-resize` scores 0.62 anchored one way and 0.98 the other, and `nw-resize` is the
/// reverse. Both anchorings are therefore tried. That case cannot be built as a small
/// picture - a non-square box can never be exactly diagonal-symmetric - so it is covered by
/// the theme corpus instead, where dropping the second anchoring sends `ne-resize` back past
/// master.
#[test]
fn a_corner_bracket_is_held_by_its_inner_corner() {
let (px, w, h) = sprite(&[
".....##", ".....##", ".....##", ".....##", ".....##", "#######", "#######",
]);
assert_eq!(infer_hotspot(&px, w, h), (6, 6));
}
/// The elongation rule stays ahead of the mirror test, so a bar that is NOT symmetric still
/// keeps its centre. Nothing in the installed theme is shaped like this - its two I-beams are
/// both symmetric - but a custom cursor can be, and this is the rustdesk#16242 round-one fix.
@@ -1019,14 +902,6 @@ mod hotspot_guess_tests {
let (px, w, h) = sprite(&["##########.", "#####......", "##########."]);
assert_eq!(infer_hotspot(&px, w, h), (4, 1));
}
/// Nothing opaque is not a shape, and must not read as a box at the origin with a hotspot in
/// the middle of it.
#[test]
fn a_blank_sprite_has_no_hotspot_to_guess() {
let (px, w, h) = sprite(&["...", "...", "..."]);
assert_eq!(infer_hotspot(&px, w, h), (0, 0));
}
}
/// DRM_MODE_ROTATE_0: the `rotation` bitmask of a plane that turned nothing.
@@ -1050,8 +925,6 @@ fn plane_rotation_answer(rc: c_int, rotation: u32) -> Option<u32> {
#[cfg(test)]
mod plane_rotation_tests {
use super::*;
use std::os::fd::AsRawFd;
use std::sync::atomic::{AtomicI32, AtomicU32, Ordering};
#[test]
fn a_plane_without_the_property_turned_nothing() {
@@ -1062,65 +935,6 @@ mod plane_rotation_tests {
assert_eq!(plane_rotation_answer(-hbb_common::libc::EINVAL, 0), None);
assert_eq!(plane_rotation_answer(-hbb_common::libc::EIO, 7), None);
}
// What the plane answers whenever it is asked. The fake release turns it, the way a compositor
// may once the frame is out, so a read taken after the release or outside the grab sees 0x1.
static PLANE: AtomicU32 = AtomicU32::new(0x1);
static FD: AtomicI32 = AtomicI32::new(-1);
static PIXELS: [u8; 16] = [0; 16];
unsafe extern "C" fn plane_rotation(_: *mut drmtap_ctx, rotation: *mut u32) -> c_int {
*rotation = PLANE.load(Ordering::SeqCst);
0
}
unsafe extern "C" fn frame_release(_: *mut drmtap_ctx, _: *mut drmtap_frame_info) {
PLANE.store(0x1, Ordering::SeqCst);
}
unsafe extern "C" fn grab_mapped(_: *mut drmtap_ctx, f: *mut drmtap_frame_info) -> c_int {
(*f).data = PIXELS.as_ptr() as *mut _;
(*f).width = 2;
(*f).height = 2;
(*f).stride = 8;
(*f).format = DRM_FORMAT_XRGB8888;
0
}
unsafe extern "C" fn grab_desc(
_: *mut drmtap_ctx,
d: *mut drmtap_dmabuf_desc,
_: *mut drmtap_frame_info,
) -> c_int {
(*d).dma_buf_fd = FD.load(Ordering::SeqCst);
(*d).width = 2;
(*d).height = 2;
(*d).num_planes = 1;
(*d).pitches[0] = 8;
0
}
#[test]
fn a_frame_keeps_the_rotation_its_plane_had_when_it_was_grabbed() {
let lib: &'static DrmtapLib = Box::leak(Box::new(DrmtapLib::fake(
grab_mapped,
frame_release,
grab_desc,
plane_rotation,
)));
let mut r = DrmReader {
lib,
ctx: std::ptr::null_mut(),
buf: Vec::new(),
last_provenance: -1,
last_plane_rotation: None,
};
PLANE.store(0x4, Ordering::SeqCst);
r.grab().expect("the fake grab succeeds");
assert_eq!(r.plane_rotation(), Some(0x4), "cpu grab");
let (fd, _peer) = std::os::unix::net::UnixStream::pair().unwrap();
FD.store(fd.as_raw_fd(), Ordering::SeqCst);
PLANE.store(0x8, Ordering::SeqCst);
r.grab_desc().expect("the fake export succeeds");
assert_eq!(r.plane_rotation(), Some(0x8), "dma-buf export");
}
}
#[cfg(test)]
-41
View File
@@ -464,41 +464,6 @@ pub fn get() -> Option<&'static DrmtapLib> {
mod tests {
use super::{abi_accepted, DRMTAP_ABI_MAJOR, DRMTAP_ABI_MINOR, DRMTAP_MIN_MINOR_PATCH};
#[test]
fn abi_gate_rejects_a_library_from_before_the_split() {
// These are refused because their MINOR differs from the verified one, which is the only
// reason the gate needs. Naming the pre-split releases keeps the intent readable, but do
// not read this as the floor doing the work: see the test below.
for (minor, patch) in [(3, 3), (4, 0), (4, 8), (4, 9)] {
assert!(
!abi_accepted(DRMTAP_ABI_MAJOR, minor, patch),
"v0.{minor}.{patch} is not the verified minor and must be refused"
);
}
}
#[test]
fn the_patch_floor_is_currently_vacuous_and_that_is_deliberate() {
// With MIN_MINOR_PATCH.0 == DRMTAP_ABI_MINOR the floor can never reject anything: the
// minor equality already forces `(minor, patch) >= (minor, 0)`. It is kept because it is
// the mechanism that WOULD do the work the next time a floor lands mid-minor, as (4, 10)
// did for the split API. This test exists so nobody reads the pre-split test above as
// evidence that the floor is live -- if that ever matters, this assert is the tripwire.
let (floor_minor, floor_patch) = DRMTAP_MIN_MINOR_PATCH;
assert_eq!(
floor_minor, DRMTAP_ABI_MINOR,
"the floor is inside the verified minor; a floor in a DIFFERENT minor is unreachable"
);
if floor_patch == 0 {
assert!(
abi_accepted(DRMTAP_ABI_MAJOR, DRMTAP_ABI_MINOR, 0),
"patch 0 of the verified minor must be accepted while the floor is 0"
);
} else {
assert!(!abi_accepted(DRMTAP_ABI_MAJOR, DRMTAP_ABI_MINOR, floor_patch - 1));
}
}
#[test]
fn abi_gate_accepts_the_floor_and_later_patches_of_the_same_minor() {
let (min_minor, min_patch) = DRMTAP_MIN_MINOR_PATCH;
@@ -528,10 +493,4 @@ mod tests {
);
}
}
#[test]
fn abi_gate_rejects_another_major_in_both_directions() {
assert!(!abi_accepted(DRMTAP_ABI_MAJOR + 1, 0, 0));
assert!(!abi_accepted(DRMTAP_ABI_MAJOR + 1, 99, 99));
}
}
-35
View File
@@ -176,38 +176,3 @@ impl Drop for CapturerGDI {
}
}
}
#[cfg(test)]
mod tests {
use super::super::*;
use super::*;
#[test]
fn test() {
match Displays::new().unwrap().next() {
Some(d) => {
let w = d.width();
let h = d.height();
let c = CapturerGDI::new(d.name(), w, h).unwrap();
let mut data = Vec::new();
c.frame(&mut data).unwrap();
let mut bitflipped = Vec::with_capacity((w * h * 4) as usize);
for y in 0..h {
for x in 0..w {
let i = (w * 4 * y + 4 * x) as usize;
bitflipped.extend_from_slice(&[data[i + 2], data[i + 1], data[i], 255]);
}
}
repng::encode(
std::fs::File::create("gdi_screen.png").unwrap(),
d.width() as u32,
d.height() as u32,
&bitflipped,
)
.unwrap();
}
_ => {
assert!(false);
}
}
}
}
-14
View File
@@ -692,17 +692,3 @@ impl CapturerMag {
TRUE
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
let mut capture_mag = CapturerMag::new((0, 0), 1920, 1080).unwrap();
capture_mag.exclude("", "RustDeskPrivacyWindow").unwrap();
std::thread::sleep(std::time::Duration::from_millis(1000 * 10));
let mut data = Vec::new();
capture_mag.frame(&mut data).unwrap();
println!("capture data len: {}", data.len());
}
}
+5 -152
View File
@@ -21,10 +21,10 @@ const COMMAND_TIMEOUT: Duration = Duration::from_millis(1000);
// drm builds only: an unnamed-endpoint failure there forks the probe child, and the pollers
// turn every few hundred milliseconds. Every other failure is one cheap in-process error.
#[cfg(any(test, feature = "drm"))]
#[cfg(feature = "drm")]
const FAILED_LOOKUP_BACKOFF: Duration = Duration::from_secs(5);
#[cfg(any(test, feature = "drm"))]
#[cfg(feature = "drm")]
static LAST_FAILED_LOOKUP: Mutex<Option<Instant>> = Mutex::new(None);
#[cfg(feature = "drm")]
@@ -183,8 +183,7 @@ fn get_primary_monitor() -> Option<String> {
.or_else(try_gdbus_primary)
}
// Pure, so the backoff policy is testable without a compositor.
#[cfg(any(test, feature = "drm"))]
#[cfg(feature = "drm")]
fn lookup_allowed(failed_at: Option<Instant>, now: Instant) -> bool {
failed_at.map_or(true, |at| {
now.saturating_duration_since(at) >= FAILED_LOOKUP_BACKOFF
@@ -302,6 +301,7 @@ pub fn clear_wayland_displays_cache() {
// every OTHER live capturer on a multi-display session. A capturer records this at build and
// treats a later bump as "the layout changed under me, rebuild" — the only trigger a rotation
// has, since it changes neither the CRTC mode nor the framebuffer size (rustdesk#15886).
#[cfg(feature = "drm")]
static SNAPSHOT_GENERATION: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
/// Whether no snapshot has been cached: the signature of an enumeration that failed at session
@@ -311,7 +311,7 @@ pub fn wayland_snapshot_missing() -> bool {
DISPLAYS.lock().unwrap().is_none()
}
#[cfg(any(test, feature = "drm"))]
#[cfg(feature = "drm")]
pub fn bump_layout_generation() {
SNAPSHOT_GENERATION.fetch_add(1, std::sync::atomic::Ordering::Release);
}
@@ -516,40 +516,6 @@ fn map_axis(v: i32, base_origin: i32, base_extent: i32, live_origin: i32, live_e
mod tests {
use super::*;
#[test]
fn test_lookup_backoff_boundaries() {
// Future `now`s sidestep Instant subtraction, which can panic near boot.
let failed_at = Instant::now();
assert!(lookup_allowed(None, failed_at));
assert!(!lookup_allowed(
Some(failed_at),
failed_at + FAILED_LOOKUP_BACKOFF / 2
));
assert!(lookup_allowed(
Some(failed_at),
failed_at + FAILED_LOOKUP_BACKOFF
));
}
#[test]
fn test_lookup_stamp_from_the_future_only_waits() {
// saturating_duration_since answers zero rather than underflowing.
let now = Instant::now();
assert!(!lookup_allowed(Some(now + FAILED_LOOKUP_BACKOFF), now));
}
#[test]
fn test_clear_keeps_the_failure_stamp() {
// The stamp describes the seat, not the cache: the ~1/s capturer rebuild loop clears,
// and dropping the stamp with it would defeat the backoff. The generation test also
// calls clear now; both only assert monotonic/unchanged state, so they can interleave.
*LAST_FAILED_LOOKUP.lock().unwrap() = Some(Instant::now());
clear_wayland_displays_cache();
let stamp = *LAST_FAILED_LOOKUP.lock().unwrap();
assert!(stamp.is_some());
*LAST_FAILED_LOOKUP.lock().unwrap() = None;
}
fn display(
x: i32,
y: i32,
@@ -569,11 +535,6 @@ mod tests {
}
}
#[test]
fn test_desktop_rect_empty() {
assert_eq!(desktop_rect_of(&[]), None);
}
#[test]
fn test_desktop_rect_single_display_uses_physical_size() {
let displays = [display(0, 0, 2880, 1800, Some((1859, 1162)))];
@@ -591,15 +552,6 @@ mod tests {
assert_eq!(desktop_rect_of(&displays), Some((0, 3779, 0, 2160)));
}
#[test]
fn test_desktop_rect_missing_logical_size_falls_back_to_physical() {
let displays = [
display(0, 0, 2560, 1440, None),
display(2560, 0, 2560, 1440, Some((2560, 1440))),
];
assert_eq!(desktop_rect_of(&displays), Some((0, 5120, 0, 1440)));
}
#[test]
fn a_single_rotated_display_swaps_the_uinput_rect() {
// Review finding 1 on rustdesk#15889: the single-display branch served the unrotated
@@ -611,31 +563,6 @@ mod tests {
assert_eq!((rects[0].w, rects[0].h), (1080, 1920));
}
#[test]
fn a_transform_flip_is_visible_to_the_drift_comparison() {
// Review finding 5: 0<->180 and 90<->270 leave every rect identical; the transform
// field is what lets `baseline != live` fire on them.
let mut a = display(0, 0, 1920, 1080, Some((1920, 1080)));
let mut b = a.clone();
a.transform = 90;
b.transform = 270;
assert_ne!(logical_rects_of(&[a.clone(), a.clone()]), logical_rects_of(&[b.clone(), b]));
}
#[test]
fn only_the_explicit_bump_moves_the_generation() {
// A cache clear must NOT bump: session inits clear too, and a bump there rebuilds
// every other live capturer (adversarial finding on the first version of this).
let before = SNAPSHOT_GENERATION.load(std::sync::atomic::Ordering::Acquire);
clear_wayland_displays_cache();
assert_eq!(
SNAPSHOT_GENERATION.load(std::sync::atomic::Ordering::Acquire),
before
);
bump_layout_generation();
assert!(SNAPSHOT_GENERATION.load(std::sync::atomic::Ordering::Acquire) > before);
}
fn rect(name: &str, x: i32, y: i32, w: i32, h: i32) -> DisplayRect {
DisplayRect {
name: name.to_owned(),
@@ -671,16 +598,6 @@ mod tests {
);
}
// A point on the rescaled display itself is squeezed to its new logical width.
#[test]
fn test_remap_scales_within_resized_display() {
let baseline = [rect("DP-1", 0, 0, 2560, 1440), rect("DP-2", 2560, 0, 2560, 1440)];
let live = [rect("DP-1", 0, 0, 2048, 1440), rect("DP-2", 2048, 0, 2560, 1440)];
// x=1280 across the 2560-wide baseline DP-1 -> proportionally across the 2048-wide
// live DP-1 (endpoint-preserving scale, so ~1px off the naive midpoint).
assert_eq!(remap_to_live_layout(1280, 500, &baseline, &live), (1023, 500));
}
// The far edge of the followed display stays reachable when it is enlarged, so hot
// corners keep working. Baseline DP-1 is 2048 wide, live DP-1 is 2560 wide; the
// client's last column (2047) must map to the live last column (2559), not 2558.
@@ -692,57 +609,6 @@ mod tests {
assert_eq!(remap_to_live_layout(0, 0, &baseline, &live), (0, 0));
}
// No drift: identical layouts map every point to itself.
#[test]
fn test_remap_identity_when_unchanged() {
let layout = [rect("DP-1", 0, 0, 2560, 1440), rect("DP-2", 2560, 0, 2560, 1440)];
assert_eq!(remap_to_live_layout(3000, 700, &layout, &layout), (3000, 700));
}
// Point outside every baseline display is left untouched.
#[test]
fn test_remap_point_outside_all_displays_unchanged() {
let baseline = [rect("DP-1", 0, 0, 2560, 1440)];
let live = [rect("DP-1", 0, 0, 2048, 1440)];
assert_eq!(remap_to_live_layout(9000, 9000, &baseline, &live), (9000, 9000));
}
// Matched display gone from the live layout (e.g. unplugged): leave the point be
// rather than mapping it somewhere wrong.
#[test]
fn test_remap_display_removed_unchanged() {
let baseline = [rect("DP-1", 0, 0, 2560, 1440), rect("DP-2", 2560, 0, 2560, 1440)];
let live = [rect("DP-1", 0, 0, 2560, 1440)];
assert_eq!(remap_to_live_layout(2600, 100, &baseline, &live), (2600, 100));
}
// Nameless compositor: fall back to index matching while the count is unchanged.
#[test]
fn test_remap_nameless_index_fallback() {
let baseline = [rect("", 0, 0, 2560, 1440), rect("", 2560, 0, 2560, 1440)];
let live = [rect("", 0, 0, 2048, 1440), rect("", 2048, 0, 2560, 1440)];
assert_eq!(remap_to_live_layout(2560, 0, &baseline, &live), (2048, 0));
}
// Nameless compositor with a changed count: cannot index-match safely, so no-op.
#[test]
fn test_remap_nameless_count_changed_unchanged() {
let baseline = [rect("", 0, 0, 2560, 1440), rect("", 2560, 0, 2560, 1440)];
let live = [rect("", 0, 0, 2048, 1440)];
assert_eq!(remap_to_live_layout(2560, 0, &baseline, &live), (2560, 0));
}
// A named display absent from the live layout, but the count is unchanged (e.g. a
// monitor was swapped for a different one at the same index): the index fallback is
// for nameless layouts only, so a named miss stays unchanged rather than mapping to
// whatever now occupies that index.
#[test]
fn test_remap_named_miss_equal_count_unchanged() {
let baseline = [rect("DP-1", 0, 0, 2560, 1440), rect("DP-2", 2560, 0, 2560, 1440)];
let live = [rect("DP-1", 0, 0, 2048, 1440), rect("HDMI-1", 2048, 0, 1920, 1080)];
assert_eq!(remap_to_live_layout(2600, 100, &baseline, &live), (2600, 100));
}
// A single display uses physical size in both baseline and live (scale reported as
// 1.0), so it never drifts and the remap is a no-op even across a rescale.
#[test]
@@ -753,17 +619,4 @@ mod tests {
vec![rect("", 0, 0, 2560, 1440)]
);
}
// Multiple displays use logical size, falling back to physical when absent.
#[test]
fn test_logical_rects_multi_display_uses_logical() {
let displays = [
display(0, 0, 2560, 1440, Some((2048, 1152))),
display(2048, 0, 1920, 1080, None),
];
assert_eq!(
logical_rects_of(&displays),
vec![rect("", 0, 0, 2048, 1152), rect("", 2048, 0, 1920, 1080)]
);
}
}
+1 -40
View File
@@ -1802,7 +1802,7 @@ fn sort_streams(
#[cfg(test)]
mod tests {
use super::{corrected_origin, reconciled_origin, stage_err, WaylandDisplayInfo};
use super::{corrected_origin, WaylandDisplayInfo};
fn output(x: i32, y: i32, mode: (i32, i32)) -> WaylandDisplayInfo {
WaylandDisplayInfo {
@@ -1867,43 +1867,4 @@ mod tests {
Some((2560, 0))
);
}
#[test]
fn a_cached_position_is_not_overridden_by_an_unmeasured_size() {
let outputs = [output(0, 0, (1440, 900)), output(1920, 0, (2880, 1800))];
// Restored from the cache, or sent by the portal: get_res() failed, so the size is the
// portal size, here the mode of the other output, and it moves nothing.
assert_eq!(
reconciled_origin((1920, 0), (1440, 900), false, &outputs),
None
);
// A measured size still moves a position that names no output of that size: xdph sends
// (0, 0) for every stream.
let pair = [output(0, 0, (1920, 1080)), output(1920, 0, (2880, 1800))];
assert_eq!(
reconciled_origin((0, 0), (2880, 1800), true, &pair),
Some((1920, 0))
);
}
#[test]
fn stage_err_keeps_the_detail_safe_for_a_placeholder() {
assert_eq!(
stage_err("start", "declined", ""),
"wl-stage:start:declined:"
);
// Braces of its own would break the placeholder lookup on the peer.
assert_eq!(
stage_err("create-session", "dbus", "org.freedesktop.{Error}"),
"wl-stage:create-session:dbus:org.freedesktop.Error"
);
assert_eq!(
stage_err("select-sources", "internal", "one\ntwo"),
"wl-stage:select-sources:internal:one two"
);
assert_eq!(
stage_err("start", "internal", &"x".repeat(300)),
format!("wl-stage:start:internal:{}", "x".repeat(200))
);
}
}