Skip to content

Commit

Permalink
Merge pull request #2 from Finomnis/increase_test_coverage
Browse files Browse the repository at this point in the history
Increase test coverage
  • Loading branch information
Finomnis authored Aug 27, 2022
2 parents 3b95870 + b8da385 commit f1b2dc6
Show file tree
Hide file tree
Showing 11 changed files with 506 additions and 10 deletions.
2 changes: 0 additions & 2 deletions TODO.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
Test coverage

Maybe later, if requested:
- Rotation (like in u8g2)
110 changes: 110 additions & 0 deletions src/content/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,113 @@ impl LineDimensionsIterator for ArgsLineDimensionsIterator<'_> {
Ok(self.dimensions_buffer[next_line - self.buffer_range.start].clone())
}
}

#[cfg(test)]
mod tests {
extern crate std;
use core::fmt::Arguments;
use std::vec::Vec;

use embedded_graphics_core::{prelude::Size, primitives::Rectangle};

use crate::fonts;

use super::*;

#[test]
fn for_each_char_produces_correct_values() {
let mut content = Vec::new();

format_args!("{}", "abc")
.for_each_char(|e| {
content.push(e);
Result::<(), &'static str>::Ok(())
})
.unwrap();

assert_eq!(content, ['a', 'b', 'c']);
}

#[test]
fn for_each_char_infallible_produces_correct_values() {
let mut content = Vec::new();

format_args!("{}", "abc").for_each_char_infallible(|e| {
content.push(e);
});

assert_eq!(content, ['a', 'b', 'c']);
}

#[test]
fn for_each_char_propagates_error() {
let result = format_args!("{}", "abc").for_each_char(|_| Err("Failed!"));

assert_eq!(result, Err("Failed!"));
}

#[test]
fn get_newline_count_provides_correct_value() {
assert_eq!(format_args!("{}", "a\nbc\n").get_newline_count(), 2);
assert_eq!(format_args!("{}", "a\nbc").get_newline_count(), 1);
assert_eq!(format_args!("{}", "").get_newline_count(), 0);
}

#[test]
fn line_dimensions_iter_provides_correct_values() {
// Nested function to deal with format_args!()'s weird lifetimes
fn run_test(args: Arguments<'_>) {
let font = FontReader::new::<fonts::u8g2_font_u8glib_4_tf>();
let mut dims = args.line_dimensions_iterator();

assert_eq!(
dims.next(&font).unwrap(),
RenderedDimensions {
advance: Point::new(4, 0),
bounding_box: Some(Rectangle::new(Point::new(0, -3), Size::new(3, 3)))
}
);
assert_eq!(
dims.next(&font).unwrap(),
RenderedDimensions {
advance: Point::new(7, 0),
bounding_box: Some(Rectangle::new(Point::new(0, -4), Size::new(6, 4)))
}
);
assert_eq!(dims.next(&font).unwrap(), RenderedDimensions::empty());
assert_eq!(dims.next(&font).unwrap(), RenderedDimensions::empty());
}

run_test(format_args!("{}", "a\nbc\n"));
}

#[test]
fn line_dimensions_iter_errors_on_glyph_not_found() {
// Nested function to deal with format_args!()'s weird lifetimes
fn run_test(args: Arguments<'_>) {
let font = FontReader::new::<fonts::u8g2_font_u8glib_4_tf>();
let mut dims = args.line_dimensions_iterator();

assert!(matches!(
dims.next(&font),
Err(LookupError::GlyphNotFound('☃'))
));
}

run_test(format_args!("{}", "☃"));
}

#[test]
fn line_dimensions_iter_creates_empty_array_when_out_of_range() {
// Nested function to deal with format_args!()'s weird lifetimes
fn run_test(args: Arguments<'_>) {
let font = FontReader::new::<fonts::u8g2_font_u8glib_4_tf>();
let mut dims = args.line_dimensions_iterator();

dims.regenerate_buffer(1000, &font).unwrap();
assert!(dims.buffer_range.is_empty());
}

run_test(format_args!("{}", "a"));
}
}
86 changes: 83 additions & 3 deletions src/content/character.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,96 @@ impl Content for char {
type LineDimensionsIter = CharLineDimensionsIterator;

fn line_dimensions_iterator(&self) -> CharLineDimensionsIterator {
CharLineDimensionsIterator { ch: *self }
CharLineDimensionsIterator { ch: Some(*self) }
}
}

pub struct CharLineDimensionsIterator {
ch: char,
ch: Option<char>,
}

impl LineDimensionsIterator for CharLineDimensionsIterator {
fn next(&mut self, font: &FontReader) -> Result<RenderedDimensions, LookupError> {
compute_glyph_dimensions(self.ch, Point::new(0, 0), font)
self.ch.take().map_or_else(
|| Ok(RenderedDimensions::empty()),
|ch| compute_glyph_dimensions(ch, Point::new(0, 0), font),
)
}
}

#[cfg(test)]
mod tests {
extern crate std;
use std::vec::Vec;

use embedded_graphics_core::{prelude::Size, primitives::Rectangle};

use crate::fonts;

use super::*;

#[test]
fn for_each_char_produces_correct_values() {
let mut content = Vec::new();

'a'.for_each_char(|e| {
content.push(e);
Result::<(), &'static str>::Ok(())
})
.unwrap();

assert_eq!(content, ['a']);
}

#[test]
fn for_each_char_infallible_produces_correct_values() {
let mut content = Vec::new();

'a'.for_each_char_infallible(|e| {
content.push(e);
});

assert_eq!(content, ['a']);
}

#[test]
fn for_each_char_propagates_error() {
let result = 'a'.for_each_char(|_| Err("Failed!"));

assert_eq!(result, Err("Failed!"));
}

#[test]
fn get_newline_count_provides_correct_value() {
assert_eq!('a'.get_newline_count(), 0);
}

#[test]
fn line_dimensions_iter_provides_correct_values() {
let font = FontReader::new::<fonts::u8g2_font_u8glib_4_tf>();
let ch = 'a';
let mut dims = ch.line_dimensions_iterator();

assert_eq!(
dims.next(&font).unwrap(),
RenderedDimensions {
advance: Point::new(4, 0),
bounding_box: Some(Rectangle::new(Point::new(0, -3), Size::new(3, 3)))
}
);
assert_eq!(dims.next(&font).unwrap(), RenderedDimensions::empty());
assert_eq!(dims.next(&font).unwrap(), RenderedDimensions::empty());
}

#[test]
fn line_dimensions_iter_errors_on_glyph_not_found() {
let font = FontReader::new::<fonts::u8g2_font_u8glib_4_tf>();
let ch = '☃';
let mut dims = ch.line_dimensions_iterator();

assert!(matches!(
dims.next(&font),
Err(LookupError::GlyphNotFound('☃'))
));
}
}
94 changes: 94 additions & 0 deletions src/content/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,97 @@ impl LineDimensionsIterator for TextLineDimensionsIterator<'_> {
compute_line_dimensions(line, Point::new(0, 0), font)
}
}

#[cfg(test)]
mod tests {
extern crate std;
use std::vec::Vec;

use embedded_graphics_core::{prelude::Size, primitives::Rectangle};

use crate::fonts;

use super::*;

#[test]
fn for_each_char_produces_correct_values() {
let mut content = Vec::new();

"abc"
.for_each_char(|e| {
content.push(e);
Result::<(), &'static str>::Ok(())
})
.unwrap();

assert_eq!(content, ['a', 'b', 'c']);
}

#[test]
fn for_each_char_infallible_produces_correct_values() {
let mut content = Vec::new();

"abc".for_each_char_infallible(|e| {
content.push(e);
});

assert_eq!(content, ['a', 'b', 'c']);
}

#[test]
fn for_each_char_propagates_error() {
let result = "abc".for_each_char(|_| Err("Failed!"));

assert_eq!(result, Err("Failed!"));
}

#[test]
fn get_newline_count_provides_correct_value() {
assert_eq!("a\nbc\n".get_newline_count(), 2);
assert_eq!("a\nbc".get_newline_count(), 1);
assert_eq!("".get_newline_count(), 0);
}

#[test]
fn line_dimensions_iter_provides_correct_values() {
let font = FontReader::new::<fonts::u8g2_font_u8glib_4_tf>();
let text = "a\nbc\n";
let mut dims = text.line_dimensions_iterator();

assert_eq!(
dims.next(&font).unwrap(),
RenderedDimensions {
advance: Point::new(4, 0),
bounding_box: Some(Rectangle::new(Point::new(0, -3), Size::new(3, 3)))
}
);
assert_eq!(
dims.next(&font).unwrap(),
RenderedDimensions {
advance: Point::new(7, 0),
bounding_box: Some(Rectangle::new(Point::new(0, -4), Size::new(6, 4)))
}
);
assert_eq!(dims.next(&font).unwrap(), RenderedDimensions::empty());
assert_eq!(dims.next(&font).unwrap(), RenderedDimensions::empty());
}

#[test]
fn line_dimensions_iter_errors_on_glyph_not_found() {
let font = FontReader::new::<fonts::u8g2_font_u8glib_4_tf>();
let text = "a\n☃";
let mut dims = text.line_dimensions_iterator();

assert_eq!(
dims.next(&font).unwrap(),
RenderedDimensions {
advance: Point::new(4, 0),
bounding_box: Some(Rectangle::new(Point::new(0, -3), Size::new(3, 3)))
}
);
assert!(matches!(
dims.next(&font),
Err(LookupError::GlyphNotFound('☃'))
));
}
}
1 change: 0 additions & 1 deletion src/font_reader/glyph_searcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use crate::font_reader::{
glyph_reader::GlyphReader, unicode_jumptable_reader::UnicodeJumptableReader, FontReader,
};

#[derive(Debug)]
pub struct GlyphSearcher<'a, const CHAR_WIDTH: usize> {
data: &'static [u8],
font: &'a FontReader,
Expand Down
63 changes: 63 additions & 0 deletions src/font_reader/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,66 @@ impl FontReader {
}
}
}

#[cfg(test)]
mod tests {
extern crate std;
use std::format;

use super::*;

struct TestFont;
impl crate::Font for TestFont {
const DATA: &'static [u8] = &[
0, 0, 4, 4, 8, 8, 8, 8, 8, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 2, // Header
b'\n', 0, // First glyph
0, 4, 255, 255, // Unicode Table
0, b'\n', 0, // Unicode entry
];
}

#[test]
fn can_read_font_properties() {
let font = FontReader::new::<TestFont>();

let expected = FontReader {
data: DebugIgnore(&[]),
supports_background_color: false,
glyph_count: 0,
m0: 4,
m1: 4,
bitcnt_w: 8,
bitcnt_h: 8,
bitcnt_x: 8,
bitcnt_y: 8,
bitcnt_d: 8,
font_bounding_box_width: 1,
font_bounding_box_height: 2,
font_bounding_box_x_offset: 3,
font_bounding_box_y_offset: 4,
ascent: 5,
descent: 6,
ascent_of_parantheses: 7,
descent_of_parantheses: 8,
array_offset_upper_a: 0,
array_offset_lower_a: 0,
array_offset_0x0100: 2,
};

assert_eq!(format!("{:?}", font), format!("{:?}", expected));
}

#[test]
fn can_handle_unicode_next_is_zero() {
// This test is specifically engineered to test an error path that doesn't happen
// in normal, correct fonts.
// This means that this should be an assert intead, but it just doesn't feel right.
// There is no formal specification that this error path is impossible, and resilient
// programming tells me it should be a normal error path.
// Sadly, that reduces our test coverage :D so let's trigger that error manually.
let font = FontReader::new::<TestFont>();
let glyph = font.retrieve_glyph_data('☃');

assert!(matches!(glyph, Err(LookupError::GlyphNotFound('☃'))));
}
}
Loading

0 comments on commit f1b2dc6

Please sign in to comment.