diff --git a/Cargo.toml b/Cargo.toml index 7d24e078..c6415b52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,6 +82,5 @@ ic-cdk = "0.17" ic-cdk-macros = "0.17" ic-cdk-timers = "0.11" ic-ledger-types = "0.14" -ic-test-state-machine-client = "3" icrc-ledger-types = "0.1.0" pocket-ic = "6" diff --git a/ic-canister-client/Cargo.toml b/ic-canister-client/Cargo.toml index 1d67b35d..c34adac8 100644 --- a/ic-canister-client/Cargo.toml +++ b/ic-canister-client/Cargo.toml @@ -10,7 +10,6 @@ description = "Client for interacting with an IC Canister" default = [] ic-agent-client = ["dep:ic-agent"] pocket-ic-client = ["dep:tokio", "ic-exports/pocket-ic-tests"] -state-machine-tests-client = ["dep:tokio", "ic-exports/ic-test-state-machine"] [dependencies] async-trait = { workspace = true } diff --git a/ic-canister-client/src/error.rs b/ic-canister-client/src/error.rs index d8ed724c..1b44535a 100644 --- a/ic-canister-client/src/error.rs +++ b/ic-canister-client/src/error.rs @@ -13,10 +13,6 @@ pub enum CanisterClientError { #[error("ic agent error: {0}")] IcAgentError(#[from] ic_agent::agent::AgentError), - #[cfg(feature = "state-machine-tests-client")] - #[error("state machine test error: {0}")] - StateMachineTestError(ic_exports::ic_test_state_machine::UserError), - #[cfg(feature = "pocket-ic-client")] #[error("pocket-ic test error: {0:?}")] PocketIcTestError(ic_exports::pocket_ic::CallError), @@ -36,13 +32,6 @@ impl From for CanisterClientError { } } -#[cfg(feature = "state-machine-tests-client")] -impl From for CanisterClientError { - fn from(error: ic_exports::ic_test_state_machine::UserError) -> Self { - CanisterClientError::StateMachineTestError(error) - } -} - pub type CanisterClientResult = Result; /// This tuple is returned incase of IC errors such as Network, canister error. diff --git a/ic-canister-client/src/lib.rs b/ic-canister-client/src/lib.rs index a1bdce29..65c6a4ac 100644 --- a/ic-canister-client/src/lib.rs +++ b/ic-canister-client/src/lib.rs @@ -5,9 +5,6 @@ pub mod client; pub mod error; pub mod ic_client; -#[cfg(feature = "state-machine-tests-client")] -pub mod state_machine_tests; - #[cfg(feature = "pocket-ic-client")] pub mod pocket_ic; @@ -20,5 +17,3 @@ pub use ic_agent; pub use ic_client::IcCanisterClient; #[cfg(feature = "pocket-ic-client")] pub use pocket_ic::PocketIcClient; -#[cfg(feature = "state-machine-tests-client")] -pub use state_machine_tests::StateMachineCanisterClient; diff --git a/ic-canister-client/src/pocket_ic.rs b/ic-canister-client/src/pocket_ic.rs index e2eb94e7..1cf1df7f 100644 --- a/ic-canister-client/src/pocket_ic.rs +++ b/ic-canister-client/src/pocket_ic.rs @@ -16,30 +16,6 @@ pub struct PocketIcClient { pub caller: Principal, } -impl Drop for PocketIcClient { - fn drop(&mut self) { - if let Some(client) = self.client.take() { - if let Ok(client) = Arc::try_unwrap(client) { - // Spawns a tokio task to drop the client. - // This workaround is necessary because Rust does not support async drop. - // - // This has some main drawbacks: - // - // 1. It panics if not executed in a tokio runtime. - // 2. As the spawn is executed in background, there's no guarantee that it will actually run. - // - // Not dropping the client will cause a memory leak in the PocketIc Server, - // however, this is not a big deal since the server will automatically clean - // the resources after 60 seconds of inactivity. - // - tokio::spawn(async move { - client.drop().await; - }); - } - } - } -} - impl PocketIcClient { /// Creates a new instance of a PocketIcClient. /// The new instance is independent and have no access to canisters of other instances. diff --git a/ic-canister-client/src/state_machine_tests.rs b/ic-canister-client/src/state_machine_tests.rs deleted file mode 100644 index 29cce92d..00000000 --- a/ic-canister-client/src/state_machine_tests.rs +++ /dev/null @@ -1,157 +0,0 @@ -use std::sync::Arc; - -use candid::utils::ArgumentEncoder; -use candid::{CandidType, Decode, Principal}; -use ic_exports::ic_kit::RejectionCode; -use ic_exports::ic_test_state_machine::{StateMachine, WasmResult}; -use serde::Deserialize; -use tokio::sync::Mutex; - -use crate::{CanisterClient, CanisterClientError, CanisterClientResult}; - -/// A client for interacting with a canister inside dfinity's -/// state machine tests framework. -#[derive(Clone)] -pub struct StateMachineCanisterClient { - state_machine: Arc>, - canister: Principal, - caller: Principal, -} - -impl StateMachineCanisterClient { - /// Creates a new instance of a StateMachineCanisterClient. - pub fn new( - state_machine: Arc>, - canister: Principal, - caller: Principal, - ) -> Self { - Self { - state_machine, - canister, - caller, - } - } - - /// Returns the caller of the canister. - pub fn caller(&self) -> Principal { - self.caller - } - - /// Replace the caller. - pub fn set_caller(&mut self, caller: Principal) { - self.caller = caller; - } - - /// Returns the canister of the canister. - pub fn canister(&self) -> Principal { - self.canister - } - - /// Replace the canister to call. - pub fn set_canister(&mut self, canister: Principal) { - self.canister = canister; - } - - /// Returns the state machine of the canister. - pub fn state_machine(&self) -> &Mutex { - self.state_machine.as_ref() - } - - /// Performs a blocking action with state machine and awaits the result. - /// - /// Arguments of the closure `f`: - /// 1) `env` - The state machine environment. - /// 2) `canister` - The canister principal. - /// 3) `caller` - The caller principal. - pub async fn with_state_machine(&self, f: F) -> R - where - F: Send + FnOnce(&StateMachine, Principal, Principal) -> R + 'static, - R: Send + 'static, - { - let client = self.state_machine.clone(); - let cansiter = self.canister; - let caller = self.caller; - - tokio::task::spawn_blocking(move || { - let locked_client = client.blocking_lock(); - f(&locked_client, cansiter, caller) - }) - .await - .unwrap() - } - - pub async fn update(&self, method: &str, args: T) -> CanisterClientResult - where - T: ArgumentEncoder + Send + Sync, - R: for<'de> Deserialize<'de> + CandidType, - { - let args = candid::encode_args(args)?; - let method = String::from(method); - - let call_result = self - .with_state_machine(move |env, canister, caller| { - env.update_call(canister, caller, &method, args) - }) - .await?; - - let reply = match call_result { - WasmResult::Reply(reply) => reply, - WasmResult::Reject(e) => { - return Err(CanisterClientError::CanisterError(( - RejectionCode::CanisterError, - e, - ))); - } - }; - - let decoded = Decode!(&reply, R)?; - Ok(decoded) - } - - pub async fn query(&self, method: &str, args: T) -> CanisterClientResult - where - T: ArgumentEncoder + Send + Sync, - R: for<'de> Deserialize<'de> + CandidType, - { - let args = candid::encode_args(args)?; - let method = String::from(method); - - let call_result = self - .with_state_machine(move |env, canister, caller| { - env.query_call(canister, caller, &method, args) - }) - .await?; - - let reply = match call_result { - WasmResult::Reply(reply) => reply, - WasmResult::Reject(e) => { - return Err(CanisterClientError::CanisterError(( - RejectionCode::CanisterError, - e, - ))); - } - }; - - let decoded = Decode!(&reply, R)?; - Ok(decoded) - } -} - -#[async_trait::async_trait] -impl CanisterClient for StateMachineCanisterClient { - async fn update(&self, method: &str, args: T) -> CanisterClientResult - where - T: ArgumentEncoder + Send + Sync, - R: for<'de> Deserialize<'de> + CandidType, - { - StateMachineCanisterClient::update(self, method, args).await - } - - async fn query(&self, method: &str, args: T) -> CanisterClientResult - where - T: ArgumentEncoder + Send + Sync, - R: for<'de> Deserialize<'de> + CandidType, - { - StateMachineCanisterClient::query(self, method, args).await - } -} diff --git a/ic-exports/Cargo.toml b/ic-exports/Cargo.toml index c51328b0..c3b1d915 100644 --- a/ic-exports/Cargo.toml +++ b/ic-exports/Cargo.toml @@ -10,7 +10,6 @@ edition.workspace = true default = [] ledger = ["ic-ledger-types"] icrc = ["icrc-ledger-types"] -ic-test-state-machine = ["flate2", "ic-test-state-machine-client", "log", "reqwest", "tokio"] pocket-ic-tests = ["flate2", "pocket-ic", "log", "reqwest", "tokio"] [dependencies] @@ -22,7 +21,6 @@ ic-cdk-timers = { workspace = true } ic-crypto-getrandom-for-wasm = { path = "../ic-crypto-getrandom-for-wasm" } ic-kit = { path = "../ic-kit" } ic-ledger-types = { workspace = true, optional = true } -ic-test-state-machine-client = { workspace = true, optional = true } icrc-ledger-types = { workspace = true, optional = true } pocket-ic = { workspace = true, optional = true } serde = { workspace = true } diff --git a/ic-exports/src/ic_test_state_machine.rs b/ic-exports/src/ic_test_state_machine.rs deleted file mode 100644 index 581e8a9e..00000000 --- a/ic-exports/src/ic_test_state_machine.rs +++ /dev/null @@ -1,111 +0,0 @@ -use std::fs::{create_dir_all, File}; -use std::io::*; -use std::path::Path; -use std::time::Duration; - -use flate2::read::GzDecoder; -pub use ic_test_state_machine_client::*; -use log::*; -use tokio::sync::OnceCell; - -pub const IC_STATE_MACHINE_BINARY_HASH: &str = "48da85ee6c03e8c15f3e90b21bf9ccae7b753ee6"; - -/// Returns the path to the ic-test-state-machine binary. -/// If the binary is not present, it downloads it. -/// See: https://github.com/dfinity/test-state-machine-client -/// -/// It supports only linux and macos -/// -/// The search_path variable is the folder where to search for the binary -/// or to download it if not present -pub async fn get_ic_test_state_machine_client_path(search_path: &str) -> &str { - static FILES: OnceCell = OnceCell::const_new(); - FILES - .get_or_init(|| async { download_binary(search_path).await }) - .await -} - -async fn download_binary(base_path: &str) -> String { - let platform = match std::env::consts::OS { - "linux" => "linux", - "macos" => "darwin", - _ => panic!("ic_test_state_machine_client requires linux or macos"), - }; - - let output_file_name = "ic-test-state-machine"; - let gz_file_name = format!("{output_file_name}.gz"); - let download_url = format!("https://download.dfinity.systems/ic/{IC_STATE_MACHINE_BINARY_HASH}/binaries/x86_64-{platform}/{gz_file_name}"); - - let dest_path_name = format!("{}/{}", base_path, "ic_test_state_machine"); - let dest_dir_path = Path::new(&dest_path_name); - let gz_dest_file_path = format!("{}/{}", dest_path_name, gz_file_name); - let output_dest_file_path = format!("{}/{}", dest_path_name, output_file_name); - - if !Path::new(&output_dest_file_path).exists() { - // Download file - { - info!( - "ic-test-state-machine binarey not found, downloading binary from: {download_url}" - ); - - let response = reqwest::Client::builder() - .timeout(Duration::from_secs(120)) - .build() - .unwrap() - .get(download_url) - .send() - .await - .unwrap(); - - create_dir_all(dest_dir_path).unwrap(); - - let mut file = match File::create(&gz_dest_file_path) { - Err(why) => panic!("couldn't create {}", why), - Ok(file) => file, - }; - let content = response.bytes().await.unwrap(); - info!("ic-test-state-machine.gz file length: {}", content.len()); - file.write_all(&content).unwrap(); - file.flush().unwrap(); - } - - // unzip file - { - info!( - "unzip ic-test-state-machine to [{}]", - dest_dir_path.to_str().unwrap() - ); - let tar_gz = File::open(gz_dest_file_path).unwrap(); - let mut tar = GzDecoder::new(tar_gz); - let mut temp = vec![]; - tar.read_to_end(&mut temp).unwrap(); - - let mut output = File::create(&output_dest_file_path).unwrap(); - output.write_all(&temp).unwrap(); - output.flush().unwrap(); - - #[cfg(target_family = "unix")] - { - use std::os::unix::prelude::PermissionsExt; - let mut perms = std::fs::metadata(&output_dest_file_path) - .unwrap() - .permissions(); - perms.set_mode(0o770); - std::fs::set_permissions(&output_dest_file_path, perms).unwrap(); - } - } - } - output_dest_file_path -} - -#[cfg(test)] -mod test { - - use super::*; - - #[tokio::test] - async fn should_get_ic_test_state_machine_client_path() { - let path = get_ic_test_state_machine_client_path("../target").await; - assert!(Path::new(&path).exists()) - } -} diff --git a/ic-exports/src/lib.rs b/ic-exports/src/lib.rs index 2b20aa02..413bd333 100644 --- a/ic-exports/src/lib.rs +++ b/ic-exports/src/lib.rs @@ -21,6 +21,3 @@ pub mod icrc_types { #[cfg(feature = "pocket-ic-tests")] pub mod pocket_ic; - -#[cfg(feature = "ic-test-state-machine")] -pub mod ic_test_state_machine; diff --git a/ic-exports/src/pocket_ic.rs b/ic-exports/src/pocket_ic.rs index 9d839207..a56937ad 100644 --- a/ic-exports/src/pocket_ic.rs +++ b/ic-exports/src/pocket_ic.rs @@ -6,7 +6,7 @@ use std::{env, fs}; use flate2::read::GzDecoder; use log::*; pub use pocket_ic::nonblocking::*; -use pocket_ic::PocketIcBuilder; +pub use pocket_ic::PocketIcBuilder; pub use pocket_ic::{common, CallError, ErrorCode, UserError, WasmResult}; use tokio::sync::OnceCell; diff --git a/ic-task-scheduler/tests/pocket_ic_tests/mod.rs b/ic-task-scheduler/tests/pocket_ic_tests/mod.rs index d8af1d0d..b936b2ec 100644 --- a/ic-task-scheduler/tests/pocket_ic_tests/mod.rs +++ b/ic-task-scheduler/tests/pocket_ic_tests/mod.rs @@ -69,7 +69,7 @@ impl PocketIcTestContext { } } -async fn deploy_dummy_scheduler_canister() -> anyhow::Result { +async fn deploy_dummy_scheduler_canister<'a>() -> anyhow::Result { let client = init_pocket_ic().await.build_async().await; println!("Creating dummy canister");