Skip to content

Commit

Permalink
runtime-tester: add support for specifying home directory
Browse files Browse the repository at this point in the history
When debugging a test it may be desired to be able to see the state of
the storage after the tests finishes.  Currently, scenarios which use
on-disk storage will use a temporary directory which will be deleted
after the scenario runs.  This of course is correct behaviour but it
makes it impossible to investigate the storage of a failing (or
otherwise misbehaving) test.

Provide a Scenario::home_dir field which specifies where the store
will be located.  Importantly, Secnario::run will not delete that
directory after executing the scenario thus letting the user see
the end result.

Issue: near#4771
  • Loading branch information
mina86 committed Sep 23, 2021
1 parent b426e31 commit eb170c1
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 14 deletions.
2 changes: 1 addition & 1 deletion test-utils/runtime-tester/src/fuzzing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl Arbitrary<'_> for Scenario {
while blocks.len() < MAX_BLOCKS && u.len() > BlockConfig::size_hint(0).0 {
blocks.push(BlockConfig::arbitrary(u, &mut scope)?);
}
Ok(Scenario { network_config, blocks, use_in_memory_store: true })
Ok(Scenario { network_config, blocks, use_in_memory_store: true, home_dir: None })
}

fn size_hint(_depth: usize) -> (usize, Option<usize>) {
Expand Down
1 change: 1 addition & 0 deletions test-utils/runtime-tester/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ fn scenario_smoke_test() {
network_config: NetworkConfig { seeds: seeds },
blocks: Vec::new(),
use_in_memory_store: true,
home_dir: None,
};

for h in 1..5 {
Expand Down
48 changes: 36 additions & 12 deletions test-utils/runtime-tester/src/run_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::path::Path;
use std::sync::Arc;
use std::time::{Duration, Instant};

use near_chain::{Block, ChainGenesis, Provenance, RuntimeAdapter};
use near_chain::{Block, ChainGenesis, Provenance};
use near_chain_configs::Genesis;
use near_client::test_utils::TestEnv;
use near_client_primitives::types::Error;
Expand All @@ -29,30 +29,34 @@ impl Scenario {
);

let tempdir;
let store = if self.use_in_memory_store {
create_test_store()
let home_dir = if self.use_in_memory_store {
Path::new(".")
} else if let Some(home_dir) = &self.home_dir {
home_dir.as_path()
} else {
tempdir = tempfile::tempdir().map_err(|err| {
Error::Other(format!("failed to create temporary directory: {}", err))
})?;
create_store(tempdir.path())
tempdir.path()
};
let store = if self.use_in_memory_store {
create_test_store()
} else {
create_store(&nearcore::get_store_path(home_dir))
};

let mut env = TestEnv::new_with_runtime(
ChainGenesis::from(&genesis),
1,
1,
vec![Arc::new(NightshadeRuntime::new(
Path::new("."),
let mut env = TestEnv::builder(ChainGenesis::from(&genesis))
.runtime_adapters(vec![Arc::new(NightshadeRuntime::new(
home_dir,
store,
&genesis,
vec![],
vec![],
None,
None,
RuntimeConfigStore::test(),
)) as Arc<dyn RuntimeAdapter>],
);
))])
.build();

let mut last_block = env.clients[0].chain.get_block_by_height(0).unwrap().clone();

Expand Down Expand Up @@ -86,6 +90,26 @@ pub struct Scenario {
pub network_config: NetworkConfig,
pub blocks: Vec<BlockConfig>,
pub use_in_memory_store: bool,

/// Path to the on-disk home directory; relevant only if
/// `use_in_memory_store` is `false`.
///
/// If this is `None` (and scenario is configured with an on-disk store) the
/// scenario will be run with a temporary directory as home directory. Once
/// the scenario finishes, that temporary directory is deleted. On the
/// other hand, if this is set it is caller’s responsible to clean the
/// directory *before and after* the test runs.
///
/// This is useful when wanting to save the storage on-disk and investigate
/// it after the test runs and it’s also why the directory is not cleared.
///
/// Note that this field is not serialised thus a scenario read from file
/// won’t have this path set. This is motivated partially by security
/// considerations since if this could be stored in JSON file than user
/// might be tricked to run a test against `/home/foo/.near/mainnet`
/// directory.
#[serde(skip_serializing)]
pub home_dir: Option<std::path::PathBuf>,
}

#[derive(Serialize, Deserialize)]
Expand Down
22 changes: 21 additions & 1 deletion test-utils/runtime-tester/src/scenario_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ impl ScenarioBuilder {
ScenarioBuilder {
height: 1,
nonce: 1,
scenario: Scenario { network_config, blocks: vec![], use_in_memory_store: true },
scenario: Scenario {
network_config,
blocks: vec![],
use_in_memory_store: true,
home_dir: None,
},
}
}

Expand All @@ -68,6 +73,21 @@ impl ScenarioBuilder {
self
}

/// Changes `home_dir` to the one specified as well as setting
/// `use_in_memory_store` to `false`.
///
/// Note that if all you want is to use on-disk store you should call
/// `in_memory_store(false)`. Setting home directory means that the caller
/// is responsible for cleaning the directory before and after the test is
/// run. This setting is intended only for one-off cases where someone
/// wants to manually inspect the store and thus does not want the home
/// directory to be deleted after the test finishes.
pub fn home_dir(mut self, home_dir: impl Into<std::path::PathBuf>) -> Self {
self.scenario.use_in_memory_store = false;
self.scenario.home_dir = Some(home_dir.into());
self
}

/// Adds empty block to the scenario with the next height (starting from 1).
pub fn add_block(&mut self) {
self.scenario.blocks.push(BlockConfig::at_height(self.height));
Expand Down

0 comments on commit eb170c1

Please sign in to comment.