From f45cbed53b90d46614f78c8f3dbca1601da0f9ab Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Wed, 3 Sep 2025 12:45:05 +0200 Subject: [PATCH] it: verify that surface damage damages connector --- src/it/test_backend.rs | 6 +- src/it/tests.rs | 2 + src/it/tests/t0034_workspace_restoration.rs | 2 + src/it/tests/t0049_surface_damage_backend.rs | 125 +++++++++++++++++++ 4 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 src/it/tests/t0049_surface_damage_backend.rs diff --git a/src/it/test_backend.rs b/src/it/test_backend.rs index 161cc532..3bbbb3d7 100644 --- a/src/it/test_backend.rs +++ b/src/it/test_backend.rs @@ -27,7 +27,7 @@ use { state::State, udmabuf::Udmabuf, utils::{ - clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, + clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, on_change::OnChange, oserror::OsError, syncqueue::SyncQueue, }, video::{ @@ -84,6 +84,7 @@ impl TestBackend { events: Default::default(), feedback: Default::default(), idle: Default::default(), + damage_calls: NumCell::new(0), }); let default_mouse = Rc::new(TestBackendMouse { common: TestInputDeviceCommon { @@ -318,6 +319,7 @@ pub struct TestConnector { pub events: OnChange, pub feedback: CloneCell>>, pub idle: TEEH, + pub damage_calls: NumCell, } impl Connector for TestConnector { @@ -338,7 +340,7 @@ impl Connector for TestConnector { } fn damage(&self) { - // nothing + self.damage_calls.fetch_add(1); } fn drm_dev(&self) -> Option { diff --git a/src/it/tests.rs b/src/it/tests.rs index d793510c..609f1224 100644 --- a/src/it/tests.rs +++ b/src/it/tests.rs @@ -79,6 +79,7 @@ mod t0045_content_type; mod t0046_buffer_release; mod t0047_surface_damage; mod t0048_frame_callback; +mod t0049_surface_damage_backend; pub trait TestCase: Sync { fn name(&self) -> &'static str; @@ -146,5 +147,6 @@ pub fn tests() -> Vec<&'static dyn TestCase> { t0046_buffer_release, t0047_surface_damage, t0048_frame_callback, + t0049_surface_damage_backend, } } diff --git a/src/it/tests/t0034_workspace_restoration.rs b/src/it/tests/t0034_workspace_restoration.rs index 9adf3192..8fa6907b 100644 --- a/src/it/tests/t0034_workspace_restoration.rs +++ b/src/it/tests/t0034_workspace_restoration.rs @@ -7,6 +7,7 @@ use { format::XRGB8888, ifs::wl_output::OutputId, it::{test_backend::TestConnector, test_error::TestResult, testrun::TestRun}, + utils::numcell::NumCell, video::drm::ConnectorType, }, std::rc::Rc, @@ -35,6 +36,7 @@ async fn test(run: Rc) -> TestResult { events: Default::default(), feedback: Default::default(), idle: Default::default(), + damage_calls: NumCell::new(0), }); let new_monitor_info = MonitorInfo { modes: vec![], diff --git a/src/it/tests/t0049_surface_damage_backend.rs b/src/it/tests/t0049_surface_damage_backend.rs new file mode 100644 index 00000000..d25d3a13 --- /dev/null +++ b/src/it/tests/t0049_surface_damage_backend.rs @@ -0,0 +1,125 @@ +use { + crate::it::{test_error::TestResult, testrun::TestRun}, + std::rc::Rc, +}; + +testcase!(); + +/// Test that committing damage on a visible surface causes the backend connector to be damaged. +/// This test verifies that surface damage triggers backend connector damage tracking AND +/// that the frontend actually calls into the backend connector's damage method, +/// ensuring the rendering pipeline knows when to update the display. +async fn test(run: Rc) -> TestResult { + run.backend.install_default()?; + let client = run.create_client().await?; + + // Get connector for tracking backend damage state + let connector_id = run.backend.default_connector.id; + let connector_data = run.state.connectors.get(&connector_id).unwrap(); + + // Create a visible window with mapped surface + let window = client.create_window().await?; + let buffer = client + .spbm + .create_buffer(crate::theme::Color::from_srgb(0, 255, 0))?; + window.surface.attach(buffer.id)?; + window.map().await?; + client.sync().await; + + // Test 1: Ensure initially the backend is not damaged + connector_data.damaged.set(false); + run.backend.default_connector.damage_calls.set(0); + tassert!(!connector_data.damaged.get()); + tassert_eq!(run.backend.default_connector.damage_calls.get(), 0); + + // Test 2: Add surface damage and commit - this should trigger backend connector damage + window.surface.damage(10, 10, 50, 50)?; + window.surface.commit()?; + client.sync().await; + + // Critical test: Verify the backend connector is now damaged AND the backend method was called + tassert!(connector_data.damaged.get()); + tassert!(run.backend.default_connector.damage_calls.get() > 0); + + // Test 3: Reset damage state and test buffer damage + connector_data.damaged.set(false); + let previous_calls = run.backend.default_connector.damage_calls.get(); + tassert!(!connector_data.damaged.get()); + + // Add buffer damage and commit - this should also trigger backend connector damage + window.surface.damage_buffer(0, 0, 1, 1)?; // Damage entire 1x1 buffer + window.surface.commit()?; + client.sync().await; + + // Verify the backend connector is damaged again AND more backend calls were made + tassert!(connector_data.damaged.get()); + tassert!(run.backend.default_connector.damage_calls.get() > previous_calls); + + // Test 4: Test that invisible surfaces do not trigger backend damage + let invisible_surface = client.comp.create_surface().await?; + let invisible_buffer = client + .spbm + .create_buffer(crate::theme::Color::from_srgb(255, 255, 0))?; + invisible_surface.attach(invisible_buffer.id)?; + invisible_surface.commit()?; // Initial commit to attach buffer + client.sync().await; + + // Reset damage state + connector_data.damaged.set(false); + let invisible_calls_before = run.backend.default_connector.damage_calls.get(); + tassert!(!connector_data.damaged.get()); + + // Add damage to invisible surface and commit + invisible_surface.damage(20, 20, 30, 30)?; + invisible_surface.commit()?; + client.sync().await; + + // Invisible surface damage should NOT trigger backend connector damage or backend calls + tassert!(!connector_data.damaged.get()); + tassert_eq!( + run.backend.default_connector.damage_calls.get(), + invisible_calls_before + ); + + // Test 5: Test multiple damage areas on visible surface + connector_data.damaged.set(false); + let multi_calls_before = run.backend.default_connector.damage_calls.get(); + tassert!(!connector_data.damaged.get()); + + // Add multiple damage rectangles to visible surface + window.surface.damage(5, 5, 10, 10)?; + window.surface.damage(25, 25, 15, 15)?; + window.surface.damage_buffer(0, 0, 1, 1)?; + window.surface.commit()?; + client.sync().await; + + // Multiple damage areas on visible surface should trigger backend connector damage and calls + tassert!(connector_data.damaged.get()); + tassert!(run.backend.default_connector.damage_calls.get() > multi_calls_before); + + // Test 6: Test that damage without commit does not trigger backend damage + connector_data.damaged.set(false); + let no_commit_calls_before = run.backend.default_connector.damage_calls.get(); + tassert!(!connector_data.damaged.get()); + + // Add damage but don't commit + window.surface.damage(40, 40, 20, 20)?; + client.sync().await; + + // Damage without commit should NOT trigger backend connector damage or backend calls + tassert!(!connector_data.damaged.get()); + tassert_eq!( + run.backend.default_connector.damage_calls.get(), + no_commit_calls_before + ); + + // Now commit the pending damage + window.surface.commit()?; + client.sync().await; + + // After commit, backend connector should be damaged and backend called + tassert!(connector_data.damaged.get()); + tassert!(run.backend.default_connector.damage_calls.get() > no_commit_calls_before); + + Ok(()) +}