cfx_storage/tests/
mod.rs

1// Copyright 2019 Conflux Foundation. All rights reserved.
2// Conflux is free software and distributed under GNU General Public License.
3// See http://www.gnu.org/licenses/
4
5mod snapshot;
6pub use snapshot::FakeSnapshotMptDb;
7
8#[cfg(test)]
9mod proofs;
10#[cfg(test)]
11mod sharded_iter_merger;
12#[cfg(test)]
13mod state;
14
15#[cfg(test)]
16const TEST_NUMBER_OF_KEYS: usize = 100000;
17
18#[derive(Default)]
19pub struct FakeDbForStateTest {}
20
21impl KeyValueDB for FakeDbForStateTest {
22    fn get(&self, _col: u32, _key: &[u8]) -> std::io::Result<Option<DBValue>> {
23        Ok(None)
24    }
25
26    fn get_by_prefix(
27        &self, _col: u32, _prefix: &[u8],
28    ) -> std::io::Result<Option<DBValue>> {
29        unreachable!()
30    }
31
32    fn write(&self, _transaction: DBTransaction) -> std::io::Result<()> {
33        Ok(())
34    }
35
36    fn iter<'a>(
37        &'a self, _col: u32,
38    ) -> Box<dyn Iterator<Item = std::io::Result<kvdb::DBKeyValue>> + 'a> {
39        unreachable!()
40    }
41
42    fn iter_with_prefix<'a>(
43        &'a self, _col: u32, _prefix: &'a [u8],
44    ) -> Box<dyn Iterator<Item = std::io::Result<kvdb::DBKeyValue>> + 'a> {
45        unreachable!()
46    }
47}
48
49#[cfg(any(test, feature = "testonly_code"))]
50pub struct FakeStateManager {
51    data_dir: String,
52    state_manager: Option<Arc<StateManager>>,
53}
54
55#[cfg(any(test, feature = "testonly_code"))]
56impl FakeStateManager {
57    fn new(
58        conflux_data_dir: String, snapshot_epoch_count: u32,
59    ) -> Result<Self> {
60        // Use a random directory to prevent conflicts in concurrently running
61        // tests.
62        let unit_test_data_dir =
63            conflux_data_dir + &random::<u64>().to_string();
64        fs::create_dir_all(unit_test_data_dir.as_str())?;
65        let mut storage_conf = StorageConfiguration::new_default(
66            &unit_test_data_dir,
67            snapshot_epoch_count,
68            20000,
69        );
70        storage_conf.delta_mpts_cache_size = 20_000_000;
71        storage_conf.delta_mpts_cache_start_size = 1_000_000;
72        storage_conf.delta_mpts_node_map_vec_size = 20_000_000;
73        storage_conf.delta_mpts_slab_idle_size = 200_000;
74
75        Ok(FakeStateManager {
76            data_dir: unit_test_data_dir,
77            state_manager: Some(Arc::new(StateManager::new(storage_conf)?)),
78        })
79    }
80}
81
82#[cfg(any(test, feature = "testonly_code"))]
83impl Drop for FakeStateManager {
84    fn drop(&mut self) {
85        self.state_manager.take();
86        fs::remove_dir_all(self.data_dir.as_str()).ok();
87        let maybe_parent_dir = Path::new(self.data_dir.as_str()).parent();
88        if let Some(parent_dir) = maybe_parent_dir {
89            fs::remove_dir(parent_dir).ok();
90        }
91    }
92}
93
94#[cfg(any(test, feature = "testonly_code"))]
95impl Deref for FakeStateManager {
96    type Target = Arc<StateManager>;
97
98    fn deref(&self) -> &Self::Target { self.state_manager.as_ref().unwrap() }
99}
100
101#[cfg(any(test, feature = "testonly_code"))]
102impl DerefMut for FakeStateManager {
103    fn deref_mut(&mut self) -> &mut Self::Target {
104        self.state_manager.as_mut().unwrap()
105    }
106}
107
108#[cfg(any(test, feature = "testonly_code"))]
109pub fn new_state_manager_for_unit_test_with_snapshot_epoch_count(
110    snapshot_epoch_count: u32,
111) -> FakeStateManager {
112    const WITH_LOGGER: bool = false;
113    if WITH_LOGGER {
114        log4rs::init_config(
115            log4rs::config::Config::builder()
116                .appender(
117                    log4rs::config::Appender::builder().build(
118                        "stdout",
119                        Box::new(
120                            log4rs::append::console::ConsoleAppender::builder()
121                                .build(),
122                        ),
123                    ),
124                )
125                .build(
126                    log4rs::config::Root::builder()
127                        .appender("stdout")
128                        .build(log::LevelFilter::Debug),
129                )
130                .unwrap(),
131        )
132        .ok();
133    }
134
135    FakeStateManager::new(
136        "./conflux_unit_test_data_dir".to_string(),
137        snapshot_epoch_count,
138    )
139    .unwrap()
140}
141
142#[cfg(any(test, feature = "testonly_code"))]
143pub fn new_state_manager_for_unit_test() -> FakeStateManager {
144    let snapshot_epoch_count = 10;
145    new_state_manager_for_unit_test_with_snapshot_epoch_count(
146        snapshot_epoch_count,
147    )
148}
149
150#[derive(Default)]
151pub struct DumpedMptKvIterator {
152    pub kv: Vec<MptKeyValue>,
153}
154
155pub struct DumpedMptKvFallibleIterator {
156    pub kv: Vec<MptKeyValue>,
157    pub index: usize,
158}
159
160impl DumpedMptKvIterator {
161    pub fn iterate<'a, DeltaMptDumper: KVInserter<MptKeyValue>>(
162        &self, dumper: &mut DeltaMptDumper,
163    ) -> Result<()> {
164        let mut sorted_kv = self.kv.clone();
165        sorted_kv.sort();
166        for kv_item in sorted_kv {
167            dumper.push(kv_item)?;
168        }
169        Ok(())
170    }
171}
172
173impl KVInserter<MptKeyValue> for DumpedMptKvIterator {
174    fn push(&mut self, v: MptKeyValue) -> Result<()> {
175        let (mpt_key, value) = v;
176        let snapshot_key =
177            StorageKeyWithSpace::from_delta_mpt_key(&mpt_key).to_key_bytes();
178
179        self.kv.push((snapshot_key, value));
180        Ok(())
181    }
182}
183
184impl FallibleIterator for DumpedMptKvFallibleIterator {
185    type Error = Error;
186    type Item = MptKeyValue;
187
188    fn next(&mut self) -> Result<Option<Self::Item>> {
189        let result = Ok(self.kv.get(self.index).cloned());
190        self.index += 1;
191        result
192    }
193}
194
195#[cfg(test)]
196fn generate_keys(number_of_keys: usize) -> Vec<Vec<u8>> {
197    let mut rng = get_rng_for_test();
198
199    let mut keys_num: Vec<u64> = Default::default();
200
201    for _i in 0..number_of_keys {
202        keys_num.push(rng.gen());
203    }
204
205    keys_num.sort();
206
207    let mut keys = vec![];
208    let mut last_key = keys_num[0];
209    for key in &keys_num[1..number_of_keys] {
210        if *key != last_key {
211            keys.push(Vec::from(key.to_ne_bytes()));
212        }
213        last_key = *key;
214    }
215
216    keys.shuffle(&mut rng);
217    keys
218}
219
220#[cfg(test)]
221fn generate_account_keys(number_of_keys: usize) -> Vec<Vec<u8>> {
222    let mut rng = get_rng_for_test();
223    (0..number_of_keys)
224        .map(|_| rng.gen::<[u8; 20]>().to_vec())
225        .collect()
226}
227
228#[cfg(test)]
229fn get_rng_for_test() -> ChaChaRng { ChaChaRng::from_seed([123; 32]) }
230
231// Kept for debugging.
232#[allow(dead_code)]
233pub fn print_mpt_key(key: &[u8]) {
234    print!("key = (");
235    for char in key {
236        print!(
237            "{}, {}, ",
238            CompressedPathRaw::first_nibble(*char),
239            CompressedPathRaw::second_nibble(*char)
240        );
241    }
242    println!(")");
243}
244
245#[cfg(any(test, feature = "testonly_code"))]
246use crate::{impls::state_manager::StateManager, StorageConfiguration};
247use crate::{
248    impls::{
249        errors::*,
250        merkle_patricia_trie::{CompressedPathRaw, MptKeyValue},
251    },
252    KVInserter,
253};
254use fallible_iterator::FallibleIterator;
255use kvdb::{DBTransaction, DBValue, KeyValueDB};
256use primitives::StorageKeyWithSpace;
257#[cfg(any(test, feature = "testonly_code"))]
258use rand::random;
259#[cfg(test)]
260use rand::{seq::SliceRandom, Rng, SeedableRng};
261#[cfg(test)]
262use rand_chacha::ChaChaRng;
263#[cfg(any(test, feature = "testonly_code"))]
264use std::{
265    fs,
266    ops::{Deref, DerefMut},
267    path::Path,
268    sync::Arc,
269};