Ability to mock behaviours for tests

This commit is contained in:
daladim 2021-04-13 23:32:07 +02:00
parent 081fc2cbc8
commit b2704bd3d2
7 changed files with 340 additions and 80 deletions

View file

@ -25,6 +25,7 @@ use my_tasks::SyncStatus;
use my_tasks::Task;
use my_tasks::calendar::cached_calendar::CachedCalendar;
use my_tasks::Provider;
use my_tasks::mock_behaviour::MockBehaviour;
pub enum LocatedState {
/// Item does not exist yet or does not exist anymore
@ -575,21 +576,21 @@ pub fn scenarii_transient_task() -> Vec<ItemScenario> {
/// Build a `Provider` that contains the data (defined in the given scenarii) before sync
pub async fn populate_test_provider_before_sync(scenarii: &[ItemScenario]) -> Provider<Cache, CachedCalendar, Cache, CachedCalendar> {
let mut provider = populate_test_provider(scenarii, false).await;
pub async fn populate_test_provider_before_sync(scenarii: &[ItemScenario], mock_behaviour: Arc<Mutex<MockBehaviour>>) -> Provider<Cache, CachedCalendar, Cache, CachedCalendar> {
let mut provider = populate_test_provider(scenarii, mock_behaviour, false).await;
apply_changes_on_provider(&mut provider, scenarii).await;
provider
}
/// Build a `Provider` that contains the data (defined in the given scenarii) after sync
pub async fn populate_test_provider_after_sync(scenarii: &[ItemScenario]) -> Provider<Cache, CachedCalendar, Cache, CachedCalendar> {
populate_test_provider(scenarii, true).await
pub async fn populate_test_provider_after_sync(scenarii: &[ItemScenario], mock_behaviour: Arc<Mutex<MockBehaviour>>) -> Provider<Cache, CachedCalendar, Cache, CachedCalendar> {
populate_test_provider(scenarii, mock_behaviour, true).await
}
async fn populate_test_provider(scenarii: &[ItemScenario], populate_for_final_state: bool) -> Provider<Cache, CachedCalendar, Cache, CachedCalendar> {
async fn populate_test_provider(scenarii: &[ItemScenario], mock_behaviour: Arc<Mutex<MockBehaviour>>, populate_for_final_state: bool) -> Provider<Cache, CachedCalendar, Cache, CachedCalendar> {
let mut local = Cache::new(&PathBuf::from(String::from("test_cache_local/")));
let mut remote = Cache::new(&PathBuf::from(String::from("test_cache_remote/")));
remote.set_is_mocking_remote_source();
remote.set_mock_behaviour(Some(mock_behaviour));
// Create the initial state, as if we synced both sources in a given state
for item in scenarii {

View file

@ -1,5 +1,10 @@
mod scenarii;
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
use std::sync::{Arc, Mutex};
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
use my_tasks::mock_behaviour::MockBehaviour;
@ -8,6 +13,8 @@ mod scenarii;
struct TestFlavour {
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
scenarii: Vec<scenarii::ItemScenario>,
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
mock_behaviour: Arc<Mutex<MockBehaviour>>,
}
#[cfg(not(feature = "local_calendar_mocks_remote_calendars"))]
@ -16,8 +23,9 @@ impl TestFlavour {
pub fn first_sync_to_local() -> Self { Self{} }
pub fn first_sync_to_server() -> Self { Self{} }
pub fn transient_task() -> Self { Self{} }
pub fn normal_with_errors() -> Self { Self{} }
pub async fn run(&self) {
pub async fn run(&self, _max_attempts: u32) {
println!("WARNING: This test required the \"integration_tests\" Cargo feature");
}
}
@ -27,34 +35,54 @@ impl TestFlavour {
pub fn normal() -> Self {
Self {
scenarii: scenarii::scenarii_basic(),
mock_behaviour: Arc::new(Mutex::new(MockBehaviour::new())),
}
}
pub fn first_sync_to_local() -> Self {
Self {
scenarii: scenarii::scenarii_first_sync_to_local(),
mock_behaviour: Arc::new(Mutex::new(MockBehaviour::new())),
}
}
pub fn first_sync_to_server() -> Self {
Self {
scenarii: scenarii::scenarii_first_sync_to_server(),
mock_behaviour: Arc::new(Mutex::new(MockBehaviour::new())),
}
}
pub fn transient_task() -> Self {
Self {
scenarii: scenarii::scenarii_transient_task(),
mock_behaviour: Arc::new(Mutex::new(MockBehaviour::new())),
}
}
pub async fn run(&self) {
let mut provider = scenarii::populate_test_provider_before_sync(&self.scenarii).await;
pub fn normal_with_errors() -> Self {
Self {
scenarii: scenarii::scenarii_basic(),
mock_behaviour: Arc::new(Mutex::new(MockBehaviour::fail_now(10))),
}
}
pub async fn run(&self, max_attempts: u32) {
self.mock_behaviour.lock().unwrap().suspend();
let mut provider = scenarii::populate_test_provider_before_sync(&self.scenarii, Arc::clone(&self.mock_behaviour)).await;
print_provider(&provider, "before sync").await;
println!("\nsyncing...\n");
provider.sync().await.unwrap();
self.mock_behaviour.lock().unwrap().resume();
for attempt in 0..max_attempts {
println!("\nSyncing...\n");
if provider.sync().await == true {
println!("Sync complete after {} attempts (multiple attempts are due to forced errors in mocked behaviour)", attempt+1);
break
}
}
self.mock_behaviour.lock().unwrap().suspend();
print_provider(&provider, "after sync").await;
@ -62,7 +90,7 @@ impl TestFlavour {
assert!(provider.remote().has_same_observable_content_as(provider.local()).await.unwrap());
// But also explicitely check that every item is expected
let expected_provider = scenarii::populate_test_provider_after_sync(&self.scenarii).await;
let expected_provider = scenarii::populate_test_provider_after_sync(&self.scenarii, Arc::clone(&self.mock_behaviour)).await;
println!("\n");
print_provider(&expected_provider, "expected after sync").await;
@ -71,7 +99,7 @@ impl TestFlavour {
// Perform a second sync, even if no change has happened, just to check
println!("Syncing again");
provider.sync().await.unwrap();
provider.sync().await;
assert!(provider.local() .has_same_observable_content_as(expected_provider.local() ).await.unwrap());
assert!(provider.remote().has_same_observable_content_as(expected_provider.remote()).await.unwrap());
}
@ -85,7 +113,7 @@ async fn test_regular_sync() {
let _ = env_logger::builder().is_test(true).try_init();
let flavour = TestFlavour::normal();
flavour.run().await;
flavour.run(1).await;
}
#[tokio::test]
@ -93,7 +121,7 @@ async fn test_sync_empty_initial_local() {
let _ = env_logger::builder().is_test(true).try_init();
let flavour = TestFlavour::first_sync_to_local();
flavour.run().await;
flavour.run(1).await;
}
#[tokio::test]
@ -101,7 +129,7 @@ async fn test_sync_empty_initial_server() {
let _ = env_logger::builder().is_test(true).try_init();
let flavour = TestFlavour::first_sync_to_server();
flavour.run().await;
flavour.run(1).await;
}
#[tokio::test]
@ -109,9 +137,16 @@ async fn test_sync_transient_task() {
let _ = env_logger::builder().is_test(true).try_init();
let flavour = TestFlavour::transient_task();
flavour.run().await;
flavour.run(1).await;
}
#[tokio::test]
async fn test_errors_in_regular_sync() {
let _ = env_logger::builder().is_test(true).try_init();
let flavour = TestFlavour::normal_with_errors();
flavour.run(100).await;
}
#[cfg(feature = "integration_tests")]
use my_tasks::{traits::CalDavSource,