extern crate shine_store;
extern crate shine_testutils;
#[macro_use]
extern crate log;
extern crate env_logger;
use std::env;
use std::sync::Arc;
use std::thread;
use shine_store::hashstore::*;
use shine_testutils::*;
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
struct TestDataId(u32);
impl Key for TestDataId {}
struct TestData(String);
impl TestData {
fn new(s: String) -> TestData {
trace!("creating '{}'", s);
TestData(s)
}
fn from_key(k: &TestDataId) -> TestData {
Self::new(format!("id: {}", k.0))
}
}
impl Drop for TestData {
fn drop(&mut self) {
trace!("dropping '{}'", self.0);
}
}
impl From<TestDataId> for TestData {
fn from(k: TestDataId) -> TestData {
TestData::from_key(&k)
}
}
#[test]
fn simple_single_threaded() {
init_test_logger(module_path!());
let store = HashStore::<TestDataId, TestData>::new();
let mut r0; let mut r1;
debug!("request 0,1");
{
let mut store = store.read();
assert!(store.get(&TestDataId(0)).is_null());
r0 = store.get_or_add(&TestDataId(0));
assert!(store[&r0].0 == format!("id: {}", 0));
r1 = store.get_or_add(&TestDataId(1));
assert!(store[&r1].0 == format!("id: {}", 1));
let r11 = store.get(&TestDataId(1));
assert!(store[&r11].0 == format!("id: {}", 1));
assert!(r11 == r1);
let r12 = store.get_or_add(&TestDataId(1));
assert!(store[&r12].0 == format!("id: {}", 1));
assert!(r12 == r1);
}
debug!("request process");
{
let mut store = store.write();
store.finalize_requests();
}
debug!("check 0,1, request 2");
{
let mut store = store.read();
assert!(store[&r0].0 == format!("id: {}", 0));
assert!(store.get(&TestDataId(0)) == r0);
assert!(store[&r1].0 == format!("id: {}", 1));
assert!(store.get(&TestDataId(1)) == r1);
let r2 = store.get_or_add(&TestDataId(2));
assert!(store[&r2].0 == format!("id: {}", 2));
}
debug!("drop 2");
{
let mut store = store.write();
store.finalize_requests();
store.drain_unused();
}
{
let store = store.read();
assert!(store.get(&TestDataId(2)).is_null());
assert!(store[&r0].0 == format!("id: {}", 0));
assert!(store.get(&TestDataId(0)) == r0);
assert!(store[&r1].0 == format!("id: {}", 1));
assert!(store.get(&TestDataId(1)) == r1);
r1.reset();
assert!(store[&store.get(&TestDataId(1))].0 == format!("id: {}", 1));
assert!(r1.is_null());
}
debug!("drop 1");
{
let mut store = store.write();
store.finalize_requests();
store.drain_unused();
}
{
let store = store.read();
assert!(store[&r0].0 == format!("id: {}", 0));
assert!(store.get(&TestDataId(0)) == r0);
assert!(store.get(&TestDataId(1)).is_null());
assert!(store.get(&TestDataId(2)).is_null());
r0.reset();
assert!(store[&store.get(&TestDataId(0))].0 == format!("id: {}", 0));
assert!(r0.is_null());
}
debug!("drop 0");
{
let mut store = store.write();
store.finalize_requests();
store.drain_unused();
assert!(store.is_empty());
}
}
#[test]
fn simple_multi_threaded() {
init_test_logger(module_path!());
assert!(
env::var("RUST_TEST_THREADS").unwrap_or("0".to_string()) == "1",
"This test shall run in single threaded test environment: RUST_TEST_THREADS=1"
);
let store = HashStore::<TestDataId, TestData>::new();
let store = Arc::new(store);
const ITER: u32 = 10;
{
let mut tp = vec![];
for i in 0..ITER {
let store = store.clone();
tp.push(thread::spawn(move || {
let mut store = store.read();
assert!(store.get(&TestDataId(0)).is_null());
let r1 = store.get_or_add(&TestDataId(1));
assert!(store[&r1].0 == format!("id: {}", 1));
let r100 = store.get_or_add(&TestDataId(100 + i));
assert!(store[&r100].0 == format!("id: {}", 100 + i));
for _ in 0..100 {
assert!(store[&r1].0 == format!("id: {}", 1));
assert!(store[&r100].0 == format!("id: {}", 100 + i));
}
}));
}
for t in tp.drain(..) {
t.join().unwrap();
}
}
info!("request process");
{
let mut store = store.write();
store.finalize_requests();
}
{
let mut tp = vec![];
for i in 0..ITER {
let store = store.clone();
tp.push(thread::spawn(move || {
let store = store.read();
assert!(store.get(&TestDataId(0)).is_null());
let r1 = store.get(&TestDataId(1));
assert!(!r1.is_null());
assert!(store[&r1].0 == format!("id: {}", 1));
let r100 = store.get(&TestDataId(100 + i));
assert!(!r100.is_null());
assert!(store[&r100].0 == format!("id: {}", 100 + i));
}));
}
for t in tp.drain(..) {
t.join().unwrap();
}
}
info!("drain");
{
let mut store = store.write();
store.finalize_requests();
store.drain_unused();
}
{
let mut tp = vec![];
for i in 0..ITER {
let store = store.clone();
tp.push(thread::spawn(move || {
let store = store.read();
assert!(store.get(&TestDataId(0)).is_null());
assert!(store.get(&TestDataId(1)).is_null());
assert!(store.get(&TestDataId(100 + i)).is_null());
}));
}
for t in tp.drain(..) {
t.join().unwrap();
}
}
}
#[test]
fn check_lock() {
init_test_logger(module_path!());
assert!(
env::var("RUST_TEST_THREADS").unwrap_or("0".to_string()) == "1",
"This test shall run in single threaded test environment: RUST_TEST_THREADS=1"
);
use std::mem;
use std::panic;
panic::set_hook(Box::new(|_info| { }));
{
let store = HashStore::<TestDataId, TestData>::new();
assert!(
panic::catch_unwind(|| {
let w = store.write();
let r = store.read();
drop(r);
drop(w);
}).is_err()
);
mem::forget(store);
}
{
let store = HashStore::<TestDataId, TestData>::new();
assert!(
panic::catch_unwind(|| {
let r = store.read();
let w = store.write();
drop(w);
drop(r);
}).is_err()
);
mem::forget(store);
}
{
let store = HashStore::<TestDataId, TestData>::new();
assert!(
panic::catch_unwind(|| {
let w1 = store.write();
let w2 = store.write();
drop(w2);
drop(w1);
}).is_err()
);
mem::forget(store);
}
panic::take_hook();
}