From: Ian Jackson Date: Mon, 15 Feb 2021 00:16:17 +0000 (+0000) Subject: hand: initial wdt, passes X-Git-Tag: otter-0.4.0~448 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=2b158d988cf0577e6447e48e68befe23de5363f6;p=otter.git hand: initial wdt, passes Signed-off-by: Ian Jackson --- diff --git a/wdriver.rs b/wdriver.rs index f5292e66..7179cc28 100644 --- a/wdriver.rs +++ b/wdriver.rs @@ -47,12 +47,14 @@ pub use otter::ensure_eq; pub use otter::commands::{MgmtCommand, MgmtResponse}; pub use otter::commands::{MgmtGameInstruction, MgmtGameResponse}; pub use otter::commands::{MgmtGameUpdateMode}; -pub use otter::gamestate::{self, Generation}; +pub use otter::gamestate::{self, Generation, PlayerId}; pub use otter::global::InstanceName; pub use otter::mgmtchannel::MgmtChannel; +pub use otter::slotmap_slot_idx::KeyDataExt; pub use otter::spec::{Coord, GameSpec, Pos, PosC}; pub use otter::toml_de; pub use otter::ui::{AbbrevPresentationLayout, PresentationLayout}; +pub use otter::ui::player_num_dasharray; pub type T4d = t4::WebDriver; pub type WDE = t4::error::WebDriverError; diff --git a/wdriver/wdt-hand.rs b/wdriver/wdt-hand.rs new file mode 100644 index 00000000..b9061a59 --- /dev/null +++ b/wdriver/wdt-hand.rs @@ -0,0 +1,320 @@ +// Copyright 2020-2021 Ian Jackson and contributors to Otter +// SPDX-License-Identifier: AGPL-3.0-or-later +// There is NO WARRANTY. + +use otter_webdriver_tests::*; + +struct Ctx { + su: Setup, + alice: Window, + bob: Window, +} +ctx_with_setup!{Ctx} + +impl Ctx { + #[throws(AE)] + fn claim(&mut self){ + let su = &mut self.su; + const HAND: &str = "6.1"; + const ALICE: &str = "1#1"; + + let chk = |w: &WindowGuard<'_>, pc: &str, player: &'static str| { + let player: PlayerId = player.try_into().context(player)?; + let player: slotmap::KeyData = player.into(); + let (player,_) = player.get_idx_version(); + let player: usize = player.try_into().unwrap(); + let player = player.try_into().unwrap(); + let dasharray = player_num_dasharray(player).0; + + let euse = w.find_element(By::Id(&format!("piece{}", pc)))?; + let epath = euse.find_element(By::Tag("path"))?; + let attr = epath.get_attribute("stroke-dasharray")?; + + ensure_eq!(attr, Some(dasharray)); + Ok::<_,AE>(()) + }; + + { + let mut w = su.w(&self.alice)?; + w.synch()?; + + let hand = w.find_piece(HAND)?; + w.action_chain() + .move_pos(&hand)? + .click() + .perform() + .context("select hand")?; + w.synch()?; + + w.action_chain() + .key_down('C') + .key_up('C') + .perform() + .context("claim hand")?; + + w.synch()?; + + chk(&w, HAND, ALICE)?; + } + + { + let mut w = su.w(&self.bob)?; + w.synch()?; + + let _hand = w.find_piece(HAND)?; + + chk(&w, HAND, ALICE)?; + } + } +/* + #[throws(AE)] + fn rotate(&mut self) -> &'static str { + let pc = "4.1"; + let su = &mut self.su; + + let chk = |w: &WindowGuard<'_>| { + let transform = format!("rotate(-90)"); + let pd = w.find_element(By::Id(&format!("piece{}",pc)))?; + ensure_eq!(pd.get_attribute("transform")?, Some(transform)); + Ok::<_,AE>(()) + }; + + { + let mut w = su.w(&self.alice)?; + let p = w.find_piece(pc)?; + w.action_chain() + .move_pos(&p)? + .click() + .release() + .key_down('l') + .key_up('l') + .perform() + .always_context("rotate")?; + + chk(&w)?; + w.synch()?; + } + + { + let mut w = su.w(&self.bob)?; + w.synch()?; + chk(&w)?; + } + + pc + } + + #[throws(AE)] + fn drag_off(&mut self, pc: &'static str) { + let su = &mut self.su; + + let chk = |w: &WindowGuard<'_>, exp_end| { + let got_end = w.find_piece(pc)?.posg()?; + ensure_eq!(got_end, exp_end); + Ok::<_,AE>(()) + }; + + let table_size = self.spec.table_size + .ok_or(anyhow!("table size missing from spec"))?; + + let exp_end = { + let mut w = su.w(&self.alice)?; + let p = w.find_piece(pc)?; + let start = p.posg()?; + let end = |d| { let mut e = start; e.0[1] = table_size.0[1] + d; e }; + let try_end = end(10); + let exp_end = end(0); + w.action_chain() + .move_w(&w, start)? + .click_and_hold() + .move_w(&w, try_end)? + .release() + .perform() + .always_context("drag off")?; + + w.synch()?; + chk(&w, exp_end)?; + + exp_end + }; + + { + let mut w = su.w(&self.bob)?; + w.synch()?; + chk(&w, exp_end)?; + } + + pc + } + + #[throws(AE)] + fn unselect(&mut self, pc: &'static str) { + let su = &mut self.su; + + let chk = |w: &WindowGuard<'_>| { + let held = w.piece_held(pc)?; + ensure_eq!(held, None); + Ok::<_,AE>(()) + }; + + { + let mut w = su.w(&self.alice)?; + w.action_chain() + .move_w(&w, PosC([10,10]))? + .click() + .release() + .perform() + .always_context("unselect by clicking elsewhere")?; + + chk(&w)?; + w.synch()?; + } + + { + let mut w = su.w(&self.bob)?; + w.synch()?; + chk(&w)?; + } + } + + #[throws(AE)] + fn conflict(&mut self) { + let pc = "1.1"; + let su = &mut self.su; + + { + let pc = "4.1"; + let w = su.w(&self.alice)?; + w.action_chain() + .move_pc(&w, pc)? + .click(); + } + + #[derive(Debug)] + struct Side<'s> { + window: &'s Window, + start: Pos, + try_end: Pos, + } + + let mut mk_side = |window, dx| { + let mut w = su.w(window)?; + let p = w.find_piece(pc)?; + let start = p.posg()?; + let try_end = start + PosC([dx, 0]); + + let (sx,sy) = w.posg2posw(start)?; + w.action_chain() + .move_to(sx,sy) + .click() + .click() + .perform() + .context("select and release")?; + + w.synch()?; + + Ok::<_,AE>(Side { window, start, try_end }) + }; + + let sides = [ + mk_side(&self.alice, -20)?, + mk_side(&self.bob, 20)?, + ]; + + dbg!(&sides); + + let pid = nix::unistd::Pid::from_raw(su.server_child.id() as nix::libc::pid_t); + nix::sys::signal::kill(pid, nix::sys::signal::SIGSTOP)?; + + for side in &sides { + let w = su.w(side.window)?; + + w.action_chain() + .move_w(&w, side.start)? + .click() + .release() + + .click_and_hold() + .move_w(&w, side.try_end)? + .release() + + .perform() + .context("conflicting drag")?; + } + + nix::sys::signal::kill(pid, nix::sys::signal::SIGCONT)?; + + #[derive(Debug)] + struct Got<'s> { + side: &'s Side<'s>, + yes: bool, + now: Pos, + log: Vec, + held: Option, + client: String, + } + impl<'s> Deref for Got<'s> { + type Target = Side<'s>; + fn deref<'t>(&'t self) -> &'t Side<'s> { &self.side } + } + + let gots = sides.iter().map(|side|{ + let mut w = su.w(side.window)?; + w.synch()?; + let p = w.find_piece(pc)?; + let now = p.posg()?; + let log = w.find_elements(By::ClassName("logmsg"))?; + let log = log.iter() + .rev() + .map(|e| e.inner_html()) + .take_while(|h| { + h.as_ref().ok() + .map(|s| s.contains("black knight")) + != Some(true) + }) + .collect::,_>>()?; + + let held = w.piece_held(&pc)?; + let client = w.client()?; + let yes = held.as_ref() == Some(&client); + + Ok::<_,AE>(Got { side, now, log, held, client, yes }) + }).collect::,AE>>()?; + + dbg!(&gots); + + let y = gots.iter().filter(|got| got.yes).next().expect("y"); + let n = gots.iter().filter(|got| !got.yes).next().expect("n"); + ensure_eq!(y.now, y.try_end); + ensure_eq!(n.now, y.try_end); + ensure_eq!(n.now, y.try_end); + ensure_eq!(n.held, y.held); + + for got in &gots { + let conflict = got.log.iter().any(|m| m.starts_with("Conflict!")); + ensure_eq!(conflict, !got.yes); + } + } +*/ +} + +#[throws(AE)] +fn tests(UsualSetup { su, alice, bob, ..}: UsualSetup) { + let mut c = Ctx { su, alice, bob }; + + test!(c, "claim", c.claim()?); + +/* + test!(c, "drag-rotate-unselect", { + let pc = c.rotate().always_context("rotate")?; + c.drag_off(pc).always_context("drag off")?; + c.unselect(pc).always_context("unselect")?; + }); + + test!(c, "conflict", c.conflict()?); +*/ + debug!("finishing"); +} + +#[throws(AE)] +fn main() { as_usual(tests)? }