123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- use std::{env, panic};
- use std::collections::HashMap;
- use std::fmt::Debug;
- use std::panic::{RefUnwindSafe, UnwindSafe};
- use std::path::Path;
- use std::sync::Mutex;
- use config::{ConfigError, Map, Value, ValueKind};
- use glob::PatternError;
- use lazy_static::lazy_static;
- use log::{Level, LevelFilter, Log, Metadata, Record};
- use rand::prelude::ThreadRng;
- use rand::Rng;
- use rstest::rstest;
- use tempfile::TempDir;
- use crate::*;
- use crate::tiles::{Connectors, Tile, TileConnection};
- use crate::tiles::tile_list::TileList;
- use crate::tiling::{Direction, MissingTilePolicy, VacancyPolicy};
- lazy_static! {
- pub(crate) static ref RANDOM_USIZE: usize = rand_usize(0, 3);
- pub(crate) static ref RANDOM_F64: f64 = rand_float();
- pub(crate) static ref RANDOM_TILES: Vec<Tile> = (0..10).map(
- |_| random_tile_text(random_connectors())
- ).collect();
- pub(crate) static ref RANDOM_STRINGS: Vec<String> =
- (0..10).map(|x| rand_string(rand_usize(x * 5, x * 5 + 5))).collect();
- pub(crate) static ref TILE_CONNECTION_TILES: Vec<Tile> = vec![
- random_tile_text(Connectors { north: 1, east: 1, south: 1, west: 1}),
- random_tile_text(Connectors { north: 1, east: 2, south: 2, west: 1}),
- random_tile_text(Connectors { north: 1, east: 1, south: 2, west: 1}),
- random_tile_text(Connectors { north: 2, east: 3, south: 1, west: 4}),
- random_tile_text(Connectors { north: 3, east: 3, south: 2, west: 4}),
- ];
- // Taken from https://stackoverflow.com/a/67433684
- pub(crate) static ref SERIAL_TEST: Mutex<()> = Mutex::default();
- pub(crate) static ref TEMPORARY_DIRECTORY: TempDir =
- tempfile::tempdir().expect("the temporary directory could not be created");
- pub(crate) static ref TEST_VALUE_KIND_VECTOR: Vec<ValueKind> = vec![
- ValueKind::Table(Map::new()),
- ValueKind::String("öß".to_string()),
- ValueKind::Array(vec![Value::new(Some(&RANDOM_STRINGS[6]), ValueKind::Nil)]),
- ValueKind::Boolean(false)
- ];
- pub(crate) static ref TEST_VALUE_VECTOR: Vec<Value> = vec![
- Value::new(Some(&RANDOM_STRINGS[2]), ValueKind::Table(Map::new())),
- Value::new(Some(&"".to_string()), ValueKind::String("öß".to_string())),
- Value::new(
- None, ValueKind::Array(vec![Value::new(Some(&RANDOM_STRINGS[6]), ValueKind::Nil)])
- ),
- Value::new(Some(&RANDOM_STRINGS[7]), ValueKind::Boolean(false))
- ];
- pub(crate) static ref TEST_VALUE_KIND_ARRAY: ValueKind =
- ValueKind::Array(TEST_VALUE_VECTOR.clone());
- }
- pub(crate) static TEST_LOGGER: TestLogger = TestLogger {
- messages: Mutex::new(vec![]),
- };
- pub(crate) struct TestLogger {
- pub messages: Mutex<Vec<String>>,
- }
- impl Log for TestLogger {
- fn enabled(&self, metadata: &Metadata) -> bool {
- metadata.level() <= Level::Trace
- }
- fn log(&self, record: &Record) {
- let mut messages = self.messages.lock().unwrap_or_else(|e| e.into_inner());
- messages.push(format!("{} ⸙ {}", record.level(), record.args()));
- }
- fn flush(&self) {}
- }
- /// Initialize the custom logger for testing.
- pub(crate) fn init_logging() {
- // Clear the message list to isolate logs of consecutive test cases.
- let mut messages = TEST_LOGGER.messages.lock().unwrap();
- messages.clear();
- // log::set_logger may only be called once, it returns an Err in subsequent calls.
- if let Ok(()) = log::set_logger(&TEST_LOGGER) {
- log::set_max_level(LevelFilter::Trace);
- }
- }
- pub(crate) fn test_data(path: &str) -> String {
- format!("{}/tests/data/{}", env!("CARGO_MANIFEST_DIR"), path)
- }
- pub(crate) fn rand_int(min: i32, max: i32) -> i32 {
- rand::thread_rng().gen_range(min..max)
- }
- pub(crate) fn rand_usize(min: usize, max: usize) -> usize {
- rand::thread_rng().gen_range(min..max)
- }
- pub(crate) fn rand_float() -> f64 {
- rand::random()
- }
- pub(crate) fn rand_string(length: usize) -> String {
- let charset = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\
- ‐‑‒–—―‖‗‘’‚‛“”„‟†‡•‣․‥…‧‰‱′″‴‵‶‷‸‹›※‼‽‾‿⁀⁁⁂⁃⁄⁅⁆⁇⁈⁉⁊⁋⁌⁍⁎⁏⁐⁑⁒⁓⁔⁕⁖⁗\
- ─━│┃┄┅┆┇┈┉┊┋┌┍┎┏";
- random_string::generate(length, charset)
- }
- pub(crate) fn random_text_tile_content_vector() -> Vec<String> {
- //! Create a vector of random length with random strings of a uniform random length.
- let mut content = Vec::new();
- let string_length = rand_usize(0, 19);
- for _ in 0..rand_usize(2, 12) {
- content.push(rand_string(string_length))
- }
- content
- }
- pub(crate) fn random_connectors() -> Connectors {
- Connectors {
- north: rand_usize(1, 10),
- east: rand_usize(1, 10),
- south: rand_usize(1, 10),
- west: rand_usize(1, 10),
- }
- }
- pub(crate) fn random_tile_text(connectors: Connectors) -> Tile {
- random_tile_text_with_weight(connectors, rand_float())
- }
- pub(crate) fn random_tile_text_with_weight(connectors: Connectors, weight: f64) -> Tile {
- Tile::new_text(
- rand_usize(67, 90),
- rand_string(rand_usize(0, 13)),
- random_text_tile_content_vector(),
- connectors,
- weight,
- )
- .unwrap()
- }
- /// Helper function that enforces that T and U are the same type.
- pub(crate) fn enforce_types_are_equal<T: Sized>(_t: T, _u: T) {}
- // Taken from https://stackoverflow.com/a/67433684
- /// Changes the current working directory to the specified path.
- pub(crate) fn with_cwd<F, P: AsRef<Path>>(path: P, closure: F)
- where
- F: Fn() + UnwindSafe + RefUnwindSafe,
- {
- let _guard = SERIAL_TEST.lock().unwrap_or_else(|e| e.into_inner());
- let old_cwd = env::current_dir();
- env::set_current_dir(path).unwrap();
- panic::catch_unwind(|| closure()).unwrap();
- if let Ok(cwd) = old_cwd {
- env::set_current_dir(cwd).expect("Resetting cwd failed");
- }
- }
- pub(crate) fn assert_expected_result_or_error<T: Sized + PartialEq + Debug>(
- result: Result<T, Error>,
- expected_result: Option<T>,
- expected_error: Option<Error>,
- ) {
- if let Some(expected_error) = expected_error {
- assert_expected_error(result, expected_error);
- } else {
- assert_eq!(
- expected_result.expect("expected Some, got None"),
- result.expect("expected Ok, got Err")
- );
- }
- }
- pub(crate) fn assert_expected_error<T: Sized + PartialEq + Debug>(
- result: Result<T, Error>,
- expected_error: Error,
- ) {
- let error = result.expect_err("Expected an Err, got Ok");
- assert_eq!(expected_error, error);
- }
- #[test]
- fn test_connectors_get_connector_is_public() {
- let connectors: Connectors = random_connectors();
- let _ = connectors.get_connector(&Direction::None);
- }
- #[test]
- fn test_tile_connectors_is_public() {
- let tile = random_tile_text(random_connectors());
- let _ = tile.connectors;
- }
- #[test]
- fn test_tile_connection_select_tile_by_priority_is_public() {
- let tile_connection = TileConnection {
- tiles: vec![0],
- weights: vec![0.0],
- };
- let _ = tile_connection.select_tile_by_priority();
- }
- #[test]
- fn test_tile_connection_select_tile_randomly_is_public() {
- let mut rng: ThreadRng = rand::thread_rng();
- let tile_connection = TileConnection {
- tiles: vec![0],
- weights: vec![0.0],
- };
- let _ = tile_connection.select_tile_randomly(&mut rng);
- }
- #[test]
- fn test_tile_grid_populate_simple_is_public() {
- let mut tile_grid = tiling::TileGrid::new(
- rand_usize(1, 10),
- rand_usize(2, 5),
- TileList {
- tiles: vec![],
- horizontal_connector_name_map: {
- let mut map = HashMap::new();
- map.insert(0, "".to_string());
- map
- },
- vertical_connector_name_map: {
- let mut map = HashMap::new();
- map.insert(0, "".to_string());
- map
- },
- },
- None,
- MissingTilePolicy::Exit,
- tiling::TileSelectionPolicy::Random,
- VacancyPolicy::Avoid,
- )
- .unwrap();
- let _ = tile_grid.populate_simple();
- }
- #[rstest]
- fn test_error_value_error_raw_initialization(
- #[values("", " ", "₪", "ita", "call")] message: String,
- ) {
- let error = Error::ValueError(message.clone());
- if let Error::ValueError(msg) = error {
- assert_eq!(msg, message);
- } else {
- panic!("Expected the ValueError variant, got something else");
- }
- }
- #[rstest]
- fn test_error_io_error_raw_initialization(#[values("", " ", "e", "lam", "ola")] message: String) {
- let error = Error::IoError(message.clone());
- if let Error::IoError(msg) = error {
- assert_eq!(msg, message);
- } else {
- panic!("Expected the IoError variant, got something else");
- }
- }
- #[test]
- fn test_error_tile_not_found_error_raw_initialization() {
- let error = Error::TileNotFoundException(tests::RANDOM_STRINGS[4].clone());
- if let Error::TileNotFoundException(msg) = error {
- assert_eq!(msg, tests::RANDOM_STRINGS[4]);
- } else {
- panic!("Expected the TileNotFoundException variant, got something else");
- }
- }
- #[test]
- fn test_error_from_config_error() {
- let config_error = ConfigError::Message(tests::RANDOM_STRINGS[0].clone());
- let error: Error = config_error.into();
- match error {
- Error::ValueError(error) => assert_eq!(tests::RANDOM_STRINGS[0].clone(), error),
- _ => panic!("Expected ValueError variant, got something else"),
- }
- }
- #[test]
- fn test_error_from_pattern_error() {
- let pattern_error = PatternError {
- pos: tests::RANDOM_USIZE.clone(),
- msg: &tests::RANDOM_STRINGS[1],
- };
- let error: Error = pattern_error.into();
- match error {
- Error::ValueError(error) => assert_eq!(
- format!(
- "Pattern syntax error near position {}: {}",
- tests::RANDOM_USIZE.clone(),
- tests::RANDOM_STRINGS[1].clone()
- ),
- error
- ),
- _ => panic!("Expected ValueError variant, got something else"),
- }
- }
|