1
mod helpers;
2

3
use helpers::{create_and_drop, CountDown};
4
use persy::{Config, OpenOptions, Persy, PersyError};
5
use std::fs;
6
use std::sync::atomic::{AtomicBool, Ordering::Relaxed};
7
use std::sync::Arc;
8
use std::thread;
9
use tempfile::{tempfile, Builder};
10

11
#[test]
12 1
fn openoptions_truncate_locked() {
13 1
    {
14
        // first time create
15 1
        let _persy_create = OpenOptions::new()
16
            .create_new(true)
17 1
            .prepare_with(|persy: &Persy| {
18 1
                let mut tx = persy.begin()?;
19 1
                tx.create_segment("seg")?;
20

21 1
                let data = b"hello";
22 1
                tx.insert("seg", &data[..])?;
23

24 1
                let prepared = tx.prepare()?;
25 1
                prepared.commit()
26 1
            })
27
            .open("./target/truncate-locked.persy")
28 1
            .unwrap();
29 0
    }
30

31
    // first acquire the file lock
32 1
    let persy_lock = OpenOptions::new().open("./target/truncate-locked.persy").unwrap();
33

34
    // then try to truncate it
35 1
    let error = OpenOptions::new()
36
        .truncate(true)
37
        .open("./target/truncate-locked.persy")
38
        .map(drop)
39 1
        .unwrap_err();
40

41
    // it must be unsuccessful
42
    match error {
43 1
        PersyError::Io(_) => (),
44 0
        otherwise => panic!("error is {:?} and not an Io error", otherwise),
45
    }
46

47
    // the file must not have been truncated
48
    // and contains the segment data
49 1
    let any_hello = persy_lock.scan("seg").unwrap().any(|(_, content)| content == b"hello");
50 1
    assert!(any_hello);
51

52 1
    fs::remove_file("./target/truncate-locked.persy").unwrap();
53 1
}
54

55
#[test]
56 1
fn openoptions_create() {
57
    {
58
        // first time create
59 1
        let _persy_create = OpenOptions::new()
60
            .create_new(true)
61
            .open("./target/create.persy")
62 1
            .unwrap();
63 0
    }
64

65
    // second time create
66 1
    let _persy_create = OpenOptions::new().create(true).open("./target/create.persy").unwrap();
67

68 1
    fs::remove_file("./target/create.persy").unwrap();
69 1
}
70

71
#[test]
72 1
fn openoptions_create_new() {
73
    // first time create
74 1
    let _persy_create = OpenOptions::new()
75
        .create_new(true)
76
        .open("./target/create-new.persy")
77 1
        .unwrap();
78

79
    // second time create
80 1
    let error = OpenOptions::new()
81
        .create_new(true)
82
        .open("./target/create-new.persy")
83
        .map(drop)
84 1
        .unwrap_err();
85

86
    match error {
87 1
        PersyError::Io(_) => (),
88 0
        otherwise => panic!("error is {:?} and not an Io error", otherwise),
89
    }
90

91 1
    fs::remove_file("./target/create-new.persy").unwrap();
92 1
}
93

94
#[test]
95 1
fn openoptions_open() {
96
    {
97
        // first time create
98 1
        let _persy_create = OpenOptions::new().create_new(true).open("./target/open.persy").unwrap();
99 0
    }
100

101
    // second time create
102 1
    let _persy_open = OpenOptions::new().open("./target/open.persy").unwrap();
103

104 1
    fs::remove_file("./target/open.persy").unwrap();
105 1
}
106

107
#[test]
108 1
fn openoptions_memory() {
109 1
    let persy_open = OpenOptions::new().memory().unwrap();
110 1
    let mut tx = persy_open.begin().unwrap();
111 1
    tx.create_segment("one").unwrap();
112 1
    tx.insert("one", "".as_bytes()).unwrap();
113 1
    tx.prepare().unwrap().commit().unwrap();
114 1
}
115

116
#[test]
117 1
fn openoptions_prepare() {
118
    {
119
        // first time create
120 1
        let _persy_create = OpenOptions::new()
121
            .create_new(true)
122
            .open("./target/prepare.persy")
123 1
            .unwrap();
124 0
    }
125

126
    {
127
        // do not prepare if already exists and not truncated
128 1
        let _persy_prepare = OpenOptions::new()
129 0
            .prepare_with(|_persy: &Persy| panic!("prepare has been called"))
130
            .open("./target/prepare.persy")
131 1
            .unwrap();
132 0
    }
133

134 1
    let as_been_initialized = Arc::new(AtomicBool::new(false));
135

136
    // second time create
137 1
    let _persy_open = OpenOptions::new()
138
        .truncate(true)
139
        .prepare_with({
140 1
            let as_been_initialized = as_been_initialized.clone();
141 1
            move |_persy: &Persy| {
142 1
                as_been_initialized.store(true, Relaxed);
143 1
                Ok(())
144 1
            }
145
        })
146
        .open("./target/prepare.persy")
147 1
        .unwrap();
148

149
    // the truncation must have trigger the prepare function
150 1
    assert_eq!(as_been_initialized.load(Relaxed), true);
151

152 1
    fs::remove_file("./target/prepare.persy").unwrap();
153 1
}
154

155
#[test]
156 1
fn create() {
157 1
    Persy::create_from_file(tempfile().unwrap()).unwrap();
158 1
}
159

160
#[test]
161 1
fn lock_double_open() {
162
    {
163 1
        Persy::create("./target/file_dd.persy").unwrap();
164 1
        let open = Persy::open("./target/file_dd.persy", Config::new());
165 1
        assert!(!open.is_err());
166 1
        let open1 = Persy::open("./target/file_dd.persy", Config::new());
167 1
        assert!(open1.is_err());
168 1
    }
169 1
    fs::remove_file("./target/file_dd.persy").unwrap();
170 1
}
171

172
#[test]
173 1
fn fail_double_create() {
174
    {
175 1
        let res = Persy::create("./target/file2.persy");
176 1
        assert!(!res.is_err());
177 0
    }
178 1
    let res = Persy::create("./target/file2.persy");
179 1
    fs::remove_file("./target/file2.persy").unwrap();
180 1
    assert!(res.is_err());
181 1
}
182

183
#[test]
184 1
fn create_open() {
185 1
    let file = Builder::new()
186
        .prefix("open")
187
        .suffix(".persy")
188
        .tempfile()
189
        .expect("expect temp file creation");
190 1
    Persy::create_from_file(file.reopen().unwrap()).unwrap();
191 1
    let open = Persy::open_from_file(file.reopen().unwrap(), Config::new());
192 1
    assert!(!open.is_err());
193 1
}
194

195
#[test]
196 1
fn test_rollback() {
197 1
    create_and_drop("rollback", |persy| {
198 1
        let mut tx = persy.begin().unwrap();
199 1
        tx.create_segment("test").unwrap();
200 1
        let finalizer = tx.prepare().unwrap();
201 1
        finalizer.commit().unwrap();
202

203 1
        let mut tx = persy.begin().unwrap();
204 1
        let rec_data: String = "something".into();
205 1
        let bytes = rec_data.into_bytes();
206 1
        let id = tx.insert("test", &bytes).unwrap();
207 1
        tx.rollback().unwrap();
208 1
        let read_after = persy.read("test", &id).unwrap();
209 1
        if let Some(_) = read_after {
210 0
            assert!(false);
211
        } else {
212
            assert!(true);
213
        }
214 1
    });
215 1
}
216

217
#[test]
218 1
fn test_rollback_precommit() {
219 1
    create_and_drop("rollback_pre", |persy| {
220 1
        let mut tx = persy.begin().unwrap();
221 1
        tx.create_segment("test").unwrap();
222 1
        let finalizer = tx.prepare().unwrap();
223 1
        finalizer.commit().unwrap();
224

225 1
        let mut tx = persy.begin().unwrap();
226 1
        let rec_data: String = "something".into();
227 1
        let bytes = rec_data.into_bytes();
228 1
        let id = tx.insert("test", &bytes).unwrap();
229 1
        let finalizer = tx.prepare().unwrap();
230 1
        finalizer.rollback().unwrap();
231 1
        let read_after = persy.read("test", &id).unwrap();
232 1
        if let Some(_) = read_after {
233 0
            assert!(false);
234
        } else {
235
            assert!(true);
236
        }
237 1
    });
238 1
}
239

240
#[test]
241 1
fn test_rollback_update() {
242 1
    create_and_drop("rollback_update", |persy| {
243 1
        let mut tx = persy.begin().unwrap();
244 1
        tx.create_segment("test").unwrap();
245 1
        let rec_data: String = "something".into();
246 1
        let bytes = rec_data.into_bytes();
247 1
        let id = tx.insert("test", &bytes).unwrap();
248 1
        let read_opt = tx.read("test", &id).unwrap();
249 1
        if let Some(read) = read_opt {
250 1
            assert_eq!(bytes, read);
251 0
        } else {
252 0
            assert!(false);
253
        }
254 1
        let finalizer = tx.prepare().unwrap();
255 1
        finalizer.commit().unwrap();
256

257 1
        let mut tx1 = persy.begin().unwrap();
258 1
        let rec_data_1: String = "something2".into();
259 1
        let bytes_1 = rec_data_1.into_bytes();
260 1
        tx1.update("test", &id, &bytes_1).unwrap();
261 1
        let read_after = tx1.read("test", &id).unwrap();
262 1
        if let Some(val) = read_after {
263 1
            assert_eq!(val, bytes_1);
264 0
        } else {
265 0
            assert!(false);
266
        }
267 1
        tx1.rollback().unwrap();
268

269 1
        let read_after = persy.read("test", &id).unwrap();
270 1
        if let Some(val) = read_after {
271 1
            assert_eq!(val, bytes);
272 0
        } else {
273 0
            assert!(false);
274
        }
275 1
    });
276 1
}
277

278
#[test]
279 1
pub fn concurrent_create() {
280 1
    create_and_drop("rollback_create", |persy| {
281 1
        let mut tx = persy.begin().expect("error on transaction begin");
282 1
        tx.create_segment("def").expect("error on segment creation");
283 1
        let fin = tx.prepare().expect("error on commit prepare");
284 1
        fin.commit().expect("error on commit");
285

286 1
        let count = Arc::new(CountDown::new(2));
287

288 1
        for _ in &[1, 2] {
289 1
            let count = count.clone();
290 1
            let persy = persy.clone();
291 1
            thread::spawn(move || {
292 1
                let mut tx = persy.begin().expect("error on transaction begin");
293 1
                let val = String::from("aaa").into_bytes();
294 1
                tx.insert("def", &val).expect("error on insert value");
295 1
                let fin = tx.prepare().expect("error on commit prepare");
296 1
                fin.commit().expect("error on commit");
297 1
                count.count_down().expect("lock not panic");
298 1
            });
299 1
        }
300

301 1
        count.wait().expect("threads not finisced");
302

303 1
        let val = String::from("aaa").into_bytes();
304 1
        let mut cc = 0;
305 1
        for (_, content) in persy.scan("def").expect("error on scan") {
306 1
            assert_eq!(content, val);
307 1
            cc += 1;
308 1
        }
309 1
        assert_eq!(cc, 2);
310 1
    });
311 1
}
312

313
#[test]
314 1
pub fn concurrent_update_removed() {
315 1
    create_and_drop("concurrent_update_remove", |persy| {
316 1
        let mut tx = persy.begin().expect("error on transaction begin");
317 1
        tx.create_segment("def").expect("error on segment creation");
318 1
        let fin = tx.prepare().expect("error on commit prepare");
319 1
        fin.commit().expect("error on commit");
320

321 1
        let mut tx = persy.begin().expect("error on transaction begin");
322 1
        let val = String::from("aaa").into_bytes();
323 1
        let id = tx.insert("def", &val).expect("error on inser value");
324 1
        let fin = tx.prepare().expect("error on commit prepare");
325 1
        fin.commit().expect("error on commit");
326

327 1
        let mut tx = persy.begin().expect("error on transaction begin");
328 1
        let val = String::from("cccc").into_bytes();
329 1
        tx.update("def", &id, &val).expect("error on update value");
330

331 1
        let count = Arc::new(CountDown::new(1));
332

333
        {
334 1
            let count = count.clone();
335 1
            let persy = persy.clone();
336 1
            let id = id.clone();
337 1
            thread::spawn(move || {
338 1
                let mut tx = persy.begin().expect("error on transaction begin");
339 1
                tx.delete("def", &id).expect("error on delete value");
340 1
                let fin = tx.prepare().expect("error on commit prepare");
341 1
                fin.commit().expect("error on commit");
342 1
                count.count_down().expect("lock not panic");
343 1
            });
344 1
        }
345

346 1
        count.wait().expect("threads not finisced");
347 1
        let fin = tx.prepare();
348 1
        assert!(fin.is_err());
349 1
    });
350 1
}
351

352
#[test]
353
#[allow(unused_must_use)]
354 1
pub fn test_rollback_prepared_tx() {
355 1
    Persy::create("./target/test_recover_rollback_prepared.persy").unwrap();
356
    let id;
357
    let val;
358
    {
359 1
        let persy = Persy::open("./target/test_recover_rollback_prepared.persy", Config::new()).unwrap();
360 1
        let mut tx = persy.begin().expect("error on transaction begin");
361 1
        tx.create_segment("def").expect("error on segment creation");
362 1
        let fin = tx.prepare().expect("error on commit prepare");
363 1
        fin.commit().expect("error on commit");
364

365 1
        let mut tx = persy.begin().expect("error on transaction begin");
366 1
        val = String::from("aaa").into_bytes();
367 1
        id = tx.insert("def", &val).expect("error on insert value");
368 1
        tx.prepare().expect("error on commit prepare");
369 1
    }
370
    {
371 1
        let persy = Persy::open_with_recover("./target/test_recover_rollback_prepared.persy", Config::new(), |_| {
372
            false
373 0
        })
374
        .unwrap();
375 1
        assert_eq!(persy.read("def", &id).expect("error reading record"), None);
376 1
    }
377

378 1
    fs::remove_file("./target/test_recover_rollback_prepared.persy").unwrap();
379 1
}
380

381
#[test]
382
#[allow(unused_must_use)]
383 1
pub fn test_autorollback_lost_finalize() {
384 1
    Persy::create("./target/test_auto_rollback.persy").unwrap();
385
    let id;
386
    {
387 1
        let persy = Persy::open("./target/test_auto_rollback.persy", Config::new()).unwrap();
388 1
        let mut tx = persy.begin().expect("error on transaction begin");
389 1
        tx.create_segment("def").expect("error on segment creation");
390 1
        let fin = tx.prepare().expect("error on commit prepare");
391 1
        fin.commit().expect("error on commit");
392

393 1
        let mut tx = persy.begin().expect("error on transaction begin");
394 1
        let val = String::from("aaa").into_bytes();
395 1
        id = tx.insert("def", &val).expect("error on insert value");
396 1
        tx.prepare().expect("error on commit prepare");
397 1
    }
398
    {
399 1
        let persy = Persy::open("./target/test_auto_rollback.persy", Config::new()).unwrap();
400 1
        assert_eq!(persy.read("def", &id).expect("error reading record"), None);
401 1
    }
402

403 1
    fs::remove_file("./target/test_auto_rollback.persy").unwrap();
404 1
}
405

406
#[test]
407 1
pub fn test_recover_stale_tx() {
408 1
    Persy::create("./target/test_recover_stale.persy").unwrap();
409
    let id;
410
    {
411 1
        let persy = Persy::open("./target/test_recover_stale.persy", Config::new()).unwrap();
412 1
        let mut tx = persy.begin().expect("error on transaction begin");
413 1
        tx.create_segment("def").expect("error on segment creation");
414 1
        let fin = tx.prepare().expect("error on commit prepare");
415 1
        fin.commit().expect("error on commit");
416

417 1
        let mut tx = persy.begin().expect("error on transaction begin");
418 1
        let val = String::from("aaa").into_bytes();
419 1
        id = tx.insert("def", &val).expect("error on insert value");
420 1
    }
421
    {
422 1
        let persy = Persy::open("./target/test_recover_stale.persy", Config::new()).unwrap();
423 1
        assert_eq!(persy.read("def", &id).expect("error reading record"), None);
424 1
    }
425

426 1
    fs::remove_file("./target/test_recover_stale.persy").unwrap();
427 1
}
428

429
#[test]
430 1
pub fn test_multiple_open_tx_close() {
431 1
    let file = Builder::new()
432
        .prefix("multiple_open_tx_close")
433
        .suffix(".persy")
434
        .tempfile()
435
        .expect("expect temp file creation");
436 1
    Persy::create_from_file(file.reopen().unwrap()).unwrap();
437
    {
438 1
        let persy = Persy::open_from_file(file.reopen().unwrap(), Config::new()).unwrap();
439 1
        let mut tx = persy.begin().expect("error on transaction begin");
440 1
        tx.create_segment("def").expect("error on segment creation");
441 1
        let fin = tx.prepare().expect("error on commit prepare");
442 1
        fin.commit().expect("error on commit");
443 1
    }
444 1
    for ite in 1..10 {
445 1
        let persy = Persy::open_from_file(file.reopen().unwrap(), Config::new()).unwrap();
446 1
        let mut tx = persy.begin().expect("error on transaction begin");
447 1
        let val = String::from("aaa").into_bytes();
448 1
        tx.insert("def", &val).expect("error on insert value");
449 1
        let fin = tx.prepare().expect("error on commit prepare");
450 1
        fin.commit().expect("error on commit");
451 1
        let mut counter = 0;
452 1
        for _ in persy.scan("def").expect("read persistent records ") {
453 1
            counter += 1;
454 1
        }
455 1
        assert_eq!(ite, counter);
456 1
    }
457 1
}
458

459
#[cfg(feature = "background_ops")]
460
#[test]
461 1
pub fn test_background_sync() {
462
    use persy::TransactionConfig;
463 1
    create_and_drop("background_sync", |persy| {
464 1
        let mut tx = persy.begin().unwrap();
465 1
        tx.create_segment("test").unwrap();
466 1
        let finalizer = tx.prepare().unwrap();
467 1
        finalizer.commit().unwrap();
468

469 1
        let mut tx = persy
470 1
            .begin_with(TransactionConfig::new().set_background_sync(true))
471 1
            .unwrap();
472 1
        let rec_data: String = "something".into();
473 1
        let bytes = rec_data.into_bytes();
474 1
        let id = tx.insert("test", &bytes).unwrap();
475 1
        let prep = tx.prepare().unwrap();
476 1
        prep.commit().unwrap();
477 1
        let read_after = persy.read("test", &id).unwrap();
478 1
        assert!(read_after.is_some());
479 1
    });
480 1
}

Read our documentation on viewing source code .

Loading