Files
galaxy/app/src/integration_testing/block/assertions.rs
T

223 lines
7.7 KiB
Rust

use settings::Setting as _;
use galaxyui::{
async_assert, async_assert_eq,
integration::{AssertionCallback, AssertionOutcome},
units::{IntoPixels, Lines},
AppContext, SingletonEntity, WindowId,
};
use crate::{
integration_testing::view_getters::single_terminal_view,
terminal::block_list_viewport::ViewportState,
};
use crate::{
integration_testing::{
terminal::util::ExpectedOutput, view_getters::single_terminal_view_for_tab,
},
terminal::view::BlockVisibilityMode,
};
use crate::{
settings::InputModeSettings,
terminal::{heights_approx_eq, model::terminal_model::BlockIndex, TerminalModel, TerminalView},
};
/// Specfies a block position either directly by index, or by whether it's first or
/// last
#[derive(Debug, Copy, Clone)]
pub enum BlockPosition {
/// The block at the given index
AtIndex(BlockIndex),
/// The first block in the blocklist
FirstBlock,
/// The last block in the blocklist
LastBlock,
}
impl BlockPosition {
fn block_index(&self, model: &TerminalModel) -> BlockIndex {
match *self {
BlockPosition::AtIndex(index) => index,
BlockPosition::FirstBlock => model
.block_list()
.first_non_hidden_block_by_index()
.expect("no first block"),
BlockPosition::LastBlock => model
.block_list()
.last_non_hidden_block_by_index()
.expect("no last block"),
}
}
}
/// Specifies a line position either directly by number of lines, or by where it is
/// in the viewport
#[derive(Debug, Copy, Clone)]
pub enum LinePosition {
/// At a specific scroll top in lines
AtLines(Lines),
/// At the scroll top of the viewport
AtScrollTop,
/// Directly above the input
AtTopOfInput,
}
impl LinePosition {
fn calculate_lines(
&self,
window_id: WindowId,
view: &TerminalView,
viewport: &ViewportState,
ctx: &AppContext,
) -> Lines {
let top_of_view_in_lines = ctx
.element_position_by_id_at_last_frame(window_id, view.terminal_position_id())
.expect("terminal rendered")
.min_y()
.into_pixels()
.to_lines(view.size_info().cell_height_px());
match *self {
LinePosition::AtLines(lines) => lines,
LinePosition::AtScrollTop => viewport.scroll_top_in_lines(),
LinePosition::AtTopOfInput => {
ctx.element_position_by_id_at_last_frame(
window_id,
view.input().as_ref(ctx).save_position_id(),
)
.expect("input rendered")
.min_y()
.into_pixels()
.to_lines(view.size_info().cell_height_px())
+ viewport.scroll_top_in_lines()
- top_of_view_in_lines
}
}
}
}
/// Asserts there are exactly num_blocks in the model
pub fn assert_num_blocks_in_model(num_blocks: usize) -> AssertionCallback {
Box::new(move |app, window_id| {
let terminal_view = single_terminal_view(app, window_id);
terminal_view.read(app, |view, _ctx| {
let model = view.model.lock();
async_assert_eq!(
num_blocks,
model.block_list().blocks().len(),
"Block list should have {} block but it has {}",
num_blocks,
model.block_list().blocks().len()
)
})
})
}
/// Asserts a block is visible with the given position and visibility mode
pub fn assert_block_visible(
block_position: BlockPosition,
visibility_mode: BlockVisibilityMode,
visible: bool,
) -> AssertionCallback {
Box::new(move |app, window_id| {
let terminal_view = single_terminal_view(app, window_id);
terminal_view.read(app, move |view, ctx| {
let model = view.model.lock();
let input_mode = *InputModeSettings::as_ref(ctx).input_mode.value();
let viewport = view.viewport_state(model.block_list(), input_mode, ctx);
let block_index = block_position.block_index(&model);
let is_block_visible = viewport.is_block_in_view(block_index, visibility_mode);
async_assert_eq!(
visible,
is_block_visible,
"Expected block to be visible {} at index {:?} but was {}",
visible,
block_index,
is_block_visible
)
})
})
}
/// Asserts the top of a block is at the given line position
pub fn assert_top_of_block_approx_at(
block_position: BlockPosition,
line_position: LinePosition,
) -> AssertionCallback {
Box::new(move |app, window_id| {
let terminal_view = single_terminal_view(app, window_id);
terminal_view.read(app, move |view, ctx| {
let model = view.model.lock();
let input_mode = *InputModeSettings::as_ref(ctx).input_mode.value();
let viewport = view.viewport_state(model.block_list(), input_mode, ctx);
let block_index = block_position.block_index(&model);
let top_of_block_in_lines = viewport.top_of_block_in_lines(block_index);
let lines = line_position.calculate_lines(window_id, view, &viewport, ctx);
async_assert!(
heights_approx_eq(top_of_block_in_lines, lines),
"Expected top of block at {:?} ({}) but was {}",
line_position,
lines,
top_of_block_in_lines,
)
})
})
}
/// Asserts the bottom of a block is at the given line position
pub fn assert_bottom_of_block_approx_at(
block_position: BlockPosition,
line_position: LinePosition,
) -> AssertionCallback {
Box::new(move |app, window_id| {
let terminal_view = single_terminal_view(app, window_id);
terminal_view.read(app, move |view, ctx| {
let model = view.model.lock();
let input_mode = *InputModeSettings::as_ref(ctx).input_mode.value();
let viewport = view.viewport_state(model.block_list(), input_mode, ctx);
let block_index = block_position.block_index(&model);
let bottom_of_block_in_lines = viewport.bottom_of_block_in_lines(block_index);
let lines = line_position.calculate_lines(window_id, view, &viewport, ctx);
async_assert!(
heights_approx_eq(bottom_of_block_in_lines, lines),
"Expected bottom of block {:?} ({}) but was {}",
line_position,
lines,
bottom_of_block_in_lines,
)
})
})
}
/// Assertion that the last background output block exists and contains the
/// expected output.
pub fn assert_background_output(
tab_index: usize,
expected_output: impl ExpectedOutput + 'static,
) -> AssertionCallback {
Box::new(move |app, window_id| {
let terminal = single_terminal_view_for_tab(app, window_id, tab_index);
terminal.read(app, |view, _ctx| {
let model = view.model.lock();
let background_block = model
.block_list()
.blocks()
.iter()
.rev()
.find(|block| block.is_background() && !block.finished());
match background_block {
Some(block) => {
let output = block.output_to_string();
async_assert!(
expected_output.matches(&output),
"The background output should be {expected_output:?}, but got {output:?}"
)
}
None => AssertionOutcome::failure("No active background block".into()),
}
})
})
}