From cbf539cbcc4a304e9fc23117cadb22fa2b1f2876 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 3 May 2022 18:32:43 +0200 Subject: [PATCH] it: test subsurface positioning --- src/it.rs | 5 +- src/it/test_client.rs | 28 +++++++- src/it/test_config.rs | 20 ++++-- src/it/test_ifs.rs | 2 + src/it/test_ifs/test_registry.rs | 20 +++++- src/it/test_ifs/test_subcompositor.rs | 60 ++++++++++++++++ src/it/test_ifs/test_subsurface.rs | 68 ++++++++++++++++++ src/it/test_transport.rs | 7 ++ src/it/test_utils.rs | 1 + src/it/test_utils/test_object_ext.rs | 17 +++++ src/it/testrun.rs | 4 +- src/it/tests.rs | 7 ++ src/it/tests/t0007_subsurface.rs | 55 ++++++++++++++ .../tests/t0007_subsurface/screenshot_1.qoi | Bin 0 -> 8141 bytes .../tests/t0007_subsurface/screenshot_2.qoi | Bin 0 -> 7832 bytes src/macros.rs | 4 ++ src/object.rs | 2 + 17 files changed, 291 insertions(+), 9 deletions(-) create mode 100644 src/it/test_ifs/test_subcompositor.rs create mode 100644 src/it/test_ifs/test_subsurface.rs create mode 100644 src/it/test_utils/test_object_ext.rs create mode 100644 src/it/tests/t0007_subsurface.rs create mode 100644 src/it/tests/t0007_subsurface/screenshot_1.qoi create mode 100644 src/it/tests/t0007_subsurface/screenshot_2.qoi diff --git a/src/it.rs b/src/it.rs index b3853b66..6b0a44f0 100644 --- a/src/it.rs +++ b/src/it.rs @@ -1,5 +1,6 @@ use { crate::{ + async_engine::Phase, it::{ test_backend::TestBackend, test_config::{with_test_config, TestConfig}, @@ -132,12 +133,14 @@ fn run_test(it_run: &ItRun, test: &'static dyn TestCase, cfg: Rc) { backend, errors: Default::default(), server_addr, - dir: dir.clone(), + out_dir: dir.clone(), + in_dir: format!("{}/{}", env!("CARGO_MANIFEST_DIR"), test.dir()), cfg: cfg.clone(), }); let errors = errors2.clone(); Box::new(async move { let future: Pin<_> = test.run(testrun.clone()).into(); + let future = state.eng.spawn2(Phase::Present, future); let timeout = state.eng.timeout(5000).unwrap(); match future::select(future, timeout).await { Either::Left((Ok(..), _)) => {} diff --git a/src/it/test_client.rs b/src/it/test_client.rs index b1643643..132c42bd 100644 --- a/src/it/test_client.rs +++ b/src/it/test_client.rs @@ -7,7 +7,8 @@ use { test_error::TestError, test_ifs::{ test_compositor::TestCompositor, test_jay_compositor::TestJayCompositor, - test_registry::TestRegistry, test_shm::TestShm, test_xdg_base::TestXdgWmBase, + test_registry::TestRegistry, test_shm::TestShm, + test_subcompositor::TestSubcompositor, test_xdg_base::TestXdgWmBase, }, test_transport::TestTransport, test_utils::test_window::TestWindow, @@ -26,6 +27,7 @@ pub struct TestClient { pub registry: Rc, pub jc: Rc, pub comp: Rc, + pub sub: Rc, pub shm: Rc, pub xdg: Rc, } @@ -40,13 +42,35 @@ impl TestClient { self.tran.sync().await } - #[allow(dead_code)] pub async fn take_screenshot(&self) -> Result, TestError> { let dmabuf = self.jc.take_screenshot().await?; let qoi = buf_to_qoi(&dmabuf); Ok(qoi) } + #[allow(dead_code)] + pub async fn save_screenshot(&self, name: &str) -> Result<(), TestError> { + let qoi = self.take_screenshot().await?; + let path = format!("{}/screenshot_{}.qoi", self.run.out_dir, name); + std::fs::write(path, qoi)?; + Ok(()) + } + + pub async fn compare_screenshot(&self, name: &str) -> Result<(), TestError> { + let actual = self.take_screenshot().await?; + let expected_path = format!("{}/screenshot_{}.qoi", self.run.in_dir, name); + let expected = std::fs::read(expected_path)?; + if actual != expected { + let actual_out_path = format!("{}/screenshot_{}_actual.qoi", self.run.out_dir, name); + let expected_out_path = + format!("{}/screenshot_{}_expected.qoi", self.run.out_dir, name); + let _ = std::fs::write(actual_out_path, actual); + let _ = std::fs::write(expected_out_path, expected); + bail!("Screenshots differ"); + } + Ok(()) + } + pub async fn create_window(&self) -> Result, TestError> { let surface = self.comp.create_surface().await?; let shm = self.shm.create_pool(0)?; diff --git a/src/it/test_config.rs b/src/it/test_config.rs index 1c879fe5..bec6f24c 100644 --- a/src/it/test_config.rs +++ b/src/it/test_config.rs @@ -1,10 +1,13 @@ use { crate::{ifs::wl_seat::SeatId, it::test_error::TestError, utils::stack::Stack}, isnt::std_1::primitive::IsntConstPtrExt, - jay_config::_private::{ - bincode_ops, - ipc::{ClientMessage, Response, ServerMessage}, - ConfigEntry, VERSION, + jay_config::{ + _private::{ + bincode_ops, + ipc::{ClientMessage, Response, ServerMessage}, + ConfigEntry, VERSION, + }, + input::Seat, }, std::{cell::Cell, ops::Deref, ptr, rc::Rc}, }; @@ -149,6 +152,15 @@ impl TestConfig { Ok(SeatId::from_raw(seat.0 as _)) } + pub fn show_workspace(&self, seat: SeatId, name: &str) -> Result<(), TestError> { + let reply = self.send_with_reply(ClientMessage::GetWorkspace { name })?; + get_response!(reply, GetWorkspace { workspace }); + self.send(ClientMessage::ShowWorkspace { + seat: Seat(seat.raw() as _), + workspace, + }) + } + fn clear(&self) { unsafe { if let Some(srv) = self.srv.take() { diff --git a/src/it/test_ifs.rs b/src/it/test_ifs.rs index 1677b59a..f724d6b4 100644 --- a/src/it/test_ifs.rs +++ b/src/it/test_ifs.rs @@ -8,6 +8,8 @@ pub mod test_screenshot; pub mod test_shm; pub mod test_shm_buffer; pub mod test_shm_pool; +pub mod test_subcompositor; +pub mod test_subsurface; pub mod test_surface; pub mod test_xdg_base; pub mod test_xdg_surface; diff --git a/src/it/test_ifs/test_registry.rs b/src/it/test_ifs/test_registry.rs index 28410852..824d8dee 100644 --- a/src/it/test_ifs/test_registry.rs +++ b/src/it/test_ifs/test_registry.rs @@ -6,7 +6,8 @@ use { test_error::TestError, test_ifs::{ test_compositor::TestCompositor, test_jay_compositor::TestJayCompositor, - test_shm::TestShm, test_xdg_base::TestXdgWmBase, + test_shm::TestShm, test_subcompositor::TestSubcompositor, + test_xdg_base::TestXdgWmBase, }, test_object::TestObject, test_transport::TestTransport, @@ -27,6 +28,7 @@ pub struct TestGlobal { pub struct TestRegistrySingletons { pub jay_compositor: u32, pub wl_compositor: u32, + pub wl_subcompositor: u32, pub wl_shm: u32, pub xdg_wm_base: u32, } @@ -38,6 +40,7 @@ pub struct TestRegistry { pub singletons: CloneCell>>, pub jay_compositor: CloneCell>>, pub compositor: CloneCell>>, + pub subcompositor: CloneCell>>, pub shm: CloneCell>>, pub xdg: CloneCell>>, pub seats: CopyHashMap>, @@ -84,6 +87,7 @@ impl TestRegistry { let singletons = singleton! { jay_compositor, wl_compositor, + wl_subcompositor, wl_shm, xdg_wm_base, }; @@ -118,6 +122,20 @@ impl TestRegistry { Ok(jc) } + pub async fn get_subcompositor(&self) -> Result, TestError> { + singleton!(self.subcompositor); + let singletons = self.get_singletons().await?; + singleton!(self.subcompositor); + let jc = Rc::new(TestSubcompositor { + id: self.tran.id(), + tran: self.tran.clone(), + destroyed: Cell::new(false), + }); + self.bind(&jc, singletons.wl_subcompositor, 1)?; + self.subcompositor.set(Some(jc.clone())); + Ok(jc) + } + pub async fn get_shm(&self) -> Result, TestError> { singleton!(self.shm); let singletons = self.get_singletons().await?; diff --git a/src/it/test_ifs/test_subcompositor.rs b/src/it/test_ifs/test_subcompositor.rs new file mode 100644 index 00000000..7c18b730 --- /dev/null +++ b/src/it/test_ifs/test_subcompositor.rs @@ -0,0 +1,60 @@ +use { + crate::{ + it::{ + test_error::TestError, test_ifs::test_subsurface::TestSubsurface, + test_object::TestObject, test_transport::TestTransport, + }, + wire::{wl_subcompositor::*, WlSubcompositorId, WlSurfaceId}, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestSubcompositor { + pub id: WlSubcompositorId, + pub tran: Rc, + pub destroyed: Cell, +} + +impl TestSubcompositor { + pub fn destroy(&self) -> Result<(), TestError> { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id })?; + } + Ok(()) + } + + pub async fn get_subsurface( + &self, + surface: WlSurfaceId, + parent: WlSurfaceId, + ) -> Result, TestError> { + let id = self.tran.id(); + self.tran.send(GetSubsurface { + self_id: self.id, + id, + surface, + parent, + })?; + self.tran.sync().await; + let ss = Rc::new(TestSubsurface { + id, + tran: self.tran.clone(), + destroyed: Cell::new(false), + server: self.tran.get_object(id)?, + }); + self.tran.add_obj(ss.clone())?; + Ok(ss) + } +} + +impl Drop for TestSubcompositor { + fn drop(&mut self) { + let _ = self.destroy(); + } +} + +test_object! { + TestSubcompositor, WlSubcompositor; +} + +impl TestObject for TestSubcompositor {} diff --git a/src/it/test_ifs/test_subsurface.rs b/src/it/test_ifs/test_subsurface.rs new file mode 100644 index 00000000..6748b9e4 --- /dev/null +++ b/src/it/test_ifs/test_subsurface.rs @@ -0,0 +1,68 @@ +use { + crate::{ + ifs::wl_surface::wl_subsurface::WlSubsurface, + it::{test_error::TestError, test_object::TestObject, test_transport::TestTransport}, + wire::{wl_subsurface::*, WlSubsurfaceId, WlSurfaceId}, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestSubsurface { + pub id: WlSubsurfaceId, + pub tran: Rc, + pub destroyed: Cell, + pub server: Rc, +} + +impl TestSubsurface { + pub fn destroy(&self) -> Result<(), TestError> { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id })?; + } + Ok(()) + } + + pub fn set_position(&self, x: i32, y: i32) -> Result<(), TestError> { + self.tran.send(SetPosition { + self_id: self.id, + x, + y, + }) + } + + pub fn place_above(&self, surface: WlSurfaceId) -> Result<(), TestError> { + self.tran.send(PlaceAbove { + self_id: self.id, + sibling: surface, + }) + } + + pub fn place_below(&self, surface: WlSurfaceId) -> Result<(), TestError> { + self.tran.send(PlaceBelow { + self_id: self.id, + sibling: surface, + }) + } + + #[allow(dead_code)] + pub fn set_sync(&self) -> Result<(), TestError> { + self.tran.send(SetSync { self_id: self.id }) + } + + #[allow(dead_code)] + pub fn set_desync(&self) -> Result<(), TestError> { + self.tran.send(SetDesync { self_id: self.id }) + } +} + +impl Drop for TestSubsurface { + fn drop(&mut self) { + let _ = self.destroy(); + } +} + +test_object! { + TestSubsurface, WlSubsurface; +} + +impl TestObject for TestSubsurface {} diff --git a/src/it/test_transport.rs b/src/it/test_transport.rs index 719479d1..5b607394 100644 --- a/src/it/test_transport.rs +++ b/src/it/test_transport.rs @@ -6,6 +6,7 @@ use { test_error::{StdError, TestError}, test_ifs::{test_callback::TestCallback, test_registry::TestRegistry}, test_object::TestObject, + test_utils::test_object_ext::TestObjectExt, testrun::TestRun, }, object::{ObjectId, WL_DISPLAY_ID}, @@ -52,6 +53,7 @@ impl TestTransport { singletons: Default::default(), jay_compositor: Default::default(), compositor: Default::default(), + subcompositor: Default::default(), shm: Default::default(), xdg: Default::default(), seats: Default::default(), @@ -169,6 +171,11 @@ impl TestTransport { self.flush_request.trigger(); Ok(()) } + + pub fn get_object, T: 'static>(&self, id: I) -> Result, TestError> { + let client = self.get_client()?; + client.objects.get_obj(id.into())?.downcast() + } } struct Outgoing { diff --git a/src/it/test_utils.rs b/src/it/test_utils.rs index 4bd1bfc6..ced27f4e 100644 --- a/src/it/test_utils.rs +++ b/src/it/test_utils.rs @@ -1 +1,2 @@ +pub mod test_object_ext; pub mod test_window; diff --git a/src/it/test_utils/test_object_ext.rs b/src/it/test_utils/test_object_ext.rs new file mode 100644 index 00000000..7ef4ad1a --- /dev/null +++ b/src/it/test_utils/test_object_ext.rs @@ -0,0 +1,17 @@ +use { + crate::{it::test_error::TestError, object::Object}, + std::rc::Rc, +}; + +pub trait TestObjectExt { + fn downcast(self: Rc) -> Result, TestError>; +} + +impl TestObjectExt for dyn Object { + fn downcast(self: Rc) -> Result, TestError> { + match self.into_any().downcast() { + Ok(t) => Ok(t), + _ => bail!("Object has an incompatible type id"), + } + } +} diff --git a/src/it/testrun.rs b/src/it/testrun.rs index a9db48ea..4a19e12b 100644 --- a/src/it/testrun.rs +++ b/src/it/testrun.rs @@ -26,7 +26,8 @@ pub struct TestRun { pub backend: Rc, pub errors: Stack, pub server_addr: c::sockaddr_un, - pub dir: String, + pub out_dir: String, + pub in_dir: String, pub cfg: Rc, } @@ -85,6 +86,7 @@ impl TestRun { tran, jc, comp: registry.get_compositor().await?, + sub: registry.get_subcompositor().await?, shm: registry.get_shm().await?, xdg: registry.get_xdg().await?, registry, diff --git a/src/it/tests.rs b/src/it/tests.rs index 6b6aa99d..2e393ab0 100644 --- a/src/it/tests.rs +++ b/src/it/tests.rs @@ -12,6 +12,10 @@ macro_rules! testcase { module_path!().strip_prefix("jay::it::tests::").unwrap() } + fn dir(&self) -> &'static str { + file!().strip_suffix(".rs").unwrap() + } + fn run( &self, testrun: std::rc::Rc, @@ -28,9 +32,11 @@ mod t0003_multi_window; mod t0004_quit; mod t0005_create_seat; mod t0006_region; +mod t0007_subsurface; pub trait TestCase: Sync { fn name(&self) -> &'static str; + fn dir(&self) -> &'static str; fn run(&self, testrun: Rc) -> Box>>; } @@ -51,5 +57,6 @@ pub fn tests() -> Vec<&'static dyn TestCase> { t0004_quit, t0005_create_seat, t0006_region, + t0007_subsurface, } } diff --git a/src/it/tests/t0007_subsurface.rs b/src/it/tests/t0007_subsurface.rs new file mode 100644 index 00000000..3fbdbd19 --- /dev/null +++ b/src/it/tests/t0007_subsurface.rs @@ -0,0 +1,55 @@ +use { + crate::{ + format::ARGB8888, + it::{test_error::TestError, testrun::TestRun}, + theme::Color, + }, + std::rc::Rc, +}; + +testcase!(); + +async fn test(run: Rc) -> Result<(), TestError> { + run.backend.install_default(); + + let seat = run.get_seat("default")?; + + run.state.eng.yield_now().await; + + run.cfg.show_workspace(seat.id(), "")?; + + let client = run.create_client().await?; + + let parent = client.create_window().await?; + parent.map().await?; + parent.set_color(0, 0, 0, 255); + + let child = client.comp.create_surface().await?; + let sub = client + .sub + .get_subsurface(child.id, parent.surface.id) + .await?; + sub.set_position(100, 100)?; + + let pool = client.shm.create_pool(100 * 100 * 4)?; + let buffer = pool.create_buffer(0, 100, 100, 100 * 4, ARGB8888)?; + buffer.fill(Color::from_rgba_straight(255, 255, 255, 255)); + + child.attach(buffer.id)?; + + parent.map().await?; + + seat.set_app_cursor(None); + + client.compare_screenshot("1").await?; + + sub.place_below(parent.surface.id)?; + parent.map().await?; + client.compare_screenshot("2").await?; + + sub.place_above(parent.surface.id)?; + parent.map().await?; + client.compare_screenshot("1").await?; + + Ok(()) +} diff --git a/src/it/tests/t0007_subsurface/screenshot_1.qoi b/src/it/tests/t0007_subsurface/screenshot_1.qoi new file mode 100644 index 0000000000000000000000000000000000000000..eca5ddef82cd60dbbbd400fd5b1b711540368114 GIT binary patch literal 8141 zcmXTS&rD-rU{+vYV2WU7_@@zCe*PZ=1H)e=@KpS~DH8YZh~xh=Ha12MfN%XB$lPJZ zzG8&zp>HTrk1R9F9u0xf5Eu=C5flPHLjOKNE)OrLjq*ps1Caqn)5BHTrk1R9F9u0xf5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R n7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu;sydiLd0Rk8SVT}O5 literal 0 HcmV?d00001 diff --git a/src/macros.rs b/src/macros.rs index 57fb72c4..872c5efe 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -18,6 +18,10 @@ macro_rules! object_base { self.id.into() } + fn into_any(self: std::rc::Rc) -> std::rc::Rc { + self + } + #[allow(unused_variables, unreachable_code)] fn handle_request( self: std::rc::Rc, diff --git a/src/object.rs b/src/object.rs index a8037303..fd0ea698 100644 --- a/src/object.rs +++ b/src/object.rs @@ -1,6 +1,7 @@ use { crate::{client::ClientError, utils::buffd::MsgParser, wire::WlDisplayId}, std::{ + any::Any, fmt::{Display, Formatter}, rc::Rc, }, @@ -31,6 +32,7 @@ impl Display for ObjectId { pub trait ObjectBase { fn id(&self) -> ObjectId; + fn into_any(self: Rc) -> Rc; fn handle_request( self: Rc, request: u32,