|
|
@@ -224,3 +224,447 @@ impl<C: Default> LogArray<C> {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+#[cfg(test)]
|
|
|
+mod tests {
|
|
|
+ use std::panic::catch_unwind;
|
|
|
+
|
|
|
+ use super::*;
|
|
|
+
|
|
|
+ fn make_log_array(len: usize) -> LogArray<i32> {
|
|
|
+ make_log_array_range(0, len)
|
|
|
+ }
|
|
|
+
|
|
|
+ fn make_log_array_range(start: usize, end: usize) -> LogArray<i32> {
|
|
|
+ let mut ret = vec![];
|
|
|
+ for i in start..end {
|
|
|
+ ret.push(LogEntry {
|
|
|
+ term: Term(i / 3),
|
|
|
+ index: i,
|
|
|
+ command: (end - i) as i32,
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ LogArray {
|
|
|
+ inner: ret,
|
|
|
+ snapshot: bytes::Bytes::from_static(&[1, 2, 3]),
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fn default_log_array() -> (usize, usize, LogArray<i32>) {
|
|
|
+ (8, 17, make_log_array_range(8, 17))
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_create() {
|
|
|
+ let log = LogArray::<i32>::create();
|
|
|
+ log.check_one_element();
|
|
|
+
|
|
|
+ assert_eq!(1, log.len());
|
|
|
+ assert_eq!((0, Term(0)), log.first_index_term());
|
|
|
+ assert_eq!(0, log[0].command);
|
|
|
+ }
|
|
|
+
|
|
|
+ // TODO(ditsing): add invariant checks to restore and write a test.
|
|
|
+ #[test]
|
|
|
+ fn test_restore() {}
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_start_offset() {
|
|
|
+ let log = make_log_array_range(9, 17);
|
|
|
+ assert_eq!(9, log.start_offset());
|
|
|
+
|
|
|
+ let log = make_log_array(9);
|
|
|
+ assert_eq!(0, log.start_offset());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_len() {
|
|
|
+ let log = make_log_array(7);
|
|
|
+ assert_eq!(7, log.len());
|
|
|
+
|
|
|
+ let log = make_log_array_range(8, 17);
|
|
|
+ assert_eq!(17, log.len());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_accessors() {
|
|
|
+ let (start, end, log) = default_log_array();
|
|
|
+ assert_eq!((start, Term(2)), log.first_index_term());
|
|
|
+ assert_eq!((end - 1, Term(5)), log.last_index_term());
|
|
|
+ assert_eq!(bytes::Bytes::from_static(&[1, 2, 3]), log.snapshot());
|
|
|
+
|
|
|
+ let all = log.all();
|
|
|
+ assert_eq!(end - start, all.len());
|
|
|
+ for i in start..end {
|
|
|
+ assert_eq!(all[i - start].index, i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_at() {
|
|
|
+ let (start, end, log) = default_log_array();
|
|
|
+
|
|
|
+ let last = log.at(end - 1);
|
|
|
+ assert_eq!(end - 1, last.index);
|
|
|
+ assert_eq!(5, last.term.0);
|
|
|
+ assert_eq!(1, last.command);
|
|
|
+
|
|
|
+ let first = log.at(start);
|
|
|
+ assert_eq!(start, first.index);
|
|
|
+ assert_eq!(2, first.term.0);
|
|
|
+ assert_eq!(9, first.command);
|
|
|
+
|
|
|
+ assert!(start < 12);
|
|
|
+ assert!(end > 12);
|
|
|
+ let middle = log.at(12);
|
|
|
+ assert_eq!(12, middle.index);
|
|
|
+ assert_eq!(4, middle.term.0);
|
|
|
+ assert_eq!(5, middle.command);
|
|
|
+
|
|
|
+ let at_before_start = catch_unwind(|| {
|
|
|
+ log.at(start - 1);
|
|
|
+ });
|
|
|
+ assert!(at_before_start.is_err());
|
|
|
+ let at_after_end = catch_unwind(|| {
|
|
|
+ log.at(end);
|
|
|
+ });
|
|
|
+ assert!(at_after_end.is_err());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_index_operator() {
|
|
|
+ let (start, end, log) = default_log_array();
|
|
|
+
|
|
|
+ for i in start..end {
|
|
|
+ assert_eq!(log[i].index, log.at(i).index);
|
|
|
+ assert_eq!(log[i].term, log.at(i).term);
|
|
|
+ assert_eq!(log[i].command, log.at(i).command);
|
|
|
+ }
|
|
|
+ assert!(catch_unwind(|| log[0].term).is_err());
|
|
|
+ assert!(catch_unwind(|| log[20].term).is_err());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_after() {
|
|
|
+ let (start, end, log) = default_log_array();
|
|
|
+
|
|
|
+ let offset = 12;
|
|
|
+ assert!(offset > start);
|
|
|
+ assert!(offset < end);
|
|
|
+
|
|
|
+ let after = log.after(offset);
|
|
|
+ assert_eq!(end - offset, after.len());
|
|
|
+ for i in offset..end {
|
|
|
+ assert_eq!(after[i - offset].index, i);
|
|
|
+ assert_eq!(after[i - offset].term.0, i / 3);
|
|
|
+ }
|
|
|
+
|
|
|
+ assert!(catch_unwind(|| log.after(start - 1)).is_err());
|
|
|
+ assert!(catch_unwind(|| log.after(start)).is_ok());
|
|
|
+ assert!(catch_unwind(|| log.after(end)).is_err());
|
|
|
+ assert!(catch_unwind(|| log.after(end + 1)).is_err());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_between() {
|
|
|
+ let (start, end, log) = default_log_array();
|
|
|
+
|
|
|
+ let between = log.between(start + 2, end);
|
|
|
+ assert_eq!(end - start - 2, between.len());
|
|
|
+ for i in start + 2..end {
|
|
|
+ assert_eq!(between[i - start - 2].index, i);
|
|
|
+ assert_eq!(between[i - start - 2].term.0, i / 3);
|
|
|
+ }
|
|
|
+
|
|
|
+ assert!(catch_unwind(|| log.between(start - 1, end)).is_err());
|
|
|
+ assert!(catch_unwind(|| log.between(start + 2, end + 1)).is_err());
|
|
|
+ assert!(catch_unwind(|| log.between(start, end)).is_ok());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_add_command() {
|
|
|
+ let (_, end, mut log) = default_log_array();
|
|
|
+ let index = log.add_command(Term(8), 9);
|
|
|
+ assert_eq!(8, log.at(index).term.0);
|
|
|
+ assert_eq!(9, log.at(index).command);
|
|
|
+ assert_eq!(index, end);
|
|
|
+ assert_eq!(index + 1, log.len());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_push() {
|
|
|
+ let (_, end, mut log) = default_log_array();
|
|
|
+ log.push(LogEntry {
|
|
|
+ term: Term(8),
|
|
|
+ index: end,
|
|
|
+ command: 1,
|
|
|
+ });
|
|
|
+ assert_eq!(8, log.at(end).term.0);
|
|
|
+ assert_eq!(1, log.at(end).command);
|
|
|
+ assert_eq!(end + 1, log.len());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ #[should_panic]
|
|
|
+ fn test_push_small_index() {
|
|
|
+ let (_, end, mut log) = default_log_array();
|
|
|
+ log.push(LogEntry {
|
|
|
+ term: Term(8),
|
|
|
+ index: end - 1,
|
|
|
+ command: 1,
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ #[should_panic]
|
|
|
+ fn test_push_big_index() {
|
|
|
+ let (_, end, mut log) = default_log_array();
|
|
|
+ log.push(LogEntry {
|
|
|
+ term: Term(8),
|
|
|
+ index: end + 1,
|
|
|
+ command: 1,
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_truncate() {
|
|
|
+ let (start, _, mut log) = default_log_array();
|
|
|
+ log.truncate(start + 5);
|
|
|
+ assert_eq!(start + 5, log.len());
|
|
|
+ for i in start..start + 5 {
|
|
|
+ assert_eq!(log[i].index, i);
|
|
|
+ assert_eq!(log[i].term.0, i / 3);
|
|
|
+ }
|
|
|
+
|
|
|
+ log.truncate(start + 1);
|
|
|
+ assert_eq!(1, log.all().len());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ #[should_panic]
|
|
|
+ fn test_truncate_at_start() {
|
|
|
+ let (start, _, mut log) = default_log_array();
|
|
|
+ log.truncate(start);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ #[should_panic]
|
|
|
+ fn test_truncate_at_end() {
|
|
|
+ let (_, end, mut log) = default_log_array();
|
|
|
+ log.truncate(end);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ #[should_panic]
|
|
|
+ fn test_truncate_before_start() {
|
|
|
+ let (start, _, mut log) = default_log_array();
|
|
|
+ log.truncate(start - 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ #[should_panic]
|
|
|
+ fn test_truncate_after_end() {
|
|
|
+ let (_, end, mut log) = default_log_array();
|
|
|
+ log.truncate(end + 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_shift() {
|
|
|
+ let (start, end, mut log) = default_log_array();
|
|
|
+ let offset = 10;
|
|
|
+ assert!(offset > start);
|
|
|
+ assert!(offset < end);
|
|
|
+
|
|
|
+ log.shift(offset, bytes::Bytes::new());
|
|
|
+
|
|
|
+ assert_eq!((offset, Term(3)), log.first_index_term());
|
|
|
+ assert_eq!(0, log[offset].command);
|
|
|
+
|
|
|
+ let all = log.all();
|
|
|
+ assert_eq!(end - offset, all.len());
|
|
|
+ for i in offset..end {
|
|
|
+ assert_eq!(i, all[i - offset].index);
|
|
|
+ assert_eq!(i / 3, all[i - offset].term.0);
|
|
|
+ }
|
|
|
+
|
|
|
+ assert_eq!(bytes::Bytes::new(), log.snapshot);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ #[should_panic]
|
|
|
+ fn test_shift_before_start() {
|
|
|
+ let (start, end, mut log) = default_log_array();
|
|
|
+ assert!(start < end);
|
|
|
+ log.shift(start - 1, bytes::Bytes::new());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ #[should_panic]
|
|
|
+ fn test_shift_at_start() {
|
|
|
+ let (start, end, mut log) = default_log_array();
|
|
|
+ assert!(start < end);
|
|
|
+ log.shift(start, bytes::Bytes::new());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ #[should_panic]
|
|
|
+ fn test_shift_at_end() {
|
|
|
+ let (start, end, mut log) = default_log_array();
|
|
|
+ assert!(start < end);
|
|
|
+ log.shift(end, bytes::Bytes::new());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ #[should_panic]
|
|
|
+ fn test_shift_after_end() {
|
|
|
+ let (start, end, mut log) = default_log_array();
|
|
|
+ assert!(start < end);
|
|
|
+ log.shift(end + 1, bytes::Bytes::new());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_reset() {
|
|
|
+ let (start, end, mut log) = default_log_array();
|
|
|
+ let dump = log.reset(88, Term(99), bytes::Bytes::from_static(&[1, 2]));
|
|
|
+ assert_eq!(1, log.all().len());
|
|
|
+ assert_eq!(bytes::Bytes::from_static(&[1, 2]), log.snapshot);
|
|
|
+ assert_eq!(88, log[88].index);
|
|
|
+ assert_eq!(99, log[88].term.0);
|
|
|
+ assert_eq!(0, log[88].command);
|
|
|
+
|
|
|
+ assert_eq!(end - start, dump.len());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_private_accessors() {
|
|
|
+ let (start, end, log) = default_log_array();
|
|
|
+ let first = log.first_entry();
|
|
|
+ assert_eq!(start, first.index);
|
|
|
+ assert_eq!(start / 3, first.term.0);
|
|
|
+
|
|
|
+ let last = log.last_entry();
|
|
|
+ assert_eq!(end - 1, last.index);
|
|
|
+ assert_eq!((end - 1) / 3, last.term.0);
|
|
|
+
|
|
|
+ assert_eq!(10 - start, log.offset(10));
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_checks_start_index() {
|
|
|
+ let (start, end, log) = default_log_array();
|
|
|
+ assert!(start < end);
|
|
|
+ assert!(catch_unwind(|| log.check_start_index(start - 8)).is_err());
|
|
|
+ assert!(catch_unwind(|| log.check_start_index(start - 1)).is_err());
|
|
|
+ assert!(catch_unwind(|| log.check_start_index(start)).is_ok());
|
|
|
+ assert!(catch_unwind(|| log.check_start_index(start + 1)).is_ok());
|
|
|
+ assert!(catch_unwind(|| log.check_start_index(end - 1)).is_ok());
|
|
|
+ assert!(catch_unwind(|| log.check_start_index(end)).is_err());
|
|
|
+ assert!(catch_unwind(|| log.check_start_index(end + 1)).is_err());
|
|
|
+ assert!(catch_unwind(|| log.check_start_index(end + 5)).is_err());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_checks_end_index() {
|
|
|
+ let (start, end, log) = default_log_array();
|
|
|
+ assert!(start < end);
|
|
|
+ assert!(catch_unwind(|| log.check_end_index(start - 8)).is_err());
|
|
|
+ assert!(catch_unwind(|| log.check_end_index(start - 1)).is_err());
|
|
|
+ assert!(catch_unwind(|| log.check_end_index(start)).is_err());
|
|
|
+ assert!(catch_unwind(|| log.check_end_index(start + 1)).is_ok());
|
|
|
+ assert!(catch_unwind(|| log.check_end_index(end - 1)).is_ok());
|
|
|
+ assert!(catch_unwind(|| log.check_end_index(end)).is_ok());
|
|
|
+ assert!(catch_unwind(|| log.check_end_index(end + 1)).is_err());
|
|
|
+ assert!(catch_unwind(|| log.check_end_index(end + 5)).is_err());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_checks_middle_index() {
|
|
|
+ let (start, end, log) = default_log_array();
|
|
|
+ assert!(start < end);
|
|
|
+ assert!(catch_unwind(|| log.check_middle_index(start - 8)).is_err());
|
|
|
+ assert!(catch_unwind(|| log.check_middle_index(start - 1)).is_err());
|
|
|
+ assert!(catch_unwind(|| log.check_middle_index(start)).is_err());
|
|
|
+ assert!(catch_unwind(|| log.check_middle_index(start + 1)).is_ok());
|
|
|
+ assert!(catch_unwind(|| log.check_middle_index(end - 1)).is_ok());
|
|
|
+ assert!(catch_unwind(|| log.check_middle_index(end)).is_err());
|
|
|
+ assert!(catch_unwind(|| log.check_middle_index(end + 1)).is_err());
|
|
|
+ assert!(catch_unwind(|| log.check_middle_index(end + 5)).is_err());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_checks_one_element() {
|
|
|
+ let log = make_log_array(0);
|
|
|
+ assert!(catch_unwind(|| log.check_one_element()).is_err());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_integration_test() {
|
|
|
+ let mut log = make_log_array(1);
|
|
|
+ log.add_command(Term(3), 19);
|
|
|
+ log.push(LogEntry {
|
|
|
+ term: Term(3),
|
|
|
+ index: 2,
|
|
|
+ command: 3,
|
|
|
+ });
|
|
|
+ log.add_command(Term(4), 20);
|
|
|
+ log.push(LogEntry {
|
|
|
+ term: Term(4),
|
|
|
+ index: 4,
|
|
|
+ command: 7,
|
|
|
+ });
|
|
|
+
|
|
|
+ for i in 0..100 {
|
|
|
+ log.add_command(Term(5), i);
|
|
|
+ }
|
|
|
+ assert_eq!(0, log.start_offset());
|
|
|
+ assert_eq!(105, log.len());
|
|
|
+
|
|
|
+ assert_eq!((0, Term(0)), log.first_index_term());
|
|
|
+ assert_eq!((104, Term(5)), log.last_index_term());
|
|
|
+
|
|
|
+ assert_eq!(8, log.at(8).index);
|
|
|
+ assert_eq!(5, log[8].term.0);
|
|
|
+ assert_eq!(7, log[4].command);
|
|
|
+
|
|
|
+ log.truncate(50);
|
|
|
+ // End changed, start does not.
|
|
|
+ assert_eq!(0, log.start_offset());
|
|
|
+ assert_eq!(50, log.len());
|
|
|
+
|
|
|
+ assert_eq!((49, Term(5)), log.last_index_term());
|
|
|
+ assert_eq!(49, log.at(49).index);
|
|
|
+ assert_eq!(44, log[49].command);
|
|
|
+ assert_eq!(5, log.at(5).term.0);
|
|
|
+ // Cannot assert 50 is out of range. log is mut and cannot be used in
|
|
|
+ // catch_unwind().
|
|
|
+
|
|
|
+ // Snapshot is not changed.
|
|
|
+ assert_eq!(bytes::Bytes::from_static(&[1, 2, 3]), log.snapshot());
|
|
|
+
|
|
|
+ log.shift(5, bytes::Bytes::new());
|
|
|
+ // Start changed, end did not;
|
|
|
+ assert_eq!(5, log.start_offset());
|
|
|
+
|
|
|
+ assert_eq!((5, Term(5)), log.first_index_term());
|
|
|
+ assert_eq!(5, log.at(5).index);
|
|
|
+ assert_eq!(5, log.at(5).term.0);
|
|
|
+ // Cannot assert 4 is out of range. log is mut and cannot be used in
|
|
|
+ // catch_unwind().
|
|
|
+
|
|
|
+ // Snapshot is changed, too.
|
|
|
+ assert_eq!(bytes::Bytes::new(), log.snapshot());
|
|
|
+
|
|
|
+ // Ranged accessors.
|
|
|
+ assert_eq!(45, log.all().len());
|
|
|
+ assert_eq!(10, log.after(40).len());
|
|
|
+ assert_eq!(20, log.between(20, 40).len());
|
|
|
+
|
|
|
+ // Reset!
|
|
|
+ log.reset(9, Term(7), bytes::Bytes::from_static(&[7, 8, 9]));
|
|
|
+ assert_eq!(10, log.len());
|
|
|
+ assert_eq!(1, log.all().len());
|
|
|
+ assert_eq!(log.first_index_term(), log.last_index_term());
|
|
|
+ assert_eq!(bytes::Bytes::from_static(&[7, 8, 9]), log.snapshot());
|
|
|
+ }
|
|
|
+}
|