mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-11-05 16:38:17 +00:00
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:
1 parent
76ab280ae9
commit
9997b7a3b8
94 files changed
+66
-9405
No files matched your search
@@ -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 {
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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)]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user