multiversx_sc_scenario/facade/
scenario_world.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use multiversx_chain_vm::world_mock::BlockchainState;

use crate::{
    scenario::{run_trace::ScenarioTrace, run_vm::ScenarioVMRunner},
    vm_go_tool::run_mx_scenario_go,
};
use multiversx_sc_meta_lib::tools::find_current_workspace;
use std::path::{Path, PathBuf};

use super::debugger_backend::DebuggerBackend;

/// A facade for contracts tests.
///
/// Contains all the context needed to execute scenarios involving contracts.
///
/// Currently defers most of the operations to the blockchain mock object directly,
/// but that one will be refactored and broken up into smaller pieces.
pub struct ScenarioWorld {
    pub(crate) current_dir: PathBuf,
    pub(crate) backend: Backend,
}

pub(crate) enum Backend {
    Debugger(Box<DebuggerBackend>),
    VmGoBackend,
}

impl Default for ScenarioWorld {
    fn default() -> Self {
        Self::debugger()
    }
}

impl ScenarioWorld {
    pub fn debugger() -> Self {
        ScenarioWorld {
            current_dir: std::env::current_dir().unwrap(),
            backend: Backend::Debugger(Box::new(DebuggerBackend {
                vm_runner: ScenarioVMRunner::new(),
                trace: None,
            })),
        }
    }

    /// Backwards compatibility only.
    pub fn new() -> Self {
        Self::debugger()
    }

    pub fn vm_go() -> Self {
        ScenarioWorld {
            current_dir: std::env::current_dir().unwrap(),
            backend: Backend::VmGoBackend,
        }
    }

    /// Runs a scenario file (`.scen.json`) with the configured backend.
    ///
    /// Will crash and produce an output if the test failed for any reason.
    pub fn run<P: AsRef<Path>>(self, relative_path: P) {
        let mut absolute_path = self.current_dir.clone();
        absolute_path.push(relative_path);
        match self.backend {
            Backend::Debugger(mut debugger) => {
                debugger.run_scenario_file(&absolute_path);
            },
            Backend::VmGoBackend => {
                run_mx_scenario_go(&absolute_path);
            },
        }
    }

    pub(crate) fn get_debugger_backend(&self) -> &DebuggerBackend {
        if let Backend::Debugger(debugger) = &self.backend {
            debugger
        } else {
            panic!("operation only available for the contract debugger backend")
        }
    }

    pub(crate) fn get_mut_debugger_backend(&mut self) -> &mut DebuggerBackend {
        if let Backend::Debugger(debugger) = &mut self.backend {
            debugger
        } else {
            panic!("operation only available for the contract debugger backend")
        }
    }

    pub(crate) fn get_state(&self) -> &BlockchainState {
        &self.get_debugger_backend().vm_runner.blockchain_mock.state
    }

    pub(crate) fn get_mut_state(&mut self) -> &mut BlockchainState {
        &mut self
            .get_mut_debugger_backend()
            .vm_runner
            .blockchain_mock
            .state
    }

    pub fn start_trace(&mut self) -> &mut Self {
        self.get_mut_debugger_backend().trace = Some(ScenarioTrace::default());
        self
    }

    /// Tells the tests where the crate lies relative to the workspace.
    /// This ensures that the paths are set correctly, including in debug mode.
    pub fn set_current_dir_from_workspace(&mut self, relative_path: &str) -> &mut Self {
        let mut path = find_current_workspace().unwrap();
        path.push(relative_path);
        self.current_dir = path;
        self
    }

    pub fn current_dir(&self) -> &PathBuf {
        &self.current_dir
    }

    /// Exports current scenario to a JSON file, as created.
    pub fn write_scenario_trace<P: AsRef<Path>>(&mut self, file_path: P) {
        if let Some(trace) = &mut self.get_mut_debugger_backend().trace {
            trace.write_scenario_trace(file_path);
        } else {
            panic!("scenario trace no initialized")
        }
    }

    #[deprecated(
        since = "0.39.0",
        note = "Renamed, use `write_scenario_trace` instead."
    )]
    pub fn write_mandos_trace<P: AsRef<Path>>(&mut self, file_path: P) {
        self.write_scenario_trace(file_path);
    }
}