diff --git a/Cargo.lock b/Cargo.lock index f13f5bcd40d..5c42d0b5fa0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7090,7 +7090,9 @@ dependencies = [ "hex", "indexmap 2.2.6", "log", + "once_cell", "parity-scale-codec", + "parking_lot 0.12.3", "path-clean", "rand 0.8.5", ] diff --git a/examples/waiter/tests/mx_lock_access.rs b/examples/waiter/tests/mx_lock_access.rs index ed431cd3b85..665938b1eb3 100644 --- a/examples/waiter/tests/mx_lock_access.rs +++ b/examples/waiter/tests/mx_lock_access.rs @@ -44,7 +44,7 @@ fn access_mx_lock_guard_from_different_msg_fails( )); } -fn init_fixture(system: &System) -> (Program<'_>, MessageId) { +fn init_fixture(system: &System) -> (Program, MessageId) { system.init_logger_with_default_filter(""); let program = Program::current(system); program.send_bytes(USER_ID, []); diff --git a/examples/waiter/tests/rw_lock_access.rs b/examples/waiter/tests/rw_lock_access.rs index c3603deea32..62bedd2edf9 100644 --- a/examples/waiter/tests/rw_lock_access.rs +++ b/examples/waiter/tests/rw_lock_access.rs @@ -88,7 +88,7 @@ fn access_rw_lock_guard_from_different_msg_fails( )); } -fn init_fixture(system: &System, lock_type: RwLockType) -> (Program<'_>, MessageId) { +fn init_fixture(system: &System, lock_type: RwLockType) -> (Program, MessageId) { system.init_logger_with_default_filter(""); let program = Program::current(system); program.send_bytes(USER_ID, []); diff --git a/gtest/Cargo.toml b/gtest/Cargo.toml index 7fe32402a76..16b94f03c43 100644 --- a/gtest/Cargo.toml +++ b/gtest/Cargo.toml @@ -30,6 +30,8 @@ log.workspace = true indexmap.workspace = true cargo_toml.workspace = true etc.workspace = true +parking_lot.workspace = true +once_cell.workspace = true [dev-dependencies] demo-custom.workspace = true diff --git a/gtest/src/blocks.rs b/gtest/src/blocks.rs index 1e4b593e792..5b913309174 100644 --- a/gtest/src/blocks.rs +++ b/gtest/src/blocks.rs @@ -18,21 +18,18 @@ //! Block timestamp and height management. +use crate::BLOCK_DURATION_IN_MSECS; use core_processor::configs::BlockInfo; +use once_cell::sync::Lazy; +use parking_lot::RwLock; use std::{ - cell::RefCell, - rc::Rc, + sync::Arc, time::{SystemTime, UNIX_EPOCH}, }; -use crate::BLOCK_DURATION_IN_MSECS; - -type BlockInfoStorageInner = Rc>>; +type BlockInfoStorageInner = Arc>>; -thread_local! { - /// Definition of the storage value storing block info (timestamp and height). - static BLOCK_INFO_STORAGE: BlockInfoStorageInner = Rc::new(RefCell::new(None)); -} +static BLOCK_INFO_STORAGE: Lazy = Lazy::new(|| Arc::new(RwLock::new(None))); #[derive(Debug)] pub(crate) struct BlocksManager { @@ -43,32 +40,27 @@ impl BlocksManager { /// Create block info storage manager with a further initialization of the /// storage. pub(crate) fn new() -> Self { - let unused = BLOCK_INFO_STORAGE.with(|bi_rc| { - let mut ref_mut = bi_rc.borrow_mut(); - if ref_mut.is_none() { - let info = BlockInfo { - height: 0, - timestamp: now(), - }; - - *ref_mut = Some(info); - } + let mut bi = BLOCK_INFO_STORAGE.write(); + if bi.is_none() { + let info = BlockInfo { + height: 0, + timestamp: now(), + }; - Rc::clone(bi_rc) - }); + *bi = Some(info); + } - Self { _unused: unused } + Self { + _unused: BLOCK_INFO_STORAGE.clone(), + } } /// Get current block info. pub(crate) fn get(&self) -> BlockInfo { - BLOCK_INFO_STORAGE.with(|bi_rc| { - bi_rc - .borrow() - .as_ref() - .copied() - .expect("instance always initialized") - }) + BLOCK_INFO_STORAGE + .read() + .clone() + .expect("instance always initialized") } /// Move blocks by one. @@ -78,17 +70,15 @@ impl BlocksManager { /// Adjusts blocks info by moving blocks by `amount`. pub(crate) fn move_blocks_by(&self, amount: u32) -> BlockInfo { - BLOCK_INFO_STORAGE.with(|bi_rc| { - let mut bi_ref_mut = bi_rc.borrow_mut(); - let Some(block_info) = bi_ref_mut.as_mut() else { - panic!("instance always initialized"); - }; - block_info.height += amount; - let duration = BLOCK_DURATION_IN_MSECS.saturating_mul(amount as u64); - block_info.timestamp += duration; - - *block_info - }) + let mut bi_ref_mut = BLOCK_INFO_STORAGE.write(); + let Some(block_info) = bi_ref_mut.as_mut() else { + panic!("instance always initialized"); + }; + block_info.height += amount; + let duration = BLOCK_DURATION_IN_MSECS.saturating_mul(amount as u64); + block_info.timestamp += duration; + + *block_info } } @@ -100,11 +90,9 @@ impl Default for BlocksManager { impl Drop for BlocksManager { fn drop(&mut self) { - BLOCK_INFO_STORAGE.with(|bi_rc| { - if Rc::strong_count(bi_rc) == 2 { - *bi_rc.borrow_mut() = None; - } - }); + if Arc::strong_count(&BLOCK_INFO_STORAGE) == 2 { + *BLOCK_INFO_STORAGE.write() = None; + } } } @@ -129,7 +117,7 @@ mod tests { // Assert all instance use same data; assert_eq!(second_instance.get().height, 2); - BLOCK_INFO_STORAGE.with(|bi_rc| bi_rc.borrow().is_some()); + assert!(BLOCK_INFO_STORAGE.read().is_some()); // Drop first instance and check whether data is removed. drop(first_instance); @@ -137,9 +125,9 @@ mod tests { second_instance.next_block(); assert_eq!(second_instance.get().height, 3); - BLOCK_INFO_STORAGE.with(|bi_rc| bi_rc.borrow().is_some()); + assert!(BLOCK_INFO_STORAGE.read().is_some()); drop(second_instance); - BLOCK_INFO_STORAGE.with(|bi_rc| bi_rc.borrow().is_none()); + assert!(BLOCK_INFO_STORAGE.read().is_none()); } } diff --git a/gtest/src/mailbox/actor.rs b/gtest/src/mailbox/actor.rs index 940b8c42e66..ed99e80c96c 100644 --- a/gtest/src/mailbox/actor.rs +++ b/gtest/src/mailbox/actor.rs @@ -23,19 +23,20 @@ use gear_core::{ ids::{prelude::MessageIdExt, MessageId, ProgramId}, message::{ReplyMessage, ReplyPacket}, }; -use std::{cell::RefCell, convert::TryInto}; +use parking_lot::RwLock; +use std::{convert::TryInto, sync::Arc}; /// Interface to a particular user mailbox. /// /// Gives a simplified interface to perform some operations /// over a particular user mailbox. -pub struct ActorMailbox<'a> { - manager: &'a RefCell, +pub struct ActorMailbox { + manager: Arc>, user_id: ProgramId, } -impl<'a> ActorMailbox<'a> { - pub(crate) fn new(user_id: ProgramId, manager: &'a RefCell) -> ActorMailbox<'a> { +impl ActorMailbox { + pub(crate) fn new(user_id: ProgramId, manager: Arc>) -> ActorMailbox { ActorMailbox { user_id, manager } } @@ -70,7 +71,7 @@ impl<'a> ActorMailbox<'a> { .find_message_by_log(&log) .ok_or(MailboxErrorImpl::ElementNotFound)?; self.manager - .borrow() + .read() .mailbox .remove(self.user_id, mailboxed_msg.id())?; @@ -90,10 +91,7 @@ impl<'a> ActorMailbox<'a> { reply_message.into_dispatch(self.user_id, mailboxed_msg.source(), mailboxed_msg.id()) }; - Ok(self - .manager - .borrow_mut() - .validate_and_run_dispatch(dispatch)) + Ok(self.manager.write().validate_and_run_dispatch(dispatch)) } /// Claims value from a message in mailbox. @@ -105,7 +103,7 @@ impl<'a> ActorMailbox<'a> { .find_message_by_log(&log.into()) .ok_or(MailboxErrorImpl::ElementNotFound)?; self.manager - .borrow_mut() + .write() .claim_value_from_mailbox(self.user_id, mailboxed_msg.id()) .unwrap_or_else(|e| unreachable!("Unexpected mailbox error: {e:?}")); @@ -118,7 +116,7 @@ impl<'a> ActorMailbox<'a> { } fn get_user_mailbox(&self) -> impl Iterator)> { - self.manager.borrow().mailbox.iter_key(self.user_id) + self.manager.read().mailbox.iter_key(self.user_id) } } @@ -136,7 +134,7 @@ mod tests { }; use std::convert::TryInto; - fn prepare_program(system: &System) -> (Program<'_>, ([u8; 32], Vec, Log)) { + fn prepare_program(system: &System) -> (Program, ([u8; 32], Vec, Log)) { let program = Program::from_binary_with_id(system, 121, WASM_BINARY); let sender = ProgramId::from(42).into_bytes(); diff --git a/gtest/src/manager.rs b/gtest/src/manager.rs index 85e3174d981..408c2eaa9be 100644 --- a/gtest/src/manager.rs +++ b/gtest/src/manager.rs @@ -57,17 +57,19 @@ use gear_core_errors::{ErrorReplyReason, SignalCode, SimpleExecutionError}; use gear_lazy_pages_common::LazyPagesCosts; use gear_lazy_pages_native_interface::LazyPagesNative; use gear_wasm_instrument::gas_metering::Schedule; +use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use rand::{rngs::StdRng, RngCore, SeedableRng}; use std::{ - cell::{Ref, RefCell, RefMut}, collections::{BTreeMap, HashMap, VecDeque}, convert::TryInto, - rc::Rc, + sync::Arc, }; const OUTGOING_LIMIT: u32 = 1024; const OUTGOING_BYTES_LIMIT: u32 = 64 * 1024 * 1024; +pub(crate) type ExtManagerPointer = Arc>; + pub(crate) type Balance = u128; #[derive(Debug)] @@ -185,15 +187,17 @@ impl Program { } #[derive(Default, Debug, Clone)] -pub(crate) struct Actors(Rc>>); +pub(crate) struct Actors(Arc>>); impl Actors { - pub fn borrow(&self) -> Ref<'_, BTreeMap> { - self.0.borrow() + pub fn borrow(&self) -> RwLockReadGuard<'_, BTreeMap> { + self.0.read() } - pub fn borrow_mut(&mut self) -> RefMut<'_, BTreeMap> { - self.0.borrow_mut() + pub fn borrow_mut( + &mut self, + ) -> RwLockWriteGuard<'_, BTreeMap> { + self.0.write() } fn insert( @@ -201,15 +205,15 @@ impl Actors { program_id: ProgramId, actor_and_balance: (TestActor, Balance), ) -> Option<(TestActor, Balance)> { - self.0.borrow_mut().insert(program_id, actor_and_balance) + self.0.write().insert(program_id, actor_and_balance) } pub fn contains_key(&self, program_id: &ProgramId) -> bool { - self.0.borrow().contains_key(program_id) + self.0.read().contains_key(program_id) } fn remove(&mut self, program_id: &ProgramId) -> Option<(TestActor, Balance)> { - self.0.borrow_mut().remove(program_id) + self.0.write().remove(program_id) } } diff --git a/gtest/src/program.rs b/gtest/src/program.rs index 00ae9123a62..a8d45e3fc31 100644 --- a/gtest/src/program.rs +++ b/gtest/src/program.rs @@ -18,7 +18,9 @@ use crate::{ log::RunResult, - manager::{Balance, ExtManager, GenuineProgram, MintMode, Program as InnerProgram, TestActor}, + manager::{ + Balance, ExtManagerPointer, GenuineProgram, MintMode, Program as InnerProgram, TestActor, + }, system::System, Result, GAS_ALLOWANCE, }; @@ -33,7 +35,6 @@ use gear_utils::{MemoryPageDump, ProgramMemoryDump}; use gear_wasm_instrument::gas_metering::Schedule; use path_clean::PathClean; use std::{ - cell::RefCell, convert::TryInto, env, ffi::OsStr, @@ -95,7 +96,7 @@ impl Gas { /// Trait for mocking gear programs. /// /// See [`Program`] and [`Program::mock`] for the usages. -pub trait WasmProgram: Debug { +pub trait WasmProgram: Debug + Send + Sync { /// Init wasm program with given `payload`. /// /// Returns `Ok(Some(payload))` if program has reply logic @@ -335,7 +336,7 @@ impl ProgramBuilder { pub fn build(self, system: &System) -> Program { let id = self .id - .unwrap_or_else(|| system.0.borrow_mut().free_id_nonce().into()); + .unwrap_or_else(|| system.0.write().free_id_nonce().into()); let schedule = Schedule::default(); let code = Code::try_new( @@ -352,11 +353,7 @@ impl ProgramBuilder { let (code, code_id) = code_and_id.into_parts(); if let Some(metadata) = self.meta { - system - .0 - .borrow_mut() - .meta_binaries - .insert(code_id, metadata); + system.0.write().meta_binaries.insert(code_id, metadata); } Program::program_with_id( @@ -387,14 +384,14 @@ impl ProgramBuilder { /// // Initialize the program from user 42 with message "init program". /// let _result = program.send(42, "init program"); /// ``` -pub struct Program<'a> { - pub(crate) manager: &'a RefCell, +pub struct Program { + pub(crate) manager: ExtManagerPointer, pub(crate) id: ProgramId, } -impl<'a> Program<'a> { +impl Program { fn program_with_id + Clone + Debug>( - system: &'a System, + system: &System, id: I, program: InnerProgram, ) -> Self { @@ -402,7 +399,7 @@ impl<'a> Program<'a> { if system .0 - .borrow_mut() + .write() .store_new_actor(program_id, program, None) .is_some() { @@ -413,7 +410,7 @@ impl<'a> Program<'a> { } Self { - manager: &system.0, + manager: system.0.clone(), id: program_id, } } @@ -421,7 +418,7 @@ impl<'a> Program<'a> { /// Get the program of the root crate with provided `system`. /// /// See [`ProgramBuilder::current`] - pub fn current(system: &'a System) -> Self { + pub fn current(system: &System) -> Self { ProgramBuilder::current().build(system) } @@ -430,7 +427,7 @@ impl<'a> Program<'a> { /// /// See also [`Program::current`]. pub fn current_with_id + Clone + Debug>( - system: &'a System, + system: &System, id: I, ) -> Self { ProgramBuilder::current().with_id(id).build(system) @@ -439,21 +436,21 @@ impl<'a> Program<'a> { /// Get optimized program of the root crate with provided `system`, /// /// See also [`Program::current`]. - pub fn current_opt(system: &'a System) -> Self { + pub fn current_opt(system: &System) -> Self { ProgramBuilder::current_opt().build(system) } /// Create a program instance from wasm file. /// /// See also [`Program::current`]. - pub fn from_file>(system: &'a System, path: P) -> Self { + pub fn from_file>(system: &System, path: P) -> Self { ProgramBuilder::from_file(path).build(system) } /// Create a program instance from wasm file with given ID. /// /// See also [`Program::from_file`]. - pub fn from_binary_with_id(system: &'a System, id: ID, binary: B) -> Self + pub fn from_binary_with_id(system: &System, id: ID, binary: B) -> Self where ID: Into + Clone + Debug, B: Into>, @@ -466,8 +463,8 @@ impl<'a> Program<'a> { /// Mock a program with provided `system` and `mock`. /// /// See [`WasmProgram`] for more details. - pub fn mock(system: &'a System, mock: T) -> Self { - let nonce = system.0.borrow_mut().free_id_nonce(); + pub fn mock(system: &System, mock: T) -> Self { + let nonce = system.0.write().free_id_nonce(); Self::mock_with_id(system, nonce, mock) } @@ -476,7 +473,7 @@ impl<'a> Program<'a> { /// and initialize it with provided `id`. /// /// See also [`Program::mock`]. - pub fn mock_with_id(system: &'a System, id: ID, mock: T) -> Self + pub fn mock_with_id(system: &System, id: ID, mock: T) -> Self where T: WasmProgram + 'static, ID: Into + Clone + Debug, @@ -562,7 +559,7 @@ impl<'a> Program<'a> { ID: Into, T: Into>, { - let mut system = self.manager.borrow_mut(); + let mut system = self.manager.write(); let source = from.into().0; @@ -597,7 +594,7 @@ impl<'a> Program<'a> { /// Send signal to the program. #[track_caller] pub fn send_signal>(&self, from: ID, code: SignalCode) -> RunResult { - let mut system = self.manager.borrow_mut(); + let mut system = self.manager.write(); let source = from.into().0; @@ -627,9 +624,7 @@ impl<'a> Program<'a> { /// Reads the program’s state as a byte vector. pub fn read_state_bytes(&self, payload: Vec) -> Result> { - self.manager - .borrow_mut() - .read_state_bytes(payload, &self.id) + self.manager.write().read_state_bytes(payload, &self.id) } /// Reads the program’s transformed state as a byte vector. The transformed @@ -686,7 +681,7 @@ impl<'a> Program<'a> { args: Option>, ) -> Result> { self.manager - .borrow_mut() + .write() .read_state_bytes_using_wasm(payload, &self.id, fn_name, wasm, args) } @@ -757,18 +752,18 @@ impl<'a> Program<'a> { /// Mint balance to the account. pub fn mint(&mut self, value: Balance) { self.manager - .borrow_mut() + .write() .mint_to(&self.id(), value, MintMode::KeepAlive) } /// Returns the balance of the account. pub fn balance(&self) -> Balance { - self.manager.borrow().balance_of(&self.id()) + self.manager.read().balance_of(&self.id()) } /// Save the program's memory to path. pub fn save_memory_dump(&self, path: impl AsRef) { - let manager = self.manager.borrow(); + let manager = self.manager.read(); let mem = manager.read_memory_pages(&self.id); let balance = manager.balance_of(&self.id); @@ -799,12 +794,8 @@ impl<'a> Program<'a> { .balance .saturating_add(memory_dump.reserved_balance); - self.manager - .borrow_mut() - .update_storage_pages(&self.id, mem); - self.manager - .borrow_mut() - .override_balance(&self.id, balance); + self.manager.write().update_storage_pages(&self.id, mem); + self.manager.write().override_balance(&self.id, balance); } } diff --git a/gtest/src/system.rs b/gtest/src/system.rs index 9b81f2c2a5d..984fec560a4 100644 --- a/gtest/src/system.rs +++ b/gtest/src/system.rs @@ -19,7 +19,7 @@ use crate::{ log::RunResult, mailbox::ActorMailbox, - manager::{Actors, Balance, ExtManager, MintMode}, + manager::{Actors, Balance, ExtManager, ExtManagerPointer, MintMode}, program::{Program, ProgramIdWrapper}, }; use codec::{Decode, DecodeAll}; @@ -32,8 +32,9 @@ use gear_core::{ }; use gear_lazy_pages::{LazyPagesStorage, LazyPagesVersion}; use gear_lazy_pages_common::LazyPagesInitContext; +use parking_lot::RwLock; use path_clean::PathClean; -use std::{borrow::Cow, cell::RefCell, env, fs, io::Write, path::Path, thread}; +use std::{borrow::Cow, cell::RefCell, env, fs, io::Write, path::Path, sync::Arc, thread}; thread_local! { /// `System` is a singleton with a one instance and no copies returned. @@ -96,7 +97,8 @@ impl LazyPagesStorage for PagesStorage { /// // Init logger with "gwasm" target set to `debug` level. /// system.init_logger(); /// ``` -pub struct System(pub(crate) RefCell); +#[derive(Clone)] +pub struct System(pub(crate) ExtManagerPointer); impl System { /// Prefix for lazy pages. @@ -127,7 +129,7 @@ impl System { *initialized = true; - Self(RefCell::new(ext_manager)) + Self(Arc::new(RwLock::new(ext_manager))) }) } @@ -176,12 +178,12 @@ impl System { /// Send raw message dispatch. pub fn send_dispatch(&self, dispatch: Dispatch) -> RunResult { - self.0.borrow_mut().validate_and_run_dispatch(dispatch) + self.0.write().validate_and_run_dispatch(dispatch) } /// Spend blocks and return all results. pub fn spend_blocks(&self, amount: u32) -> Vec { - let mut manager = self.0.borrow_mut(); + let mut manager = self.0.write(); let block_height = manager.blocks_manager.get().height; (block_height..block_height + amount) @@ -200,23 +202,23 @@ impl System { /// Return the current block height of the testing environment. pub fn block_height(&self) -> u32 { - self.0.borrow().blocks_manager.get().height + self.0.read().blocks_manager.get().height } /// Return the current block timestamp of the testing environment. pub fn block_timestamp(&self) -> u64 { - self.0.borrow().blocks_manager.get().timestamp + self.0.read().blocks_manager.get().timestamp } /// Returns a [`Program`] by `id`. pub fn get_program>(&self, id: ID) -> Option { let id = id.into().0; - let manager = self.0.borrow(); + let manager = self.0.read(); if manager.is_program(&id) { Some(Program { id, - manager: &self.0, + manager: self.0.clone(), }) } else { None @@ -230,7 +232,7 @@ impl System { /// Returns a list of programs. pub fn programs(&self) -> Vec { - let manager = self.0.borrow(); + let manager = self.0.read(); let actors = manager.actors.borrow(); actors .keys() @@ -238,7 +240,7 @@ impl System { .filter(|id| manager.is_program(id)) .map(|id| Program { id, - manager: &self.0, + manager: self.0.clone(), }) .collect() } @@ -250,7 +252,7 @@ impl System { /// exited or terminated that it can't be called anymore. pub fn is_active_program>(&self, id: ID) -> bool { let program_id = id.into().0; - self.0.borrow().is_active_program(&program_id) + self.0.read().is_active_program(&program_id) } /// Saves code to the storage and returns its code hash @@ -262,7 +264,7 @@ impl System { /// must be in storage at the time of the function call. So this method /// stores the code in storage. pub fn submit_code(&self, binary: impl Into>) -> CodeId { - self.0.borrow_mut().store_new_code(binary.into()) + self.0.write().store_new_code(binary.into()) } /// Saves code from file to the storage and returns its code hash @@ -276,7 +278,7 @@ impl System { code_path.as_ref().to_string_lossy() ) }); - self.0.borrow_mut().store_new_code(code) + self.0.write().store_new_code(code) } /// Saves code to the storage and returns its code hash @@ -295,7 +297,7 @@ impl System { /// Returns previously submitted code by its code hash. pub fn submitted_code(&self, code_id: CodeId) -> Option> { - self.0.borrow().read_code(code_id).map(|code| code.to_vec()) + self.0.read().read_code(code_id).map(|code| code.to_vec()) } /// Extract mailbox of user with given `id`. @@ -305,24 +307,24 @@ impl System { #[track_caller] pub fn get_mailbox>(&self, id: ID) -> ActorMailbox { let program_id = id.into().0; - if !self.0.borrow().is_user(&program_id) { + if !self.0.read().is_user(&program_id) { panic!("Mailbox available only for users"); } - ActorMailbox::new(program_id, &self.0) + ActorMailbox::new(program_id, self.0.clone()) } /// Mint balance to user with given `id` and `value`. pub fn mint_to>(&self, id: ID, value: Balance) { let actor_id = id.into().0; self.0 - .borrow_mut() + .write() .mint_to(&actor_id, value, MintMode::KeepAlive); } /// Returns balance of user with given `id`. pub fn balance_of>(&self, id: ID) -> Balance { let actor_id = id.into().0; - self.0.borrow().balance_of(&actor_id) + self.0.read().balance_of(&actor_id) } } @@ -330,8 +332,8 @@ impl Drop for System { fn drop(&mut self) { // Uninitialize SYSTEM_INITIALIZED.with_borrow_mut(|initialized| *initialized = false); - self.0.borrow().gas_tree.reset(); - self.0.borrow().mailbox.reset(); + self.0.read().gas_tree.reset(); + self.0.read().mailbox.reset(); } } @@ -363,4 +365,20 @@ mod tests { h.join().expect("internal error failed joining thread"); } + + #[test] + fn test_multithread_sync() { + let first_instance = System::new(); + first_instance.spend_blocks(5); + + assert_eq!(first_instance.block_height(), 5); + + let h = std::thread::spawn(move || { + let second_instance = first_instance.clone(); + second_instance.spend_blocks(10); + assert_eq!(second_instance.block_height(), 15); + }); + + h.join().expect("internal error failed joining thread"); + } } diff --git a/utils/cargo-gbuild/tests/smoke.rs b/utils/cargo-gbuild/tests/smoke.rs index ea90273e9f0..27f7434e7a3 100644 --- a/utils/cargo-gbuild/tests/smoke.rs +++ b/utils/cargo-gbuild/tests/smoke.rs @@ -21,7 +21,7 @@ use cargo_gbuild::GBuild; use gtest::{state_args, Program, System}; use std::{fs, path::PathBuf, process::Command}; -fn ping(sys: &System, prog: PathBuf) -> Program<'_> { +fn ping(sys: &System, prog: PathBuf) -> Program { // Get program from artifact let user = 0; let program = Program::from_file(sys, prog);