diff --git a/crates/curp/src/server/conflict/mod.rs b/crates/curp/src/server/conflict/mod.rs index 87aa31d27..67c3f426b 100644 --- a/crates/curp/src/server/conflict/mod.rs +++ b/crates/curp/src/server/conflict/mod.rs @@ -101,6 +101,20 @@ impl<C> PartialEq for CommandEntry<C> { impl<C> Eq for CommandEntry<C> {} +impl<C> PartialOrd for CommandEntry<C> { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { + Some(self.cmp(other)) + } +} + +impl<C> Ord for CommandEntry<C> { + #[inline] + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.id.cmp(&other.id) + } +} + impl<C> From<CommandEntry<C>> for PoolEntry<C> { fn from(entry: CommandEntry<C>) -> Self { PoolEntry { diff --git a/crates/xline/src/conflict/tests.rs b/crates/xline/src/conflict/tests.rs index 886b3b1eb..5a003b39e 100644 --- a/crates/xline/src/conflict/tests.rs +++ b/crates/xline/src/conflict/tests.rs @@ -28,7 +28,10 @@ fn kv_sp_operations_are_ok() { assert!(sp.insert_if_not_conflict(entry2.clone()).is_none()); assert!(sp.insert_if_not_conflict(entry3.clone()).is_some()); assert!(sp.insert_if_not_conflict(entry4.clone()).is_none()); - assert_eq!(sp.all().len(), 3); + compare_commands( + sp.all(), + vec![entry1.clone(), entry2.clone(), entry4.clone()], + ); assert_eq!(sp.len(), 3); sp.remove(entry1.clone()); assert!(sp.insert_if_not_conflict(entry3.clone()).is_some()); @@ -56,10 +59,25 @@ fn kv_ucp_operations_are_ok() { assert!(!ucp.insert(entry3.clone())); assert!(ucp.insert(entry4.clone())); assert!(ucp.insert(entry5.clone())); - assert_eq!(ucp.all().len(), 5); + compare_commands( + ucp.all(), + vec![ + entry1.clone(), + entry2.clone(), + entry3.clone(), + entry4.clone(), + entry5.clone(), + ], + ); assert_eq!(ucp.len(), 5); - assert_eq!(ucp.all_conflict(&entry1).len(), 3); - assert_eq!(ucp.all_conflict(&entry6).len(), 2); + compare_commands( + ucp.all_conflict(&entry1), + vec![entry1.clone(), entry2.clone(), entry4.clone()], + ); + compare_commands( + ucp.all_conflict(&entry6), + vec![entry4.clone(), entry5.clone()], + ); ucp.remove(entry4.clone()); ucp.remove(entry5.clone()); assert!(!ucp.insert(entry6.clone())); @@ -81,7 +99,7 @@ fn lease_sp_operations_are_ok() { assert!(sp.insert_if_not_conflict(entry2.clone()).is_none()); assert!(sp.insert_if_not_conflict(entry3.clone()).is_some()); assert!(sp.insert_if_not_conflict(entry4.clone()).is_some()); - assert_eq!(sp.all().len(), 2); + compare_commands(sp.all(), vec![entry1.clone(), entry2.clone()]); assert_eq!(sp.len(), 2); sp.remove(entry1); sp.remove(entry2); @@ -106,10 +124,25 @@ fn lease_ucp_operations_are_ok() { assert!(!ucp.insert(entry3.clone())); assert!(ucp.insert(entry4.clone())); assert!(ucp.insert(entry5.clone())); - assert_eq!(ucp.all().len(), 5); + compare_commands( + ucp.all(), + vec![ + entry1.clone(), + entry2.clone(), + entry3.clone(), + entry4.clone(), + entry5.clone(), + ], + ); assert_eq!(ucp.len(), 5); - assert_eq!(ucp.all_conflict(&entry1).len(), 3); - assert_eq!(ucp.all_conflict(&entry3).len(), 2); + compare_commands( + ucp.all_conflict(&entry1), + vec![entry1.clone(), entry2.clone(), entry4.clone()], + ); + compare_commands( + ucp.all_conflict(&entry3), + vec![entry3.clone(), entry5.clone()], + ); ucp.remove(entry3.clone()); ucp.remove(entry5.clone()); assert!(!ucp.insert(entry5.clone())); @@ -127,7 +160,7 @@ fn exclusive_sp_operations_are_ok() { assert!(sp.insert_if_not_conflict(entry1.clone()).is_some()); assert!(sp.insert_if_not_conflict(entry1.clone()).is_some()); assert!(sp.insert_if_not_conflict(entry2.clone()).is_some()); - assert_eq!(sp.all().len(), 1); + compare_commands(sp.all(), vec![entry1.clone()]); assert_eq!(sp.len(), 1); sp.remove(entry1); assert!(sp.insert_if_not_conflict(entry2).is_some()); @@ -144,8 +177,11 @@ fn exclusive_ucp_operations_are_ok() { let entry2 = gen.gen_role_add(); assert!(ucp.insert(entry1.clone())); assert!(ucp.insert(entry2.clone())); - assert_eq!(ucp.all().len(), 2); - assert_eq!(ucp.all_conflict(&entry1).len(), 2); + compare_commands(ucp.all(), vec![entry1.clone(), entry2.clone()]); + compare_commands( + ucp.all_conflict(&entry1), + vec![entry1.clone(), entry2.clone()], + ); assert_eq!(ucp.len(), 2); ucp.remove(entry1.clone()); ucp.remove(entry2.clone()); @@ -155,6 +191,12 @@ fn exclusive_ucp_operations_are_ok() { assert_eq!(ucp.len(), 0); } +fn compare_commands(mut a: Vec<CommandEntry<Command>>, mut b: Vec<CommandEntry<Command>>) { + a.sort_unstable(); + b.sort_unstable(); + assert_eq!(a, b); +} + #[derive(Default)] struct EntryGenerator { id: u64, diff --git a/crates/xlineapi/src/command.rs b/crates/xlineapi/src/command.rs index fddca5159..f51169b45 100644 --- a/crates/xlineapi/src/command.rs +++ b/crates/xlineapi/src/command.rs @@ -209,8 +209,7 @@ impl From<KeyRange> for PbKeyRange { } /// Command to run consensus protocol -#[cfg_attr(test, derive(PartialEq))] -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct Command { /// Request data request: RequestWrapper,