1
0
Fork 0
forked from wry/wry

autocommit 2022-05-01 21:44:09 CEST

This commit is contained in:
Julian Orth 2022-05-01 21:44:09 +02:00
parent cca3850800
commit 04580c4aeb
38 changed files with 815 additions and 124 deletions

View file

@ -2,26 +2,139 @@ use {
crate::{
async_engine::SpawnedFuture,
backend::{
Backend, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, InputDevice,
InputDeviceAccelProfile, InputDeviceCapability, InputDeviceId, InputEvent,
TransformMatrix,
Backend, BackendEvent, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId,
InputDevice, InputDeviceAccelProfile, InputDeviceCapability, InputDeviceId, InputEvent,
Mode, MonitorInfo, TransformMatrix,
},
compositor::TestFuture,
render::{RenderContext, RenderError},
state::State,
utils::{clonecell::CloneCell, copyhashmap::CopyHashMap, syncqueue::SyncQueue},
utils::{
clonecell::CloneCell, copyhashmap::CopyHashMap, oserror::OsError, syncqueue::SyncQueue,
},
video::drm::{ConnectorType, Drm},
},
std::{any::Any, cell::Cell, error::Error, pin::Pin, rc::Rc},
bstr::ByteSlice,
std::{any::Any, cell::Cell, io, os::unix::ffi::OsStrExt, pin::Pin, rc::Rc},
thiserror::Error,
uapi::c,
};
#[derive(Debug, Error)]
pub enum TestBackendError {
#[error("Could not read /dev/dri")]
ReadDri(#[source] io::Error),
#[error("There are no drm nodes in /dev/dri")]
NoDrmNode,
#[error("Could not open drm node {0}")]
OpenDrmNode(String, #[source] OsError),
#[error("Could not create a render context")]
RenderContext(#[source] RenderError),
}
pub struct TestBackend {
pub state: Rc<State>,
pub test_future: TestFuture,
pub default_connector: Rc<TestConnector>,
}
impl TestBackend {
pub fn new(state: &Rc<State>, future: TestFuture) -> Self {
let connector = Rc::new(TestConnector {
id: state.connector_ids.next(),
kernel_id: ConnectorKernelId {
ty: ConnectorType::VGA,
idx: 1,
},
events: Default::default(),
on_change: Default::default(),
});
Self {
state: state.clone(),
test_future: future,
default_connector: connector,
}
}
pub fn install_default(&self) {
self.state
.backend_events
.push(BackendEvent::NewConnector(self.default_connector.clone()));
let mode = Mode {
width: 800,
height: 600,
refresh_rate_millihz: 60_000,
};
self.default_connector
.events
.push(ConnectorEvent::Connected(MonitorInfo {
modes: vec![mode],
manufacturer: "jay".to_string(),
product: "TestConnector".to_string(),
serial_number: self.default_connector.id.to_string(),
initial_mode: mode,
width_mm: 80,
height_mm: 60,
}));
}
fn create_render_context(&self) -> Result<(), TestBackendError> {
let dri = match std::fs::read_dir("/dev/dri") {
Ok(d) => d,
Err(e) => return Err(TestBackendError::ReadDri(e)),
};
let mut files = vec![];
for f in dri {
let f = match f {
Ok(f) => f,
Err(e) => return Err(TestBackendError::ReadDri(e)),
};
files.push(f.path());
}
let node = 'node: {
for f in &files {
if let Some(file) = f.file_name() {
if file.as_bytes().starts_with_str("renderD") {
break 'node f;
}
}
}
for f in &files {
if let Some(file) = f.file_name() {
if file.as_bytes().starts_with_str("card") {
break 'node f;
}
}
}
return Err(TestBackendError::NoDrmNode);
};
let file = match uapi::open(node.as_path(), c::O_RDWR | c::O_CLOEXEC, 0) {
Ok(f) => f,
Err(e) => {
return Err(TestBackendError::OpenDrmNode(
node.as_os_str().as_bytes().as_bstr().to_string(),
e.into(),
))
}
};
let drm = Drm::open_existing(file);
let ctx = match RenderContext::from_drm_device(&drm) {
Ok(ctx) => ctx,
Err(e) => return Err(TestBackendError::RenderContext(e)),
};
self.state.set_render_ctx(&Rc::new(ctx));
Ok(())
}
}
impl Backend for TestBackend {
fn run(self: Rc<Self>) -> SpawnedFuture<Result<(), Box<dyn Error>>> {
fn run(self: Rc<Self>) -> SpawnedFuture<Result<(), Box<dyn std::error::Error>>> {
let future = (self.test_future)(&self.state);
let slf = self.clone();
self.state.eng.spawn(async move {
if let Err(e) = slf.create_render_context() {
return Err(Box::new(e) as Box<_>);
}
let future: Pin<_> = future.into();
future.await;
Ok(())
@ -72,7 +185,7 @@ impl Connector for TestConnector {
}
fn damage(&self) {
todo!()
// nothing
}
}

View file

@ -1,10 +1,12 @@
use {
crate::{
cli::screenshot::buf_to_qoi,
client::Client,
it::{
test_error::TestError,
test_ifs::{
test_compositor::TestCompositor, test_jay_compositor::TestJayCompositor,
test_registry::TestRegistry, test_shm::TestShm,
test_registry::TestRegistry, test_shm::TestShm, test_xdg_base::TestXdgWmBase,
},
test_transport::TestTransport,
testrun::TestRun,
@ -16,19 +18,32 @@ use {
pub struct TestClient {
pub run: Rc<TestRun>,
pub server: Rc<Client>,
pub transport: Rc<TestTransport>,
pub tran: Rc<TestTransport>,
pub registry: Rc<TestRegistry>,
pub jc: Rc<TestJayCompositor>,
pub comp: Rc<TestCompositor>,
pub shm: Rc<TestShm>,
pub xdg: Rc<TestXdgWmBase>,
}
impl TestClient {
pub fn error(&self, msg: &str) {
self.transport.error(msg)
self.tran.error(msg)
}
pub async fn sync(self: &Rc<Self>) {
self.transport.sync().await
self.tran.sync().await
}
pub async fn take_screenshot(&self) -> Result<Vec<u8>, TestError> {
let dmabuf = self.jc.take_screenshot().await?;
let qoi = buf_to_qoi(&dmabuf);
Ok(qoi)
}
}
impl Drop for TestClient {
fn drop(&mut self) {
self.tran.kill();
}
}

View file

@ -3,6 +3,11 @@ pub mod test_compositor;
pub mod test_display;
pub mod test_jay_compositor;
pub mod test_registry;
pub mod test_screenshot;
pub mod test_shm;
pub mod test_shm_buffer;
pub mod test_shm_pool;
pub mod test_surface;
pub mod test_xdg_base;
pub mod test_xdg_surface;
pub mod test_xdg_toplevel;

View file

@ -12,7 +12,7 @@ use {
pub struct TestCallback {
pub id: WlCallbackId,
pub transport: Rc<TestTransport>,
pub tran: Rc<TestTransport>,
pub handler: Cell<Option<Box<dyn FnOnce()>>>,
pub done: Cell<bool>,
}

View file

@ -1,14 +1,38 @@
use {
crate::{
it::{test_object::TestObject, test_transport::TestTransport},
wire::WlCompositorId,
it::{
test_error::TestError, test_ifs::test_surface::TestSurface, test_object::TestObject,
test_transport::TestTransport,
},
wire::{wl_compositor::CreateSurface, WlCompositorId},
},
std::rc::Rc,
std::{cell::Cell, rc::Rc},
};
pub struct TestCompositor {
pub id: WlCompositorId,
pub transport: Rc<TestTransport>,
pub tran: Rc<TestTransport>,
}
impl TestCompositor {
pub async fn create_surface(&self) -> Result<Rc<TestSurface>, TestError> {
let id = self.tran.id();
self.tran.send(CreateSurface {
self_id: self.id,
id,
});
self.tran.sync().await;
let client = self.tran.get_client()?;
let server = client.lookup(id)?;
let surface = Rc::new(TestSurface {
id,
tran: self.tran.clone(),
server,
destroyed: Cell::new(false),
});
self.tran.add_obj(surface.clone())?;
Ok(surface)
}
}
test_object! {

View file

@ -12,7 +12,7 @@ use {
};
pub struct TestDisplay {
pub transport: Rc<TestTransport>,
pub tran: Rc<TestTransport>,
pub id: WlDisplayId,
}
@ -20,25 +20,25 @@ impl TestDisplay {
fn handle_error(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> {
let ev = Error::parse_full(parser)?;
let msg = format!("Compositor sent an error: {}", ev.message);
self.transport.error(&msg);
self.transport.kill();
self.tran.error(&msg);
self.tran.kill();
Ok(())
}
fn handle_delete_id(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> {
let ev = DeleteId::parse_full(parser)?;
match self.transport.objects.remove(&ObjectId::from_raw(ev.id)) {
match self.tran.objects.remove(&ObjectId::from_raw(ev.id)) {
None => {
let msg = format!(
"Compositor sent delete_id for object {} which does not exist",
ev.id
);
self.transport.error(&msg);
self.transport.kill();
self.tran.error(&msg);
self.tran.kill();
}
Some(obj) => {
obj.on_remove(&self.transport);
self.transport.obj_ids.borrow_mut().release(ev.id);
obj.on_remove(&self.tran);
self.tran.obj_ids.borrow_mut().release(ev.id);
}
}
Ok(())

View file

@ -2,12 +2,13 @@ use {
crate::{
client::ClientId,
it::{
test_error::TestError, test_object::TestObject, test_transport::TestTransport,
testrun::ParseFull,
test_error::TestError, test_ifs::test_screenshot::TestJayScreenshot,
test_object::TestObject, test_transport::TestTransport, testrun::ParseFull,
},
utils::buffd::MsgParser,
wire::{
jay_compositor::{self, *},
jay_screenshot::Dmabuf,
JayCompositorId,
},
},
@ -16,25 +17,44 @@ use {
pub struct TestJayCompositor {
pub id: JayCompositorId,
pub transport: Rc<TestTransport>,
pub tran: Rc<TestTransport>,
pub client_id: Cell<Option<ClientId>>,
}
impl TestJayCompositor {
pub async fn get_client_id(&self) -> Result<ClientId, TestError> {
if self.client_id.get().is_none() {
self.transport.send(GetClientId { self_id: self.id });
self.tran.send(GetClientId { self_id: self.id });
}
self.transport.sync().await;
self.tran.sync().await;
match self.client_id.get() {
Some(c) => Ok(c),
_ => bail!("Compositor did not send a client id"),
}
}
pub async fn take_screenshot(&self) -> Result<Dmabuf, TestError> {
let js = Rc::new(TestJayScreenshot {
id: self.tran.id(),
result: Cell::new(None),
});
self.tran.send(TakeScreenshot {
self_id: self.id,
id: js.id,
});
self.tran.add_obj(js.clone())?;
self.tran.sync().await;
match js.result.take() {
Some(Ok(res)) => Ok(res),
Some(Err(res)) => bail!("Compositor could not take a screenshot: {}", res),
None => bail!("Compositor did not send a screenshot"),
}
}
fn handle_client_id(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> {
let ev = jay_compositor::ClientId::parse_full(parser)?;
self.client_id.set(Some(ClientId::from_raw(ev.client_id)));
self.tran.client_id.set(ClientId::from_raw(ev.client_id));
Ok(())
}
}

View file

@ -4,7 +4,7 @@ use {
test_error::TestError,
test_ifs::{
test_compositor::TestCompositor, test_jay_compositor::TestJayCompositor,
test_shm::TestShm,
test_shm::TestShm, test_xdg_base::TestXdgWmBase,
},
test_object::TestObject,
test_transport::TestTransport,
@ -26,16 +26,18 @@ pub struct TestRegistrySingletons {
pub jay_compositor: u32,
pub wl_compositor: u32,
pub wl_shm: u32,
pub xdg_wm_base: u32,
}
pub struct TestRegistry {
pub id: WlRegistryId,
pub transport: Rc<TestTransport>,
pub tran: Rc<TestTransport>,
pub globals: CopyHashMap<u32, Rc<TestGlobal>>,
pub singletons: CloneCell<Option<Rc<TestRegistrySingletons>>>,
pub jay_compositor: CloneCell<Option<Rc<TestJayCompositor>>>,
pub compositor: CloneCell<Option<Rc<TestCompositor>>>,
pub shm: CloneCell<Option<Rc<TestShm>>>,
pub xdg: CloneCell<Option<Rc<TestXdgWmBase>>>,
}
macro_rules! singleton {
@ -49,22 +51,22 @@ macro_rules! singleton {
impl TestRegistry {
pub async fn get_singletons(&self) -> Result<Rc<TestRegistrySingletons>, TestError> {
singleton!(self.singletons);
self.transport.sync().await;
self.tran.sync().await;
singleton!(self.singletons);
let mut jay_compositor = 0;
let mut wl_compositor = 0;
let mut wl_shm = 0;
for global in self.globals.lock().values() {
match global.interface.as_str() {
"jay_compositor" => jay_compositor = global.name,
"wl_compositor" => wl_compositor = global.name,
"wl_shm" => wl_shm = global.name,
_ => {}
}
}
macro_rules! singleton {
($($name:ident,)*) => {
TestRegistrySingletons {
($($name:ident,)*) => {{
$(
let mut $name = 0;
)*
for global in self.globals.lock().values() {
match global.interface.as_str() {
$(
stringify!($name) => $name = global.name,
)*
_ => {}
}
}
Rc::new(TestRegistrySingletons {
$(
$name: {
if $name == 0 {
@ -73,14 +75,15 @@ impl TestRegistry {
$name
},
)*
}
}
})
}}
}
let singletons = Rc::new(singleton! {
let singletons = singleton! {
jay_compositor,
wl_compositor,
wl_shm,
});
xdg_wm_base,
};
self.singletons.set(Some(singletons.clone()));
Ok(singletons)
}
@ -90,8 +93,8 @@ impl TestRegistry {
let singletons = self.get_singletons().await?;
singleton!(self.jay_compositor);
let jc = Rc::new(TestJayCompositor {
id: self.transport.id(),
transport: self.transport.clone(),
id: self.tran.id(),
tran: self.tran.clone(),
client_id: Default::default(),
});
self.bind(&jc, singletons.jay_compositor, 1)?;
@ -104,8 +107,8 @@ impl TestRegistry {
let singletons = self.get_singletons().await?;
singleton!(self.compositor);
let jc = Rc::new(TestCompositor {
id: self.transport.id(),
transport: self.transport.clone(),
id: self.tran.id(),
tran: self.tran.clone(),
});
self.bind(&jc, singletons.wl_compositor, 4)?;
self.compositor.set(Some(jc.clone()));
@ -117,8 +120,8 @@ impl TestRegistry {
let singletons = self.get_singletons().await?;
singleton!(self.shm);
let jc = Rc::new(TestShm {
id: self.transport.id(),
transport: self.transport.clone(),
id: self.tran.id(),
tran: self.tran.clone(),
formats: Default::default(),
formats_awaited: Cell::new(false),
});
@ -127,20 +130,34 @@ impl TestRegistry {
Ok(jc)
}
pub async fn get_xdg(&self) -> Result<Rc<TestXdgWmBase>, TestError> {
singleton!(self.xdg);
let singletons = self.get_singletons().await?;
singleton!(self.xdg);
let jc = Rc::new(TestXdgWmBase {
id: self.tran.id(),
tran: self.tran.clone(),
destroyed: Cell::new(false),
});
self.bind(&jc, singletons.xdg_wm_base, 3)?;
self.xdg.set(Some(jc.clone()));
Ok(jc)
}
pub fn bind<O: TestObject>(
&self,
obj: &Rc<O>,
name: u32,
version: u32,
) -> Result<(), TestError> {
self.transport.send(Bind {
self.tran.send(Bind {
self_id: self.id,
name,
interface: obj.interface().name(),
version,
id: obj.id().into(),
});
self.transport.add_obj(obj.clone())?;
self.tran.add_obj(obj.clone())?;
Ok(())
}
@ -155,7 +172,7 @@ impl TestRegistry {
}),
);
if prev.is_some() {
self.transport.error(&format!(
self.tran.error(&format!(
"Compositor sent global {} multiple times",
ev.name
));
@ -166,7 +183,7 @@ impl TestRegistry {
fn handle_global_remove(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> {
let ev = GlobalRemove::parse_full(parser)?;
if self.globals.remove(&ev.name).is_none() {
self.transport.error(&format!(
self.tran.error(&format!(
"Compositor sent global_remove for {} which does not exist",
ev.name
));

View file

@ -0,0 +1,36 @@
use {
crate::{
it::{test_error::TestError, test_object::TestObject, testrun::ParseFull},
utils::buffd::MsgParser,
wire::{jay_screenshot::*, JayScreenshotId},
},
std::cell::Cell,
};
pub struct TestJayScreenshot {
pub id: JayScreenshotId,
pub result: Cell<Option<Result<Dmabuf, String>>>,
}
impl TestJayScreenshot {
fn handle_dmabuf(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> {
let ev = Dmabuf::parse_full(parser)?;
self.result.set(Some(Ok(ev)));
Ok(())
}
fn handle_error(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> {
let ev = Error::parse_full(parser)?;
self.result.set(Some(Err(ev.msg.to_string())));
Ok(())
}
}
test_object! {
TestJayScreenshot, JayScreenshot;
DMABUF => handle_dmabuf,
ERROR => handle_error,
}
impl TestObject for TestJayScreenshot {}

View file

@ -12,7 +12,7 @@ use {
pub struct TestShm {
pub id: WlShmId,
pub transport: Rc<TestTransport>,
pub tran: Rc<TestTransport>,
pub formats: CopyHashMap<u32, ()>,
pub formats_awaited: Cell<bool>,
}
@ -20,7 +20,7 @@ pub struct TestShm {
impl TestShm {
pub async fn formats(&self) -> &CopyHashMap<u32, ()> {
if !self.formats_awaited.replace(true) {
self.transport.sync().await;
self.tran.sync().await;
}
&self.formats
}
@ -28,18 +28,18 @@ impl TestShm {
pub fn create_pool(&self, size: usize) -> Result<Rc<TestShmPool>, TestError> {
let mem = TestMem::new(size)?;
let pool = Rc::new(TestShmPool {
id: self.transport.id(),
transport: self.transport.clone(),
id: self.tran.id(),
tran: self.tran.clone(),
mem: CloneCell::new(mem.clone()),
destroyed: Cell::new(false),
});
self.transport.send(CreatePool {
self.tran.send(CreatePool {
self_id: self.id,
id: pool.id,
fd: mem.fd.clone(),
size: size as _,
});
self.transport.add_obj(pool.clone())?;
self.tran.add_obj(pool.clone())?;
Ok(pool)
}

View file

@ -4,7 +4,8 @@ use {
test_error::TestError, test_mem::TestMem, test_object::TestObject,
test_transport::TestTransport, testrun::ParseFull,
},
utils::buffd::MsgParser,
theme::Color,
utils::{buffd::MsgParser, windows::WindowsExt},
wire::{wl_buffer::*, WlBufferId},
},
std::{
@ -16,7 +17,7 @@ use {
pub struct TestShmBuffer {
pub id: WlBufferId,
pub transport: Rc<TestTransport>,
pub tran: Rc<TestTransport>,
pub range: Range<usize>,
pub mem: Rc<TestMem>,
pub released: Cell<bool>,
@ -24,11 +25,21 @@ pub struct TestShmBuffer {
}
impl TestShmBuffer {
pub fn fill(&self, color: Color) {
let [cr, cg, cb, ca] = color.to_rgba_premultiplied();
for [b, g, r, a] in self.deref().array_chunks_ext::<4>() {
r.set(cr);
g.set(cg);
b.set(cb);
a.set(ca);
}
}
pub fn destroy(&self) {
if self.destroyed.replace(true) {
return;
}
self.transport.send(Destroy { self_id: self.id });
self.tran.send(Destroy { self_id: self.id });
}
fn handle_release(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> {

View file

@ -13,7 +13,7 @@ use {
pub struct TestShmPool {
pub id: WlShmPoolId,
pub transport: Rc<TestTransport>,
pub tran: Rc<TestTransport>,
pub mem: CloneCell<Rc<TestMem>>,
pub destroyed: Cell<bool>,
}
@ -35,15 +35,15 @@ impl TestShmPool {
bail!("Out-of-bounds buffer");
}
let buffer = Rc::new(TestShmBuffer {
id: self.transport.id(),
transport: self.transport.clone(),
id: self.tran.id(),
tran: self.tran.clone(),
range: start..end,
mem,
released: Cell::new(true),
destroyed: Cell::new(false),
});
self.transport.add_obj(buffer.clone())?;
self.transport.send(CreateBuffer {
self.tran.add_obj(buffer.clone())?;
self.tran.send(CreateBuffer {
self_id: self.id,
id: buffer.id,
offset,
@ -58,7 +58,7 @@ impl TestShmPool {
pub fn resize(&self, size: usize) -> Result<(), TestError> {
let mem = self.mem.get().grow(size)?;
self.mem.set(mem);
self.transport.send(Resize {
self.tran.send(Resize {
self_id: self.id,
size: size as _,
});
@ -69,7 +69,7 @@ impl TestShmPool {
if self.destroyed.replace(true) {
return;
}
self.transport.send(Destroy { self_id: self.id });
self.tran.send(Destroy { self_id: self.id });
}
}

View file

@ -0,0 +1,65 @@
use {
crate::{
ifs::wl_surface::WlSurface,
it::{
test_error::TestError, test_object::TestObject, test_transport::TestTransport,
testrun::ParseFull,
},
utils::buffd::MsgParser,
wire::{wl_surface::*, WlBufferId, WlSurfaceId},
},
std::{cell::Cell, rc::Rc},
};
pub struct TestSurface {
pub id: WlSurfaceId,
pub tran: Rc<TestTransport>,
pub server: Rc<WlSurface>,
pub destroyed: Cell<bool>,
}
impl TestSurface {
pub fn destroy(&self) {
if !self.destroyed.replace(true) {
self.tran.send(Destroy { self_id: self.id });
}
}
pub fn attach(&self, buffer_id: WlBufferId) {
self.tran.send(Attach {
self_id: self.id,
buffer: buffer_id,
x: 0,
y: 0,
});
}
pub fn commit(&self) {
self.tran.send(Commit { self_id: self.id });
}
fn handle_enter(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> {
let _ev = Enter::parse_full(parser)?;
Ok(())
}
fn handle_leave(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> {
let _ev = Leave::parse_full(parser)?;
Ok(())
}
}
impl Drop for TestSurface {
fn drop(&mut self) {
self.destroy();
}
}
test_object! {
TestSurface, WlSurface;
ENTER => handle_enter,
LEAVE => handle_leave,
}
impl TestObject for TestSurface {}

View file

@ -0,0 +1,68 @@
use {
crate::{
it::{
test_error::TestError, test_ifs::test_xdg_surface::TestXdgSurface,
test_object::TestObject, test_transport::TestTransport, testrun::ParseFull,
},
utils::buffd::MsgParser,
wire::{xdg_wm_base::*, WlSurfaceId, XdgWmBaseId},
},
std::{cell::Cell, rc::Rc},
};
pub struct TestXdgWmBase {
pub id: XdgWmBaseId,
pub tran: Rc<TestTransport>,
pub destroyed: Cell<bool>,
}
impl TestXdgWmBase {
pub fn destroy(&self) {
if !self.destroyed.replace(true) {
self.tran.send(Destroy { self_id: self.id });
}
}
pub async fn create_xdg_surface(
&self,
surface: WlSurfaceId,
) -> Result<Rc<TestXdgSurface>, TestError> {
let id = self.tran.id();
self.tran.send(GetXdgSurface {
self_id: self.id,
id,
surface,
});
self.tran.sync().await;
let client = self.tran.get_client()?;
let server = client.lookup(id)?;
let xdg = Rc::new(TestXdgSurface {
id,
tran: self.tran.clone(),
server,
destroyed: Cell::new(false),
last_serial: Cell::new(0),
});
self.tran.add_obj(xdg.clone())?;
Ok(xdg)
}
fn handle_ping(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> {
let _ev = Ping::parse_full(parser)?;
Ok(())
}
}
test_object! {
TestXdgWmBase, XdgWmBase;
PING => handle_ping,
}
impl TestObject for TestXdgWmBase {}
impl Drop for TestXdgWmBase {
fn drop(&mut self) {
self.destroy();
}
}

View file

@ -0,0 +1,78 @@
use {
crate::{
ifs::wl_surface::xdg_surface::XdgSurface,
it::{
test_error::TestError, test_ifs::test_xdg_toplevel::TestXdgToplevel,
test_object::TestObject, test_transport::TestTransport, testrun::ParseFull,
},
utils::buffd::MsgParser,
wire::{xdg_surface::*, XdgSurfaceId},
},
std::{cell::Cell, rc::Rc},
};
pub struct TestXdgSurface {
pub id: XdgSurfaceId,
pub tran: Rc<TestTransport>,
pub server: Rc<XdgSurface>,
pub destroyed: Cell<bool>,
pub last_serial: Cell<u32>,
}
impl TestXdgSurface {
pub fn destroy(&self) {
if !self.destroyed.replace(true) {
self.tran.send(Destroy { self_id: self.id });
}
}
pub async fn create_toplevel(&self) -> Result<Rc<TestXdgToplevel>, TestError> {
let id = self.tran.id();
self.tran.send(GetToplevel {
self_id: self.id,
id,
});
self.tran.sync().await;
let client = self.tran.get_client()?;
let server = client.lookup(id)?;
let tl = Rc::new(TestXdgToplevel {
id,
tran: self.tran.clone(),
destroyed: Cell::new(false),
server,
width: Cell::new(0),
height: Cell::new(0),
states: Default::default(),
close_requested: Cell::new(false),
});
self.tran.add_obj(tl.clone())?;
Ok(tl)
}
pub fn ack_configure(&self, serial: u32) {
self.tran.send(AckConfigure {
self_id: self.id,
serial,
});
}
fn handle_configure(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> {
let ev = Configure::parse_full(parser)?;
self.last_serial.set(ev.serial);
Ok(())
}
}
impl Drop for TestXdgSurface {
fn drop(&mut self) {
self.destroy();
}
}
test_object! {
TestXdgSurface, XdgSurface;
CONFIGURE => handle_configure,
}
impl TestObject for TestXdgSurface {}

View file

@ -0,0 +1,72 @@
use {
crate::{
ifs::wl_surface::xdg_surface::xdg_toplevel::XdgToplevel,
it::{
test_error::TestError, test_object::TestObject, test_transport::TestTransport,
testrun::ParseFull,
},
utils::buffd::MsgParser,
wire::{xdg_toplevel::*, XdgToplevelId},
},
ahash::AHashSet,
std::{
cell::{Cell, RefCell},
rc::Rc,
},
};
pub struct TestXdgToplevel {
pub id: XdgToplevelId,
pub tran: Rc<TestTransport>,
pub destroyed: Cell<bool>,
pub server: Rc<XdgToplevel>,
pub width: Cell<i32>,
pub height: Cell<i32>,
pub states: RefCell<AHashSet<u32>>,
pub close_requested: Cell<bool>,
}
impl TestXdgToplevel {
pub fn destroy(&self) {
if !self.destroyed.replace(true) {
self.tran.send(Destroy { self_id: self.id });
}
}
fn handle_configure(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> {
let ev = Configure::parse_full(parser)?;
self.width.set(ev.width);
self.height.set(ev.height);
*self.states.borrow_mut() = ev.states.iter().copied().collect();
Ok(())
}
fn handle_close(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> {
let _ev = Close::parse_full(parser)?;
self.close_requested.set(true);
Ok(())
}
fn handle_configure_bounds(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> {
let _ev = ConfigureBounds::parse_full(parser)?;
Ok(())
}
}
impl Drop for TestXdgToplevel {
fn drop(&mut self) {
self.destroy();
}
}
test_object! {
TestXdgToplevel, XdgToplevel;
CONFIGURE => handle_configure,
CLOSE => handle_close,
CONFIGURE_BOUNDS => handle_configure_bounds,
}
impl TestObject for TestXdgToplevel {}

View file

@ -46,6 +46,9 @@ impl Deref for TestMem {
}
fn map(fd: c::c_int, size: usize) -> Result<*const [Cell<u8>], TestError> {
if size == 0 {
return Ok(&[]);
}
unsafe {
let res = c::mmap(
ptr::null_mut(),

View file

@ -25,7 +25,7 @@ macro_rules! test_object {
$(
$code => $oname::$f(&self, parser).with_context(|| format!("While handling a `{}` event", stringify!($f))),
)*
_ => Err(crate::it::test_error::TestError::new("Unknown event {}")),
_ => Err(crate::it::test_error::TestError::new(format!("Unknown event {}", request))),
};
res.with_context(|| format!("In object {} of type `{}`", self.id(), self.interface().name()))
}

View file

@ -1,7 +1,7 @@
use {
crate::{
async_engine::{AsyncFd, SpawnedFuture},
client::{ClientId, EventFormatter},
client::{Client, ClientId, EventFormatter},
it::{
test_error::{StdError, TestError},
test_ifs::{test_callback::TestCallback, test_registry::TestRegistry},
@ -47,12 +47,13 @@ impl TestTransport {
pub fn get_registry(self: &Rc<Self>) -> Rc<TestRegistry> {
let reg = Rc::new(TestRegistry {
id: self.id(),
transport: self.clone(),
tran: self.clone(),
globals: Default::default(),
singletons: Default::default(),
jay_compositor: Default::default(),
compositor: Default::default(),
shm: Default::default(),
xdg: Default::default(),
});
self.send(wl_display::GetRegistry {
self_id: WL_DISPLAY_ID,
@ -62,6 +63,14 @@ impl TestTransport {
reg
}
pub fn get_client(&self) -> Result<Rc<Client>, TestError> {
self.run
.state
.clients
.get(self.client_id.get())
.map_err(|e| e.into())
}
pub fn add_obj(&self, obj: Rc<dyn TestObject>) -> Result<(), TestError> {
if self.killed.get() {
bail!("Transport has already been killed");
@ -84,7 +93,7 @@ impl TestTransport {
pub fn sync(self: &Rc<Self>) -> impl Future<Output = ()> {
let cb = Rc::new(TestCallback {
id: self.id(),
transport: self.clone(),
tran: self.clone(),
handler: Cell::new(None),
done: Cell::new(self.killed.get()),
});

View file

@ -24,6 +24,7 @@ pub struct TestRun {
pub backend: Rc<TestBackend>,
pub errors: Stack<String>,
pub server_addr: c::sockaddr_un,
pub dir: String,
}
impl TestRun {
@ -53,7 +54,7 @@ impl TestRun {
let mut obj_ids = Bitfield::default();
obj_ids.take(0);
obj_ids.take(1);
let transport = Rc::new(TestTransport {
let tran = Rc::new(TestTransport {
run: self.clone(),
fd,
client_id: Cell::new(ClientId::from_raw(0)),
@ -66,22 +67,23 @@ impl TestRun {
obj_ids: RefCell::new(obj_ids),
killed: Cell::new(false),
});
transport.add_obj(Rc::new(TestDisplay {
transport: transport.clone(),
tran.add_obj(Rc::new(TestDisplay {
tran: tran.clone(),
id: WL_DISPLAY_ID,
}))?;
transport.init();
let registry = transport.get_registry();
tran.init();
let registry = tran.get_registry();
let jc = registry.get_jay_compositor().await?;
let client_id = jc.get_client_id().await?;
let client = self.state.clients.get(client_id)?;
Ok(Rc::new(TestClient {
run: self.clone(),
server: client,
transport,
tran,
jc,
comp: registry.get_compositor().await?,
shm: registry.get_shm().await?,
xdg: registry.get_xdg().await?,
registry,
}))
}

View file

@ -35,7 +35,26 @@ macro_rules! tassert {
};
}
macro_rules! tassert_eq {
($left:expr, $right:expr) => {{
let left = $left;
let right = $right;
if left != right {
bail!(
"Assert `{} = {:?} = {:?} = {}` failed ({}:{})",
stringify!($left),
left,
right,
stringify!($right),
file!(),
line!()
);
}
}};
}
mod t0001_shm_formats;
mod t0002_window;
pub trait TestCase {
fn name(&self) -> &'static str;
@ -43,5 +62,5 @@ pub trait TestCase {
}
pub fn tests() -> Vec<&'static dyn TestCase> {
vec![&t0001_shm_formats::Test]
vec![&t0001_shm_formats::Test, &t0002_window::Test]
}

View file

@ -0,0 +1,56 @@
use {
crate::{
format::ARGB8888,
it::{test_error::TestError, testrun::TestRun},
theme::Color,
},
std::rc::Rc,
};
testcase!();
/// Create and map a single surface
async fn test(run: Rc<TestRun>) -> Result<(), TestError> {
run.backend.install_default();
let client = run.create_client().await?;
let surface = client.comp.create_surface().await?;
let xdg_surface = client.xdg.create_xdg_surface(surface.id).await?;
let xdg_toplevel = xdg_surface.create_toplevel().await?;
surface.commit();
client.sync().await;
{
let pool = client.shm.create_pool(0)?;
let buffer = pool.create_buffer(0, 0, 0, 0, ARGB8888)?;
xdg_surface.ack_configure(xdg_surface.last_serial.get());
surface.attach(buffer.id);
surface.commit();
client.sync().await;
}
tassert_eq!(xdg_toplevel.width.get(), 800);
tassert!(xdg_toplevel.height.get() >= 500);
{
let pool = client
.shm
.create_pool((xdg_toplevel.width.get() * xdg_toplevel.height.get() * 4) as _)?;
let buffer = pool.create_buffer(
0,
xdg_toplevel.width.get(),
xdg_toplevel.height.get(),
xdg_toplevel.width.get() * 4,
ARGB8888,
)?;
buffer.fill(Color::from_rgba_straight(255, 0, 0, 100));
xdg_surface.ack_configure(xdg_surface.last_serial.get());
surface.attach(buffer.id);
surface.commit();
}
let screenshot = client.take_screenshot().await?;
std::fs::write(format!("{}/screenshot.qoi", run.dir), screenshot)?;
Ok(())
}