From 8e4f55f189b3835b6fdc9469f7f2363c1649638c Mon Sep 17 00:00:00 2001 From: Sabaun Taraki Date: Wed, 14 Aug 2024 11:46:12 +0300 Subject: [PATCH] refactor(gtest): Change block execution model in `gtest` to be similar to the vara-runtime (#4119) --- examples/autoreply/src/lib.rs | 14 +- examples/custom/src/btree.rs | 11 +- examples/distributor/src/lib.rs | 40 ++- examples/gas-burned/src/lib.rs | 18 +- examples/new-meta/tests/read_state.rs | 1 + examples/node/src/lib.rs | 39 ++- examples/program-factory/src/lib.rs | 56 ++-- examples/reserve-gas/src/lib.rs | 5 +- examples/signal-entry/src/lib.rs | 26 +- examples/stack-allocations/src/lib.rs | 9 +- examples/syscall-error/src/lib.rs | 5 +- examples/wait_wake/src/lib.rs | 46 +-- examples/waiter/tests/mx_lock_access.rs | 13 +- examples/waiter/tests/rw_lock_access.rs | 13 +- gtest/src/lib.rs | 72 +++-- gtest/src/log.rs | 97 +++---- gtest/src/mailbox/actor.rs | 78 ++--- gtest/src/manager.rs | 317 ++++++++++----------- gtest/src/program.rs | 173 +++++------ gtest/src/system.rs | 138 ++++++++- utils/cargo-gbuild/test-program/src/lib.rs | 10 +- utils/cargo-gbuild/tests/smoke.rs | 10 +- utils/wasm-builder/test-program/src/lib.rs | 6 +- 23 files changed, 679 insertions(+), 518 deletions(-) diff --git a/examples/autoreply/src/lib.rs b/examples/autoreply/src/lib.rs index 677f4c08cb1..f36016841ca 100644 --- a/examples/autoreply/src/lib.rs +++ b/examples/autoreply/src/lib.rs @@ -48,17 +48,19 @@ mod tests { let from = 42; // Init Program-1 - let res = prog1.send(from, ActorId::zero()); - assert!(!res.main_failed()); + let init_msg1 = prog1.send(from, ActorId::zero()); // Init Program-2 with Program-1 as destination let prog2 = Program::current(&system); - let res = prog2.send(from, prog1_id); - assert!(!res.main_failed()); + let init_msg2 = prog2.send(from, prog1_id); // Send a message from Program-2 to Program-1 - let res = prog2.send_bytes(from, b"Let's go!"); - assert!(!res.main_failed()); + let msg3 = prog2.send_bytes(from, b"Let's go!"); + + let res = system.run_next_block(); + for msg in [init_msg1, init_msg2, msg3] { + assert!(res.succeed.contains(&msg)); + } // Check whether the auto-reply was received let reply_received: bool = prog2 diff --git a/examples/custom/src/btree.rs b/examples/custom/src/btree.rs index a19699b0ae1..5711edb49b3 100644 --- a/examples/custom/src/btree.rs +++ b/examples/custom/src/btree.rs @@ -105,7 +105,8 @@ mod tests { let from = 42; - let res = program.send(from, InitMessage::BTree); + program.send(from, InitMessage::BTree); + let res = system.run_next_block(); let log = Log::builder().source(program.id()).dest(from); assert!(res.contains(&log)); } @@ -119,8 +120,7 @@ mod tests { let from = 42; - let _res = program.send(from, InitMessage::BTree); - + program.send(from, InitMessage::BTree); IntoIterator::into_iter([ Request::Insert(0, 1), Request::Insert(0, 2), @@ -131,7 +131,10 @@ mod tests { Request::Clear, Request::List, ]) - .map(|r| program.send(from, r)) + .map(|r| { + program.send(from, r); + system.run_next_block() + }) .zip(IntoIterator::into_iter([ Reply::Value(None), Reply::Value(Some(1)), diff --git a/examples/distributor/src/lib.rs b/examples/distributor/src/lib.rs index 6e0c30cf5a2..6cdb3b12012 100644 --- a/examples/distributor/src/lib.rs +++ b/examples/distributor/src/lib.rs @@ -69,7 +69,8 @@ mod tests { let from = 42; - let res = program.send_bytes(from, b"init"); + program.send_bytes(from, b"init"); + let res = system.run_next_block(); let log = Log::builder().source(program.id()).dest(from); assert!(res.contains(&log)); } @@ -83,16 +84,19 @@ mod tests { let from = 42; - let _res = program.send_bytes(from, b"init"); + // Init + program.send_bytes(from, b"init"); - let res = program.send(from, Request::Receive(10)); + program.send(from, Request::Receive(10)); + let res = system.run_next_block(); let log = Log::builder() .source(program.id()) .dest(from) .payload(Reply::Success); assert!(res.contains(&log)); - let res = program.send(from, Request::Report); + program.send(from, Request::Report); + let res = system.run_next_block(); let log = Log::builder() .source(program.id()) .dest(from) @@ -111,22 +115,24 @@ mod tests { let from = 42; let program_1 = Program::current_with_id(system, program_1_id); - let _res = program_1.send_bytes(from, b"init"); + program_1.send_bytes(from, b"init"); let program_2 = Program::current_with_id(system, program_2_id); - let _res = program_2.send_bytes(from, b"init"); + program_2.send_bytes(from, b"init"); let program_3 = Program::current_with_id(system, program_3_id); - let _res = program_3.send_bytes(from, b"init"); + program_3.send_bytes(from, b"init"); - let res = program_1.send(from, Request::Join(program_2_id.into())); + program_1.send(from, Request::Join(program_2_id.into())); + let res = system.run_next_block(); let log = Log::builder() .source(program_1.id()) .dest(from) .payload(Reply::Success); assert!(res.contains(&log)); - let res = program_1.send(from, Request::Join(program_3_id.into())); + program_1.send(from, Request::Join(program_3_id.into())); + let res = system.run_next_block(); let log = Log::builder() .source(program_1.id()) .dest(from) @@ -143,21 +149,24 @@ mod tests { let from = 42; - let res = program_1.send(from, Request::Receive(11)); + program_1.send(from, Request::Receive(11)); + let res = system.run_next_block(); let log = Log::builder() .source(program_1.id()) .dest(from) .payload(Reply::Success); assert!(res.contains(&log)); - let res = program_2.send(from, Request::Report); + program_2.send(from, Request::Report); + let res = system.run_next_block(); let log = Log::builder() .source(program_2.id()) .dest(from) .payload(Reply::Amount(5)); assert!(res.contains(&log)); - let res = program_1.send(from, Request::Report); + program_1.send(from, Request::Report); + let res = system.run_next_block(); let log = Log::builder() .source(program_1.id()) .dest(from) @@ -175,10 +184,13 @@ mod tests { let from = 42; let program_4 = Program::current_with_id(&system, program_4_id); - let _res = program_4.send_bytes(from, b"init"); + program_4.send_bytes(from, b"init"); IntoIterator::into_iter([Request::Receive(11), Request::Join(program_4_id.into())]) - .map(|request| program_1.send(from, request)) + .map(|request| { + program_1.send(from, request); + system.run_next_block() + }) .zip(IntoIterator::into_iter([Reply::Success, Reply::Success])) .for_each(|(result, reply)| { let log = Log::builder() diff --git a/examples/gas-burned/src/lib.rs b/examples/gas-burned/src/lib.rs index 45e628fea74..5b6f056cc6b 100644 --- a/examples/gas-burned/src/lib.rs +++ b/examples/gas-burned/src/lib.rs @@ -23,13 +23,23 @@ mod tests { let from = 42; let program = Program::current(&system); - let res = program.send_bytes(from, "init"); - let init_gas_burned = res.main_gas_burned(); + let init_msg_id = program.send_bytes(from, "init"); + let res = system.run_next_block(); + let init_gas_burned = res + .gas_burned + .get(&init_msg_id) + .copied() + .expect("internal error: init message isn't sent"); log::debug!("Init gas burned: {init_gas_burned}"); assert!(init_gas_burned > Gas::zero()); - let res = program.send_bytes(from, "handle"); - let handle_gas_burned = res.main_gas_burned(); + let handle_msg_id = program.send_bytes(from, "handle"); + let res = system.run_next_block(); + let handle_gas_burned = res + .gas_burned + .get(&handle_msg_id) + .copied() + .expect("internal error: init message isn't sent"); log::debug!("Handle gas burned: {handle_gas_burned}"); assert!(handle_gas_burned > init_gas_burned); } diff --git a/examples/new-meta/tests/read_state.rs b/examples/new-meta/tests/read_state.rs index 9c7fffdc380..2caf1b64948 100644 --- a/examples/new-meta/tests/read_state.rs +++ b/examples/new-meta/tests/read_state.rs @@ -192,5 +192,6 @@ fn initialize_current_program(system: &System) -> Program { currency: "USD".into(), }, ); + system.run_next_block(); program } diff --git a/examples/node/src/lib.rs b/examples/node/src/lib.rs index 87cd998e099..3718cbd44fe 100644 --- a/examples/node/src/lib.rs +++ b/examples/node/src/lib.rs @@ -71,8 +71,9 @@ mod tests { let from = 42; let program = Program::current(&system); - let res = program.send(from, Request::IsReady); - assert!(res.main_failed()); + let msg_id = program.send(from, Request::IsReady); + let res = system.run_next_block(); + assert!(res.failed.contains(&msg_id)); } #[test] @@ -83,9 +84,10 @@ mod tests { let from = 42; let program = Program::current(&system); - let res = program.send(from, Initialization { status: 5 }); + let msg_id = program.send(from, Initialization { status: 5 }); let log = Log::builder().source(program.id()).dest(from); - assert!(!res.main_failed()); + let res = system.run_next_block(); + assert!(res.succeed.contains(&msg_id)); assert!(res.contains(&log)); } @@ -97,23 +99,26 @@ mod tests { let from = 42; let program = Program::current(&system); - let _res = program.send(from, Initialization { status: 5 }); + program.send(from, Initialization { status: 5 }); - let res = program.send(from, Request::IsReady); + program.send(from, Request::IsReady); let log = Log::builder() .source(program.id()) .dest(from) .payload(Reply::Yes); + let res = system.run_next_block(); assert!(res.contains(&log)); - let res = program.send(from, Request::Begin(Operation { to_status: 7 })); + program.send(from, Request::Begin(Operation { to_status: 7 })); + let res = system.run_next_block(); let log = Log::builder() .source(program.id()) .dest(from) .payload(Reply::Success); assert!(res.contains(&log)); - let res = program.send(from, Request::Commit); + program.send(from, Request::Commit); + let res = system.run_next_block(); let log = Log::builder() .source(program.id()) .dest(from) @@ -133,36 +138,40 @@ mod tests { let program_3_id = 3; let program_1 = Program::current_with_id(&system, program_1_id); - let _res = program_1.send(from, Initialization { status: 5 }); + program_1.send(from, Initialization { status: 5 }); let program_2 = Program::current_with_id(&system, program_2_id); - let _res = program_2.send(from, Initialization { status: 5 }); + program_2.send(from, Initialization { status: 5 }); let program_3 = Program::current_with_id(&system, program_3_id); - let _res = program_3.send(from, Initialization { status: 9 }); + program_3.send(from, Initialization { status: 9 }); - let res = program_1.send(from, Request::Add(program_2_id.into())); + program_1.send(from, Request::Add(program_2_id.into())); + let res = system.run_next_block(); let log = Log::builder() .source(program_1.id()) .dest(from) .payload(Reply::Success); assert!(res.contains(&log)); - let res = program_1.send(from, Request::Add(program_3_id.into())); + program_1.send(from, Request::Add(program_3_id.into())); + let res = system.run_next_block(); let log = Log::builder() .source(program_1.id()) .dest(from) .payload(Reply::Success); assert!(res.contains(&log)); - let res = program_1.send(from, Request::Begin(Operation { to_status: 7 })); + program_1.send(from, Request::Begin(Operation { to_status: 7 })); + let res = system.run_next_block(); let log = Log::builder() .source(program_1.id()) .dest(from) .payload(Reply::Success); assert!(res.contains(&log)); - let res = program_1.send(from, Request::Commit); + program_1.send(from, Request::Commit); + let res = system.run_next_block(); let log = Log::builder() .source(program_1.id()) .dest(from) diff --git a/examples/program-factory/src/lib.rs b/examples/program-factory/src/lib.rs index e0a169a5b29..fa288276848 100644 --- a/examples/program-factory/src/lib.rs +++ b/examples/program-factory/src/lib.rs @@ -70,9 +70,10 @@ mod tests { let user_id = 10001; sys.mint_to(user_id, 100 * UNITS); - // Send `init` msg to factory - let res = factory.send_bytes_with_value(user_id, "EMPTY", 10 * UNITS); - assert!(!res.main_failed()); + // Send init msg to factory + let msg_id = factory.send_bytes_with_value(user_id, "EMPTY", 10 * UNITS); + let res = sys.run_next_block(); + assert!(res.succeed.contains(&msg_id)); assert!(sys.is_active_program(100)); factory @@ -96,14 +97,12 @@ mod tests { sys.init_logger(); let factory = prepare_factory(&sys); - // Send `handle` msg to factory to create a new child - let res = factory.send_bytes(10001, CreateProgram::Default.encode()); - let child_id_expected = calculate_program_id( - CHILD_CODE_HASH.into(), - &0i32.to_le_bytes(), - Some(res.sent_message_id()), - ); - assert!(!res.main_failed()); + // Send handle msg to factory to create a new child + let msg_id = factory.send_bytes(10001, CreateProgram::Default.encode()); + let res = sys.run_next_block(); + let child_id_expected = + calculate_program_id(CHILD_CODE_HASH.into(), &0i32.to_le_bytes(), Some(msg_id)); + assert!(res.succeed.contains(&msg_id)); assert!(sys.is_active_program(child_id_expected)); } @@ -116,25 +115,25 @@ mod tests { let salt = 0i32.to_be_bytes(); let payload = CreateProgram::Custom(vec![(CHILD_CODE_HASH, salt.to_vec(), 100_000_000)]); - // Send `handle` msg to factory to create a new child - let res = factory.send_bytes(10001, payload.encode()); + // Send handle msg to factory to create a new child + let msg_id = factory.send_bytes(10001, payload.encode()); + let res = sys.run_next_block(); - let child_id_expected = - calculate_program_id(CHILD_CODE_HASH.into(), &salt, Some(res.sent_message_id())); + let child_id_expected = calculate_program_id(CHILD_CODE_HASH.into(), &salt, Some(msg_id)); - assert!(!res.main_failed()); + assert!(res.succeed.contains(&msg_id)); assert!(sys.is_active_program(child_id_expected)); - // Send `handle` msg to create a duplicate - let res = factory.send_bytes(10001, payload.encode()); + // Send handle msg to create a duplicate + let msg_id = factory.send_bytes(10001, payload.encode()); + let res = sys.run_next_block(); - let child_id_expected = - calculate_program_id(CHILD_CODE_HASH.into(), &salt, Some(res.sent_message_id())); + let child_id_expected = calculate_program_id(CHILD_CODE_HASH.into(), &salt, Some(msg_id)); - assert!(!res.main_failed()); + assert!(res.succeed.contains(&msg_id)); assert!(sys.is_active_program(child_id_expected)); - assert_eq!(res.total_processed(), 3 + 1 + 1); // +1 for the original message, initiated by user +1 for auto generated replies + assert_eq!(res.total_processed, 3 + 1 + 1); // +1 for the original message, initiated by user +1 for auto generated replies } #[test] @@ -147,13 +146,11 @@ mod tests { let non_existing_code_hash = [10u8; 32]; let salt = b"some_salt"; let payload = CreateProgram::Custom(vec![(non_existing_code_hash, salt.to_vec(), 100_000)]); - let res = factory.send_bytes(10001, payload.encode()); - let fictional_program_id = calculate_program_id( - non_existing_code_hash.into(), - salt, - Some(res.sent_message_id()), - ); - assert!(!res.main_failed()); + let msg_id = factory.send_bytes(10001, payload.encode()); + let res = sys.run_next_block(); + let fictional_program_id = + calculate_program_id(non_existing_code_hash.into(), salt, Some(msg_id)); + assert!(res.succeed.contains(&msg_id)); // No new program with fictional id assert!(!sys.is_active_program(fictional_program_id)); } @@ -175,5 +172,6 @@ mod tests { 100_000, )]); factory.send_bytes(10001, payload.encode()); + let _ = sys.run_next_block(); } } diff --git a/examples/reserve-gas/src/lib.rs b/examples/reserve-gas/src/lib.rs index 85c616371bf..ee335804869 100644 --- a/examples/reserve-gas/src/lib.rs +++ b/examples/reserve-gas/src/lib.rs @@ -78,7 +78,7 @@ mod tests { let program = Program::current(&system); - let res = program.send( + let msg_id = program.send( 0, InitAction::Normal(vec![ // orphan reservation; will be removed automatically @@ -87,6 +87,7 @@ mod tests { (25_000, 5), ]), ); - assert!(!res.main_failed()); + let res = system.run_next_block(); + assert!(res.succeed.contains(&msg_id)); } } diff --git a/examples/signal-entry/src/lib.rs b/examples/signal-entry/src/lib.rs index 53abc5ee02c..67dfbeb6573 100644 --- a/examples/signal-entry/src/lib.rs +++ b/examples/signal-entry/src/lib.rs @@ -65,18 +65,34 @@ mod wasm; #[cfg(test)] mod tests { - use gstd::errors::{SignalCode, SimpleExecutionError}; - use gtest::{Program, System}; + use crate::HandleAction; + use gtest::{Log, Program, System}; #[test] fn signal_can_be_sent() { let system = System::new(); system.init_logger(); + let user_id = 42; let program = Program::current(&system); - let signal_code: SignalCode = SimpleExecutionError::UserspacePanic.into(); - let res = program.send_signal(0, signal_code); - assert!(!res.main_failed()); + // Initialize program + program.send_bytes(user_id, b"init_program"); + system.run_next_block(); + + // Make program panic + let msg_id = program.send(user_id, HandleAction::Panic); + let res = system.run_next_block(); + + // Checking signal executed successfully by checking if there are failed messages. + assert_eq!(res.failed.len(), 1); + assert!(res.failed.contains(&msg_id)); + assert!(res.not_executed.is_empty()); + + // Signal sends user message + let log = Log::builder().dest(user_id).payload(b"handle_signal"); + assert!(res.contains(&log)); + let mailbox = system.get_mailbox(user_id); + assert!(mailbox.contains(&log)); } } diff --git a/examples/stack-allocations/src/lib.rs b/examples/stack-allocations/src/lib.rs index 6438e7c775a..371fc252514 100644 --- a/examples/stack-allocations/src/lib.rs +++ b/examples/stack-allocations/src/lib.rs @@ -103,20 +103,21 @@ mod tests { } // Init program - if program.send(from, InitConfig { actions }).main_failed() { - panic!("Init failed"); - } + let msg_id = program.send(from, InitConfig { actions }); + let res = system.run_next_block(); + assert!(res.succeed.contains(&msg_id)); let number: u8 = rng.gen_range(0..=MAX_NUMBER); let expected_check_sum = actions_amount * number as usize * HANDLE_DATA_SIZE; // Send data to handle - let res = program.send( + program.send( from, HandleData { data: [number; HANDLE_DATA_SIZE], }, ); + let res = system.run_next_block(); assert_eq!( expected_check_sum as u32, diff --git a/examples/syscall-error/src/lib.rs b/examples/syscall-error/src/lib.rs index 83edf6ed522..1ca6d9c2a6f 100644 --- a/examples/syscall-error/src/lib.rs +++ b/examples/syscall-error/src/lib.rs @@ -42,7 +42,8 @@ mod tests { let program = Program::current(&system); - let res = program.send_bytes(0, b"dummy"); - assert!(!res.main_failed()); + let msg_id = program.send_bytes(0, b"dummy"); + let res = system.run_next_block(); + assert!(res.succeed.contains(&msg_id)); } } diff --git a/examples/wait_wake/src/lib.rs b/examples/wait_wake/src/lib.rs index d450e946f0a..25daabcfb6f 100644 --- a/examples/wait_wake/src/lib.rs +++ b/examples/wait_wake/src/lib.rs @@ -53,7 +53,8 @@ mod tests { let from = 42; - let res = program.send_bytes(from, b"init"); + program.send_bytes(from, b"init"); + let res = system.run_next_block(); let log = Log::builder().source(program.id()).dest(from); assert!(res.contains(&log)); } @@ -66,26 +67,29 @@ mod tests { let from = 42; let program = Program::current(&system); - let _res = program.send_bytes(from, b"init"); + program.send_bytes(from, b"init"); + system.run_next_block(); let msg_1_echo_wait = 100; - let res = program.send(from, Request::EchoWait(msg_1_echo_wait)); - let msg_id_1 = res.sent_message_id(); + let msg_id_1 = program.send(from, Request::EchoWait(msg_1_echo_wait)); + let res = system.run_next_block(); assert!(res.log().is_empty()); let msg_2_echo_wait = 200; - let res = program.send(from, Request::EchoWait(msg_2_echo_wait)); - let msg_id_2 = res.sent_message_id(); + let msg_id_2 = program.send(from, Request::EchoWait(msg_2_echo_wait)); + let res = system.run_next_block(); assert!(res.log().is_empty()); - let res = program.send(from, Request::Wake(msg_id_1.into())); + program.send(from, Request::Wake(msg_id_1.into())); + let res = system.run_next_block(); let log = Log::builder() .source(program.id()) .dest(from) .payload(msg_1_echo_wait); assert!(res.contains(&log)); - let res = program.send(from, Request::Wake(msg_id_2.into())); + program.send(from, Request::Wake(msg_id_2.into())); + let res = system.run_next_block(); let log = Log::builder() .source(program.id()) .dest(from) @@ -101,30 +105,34 @@ mod tests { let from = 42; let program_1 = Program::current(&system); - let _res = program_1.send_bytes(from, b"init"); + program_1.send_bytes(from, b"init"); + system.run_next_block(); let program_2 = Program::current(&system); - let _res = program_2.send_bytes(from, b"init"); + program_2.send_bytes(from, b"init"); + system.run_next_block(); let msg_1_echo_wait = 100; - let res = program_1.send(from, Request::EchoWait(msg_1_echo_wait)); - let msg_id_1 = res.sent_message_id(); + let msg_id_1 = program_1.send(from, Request::EchoWait(msg_1_echo_wait)); + let res = system.run_next_block(); assert!(res.log().is_empty()); let msg_2_echo_wait = 200; - let res = program_2.send(from, Request::EchoWait(msg_2_echo_wait)); - let msg_id_2 = res.sent_message_id(); + let msg_id_2 = program_2.send(from, Request::EchoWait(msg_2_echo_wait)); + let res = system.run_next_block(); assert!(res.log().is_empty()); // try to wake other messages - let res = program_2.send(from, Request::Wake(msg_id_1.into())); + program_2.send(from, Request::Wake(msg_id_1.into())); + let res = system.run_next_block(); let log = Log::builder() .source(program_2.id()) .dest(from) .payload_bytes([]); assert!(res.contains(&log)); - let res = program_1.send(from, Request::Wake(msg_id_2.into())); + program_1.send(from, Request::Wake(msg_id_2.into())); + let res = system.run_next_block(); let log = Log::builder() .source(program_1.id()) .dest(from) @@ -132,14 +140,16 @@ mod tests { assert!(res.contains(&log)); // wake msg_1 for program_1 and msg_2 for program_2 - let res = program_1.send(from, Request::Wake(msg_id_1.into())); + program_1.send(from, Request::Wake(msg_id_1.into())); + let res = system.run_next_block(); let log = Log::builder() .source(program_1.id()) .dest(from) .payload(msg_1_echo_wait); assert!(res.contains(&log)); - let res = program_2.send(from, Request::Wake(msg_id_2.into())); + program_2.send(from, Request::Wake(msg_id_2.into())); + let res = system.run_next_block(); let log = Log::builder() .source(program_2.id()) .dest(from) diff --git a/examples/waiter/tests/mx_lock_access.rs b/examples/waiter/tests/mx_lock_access.rs index ed431cd3b85..f00269db7fe 100644 --- a/examples/waiter/tests/mx_lock_access.rs +++ b/examples/waiter/tests/mx_lock_access.rs @@ -35,12 +35,12 @@ fn access_mx_lock_guard_from_different_msg_fails( let system = System::new(); let (program, lock_msg_id) = init_fixture(&system); - let lock_access_result = + let lock_access_msg_id = program.send(USER_ID, Command::MxLockStaticAccess(lock_access_subcommand)); + let lock_access_result = system.run_next_block(); - lock_access_result.assert_panicked_with(format!( - "Mutex guard held by message {lock_msg_id} is being accessed by message {}", - lock_access_result.sent_message_id(), + lock_access_result.assert_panicked_with(lock_access_msg_id, format!( + "Mutex guard held by message {lock_msg_id} is being accessed by message {lock_access_msg_id}" )); } @@ -48,12 +48,13 @@ fn init_fixture(system: &System) -> (Program<'_>, MessageId) { system.init_logger_with_default_filter(""); let program = Program::current(system); program.send_bytes(USER_ID, []); - let lock_result = program.send( + let msg_id = program.send( USER_ID, Command::MxLock( None, MxLockContinuation::General(LockContinuation::MoveToStatic), ), ); - (program, lock_result.sent_message_id()) + system.run_next_block(); + (program, msg_id) } diff --git a/examples/waiter/tests/rw_lock_access.rs b/examples/waiter/tests/rw_lock_access.rs index c3603deea32..a17d2f8fd30 100644 --- a/examples/waiter/tests/rw_lock_access.rs +++ b/examples/waiter/tests/rw_lock_access.rs @@ -77,14 +77,14 @@ fn access_rw_lock_guard_from_different_msg_fails( let system = System::new(); let (program, lock_msg_id) = init_fixture(&system, lock_type); - let lock_access_result = program.send( + let lock_access_msg_id = program.send( USER_ID, Command::RwLockStaticAccess(lock_type, lock_access_subcommand), ); + let lock_access_result = system.run_next_block(); - lock_access_result.assert_panicked_with(format!( - "{lock_type:?} lock guard held by message {lock_msg_id} is being accessed by message {}", - lock_access_result.sent_message_id(), + lock_access_result.assert_panicked_with(lock_access_msg_id, format!( + "{lock_type:?} lock guard held by message {lock_msg_id} is being accessed by message {lock_access_msg_id}" )); } @@ -92,12 +92,13 @@ fn init_fixture(system: &System, lock_type: RwLockType) -> (Program<'_>, Message system.init_logger_with_default_filter(""); let program = Program::current(system); program.send_bytes(USER_ID, []); - let lock_result = program.send( + let msg_id = program.send( USER_ID, Command::RwLock( lock_type, RwLockContinuation::General(LockContinuation::MoveToStatic), ), ); - (program, lock_result.sent_message_id()) + system.run_next_block(); + (program, msg_id) } diff --git a/gtest/src/lib.rs b/gtest/src/lib.rs index b48e4a155a4..a2dfed4d204 100644 --- a/gtest/src/lib.rs +++ b/gtest/src/lib.rs @@ -124,13 +124,17 @@ //! let prog = Program::current(&sys); //! //! // Send an init message to the program. -//! let res = prog.send_bytes(USER_ID, b"Doesn't matter"); +//! let init_message_id = prog.send_bytes(USER_ID, b"Doesn't matter"); +//! +//! // Run execution of the block which will contain `init_message_id` +//! let block_run_result = sys.run_next_block(); //! //! // Check whether the program was initialized successfully. -//! assert!(!res.main_failed()); +//! assert!(block_run_result.succeed.contains(&init_message_id)); //! //! // Send a handle message to the program. -//! let res = prog.send_bytes(USER_ID, b"PING"); +//! let handle_message_id = prog.send_bytes(USER_ID, b"PING"); +//! let block_run_result = sys.run_next_block(); //! //! // Check the result of the program execution. //! // 1. Create a log pattern with the expected result. @@ -140,10 +144,10 @@ //! .payload_bytes(b"PONG"); //! //! // 2. Check whether the program was executed successfully. -//! assert!(!res.main_failed()); +//! assert!(block_run_result.succeed.contains(&handle_message_id)); //! //! // 3. Make sure the log entry is in the result. -//! assert!(res.contains(&log)); +//! assert!(block_run_result.contains(&log)); //! } //! } //! ``` @@ -279,22 +283,29 @@ //! //! ## Processing the result of the program execution //! -//! Any sending functions in the lib returns [`RunResult`] structure. +//! Any sending functions in the lib returns an id of the sent message. +//! +//! In order to actually get the result of the program execution the block +//! execution should be triggered (see "Block execution model" section). +//! Block execution function returns the result of the block run +//! ([`BlockRunResult`]) //! //! It contains the final result of the processing message and others, which //! were created during the execution. //! -//! It has 4 main functions: +//! It has 2 main functions: +//! +//! - [`BlockRunResult::log`] — returns the reference to the Vec produced to +//! users messages. You may assert them as you wish, iterating through them. +//! - [`BlockRunResult::contains`] — returns bool which shows that logs contain +//! a given log. Syntax sugar around `res.log().iter().any(|v| v == arg)`. +//! +//! Fields of the type are public, and some of them can be really useful: //! -//! - [`RunResult::log`] — returns the reference to the Vec produced to users -//! messages. You may assert them as you wish, iterating through them. -//! - [`RunResult::main_failed`] — returns bool which shows that there was panic -//! during the execution of the main message. -//! - [`RunResult::others_failed`] — returns bool which shows that there was -//! panic during the execution of the created messages during the main -//! execution. Equals false if no others were called. -//! - [`RunResult::contains`] — returns bool which shows that logs contain a -//! given log. Syntax sugar around `res.log().iter().any(|v| v == arg)`. +//! - field `succeed` is a set of ids of messages that were successfully +//! executed. +//! - field `failed` is a set of ids of messages that failed during the +//! execution. //! //! To build a log for assertion you need to use [`Log`] structure with its //! builders. All fields here are optional. Assertion with `Log`s from core are @@ -332,18 +343,31 @@ //! assert_eq!(y, y_from); //! ``` //! -//! ## Spending blocks +//! ## Blocks execution model +//! +//! Block execution has 2 main step: +//! - tasks processing +//! - messages processing +//! +//! Tasks processing is a step, when all scheduled for the current block number +//! tasks are tried to be processed. This includes processing delayed +//! dispatches, waking waited messages and etc. +//! +//! Messages processing is a step, when messages from the queue are processed +//! until either the queue is empty or the block gas allowance is not enough for +//! the execution. //! -//! You may control time in the system by spending blocks. +//! Blocks can't be "spent" without their execution except for use the +//! [`System::run_scheduled_tasks`] method, which doesn't process the message +//! queue, but only processes scheduled tasks triggering blocks info +//! adjustments, which can be used to "spend" blocks. //! -//! It adds the amount of blocks passed as arguments to the current block of the -//! system. Same for the timestamp. Note, that for now 1 block in Gear-based -//! network is 3 sec duration. +//! Note, that for now 1 block in Gear-based network is 3 sec duration. //! //! ```no_run //! # let sys = gtest::System::new(); -//! // Spend 150 blocks (7.5 mins for 3 sec block). -//! sys.spend_blocks(150); +//! // Spend 150 blocks by running only the task pool (7.5 mins for 3 sec block). +//! sys.run_scheduled_tasks(150); //! ``` //! //! Note that processing messages (e.g. by using @@ -429,7 +453,7 @@ mod manager; mod program; mod system; -pub use crate::log::{CoreLog, Log, RunResult}; +pub use crate::log::{BlockRunResult, CoreLog, Log}; pub use codec; pub use error::{Result, TestError}; pub use mailbox::ActorMailbox; diff --git a/gtest/src/log.rs b/gtest/src/log.rs index 2e45c0981f7..29824cfb8e0 100644 --- a/gtest/src/log.rs +++ b/gtest/src/log.rs @@ -18,12 +18,17 @@ use crate::program::{Gas, ProgramIdWrapper}; use codec::{Codec, Encode}; +use core_processor::configs::BlockInfo; use gear_core::{ ids::{MessageId, ProgramId}, message::{Payload, StoredMessage, UserStoredMessage}, }; use gear_core_errors::{ErrorReplyReason, ReplyCode, SimpleExecutionError, SuccessReplyReason}; -use std::{collections::BTreeMap, convert::TryInto, fmt::Debug}; +use std::{ + collections::{BTreeMap, BTreeSet}, + convert::TryInto, + fmt::Debug, +}; /// A log that emitted by a program, for user defined logs, /// see [`Log`]. @@ -372,20 +377,35 @@ impl PartialEq for CoreLog { } } -/// The result of a message run. -#[derive(Debug, Clone)] -pub struct RunResult { - pub(crate) log: Vec, - pub(crate) main_failed: bool, - pub(crate) others_failed: bool, - pub(crate) message_id: MessageId, - pub(crate) total_processed: u32, - pub(crate) main_gas_burned: Gas, - pub(crate) others_gas_burned: BTreeMap, +/// Result of running the block. +#[derive(Debug, Default)] +pub struct BlockRunResult { + /// Executed block info. + pub block_info: BlockInfo, + /// Gas allowance spent during the execution. + pub gas_allowance_spent: Gas, + /// Set of successfully executed messages + /// during the current block execution. + pub succeed: BTreeSet, + /// Set of failed messages during the current + /// block execution. + pub failed: BTreeSet, + /// Set of not executed messages + /// during the current block execution. + pub not_executed: BTreeSet, + /// Total messages processed during the current + /// execution. + pub total_processed: u32, + // TODO #4122 + /// Logs created during the current execution. + pub log: Vec, + /// Mapping gas burned for each message during + /// the current block execution. + pub gas_burned: BTreeMap, } -impl RunResult { - /// If the result contains a specific log. +impl BlockRunResult { + /// Check, if the result contains a specific log. pub fn contains + Clone>(&self, log: &T) -> bool { let log = log.clone().into(); @@ -397,36 +417,6 @@ impl RunResult { &self.log } - /// If main message failed. - pub fn main_failed(&self) -> bool { - self.main_failed - } - - /// If any other messages failed. - pub fn others_failed(&self) -> bool { - self.others_failed - } - - /// Get the message id. - pub fn sent_message_id(&self) -> MessageId { - self.message_id - } - - /// Get the total number of processed messages. - pub fn total_processed(&self) -> u32 { - self.total_processed - } - - /// Get the total gas burned by the main message. - pub fn main_gas_burned(&self) -> Gas { - self.main_gas_burned - } - - /// Get the total gas burned by the other messages. - pub fn others_gas_burned(&self) -> &BTreeMap { - &self.others_gas_burned - } - /// Returns decoded logs. pub fn decoded_log(&self) -> Vec> { self.log @@ -436,16 +426,11 @@ impl RunResult { .collect() } - /// If the main message panicked. - pub fn main_panicked(&self) -> bool { - self.main_panic_log().is_some() - } - - /// Asserts that the main message panicked and that the panic contained a + /// Asserts that the message panicked and that the panic contained a /// given message. #[track_caller] - pub fn assert_panicked_with(&self, msg: impl Into) { - let panic_log = self.main_panic_log(); + pub fn assert_panicked_with(&self, message_id: MessageId, msg: impl Into) { + let panic_log = self.message_panic_log(message_id); assert!(panic_log.is_some(), "Program did not panic"); let msg = msg.into(); let payload = String::from_utf8( @@ -463,18 +448,18 @@ impl RunResult { } /// Trying to get the panic log. - fn main_panic_log(&self) -> Option<&CoreLog> { - let main_log = self + fn message_panic_log(&self, message_id: MessageId) -> Option<&CoreLog> { + let msg_log = self .log .iter() - .find(|log| log.reply_to == Some(self.message_id))?; + .find(|log| log.reply_to == Some(message_id))?; let is_panic = matches!( - main_log.reply_code(), + msg_log.reply_code(), Some(ReplyCode::Error(ErrorReplyReason::Execution( SimpleExecutionError::UserspacePanic ))) ); - is_panic.then_some(main_log) + is_panic.then_some(msg_log) } } diff --git a/gtest/src/mailbox/actor.rs b/gtest/src/mailbox/actor.rs index 940b8c42e66..7daa1f8eb4a 100644 --- a/gtest/src/mailbox/actor.rs +++ b/gtest/src/mailbox/actor.rs @@ -16,7 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use crate::{manager::ExtManager, Log, RunResult, GAS_ALLOWANCE}; +use crate::{manager::ExtManager, Log, GAS_ALLOWANCE}; use codec::Encode; use gear_common::{auxiliary::mailbox::*, storage::Interval}; use gear_core::{ @@ -54,7 +54,7 @@ impl<'a> ActorMailbox<'a> { log: Log, payload: impl Encode, value: u128, - ) -> Result { + ) -> Result { self.reply_bytes(log, payload.encode(), value) } @@ -65,7 +65,7 @@ impl<'a> ActorMailbox<'a> { log: Log, raw_payload: impl AsRef<[u8]>, value: u128, - ) -> Result { + ) -> Result { let mailboxed_msg = self .find_message_by_log(&log) .ok_or(MailboxErrorImpl::ElementNotFound)?; @@ -93,7 +93,7 @@ impl<'a> ActorMailbox<'a> { Ok(self .manager .borrow_mut() - .validate_and_run_dispatch(dispatch)) + .validate_and_route_dispatch(dispatch)) } /// Claims value from a message in mailbox. @@ -124,17 +124,10 @@ impl<'a> ActorMailbox<'a> { #[cfg(test)] mod tests { - use crate::{ - program::ProgramIdWrapper, Log, Program, System, EXISTENTIAL_DEPOSIT, GAS_ALLOWANCE, - }; + use crate::{Log, Program, System, EXISTENTIAL_DEPOSIT}; use codec::Encode; use demo_constructor::{Call, Calls, Scheme, WASM_BINARY}; - use gear_common::auxiliary::mailbox::MailboxErrorImpl; - use gear_core::{ - ids::{prelude::MessageIdExt, MessageId, ProgramId}, - message::{Dispatch, DispatchKind, HandleMessage, HandlePacket, Payload}, - }; - use std::convert::TryInto; + use gear_core::ids::ProgramId; fn prepare_program(system: &System) -> (Program<'_>, ([u8; 32], Vec, Log)) { let program = Program::from_binary_with_id(system, 121, WASM_BINARY); @@ -143,44 +136,13 @@ mod tests { let payload = b"sup!".to_vec(); let log = Log::builder().dest(sender).payload_bytes(payload.clone()); - let res = program.send(sender, Scheme::empty()); - assert!(!res.main_failed()); + let msg_id = program.send(sender, Scheme::empty()); + let res = system.run_next_block(); + assert!(res.succeed.contains(&msg_id)); (program, (sender, payload, log)) } - #[test] - fn user2user_doesnt_reach_mailbox() { - let system = System::new(); - - let source = ProgramIdWrapper::from(100).0; - let message_id: MessageId = MessageId::generate_from_user(0, source, 0); - let destination = ProgramIdWrapper::from(200).0; - let payload: Payload = vec![1, 2, 3].try_into().expect("len exceed"); - let log = Log::builder() - .dest(destination) - .payload_bytes(payload.inner()); - - let dispatch = Dispatch::new( - DispatchKind::Handle, - HandleMessage::from_packet( - message_id, - HandlePacket::new_with_gas(destination, payload, GAS_ALLOWANCE, 0), - ) - .into_message(source), - ); - - // Log exists - let res = system.send_dispatch(dispatch); - assert!(res.contains(&log)); - - // But message doesn't exist in mailbox - let mailbox = system.get_mailbox(destination); - let res = mailbox.reply(log, b"", 0); - assert!(res.is_err()); - assert_eq!(res.unwrap_err(), MailboxErrorImpl::ElementNotFound); - } - #[test] fn claim_value_from_mailbox() { let system = System::new(); @@ -191,8 +153,9 @@ mod tests { let value_send = 2 * EXISTENTIAL_DEPOSIT; let handle = Calls::builder().send_value(sender, payload, value_send); - let res = program.send_bytes_with_value(sender, handle.encode(), value_send); - assert!(!res.main_failed()); + let msg_id = program.send_bytes_with_value(sender, handle.encode(), value_send); + let res = system.run_next_block(); + assert!(res.succeed.contains(&msg_id)); assert!(res.contains(&log)); assert_eq!(system.balance_of(sender), original_balance - value_send); @@ -208,16 +171,18 @@ mod tests { let (program, (sender, payload, log)) = prepare_program(&system); let handle = Calls::builder().send(sender, payload); - let res = program.send(sender, handle); - assert!(!res.main_failed()); + let msg_id = program.send(sender, handle); + let res = system.run_next_block(); + assert!(res.succeed.contains(&msg_id)); assert!(res.contains(&log)); let mailbox = system.get_mailbox(sender); assert!(mailbox.contains(&log)); - let res = mailbox + let msg_id = mailbox .reply(log, Calls::default(), 0) .expect("sending reply failed: didn't find message in mailbox"); - assert!(!res.main_failed()); + let res = system.run_next_block(); + assert!(res.succeed.contains(&msg_id)); } #[test] @@ -233,10 +198,11 @@ mod tests { 0.into(), delay.into(), )); - let res = program.send(sender, handle); - assert!(!res.main_failed()); + let msg_id = program.send(sender, handle); + let res = system.run_next_block(); + assert!(res.succeed.contains(&msg_id)); - let results = system.spend_blocks(delay); + let results = system.run_scheduled_tasks(delay); let delayed_dispatch_res = results.last().expect("internal error: no blocks spent"); assert!(delayed_dispatch_res.contains(&log)); diff --git a/gtest/src/manager.rs b/gtest/src/manager.rs index 3d5c9fd5e0d..70586bdd701 100644 --- a/gtest/src/manager.rs +++ b/gtest/src/manager.rs @@ -19,7 +19,7 @@ use crate::{ blocks::BlocksManager, gas_tree::GasTreeManager, - log::{CoreLog, RunResult}, + log::{BlockRunResult, CoreLog}, mailbox::MailboxManager, program::{Gas, WasmProgram}, Result, TestError, DISPATCH_HOLD_COST, EPOCH_DURATION_IN_BLOCKS, EXISTENTIAL_DEPOSIT, @@ -62,8 +62,9 @@ use gear_wasm_instrument::gas_metering::Schedule; use rand::{rngs::StdRng, RngCore, SeedableRng}; use std::{ cell::{Ref, RefCell, RefMut}, - collections::{BTreeMap, HashMap, VecDeque}, + collections::{BTreeMap, BTreeSet, HashMap, VecDeque}, convert::TryInto, + mem, rc::Rc, }; @@ -245,15 +246,14 @@ pub(crate) struct ExtManager { pub(crate) gas_tree: GasTreeManager, pub(crate) gas_allowance: Gas, pub(crate) delayed_dispatches: HashMap>, + pub(crate) messages_processing_enabled: bool, - // Last run info - pub(crate) origin: ProgramId, - pub(crate) msg_id: MessageId, + // Last block execution info + pub(crate) succeed: BTreeSet, + pub(crate) failed: BTreeSet, + pub(crate) not_executed: BTreeSet, + pub(crate) gas_burned: BTreeMap, pub(crate) log: Vec, - pub(crate) main_failed: bool, - pub(crate) others_failed: bool, - pub(crate) main_gas_burned: Gas, - pub(crate) others_gas_burned: BTreeMap, } impl ExtManager { @@ -263,6 +263,7 @@ impl ExtManager { msg_nonce: 1, id_nonce: 1, blocks_manager: BlocksManager::new(), + messages_processing_enabled: true, random_data: ( { let mut rng = StdRng::seed_from_u64(INITIAL_RANDOM_SEED); @@ -314,7 +315,7 @@ impl ExtManager { } /// Insert message into the delayed queue. - pub(crate) fn send_delayed_dispatch(&mut self, dispatch: Dispatch, bn: u32) { + fn send_delayed_dispatch(&mut self, dispatch: Dispatch, bn: u32) { self.delayed_dispatches .entry(bn) .or_default() @@ -322,46 +323,43 @@ impl ExtManager { } /// Process all delayed dispatches. - pub(crate) fn process_delayed_dispatches(&mut self, bn: u32) -> Vec { - self.delayed_dispatches - .remove(&bn) - .map(|dispatches| { - dispatches - .into_iter() - .map(|dispatch| self.run_dispatch(dispatch, true)) - .collect() - }) - .unwrap_or_default() + pub(crate) fn process_delayed_dispatches(&mut self, bn: u32) { + let Some(dispatches) = self.delayed_dispatches.remove(&bn) else { + return; + }; + + for dispatch in dispatches { + self.route_dispatch_from_task_pool(dispatch); + } } /// Process scheduled wait list. - pub(crate) fn process_scheduled_wait_list(&mut self, bn: u32) -> Vec { - self.wait_list_schedules - .remove(&bn) - .map(|ids| { - ids.into_iter() - .filter_map(|key| { - self.wait_list.remove(&key).map(|dispatch| { - let (kind, message, ..) = dispatch.into_parts(); - let message = Message::new( - message.id(), - message.source(), - message.destination(), - message - .payload_bytes() - .to_vec() - .try_into() - .unwrap_or_default(), - self.gas_tree.get_limit(message.id()).ok(), - message.value(), - message.details(), - ); - self.run_dispatch(Dispatch::new(kind, message), true) - }) - }) - .collect() - }) - .unwrap_or_default() + pub(crate) fn process_scheduled_wait_list(&mut self, bn: u32) { + let Some(wl_schedules) = self.wait_list_schedules.remove(&bn) else { + return; + }; + + for wl_schedule in wl_schedules { + let Some(dispatch) = self.wait_list.remove(&wl_schedule) else { + continue; + }; + + let (kind, message, ..) = dispatch.into_parts(); + let message = Message::new( + message.id(), + message.source(), + message.destination(), + message + .payload_bytes() + .to_vec() + .try_into() + .unwrap_or_default(), + self.gas_tree.get_limit(message.id()).ok(), + message.value(), + message.details(), + ); + self.route_dispatch_from_task_pool(Dispatch::new(kind, message)); + } } /// Check if the current block number should trigger new epoch and reset @@ -400,9 +398,95 @@ impl ExtManager { } } - pub(crate) fn validate_and_run_dispatch(&mut self, dispatch: Dispatch) -> RunResult { + pub(crate) fn validate_and_route_dispatch(&mut self, dispatch: Dispatch) -> MessageId { self.validate_dispatch(&dispatch); - self.run_dispatch(dispatch, false) + let gas_limit = dispatch + .gas_limit() + .unwrap_or_else(|| unreachable!("message from program API always has gas")); + self.gas_tree + .create(dispatch.source(), dispatch.id(), gas_limit) + .unwrap_or_else(|e| unreachable!("GasTree corrupted! {:?}", e)); + self.route_dispatch(dispatch) + } + + pub(crate) fn route_dispatch(&mut self, dispatch: Dispatch) -> MessageId { + let stored_dispatch = dispatch.into_stored(); + if self.is_user(&stored_dispatch.destination()) { + panic!("Program API only sends message to programs.") + } + + let message_id = stored_dispatch.id(); + self.dispatches.push_back(stored_dispatch); + + message_id + } + + // TODO #4120 Charge for task pool processing the gas from gas allowance + // TODO #4121 + #[track_caller] + pub(crate) fn run_new_block(&mut self, allowance: Gas) -> BlockRunResult { + self.gas_allowance = allowance; + self.blocks_manager.next_block(); + let new_block_bn = self.blocks_manager.get().height; + + self.process_tasks(new_block_bn); + let total_processed = self.process_messages(); + + BlockRunResult { + block_info: self.blocks_manager.get(), + gas_allowance_spent: Gas(GAS_ALLOWANCE) - self.gas_allowance, + succeed: mem::take(&mut self.succeed), + failed: mem::take(&mut self.failed), + not_executed: mem::take(&mut self.not_executed), + total_processed, + log: mem::take(&mut self.log) + .into_iter() + .map(CoreLog::from) + .collect(), + gas_burned: mem::take(&mut self.gas_burned), + } + } + + #[track_caller] + pub(crate) fn process_tasks(&mut self, bn: u32) { + self.process_delayed_dispatches(bn); + self.process_scheduled_wait_list(bn); + } + + #[track_caller] + fn process_messages(&mut self) -> u32 { + self.messages_processing_enabled = true; + + let mut total_processed = 0; + while self.messages_processing_enabled { + let dispatch = match self.dispatches.pop_front() { + Some(dispatch) => dispatch, + None => break, + }; + + let mut actors = self.actors.borrow_mut(); + let (actor, balance) = actors + .get_mut(&dispatch.destination()) + .expect("Somehow message queue contains message for user"); + let balance = *balance; + + if actor.is_dormant() { + drop(actors); + self.process_dormant(balance, dispatch); + } else if let Some((data, code)) = actor.get_executable_actor_data() { + drop(actors); + self.process_normal(balance, data, code, dispatch); + } else if let Some(mock) = actor.take_mock() { + drop(actors); + self.process_mock(mock, dispatch); + } else { + unreachable!(); + } + + total_processed += 1; + } + + total_processed } #[track_caller] @@ -433,26 +517,8 @@ impl ExtManager { } #[track_caller] - pub(crate) fn run_dispatch(&mut self, dispatch: Dispatch, from_task_pool: bool) -> RunResult { - self.prepare_for(&dispatch, !from_task_pool); - + pub(crate) fn route_dispatch_from_task_pool(&mut self, dispatch: Dispatch) { if self.is_program(&dispatch.destination()) { - if !from_task_pool { - let gas_limit = matches!(dispatch.kind(), DispatchKind::Signal) - .then(|| { - assert!( - dispatch.gas_limit().is_none(), - "signals must be sent with `None` gas limit" - ); - GAS_ALLOWANCE - }) - .or_else(|| dispatch.gas_limit()) - .unwrap_or_else(|| unreachable!("message from program API has always gas")); - self.gas_tree - .create(dispatch.source(), dispatch.id(), gas_limit) - .unwrap_or_else(|e| unreachable!("GasTree corrupted! {:?}", e)); - } - self.dispatches.push_back(dispatch.into_stored()); } else { let message = dispatch.into_parts().1.into_stored(); @@ -467,44 +533,6 @@ impl ExtManager { self.log.push(message) } - - let mut total_processed = 0; - while let Some(dispatch) = self.dispatches.pop_front() { - let dest = dispatch.destination(); - - let mut actors = self.actors.borrow_mut(); - let (actor, balance) = actors - .get_mut(&dest) - .expect("Somehow message queue contains message for user"); - let balance = *balance; - - if actor.is_dormant() { - drop(actors); - self.process_dormant(balance, dispatch); - } else if let Some((data, code)) = actor.get_executable_actor_data() { - drop(actors); - self.process_normal(balance, data, code, dispatch); - } else if let Some(mock) = actor.take_mock() { - drop(actors); - self.process_mock(mock, dispatch); - } else { - unreachable!(); - } - - total_processed += 1; - } - - let log = self.log.clone(); - - RunResult { - main_failed: self.main_failed, - others_failed: self.others_failed, - log: log.into_iter().map(CoreLog::from).collect(), - message_id: self.msg_id, - total_processed, - main_gas_burned: self.main_gas_burned, - others_gas_burned: self.others_gas_burned.clone(), - } } /// Call non-void meta function from actor stored in manager. @@ -672,35 +700,6 @@ impl ExtManager { } } - #[track_caller] - fn prepare_for(&mut self, dispatch: &Dispatch, update_block: bool) { - self.msg_id = dispatch.id(); - self.origin = dispatch.source(); - self.log.clear(); - self.main_failed = false; - self.others_failed = false; - self.main_gas_burned = Gas::zero(); - self.others_gas_burned = { - let mut m = BTreeMap::new(); - let block_height = self.blocks_manager.get().height; - m.insert(block_height, Gas::zero()); - - m - }; - self.gas_allowance = Gas(GAS_ALLOWANCE); - if update_block { - let _ = self.blocks_manager.next_block(); - } - } - - fn mark_failed(&mut self, msg_id: MessageId) { - if self.msg_id == msg_id { - self.main_failed = true; - } else { - self.others_failed = true; - } - } - #[track_caller] fn init_success(&mut self, program_id: ProgramId) { let mut actors = self.actors.borrow_mut(); @@ -709,21 +708,16 @@ impl ExtManager { .expect("Can't find existing program"); actor.set_initialized(); - - drop(actors); } #[track_caller] - fn init_failure(&mut self, message_id: MessageId, program_id: ProgramId) { + fn init_failure(&mut self, program_id: ProgramId) { let mut actors = self.actors.borrow_mut(); let (actor, _) = actors .get_mut(&program_id) .expect("Can't find existing program"); *actor = TestActor::Dormant; - - drop(actors); - self.mark_failed(message_id); } fn process_mock(&mut self, mut mock: Box, dispatch: StoredDispatch) { @@ -1007,14 +1001,23 @@ impl JournalHandler for ExtManager { outcome: DispatchOutcome, ) { match outcome { - DispatchOutcome::MessageTrap { .. } => self.mark_failed(message_id), - DispatchOutcome::Success - | DispatchOutcome::NoExecution - | DispatchOutcome::Exit { .. } => {} + DispatchOutcome::MessageTrap { .. } => { + self.failed.insert(message_id); + } + DispatchOutcome::NoExecution => { + self.not_executed.insert(message_id); + } + DispatchOutcome::Success | DispatchOutcome::Exit { .. } => { + self.succeed.insert(message_id); + } DispatchOutcome::InitFailure { program_id, .. } => { - self.init_failure(message_id, program_id) + self.init_failure(program_id); + self.failed.insert(message_id); + } + DispatchOutcome::InitSuccess { program_id, .. } => { + self.init_success(program_id); + self.succeed.insert(message_id); } - DispatchOutcome::InitSuccess { program_id, .. } => self.init_success(program_id), } } @@ -1024,15 +1027,12 @@ impl JournalHandler for ExtManager { .spend(message_id, amount) .unwrap_or_else(|e| unreachable!("GasTree corrupted! {:?}", e)); - if self.msg_id == message_id { - self.main_gas_burned = self.main_gas_burned.saturating_add(Gas(amount)); - } else { - self.others_gas_burned - .entry(self.blocks_manager.get().height) - .and_modify(|others_gas_burned| { - *others_gas_burned = others_gas_burned.saturating_add(Gas(amount)) - }); - } + self.gas_burned + .entry(message_id) + .and_modify(|gas| { + *gas += Gas(amount); + }) + .or_insert(Gas(amount)); } fn exit_dispatch(&mut self, id_exited: ProgramId, value_destination: ProgramId) { @@ -1265,11 +1265,8 @@ impl JournalHandler for ExtManager { gas_burned, ); - // Update gas allowance and start a new block with the `dispatch` being first in - // the queue. - self.gas_allowance = Gas(GAS_ALLOWANCE); + self.messages_processing_enabled = false; self.dispatches.push_front(dispatch); - self.blocks_manager.next_block(); } fn reserve_gas( diff --git a/gtest/src/program.rs b/gtest/src/program.rs index bf50f745541..5a346971e47 100644 --- a/gtest/src/program.rs +++ b/gtest/src/program.rs @@ -17,7 +17,6 @@ // along with this program. If not, see . use crate::{ - log::RunResult, manager::{Balance, ExtManager, GenuineProgram, MintMode, Program as InnerProgram, TestActor}, system::System, Result, GAS_ALLOWANCE, @@ -26,9 +25,8 @@ use codec::{Codec, Decode, Encode}; use gear_core::{ code::{Code, CodeAndId, InstrumentedCodeAndId}, ids::{prelude::*, CodeId, MessageId, ProgramId}, - message::{Dispatch, DispatchKind, Message, SignalMessage}, + message::{Dispatch, DispatchKind, Message}, }; -use gear_core_errors::SignalCode; use gear_utils::{MemoryPageDump, ProgramMemoryDump}; use gear_wasm_instrument::gas_metering::Schedule; use path_clean::PathClean; @@ -485,7 +483,7 @@ impl<'a> Program<'a> { } /// Send message to the program. - pub fn send(&self, from: ID, payload: C) -> RunResult + pub fn send(&self, from: ID, payload: C) -> MessageId where ID: Into, C: Codec, @@ -494,7 +492,7 @@ impl<'a> Program<'a> { } /// Send message to the program with value. - pub fn send_with_value(&self, from: ID, payload: C, value: u128) -> RunResult + pub fn send_with_value(&self, from: ID, payload: C, value: u128) -> MessageId where ID: Into, C: Codec, @@ -509,7 +507,7 @@ impl<'a> Program<'a> { payload: P, gas_limit: u64, value: u128, - ) -> RunResult + ) -> MessageId where ID: Into, P: Encode, @@ -518,7 +516,7 @@ impl<'a> Program<'a> { } /// Send message to the program with bytes payload. - pub fn send_bytes(&self, from: ID, payload: T) -> RunResult + pub fn send_bytes(&self, from: ID, payload: T) -> MessageId where ID: Into, T: Into>, @@ -528,7 +526,7 @@ impl<'a> Program<'a> { /// Send the message to the program with bytes payload and value. #[track_caller] - pub fn send_bytes_with_value(&self, from: ID, payload: T, value: u128) -> RunResult + pub fn send_bytes_with_value(&self, from: ID, payload: T, value: u128) -> MessageId where ID: Into, T: Into>, @@ -543,7 +541,7 @@ impl<'a> Program<'a> { payload: T, gas_limit: u64, value: u128, - ) -> RunResult + ) -> MessageId where ID: Into, T: Into>, @@ -557,7 +555,7 @@ impl<'a> Program<'a> { payload: T, gas_limit: u64, value: u128, - ) -> RunResult + ) -> MessageId where ID: Into, T: Into>, @@ -591,33 +589,7 @@ impl<'a> Program<'a> { }; drop(actors); - system.validate_and_run_dispatch(Dispatch::new(kind, message)) - } - - /// 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 source = from.into().0; - - let origin_msg_id = MessageId::generate_from_user( - system.blocks_manager.get().height, - source, - system.fetch_inc_message_nonce() as u128, - ); - let message = SignalMessage::new(origin_msg_id, code); - - let mut actors = system.actors.borrow_mut(); - let (actor, _) = actors.get_mut(&self.id).expect("Can't fail"); - - if let TestActor::Uninitialized(id @ None, _) = actor { - *id = Some(message.id()); - }; - - drop(actors); - let dispatch = message.into_dispatch(origin_msg_id, self.id); - system.validate_and_run_dispatch(dispatch) + system.validate_and_route_dispatch(Dispatch::new(kind, message)) } /// Get program id. @@ -916,11 +888,14 @@ mod tests { let prog = Program::from_binary_with_id(&sys, 137, WASM_BINARY); - let run_result = prog.send(user_id, scheme); - assert!(!run_result.main_failed()); - let run_result = prog.send(user_id, *b"Hello"); + let msg_id = prog.send(user_id, scheme); + let res = sys.run_next_block(); + assert!(res.succeed.contains(&msg_id)); + + let msg_id = prog.send(user_id, *b"Hello"); + let res = sys.run_next_block(); - run_result.assert_panicked_with(panic_message); + res.assert_panicked_with(msg_id, panic_message); let log = Log::builder().payload_bytes(message); let value = sys.get_mailbox(user_id).claim_value(log); assert!(value.is_ok()); @@ -936,18 +911,22 @@ mod tests { let prog = Program::from_binary_with_id(&sys, 137, demo_futures_unordered::WASM_BINARY); let init_msg_payload = String::from("InvalidInput"); - let run_result = prog.send(user_id, init_msg_payload); + let msg_id = prog.send(user_id, init_msg_payload); - run_result.assert_panicked_with("Failed to load destination: Decode(Error)"); + let res = sys.run_next_block(); - let run_result = prog.send(user_id, String::from("should_be_skipped")); + res.assert_panicked_with(msg_id, "Failed to load destination: Decode(Error)"); + + let msg_id = prog.send(user_id, String::from("should_be_skipped")); + + let res = sys.run_next_block(); let expected_log = Log::error_builder(ErrorReplyReason::InactiveActor) .source(prog.id()) .dest(user_id); - assert!(!run_result.main_failed()); - assert!(run_result.contains(&expected_log)); + assert!(res.not_executed.contains(&msg_id)); + assert!(res.contains(&expected_log)); } #[test] @@ -965,10 +944,12 @@ mod tests { assert_eq!(prog.balance(), 2 * crate::EXISTENTIAL_DEPOSIT); prog.send_with_value(user_id, "init".to_string(), crate::EXISTENTIAL_DEPOSIT); + sys.run_next_block(); assert_eq!(prog.balance(), 3 * crate::EXISTENTIAL_DEPOSIT); assert_eq!(sys.balance_of(user_id), 9 * crate::EXISTENTIAL_DEPOSIT); prog.send_with_value(user_id, "PING".to_string(), 2 * crate::EXISTENTIAL_DEPOSIT); + sys.run_next_block(); assert_eq!(prog.balance(), 5 * crate::EXISTENTIAL_DEPOSIT); assert_eq!(sys.balance_of(user_id), 7 * crate::EXISTENTIAL_DEPOSIT); } @@ -991,21 +972,26 @@ mod tests { let prog = Program::from_binary_with_id(&sys, 137, demo_piggy_bank::WASM_BINARY); prog.send_bytes(receiver, b"init"); + sys.run_next_block(); assert_eq!(prog.balance(), 0); // Send values to the program prog.send_bytes_with_value(sender0, b"insert", 2 * crate::EXISTENTIAL_DEPOSIT); + sys.run_next_block(); assert_eq!(sys.balance_of(sender0), 18 * crate::EXISTENTIAL_DEPOSIT); prog.send_bytes_with_value(sender1, b"insert", 4 * crate::EXISTENTIAL_DEPOSIT); + sys.run_next_block(); assert_eq!(sys.balance_of(sender1), 16 * crate::EXISTENTIAL_DEPOSIT); prog.send_bytes_with_value(sender2, b"insert", 6 * crate::EXISTENTIAL_DEPOSIT); + sys.run_next_block(); assert_eq!(sys.balance_of(sender2), 14 * crate::EXISTENTIAL_DEPOSIT); // Check program's balance assert_eq!(prog.balance(), (2 + 4 + 6) * crate::EXISTENTIAL_DEPOSIT); // Request to smash the piggy bank and send the value to the receiver address - let res = prog.send_bytes(receiver, b"smash"); + prog.send_bytes(receiver, b"smash"); + let res = sys.run_next_block(); let reply_to_id = { let log = res.log(); // 1 auto reply and 1 message from program @@ -1057,6 +1043,7 @@ mod tests { assert_eq!(sys.balance_of(user), crate::EXISTENTIAL_DEPOSIT); prog.send_bytes_with_value(user, b"init", crate::EXISTENTIAL_DEPOSIT + 1); + sys.run_next_block(); } #[test] @@ -1072,9 +1059,11 @@ mod tests { let prog = Program::from_binary_with_id(&sys, 137, demo_piggy_bank::WASM_BINARY); prog.send_bytes(receiver, b"init"); + sys.run_next_block(); // Get zero value to the receiver's mailbox prog.send_bytes(receiver, b"smash"); + sys.run_next_block(); let receiver_mailbox = sys.get_mailbox(receiver); assert!(receiver_mailbox @@ -1085,6 +1074,7 @@ mod tests { // Get the value > ED to the receiver's mailbox prog.send_bytes_with_value(sender, b"insert", 2 * crate::EXISTENTIAL_DEPOSIT); prog.send_bytes(receiver, b"smash"); + sys.run_next_block(); // Check receiver's balance assert!(receiver_mailbox @@ -1117,14 +1107,16 @@ mod tests { // Init capacitor with limit = 15 prog.send(signer, InitMessage::Capacitor("15".to_string())); + sys.run_next_block(); // Charge capacitor with charge = 10 - let response = dbg!(prog.send_bytes(signer, b"10")); + dbg!(prog.send_bytes(signer, b"10")); + let res = sys.run_next_block(); let log = Log::builder() .source(prog.id()) .dest(signer) .payload_bytes([]); - assert!(response.contains(&log)); + assert!(res.contains(&log)); let cleanup = CleanupFolderOnDrop { path: "./296c6962726".to_string(), @@ -1132,25 +1124,27 @@ mod tests { prog.save_memory_dump("./296c6962726/demo_custom.dump"); // Charge capacitor with charge = 10 - let response = prog.send_bytes(signer, b"10"); + prog.send_bytes(signer, b"10"); + let res = sys.run_next_block(); let log = Log::builder() .source(prog.id()) .dest(signer) .payload_bytes("Discharged: 20"); // dbg!(log.clone()); - assert!(response.contains(&log)); + assert!(res.contains(&log)); assert!(signer_mailbox.claim_value(log).is_ok()); prog.load_memory_dump("./296c6962726/demo_custom.dump"); drop(cleanup); // Charge capacitor with charge = 10 - let response = prog.send_bytes(signer, b"10"); + prog.send_bytes(signer, b"10"); + let res = sys.run_next_block(); let log = Log::builder() .source(prog.id()) .dest(signer) .payload_bytes("Discharged: 20"); - assert!(response.contains(&log)); + assert!(res.contains(&log)); assert!(signer_mailbox.claim_value(log).is_ok()); } @@ -1166,22 +1160,24 @@ mod tests { // Init simple waiter prog.send(signer, InitMessage::SimpleWaiter); + sys.run_next_block(); // Invoke `exec::wait_for` when running for the first time - let result = prog.send_bytes(signer, b"doesn't matter"); + prog.send_bytes(signer, b"doesn't matter"); + let result = sys.run_next_block(); // No log entries as the program is waiting assert!(result.log().is_empty()); - // Spend 20 blocks and make the waiter to wake up - let results = sys.spend_blocks(20); + // Run task pool to make the waiter to wake up + let _ = sys.run_scheduled_tasks(20); + let res = sys.run_next_block(); let log = Log::builder() .source(prog.id()) .dest(signer) .payload_bytes("hello"); - - assert!(results.iter().any(|result| result.contains(&log))); + assert!(res.contains(&log)); } // Test for issue#3699 @@ -1196,15 +1192,18 @@ mod tests { // Init reserver prog.send(signer, InitMessage::Reserver); + sys.run_next_block(); for _ in 0..258 { // Reserve - let result = prog.send_bytes(signer, b"reserve"); - assert!(!result.main_failed()); + let msg_id = prog.send_bytes(signer, b"reserve"); + let result = sys.run_next_block(); + assert!(result.succeed.contains(&msg_id)); // Spend - let result = prog.send_bytes(signer, b"send from reservation"); - assert!(!result.main_failed()); + let msg_id = prog.send_bytes(signer, b"send from reservation"); + let result = sys.run_next_block(); + assert!(result.succeed.contains(&msg_id)); } } @@ -1218,11 +1217,13 @@ mod tests { let user_id = [42; 32]; let prog = Program::from_binary_with_id(&sys, 137, WASM_BINARY); - let run_result = prog.send(user_id, demo_exit_handle::scheme()); - assert!(!run_result.main_failed()); + let msg_id = prog.send(user_id, demo_exit_handle::scheme()); + let result = sys.run_next_block(); + assert!(result.succeed.contains(&msg_id)); - let run_result = prog.send_bytes(user_id, []); - assert!(!run_result.main_failed()); + let msg_id = prog.send_bytes(user_id, []); + let result = sys.run_next_block(); + assert!(result.succeed.contains(&msg_id)); } #[test] @@ -1235,7 +1236,8 @@ mod tests { let user_id = ActorId::zero(); // set insufficient gas for execution - let res = prog.send_with_gas(user_id, "init".to_string(), 1, 0); + let msg_id = prog.send_with_gas(user_id, "init".to_string(), 1, 0); + let res = sys.run_next_block(); let expected_log = Log::builder() @@ -1246,7 +1248,7 @@ mod tests { ))); assert!(res.contains(&expected_log)); - assert!(res.main_failed()); + assert!(res.failed.contains(&msg_id)); } #[test] @@ -1260,13 +1262,15 @@ mod tests { let prog = Program::from_binary_with_id(&sys, 4242, WASM_BINARY); // Initialize program - let res = prog.send(user_id, Scheme::empty()); - assert!(!res.main_failed()); + let msg_id = prog.send(user_id, Scheme::empty()); + let res = sys.run_next_block(); + assert!(res.succeed.contains(&msg_id)); // Reserve gas handle let handle = Calls::builder().reserve_gas(1_000_000, 10); - let res = prog.send(user_id, handle); - assert!(!res.main_failed()); + let msg_id = prog.send(user_id, handle); + let res = sys.run_next_block(); + assert!(res.succeed.contains(&msg_id)); // Get reservation id from program let reservation_id = sys @@ -1288,8 +1292,9 @@ mod tests { // Unreserve gas handle let handle = Calls::builder().unreserve_gas(reservation_id.into_bytes()); - let res = prog.send(user_id, handle); - assert!(!res.main_failed()); + let msg_id = prog.send(user_id, handle); + let res = sys.run_next_block(); + assert!(res.succeed.contains(&msg_id)); // Check reservation is removed from the tree assert!(!sys.0.borrow().gas_tree.exists(reservation_id)); @@ -1307,8 +1312,9 @@ mod tests { let prog = Program::from_binary_with_id(&sys, prog_id, WASM_BINARY); // Initialize program - let res = prog.send(user_id, Scheme::empty()); - assert!(!res.main_failed()); + let msg_id = prog.send(user_id, Scheme::empty()); + let res = sys.run_next_block(); + assert!(res.succeed.contains(&msg_id)); // Send user message from reservation let payload = b"to_user".to_vec(); @@ -1316,8 +1322,9 @@ mod tests { .reserve_gas(10_000_000_000, 5) .store("reservation") .reservation_send_value("reservation", user_id.into_origin().0, payload.clone(), 0); - let res = prog.send(user_id, handle); - assert!(!res.main_failed()); + let msg_id = prog.send(user_id, handle); + let res = sys.run_next_block(); + assert!(res.succeed.contains(&msg_id)); // Check user message in mailbox let mailbox = sys.get_mailbox(user_id); @@ -1334,16 +1341,18 @@ mod tests { Calls::builder().noop(), Calls::builder().noop(), ); - let res = new_program.send(user_id, scheme); - assert!(!res.main_failed()); + let msg_id = new_program.send(user_id, scheme); + let res = sys.run_next_block(); + assert!(res.succeed.contains(&msg_id)); // Send program message from reservation let handle = Calls::builder() .reserve_gas(10_000_000_000, 5) .store("reservation") .reservation_send_value("reservation", new_prog_id.into_origin().0, [], 0); - let res = prog.send(user_id, handle); - assert!(!res.main_failed()); + let msg_id = prog.send(user_id, handle); + let res = sys.run_next_block(); + assert!(res.succeed.contains(&msg_id)); assert!(mailbox.contains(&Log::builder().payload_bytes(payload).source(new_prog_id))); } } diff --git a/gtest/src/system.rs b/gtest/src/system.rs index 9b81f2c2a5d..a23b262f0f6 100644 --- a/gtest/src/system.rs +++ b/gtest/src/system.rs @@ -17,23 +17,23 @@ // along with this program. If not, see . use crate::{ - log::RunResult, + log::{BlockRunResult, CoreLog}, mailbox::ActorMailbox, manager::{Actors, Balance, ExtManager, MintMode}, program::{Program, ProgramIdWrapper}, + Gas, GAS_ALLOWANCE, }; use codec::{Decode, DecodeAll}; use colored::Colorize; use env_logger::{Builder, Env}; use gear_core::{ ids::{CodeId, ProgramId}, - message::Dispatch, pages::GearPage, }; use gear_lazy_pages::{LazyPagesStorage, LazyPagesVersion}; use gear_lazy_pages_common::LazyPagesInitContext; 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, mem, path::Path, thread}; thread_local! { /// `System` is a singleton with a one instance and no copies returned. @@ -174,13 +174,69 @@ impl System { .try_init(); } - /// Send raw message dispatch. - pub fn send_dispatch(&self, dispatch: Dispatch) -> RunResult { - self.0.borrow_mut().validate_and_run_dispatch(dispatch) + /// Run next block. + /// + /// Block execution model is the following: + /// - increase the block number, update the timestamp + /// - process tasks from the task pool + /// - process messages in the queue. + /// + /// The system is always initialized with a 0 block number. Current block + /// number in the system is the number of the already executed block, + /// therefore block execution starts with a block info update (block + /// number increase, timestamp update). For example, if current block + /// number is 2, it means that messages and tasks on 2 were executed, so + /// the method goes to block number 3 and executes tasks and messages for + /// the updated block number. + /// + /// Task processing basically tries to execute the scheduled to the specific + /// block tasks: + /// - delayed sending + /// - waking message + /// - removing from the mailbox + /// - removing reservations + /// - removing stalled wait message. + /// + /// Messages processing executes messages until either queue becomes empty + /// or block gas allowance is fully consumed. + pub fn run_next_block(&self) -> BlockRunResult { + self.run_next_block_with_allowance(Gas(GAS_ALLOWANCE)) + } + + /// Runs blocks same as [`Self::run_next_block`], but with limited + /// allowance. + pub fn run_next_block_with_allowance(&self, allowance: Gas) -> BlockRunResult { + if allowance > Gas(GAS_ALLOWANCE) { + panic!("Provided allowance more than allowed limit of {GAS_ALLOWANCE}."); + } + + self.0.borrow_mut().run_new_block(allowance) + } + + /// Runs blocks same as [`Self::run_next_block`], but executes blocks to + /// block number `bn` including it. + pub fn run_to_block(&self, bn: u32) -> Vec { + let mut manager = self.0.borrow_mut(); + + let mut current_block = manager.blocks_manager.get().height; + if current_block > bn { + panic!("Can't run blocks until bn {bn}, as current bn is {current_block}"); + } + + let mut ret = Vec::with_capacity((bn - current_block) as usize); + while current_block != bn { + let res = manager.run_new_block(Gas(GAS_ALLOWANCE)); + ret.push(res); + + current_block = manager.blocks_manager.get().height; + } + + ret } - /// Spend blocks and return all results. - pub fn spend_blocks(&self, amount: u32) -> Vec { + /// Runs `amount` of blocks only with processing task pool, without + /// processing the message queue. + pub fn run_scheduled_tasks(&self, amount: u32) -> Vec { let mut manager = self.0.borrow_mut(); let block_height = manager.blocks_manager.get().height; @@ -190,12 +246,20 @@ impl System { let block_info = manager.blocks_manager.next_block(); let next_block_number = block_info.height; - let mut results = manager.process_delayed_dispatches(next_block_number); - results.extend(manager.process_scheduled_wait_list(next_block_number)); - results + manager.process_tasks(next_block_number); + + let log = mem::take(&mut manager.log) + .into_iter() + .map(CoreLog::from) + .collect(); + BlockRunResult { + block_info, + gas_allowance_spent: Gas(GAS_ALLOWANCE) - manager.gas_allowance, + log, + ..Default::default() + } }) - .collect::>>() - .concat() + .collect() } /// Return the current block height of the testing environment. @@ -350,17 +414,61 @@ mod tests { #[test] fn test_multithread_copy_singleton() { let first_instance = System::new(); - first_instance.spend_blocks(5); + first_instance.run_scheduled_tasks(5); assert_eq!(first_instance.block_height(), 5); let h = std::thread::spawn(|| { let second_instance = System::new(); - second_instance.spend_blocks(10); + second_instance.run_scheduled_tasks(10); assert_eq!(second_instance.block_height(), 10); }); h.join().expect("internal error failed joining thread"); } + + #[test] + fn test_bn_adjustments() { + let sys = System::new(); + assert_eq!(sys.block_height(), 0); + + // ### Check block info after run to next block ### + let res = sys.run_next_block(); + let block_info = res.block_info; + assert_eq!(block_info.height, sys.block_height()); + assert_eq!(block_info.height, 1); + + // ### Check block info after run to block ### + let current_height = block_info.height; + let until_height = 5; + let results = sys.run_to_block(until_height); + assert_eq!(results.len(), (until_height - current_height) as usize); + + // Check first block executed is always the next block + let first_run = results.first().expect("checked above"); + assert_eq!(first_run.block_info.height, current_height + 1); + + // Check the last block executed number + let last_run = results.last().expect("checked above"); + assert_eq!(last_run.block_info.height, until_height); + assert_eq!(last_run.block_info.height, sys.block_height()); + + // ### Check block info after running the task pool ### + let current_height = last_run.block_info.height; + let amount_of_blocks = 10; + let results = sys.run_scheduled_tasks(amount_of_blocks); + assert_eq!(results.len(), amount_of_blocks as usize); + + let first_run = results.first().expect("checked above"); + assert_eq!(first_run.block_info.height, current_height + 1); + + let last_run = results.last().expect("checked above"); + assert_eq!( + last_run.block_info.height, + current_height + amount_of_blocks + ); + + assert_eq!(last_run.block_info.height, 15); + } } diff --git a/utils/cargo-gbuild/test-program/src/lib.rs b/utils/cargo-gbuild/test-program/src/lib.rs index 659dc8eff36..ac5e4e501c3 100644 --- a/utils/cargo-gbuild/test-program/src/lib.rs +++ b/utils/cargo-gbuild/test-program/src/lib.rs @@ -55,13 +55,15 @@ mod tests { let program = Program::current(&system); // Init program - let res = program.send_bytes(user, b"PING"); - assert!(!res.main_failed()); + let msg_id = program.send_bytes(user, b"PING"); + let res = system.run_next_block(); + assert!(res.succeed.contains(&msg_id)); assert!(res.contains(&(user, b"INIT_PONG"))); // Handle program - let res = program.send_bytes(user, b"PING"); - assert!(!res.main_failed()); + let msg_id = program.send_bytes(user, b"PING"); + let res = system.run_next_block(); + assert!(res.succeed.contains(&msg_id)); assert!(res.contains(&(user, b"HANDLE_PONG"))); } } diff --git a/utils/cargo-gbuild/tests/smoke.rs b/utils/cargo-gbuild/tests/smoke.rs index 679cb89ea11..988c63a3901 100644 --- a/utils/cargo-gbuild/tests/smoke.rs +++ b/utils/cargo-gbuild/tests/smoke.rs @@ -27,13 +27,15 @@ fn ping(sys: &System, prog: PathBuf) -> Program<'_> { let program = Program::from_file(sys, prog); // Init program - let res = program.send_bytes(user, b"PING"); - assert!(!res.main_failed()); + let msg_id = program.send_bytes(user, b"PING"); + let res = sys.run_next_block(); + assert!(res.succeed.contains(&msg_id)); assert!(res.contains(&(user, b"INIT_PONG"))); // Handle program - let res = program.send_bytes(user, b"PING"); - assert!(!res.main_failed()); + let msg_id = program.send_bytes(user, b"PING"); + let res = sys.run_next_block(); + assert!(res.succeed.contains(&msg_id)); assert!(res.contains(&(user, b"HANDLE_PONG"))); program diff --git a/utils/wasm-builder/test-program/src/lib.rs b/utils/wasm-builder/test-program/src/lib.rs index 844cb9d143b..b5d2b50cec5 100644 --- a/utils/wasm-builder/test-program/src/lib.rs +++ b/utils/wasm-builder/test-program/src/lib.rs @@ -36,10 +36,12 @@ mod gtest_tests { let this_program = Program::current(&system); - let res = this_program.send_bytes(123, "INIT"); + this_program.send_bytes(123, "INIT"); + let res = system.run_next_block(); assert!(res.contains(&Log::builder().source(1).dest(123).payload_bytes([]))); - let res = this_program.send_bytes(123, "Hi"); + this_program.send_bytes(123, "Hi"); + let res = system.run_next_block(); assert!(res.contains( &Log::builder() .source(1)