summaryrefslogtreecommitdiffstats
path: root/src/test_helpers/assertions/assert_rendered_output.rs
blob: 923093253dab16ac235b0775f26888538d935369 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# SPDX-License-Identifier: GPL-3.0-or-later

# THIS IS NOT A COMPLETE Makefile
# IT IS INCLUDED BY ITS PARENT'S Makefile.am
# IT IS REQUIRED TO REFERENCE ALL FILES RELATIVE TO THE PARENT

# install these files
dist_charts_DATA       += apcupsd/apcupsd.chart.sh
dist_chartsconfig_DATA += apcupsd/apcupsd.conf

# do not install these files, but include them in the 
mod patterns;
mod render_style;
mod render_view_data;
mod render_view_line;

use std::fmt::{Debug, Formatter};

use bitflags::bitflags;
use itertools::Itertools;

pub(crate) use self::{
	patterns::{
		ActionPattern,
		AllPattern,
		AnyLinePattern,
		AnyPattern,
		ContainsPattern,
		EndsWithPattern,
		ExactPattern,
		LinePattern,
		NotPattern,
		StartsWithPattern,
	},
	render_style::render_style,
	render_view_data::render_view_data,
	render_view_line::render_view_line,
};
use crate::{
	test_helpers::shared::replace_invisibles,
	view::{ViewData, ViewLine},
};

bitflags! {
	/// Options for the `assert_rendered_output!` macro
	#[derive(Default, PartialEq, Eq, Debug, Clone, Copy)]
	pub(crate) struct AssertRenderOptions: u8 {
		/// Ignore trailing whitespace
		const INCLUDE_TRAILING_WHITESPACE = 0b0000_0001;
		/// Ignore pinned indicator
		const INCLUDE_PINNED = 0b0000_0010;
		/// Don't include style information
		const INCLUDE_STYLE = 0b0000_0100;
		/// Only render the body, in this mode {BODY} is also not rendered
		const BODY_ONLY = 0b0000_1000;
	}
}

#[allow(clippy::string_slice, clippy::panic)]
pub(crate) fn _assert_rendered_output(
	options: AssertRenderOptions,
	actual: &[String],
	expected_patterns: &[Box<dyn LinePattern>],
) {
	let mut mismatch = false;
	let mut error_output = vec![
		String::from("\nUnexpected output!"),
		String::from("--- Expected"),
		String::from("+++ Actual"),
		String::from("=========="),
	];

	for (expected_pattern, output_line) in expected_patterns.iter().zip(actual.iter()) {
		let output = if options.contains(AssertRenderOptions::INCLUDE_TRAILING_WHITESPACE) {
			output_line.as_str()
		}
		else {
			output_line.trim_end()
		};

		if expected_pattern.matches(output) {
			error_output.push(format!(" {}", expected_pattern.expected()));
		}
		else {
			mismatch = true;
			error_output.push(format!("-{}", expected_pattern.expected()));
			error_output.push(format!("+{}", expected_pattern.actual(output)));
		}
	}

	match expected_patterns.len() {
		a if a > actual.len() => {
			mismatch = true;
			for expected_pattern in expected_patterns.iter().skip(actual.len()) {
				error_output.push(format!("-{}", expected_pattern.expected().as_str()));
			}
		},
		a if a < actual.len() => {
			mismatch = true;
			for line in actual.iter().skip(expected_patterns.len()) {
				error_output.push(format!("+{}", replace_invisibles(line)));
			}
		},
		_ => {},
	}

	if mismatch {
		error_output.push(String::from("==========\n"));
		panic!("{}", error_output.join("\n"));
	}
}

/// Assert the rendered output from a `ViewData`. Generally this function is not used directly,
/// instead use the `assert_rendered_output!` macro.
pub(crate) fn _assert_rendered_output_from_view_data(
	view_data: &ViewData,
	expected: &[Box<dyn LinePattern>],
	options: AssertRenderOptions,
	skip_start: Option<usize>,
	skip_end: Option<usize>,
) {
	let rendered = render_view_data(view_data, options);
	let mut length = rendered.len();
	let mut output_iter: Box<dyn Iterator<Item = String>> = Box::new(rendered.into_iter());

	if let Some(skip) = skip_start {
		length = length.saturating_sub(skip);
		output_iter = Box::new(output_iter.skip(skip));
	}

	if let Some(skip) = skip_end {
		output_iter = Box::new(output_iter.take(length.saturating_sub(skip)));
	}

	_assert_rendered_output(options, &output_iter.collect::<Vec<String>>(), expected);
}

/// Create an assertion on a line. For use in `assert_rendered_output` macro.
#[macro_export]
macro_rules! render_line {
	($line:expr) => {{
		$crate::test_helpers::assertions::assert_rendered_output::ExactPattern::new($line)
	}};
	(Line) => {{
		$crate::test_helpers::assertions::assert_rendered_output::AnyLinePattern::new()
	}};
	(StartsWith $line:expr) => {{
		$crate::test_helpers::assertions::assert_rendered_output::StartsWithPattern::new($line)
	}};
	(Not StartsWith $line:expr) => {{
		$crate::test_helpers::assertions::assert_rendered_output::NotPattern::new(
			Box::new(
				$crate::test_helpers::assertions::assert_rendered_output::StartsWithPattern::new($line)
			)
		)
	}};
	(EndsWith $line:expr) => {{
		$crate::test_helpers::assertions::assert_rendered_output::EndsWithPattern::new($line)
	}};
	(Not EndsWith $line:expr) => {{
		$crate::test_helpers::assertions::assert_rendered_output::NotPattern::new(
			Box::new($crate::test_helpers::assertions::assert_rendered_output::EndsWithPattern::new($line))
		)
	}};
	(Contains $line:expr) => {{
		$crate::test_helpers::assertions::assert_rendered_output::ContainsPattern::new($line)
	}};
	(Not Contains $line:expr) => {{
		$crate::test_helpers::assertions::assert_rendered_output::NotPattern::new(
			Box::new(
				$crate::test_helpers::assertions::assert_rendered_output::ContainsPattern::new($line)
			)
		)
	}};
	(Not $pattern:expr) => {{
		$crate::test_helpers::assertions::assert_rendered_output::NotPattern::new(Box::new($pattern))
	}};
	(All $($patterns:expr),*) => {{
		let patterns: Vec<Box<dyn LinePattern>> = vec![$( Box::new($patterns), )*];
		$crate::test_helpers::assertions::assert_rendered_output::AllPattern::new(patterns)
	}};
	(Any $($patterns:expr),*) => {{
		let patterns: Vec<Box<dyn LinePattern>> = vec![$( Box::new($patterns), )*];
		$crate::test_helpers::assertions::assert_rendered_output::AnyPattern::new(patterns)
	}};
}

#[macro_export]
macro_rules! action_line {
	(Break) => {{
		use $crate::test_helpers::assertions::assert_rendered_output::ActionPattern;
		ActionPattern::new_break(false)
	}};
	(Selected Break) => {{
		use $crate::test_helpers::assertions::assert_rendered_output::ActionPattern;
		ActionPattern::new_break(true)
	}};
	(Drop $hash:expr, $comment:expr) => {{
		use $crate::test_helpers::assertions::assert_rendered_output::ActionPattern;
		ActionPattern::new_drop($hash, $comment, false)
	}};
	(Selected Drop $hash:expr, $comment:expr) => {{
		use $crate::test_helpers::assertions::assert_rendered_output::ActionPattern;
		ActionPattern::new_drop($hash, $comment, true)
	}};
	(Edit $hash:expr, $comment:expr) => {{
		use $crate::test_helpers::assertions::assert_rendered_output::ActionPattern;
		ActionPattern::new_edit($hash, $comment, false)
	}};
	(Selected Edit $hash:expr, $comment:expr) => {{
		use $crate::test_helpers::assertions::assert_rendered_output::ActionPattern;
		ActionPattern::new_edit($hash, $comment, true)
	}};
	(Fixup $hash:expr, $comment:expr) => {{
		use $crate::test_helpers::assertions::assert_rendered_output::ActionPattern;
		ActionPattern::new_fixup($hash, $comment, false)
	}};
	(Selected Fixup $hash:expr, $comment:expr) => {{
		use $crate::test_helpers::assertions::assert_rendered_output::ActionPattern;
		ActionPattern::new_fixup($hash, $comment, true)
	}};
	(Pick $hash:expr, $comment:expr) => {{
		use $crate::test_helpers::assertions::assert_rendered_output::ActionPattern;
		ActionPattern::new_pick($hash, $comment, false)
	}};
	(Selected Pick $hash:expr, $comment:expr) => {{
		use $crate::test_helpers::assertions::assert_rendered_output::ActionPattern;
		ActionPattern::new_pick($hash, $comment, true)
	}};
	(Reword $hash:expr, $comment:expr) => {{
		use $crate::test_helpers::assertions::assert_rendered_output::ActionPattern;
		ActionPattern::new_reword($hash, $comment, false)
	}};
	(Selected Reword $hash:expr, $comment:expr) => {{
		use $crate::test_helpers::assertions::assert_rendered_output::ActionPattern;
		ActionPattern::new_reword($hash, $comment, true)
	}};
	(Squash $hash:expr, $comment:expr)