Fixed a SyncStatus inconsistency
This commit is contained in:
parent
841d14807b
commit
3d9adcec08
6 changed files with 90 additions and 29 deletions
|
@ -17,10 +17,43 @@ pub struct CachedCalendar {
|
||||||
name: String,
|
name: String,
|
||||||
id: CalendarId,
|
id: CalendarId,
|
||||||
supported_components: SupportedComponents,
|
supported_components: SupportedComponents,
|
||||||
|
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
|
||||||
|
is_mocking_remote_calendar: bool,
|
||||||
|
|
||||||
items: HashMap<ItemId, Item>,
|
items: HashMap<ItemId, Item>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CachedCalendar {
|
||||||
|
/// Activate the "mocking remote calendar" feature (i.e. ignore sync statuses, since this is what an actual CalDAV sever would do)
|
||||||
|
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
|
||||||
|
pub fn set_is_mocking_remote_calendar(&mut self) {
|
||||||
|
self.is_mocking_remote_calendar = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an item
|
||||||
|
async fn regular_add_item(&mut self, item: Item) -> Result<SyncStatus, Box<dyn Error>> {
|
||||||
|
// TODO: here (and in the remote version, display an errror in case we overwrite something?)
|
||||||
|
let ss_clone = item.sync_status().clone();
|
||||||
|
log::debug!("Adding an item with {:?}", ss_clone);
|
||||||
|
self.items.insert(item.id().clone(), item);
|
||||||
|
Ok(ss_clone)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an item, but force a "synced" SyncStatus. This is the typical behaviour on a remote calendar
|
||||||
|
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
|
||||||
|
async fn add_item_force_synced(&mut self, mut item: Item) -> Result<SyncStatus, Box<dyn Error>> {
|
||||||
|
log::debug!("Adding an item, but forces a synced SyncStatus");
|
||||||
|
match item.sync_status() {
|
||||||
|
SyncStatus::Synced(_) => (),
|
||||||
|
_ => item.set_sync_status(SyncStatus::random_synced()),
|
||||||
|
};
|
||||||
|
let ss_clone = item.sync_status().clone();
|
||||||
|
self.items.insert(item.id().clone(), item);
|
||||||
|
Ok(ss_clone)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl BaseCalendar for CachedCalendar {
|
impl BaseCalendar for CachedCalendar {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
|
@ -35,10 +68,17 @@ impl BaseCalendar for CachedCalendar {
|
||||||
self.supported_components
|
self.supported_components
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn add_item(&mut self, item: Item) -> Result<(), Box<dyn Error>> {
|
#[cfg(not(feature = "local_calendar_mocks_remote_calendars"))]
|
||||||
// TODO: here (and in the remote version, display an errror in case we overwrite something?)
|
async fn add_item(&mut self, item: Item) -> Result<SyncStatus, Box<dyn Error>> {
|
||||||
self.items.insert(item.id().clone(), item);
|
self.regular_add_item(item).await
|
||||||
Ok(())
|
}
|
||||||
|
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
|
||||||
|
async fn add_item(&mut self, item: Item) -> Result<SyncStatus, Box<dyn Error>> {
|
||||||
|
if self.is_mocking_remote_calendar {
|
||||||
|
self.add_item_force_synced(item).await
|
||||||
|
} else {
|
||||||
|
self.regular_add_item(item).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +87,8 @@ impl CompleteCalendar for CachedCalendar {
|
||||||
fn new(name: String, id: CalendarId, supported_components: SupportedComponents) -> Self {
|
fn new(name: String, id: CalendarId, supported_components: SupportedComponents) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name, id, supported_components,
|
name, id, supported_components,
|
||||||
|
#[cfg(feature = "local_calendar_mocks_remote_calendars")]
|
||||||
|
is_mocking_remote_calendar: false,
|
||||||
items: HashMap::new(),
|
items: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,8 +58,8 @@ impl BaseCalendar for RemoteCalendar {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add an item into this calendar
|
/// Add an item into this calendar
|
||||||
async fn add_item(&mut self, _item: Item) -> Result<(), Box<dyn Error>> {
|
async fn add_item(&mut self, _item: Item) -> Result<SyncStatus, Box<dyn Error>> {
|
||||||
Err("Not implemented".into())
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -247,14 +247,18 @@ where
|
||||||
|
|
||||||
for id_add in local_additions {
|
for id_add in local_additions {
|
||||||
log::debug!("> Pushing local addition {} to the server", id_add);
|
log::debug!("> Pushing local addition {} to the server", id_add);
|
||||||
match cal_local.get_item_by_id_ref(&id_add).await {
|
match cal_local.get_item_by_id_mut(&id_add).await {
|
||||||
None => {
|
None => {
|
||||||
log::error!("Inconsistency: created item {} has been marked for upload but is locally missing", id_add);
|
log::error!("Inconsistency: created item {} has been marked for upload but is locally missing", id_add);
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
Some(item) => {
|
Some(item) => {
|
||||||
if let Err(err) = cal_remote.add_item(item.clone()).await {
|
match cal_remote.add_item(item.clone()).await {
|
||||||
log::error!("Unable to add item {} to remote calendar: {}", id_add, err);
|
Err(err) => log::error!("Unable to add item {} to remote calendar: {}", id_add, err),
|
||||||
|
Ok(new_ss) => {
|
||||||
|
// Update local sync status
|
||||||
|
item.set_sync_status(new_ss);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -262,7 +266,7 @@ where
|
||||||
|
|
||||||
for id_change in local_changes {
|
for id_change in local_changes {
|
||||||
log::debug!("> Pushing local change {} to the server", id_change);
|
log::debug!("> Pushing local change {} to the server", id_change);
|
||||||
match cal_local.get_item_by_id_ref(&id_change).await {
|
match cal_local.get_item_by_id_mut(&id_change).await {
|
||||||
None => {
|
None => {
|
||||||
log::error!("Inconsistency: modified item {} has been marked for upload but is locally missing", id_change);
|
log::error!("Inconsistency: modified item {} has been marked for upload but is locally missing", id_change);
|
||||||
continue;
|
continue;
|
||||||
|
@ -271,8 +275,12 @@ where
|
||||||
if let Err(err) = cal_remote.delete_item(&id_change).await {
|
if let Err(err) = cal_remote.delete_item(&id_change).await {
|
||||||
log::error!("Unable to delete item {} from remote calendar: {}", id_change, err);
|
log::error!("Unable to delete item {} from remote calendar: {}", id_change, err);
|
||||||
}
|
}
|
||||||
if let Err(err) = cal_remote.add_item(item.clone()).await {
|
match cal_remote.add_item(item.clone()).await {
|
||||||
log::error!("Unable to add item {} to remote calendar: {}", id_change, err);
|
Err(err) => log::error!("Unable to add item {} to remote calendar: {}", id_change, err),
|
||||||
|
Ok(new_ss) => {
|
||||||
|
// Update local sync status
|
||||||
|
item.set_sync_status(new_ss);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,6 +4,7 @@ use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
use crate::SyncStatus;
|
||||||
use crate::item::Item;
|
use crate::item::Item;
|
||||||
use crate::item::ItemId;
|
use crate::item::ItemId;
|
||||||
use crate::item::VersionTag;
|
use crate::item::VersionTag;
|
||||||
|
@ -34,8 +35,10 @@ pub trait BaseCalendar {
|
||||||
/// Returns the supported kinds of components for this calendar
|
/// Returns the supported kinds of components for this calendar
|
||||||
fn supported_components(&self) -> crate::calendar::SupportedComponents;
|
fn supported_components(&self) -> crate::calendar::SupportedComponents;
|
||||||
|
|
||||||
/// Add an item into this calendar
|
/// Add an item into this calendar, and return its new sync status.
|
||||||
async fn add_item(&mut self, item: Item) -> Result<(), Box<dyn Error>>;
|
/// For local calendars, the sync status is not modified.
|
||||||
|
/// For remote calendars, the sync status is updated by the server
|
||||||
|
async fn add_item(&mut self, item: Item) -> Result<SyncStatus, Box<dyn Error>>;
|
||||||
|
|
||||||
/// Returns whether this calDAV calendar supports to-do items
|
/// Returns whether this calDAV calendar supports to-do items
|
||||||
fn supports_todo(&self) -> bool {
|
fn supports_todo(&self) -> bool {
|
||||||
|
|
|
@ -106,9 +106,10 @@ pub fn print_task(item: &Item) {
|
||||||
status += " ";
|
status += " ";
|
||||||
}
|
}
|
||||||
match task.sync_status() {
|
match task.sync_status() {
|
||||||
|
SyncStatus::NotSynced => { status += " "; },
|
||||||
|
SyncStatus::Synced(_) => { status += "="; },
|
||||||
SyncStatus::LocallyModified(_) => { status += "~"; },
|
SyncStatus::LocallyModified(_) => { status += "~"; },
|
||||||
SyncStatus::LocallyDeleted(_) => { status += "x"; },
|
SyncStatus::LocallyDeleted(_) => { status += "x"; },
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
println!(" {} {}\t{}", status, task.name(), task.id());
|
println!(" {} {}\t{}", status, task.name(), task.id());
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
//! This module builds actual CalDAV sources (actually [`crate::cache::Cache`]s, that can also mock what would be [`crate::client::Client`]s in a real program) and [`crate::provider::Provider]`s that contain this data
|
//! This module builds actual CalDAV sources (actually [`crate::cache::Cache`]s, that can also mock what would be [`crate::client::Client`]s in a real program) and [`crate::provider::Provider]`s that contain this data
|
||||||
//!
|
//!
|
||||||
//! This module can also check the sources after a sync contain the actual data we expect
|
//! This module can also check the sources after a sync contain the actual data we expect
|
||||||
#![cfg(feature = "integration_tests")]
|
#![cfg(feature = "local_calendar_mocks_remote_calendars")]
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
@ -388,17 +388,23 @@ pub async fn populate_test_provider_after_sync(scenarii: &[ItemScenario]) -> Pro
|
||||||
populate_test_provider(scenarii, true).await
|
populate_test_provider(scenarii, true).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn populate_test_provider(scenarii: &[ItemScenario], final_state: bool) -> Provider<Cache, CachedCalendar, Cache, CachedCalendar> {
|
async fn populate_test_provider(scenarii: &[ItemScenario], populate_for_final_state: bool) -> Provider<Cache, CachedCalendar, Cache, CachedCalendar> {
|
||||||
let mut remote = Cache::new(&PathBuf::from(String::from("test_cache_remote/")));
|
let mut remote = Cache::new(&PathBuf::from(String::from("test_cache_remote/")));
|
||||||
let mut local = Cache::new(&PathBuf::from(String::from("test_cache_local/")));
|
let mut local = Cache::new(&PathBuf::from(String::from("test_cache_local/")));
|
||||||
|
|
||||||
// Create the initial state, as if we synced both sources in a given state
|
// Create the initial state, as if we synced both sources in a given state
|
||||||
for item in scenarii {
|
for item in scenarii {
|
||||||
let required_state = if final_state { &item.after_sync } else { &item.initial_state };
|
let required_state = if populate_for_final_state { &item.after_sync } else { &item.initial_state };
|
||||||
let (state, sync_status) = match required_state {
|
let (state, sync_status) = match required_state {
|
||||||
LocatedState::None => continue,
|
LocatedState::None => continue,
|
||||||
LocatedState::Local(s) => (s, SyncStatus::NotSynced),
|
LocatedState::Local(s) => {
|
||||||
LocatedState::Remote(s) => (s, SyncStatus::random_synced()),
|
assert!(populate_for_final_state == false, "You are not supposed to expect an item in this state after sync");
|
||||||
|
(s, SyncStatus::NotSynced)
|
||||||
|
},
|
||||||
|
LocatedState::Remote(s) => {
|
||||||
|
assert!(populate_for_final_state == false, "You are not supposed to expect an item in this state after sync");
|
||||||
|
(s, SyncStatus::random_synced())
|
||||||
|
}
|
||||||
LocatedState::BothSynced(s) => (s, SyncStatus::random_synced()),
|
LocatedState::BothSynced(s) => (s, SyncStatus::random_synced()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -413,14 +419,14 @@ async fn populate_test_provider(scenarii: &[ItemScenario], final_state: bool) ->
|
||||||
match required_state {
|
match required_state {
|
||||||
LocatedState::None => panic!("Should not happen, we've continued already"),
|
LocatedState::None => panic!("Should not happen, we've continued already"),
|
||||||
LocatedState::Local(s) => {
|
LocatedState::Local(s) => {
|
||||||
get_or_insert_calendar(&mut local, &s.calendar).await.unwrap().lock().unwrap().add_item(new_item).await.unwrap();
|
get_or_insert_calendar(&mut local, &s.calendar, false).await.unwrap().lock().unwrap().add_item(new_item).await.unwrap();
|
||||||
},
|
},
|
||||||
LocatedState::Remote(s) => {
|
LocatedState::Remote(s) => {
|
||||||
get_or_insert_calendar(&mut remote, &s.calendar).await.unwrap().lock().unwrap().add_item(new_item).await.unwrap();
|
get_or_insert_calendar(&mut remote, &s.calendar, true).await.unwrap().lock().unwrap().add_item(new_item).await.unwrap();
|
||||||
},
|
},
|
||||||
LocatedState::BothSynced(s) => {
|
LocatedState::BothSynced(s) => {
|
||||||
get_or_insert_calendar(&mut local, &s.calendar).await.unwrap().lock().unwrap().add_item(new_item.clone()).await.unwrap();
|
get_or_insert_calendar(&mut local, &s.calendar, false).await.unwrap().lock().unwrap().add_item(new_item.clone()).await.unwrap();
|
||||||
get_or_insert_calendar(&mut remote, &s.calendar).await.unwrap().lock().unwrap().add_item(new_item).await.unwrap();
|
get_or_insert_calendar(&mut remote, &s.calendar, true).await.unwrap().lock().unwrap().add_item(new_item).await.unwrap();
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -448,17 +454,18 @@ async fn apply_changes_on_provider(provider: &mut Provider<Cache, CachedCalendar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_or_insert_calendar<S, C>(source: &mut S, id: &CalendarId) -> Result<Arc<Mutex<C>>, Box<dyn Error>>
|
async fn get_or_insert_calendar(source: &mut Cache, id: &CalendarId, should_mock_remote_calendar: bool)
|
||||||
where
|
-> Result<Arc<Mutex<CachedCalendar>>, Box<dyn Error>>
|
||||||
S: CalDavSource<C>,
|
|
||||||
C: CompleteCalendar + DavCalendar, // in this test, we're using a calendar that mocks both kinds
|
|
||||||
{
|
{
|
||||||
match source.get_calendar(id).await {
|
match source.get_calendar(id).await {
|
||||||
Some(cal) => Ok(cal),
|
Some(cal) => Ok(cal),
|
||||||
None => {
|
None => {
|
||||||
let new_name = format!("Calendar for ID {}", id);
|
let new_name = format!("Calendar for ID {}", id);
|
||||||
let supported_components = SupportedComponents::TODO;
|
let supported_components = SupportedComponents::TODO;
|
||||||
let cal = C::new(new_name.to_string(), id.clone(), supported_components);
|
let mut cal = CachedCalendar::new(new_name.to_string(), id.clone(), supported_components);
|
||||||
|
if should_mock_remote_calendar {
|
||||||
|
cal.set_is_mocking_remote_calendar();
|
||||||
|
}
|
||||||
source.insert_calendar(cal).await
|
source.insert_calendar(cal).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue