#![allow(dead_code)]
use embedded_charts::{
chart::traits::{Chart, ChartBuilder, ChartConfig},
data::{point::Point2D, series::StaticDataSeries, DataSeries},
error::{ChartError, ChartResult},
};
use embedded_graphics::{pixelcolor::Rgb565, prelude::*, primitives::Rectangle};
use super::{create_test_display, PerformanceMetrics, TestColors, TEST_VIEWPORT};
pub struct ChartTestSuite;
impl ChartTestSuite {
pub fn test_chart_rendering<T>(
chart: &T,
test_data: &[StaticDataSeries<Point2D, 256>],
) -> ChartResult<()>
where
T: Chart<Rgb565, Data = StaticDataSeries<Point2D, 256>, Config = ChartConfig<Rgb565>>,
{
let config = super::create_test_config();
for data in test_data {
let mut display = create_test_display();
chart.draw(data, &config, TEST_VIEWPORT, &mut display)?;
if !data.is_empty()
&& (display.affected_area().size.width == 0
|| display.affected_area().size.height == 0)
{
return Err(ChartError::RenderingError);
}
}
Ok(())
}
pub fn test_viewport_scaling<T>(
chart: &T,
data: &StaticDataSeries<Point2D, 256>,
) -> ChartResult<()>
where
T: Chart<Rgb565, Data = StaticDataSeries<Point2D, 256>, Config = ChartConfig<Rgb565>>,
{
let config = super::create_test_config();
let viewports = [
Rectangle::new(Point::zero(), Size::new(100, 100)), Rectangle::new(Point::zero(), Size::new(320, 240)), Rectangle::new(Point::zero(), Size::new(800, 600)), Rectangle::new(Point::zero(), Size::new(64, 48)), ];
for &viewport in &viewports {
let mut display = super::create_test_display();
chart.draw(data, &config, viewport, &mut display)?;
}
Ok(())
}
pub fn measure_chart_performance<T>(
chart: &T,
data: &StaticDataSeries<Point2D, 256>,
) -> ChartResult<PerformanceMetrics>
where
T: Chart<Rgb565, Data = StaticDataSeries<Point2D, 256>, Config = ChartConfig<Rgb565>>,
{
let config = super::create_test_config();
let mut display = create_test_display();
let start_time = 0; chart.draw(data, &config, TEST_VIEWPORT, &mut display)?;
let end_time = 1000;
let mut metrics = PerformanceMetrics::new();
metrics.render_time_ns = end_time - start_time;
metrics.draw_calls = 1;
Ok(metrics)
}
pub fn test_error_handling<T>(chart: &T) -> ChartResult<()>
where
T: Chart<Rgb565, Data = StaticDataSeries<Point2D, 256>, Config = ChartConfig<Rgb565>>,
{
let config = super::create_test_config();
let mut display = create_test_display();
let empty_data = StaticDataSeries::new();
let result = chart.draw(&empty_data, &config, TEST_VIEWPORT, &mut display);
match result {
Ok(_) => {} Err(ChartError::InsufficientData) => {} Err(other) => return Err(other), }
let zero_viewport = Rectangle::new(Point::zero(), Size::zero());
let mut valid_data = StaticDataSeries::new();
valid_data.push(Point2D::new(0.0, 0.0)).ok();
let result = chart.draw(&valid_data, &config, zero_viewport, &mut display);
match result {
Ok(_) | Err(ChartError::InvalidRange) => {}
Err(other) => return Err(other),
}
Ok(())
}
pub fn test_color_configurations<T>(
chart: &T,
data: &StaticDataSeries<Point2D, 256>,
) -> ChartResult<()>
where
T: Chart<Rgb565, Data = StaticDataSeries<Point2D, 256>, Config = ChartConfig<Rgb565>>,
{
let mut display = create_test_display();
let color_configs = [
ChartConfig {
title: None,
background_color: Some(TestColors::BACKGROUND),
margins: super::TEST_MARGINS,
grid_color: Some(TestColors::GRID),
show_grid: true,
},
ChartConfig {
title: None,
background_color: None, margins: super::TEST_MARGINS,
grid_color: Some(TestColors::PRIMARY),
show_grid: false,
},
];
for config in &color_configs {
chart.draw(data, config, TEST_VIEWPORT, &mut display)?;
}
Ok(())
}
pub fn test_deterministic_output<T>(
chart: &T,
data: &StaticDataSeries<Point2D, 256>,
) -> ChartResult<()>
where
T: Chart<Rgb565, Data = StaticDataSeries<Point2D, 256>, Config = ChartConfig<Rgb565>>,
{
let config = super::create_test_config();
let mut display1 = create_test_display();
let mut display2 = create_test_display();
chart.draw(data, &config, TEST_VIEWPORT, &mut display1)?;
chart.draw(data, &config, TEST_VIEWPORT, &mut display2)?;
if display1.affected_area() != display2.affected_area() {
return Err(ChartError::RenderingError);
}
Ok(())
}
}
#[cfg(feature = "line")]
pub mod line_chart_testing {
use super::*;
use embedded_charts::chart::line::{LineChart, MarkerShape, MarkerStyle};
pub struct LineChartTester;
impl LineChartTester {
pub fn test_marker_configurations(
data: &StaticDataSeries<Point2D, 256>,
) -> ChartResult<()> {
let marker_configs = [
Some(MarkerStyle {
shape: MarkerShape::Circle,
size: 4,
color: TestColors::PRIMARY,
visible: true,
}),
Some(MarkerStyle {
shape: MarkerShape::Square,
size: 6,
color: TestColors::SECONDARY,
visible: true,
}),
Some(MarkerStyle {
shape: MarkerShape::Diamond,
size: 8,
color: TestColors::ACCENT,
visible: true,
}),
None, ];
for marker_config in &marker_configs {
let chart = LineChart::builder()
.line_color(TestColors::PRIMARY)
.line_width(2)
.with_markers(marker_config.unwrap_or(MarkerStyle {
shape: MarkerShape::Circle,
size: 4,
color: TestColors::PRIMARY,
visible: false,
}))
.build()?;
ChartTestSuite::test_chart_rendering(&chart, &[data.clone()])?;
}
Ok(())
}
pub fn test_area_fill(data: &StaticDataSeries<Point2D, 256>) -> ChartResult<()> {
let chart = LineChart::builder()
.line_color(TestColors::PRIMARY)
.line_width(2)
.fill_area(TestColors::SECONDARY)
.build()?;
ChartTestSuite::test_chart_rendering(&chart, &[data.clone()])?;
Ok(())
}
pub fn test_smooth_curves(data: &StaticDataSeries<Point2D, 256>) -> ChartResult<()> {
let subdivisions = [2, 4, 8, 16];
for &subdivisions in &subdivisions {
let chart = LineChart::builder()
.line_color(TestColors::PRIMARY)
.line_width(2)
.smooth(true)
.smooth_subdivisions(subdivisions)
.build()?;
ChartTestSuite::test_chart_rendering(&chart, &[data.clone()])?;
}
Ok(())
}
pub fn test_line_widths(data: &StaticDataSeries<Point2D, 256>) -> ChartResult<()> {
let widths = [1, 2, 3, 5, 8];
for &width in &widths {
let chart = LineChart::builder()
.line_color(TestColors::PRIMARY)
.line_width(width)
.build()?;
ChartTestSuite::test_chart_rendering(&chart, &[data.clone()])?;
}
Ok(())
}
}
}
pub mod memory_testing {
use super::super::MemoryMetrics;
use super::*;
pub struct MemoryTester;
impl MemoryTester {
pub fn test_memory_bounds<T>(
chart: &T,
data: &StaticDataSeries<Point2D, 256>,
max_bytes: usize,
) -> ChartResult<()>
where
T: Chart<Rgb565, Data = StaticDataSeries<Point2D, 256>, Config = ChartConfig<Rgb565>>,
{
let config = super::super::create_test_config();
let mut display = create_test_display();
chart.draw(data, &config, TEST_VIEWPORT, &mut display)?;
let metrics = MemoryMetrics::new();
super::super::assertions::assert_memory_constraints(&metrics, max_bytes)?;
Ok(())
}
pub fn test_max_capacity<T>(chart: &T) -> ChartResult<()>
where
T: Chart<Rgb565, Data = StaticDataSeries<Point2D, 256>, Config = ChartConfig<Rgb565>>,
{
let mut max_data = StaticDataSeries::new();
for i in 0..256 {
max_data.push(Point2D::new(i as f32, (i % 100) as f32)).ok();
}
let config = super::super::create_test_config();
let mut display = create_test_display();
chart.draw(&max_data, &config, TEST_VIEWPORT, &mut display)?;
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "line")]
use super::super::data_generators;
#[test]
#[cfg(feature = "line")]
#[ignore = "MockDisplay has limitations with pixel overlap detection"]
fn test_chart_test_suite() {
use embedded_charts::chart::line::LineChart;
let chart = LineChart::new();
let data = data_generators::generate_test_data(super::super::TestDataPattern::Linear, 10);
let result = ChartTestSuite::test_chart_rendering(&chart, &[data]);
assert!(result.is_ok());
}
#[test]
fn test_performance_measurement() {
let metrics = PerformanceMetrics::new();
assert_eq!(metrics.render_time_ns, 0);
}
}