/* * Copyright (C) 2023 Valentin Lorentz * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ use std::path::PathBuf; use std::sync::Arc; use color_eyre::eyre::WrapErr; use pretty_assertions::assert_eq; use ratatui::prelude::*; use ratatrix::buffers::{BufferItem, BufferItemContent}; use ratatrix::components::Backlog; use ratatrix::config::Config; use ratatrix::widgets::Prerender; fn config() -> Arc { std::env::set_var("RATATRIX_CONFIG", PathBuf::from(".config/")); let c = Config::new(); std::env::remove_var("RATATRIX_CONFIG"); Arc::new(c.unwrap()) } fn rect(x: u16, y: u16, width: u16, height: u16) -> Rect { Rect { x, y, width, height, } } macro_rules! items_iter { [ $($item: expr),* ] => { || vec![ $( { let BufferItem { content, prerender, unique_id } = &$item; BufferItem { content: content.clone(), prerender, unique_id: *unique_id, } }, )* ].into_iter() } } #[test] fn test_single_item() { let mut bl = Backlog::new(config()); let prerender = Prerender::new(); let item = BufferItem { content: BufferItemContent::SimpleText(Text::raw("hello")), prerender: &prerender, unique_id: None, }; let mut buf = Buffer::empty(rect(0, 0, 18, 8)); bl.draw_items(&mut buf, rect(3, 2, 12, 4), items_iter![item]) .context("Failed to draw") .unwrap(); let expected = Buffer::with_lines(vec![ " ", " ", " ┌──────────┐ ", " │ │ ", " │hello │ ", " └──────────┘ ", " ", " ", ]); assert_eq!(buf, expected); } #[test] fn test_single_item_cached() { let mut bl = Backlog::new(config()); let prerender = Prerender::new(); let item = BufferItem { content: BufferItemContent::SimpleText(Text::raw("hello")), prerender: &prerender, unique_id: None, }; let mut buf = Buffer::empty(rect(0, 0, 18, 8)); let area = rect(3, 2, 12, 4); bl.draw_items(&mut buf, area, items_iter![item]) .context("Failed to draw") .unwrap(); let expected = Buffer::with_lines(vec![ " ", " ", " ┌──────────┐ ", " │ │ ", " │hello │ ", " └──────────┘ ", " ", " ", ]); assert_eq!(buf, expected); assert_eq!(prerender.key(), Some(10)); let item = BufferItem { content: BufferItemContent::SimpleText(Text::raw("hello")), prerender: &prerender, unique_id: None, }; let mut buf = Buffer::empty(rect(0, 0, 18, 8)); bl.draw_items(&mut buf, area, items_iter![item]) .context("Failed to draw") .unwrap(); assert_eq!(buf, expected); } /// Checks that the prerender cache does not store empty columns to the right #[test] fn test_only_necessary_width() { let mut bl = Backlog::new(config()); let prerender1 = Prerender::new(); let prerender2 = Prerender::new(); let item1 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("hi\nworld")), prerender: &prerender1, unique_id: None, }; let item2 = BufferItem { content: BufferItemContent::SimpleText(Text::raw(":)")), prerender: &prerender2, unique_id: None, }; let mut cell = ratatui::buffer::Cell::default(); cell.set_char('.'); let mut buf = Buffer::filled(rect(0, 0, 18, 7), &cell); // poisoned buffer let area = rect(3, 1, 12, 5); bl.draw_items(&mut buf, area, items_iter![item2, item1]) .context("Failed to draw") .unwrap(); let expected = Buffer::with_lines(vec![ "..................", "...┌──────────┐...", "...│hi........│...", "...│world.....│...", "...│:)........│...", "...└──────────┘...", "..................", ]); assert_eq!(buf, expected); assert_eq!(prerender1.key(), Some(10)); let item1 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("hi\nworld")), prerender: &prerender1, unique_id: None, }; let item2 = BufferItem { content: BufferItemContent::SimpleText(Text::raw(":)")), prerender: &prerender2, unique_id: None, }; let mut buf = Buffer::empty(rect(0, 0, 18, 7)); bl.draw_items(&mut buf, area, items_iter![item2, item1]) .context("Failed to draw") .unwrap(); let expected = Buffer::with_lines(vec![ " ", " ┌──────────┐ ", " │hi... │ ", // dots are leftover from the poisoned buffer above " │world │ ", " │:) │ ", " └──────────┘ ", " ", ]); assert_eq!(buf, expected); } #[test] fn test_single_item_tight() { let mut bl = Backlog::new(config()); let prerender = Prerender::new(); let item = BufferItem { content: BufferItemContent::SimpleText(Text::raw("hello")), prerender: &prerender, unique_id: None, }; let mut buf = Buffer::empty(rect(0, 0, 13, 7)); bl.draw_items(&mut buf, rect(3, 2, 7, 3), items_iter![item]) .context("Failed to draw") .unwrap(); let expected = Buffer::with_lines(vec![ " ", " ", " ┌─────┐ ", " │hello│ ", " └─────┘ ", " ", " ", ]); assert_eq!(buf, expected); } #[test] fn test_two_items() { let mut bl = Backlog::new(config()); let prerender1 = Prerender::new(); let item1 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("hi")), prerender: &prerender1, unique_id: None, }; let prerender2 = Prerender::new(); let item2 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("world")), prerender: &prerender2, unique_id: None, }; let mut buf = Buffer::empty(rect(0, 0, 14, 7)); bl.draw_items(&mut buf, rect(1, 1, 12, 5), items_iter![item2, item1]) .context("Failed to draw") .unwrap(); let expected = Buffer::with_lines(vec![ " ", " ┌──────────┐ ", " │ │ ", " │hi │ ", " │world │ ", " └──────────┘ ", " ", ]); assert_eq!(buf, expected); } #[test] fn test_two_items_scroll() { let mut bl = Backlog::new(config()); let prerender1 = Prerender::new(); let prerender2 = Prerender::new(); let item1 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("hi")), prerender: &prerender1, unique_id: Some(123), }; let item2 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("world")), prerender: &prerender2, unique_id: Some(456), }; let mut buf = Buffer::empty(rect(0, 0, 14, 7)); bl.draw_items(&mut buf, rect(1, 1, 12, 5), items_iter![item2, item1]) .context("Failed to draw") .unwrap(); let expected = Buffer::with_lines(vec![ " ", " ┌──────────┐ ", " │ │ ", " │hi │ ", " │world │ ", " └──────────┘ ", " ", ]); assert_eq!(buf, expected); bl.scroll_up(1); let item1 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("hi")), prerender: &prerender1, unique_id: Some(123), }; let item2 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("world")), prerender: &prerender2, unique_id: Some(456), }; let mut buf = Buffer::empty(rect(0, 0, 14, 7)); bl.draw_items(&mut buf, rect(1, 1, 12, 5), items_iter![item2, item1]) .context("Failed to draw") .unwrap(); let expected = Buffer::with_lines(vec![ " ", " ┌──────────┐ ", " │ │ ", " │ │ ", " │hi │ ", " └──────────┘ ", " ", ]); assert_eq!(buf, expected); bl.scroll_up(1); let item1 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("hi")), prerender: &prerender1, unique_id: Some(123), }; let item2 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("world")), prerender: &prerender2, unique_id: Some(456), }; let mut buf = Buffer::empty(rect(0, 0, 14, 7)); bl.draw_items(&mut buf, rect(1, 1, 12, 5), items_iter![item2, item1]) .context("Failed to draw") .unwrap(); let expected = Buffer::with_lines(vec![ " ", " ┌──────────┐ ", " │ │ ", " │ │ ", " │ │ ", " └──────────┘ ", " ", ]); assert_eq!(buf, expected); } #[test] fn test_two_items_multiline() { let mut bl = Backlog::new(config()); let prerender1 = Prerender::new(); let item1 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("hi")), prerender: &prerender1, unique_id: None, }; let prerender2 = Prerender::new(); let item2 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("world\n!")), prerender: &prerender2, unique_id: None, }; let mut buf = Buffer::empty(rect(0, 0, 14, 7)); bl.draw_items(&mut buf, rect(1, 1, 12, 5), items_iter![item2, item1]) .context("Failed to draw") .unwrap(); let expected = Buffer::with_lines(vec![ " ", " ┌──────────┐ ", " │hi │ ", " │world │ ", " │! │ ", " └──────────┘ ", " ", ]); assert_eq!(buf, expected); } #[test] fn test_two_items_tight() { let mut bl = Backlog::new(config()); let prerender1 = Prerender::new(); let item1 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("hi")), prerender: &prerender1, unique_id: None, }; let prerender2 = Prerender::new(); let item2 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("world")), prerender: &prerender2, unique_id: None, }; let mut buf = Buffer::empty(rect(0, 0, 9, 6)); bl.draw_items(&mut buf, rect(1, 1, 7, 4), items_iter![item2, item1]) .context("Failed to draw") .unwrap(); let expected = Buffer::with_lines(vec![ " ", " ┌─────┐ ", " │hi │ ", " │world│ ", " └─────┘ ", " ", ]); assert_eq!(buf, expected); } #[test] fn test_cache_moved() { let mut bl = Backlog::new(config()); let prerender1 = Prerender::new(); let item1 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("hi")), prerender: &prerender1, unique_id: None, }; // Draw once let mut buf = Buffer::empty(rect(0, 0, 14, 7)); bl.draw_items(&mut buf, rect(1, 1, 12, 5), items_iter![item1]) .context("Failed to draw") .unwrap(); let expected = Buffer::with_lines(vec![ " ", " ┌──────────┐ ", " │ │ ", " │ │ ", " │hi │ ", " └──────────┘ ", " ", ]); assert_eq!(buf, expected); // New item added at bottom let item1 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("hi")), prerender: &prerender1, unique_id: None, }; let prerender2 = Prerender::new(); let item2 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("world")), prerender: &prerender2, unique_id: None, }; let mut buf = Buffer::empty(rect(0, 0, 14, 7)); bl.draw_items(&mut buf, rect(1, 1, 12, 5), items_iter![item2, item1]) .context("Failed to draw") .unwrap(); let expected = Buffer::with_lines(vec![ " ", " ┌──────────┐ ", " │ │ ", " │hi │ ", " │world │ ", " └──────────┘ ", " ", ]); assert_eq!(buf, expected); } #[test] fn test_overflow_and_scroll() { let mut bl = Backlog::new(config()); let prerender1 = Prerender::new(); let prerender2 = Prerender::new(); let prerender3 = Prerender::new(); let item1 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("line1 x")), prerender: &prerender1, unique_id: None, }; let item2 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("line2 y\nline3 y\nline4 y")), prerender: &prerender2, unique_id: None, }; let item3 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("line5 z")), prerender: &prerender3, unique_id: None, }; let mut buf = Buffer::empty(rect(0, 0, 14, 7)); bl.draw_items( &mut buf, rect(1, 1, 12, 5), items_iter![item3, item2, item1], ) .context("Failed to draw") .unwrap(); let expected = Buffer::with_lines(vec![ " ", " ┌──────────┐ ", " │line3 y │ ", " │line4 y │ ", " │line5 z │ ", " └──────────┘ ", " ", ]); assert_eq!(buf, expected); bl.scroll_up(1); let item1 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("line1 x")), prerender: &prerender1, unique_id: None, }; let item2 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("line2 y\nline3 y\nline4 y")), prerender: &prerender2, unique_id: None, }; let item3 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("line5 z")), prerender: &prerender3, unique_id: None, }; let mut buf = Buffer::empty(rect(0, 0, 14, 7)); bl.draw_items( &mut buf, rect(1, 1, 12, 5), items_iter![item3, item2, item1], ) .context("Failed to draw") .unwrap(); let expected = Buffer::with_lines(vec![ " ", " ┌──────────┐ ", " │line2 y │ ", " │line3 y │ ", " │line4 y │ ", " └──────────┘ ", " ", ]); assert_eq!(buf, expected); bl.scroll_up(1); let item1 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("line1 x")), prerender: &prerender1, unique_id: None, }; let item2 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("line2 y\nline3 y\nline4 y")), prerender: &prerender2, unique_id: None, }; let item3 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("line5 z")), prerender: &prerender3, unique_id: None, }; let mut buf = Buffer::empty(rect(0, 0, 14, 7)); bl.draw_items( &mut buf, rect(1, 1, 12, 5), items_iter![item3, item2, item1], ) .context("Failed to draw") .unwrap(); let expected = Buffer::with_lines(vec![ " ", " ┌──────────┐ ", " │line1 x │ ", " │line2 y │ ", " │line3 y │ ", " └──────────┘ ", " ", ]); assert_eq!(buf, expected); bl.scroll_up(1); let item1 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("line1 x")), prerender: &prerender1, unique_id: None, }; let item2 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("line2 y\nline3 y\nline4 y")), prerender: &prerender2, unique_id: None, }; let item3 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("line5 z")), prerender: &prerender3, unique_id: None, }; let mut buf = Buffer::empty(rect(0, 0, 14, 7)); bl.draw_items( &mut buf, rect(1, 1, 12, 5), items_iter![item3, item2, item1], ) .context("Failed to draw") .unwrap(); let expected = Buffer::with_lines(vec![ " ", " ┌──────────┐ ", " │ │ ", " │line1 x │ ", " │line2 y │ ", " └──────────┘ ", " ", ]); assert_eq!(buf, expected); } #[test] fn test_scrolledup_new_line() { let mut bl = Backlog::new(config()); let prerender1 = Prerender::new(); let item1 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("hi\nworld")), prerender: &prerender1, unique_id: Some(123), }; // Draw once let mut buf = Buffer::empty(rect(0, 0, 14, 7)); bl.draw_items(&mut buf, rect(1, 1, 12, 5), items_iter![item1]) .context("Failed to draw") .unwrap(); let expected = Buffer::with_lines(vec![ " ", " ┌──────────┐ ", " │ │ ", " │hi │ ", " │world │ ", " └──────────┘ ", " ", ]); assert_eq!(buf, expected); // Scroll up one line bl.scroll_up(1); let item1 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("hi\nworld")), prerender: &prerender1, unique_id: Some(123), }; let mut buf = Buffer::empty(rect(0, 0, 14, 7)); bl.draw_items(&mut buf, rect(1, 1, 12, 5), items_iter![item1]) .context("Failed to draw") .unwrap(); let expected = Buffer::with_lines(vec![ " ", " ┌──────────┐ ", " │ │ ", " │ │ ", " │hi │ ", " └──────────┘ ", " ", ]); assert_eq!(buf, expected); // New item added at bottom, displayed paragraph should not move up let item1 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("hi\nworld")), prerender: &prerender1, unique_id: Some(123), }; let prerender2 = Prerender::new(); let item2 = BufferItem { content: BufferItemContent::SimpleText(Text::raw("!")), prerender: &prerender2, unique_id: Some(456), }; let mut buf = Buffer::empty(rect(0, 0, 14, 7)); bl.draw_items(&mut buf, rect(1, 1, 12, 5), items_iter![item2, item1]) .context("Failed to draw") .unwrap(); let expected = Buffer::with_lines(vec![ " ", " ┌──────────┐ ", " │ │ ", " │ │ ", " │hi │ ", " └──────────┘ ", " ", ]); assert_eq!(buf, expected); }