tests: gui tests

This commit is contained in:
Jack Wills
2024-01-14 10:06:41 +00:00
parent 8e9243d884
commit 53543a1b72
8 changed files with 2235 additions and 316 deletions
+2 -1
View File
@@ -157,7 +157,8 @@ see <a href="https://forums.raspberrypi.com/viewtopic.php?t=203128" target='_bla
## Tests
As of yet untested, needs work
~~As of yet untested, needs work~~
The work has been done
```shell
cargo test -- --test-threads=1
+95
View File
@@ -630,3 +630,98 @@ impl Columns {
}
}
}
#[cfg(test)]
mod tests {
use ratatui::widgets::ListItem;
use crate::{
app_data::{ContainerImage, Logs},
ui::log_sanitizer,
};
use super::{ByteStats, ContainerName, CpuStats, LogsTz};
#[test]
// Display CpuStats as a string
fn test_container_state_cpustats_to_string() {
let test = |f: f64, s: &str| {
assert_eq!(CpuStats::new(f).to_string(), s);
};
test(0.0, "00.00%");
test(1.5, "01.50%");
test(15.15, "15.15%");
test(150.15, "150.15%");
}
#[test]
// Display bytestats as a string, convert into correct data unit (Kb, MB, GB)
fn test_container_state_bytestats_to_string() {
let test = |u: u64, s: &str| {
assert_eq!(ByteStats::new(u).to_string(), s);
};
test(0, "0.00 kB");
test(150, "0.15 kB");
test(1500, "1.50 kB");
test(150_000, "150.00 kB");
test(1_500_000, "1.50 MB");
test(15_000_000, "15.00 MB");
test(150_000_000, "150.00 MB");
test(1_500_000_000, "1.50 GB");
test(15_000_000_000, "15.00 GB");
test(150_000_000_000, "150.00 GB");
}
#[test]
/// ContainerName as string truncated correctly
fn test_container_state_container_name_to_string() {
let result = ContainerName::from("name_01");
assert_eq!(result.to_string(), "name_01");
let result = ContainerName::from("name_01_name_01_name_01_name_01_");
assert_eq!(result.to_string(), "name_01_name_01_name_01_name_…");
let result = result.get();
assert_eq!(result, "name_01_name_01_name_01_name_01_");
}
#[test]
/// ContainerImage as string truncated correctly
fn test_container_state_container_image() {
let result = ContainerImage::from("name_01");
assert_eq!(result.to_string(), "name_01");
let result = ContainerImage::from("name_01_name_01_name_01_name_01_");
assert_eq!(result.to_string(), "name_01_name_01_name_01_name_…");
let result = result.get();
assert_eq!(result, "name_01_name_01_name_01_name_01_");
}
#[test]
/// Logs can only contain 1 entry per LogzTz
fn test_container_state_logz() {
let input = "2023-01-14T19:13:30.783138328Z Lorem ipsum dolor sit amet";
let tz = LogsTz::from(input);
let mut logs = Logs::default();
let line = log_sanitizer::remove_ansi(input);
logs.insert(ListItem::new(line.clone()), tz.clone());
logs.insert(ListItem::new(line.clone()), tz.clone());
logs.insert(ListItem::new(line), tz);
assert_eq!(logs.logs.items.len(), 1);
let input = "2023-01-15T19:13:30.783138328Z Lorem ipsum dolor sit amet";
let tz = LogsTz::from(input);
let line = log_sanitizer::remove_ansi(input);
logs.insert(ListItem::new(line.clone()), tz.clone());
logs.insert(ListItem::new(line.clone()), tz.clone());
logs.insert(ListItem::new(line), tz);
assert_eq!(logs.logs.items.len(), 2);
}
}
+47 -155
View File
@@ -54,9 +54,9 @@ impl fmt::Display for Header {
}
}
#[cfg(not(debug_assertions))]
/// Global app_state, stored in an Arc<Mutex>
#[derive(Debug, Clone)]
#[cfg(not(test))]
pub struct AppData {
containers: StatefulList<ContainerItem>,
error: Option<AppError>,
@@ -64,31 +64,17 @@ pub struct AppData {
pub args: CliArgs,
}
#[cfg(debug_assertions)]
/// Global app_state, stored in an Arc<Mutex>
#[derive(Debug, Clone)]
#[cfg(test)]
pub struct AppData {
containers: StatefulList<ContainerItem>,
error: Option<AppError>,
sorted_by: Option<(Header, SortedOrder)>,
debug_string: String,
pub containers: StatefulList<ContainerItem>,
pub error: Option<AppError>,
pub sorted_by: Option<(Header, SortedOrder)>,
pub args: CliArgs,
}
impl AppData {
#[cfg(debug_assertions)]
pub fn get_debug_string(&self) -> &str {
&self.debug_string
}
#[cfg(debug_assertions)]
#[allow(unused)]
pub fn push_debug_string(&mut self, x: &str) {
self.debug_string.push_str(x);
}
/// Generate a default app_state
#[cfg(not(debug_assertions))]
pub fn default(args: CliArgs) -> Self {
Self {
args,
@@ -98,18 +84,6 @@ impl AppData {
}
}
/// Generate a default app_state
#[cfg(debug_assertions)]
pub fn default(args: CliArgs) -> Self {
Self {
args,
containers: StatefulList::new(vec![]),
error: None,
sorted_by: None,
debug_string: String::new(),
}
}
/// Current time as unix timestamp
#[allow(clippy::expect_used)]
fn get_systemtime() -> u64 {
@@ -380,7 +354,7 @@ impl AppData {
.map_or_else(String::new, |ci| {
let logs_len = ci.logs.get_state_title();
let prefix = if logs_len.is_empty() {
String::new()
String::from(" ")
} else {
format!("{logs_len} ")
};
@@ -690,70 +664,21 @@ impl AppData {
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::many_single_char_names, unused)]
#[allow(clippy::unwrap_used, clippy::many_single_char_names)]
mod tests {
use super::*;
use crate::tests::{gen_appdata, gen_container_summary, gen_containers};
use std::collections::VecDeque;
use super::*;
const fn gen_args() -> CliArgs {
CliArgs {
color: false,
docker_interval: 1000,
gui: true,
host: None,
in_container: false,
save_dir: None,
raw: false,
show_self: false,
timestamp: false,
use_cli: false,
}
}
fn gen_item(id: &ContainerId, index: usize) -> ContainerItem {
ContainerItem::new(
u64::try_from(index).unwrap(),
id.clone(),
format!("image_{index}"),
false,
format!("container_{index}"),
State::Running,
format!("Up {index} hour"),
)
}
fn gen_appdata(containers: &[ContainerItem]) -> AppData {
AppData {
containers: StatefulList::new(containers.to_vec()),
error: None,
sorted_by: None,
debug_string: String::new(),
args: gen_args(),
}
}
fn gen_containers() -> (Vec<ContainerId>, Vec<ContainerItem>) {
let ids = (1..=3)
.map(|i| ContainerId::from(format!("{i}").as_str()))
.collect::<Vec<_>>();
let containers = ids
.iter()
.enumerate()
.map(|(index, id)| gen_item(id, index + 1))
.collect::<Vec<_>>();
(ids, containers)
}
// ******** //
// ******* //
// Sort by //
// ******** //
// ******* //
#[test]
/// Sort by header: name
fn test_app_data_set_sort_by_header_name() {
let (ids, containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
@@ -780,7 +705,7 @@ mod tests {
#[test]
/// Sort by header: state
fn test_app_data_set_sort_by_header_state() {
let (ids, containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
@@ -817,7 +742,7 @@ mod tests {
#[test]
/// Sort by header: status
fn test_app_data_set_sort_by_header_status() {
let (ids, containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
@@ -853,7 +778,7 @@ mod tests {
#[test]
/// Sort by header: cpu
fn test_app_data_set_sort_by_header_cpu() {
let (ids, containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
@@ -890,7 +815,7 @@ mod tests {
#[test]
/// Sort by header: memory
fn test_app_data_set_sort_by_header_mem() {
let (ids, containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
@@ -927,7 +852,7 @@ mod tests {
#[test]
/// Sort by header: id
fn test_app_data_set_sort_by_header_id() {
let (ids, containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
@@ -954,7 +879,7 @@ mod tests {
#[test]
/// Sort by header: image
fn test_app_data_set_sort_by_header_image() {
let (ids, containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
@@ -981,7 +906,7 @@ mod tests {
#[test]
/// Sort by header: rx
fn test_app_data_set_sort_by_header_rx() {
let (ids, containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
@@ -1018,7 +943,7 @@ mod tests {
#[test]
/// Sort by header: tx
fn test_app_data_set_sort_by_header_tx() {
let (ids, containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
@@ -1055,7 +980,7 @@ mod tests {
#[test]
/// Sort by header when selected headers match
fn test_app_data_set_sort_by_header_match() {
let (ids, containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
@@ -1082,7 +1007,7 @@ mod tests {
#[test]
/// reset sorted
fn test_app_data_reset_sorted() {
let (ids, containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
@@ -1121,18 +1046,18 @@ mod tests {
#[test]
/// Get len of current containers vec
fn test_app_data_get_container_len() {
let (ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
let (_ids, containers) = gen_containers();
let app_data = gen_appdata(&containers);
assert_eq!(app_data.get_container_len(), 3);
}
#[test]
/// Select the first container
fn test_app_data_containers_start() {
let (ids, containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
/// No container selected
// No container selected
let result = app_data.get_container_state();
assert_eq!(result.selected(), None);
assert_eq!(result.offset(), 0);
@@ -1174,7 +1099,7 @@ mod tests {
/// advance container list state by one
/// get get_selected_container_id() & get_selected_container_id_state_name() return valid Some data
fn test_app_data_containers_next() {
let (ids, containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
// Advance list state by 1
@@ -1202,7 +1127,7 @@ mod tests {
/// advance container list state to the end
/// get get_selected_container_id() & get_selected_container_id_state_name() return valid Some data
fn test_app_data_containers_end() {
let (ids, containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
app_data.containers_end();
@@ -1240,7 +1165,7 @@ mod tests {
#[test]
/// go to previous container
fn test_app_data_containers_prev() {
let (ids, containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
app_data.containers_end();
@@ -1253,7 +1178,7 @@ mod tests {
#[test]
// Get the currently selected container
fn test_app_data_get_selected_container() {
let (ids, mut containers) = gen_containers();
let (_ids, mut containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
let result = app_data.get_selected_container();
@@ -1265,7 +1190,7 @@ mod tests {
let result = app_data.get_selected_container();
assert_eq!(result, Some(&containers[1]));
/// As above, but now as mut
// As above, but now as mut
let result = app_data.get_mut_selected_container();
assert_eq!(result, Some(&mut containers[1]));
}
@@ -1273,7 +1198,7 @@ mod tests {
#[test]
// Get mut container by id
fn test_app_data_get_container_by_id() {
let (ids, mut containers) = gen_containers();
let (_ids, mut containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
let result = app_data.get_container_by_id(&ContainerId::from("2"));
@@ -1283,7 +1208,7 @@ mod tests {
#[test]
// Get just the containers name by id
fn test_app_data_get_container_name_by_id() {
let (ids, mut containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
let result = app_data.get_container_name_by_id(&ContainerId::from("2"));
@@ -1293,7 +1218,7 @@ mod tests {
#[test]
// Get the id of the currently selected container
fn test_app_data_get_selected_container_id() {
let (ids, mut containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
app_data.containers_end();
@@ -1303,7 +1228,7 @@ mod tests {
#[test]
fn test_app_data_get_selected_container_id_state_name() {
let (ids, mut containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
app_data.containers_end();
@@ -1325,7 +1250,7 @@ mod tests {
#[test]
/// Docker commands returned correctly
fn test_app_data_selected_docker_command() {
let (ids, mut containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
// No commands when no container selected
@@ -1343,7 +1268,7 @@ mod tests {
#[test]
/// Docker command next works
fn test_app_data_selected_docker_command_next() {
let (ids, mut containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
app_data.containers_start();
app_data.docker_controls_start();
@@ -1356,7 +1281,7 @@ mod tests {
#[test]
/// Dockercommand end works, and next has no effect when at end
fn test_app_data_selected_docker_command_end() {
let (ids, mut containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
app_data.containers_start();
app_data.docker_controls_end();
@@ -1364,7 +1289,7 @@ mod tests {
let result = app_data.selected_docker_controls();
assert_eq!(result, Some(DockerControls::Delete));
/// Next has no effect when at end
// Next has no effect when at end
app_data.docker_controls_next();
let result = app_data.selected_docker_controls();
assert_eq!(result, Some(DockerControls::Delete));
@@ -1373,7 +1298,7 @@ mod tests {
#[test]
/// Docker commands previous works, and has no effect when at start
fn test_app_data_selected_docker_command_previous() {
let (ids, mut containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
app_data.containers_start();
app_data.docker_controls_end();
@@ -1382,7 +1307,7 @@ mod tests {
let result = app_data.selected_docker_controls();
assert_eq!(result, Some(DockerControls::Stop));
/// previous has no effect when at start
// previous has no effect when at start
app_data.docker_controls_start();
app_data.docker_controls_previous();
let result = app_data.selected_docker_controls();
@@ -1696,7 +1621,7 @@ mod tests {
#[test]
/// Chart data returned correctly
fn test_app_data_get_chart_data() {
let (ids, containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
let result = app_data.get_chart_data();
@@ -1734,8 +1659,8 @@ mod tests {
#[test]
/// Header widths return correctly
fn test_app_data_get_width() {
let (ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
let (_ids, containers) = gen_containers();
let app_data = gen_appdata(&containers);
let result = app_data.get_width();
let expected = Columns {
@@ -1780,45 +1705,12 @@ mod tests {
#[test]
/// Update stats functioning
fn test_app_data_update_containers() {
let (ids, containers) = gen_containers();
let (_ids, containers) = gen_containers();
let mut app_data = gen_appdata(&containers);
let result_pre = app_data.get_container_items().clone();
let mut input = vec![
ContainerSummary {
id: Some("1".to_owned()),
names: Some(vec!["container_1".to_owned()]),
image: Some("image_1".to_owned()),
image_id: Some("1".to_owned()),
command: None,
created: Some(1),
ports: None,
size_rw: None,
size_root_fs: None,
labels: None,
state: Some("paused".to_owned()),
status: Some("Up 1 hour".to_owned()),
host_config: None,
network_settings: None,
mounts: None,
},
ContainerSummary {
id: Some("2".to_owned()),
names: Some(vec!["container_2".to_owned()]),
image: Some("image_2".to_owned()),
image_id: Some("2".to_owned()),
command: None,
created: Some(2),
ports: None,
size_rw: None,
size_root_fs: None,
labels: None,
state: Some("dead".to_owned()),
status: Some("Up 2 hour".to_owned()),
host_config: None,
network_settings: None,
mounts: None,
},
gen_container_summary(1, "paused"),
gen_container_summary(2, "dead"),
];
app_data.update_containers(&mut input);
+145 -3
View File
@@ -71,6 +71,7 @@ pub struct DockerData {
impl DockerData {
/// Use docker stats to calculate current cpu usage
#[allow(clippy::cast_precision_loss)]
// FIX: this can overflow
fn calculate_usage(stats: &Stats) -> f64 {
let mut cpu_percentage = 0.0;
let previous_cpu = stats.precpu_stats.cpu_usage.total_usage;
@@ -162,7 +163,6 @@ impl DockerData {
/// Update all stats, spawn each container into own tokio::spawn thread
fn update_all_container_stats(&mut self, all_ids: &[(State, ContainerId)]) {
for (state, id) in all_ids {
// let init = self.init.as_ref().map_or_else(|| None, |x| Some((Arc::clone(x), all_ids.len())));
let docker = Arc::clone(&self.docker);
let app_data = Arc::clone(&self.app_data);
let spawns = Arc::clone(&self.spawns);
@@ -436,7 +436,7 @@ impl DockerData {
}
/// Send an update message every x ms, where x is the args.docker_interval
fn croner(args: &CliArgs, docker_tx: Sender<DockerMessage>) {
fn scheduler(args: &CliArgs, docker_tx: Sender<DockerMessage>) {
let update_duration = std::time::Duration::from_millis(u64::from(args.docker_interval));
let mut now = std::time::Instant::now();
tokio::spawn(async move {
@@ -472,10 +472,152 @@ impl DockerData {
spawns: Arc::new(Mutex::new(HashMap::new())),
};
inner.initialise_container_data().await;
Self::croner(&args, docker_tx);
Self::scheduler(&args, docker_tx);
inner.message_handler().await;
}
}
}
// tests, use redis-test container, check logs exists, and selector of logs, and that it increases, and matches end, when you run restart on the docker containers
#[cfg(test)]
mod tests {
use bollard::container::{
BlkioStats, CPUStats, CPUUsage, MemoryStats, PidsStats, StorageStats, ThrottlingData,
};
use super::*;
#[allow(clippy::too_many_lines)]
fn gen_stats(x: u64, y: u64) -> Stats {
Stats {
read: String::new(),
preread: String::new(),
num_procs: 0,
pids_stats: PidsStats {
current: None,
limit: None,
},
network: None,
networks: None,
memory_stats: MemoryStats {
stats: None,
max_usage: None,
usage: None,
failcnt: None,
limit: None,
commit: None,
commit_peak: None,
commitbytes: None,
commitpeakbytes: None,
privateworkingset: None,
},
blkio_stats: BlkioStats {
io_service_bytes_recursive: None,
io_serviced_recursive: None,
io_queue_recursive: None,
io_service_time_recursive: None,
io_wait_time_recursive: None,
io_merged_recursive: None,
io_time_recursive: None,
sectors_recursive: None,
},
cpu_stats: CPUStats {
cpu_usage: CPUUsage {
percpu_usage: Some(vec![
291_593_800,
182_192_900,
195_048_700,
23_032_300,
132_928_700,
235_555_600,
120_225_700,
175_752_000,
213_060_300,
95_321_600,
226_821_000,
0,
109_151_300,
0,
86_240_200,
1_884_400,
59_077_300,
23_224_900,
95_386_300,
144_987_400,
]),
total_usage: 250_000_000,
usage_in_usermode: 1_020_000_000,
usage_in_kernelmode: 1_030_000_000,
},
system_cpu_usage: Some(x),
online_cpus: Some(1),
throttling_data: ThrottlingData {
periods: 0,
throttled_periods: 0,
throttled_time: 0,
},
},
precpu_stats: CPUStats {
cpu_usage: CPUUsage {
percpu_usage: Some(vec![
291_593_800,
182_192_900,
195_048_700,
23_032_300,
132_928_700,
235_555_600,
120_225_700,
175_752_000,
213_060_300,
95_321_600,
226_821_000,
0,
109_151_300,
0,
86_240_200,
1_884_400,
59_077_300,
23_224_900,
93_831_100,
144_987_400,
]),
total_usage: 200_000_000,
usage_in_usermode: 1_020_000_000,
usage_in_kernelmode: 1_020_000_000,
},
system_cpu_usage: Some(y),
online_cpus: Some(1),
throttling_data: ThrottlingData {
periods: 0,
throttled_periods: 0,
throttled_time: 0,
},
},
storage_stats: StorageStats {
read_count_normalized: None,
read_size_bytes: None,
write_count_normalized: None,
write_size_bytes: None,
},
name: "/container_1".to_owned(),
id: "1".to_owned(),
}
}
#[test]
#[allow(clippy::float_cmp)]
/// Test the stats calculator, had to cheat here to get round input/outputs
fn test_calculate_usage_no_previous_cpu() {
let stats = gen_stats(1_000_000_000, 900_000_000);
let result = DockerData::calculate_usage(&stats);
assert_eq!(result, 50.0);
let stats = gen_stats(1_000_000_000, 800_000_000);
let result = DockerData::calculate_usage(&stats);
assert_eq!(result, 25.0);
let stats = gen_stats(1_000_000_000, 750_000_000);
let result = DockerData::calculate_usage(&stats);
assert_eq!(result, 20.00);
}
}
+79
View File
@@ -164,3 +164,82 @@ async fn main() {
}
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::many_single_char_names, unused)]
mod tests {
use bollard::service::ContainerSummary;
use crate::{
app_data::{AppData, ContainerId, ContainerItem, State, StatefulList},
parse_args::CliArgs,
};
pub const fn gen_args() -> CliArgs {
CliArgs {
color: false,
docker_interval: 1000,
gui: true,
host: None,
in_container: false,
save_dir: None,
raw: false,
show_self: false,
timestamp: false,
use_cli: false,
}
}
pub fn gen_item(id: &ContainerId, index: usize) -> ContainerItem {
ContainerItem::new(
u64::try_from(index).unwrap(),
id.clone(),
format!("image_{index}"),
false,
format!("container_{index}"),
State::Running,
format!("Up {index} hour"),
)
}
pub fn gen_appdata(containers: &[ContainerItem]) -> AppData {
AppData {
containers: StatefulList::new(containers.to_vec()),
error: None,
sorted_by: None,
args: gen_args(),
}
}
pub fn gen_containers() -> (Vec<ContainerId>, Vec<ContainerItem>) {
let ids = (1..=3)
.map(|i| ContainerId::from(format!("{i}").as_str()))
.collect::<Vec<_>>();
let containers = ids
.iter()
.enumerate()
.map(|(index, id)| gen_item(id, index + 1))
.collect::<Vec<_>>();
(ids, containers)
}
pub fn gen_container_summary(index: usize, state: &str) -> ContainerSummary {
ContainerSummary {
id: Some(format!("{index}")),
names: Some(vec![format!("container_{}", index)]),
image: Some(format!("image_{index}")),
image_id: Some(format!("{index}")),
command: None,
created: Some(i64::try_from(index).unwrap()),
ports: None,
size_rw: None,
size_root_fs: None,
labels: None,
state: Some(state.to_owned()),
status: Some(format!("Up {index} hour")),
host_config: None,
network_settings: None,
mounts: None,
}
}
}
+76
View File
@@ -72,3 +72,79 @@ pub mod log_sanitizer {
}
}
}
#[cfg(test)]
mod tests {
use ratatui::{
style::{Color, Style},
text::{Line, Span},
};
use super::log_sanitizer;
// This spells out "oxker", with each char having a foreground and background colour
const INPUT: &str = "\x1b[31;47mo\x1b[32;40mx\x1b[33;41mk\x1b[34;42me\x1b[35;43mr\x1b[0m";
#[test]
/// Return test raw, as in show escape codes
fn color_match_raw() {
let result = log_sanitizer::raw(INPUT);
let expected = vec![Line {
spans: [Span {
content: std::borrow::Cow::Borrowed(
"\x1b[31;47mo\x1b[32;40mx\x1b[33;41mk\x1b[34;42me\x1b[35;43mr\x1b[0m",
),
style: Style::default(),
}]
.to_vec(),
alignment: None,
}];
assert_eq!(result, expected);
}
#[test]
// Use the escape codes to colorize the text
fn color_match_colorize() {
let result = log_sanitizer::colorize_logs(INPUT);
let expected = vec![Line {
spans: vec![
Span {
content: std::borrow::Cow::Borrowed("o"),
style: Style::default().fg(Color::Red).bg(Color::White),
},
Span {
content: std::borrow::Cow::Borrowed("x"),
style: Style::default().fg(Color::Green).bg(Color::Black),
},
Span {
content: std::borrow::Cow::Borrowed("k"),
style: Style::default().fg(Color::Yellow).bg(Color::Red),
},
Span {
content: std::borrow::Cow::Borrowed("e"),
style: Style::default().fg(Color::Blue).bg(Color::Green),
},
Span {
content: std::borrow::Cow::Borrowed("r"),
style: Style::default().fg(Color::Magenta).bg(Color::Yellow),
},
],
alignment: None,
}];
assert_eq!(result, expected);
}
#[test]
// Remove all escape ansi codes from given input
fn color_match_remove_ansi() {
let result = log_sanitizer::remove_ansi(INPUT);
let expected = vec![Line {
spans: vec![Span {
content: std::borrow::Cow::Borrowed("oxker"),
style: Style::default(),
}],
alignment: None,
}];
assert_eq!(result, expected);
}
}
+1747 -92
View File
File diff suppressed because it is too large Load Diff
+7 -28
View File
@@ -217,22 +217,6 @@ impl Ui {
}
}
#[cfg(not(debug_assertions))]
fn get_wholelayout(f: &Frame) -> std::rc::Rc<[ratatui::layout::Rect]> {
Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Min(1), Constraint::Min(100)].as_ref())
.split(f.size())
}
#[cfg(debug_assertions)]
fn get_wholelayout(f: &Frame) -> std::rc::Rc<[ratatui::layout::Rect]> {
Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Min(1), Constraint::Min(1), Constraint::Min(100)].as_ref())
.split(f.size())
}
/// Frequent data required by multiple framde drawing functions, can reduce mutex reads by placing it all in here
#[derive(Debug)]
pub struct FrameData {
@@ -279,21 +263,16 @@ impl From<(MutexGuard<'_, AppData>, MutexGuard<'_, GuiState>)> for FrameData {
fn draw_frame(f: &mut Frame, app_data: &Arc<Mutex<AppData>>, gui_state: &Arc<Mutex<GuiState>>) {
let fd = FrameData::from((app_data.lock(), gui_state.lock()));
let whole_layout = get_wholelayout(f);
#[cfg(debug_assertions)]
draw_blocks::debug_bar(whole_layout[0], f, app_data.lock().get_debug_string());
#[cfg(debug_assertions)]
let whole_layout_split = (1, 2);
#[cfg(not(debug_assertions))]
let whole_layout_split = (0, 1);
let whole_layout = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Min(1), Constraint::Min(100)].as_ref())
.split(f.size());
// Split into 3, containers+controls, logs, then graphs
let upper_main = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Max(fd.height), Constraint::Percentage(50)].as_ref())
.split(whole_layout[whole_layout_split.1]);
.split(whole_layout[1]);
let top_split = if fd.has_containers {
vec![Constraint::Percentage(90), Constraint::Percentage(10)]
@@ -318,11 +297,11 @@ fn draw_frame(f: &mut Frame, app_data: &Arc<Mutex<AppData>>, gui_state: &Arc<Mut
.constraints(lower_split)
.split(upper_main[1]);
draw_blocks::containers(app_data, top_panel[0], f, &fd, gui_state, &fd.columns);
draw_blocks::containers(app_data, top_panel[0], f, &fd, gui_state);
draw_blocks::logs(app_data, lower_main[0], f, &fd, gui_state);
draw_blocks::heading_bar(whole_layout[whole_layout_split.0], f, &fd, gui_state);
draw_blocks::heading_bar(whole_layout[0], f, &fd, gui_state);
if let Some(id) = fd.delete_confirm.as_ref() {
app_data.lock().get_container_name_by_id(id).map_or_else(