diff --git a/test-validator/src/lib.rs b/test-validator/src/lib.rs index b251b2c6b..c3e8191a7 100644 --- a/test-validator/src/lib.rs +++ b/test-validator/src/lib.rs @@ -48,7 +48,9 @@ use { solana_streamer::socket::SocketAddrSpace, std::{ collections::{HashMap, HashSet}, - fs::{remove_dir_all, File}, + ffi::OsStr, + fmt::Display, + fs::{self, remove_dir_all, File}, io::Read, net::{IpAddr, Ipv4Addr, SocketAddr}, path::{Path, PathBuf}, @@ -334,6 +336,39 @@ impl TestValidatorGenesis { self } + pub fn add_accounts_from_directories(&mut self, dirs: T) -> &mut Self + where + T: IntoIterator, + P: AsRef + Display, + { + let mut json_files: HashSet = HashSet::new(); + for dir in dirs { + let matched_files = fs::read_dir(&dir) + .unwrap_or_else(|err| { + error!("Cannot read directory {}: {}", dir, err); + solana_core::validator::abort(); + }) + .flatten() + .map(|entry| entry.path()) + .filter(|path| path.is_file() && path.extension() == Some(OsStr::new("json"))) + .map(|path| String::from(path.to_string_lossy())); + + json_files.extend(matched_files); + } + + let accounts: Vec<_> = json_files + .iter() + .map(|filename| AccountInfo { + address: None, + filename, + }) + .collect(); + + self.add_accounts_from_json_files(&accounts); + + self + } + /// Add an account to the test environment with the account data in the provided `filename` pub fn add_account_with_file_data( &mut self, diff --git a/validator/src/bin/solana-test-validator.rs b/validator/src/bin/solana-test-validator.rs index 3fd6f6da6..9f8028a93 100644 --- a/validator/src/bin/solana-test-validator.rs +++ b/validator/src/bin/solana-test-validator.rs @@ -218,6 +218,30 @@ fn main() { If the ledger already exists then this parameter is silently ignored", ), ) + .arg( + Arg::with_name("accounts_from_dir") + .long("accounts-from-dir") + .value_name("DIRECTORY") + .validator(|value| { + value + .parse::() + .map_err(|err| format!("error parsing '{}': {}", value, err)) + .and_then(|path| { + if path.exists() && path.is_dir() { + Ok(()) + } else { + Err(format!("wrong directory '{}'", value)) + } + }) + }) + .takes_value(true) + .multiple(true) + .help( + "Load all the accounts from the JSON files found in the specified DIRECTORY \ + (see also the `--account` flag). \ + If the ledger already exists then this parameter is silently ignored", + ), + ) .arg( Arg::with_name("no_bpf_jit") .long("no-bpf-jit") @@ -567,6 +591,11 @@ fn main() { } } + let accounts_from_dirs: HashSet<_> = matches + .values_of("accounts_from_dir") + .unwrap_or_default() + .collect(); + let accounts_to_clone: HashSet<_> = pubkeys_of(&matches, "clone_account") .map(|v| v.into_iter().collect()) .unwrap_or_default(); @@ -727,6 +756,7 @@ fn main() { .rpc_port(rpc_port) .add_programs_with_path(&programs_to_load) .add_accounts_from_json_files(&accounts_to_load) + .add_accounts_from_directories(&accounts_from_dirs) .deactivate_features(&features_to_deactivate); if !accounts_to_clone.is_empty() {