Skip to content

Commit

Permalink
Add option to disable test snapshots on Env (#1235)
Browse files Browse the repository at this point in the history
### What
Add option to disable test snapshots on Env

### Why
In some situations a developer needs to disable test snapshots. The
default is that they are always on, in the interest of giving all
developers a way to identify unexpected and otherwise untested for
changed behavior during SDK upgrades, no protocol releases, or their own
developer lifecycle.

Close #1225

### Merging
This change is dependent on the following change merging first, and will
be kept in draft state until then:
- #1234
  • Loading branch information
leighmcculloch authored Mar 1, 2024
1 parent 9590be0 commit 09b3a4c
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 6 deletions.
32 changes: 28 additions & 4 deletions soroban-sdk/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,18 +235,38 @@ impl Default for Env {

#[cfg(any(test, feature = "testutils"))]
fn default() -> Self {
Self::default_with_testutils()
Self::new_with_config(EnvTestConfig::default())
}
}

#[cfg(any(test, feature = "testutils"))]
#[derive(Clone, Default)]
struct EnvTestState {
config: EnvTestConfig,
generators: Rc<RefCell<Generators>>,
auth_snapshot: Rc<RefCell<AuthSnapshot>>,
snapshot: Option<Rc<LedgerSnapshot>>,
}

/// Config for changing the default behavior of the Env when used in tests.
#[cfg(any(test, feature = "testutils"))]
#[derive(Clone)]
pub struct EnvTestConfig {
/// Capture a test snapshot when the Env is dropped, causing a test snapshot
/// JSON file to be written to disk when the Env is no longer referenced.
/// Defaults to true.
pub capture_snapshot_at_drop: bool,
}

#[cfg(any(test, feature = "testutils"))]
impl Default for EnvTestConfig {
fn default() -> Self {
Self {
capture_snapshot_at_drop: true,
}
}
}

impl Env {
/// Panic with the given error.
///
Expand Down Expand Up @@ -457,7 +477,7 @@ impl Env {
f((*self.test_state.generators).borrow_mut())
}

fn default_with_testutils() -> Env {
pub fn new_with_config(config: EnvTestConfig) -> Env {
struct EmptySnapshotSource();

impl internal::storage::SnapshotSource for EmptySnapshotSource {
Expand Down Expand Up @@ -487,11 +507,12 @@ impl Env {
max_entry_ttl: 6_312_000,
};

Env::new_for_testutils(rf, None, info, None)
Env::new_for_testutils(config, rf, None, info, None)
}

/// Used by multiple constructors to configure test environments consistently.
fn new_for_testutils(
config: EnvTestConfig,
recording_footprint: Rc<dyn internal::storage::SnapshotSource>,
generators: Option<Rc<RefCell<Generators>>>,
ledger_info: internal::LedgerInfo,
Expand Down Expand Up @@ -531,6 +552,7 @@ impl Env {
let env = Env {
env_impl,
test_state: EnvTestState {
config,
generators: generators.unwrap_or_default(),
snapshot,
auth_snapshot,
Expand Down Expand Up @@ -1219,6 +1241,7 @@ impl Env {
/// Events, as an output source only, are not loaded into the Env.
pub fn from_snapshot(s: Snapshot) -> Env {
Env::new_for_testutils(
EnvTestConfig::default(), // TODO: Allow setting the config.
Rc::new(s.ledger.clone()),
Some(Rc::new(RefCell::new(s.generators))),
s.ledger.ledger_info(),
Expand Down Expand Up @@ -1263,6 +1286,7 @@ impl Env {
/// The ledger info and state in the snapshot are loaded into the Env.
pub fn from_ledger_snapshot(s: LedgerSnapshot) -> Env {
Env::new_for_testutils(
EnvTestConfig::default(), // TODO: Allow setting the config.
Rc::new(s.clone()),
None,
s.ledger_info(),
Expand Down Expand Up @@ -1329,7 +1353,7 @@ impl Drop for Env {
// snapshot at that point when no other references to the host exist,
// because it is only when there are no other references that the host
// is being dropped.
if self.env_impl.can_finish() {
if self.env_impl.can_finish() && self.test_state.config.capture_snapshot_at_drop {
self.to_test_snapshot_file();
}
}
Expand Down
3 changes: 1 addition & 2 deletions soroban-sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -725,9 +725,8 @@ mod env;
mod address;
mod symbol;

pub use env::ConversionError;
pub use env::{ConversionError, Env};

pub use env::Env;
/// Raw value of the Soroban smart contract platform that types can be converted
/// to and from for storing, or passing between contracts.
///
Expand Down
28 changes: 28 additions & 0 deletions soroban-sdk/src/tests/env.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::{
self as soroban_sdk, contract, contractimpl,
env::EnvTestConfig,
testutils::{Address as _, Logs as _},
Address, Env, Error,
};
Expand Down Expand Up @@ -147,3 +148,30 @@ fn test_snapshot_file() {
let _ = std::fs::remove_file(&p1);
let _ = std::fs::remove_file(&p2);
}

/// Test that the test snapshot file is not written when disabled.
#[test]
fn test_snapshot_file_disabled() {
let p = std::path::Path::new("test_snapshots")
.join("tests")
.join("env")
.join("test_snapshot_file_disabled");
let p1 = p.with_extension("1.json");
assert!(!p1.exists());
let p2 = p.with_extension("2.json");
assert!(!p2.exists());
{
let e1 = Env::default();
let _ = e1.register_contract(None, Contract);
let e2 = Env::new_with_config(EnvTestConfig {
capture_snapshot_at_drop: false,
});
let _ = e2.register_contract(None, Contract);
assert!(!p1.exists());
assert!(!p2.exists());
}
assert!(p1.exists());
assert!(!p2.exists());
let _ = std::fs::remove_file(&p1);
let _ = std::fs::remove_file(&p2);
}
2 changes: 2 additions & 0 deletions soroban-sdk/src/testutils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub mod storage;
use crate::{xdr, Env, Val, Vec};
use soroban_ledger_snapshot::LedgerSnapshot;

pub use crate::env::EnvTestConfig;

#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct Snapshot {
Expand Down

0 comments on commit 09b3a4c

Please sign in to comment.