mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-01-30 20:34:49 +01:00
Add some tests
First ever squash no clue what I'm doing
This commit is contained in:
parent
e3504038ee
commit
140b0200aa
5 changed files with 419 additions and 30 deletions
6
.github/workflows/ci.pinnacle.yml
vendored
6
.github/workflows/ci.pinnacle.yml
vendored
|
@ -53,10 +53,10 @@ jobs:
|
|||
uses: leafo/gh-actions-luarocks@v4
|
||||
- name: Build
|
||||
if: ${{ runner.debug != '1' }}
|
||||
run: cargo test
|
||||
run: cargo test -- --test-threads=1
|
||||
- name: Build (debug)
|
||||
if: ${{ runner.debug == '1' }}
|
||||
run: RUST_LOG=debug cargo test -- --nocapture
|
||||
run: RUST_LOG=debug cargo test -- --nocapture --test-threads=1
|
||||
check-format:
|
||||
runs-on: ubuntu-latest
|
||||
name: Check formatting
|
||||
|
@ -79,6 +79,8 @@ jobs:
|
|||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: clippy
|
||||
- name: Cache stuff
|
||||
uses: Swatinem/rust-cache@v2
|
||||
- name: Get dependencies
|
||||
run: sudo apt update && sudo apt install libwayland-dev libxkbcommon-dev libudev-dev libinput-dev libgbm-dev libseat-dev libsystemd-dev protobuf-compiler xwayland libegl-dev
|
||||
- name: Setup Lua
|
||||
|
|
|
@ -224,7 +224,7 @@ local _fullscreen_or_maximized_keys = {
|
|||
--- -- A simple window rule. This one will cause Firefox to open on tag "Browser".
|
||||
---Window.add_window_rule({
|
||||
--- cond = { classes = { "firefox" } },
|
||||
--- rule = { tags = { "Browser" } },
|
||||
--- rule = { tags = { Tag.get("Browser") } },
|
||||
---})
|
||||
---
|
||||
--- -- To apply rules when *all* provided conditions are true, use `all`.
|
||||
|
@ -234,8 +234,8 @@ local _fullscreen_or_maximized_keys = {
|
|||
--- cond = {
|
||||
--- all = {
|
||||
--- {
|
||||
--- class = "steam",
|
||||
--- tag = Tag:get("5"),
|
||||
--- classes = { "steam" },
|
||||
--- tags = { Tag.get("5") },
|
||||
--- }
|
||||
--- }
|
||||
--- },
|
||||
|
@ -246,8 +246,8 @@ local _fullscreen_or_maximized_keys = {
|
|||
--- -- Thus, the above can be shortened to:
|
||||
---Window.add_window_rule({
|
||||
--- cond = {
|
||||
--- class = "steam",
|
||||
--- tag = Tag:get("5"),
|
||||
--- classes = { "steam" },
|
||||
--- tags = { Tag.get("5") },
|
||||
--- },
|
||||
--- rule = { fullscreen_or_maximized = "fullscreen" },
|
||||
---})
|
||||
|
@ -268,17 +268,17 @@ local _fullscreen_or_maximized_keys = {
|
|||
--- cond = {
|
||||
--- all = { -- This `all` block is needed because the outermost block cannot be an array.
|
||||
--- { any = {
|
||||
--- { class = { "firefox", "thunderbird", "discord" } }
|
||||
--- { classes = { "firefox", "thunderbird", "discord" } }
|
||||
--- } },
|
||||
--- { any = {
|
||||
--- -- Because `tag` is inside an `all` block,
|
||||
--- -- the window must have all these tags for this to be true.
|
||||
--- -- If it was in an `any` block, only one tag would need to match.
|
||||
--- { all = {
|
||||
--- { tag = { "A", "B", "C" } }
|
||||
--- { tags = { Tag.get("A"), Tag.get("B"), Tag.get("C") } }
|
||||
--- } },
|
||||
--- { all = {
|
||||
--- { tag = { "1", "2" } }
|
||||
--- { tags = { Tag.get("1"), Tag.get("2") } }
|
||||
--- } },
|
||||
--- } }
|
||||
--- }
|
||||
|
|
|
@ -412,6 +412,8 @@ fn generate_config(args: ConfigGen) -> anyhow::Result<()> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Context;
|
||||
|
||||
use super::*;
|
||||
|
||||
// TODO: find a way to test the interactive bits programmatically
|
||||
|
@ -428,9 +430,7 @@ mod tests {
|
|||
"--lang",
|
||||
"rust",
|
||||
"--dir",
|
||||
temp_dir
|
||||
.to_str()
|
||||
.ok_or(anyhow::anyhow!("not valid unicode"))?,
|
||||
temp_dir.to_str().context("not valid unicode")?,
|
||||
"--non-interactive",
|
||||
]);
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ use std::num::NonZeroU32;
|
|||
|
||||
use crate::{output::OutputName, tag::TagId, window::window_state::FullscreenOrMaximized};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, Default)]
|
||||
pub struct WindowRuleCondition {
|
||||
/// This condition is met when any of the conditions provided is met.
|
||||
#[serde(default)]
|
||||
|
@ -137,7 +137,7 @@ impl WindowRuleCondition {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, Default)]
|
||||
pub struct WindowRule {
|
||||
/// Set the output the window will open on.
|
||||
#[serde(default)]
|
||||
|
|
417
tests/lua_api.rs
417
tests/lua_api.rs
|
@ -1,10 +1,14 @@
|
|||
use std::{
|
||||
io::Write,
|
||||
panic::UnwindSafe,
|
||||
process::{Command, Stdio},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use pinnacle::{backend::dummy::setup_dummy, state::State};
|
||||
use pinnacle::{
|
||||
backend::dummy::setup_dummy,
|
||||
state::{State, WithState},
|
||||
};
|
||||
use smithay::reexports::calloop::{
|
||||
self,
|
||||
channel::{Event, Sender},
|
||||
|
@ -13,21 +17,35 @@ use smithay::reexports::calloop::{
|
|||
use test_log::test;
|
||||
|
||||
fn run_lua(ident: &str, code: &str) {
|
||||
let code = format!(r#"require("pinnacle").setup(function({ident}) {code} end)"#);
|
||||
#[rustfmt::skip]
|
||||
let code = format!(r#"
|
||||
require("pinnacle").setup(function({ident})
|
||||
local run = function({ident})
|
||||
{code}
|
||||
end
|
||||
|
||||
local success, err = pcall(run, {ident})
|
||||
|
||||
if not success then
|
||||
print(err)
|
||||
os.exit(1)
|
||||
end
|
||||
end)
|
||||
"#);
|
||||
|
||||
let mut child = Command::new("lua").stdin(Stdio::piped()).spawn().unwrap();
|
||||
|
||||
let mut stdin = child
|
||||
.stdin
|
||||
.take()
|
||||
.ok_or_else(|| anyhow::anyhow!("failed to open child stdin"))
|
||||
.unwrap();
|
||||
let mut stdin = child.stdin.take().unwrap();
|
||||
|
||||
stdin.write_all(code.as_bytes()).unwrap();
|
||||
|
||||
drop(stdin);
|
||||
|
||||
child.wait().unwrap();
|
||||
let exit_status = child.wait().unwrap();
|
||||
|
||||
if exit_status.code().is_some_and(|code| code != 0) {
|
||||
panic!("lua code panicked");
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
|
@ -49,7 +67,7 @@ macro_rules! run_lua {
|
|||
}
|
||||
|
||||
fn test_lua_api(
|
||||
test: impl FnOnce(Sender<Box<dyn FnOnce(&mut State) + Send>>) + Send + 'static,
|
||||
test: impl FnOnce(Sender<Box<dyn FnOnce(&mut State) + Send>>) + Send + UnwindSafe + 'static,
|
||||
) -> anyhow::Result<()> {
|
||||
let (mut state, mut event_loop) = setup_dummy(true, None)?;
|
||||
|
||||
|
@ -59,7 +77,7 @@ fn test_lua_api(
|
|||
.handle()
|
||||
.insert_source(recv, |event, _, state| match event {
|
||||
Event::Msg(f) => f(state),
|
||||
Event::Closed => panic!(),
|
||||
Event::Closed => (),
|
||||
})
|
||||
.map_err(|_| anyhow::anyhow!("failed to insert source"))?;
|
||||
|
||||
|
@ -67,7 +85,17 @@ fn test_lua_api(
|
|||
|
||||
state.start_grpc_server(tempdir.path())?;
|
||||
|
||||
std::thread::spawn(move || test(sender));
|
||||
let loop_signal = event_loop.get_signal();
|
||||
|
||||
let join_handle = std::thread::spawn(move || {
|
||||
let res = std::panic::catch_unwind(|| {
|
||||
test(sender);
|
||||
});
|
||||
loop_signal.stop();
|
||||
if let Err(err) = res {
|
||||
std::panic::resume_unwind(err);
|
||||
}
|
||||
});
|
||||
|
||||
event_loop.run(None, &mut state, |state| {
|
||||
state.fixup_z_layering();
|
||||
|
@ -88,15 +116,277 @@ fn test_lua_api(
|
|||
);
|
||||
})?;
|
||||
|
||||
if let Err(err) = join_handle.join() {
|
||||
panic!("{err:?}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
mod coverage {
|
||||
use pinnacle::{
|
||||
tag::TagId,
|
||||
window::{
|
||||
rules::{WindowRule, WindowRuleCondition},
|
||||
window_state::FullscreenOrMaximized,
|
||||
},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
// Process
|
||||
|
||||
#[tokio::main]
|
||||
#[self::test]
|
||||
async fn process_spawn() -> anyhow::Result<()> {
|
||||
test_lua_api(|sender| {
|
||||
run_lua! { |Pinnacle|
|
||||
Pinnacle.process.spawn("foot")
|
||||
}
|
||||
|
||||
sleep_secs(1);
|
||||
|
||||
assert(&sender, |state| {
|
||||
assert_eq!(state.windows.len(), 1);
|
||||
assert_eq!(state.windows[0].class(), Some("foot".to_string()));
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
#[self::test]
|
||||
async fn process_set_env() -> anyhow::Result<()> {
|
||||
test_lua_api(|sender| {
|
||||
run_lua! { |Pinnacle|
|
||||
Pinnacle.process.set_env("PROCESS_SET_ENV", "env value")
|
||||
}
|
||||
|
||||
sleep_secs(1);
|
||||
|
||||
assert(&sender, |_state| {
|
||||
assert_eq!(
|
||||
std::env::var("PROCESS_SET_ENV"),
|
||||
Ok("env value".to_string())
|
||||
);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
// Window
|
||||
|
||||
#[tokio::main]
|
||||
#[self::test]
|
||||
async fn window_get_all() -> anyhow::Result<()> {
|
||||
test_lua_api(|_sender| {
|
||||
run_lua! { |Pinnacle|
|
||||
assert(#Pinnacle.window.get_all() == 0)
|
||||
|
||||
for i = 1, 5 do
|
||||
Pinnacle.process.spawn("foot")
|
||||
end
|
||||
}
|
||||
|
||||
sleep_secs(1);
|
||||
|
||||
run_lua! { |Pinnacle|
|
||||
assert(#Pinnacle.window.get_all() == 5)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
#[self::test]
|
||||
async fn window_get_focused() -> anyhow::Result<()> {
|
||||
test_lua_api(|_sender| {
|
||||
run_lua! { |Pinnacle|
|
||||
assert(not Pinnacle.window.get_focused())
|
||||
|
||||
Pinnacle.tag.add(Pinnacle.output.get_focused(), "1")[1]:set_active(true)
|
||||
Pinnacle.process.spawn("foot")
|
||||
}
|
||||
|
||||
sleep_secs(1);
|
||||
|
||||
run_lua! { |Pinnacle|
|
||||
assert(Pinnacle.window.get_focused())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
#[self::test]
|
||||
async fn window_add_window_rule() -> anyhow::Result<()> {
|
||||
test_lua_api(|sender| {
|
||||
run_lua! { |Pinnacle|
|
||||
Pinnacle.tag.add(Pinnacle.output.get_focused(), "Tag Name")
|
||||
Pinnacle.window.add_window_rule({
|
||||
cond = { classes = { "firefox" } },
|
||||
rule = { tags = { Pinnacle.tag.get("Tag Name") } },
|
||||
})
|
||||
}
|
||||
|
||||
sleep_secs(1);
|
||||
|
||||
assert(&sender, |state| {
|
||||
assert_eq!(state.config.window_rules.len(), 1);
|
||||
assert_eq!(
|
||||
state.config.window_rules[0],
|
||||
(
|
||||
WindowRuleCondition {
|
||||
class: Some(vec!["firefox".to_string()]),
|
||||
..Default::default()
|
||||
},
|
||||
WindowRule {
|
||||
tags: Some(vec![TagId::Some(0)]),
|
||||
..Default::default()
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
run_lua! { |Pinnacle|
|
||||
Pinnacle.tag.add(Pinnacle.output.get_focused(), "Tag Name 2")
|
||||
Pinnacle.window.add_window_rule({
|
||||
cond = {
|
||||
all = {
|
||||
{
|
||||
classes = { "steam" },
|
||||
tags = {
|
||||
Pinnacle.tag.get("Tag Name"),
|
||||
Pinnacle.tag.get("Tag Name 2"),
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
rule = { fullscreen_or_maximized = "fullscreen" },
|
||||
})
|
||||
}
|
||||
|
||||
sleep_secs(1);
|
||||
|
||||
assert(&sender, |state| {
|
||||
assert_eq!(state.config.window_rules.len(), 2);
|
||||
assert_eq!(
|
||||
state.config.window_rules[1],
|
||||
(
|
||||
WindowRuleCondition {
|
||||
cond_all: Some(vec![WindowRuleCondition {
|
||||
class: Some(vec!["steam".to_string()]),
|
||||
tag: Some(vec![TagId::Some(0), TagId::Some(1)]),
|
||||
..Default::default()
|
||||
}]),
|
||||
..Default::default()
|
||||
},
|
||||
WindowRule {
|
||||
fullscreen_or_maximized: Some(FullscreenOrMaximized::Fullscreen),
|
||||
..Default::default()
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: window_begin_move
|
||||
// TODO: window_begin_resize
|
||||
|
||||
// WindowHandle
|
||||
|
||||
#[tokio::main]
|
||||
#[self::test]
|
||||
async fn window_handle_close() -> anyhow::Result<()> {
|
||||
test_lua_api(|sender| {
|
||||
run_lua! { |Pinnacle|
|
||||
Pinnacle.process.spawn("foot")
|
||||
}
|
||||
|
||||
sleep_secs(1);
|
||||
|
||||
assert(&sender, |state| {
|
||||
assert_eq!(state.windows.len(), 1);
|
||||
});
|
||||
|
||||
run_lua! { |Pinnacle|
|
||||
Pinnacle.window.get_all()[1]:close()
|
||||
}
|
||||
|
||||
sleep_secs(1);
|
||||
|
||||
assert(&sender, |state| {
|
||||
assert_eq!(state.windows.len(), 0);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
#[self::test]
|
||||
async fn window_handle_move_to_tag() -> anyhow::Result<()> {
|
||||
test_lua_api(|sender| {
|
||||
run_lua! { |Pinnacle|
|
||||
local tags = Pinnacle.tag.add(Pinnacle.output.get_focused(), "1", "2", "3")
|
||||
tags[1]:set_active(true)
|
||||
tags[2]:set_active(true)
|
||||
Pinnacle.process.spawn("foot")
|
||||
}
|
||||
|
||||
sleep_secs(1);
|
||||
|
||||
assert(&sender, |state| {
|
||||
assert_eq!(
|
||||
state.windows[0].with_state(|st| st
|
||||
.tags
|
||||
.iter()
|
||||
.map(|tag| tag.name())
|
||||
.collect::<Vec<_>>()),
|
||||
vec!["1", "2"]
|
||||
);
|
||||
});
|
||||
|
||||
// Correct usage
|
||||
run_lua! { |Pinnacle|
|
||||
Pinnacle.window.get_all()[1]:move_to_tag(Pinnacle.tag.get("3"))
|
||||
}
|
||||
|
||||
sleep_secs(1);
|
||||
|
||||
assert(&sender, |state| {
|
||||
assert_eq!(
|
||||
state.windows[0].with_state(|st| st
|
||||
.tags
|
||||
.iter()
|
||||
.map(|tag| tag.name())
|
||||
.collect::<Vec<_>>()),
|
||||
vec!["3"]
|
||||
);
|
||||
});
|
||||
|
||||
// Move to the same tag
|
||||
run_lua! { |Pinnacle|
|
||||
Pinnacle.window.get_all()[1]:move_to_tag(Pinnacle.tag.get("3"))
|
||||
}
|
||||
|
||||
sleep_secs(1);
|
||||
|
||||
assert(&sender, |state| {
|
||||
assert_eq!(
|
||||
state.windows[0].with_state(|st| st
|
||||
.tags
|
||||
.iter()
|
||||
.map(|tag| tag.name())
|
||||
.collect::<Vec<_>>()),
|
||||
vec!["3"]
|
||||
);
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
#[test]
|
||||
async fn window_count_with_tag_is_correct() -> anyhow::Result<()> {
|
||||
test_lua_api(|sender| {
|
||||
run_lua! { |Pinnacle|
|
||||
Pinnacle.tag.add(Pinnacle.output.get_all()[1], "1")
|
||||
Pinnacle.tag.add(Pinnacle.output.get_focused(), "1")
|
||||
Pinnacle.process.spawn("foot")
|
||||
}
|
||||
|
||||
|
@ -104,10 +394,107 @@ async fn window_count_with_tag_is_correct() -> anyhow::Result<()> {
|
|||
|
||||
assert(&sender, |state| assert_eq!(state.windows.len(), 1));
|
||||
|
||||
run_lua! { |Pinnacle|
|
||||
for i = 1, 20 do
|
||||
Pinnacle.process.spawn("foot")
|
||||
end
|
||||
}
|
||||
|
||||
sleep_secs(1);
|
||||
|
||||
run_lua! { |Pinnacle|
|
||||
Pinnacle.quit()
|
||||
}
|
||||
assert(&sender, |state| assert_eq!(state.windows.len(), 21));
|
||||
})
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
#[test]
|
||||
async fn window_count_without_tag_is_correct() -> anyhow::Result<()> {
|
||||
test_lua_api(|sender| {
|
||||
run_lua! { |Pinnacle|
|
||||
Pinnacle.process.spawn("foot")
|
||||
}
|
||||
|
||||
sleep_secs(1);
|
||||
|
||||
assert(&sender, |state| assert_eq!(state.windows.len(), 1));
|
||||
})
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
#[test]
|
||||
async fn spawned_window_on_active_tag_has_keyboard_focus() -> anyhow::Result<()> {
|
||||
test_lua_api(|sender| {
|
||||
run_lua! { |Pinnacle|
|
||||
Pinnacle.tag.add(Pinnacle.output.get_focused(), "1")[1]:set_active(true)
|
||||
Pinnacle.process.spawn("foot")
|
||||
}
|
||||
|
||||
sleep_secs(1);
|
||||
|
||||
assert(&sender, |state| {
|
||||
assert_eq!(
|
||||
state
|
||||
.focused_window(state.focused_output().unwrap())
|
||||
.unwrap()
|
||||
.class(),
|
||||
Some("foot".to_string())
|
||||
);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
#[test]
|
||||
async fn spawned_window_on_inactive_tag_does_not_have_keyboard_focus() -> anyhow::Result<()> {
|
||||
test_lua_api(|sender| {
|
||||
run_lua! { |Pinnacle|
|
||||
Pinnacle.tag.add(Pinnacle.output.get_focused(), "1")
|
||||
Pinnacle.process.spawn("foot")
|
||||
}
|
||||
|
||||
sleep_secs(1);
|
||||
|
||||
assert(&sender, |state| {
|
||||
assert_eq!(state.focused_window(state.focused_output().unwrap()), None);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
#[test]
|
||||
async fn spawned_window_has_correct_tags() -> anyhow::Result<()> {
|
||||
test_lua_api(|sender| {
|
||||
run_lua! { |Pinnacle|
|
||||
Pinnacle.tag.add(Pinnacle.output.get_focused(), "1", "2", "3")
|
||||
Pinnacle.process.spawn("foot")
|
||||
}
|
||||
|
||||
sleep_secs(1);
|
||||
|
||||
assert(&sender, |state| {
|
||||
assert_eq!(state.windows.len(), 1);
|
||||
assert_eq!(state.windows[0].with_state(|st| st.tags.len()), 1);
|
||||
});
|
||||
|
||||
run_lua! { |Pinnacle|
|
||||
Pinnacle.tag.get("1"):set_active(true)
|
||||
Pinnacle.tag.get("3"):set_active(true)
|
||||
Pinnacle.process.spawn("foot")
|
||||
}
|
||||
|
||||
sleep_secs(1);
|
||||
|
||||
assert(&sender, |state| {
|
||||
assert_eq!(state.windows.len(), 2);
|
||||
assert_eq!(state.windows[1].with_state(|st| st.tags.len()), 2);
|
||||
assert_eq!(
|
||||
state.windows[1].with_state(|st| st
|
||||
.tags
|
||||
.iter()
|
||||
.map(|tag| tag.name())
|
||||
.collect::<Vec<_>>()),
|
||||
vec!["1", "3"]
|
||||
);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue