1use clap::Parser;
5use move_cli::base::{
6 self,
7 test::{self, UnitTestResult},
8};
9use move_package::BuildConfig;
10use move_unit_test::{UnitTestingConfig, extensions::set_extension_hook};
11use move_vm_runtime::native_extensions::NativeContextExtensions;
12use once_cell::sync::Lazy;
13use std::{cell::RefCell, collections::BTreeMap, path::Path, rc::Rc, sync::Arc};
14use sui_move_build::{decorate_warnings, implicit_deps};
15use sui_move_natives::{
16 NativesCostTable, object_runtime::ObjectRuntime, test_scenario::InMemoryTestStore,
17 transaction_context::TransactionContext,
18};
19use sui_package_management::system_package_versions::latest_system_packages;
20use sui_protocol_config::ProtocolConfig;
21use sui_types::{
22 base_types::{SuiAddress, TxContext},
23 digests::TransactionDigest,
24 gas_model::tables::initial_cost_schedule_for_unit_tests,
25 in_memory_storage::InMemoryStorage,
26 metrics::LimitsMetrics,
27};
28
29const MAX_UNIT_TEST_INSTRUCTIONS: u64 = 1_000_000;
31
32#[derive(Parser)]
33#[group(id = "sui-move-test")]
34pub struct Test {
35 #[clap(flatten)]
36 pub test: test::Test,
37}
38
39impl Test {
40 pub fn execute(
41 self,
42 path: Option<&Path>,
43 build_config: BuildConfig,
44 ) -> anyhow::Result<UnitTestResult> {
45 let compute_coverage = self.test.compute_coverage;
46 if !cfg!(feature = "tracing") && compute_coverage {
47 return Err(anyhow::anyhow!(
48 "The --coverage flag is currently supported only in builds built with the `tracing` feature enabled. \
49 Please build the Sui CLI from source with `--features tracing` to use this flag."
50 ));
51 }
52 let save_disassembly = self.test.trace;
54 let rerooted_path = base::reroot_path(path)?;
56 let unit_test_config = self.test.unit_test_config();
57 run_move_unit_tests(
58 &rerooted_path,
59 build_config,
60 Some(unit_test_config),
61 compute_coverage,
62 save_disassembly,
63 )
64 }
65}
66
67thread_local! {
69 static TEST_STORE_INNER: RefCell<InMemoryStorage> = RefCell::new(InMemoryStorage::default());
70}
71
72static TEST_STORE: Lazy<InMemoryTestStore> = Lazy::new(|| InMemoryTestStore(&TEST_STORE_INNER));
73
74static SET_EXTENSION_HOOK: Lazy<()> =
75 Lazy::new(|| set_extension_hook(Box::new(new_testing_object_and_natives_cost_runtime)));
76
77pub fn run_move_unit_tests(
80 path: &Path,
81 mut build_config: BuildConfig,
82 config: Option<UnitTestingConfig>,
83 compute_coverage: bool,
84 save_disassembly: bool,
85) -> anyhow::Result<UnitTestResult> {
86 Lazy::force(&SET_EXTENSION_HOOK);
88
89 let config = config
90 .unwrap_or_else(|| UnitTestingConfig::default_with_bound(Some(MAX_UNIT_TEST_INSTRUCTIONS)));
91 build_config.implicit_dependencies = implicit_deps(latest_system_packages());
92
93 let result = move_cli::base::test::run_move_unit_tests(
94 path,
95 build_config,
96 UnitTestingConfig {
97 report_stacktrace_on_abort: true,
98 ..config
99 },
100 sui_move_natives::all_natives(
101 false,
102 &ProtocolConfig::get_for_max_version_UNSAFE(),
103 ),
104 Some(initial_cost_schedule_for_unit_tests()),
105 compute_coverage,
106 save_disassembly,
107 &mut std::io::stdout(),
108 );
109 result.map(|(test_result, warning_diags)| {
110 if test_result == UnitTestResult::Success
111 && let Some(diags) = warning_diags
112 {
113 decorate_warnings(diags, None);
114 }
115 test_result
116 })
117}
118
119fn new_testing_object_and_natives_cost_runtime(ext: &mut NativeContextExtensions) {
120 let registry = prometheus::Registry::new();
122 let metrics = Arc::new(LimitsMetrics::new(®istry));
123 let store = Lazy::force(&TEST_STORE);
124 let protocol_config = ProtocolConfig::get_for_max_version_UNSAFE();
125
126 ext.add(ObjectRuntime::new(
127 store,
128 BTreeMap::new(),
129 false,
130 Box::leak(Box::new(ProtocolConfig::get_for_max_version_UNSAFE())), metrics,
132 0, ));
134 ext.add(NativesCostTable::from_protocol_config(&protocol_config));
135 let tx_context = TxContext::new_from_components(
136 &SuiAddress::ZERO,
137 &TransactionDigest::default(),
138 &0,
139 0,
140 0,
141 0,
142 0,
143 None,
144 &protocol_config,
145 );
146 ext.add(TransactionContext::new_for_testing(Rc::new(RefCell::new(
147 tx_context,
148 ))));
149 ext.add(store);
150}