Skip to content

Commit

Permalink
Added CRD data extraction and output condenser
Browse files Browse the repository at this point in the history
  • Loading branch information
cartrius-a committed Aug 7, 2024
1 parent 7f6b52b commit 2661a16
Show file tree
Hide file tree
Showing 2 changed files with 257 additions and 36 deletions.
2 changes: 1 addition & 1 deletion model/src/test_manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pub use error::{Error, Result};
pub use manager::{read_manifest, TestManager};
use serde::{Deserialize, Serialize};
use serde_plain::derive_fromstr_from_deserialize;
pub use status::{StatusColumn, StatusSnapshot};
pub use status::{crd_state, crd_type, crd_results, ResultType, StatusColumn, StatusSnapshot};
use std::collections::HashMap;

mod delete;
Expand Down
291 changes: 256 additions & 35 deletions model/src/test_manager/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,6 @@ use tabled::settings::{
};
use tabled::Table;

#[derive(Clone)]
pub struct StatusColumn {
header: String,
// If the Vec contains more than 1 value, each value will occupy a single box stacked
// vertically. If no value should be printed an empty Vec should be returned.
values: fn(&Crd) -> Vec<String>,
alignment: TextAlignment,
width: Option<usize>,
}

impl std::fmt::Debug for StatusColumn {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AdditionalColumn")
Expand All @@ -32,10 +22,22 @@ impl std::fmt::Debug for StatusColumn {
}
}

#[derive(Clone)]
pub struct StatusColumn {
header: String,
// If the Vec contains more than 1 value, each value will occupy a single box stacked
// vertically. If no value should be printed an empty Vec should be returned.
values: fn(&Crd) -> Vec<String>,
condensed_values: Option<fn(&[Crd]) -> Vec<String>>,
alignment: TextAlignment,
width: Option<usize>,
}

impl Default for StatusColumn {
fn default() -> Self {
Self {
values: |_| Default::default(),
condensed_values: None,
header: Default::default(),
alignment: Default::default(),
width: Default::default(),
Expand Down Expand Up @@ -142,6 +144,96 @@ impl StatusSnapshot {
self.columns = columns;
self
}

pub fn use_crds(&self) -> &Vec<Crd> {
&self.crds
}

// Extracts data from one CRD
pub fn extract_crd_data(crd: &Crd) -> Vec<Vec<String>> {
let mut crd_data = Vec::new();

crd_data.push(crd_type(crd));
crd_data.push(crd.labels().get("testsys/type").cloned().into_iter().collect());
crd_data.push(crd.labels().get("testsys/cluster").cloned().into_iter().collect());
crd_data.push(crd.labels().get("testsys/arch").cloned().into_iter().collect());
crd_data.push(crd.labels().get("testsys/variant").cloned().into_iter().collect());
crd_data.push(crd_state(crd));
crd_data.push(crd_results(crd, ResultType::Passed));
crd_data.push(crd_results(crd, ResultType::Failed));
crd_data.push(crd_results(crd, ResultType::Skipped));
crd_data
}

pub fn extract_full_crd_data(&self) -> Vec<Vec<String>> {
self.crds
.iter()
.map(|crd| {
Self::extract_crd_data(crd)
.into_iter()
.flat_map(|v| if v.is_empty() { vec![String::from("")] } else { v })
.collect()
})
.collect()
}

// Uses the full CRD dataset and condenses the output if a 5-stage migration test fully passes
pub fn condense_crd_data(&self) -> Vec<Vec<String>> {
let full_crd_data = self.extract_full_crd_data();
let mut result_data: Vec<Vec<String>> = vec![vec![]; 9];
for i in 0..full_crd_data.len() {
let curr_arch = &full_crd_data[i][3];
let curr_variant = &full_crd_data[i][4];
let curr_cluster = &full_crd_data[i][2];
if full_crd_data[i][1] != "migration" {
Self::add_row(&mut result_data, &full_crd_data, i);
} else if i + 4 < full_crd_data.len()
&& full_crd_data[i + 4][1] == "migration"
&& (*curr_arch == full_crd_data[i + 4][3])
&& (*curr_variant == full_crd_data[i + 4][4])
&& (*curr_cluster == full_crd_data[i + 4][2]) {
if !full_crd_data[i + 4][6].is_empty()
&& full_crd_data[i + 4][6].parse::<i32>().unwrap_or(0) > 0
&& full_crd_data[i + 4][7].parse::<i32>().unwrap_or(0) == 0 {
let sum_passed: String = (0..=4)
.map(|j| full_crd_data[i + j][6].parse::<i32>().unwrap_or(0))
.sum::<i32>()
.to_string();
let sum_skipped: String = (0..=4)
.map(|j| full_crd_data[i + j][8].parse::<i32>().unwrap_or(0))
.sum::<i32>()
.to_string();
Self::add_row_with_sums(&mut result_data, &full_crd_data, i, sum_passed, sum_skipped);
} else {
for j in 0..=4 {
Self::add_row(&mut result_data, &full_crd_data, i + j);
}
}
}
}
result_data
}

fn add_row(result_data: &mut Vec<Vec<String>>, full_crd_data: &[Vec<String>], i: usize) {
if full_crd_data[i][0] == "Resource" {
for j in 0..6 {
result_data[j].push(full_crd_data[i][j].clone());
}
} else {
for j in 0..9 {
result_data[j].push(full_crd_data[i][j].clone());
}
}
}

fn add_row_with_sums(result_data: &mut Vec<Vec<String>>, full_crd_data: &[Vec<String>], i: usize, sum_passed: String, sum_skipped: String) {
for j in 0..6 {
result_data[j].push(full_crd_data[i][j].clone());
}
result_data[6].push(sum_passed);
result_data[7].push(full_crd_data[i][7].clone());
result_data[8].push(sum_skipped);
}
}

impl From<&StatusSnapshot> for Table {
Expand All @@ -151,26 +243,42 @@ impl From<&StatusSnapshot> for Table {
.iter()
.map(|column| vec![column.header.to_string()])
.collect();
let status_data = snapshot
.crds
.iter()
.map(|crd| snapshot.columns.iter().map(|column| (column.values)(crd)))
.fold(headers, |data, x| {
let mut row_count = 0;
// Determine how many rows this CRD will take in the status table.
for col in x.clone() {
row_count = max(row_count, col.len());
}
data.into_iter()
.zip(x)
.map(|(mut data_col, mut crd_data)| {
// Extend each Vec from this CRD to have the same number of rows.
crd_data.resize(row_count, "".into());
data_col.extend(crd_data);
data_col
})
.collect()
});

let status_data = if snapshot.columns.iter().any(|col| col.condensed_values.is_some()) {
headers.into_iter().zip(
snapshot.columns.iter().map(|column| {
if let Some(condensed_fn) = column.condensed_values {
condensed_fn(&snapshot.crds)
} else {
snapshot.crds.iter().flat_map(|crd| (column.values)(crd)).collect()
}
})
).map(|(mut header, data)| {
header.extend(data);
header
}).collect()
} else {
snapshot
.crds
.iter()
.map(|crd| snapshot.columns.iter().map(|column| (column.values)(crd)))
.fold(headers, |data, x| {
let mut row_count = 0;
// Determine how many rows this CRD will take in the status table.
for col in x.clone() {
row_count = max(row_count, col.len());
}
data.into_iter()
.zip(x)
.map(|(mut data_col, mut crd_data)| {
// Extend each Vec from this CRD to have the same number of rows.
crd_data.resize(row_count, "".into());
data_col.extend(crd_data);
data_col
})
.collect()
})
};

let mut table = Builder::from_iter(status_data)
.index()
Expand Down Expand Up @@ -224,6 +332,7 @@ impl Display for StatusSnapshot {

// The following contains several common status columns for users.
impl StatusColumn {

pub fn name() -> StatusColumn {
StatusColumn {
header: "NAME".to_string(),
Expand Down Expand Up @@ -252,6 +361,7 @@ impl StatusColumn {
StatusColumn {
header: "PASSED".to_string(),
values: |crd| crd_results(crd, ResultType::Passed),
condensed_values: None,
alignment: TextAlignment::Right,
width: Some(6),
}
Expand All @@ -261,6 +371,7 @@ impl StatusColumn {
StatusColumn {
header: "FAILED".to_string(),
values: |crd| crd_results(crd, ResultType::Failed),
condensed_values: None,
alignment: TextAlignment::Right,
width: Some(6),
}
Expand All @@ -270,6 +381,7 @@ impl StatusColumn {
StatusColumn {
header: "SKIPPED".to_string(),
values: |crd| crd_results(crd, ResultType::Skipped),
condensed_values: None,
alignment: TextAlignment::Right,
width: Some(7),
}
Expand All @@ -279,6 +391,7 @@ impl StatusColumn {
StatusColumn {
header: "LAST UPDATE".to_string(),
values: crd_time,
condensed_values: None,
alignment: TextAlignment::Left,
width: Some(20),
}
Expand All @@ -291,6 +404,114 @@ impl StatusColumn {
..Default::default()
}
}

pub fn condensed_crd_type() -> StatusColumn {
StatusColumn {
header: "CRD-TYPE".to_string(),
values: |_| vec![], // This won't be used in condensed view
condensed_values: Some(|crds| {
let snapshot = StatusSnapshot::new(crds.to_vec());
snapshot.condense_crd_data()[0].clone()
}),
..Default::default()
}
}

pub fn condensed_test_type() -> StatusColumn {
StatusColumn {
header: "TEST-TYPE".to_string(),
values: |_| vec![], // This won't be used in condensed view
condensed_values: Some(|crds| {
let snapshot = StatusSnapshot::new(crds.to_vec());
snapshot.condense_crd_data()[1].clone()
}),
..Default::default()
}
}

pub fn condensed_cluster() -> StatusColumn {
StatusColumn {
header: "CLUSTER".to_string(),
values: |_| vec![], // This won't be used in condensed view
condensed_values: Some(|crds| {
let snapshot = StatusSnapshot::new(crds.to_vec());
snapshot.condense_crd_data()[2].clone()
}),
..Default::default()
}
}

pub fn condensed_arch() -> StatusColumn {
StatusColumn {
header: "ARCH".to_string(),
values: |_| vec![], // This won't be used in condensed view
condensed_values: Some(|crds| {
let snapshot = StatusSnapshot::new(crds.to_vec());
snapshot.condense_crd_data()[3].clone()
}),
..Default::default()
}
}

pub fn condensed_variant() -> StatusColumn {
StatusColumn {
header: "VARIANT".to_string(),
values: |_| vec![], // This won't be used in condensed view
condensed_values: Some(|crds| {
let snapshot = StatusSnapshot::new(crds.to_vec());
snapshot.condense_crd_data()[4].clone()
}),
..Default::default()
}
}

pub fn condensed_status() -> StatusColumn {
StatusColumn {
header: "STATUS".to_string(),
values: |_| vec![], // This won't be used in condensed view
condensed_values: Some(|crds| {
let snapshot = StatusSnapshot::new(crds.to_vec());
snapshot.condense_crd_data()[5].clone()
}),
..Default::default()
}
}

pub fn condensed_passed() -> StatusColumn {
StatusColumn {
header: "PASSED".to_string(),
values: |_| vec![], // This won't be used in condensed view
condensed_values: Some(|crds| {
let snapshot = StatusSnapshot::new(crds.to_vec());
snapshot.condense_crd_data()[6].clone()
}),
..Default::default()
}
}

pub fn condensed_failed() -> StatusColumn {
StatusColumn {
header: "FAILED".to_string(),
values: |_| vec![], // This won't be used in condensed view
condensed_values: Some(|crds| {
let snapshot = StatusSnapshot::new(crds.to_vec());
snapshot.condense_crd_data()[7].clone()
}),
..Default::default()
}
}

pub fn condensed_skipped() -> StatusColumn {
StatusColumn {
header: "SKIPPED".to_string(),
values: |_| vec![], // This won't be used in condensed view
condensed_values: Some(|crds| {
let snapshot = StatusSnapshot::new(crds.to_vec());
snapshot.condense_crd_data()[8].clone()
}),
..Default::default()
}
}
}

/// Determine the time of the last update to the CRD
Expand All @@ -309,15 +530,15 @@ fn crd_time(crd: &Crd) -> Vec<String> {
}

/// Determine the type of the CRD
fn crd_type(crd: &Crd) -> Vec<String> {
pub fn crd_type(crd: &Crd) -> Vec<String> {
match crd {
Crd::Test(_) => vec!["Test".to_string()],
Crd::Resource(_) => vec!["Resource".to_string()],
}
}

/// Determine the state of the CRD
fn crd_state(crd: &Crd) -> Vec<String> {
pub fn crd_state(crd: &Crd) -> Vec<String> {
match crd {
Crd::Test(test) => vec![test.test_user_state().to_string()],
Crd::Resource(resource) => {
Expand All @@ -336,14 +557,14 @@ fn crd_state(crd: &Crd) -> Vec<String> {
}
}

enum ResultType {
pub enum ResultType {
Passed,
Failed,
Skipped,
}

/// Collect the
fn crd_results(crd: &Crd, res_type: ResultType) -> Vec<String> {
/// Collect the CRD results
pub fn crd_results(crd: &Crd, res_type: ResultType) -> Vec<String> {
match crd {
Crd::Resource(_) => Default::default(),
Crd::Test(test) => {
Expand Down

0 comments on commit 2661a16

Please sign in to comment.