diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8e68b230953..a5cdf511a8a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -213,7 +213,6 @@ go_deps.bzl @dfinity/idx /rs/rust_canisters/xnet_test/ @dfinity/ic-message-routing-owners /rs/rust_canisters/downstream_calls_test/ @dfinity/ic-message-routing-owners /rs/rust_canisters/random_traffic_test/ @dfinity/ic-message-routing-owners -/rs/scenario_tests/ @dfinity/idx /rs/sns/ @dfinity/nns-team /rs/starter/ @dfinity/networking /rs/state_layout/ @dfinity/ic-message-routing-owners diff --git a/Cargo.lock b/Cargo.lock index 1ba27c567cf..d853e39e30a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -493,7 +493,7 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" dependencies = [ - "term 0.7.0", + "term", ] [[package]] @@ -3502,16 +3502,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "dirs" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" -dependencies = [ - "cfg-if 0.1.10", - "dirs-sys", -] - [[package]] name = "dirs-next" version = "2.0.0" @@ -3522,17 +3512,6 @@ dependencies = [ "dirs-sys-next", ] -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi 0.3.9", -] - [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -11671,37 +11650,6 @@ dependencies = [ "url", ] -[[package]] -name = "ic-scenario-tests" -version = "0.9.0" -dependencies = [ - "candid", - "canister-test", - "chrono", - "colored", - "dfn_candid", - "dfn_json", - "futures", - "ic-base-types", - "ic-canister-client", - "ic-crypto-ed25519", - "ic-crypto-utils-threshold-sig-der", - "ic-metrics", - "ic-protobuf", - "ic-registry-client", - "ic-registry-client-helpers", - "ic-registry-nns-data-provider-wrappers", - "ic-registry-routing-table", - "ic-test-identity", - "ic-types", - "serde", - "slog", - "tester", - "tokio", - "url", - "xnet-test", -] - [[package]] name = "ic-sender-canister" version = "0.9.0" @@ -14790,7 +14738,7 @@ dependencies = [ "regex", "regex-syntax 0.8.5", "string_cache", - "term 0.7.0", + "term", "tiny-keccak", "unicode-xid", "walkdir", @@ -19797,7 +19745,7 @@ checksum = "b6e022d0b998abfe5c3782c1f03551a596269450ccd677ea51c56f8b214610e8" dependencies = [ "is-terminal", "slog", - "term 0.7.0", + "term", "thread_local", "time", ] @@ -20434,16 +20382,6 @@ dependencies = [ "utf-8", ] -[[package]] -name = "term" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0863a3345e70f61d613eab32ee046ccd1bcc5f9105fe402c61fcd0c13eeb8b5" -dependencies = [ - "dirs", - "winapi 0.3.9", -] - [[package]] name = "term" version = "0.7.0" @@ -20513,17 +20451,6 @@ dependencies = [ "syn 2.0.87", ] -[[package]] -name = "tester" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee72ec31009a42b53de9a6b7d8f462b493ab3b1e4767bda1fcdbb52127f13b6c" -dependencies = [ - "getopts", - "libc", - "term 0.6.1", -] - [[package]] name = "testing-verification-tests" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index 987c2b9f6dc..9bd04ae4d02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -329,7 +329,6 @@ members = [ "rs/rust_canisters/statesync_test", "rs/rust_canisters/tests", "rs/rust_canisters/xnet_test", - "rs/scenario_tests", "rs/sns/audit", "rs/sns/cli", "rs/sns/governance", diff --git a/publish/binaries/BUILD.bazel b/publish/binaries/BUILD.bazel index 9769c406d53..0368b24c2e3 100644 --- a/publish/binaries/BUILD.bazel +++ b/publish/binaries/BUILD.bazel @@ -39,8 +39,6 @@ ALL_BINARIES = { "types-test": "//rs/types/types:types_test", "replicated-state-test": "//rs/replicated_state:replicated_state_test_binary", # needed by DRE for release qualification - "e2e-test-driver": "//rs/scenario_tests:e2e-test-driver", - # needed by DRE for release qualification "ic-workload-generator": "//rs/workload_generator:ic-workload-generator", } diff --git a/rs/canister_client/BUILD.bazel b/rs/canister_client/BUILD.bazel index 83b563dd2cb..7f200a2c24a 100644 --- a/rs/canister_client/BUILD.bazel +++ b/rs/canister_client/BUILD.bazel @@ -11,7 +11,6 @@ package(default_visibility = [ "//rs/replay:__pkg__", "//rs/rosetta-api:__subpackages__", "//rs/rust_canisters/canister_test:__pkg__", - "//rs/scenario_tests:__pkg__", "//rs/tests:__subpackages__", "//rs/workload_generator:__pkg__", ]) diff --git a/rs/crypto/internal/crypto_lib/types/BUILD.bazel b/rs/crypto/internal/crypto_lib/types/BUILD.bazel index b5f2378e6d4..fea572a7f15 100644 --- a/rs/crypto/internal/crypto_lib/types/BUILD.bazel +++ b/rs/crypto/internal/crypto_lib/types/BUILD.bazel @@ -16,7 +16,6 @@ rust_library( "//rs/crypto:__subpackages__", "//rs/registry/admin:__pkg__", "//rs/replay:__pkg__", - "//rs/scenario_tests:__pkg__", "//rs/test_utilities:__pkg__", "//rs/types/types:__pkg__", "//rs/validator/http_request_test_utils:__subpackages__", diff --git a/rs/registry/nns_data_provider_wrappers/BUILD.bazel b/rs/registry/nns_data_provider_wrappers/BUILD.bazel index ee301cbd256..4bb132c13ec 100644 --- a/rs/registry/nns_data_provider_wrappers/BUILD.bazel +++ b/rs/registry/nns_data_provider_wrappers/BUILD.bazel @@ -6,7 +6,6 @@ load("@rules_rust//rust:defs.bzl", "rust_library") package(default_visibility = [ "//rs/registry/admin:__pkg__", "//rs/registry/regedit:__pkg__", - "//rs/scenario_tests:__pkg__", ]) DEPENDENCIES = [ diff --git a/rs/rust_canisters/dfn_candid/BUILD.bazel b/rs/rust_canisters/dfn_candid/BUILD.bazel index 75092abaeea..a9ad5f0da02 100644 --- a/rs/rust_canisters/dfn_candid/BUILD.bazel +++ b/rs/rust_canisters/dfn_candid/BUILD.bazel @@ -12,7 +12,6 @@ package(default_visibility = [ "//rs/registry/canister:__pkg__", "//rs/rosetta-api/icp:__pkg__", "//rs/rust_canisters:__subpackages__", - "//rs/scenario_tests:__pkg__", "//rs/sns:__subpackages__", "//rs/tests:__subpackages__", ]) diff --git a/rs/rust_canisters/dfn_json/BUILD.bazel b/rs/rust_canisters/dfn_json/BUILD.bazel index 45716801214..a43e457225c 100644 --- a/rs/rust_canisters/dfn_json/BUILD.bazel +++ b/rs/rust_canisters/dfn_json/BUILD.bazel @@ -5,7 +5,6 @@ package(default_visibility = [ "//rs/nns/integration_tests:__pkg__", "//rs/nns/test_utils:__pkg__", "//rs/rust_canisters:__subpackages__", - "//rs/scenario_tests:__pkg__", "//rs/tests:__subpackages__", ]) diff --git a/rs/scenario_tests/BUILD.bazel b/rs/scenario_tests/BUILD.bazel deleted file mode 100644 index dd6e4d2b419..00000000000 --- a/rs/scenario_tests/BUILD.bazel +++ /dev/null @@ -1,69 +0,0 @@ -load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_test") - -package(default_visibility = ["//visibility:public"]) - -DEPENDENCIES = [ - # Keep sorted. - "//rs/canister_client", - "//rs/crypto/ed25519", - "//rs/crypto/utils/threshold_sig_der", - "//rs/monitoring/metrics", - "//rs/protobuf", - "//rs/registry/client", - "//rs/registry/helpers", - "//rs/registry/nns_data_provider_wrappers", - "//rs/registry/routing_table", - "//rs/rust_canisters/canister_test", - "//rs/rust_canisters/dfn_candid", - "//rs/rust_canisters/dfn_json", - "//rs/rust_canisters/xnet_test", - "//rs/test_utilities/identity", - "//rs/types/base_types", - "//rs/types/types", - "@crate_index//:candid", - "@crate_index//:chrono", - "@crate_index//:colored", - "@crate_index//:futures", - "@crate_index//:prost", - "@crate_index//:serde", - "@crate_index//:slog", - "@crate_index//:tester", - "@crate_index//:tokio", - "@crate_index//:url", -] - -MACRO_DEPENDENCIES = [] - -DEV_DEPENDENCIES = [] - -MACRO_DEV_DEPENDENCIES = [] - -ALIASES = {} - -rust_library( - name = "scenario_tests", - testonly = True, - srcs = glob(["src/**"]), - aliases = ALIASES, - crate_name = "ic_scenario_tests", - proc_macro_deps = MACRO_DEPENDENCIES, - version = "0.9.0", - deps = DEPENDENCIES, -) - -rust_binary( - name = "e2e-test-driver", - testonly = True, - srcs = ["src/main.rs"], - aliases = ALIASES, - proc_macro_deps = MACRO_DEPENDENCIES, - deps = DEPENDENCIES + [":scenario_tests"], -) - -rust_test( - name = "scenario_tests_test", - aliases = ALIASES, - crate = ":scenario_tests", - proc_macro_deps = MACRO_DEPENDENCIES + MACRO_DEV_DEPENDENCIES, - deps = DEPENDENCIES + DEV_DEPENDENCIES, -) diff --git a/rs/scenario_tests/Cargo.toml b/rs/scenario_tests/Cargo.toml deleted file mode 100644 index 14c1971f300..00000000000 --- a/rs/scenario_tests/Cargo.toml +++ /dev/null @@ -1,39 +0,0 @@ -[package] -name = "ic-scenario-tests" -autotests = false -version.workspace = true -authors.workspace = true -edition.workspace = true -description.workspace = true -documentation.workspace = true - -[dependencies] -candid = { workspace = true } -canister-test = { path = "../rust_canisters/canister_test" } -chrono = { workspace = true } -colored = "2.0.0" -dfn_candid = { path = "../rust_canisters/dfn_candid" } -dfn_json = { path = "../rust_canisters/dfn_json" } -futures = { workspace = true } -ic-base-types = { path = "../types/base_types" } -ic-canister-client = { path = "../canister_client" } -ic-crypto-ed25519 = { path = "../crypto/ed25519" } -ic-crypto-utils-threshold-sig-der = { path = "../crypto/utils/threshold_sig_der" } -ic-metrics = { path = "../monitoring/metrics" } -ic-protobuf = { path = "../protobuf" } -ic-registry-client = { path = "../registry/client" } -ic-registry-client-helpers = { path = "../registry/helpers" } -ic-registry-nns-data-provider-wrappers = { path = "../registry/nns_data_provider_wrappers" } -ic-registry-routing-table = { path = "../registry/routing_table" } -ic-test-identity = { path = "../test_utilities/identity" } -ic-types = { path = "../types/types" } -serde = { workspace = true } -slog = { workspace = true } -tester = "0.7.0" -tokio = { workspace = true } -url = { workspace = true } -xnet-test = { path = "../rust_canisters/xnet_test" } - -[[bin]] -name = "e2e-test-driver" -path = "src/main.rs" diff --git a/rs/scenario_tests/DEPRECATION_WARNING.adoc b/rs/scenario_tests/DEPRECATION_WARNING.adoc deleted file mode 100644 index f9487d0392a..00000000000 --- a/rs/scenario_tests/DEPRECATION_WARNING.adoc +++ /dev/null @@ -1,2 +0,0 @@ -**This crate is considered deprecated. If you intend to write System Tests, talk to the -testing team.** \ No newline at end of file diff --git a/rs/scenario_tests/README.adoc b/rs/scenario_tests/README.adoc deleted file mode 100644 index 8e7a9f897de..00000000000 --- a/rs/scenario_tests/README.adoc +++ /dev/null @@ -1,241 +0,0 @@ -**This crate is considered deprecated. If you intend to write System Tests, talk to the -testing team.** - -= Scenario Tests -This directory contains everything you need to write and run scenario tests for the IC. -This crate is host to both, the scenario tests and a Rust-API to write those tests. - -== System Tests - -=== Where do I put my system tests? - -Tests scenarios are in `tests/testcase/`. Say we want to create a new test `my_specific_scenario`. The first step -is writing the necessary code in `tests/testcase/my_specific_scenario.rs`, include `pup mod my_specific_scenario;` in -`tests/testcase.rs` and add a corresponding raclette `test_entry` in `tests/main.rs`. - -=== How do I run system tests? -First, run a `nix-shell` in `ic/rs` and `cd` to this directory. -run `cargo test --tests` here to compile and run all the test scenarios listed in `tests/testcase/`. -Please, adjust the table at the bottom of this file accordingly if you want to change the expectation from -failing to passing for your test. - -=== Dependencies -The tests start a `replica` process for each node. Thus, when running the tests (`cargo test testcase_`), a `replica` and a `orchestrator` binary needs to be on the `$PATH`. - -It is recommended to use a *release*-build of the `replica`. You can build a release-build using the following command in the `rs/` directory: - -``` -$ cargo build --bin replica --release -``` - -The built replica is placed under `ic/rs/target/x86_64-/release`, where `` refers to the target platform you are running on (e.g. `apple-darwin`). Either add this directory to your `$PATH` variable, or copy the replica binary to a directory already contained in `$PATH`, before running the tests. - -==== Known Issues and caveats - -* You may also use a debug-build of the `replica` for tests, of course. In general, this is not recommended, as, typically, multiple replicas are started per test and there is a significant performance difference between debug- and release-builds of the replica. - -* The `IcHandle::ready` function needs work. Currently, it starts up the different replicas and attempts to query the `api/v2/status` endpoint of each -replica until either they all respond or some specified timeout is reached. Still, sometimes we see tests that were supposed to pass are failing with a `ConnectionRefused` error. -We are working to fix this. - - -=== How do I write system tests? - -Look at examples. Below is a rudimentary sketch. - -System Tests are multi-node tests that run on a single CPU/operating system. The API is subject to change (!!!), so look at the examples in `tests/testcase/` for the most recent API version. The following should give a rough idea how System Tests look like regardless of how the API might evolve: - -[source,rust] ----- -async fn test() { - // Setup the IC - let ic = InternetComputer::new() - .with_subnet(NodeCount::from(4)) - .with_subnet(NodeCount::from(28)) - .start() - .await - .ready() - .await - .expect("Not ready yet"); - - // calling start() starts an orchestrator in the background that - // runs all the required nodes as processes. The orchestrator - // is torn down when `ic` is dropped. - - // `ic` is of type `ICInstance` which provides an API to access - // the network toplogy. E.g., to get a handle on the public API - // of the first node in the first subnet, you can call - let api = ic.subnet_by_idx(0).node_by_idx(0).api(); - - // `api` is a Runtime-instance as known from the `canister_test`- - // framework. Thus, we can build and install rust canister as - // follows ... - - // assuming we have a canister binary called 'json' - let proj = Project::new(std::env::var("CARGO_MANIFEST_DIR").unwrap()); - - // the canister object `json` provides the method familiar from the - // canister test framework - let json = proj.cargo_bin("json", &[]).install_(&r, Vec::new()).await?; - - // if you want to pull in canister binaries from other crates, you - // might need pass a different path in place of - // `std::env::var("CARGO_MANIFEST_DIR")` - - // install with api from subnet(0) node(0) - json.install(api); - - // We might provide a function that gives you an api of a random - // node - let api1 = ic.api(); - - assert_eq!(ic.management_url().to_string(), "http://localhost:8080/"); -} ----- - -A lot of the API is currently just stubbed out. Feel free to program against the current API and add comments in tests to give hints at what is missing. - -=== Which tests are currently expected to pass? - -.Tests under `tests/testcase/` -|=== -|Test Name |OKR |Expectation | Owner| File - -|Appropriate Params -|4.1 -|failing, not yet impl. -|None -|`tests/testcases/t4_1_appropriate_params.rs` - -|XNet Messaging -|4.3 -|passing -|None -|`tests/testcases/t4_3_xnet_slo.rs` - -|Does Not Stop -|5.1 -|passing -|None -|`tests/testcases/t5_1_does_not_stop.rs` - -|Does Not Stop -|5.2 -|failing, only E2E test is implemented yet -|DMD -|`tests/testcases/t5_2_does_not_stop.rs` - -|Does Not Stop -|5.3 -|failing, not yet impl. -|DMD -|`tests/testcases/t5_3_does_not_stop.rs` - -|Decentralization -|6.1 -|failing, not yet impl. -|None -|`tests/testcases/t6_1_decentralization.rs` - -|Decentralization -|6.2 -|failing, not yet impl. -|None -|`tests/testcases/t6_2_decentralization.rs` - -|Decentralization -|6.3 -|failing, not yet impl. -|None -|`tests/testcases/t6_3_decentralization.rs` - -|Decentralization -|6.4 -|failing, not yet impl. -|None -|`tests/testcases/t6_4_decentralization.rs` - -|Topology Change -|7.1 -|failing, not yet impl. -|DSD -|`tests/testcases/t7_1_decentralization.rs` - -|Topology Change -|7.2 -|failing, not yet impl. -|DSD -|`tests/testcases/t7_2_decentralization.rs` - -|Topology Change -|7.3 -|passing -|None -|`tests/testcases/t7_3_decentralization.rs` - -|Upgrade -|9.1 -|passing -|None -|`tests/testcases/t9_1_decentralization.rs` - -|Malicious Nodes -|10.1 -|passing -|Hassen -|`tests/testcases/t10_1_malicious_nodes.rs` - -|Malicious Nodes -|10.2 -|failing, not yet impl. -|Hassen -|`tests/testcases/t10_2_malicious_nodes.rs` - -|Malicious Nodes -|10.3 -|passing -|Hassen -|`tests/testcases/t10_3_malicious_nodes.rs` - -|Malicious Nodes -|10.4 -|passing -|Hassen -|`tests/testcases/t10_4_malicious_nodes.rs` - -|Malicious Nodes -|10.5 -|passing -|Hassen -|`tests/testcases/t10_5_malicious_nodes.rs` - -|Malicious Nodes -|10.6 -|passing -|Hassen -|`tests/testcases/t10_6_malicious_nodes.rs` - -|Malicious Nodes -|10.7 -|passing -|Hassen -|`tests/testcases/t10_7_malicious_nodes.rs` - -|Malicious Users -|11.1 -|failing, not yet impl. -|Eftychis -|`tests/testcases/t11_1_malicious_users.rs` - -|Malicious Users -|11.2 -|failing, not yet impl. -|None -|`tests/testcases/t11_2_malicious_users.rs` - -|Malicious Users -|11.3 -|failing, not yet impl. -|None -|`tests/testcases/t11_3_malicious_users.rs` -|=== diff --git a/rs/scenario_tests/src/api.rs b/rs/scenario_tests/src/api.rs deleted file mode 100644 index 529d9423e95..00000000000 --- a/rs/scenario_tests/src/api.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod e2e; -pub mod handle; diff --git a/rs/scenario_tests/src/api/e2e.rs b/rs/scenario_tests/src/api/e2e.rs deleted file mode 100644 index 4cba18bbbd3..00000000000 --- a/rs/scenario_tests/src/api/e2e.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod handle; -pub mod testnet; diff --git a/rs/scenario_tests/src/api/e2e/handle.rs b/rs/scenario_tests/src/api/e2e/handle.rs deleted file mode 100644 index 58ee51be6e9..00000000000 --- a/rs/scenario_tests/src/api/e2e/handle.rs +++ /dev/null @@ -1,169 +0,0 @@ -use crate::api::e2e::testnet::{Testnet, TestnetT}; -use crate::api::handle::{Ic, Node, Subnet}; -use canister_test::*; -use ic_canister_client::{Agent, HttpClient, Sender}; -use ic_crypto_ed25519::{PrivateKey, PublicKey}; -use ic_types::{NodeId, SubnetId}; -use std::sync::Arc; -use url::Url; - -/// Handle for a testnet node. -pub struct NodeHandle { - id: NodeId, - ic_instance: Arc, -} - -impl NodeHandle { - pub(crate) fn new(id: NodeId, ic_instance: Arc) -> Self { - Self { id, ic_instance } - } - - pub fn id(&self) -> NodeId { - self.id - } -} - -impl Node for NodeHandle { - fn api(&self) -> Runtime { - // We use the anonymous user for now. - Runtime::Remote(RemoteTestRuntime { - agent: Agent::new_with_client( - self.ic_instance.agent_client.clone(), - self.ic_instance.node_api_url(self.id), - Sender::from_keypair(&self.ic_instance.caller_principal.0), - ), - effective_canister_id: self.ic_instance.testnet.effective_canister_id(self.id), - }) - } -} - -/// Handle for a testnet subnet. -pub struct SubnetHandle { - id: SubnetId, - nodes: Vec, - ic_instance: Arc, -} - -impl SubnetHandle { - pub(crate) fn new(id: SubnetId, nodes: Vec, ic_instance: Arc) -> Self { - Self { - id, - nodes, - ic_instance, - } - } - - pub fn id(&self) -> SubnetId { - self.id - } -} - -impl Subnet for SubnetHandle { - fn node_by_idx(&self, idx: usize) -> Box { - Box::new(NodeHandle::new(self.nodes[idx], self.ic_instance.clone())) - } - - fn node(&self, id: NodeId) -> Box { - assert!(self.nodes.contains(&id)); - Box::new(NodeHandle::new(id, self.ic_instance.clone())) - } -} - -/// Handle for a testnet. -pub struct IcHandle { - inner: Arc, -} - -impl IcHandle { - /// Creates an IC handle wrapping the given testnet configuration. - pub fn from_testnet(testnet: Testnet) -> Self { - let inner = Arc::new(IcInnerHandle::from_testnet(testnet)); - Self { inner } - } - - /// Creates an IC handle wrapping the given testnet configuration and the - /// keypair corresponding to the PEM encoded secret key file located at - /// `key_file`. - pub fn from_testnet_with_principal_from_file(testnet: Testnet, key_file: String) -> Self { - let inner = Arc::new(IcInnerHandle::from_testnet_with_principal_from_file( - testnet, key_file, - )); - Self { inner } - } -} - -impl Ic for IcHandle { - fn subnet_ids(&self) -> Vec { - self.inner.testnet.subnet_ids() - } - - fn subnet(&self, id: SubnetId) -> Box { - Box::new(SubnetHandle::new( - id, - self.inner.testnet.node_ids(id), - self.inner.clone(), - )) - } - - fn route(&self, principal_id: PrincipalId) -> Option { - self.inner.testnet.route(principal_id) - } - - fn get_principal(&self) -> Option { - Some(self.inner.caller_principal.1) - } -} - -/// A testnet-based internet computer instance. -pub(crate) struct IcInnerHandle { - pub(crate) testnet: Testnet, - - // Retain the same agent client for all http connections. - agent_client: HttpClient, - - // The keypair corresponding to the principal that is used for calling into the IC - caller_principal: (ic_canister_client::Ed25519KeyPair, PrincipalId), -} - -impl IcInnerHandle { - pub fn from_testnet(testnet: Testnet) -> Self { - Self::from_testnet_with_principal(testnet, *ic_test_identity::TEST_IDENTITY_KEYPAIR) - } - - fn from_testnet_with_principal( - testnet: Testnet, - caller_principal: ic_canister_client::Ed25519KeyPair, - ) -> Self { - let principal_id = PrincipalId::new_self_authenticating( - &PublicKey::deserialize_raw(&caller_principal.public_key) - .expect("Invalid public key") - .serialize_rfc8410_der(), - ); - Self { - testnet, - agent_client: HttpClient::new(), - caller_principal: (caller_principal, principal_id), - } - } - - pub fn from_testnet_with_principal_from_file(testnet: Testnet, key_file: String) -> Self { - let key_file = std::fs::read_to_string(key_file.clone()) - .unwrap_or_else(|_| panic!("Failed to load principal key from file {}", key_file)); - let secret_key = PrivateKey::deserialize_pkcs8_pem(&key_file).expect("Invalid secret key."); - - let public_key = secret_key.public_key(); - - Self::from_testnet_with_principal( - testnet, - ic_canister_client::Ed25519KeyPair { - secret_key: secret_key.serialize_raw(), - public_key: public_key.serialize_raw(), - }, - ) - } - - /// Url for the Public Api of node `node_id`. - pub fn node_api_url(&self, node_id: NodeId) -> Url { - self.testnet.node_api_url(node_id) - } -} diff --git a/rs/scenario_tests/src/api/e2e/testnet.rs b/rs/scenario_tests/src/api/e2e/testnet.rs deleted file mode 100644 index 8a91a731d67..00000000000 --- a/rs/scenario_tests/src/api/e2e/testnet.rs +++ /dev/null @@ -1,215 +0,0 @@ -use ic_crypto_utils_threshold_sig_der::parse_threshold_sig_key; -use ic_metrics::MetricsRegistry; -use ic_protobuf::registry::subnet::v1::SubnetType; -use ic_registry_client::client::{RegistryClient, RegistryClientImpl}; -use ic_registry_client_helpers::{ - node::NodeRegistry, - routing_table::RoutingTableRegistry, - subnet::{SubnetListRegistry, SubnetRegistry}, -}; -use ic_registry_nns_data_provider_wrappers::create_nns_data_provider; -use ic_registry_routing_table::CanisterIdRange; -use ic_registry_routing_table::RoutingTable; -use ic_types::{NodeId, PrincipalId, SubnetId}; -use std::{ - collections::BTreeMap, convert::TryFrom, net::SocketAddr, path::PathBuf, result::Result, - sync::Arc, -}; -use url::Url; - -/// Trait providing helper methods for accessing subnets and nodes. -pub trait TestnetT { - /// Sorted IDs of subnets on this testnet, - fn subnet_ids(&self) -> Vec; - - /// Sorted IDs of nodes on the given subnet, - fn node_ids(&self, subnet_id: SubnetId) -> Vec; - - /// Url for the Public Api of node `node_id`. - fn node_api_url(&self, node_id: NodeId) -> Url; - - /// Returns the subnet hosting the given `PrincipalId`, if any. - fn route(&self, principal_id: PrincipalId) -> Option; - - /// Returns a canister id in the canister ranges of the subnet to which the node belongs. - fn effective_canister_id(&self, node_id: NodeId) -> PrincipalId; -} - -#[derive(Debug)] -pub struct Testnet { - subnet_ids: Vec, - subnets: BTreeMap, - routing_table: RoutingTable, -} - -#[derive(Debug, Default)] -struct Subnet { - node_ids: Vec, - nodes: BTreeMap, -} - -#[derive(Debug)] -struct Node { - endpoint_url: Url, -} - -pub fn registry_client( - nns_url: &str, - nns_public_key_path: Option, -) -> Arc { - let nns_url = Url::parse(nns_url).unwrap(); - let nns_public_key = nns_public_key_path.map(|path| { - parse_threshold_sig_key(&PathBuf::from(&path)).expect("Unable to parse public key file") - }); - let data_provider = create_nns_data_provider( - tokio::runtime::Handle::current(), - vec![nns_url], - nns_public_key, - ); - let metrics_registry = MetricsRegistry::new(); - let registry_client = Arc::new(RegistryClientImpl::new( - data_provider, - Some(&metrics_registry), - )); - registry_client.try_polling_latest_version(100).unwrap(); - - registry_client -} - -/// Loads testnet topology from the latest version of the registry. -pub fn load_testnet_topology( - registry_client: &Arc, -) -> Result { - let mut subnets = BTreeMap::new(); - - let registry_version = registry_client.get_latest_version(); - - // Fetch all subnet IDs, propagate errors. - let subnet_ids = registry_client - .get_subnet_ids(registry_version) - .map_err(|e| e.to_string())? - .unwrap_or_default(); - - // No subnets is a problem. - if subnet_ids.is_empty() { - return Err("no subnets in registry".into()); - } - - let routing_table = registry_client - .get_routing_table(registry_version) - .map_err(|e| e.to_string())? - .ok_or("no routing table record")?; - - let mut app_subnet_ids = Vec::new(); - for subnet_id in subnet_ids { - let mut subnet = Subnet::default(); - - let subnet_record = registry_client - .get_subnet_record(subnet_id, registry_version) - .map_err(|e| e.to_string())? - .unwrap_or_default(); - if subnet_record.subnet_type == SubnetType::System as i32 { - // Skip NNS subnet. - continue; - } - app_subnet_ids.push(subnet_id); - // Fetch all node IDs for this subnet, propagate errors. - let node_ids = registry_client - .get_node_ids_on_subnet(subnet_id, registry_version) - .map_err(|e| e.to_string())? - .unwrap_or_default(); - - if node_ids.is_empty() { - return Err(format!("subnet {} has no nodes", subnet_id)); - } - subnet.node_ids.clone_from(&node_ids); - for node_id in node_ids { - let node_record = registry_client - .get_node_record(node_id, registry_version) - .map_err(|e| e.to_string())? - .ok_or(format!("node {} has no transport info", node_id))?; - - let http_conn_endpoint = node_record.http.unwrap(); - let addr = SocketAddr::new( - http_conn_endpoint.ip_addr.parse().unwrap(), - u16::try_from(http_conn_endpoint.port).unwrap(), - ); - - // Seen bogus registry entries where the connection endpoint exists - // but is 0.0.0.0. - if addr.ip().is_unspecified() { - return Err(format!( - "unspecified HTTP connection endpoint for node {}", - node_id - )); - } - - let endpoint_url = Url::parse(&format!("http://{}/", addr)).unwrap(); - - subnet.nodes.insert(node_id, Node { endpoint_url }); - } - subnets.insert(subnet_id, subnet); - } - - Ok(Testnet { - subnet_ids: app_subnet_ids, - subnets, - routing_table, - }) -} - -impl TestnetT for Testnet { - fn subnet_ids(&self) -> Vec { - self.subnet_ids.clone() - } - - fn node_ids(&self, subnet_id: SubnetId) -> Vec { - let node_ids: Vec<_> = self.subnets.get(&subnet_id).unwrap().node_ids.clone(); - assert!(!node_ids.is_empty(), "Subnet not found: {}", subnet_id); - node_ids - } - - fn node_api_url(&self, node_id: NodeId) -> Url { - let node = self - .subnets - .values() - .flat_map(|subnet| subnet.nodes.iter()) - .filter(|(id, _)| **id == node_id) - .map(|(_, node)| node) - .fold(None, |out, n| { - assert!(out.is_none(), "Duplicate node {}", node_id); - Some(n) - }) - .unwrap_or_else(|| panic!("Node not found: {}", node_id)); - - node.endpoint_url.clone() - } - - fn route(&self, principal_id: PrincipalId) -> Option { - self.routing_table.route(principal_id) - } - - fn effective_canister_id(&self, node_id: NodeId) -> PrincipalId { - let subnet_id: Option = self - .subnet_ids - .clone() - .iter() - .find(|s| self.node_ids(**s).contains(&node_id)) - .copied(); - match subnet_id { - Some(sub) => { - let canister_ranges: Vec = self - .routing_table - .iter() - .filter(|(_, sub_id)| sub_id.get() == sub.get()) - .map(|(ran, _)| *ran) - .collect(); - match canister_ranges.first() { - Some(range) => range.start.get(), - None => PrincipalId::default(), - } - } - None => PrincipalId::default(), - } - } -} diff --git a/rs/scenario_tests/src/api/handle.rs b/rs/scenario_tests/src/api/handle.rs deleted file mode 100644 index 306b457d630..00000000000 --- a/rs/scenario_tests/src/api/handle.rs +++ /dev/null @@ -1,33 +0,0 @@ -use canister_test::Runtime; -use ic_types::{NodeId, PrincipalId, SubnetId}; - -/// Shim for `IcHandle` implementations. -pub trait Ic { - /// Iterator over subnet ids. - fn subnet_ids(&self) -> Vec; - - /// Retrieves a handle to the subnet with the given ID. - fn subnet(&self, id: SubnetId) -> Box; - - /// Returns the subnet hosting the given `PrincipalId`, if any. - fn route(&self, principal_id: PrincipalId) -> Option; - - /// Returns the principal ID that is used to interact with this Ic instance, - /// or `None` if there is no explicit identity - fn get_principal(&self) -> Option; -} - -/// Shim for `SubnetHandle` implementations. -pub trait Subnet { - /// Retrieves a handle to the node with the given index on this subnet. - fn node_by_idx(&self, idx: usize) -> Box; - - /// Retrieves a handle to the node with the given ID on this subnet. - fn node(&self, id: NodeId) -> Box; -} - -/// Shim for `NodeHandle` implementations. -pub trait Node { - /// An agent that targets this node. - fn api(&self) -> Runtime; -} diff --git a/rs/scenario_tests/src/lib.rs b/rs/scenario_tests/src/lib.rs deleted file mode 100644 index daee72cd501..00000000000 --- a/rs/scenario_tests/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod api; -pub mod runner; -pub mod tests; diff --git a/rs/scenario_tests/src/main.rs b/rs/scenario_tests/src/main.rs deleted file mode 100644 index 948f2f58a81..00000000000 --- a/rs/scenario_tests/src/main.rs +++ /dev/null @@ -1,236 +0,0 @@ -use ic_scenario_tests::api::e2e::testnet::*; -use ic_scenario_tests::runner::{passing_test_async, runner}; -use ic_scenario_tests::tests::e2e::*; - -#[derive(Default)] -struct Config { - nns_url: Option, - nns_public_key: Option, - subnets: Option, - runtime: Option, - sleeptime: Option, - rate: Option, - payload_size: Option, - num_canisters: Option, - size_level: Option, - random_seed: Option, - targeted_latency: Option, - principal_key_file: Option, - wallet_canisters: Option>, - cycles_per_subnet: Option, - canisters_to_cleanup: Option>, - skip_cleanup: bool, - delete_canister_retries: Option, - all_to_one: bool, - canisters_per_subnet: Option, - canister_to_subnet_rate: Option, -} - -pub fn main() { - let mut config = Config::default(); - let mut args = std::env::args(); - // Skip binary name. - let binary = args.next().unwrap_or_default(); - while let Some(arg) = args.next() { - match arg.as_str() { - "--nns_url" => config.nns_url = Some(args.next().expect("Missing NNS URL")), - "--nns_public_key" => { - config.nns_url = Some(args.next().expect("Missing NNS public key path")) - } - "--subnets" => { - config.subnets = Some( - args.next() - .expect("Missing subnets value") - .parse() - .expect("Invalid subnets, expected u64 value"), - ) - } - "--runtime" => { - config.runtime = Some( - args.next() - .expect("Missing runtime value") - .parse() - .expect("Invalid runtime, expected u64 value"), - ) - } - "--sleeptime" => { - config.sleeptime = Some( - args.next() - .expect("Missing sleeptime value") - .parse() - .expect("Invalid sleeptime, expected u64 value"), - ) - } - "--rate" => { - config.rate = Some( - args.next() - .expect("Missing rate value") - .parse() - .expect("Parse error"), - ) - } - "--payload_size" => { - config.payload_size = Some( - args.next() - .expect("Missing payload_size value") - .parse() - .expect("Invalid payload_size, expected u64 value"), - ) - } - "--num_canisters" => { - config.num_canisters = Some( - args.next() - .expect("Missing value for num_canisters") - .parse() - .expect("Invalid num_canisters, expected u64 value"), - ) - } - "--size_level" => { - config.size_level = Some( - args.next() - .expect("Missing value for size_level") - .parse() - .expect("Invalid size_level, expected u64 value"), - ) - } - "--random_seed" => { - config.random_seed = Some( - args.next() - .expect("Missing value for random_seed") - .parse() - .expect("Invalid size_level, expected u64 value"), - ) - } - "--targeted_latency" => { - config.targeted_latency = Some( - args.next() - .expect("Missing value for targeted_latency") - .parse() - .expect("Invalid targeted_latency, expected u64 value"), - ) - } - "--principal_key" => { - config.principal_key_file = - Some(args.next().expect("Missing value for principal_key")) - } - "--wallet_canisters" => { - config.wallet_canisters = Some( - args.next() - .expect("Missing value for wallet_canisters") - .split(',') - .map(|s| s.into()) - .collect(), - ) - } - "--cycles_per_subnet" => { - config.cycles_per_subnet = Some( - args.next() - .expect("Missing value for cycles_per_subnet") - .parse() - .expect("Invalid cycles_per_subnet, expected u64 value"), - ) - } - "--canisters_to_cleanup" => { - config.canisters_to_cleanup = Some( - args.next() - .expect("Missing value for canisters_to_cleanup") - .split(',') - .map(|s| s.into()) - .collect(), - ) - } - "--skip_cleanup" => { - config.skip_cleanup = true; - } - "--delete_canister_retries" => { - config.delete_canister_retries = Some( - args.next() - .expect("Missing value for delete_canister_retries") - .parse() - .expect("Invalid delete_canister_retries, expected u64 value"), - ) - } - "--all_to_one" => { - config.all_to_one = true; - } - "--canisters_per_subnet" => { - config.canisters_per_subnet = Some( - args.next() - .expect("Missing value for canisters_per_subnet") - .parse() - .expect("Invalid canisters_per_subnet, expected u64 value"), - ) - } - "--canister_to_subnet_rate" => { - config.canister_to_subnet_rate = Some( - args.next() - .expect("Missing value for canister_to_subnet_rate") - .parse() - .expect("Invalid canister_to_subnet_rate, expected u64 value"), - ) - } - // Remaining arguments will be passed to `runner()`. - "--" => break, - other => panic!("Unexpected command line flag: \"{}\"", other), - } - } - - let nns_public_key_path = config.nns_public_key.clone(); - let testnet = config - .nns_url - .as_ref() - .map(|nns_url| { - tokio::runtime::Runtime::new().unwrap().block_on(async { - load_testnet_topology(®istry_client(nns_url, nns_public_key_path)) - .unwrap_or_else(|e| { - panic!("Failed to load testnet topology from {}: {}", nns_url, e) - }) - }) - }) - .expect("Missing required argument: nns_url"); - - let flag = args.next().expect("Missing E2E test CLI flag"); - match flag.as_str() { - "4.3" => { - let testcase = move || { - testcase_4_3_xnet_slo::e2e_test( - testnet, - config.subnets, - config.runtime, - config.rate, - config.payload_size, - config.targeted_latency, - config.principal_key_file, - config.wallet_canisters, - config.cycles_per_subnet, - config.canisters_to_cleanup, - config.skip_cleanup, - config.delete_canister_retries, - config.all_to_one, - config.canisters_per_subnet, - config.canister_to_subnet_rate, - ) - }; - runner( - vec![passing_test_async("Xnet Messaging 4.3", testcase)], - std::iter::once(binary).chain(args).collect(), - ) - } - "5.2" => { - let testcase = move || { - testcase_5_2_does_not_stop::e2e_test( - testnet, - config.sleeptime, - config.num_canisters, - config.size_level, - config.random_seed, - ) - }; - runner( - vec![passing_test_async("Statesync 5.2", testcase)], - std::iter::once(binary).chain(args).collect(), - ) - } - _ => panic!("Unknown E2E test {}", flag), - } -} diff --git a/rs/scenario_tests/src/runner.rs b/rs/scenario_tests/src/runner.rs deleted file mode 100644 index e864c51a9a6..00000000000 --- a/rs/scenario_tests/src/runner.rs +++ /dev/null @@ -1,168 +0,0 @@ -use colored::*; -use core::future::Future; -use tester::{ - assert_test_result, test_main, DynTestFn, ShouldPanic, StaticTestName, TestDesc, TestDescAndFn, - TestType, -}; - -/// This function reuses and tweaks the code generally used by cargo test. -/// This will work both when called by 'cargo run' and 'cargo test', although -/// test somewhat mangles the output. -/// This currently doesn't check that failing tests are actually failing -pub fn runner(tests: Vec, args: Vec) { - fn partition_map Result>(v: Vec, f: F) -> (Vec, Vec) { - v.into_iter() - .fold((Vec::new(), Vec::new()), |(mut oks, mut errs), elem| { - match f(elem) { - Ok(v) => oks.push(v), - Err(v) => errs.push(v), - }; - (oks, errs) - }) - } - - let (should_pass, should_fail) = partition_map(tests, passing); - - println!("Scenario tests:"); - for test in &should_pass { - println!("\t{0} Running {1}", "✔".green(), test.desc.name) - } - - test_main(&args, should_pass, None); - - for test in should_fail { - match test { - Test::Passing(_) => panic!("This should be impossible"), - Test::Failing(test, excuse, name) => { - let name_and_shame = match name { - Some(name) => format!("and it's {}'s job to fix it", name.red()), - None => "".to_string(), - }; - println!( - "\t{0} {1} was not run because {2} {3}", - "✘".red(), - test.desc.name, - excuse, - name_and_shame, - ) - } - } - } - println!(); -} - -pub fn failing_test( - test_name: &'static str, - test: F, - excuse: &str, - assignee: Option<&str>, -) -> Test -where - F: FnOnce() + Send + 'static, -{ - let mut test = test_builder(test_name, test); - test.desc.allow_fail = true; - Test::Failing(test, excuse.to_string(), assignee.map(|s| s.to_string())) -} - -pub fn failing_test_async( - test_name: &'static str, - test: impl FnOnce() -> Fut + Send + 'static, - excuse: &str, - assignee: Option<&str>, -) -> Test -where - Fut: Future, -{ - let mut test = test_builder(test_name, move || { - tokio::runtime::Runtime::new().unwrap().block_on(test()) - }); - test.desc.allow_fail = true; - Test::Failing(test, excuse.to_string(), assignee.map(|s| s.to_string())) -} - -pub fn passing_test(name: &'static str, test: F) -> Test -where - F: FnOnce() + Send + 'static, -{ - Test::Passing(test_builder(name, test)) -} - -pub fn passing_test_async( - name: &'static str, - test: impl FnOnce() -> Fut + Send + 'static, -) -> Test -where - Fut: Future, -{ - passing_test(name, move || { - tokio::runtime::Runtime::new().unwrap().block_on(test()) - }) -} - -pub enum Test { - Passing(TestDescAndFn), - Failing(TestDescAndFn, String, Option), -} - -#[allow(clippy::result_large_err)] -fn passing(t: Test) -> Result { - match t { - Test::Passing(t) => Ok(t), - t => Err(t), - } -} - -#[allow(clippy::unit_arg)] -fn test_builder(name: &'static str, test: F) -> TestDescAndFn -where - F: FnOnce() + Send + 'static, -{ - TestDescAndFn { - desc: TestDesc { - name: StaticTestName(name), - ignore: false, - allow_fail: false, - should_panic: ShouldPanic::No, - test_type: TestType::Unknown, - }, - testfn: DynTestFn(Box::new(|| assert_test_result(test()))), - } -} - -#[cfg(test)] -fn test_fail() { - panic!("Oh no!") -} - -#[cfg(test)] -pub fn test_pass() {} - -/// Here is an example of how to use this testing framework -#[test] -#[ignore] -pub fn run_tests() { - runner( - vec![ - passing_test("Example passing test", test_pass), - failing_test( - "Example failing test", - test_fail, - "the test is not implemented", - None, - ), - ], - vec![], - ) -} - -// It's actually impossible to use should_panic on this function because of the -// way the rust test framework interacts with itself -#[ignore] -#[test] -pub fn run_tests_and_fail() { - runner( - vec![passing_test("Example failing test", test_fail)], - vec![], - ) -} diff --git a/rs/scenario_tests/src/tests.rs b/rs/scenario_tests/src/tests.rs deleted file mode 100644 index 19cb083627e..00000000000 --- a/rs/scenario_tests/src/tests.rs +++ /dev/null @@ -1,301 +0,0 @@ -use crate::api::handle::Ic; -use candid::{CandidType, Deserialize, Principal}; -use canister_test::{Canister, Runtime}; -use dfn_candid::candid; -use futures::{future::join_all, Future}; -use ic_base_types::{CanisterId, PrincipalId, SubnetId}; -use std::collections::BTreeMap; -use std::iter::FromIterator; -use std::str::FromStr; -use std::time::Duration; - -pub mod e2e; -pub mod testcase_4_3_xnet_slo; -pub mod testcase_5_2_does_not_stop; - -/// A handle that wraps information identifying a canister. -#[derive(Clone)] -enum CanisterHandle<'a> { - CanisterId(CanisterId), - Canister(Canister<'a>), -} - -impl<'a> CanisterHandle<'a> { - fn canister(&'a self, api: &'a Runtime) -> Canister<'a> { - match self { - CanisterHandle::CanisterId(id) => Canister::new(api, *id), - CanisterHandle::Canister(canister) => canister.clone(), - } - } - - fn canister_id(&self) -> CanisterId { - match self { - CanisterHandle::CanisterId(id) => *id, - CanisterHandle::Canister(canister) => canister.canister_id(), - } - } -} - -impl From for CanisterHandle<'_> { - fn from(canister_id: CanisterId) -> Self { - CanisterHandle::CanisterId(canister_id) - } -} - -impl<'a> From> for CanisterHandle<'a> { - fn from(canister: Canister<'a>) -> Self { - CanisterHandle::Canister(canister) - } -} - -/// A struct that wraps a canister together with an optional associated wallet -/// plus a runtime that allows to communicate with the canister. -pub struct CanisterLocator<'a> { - canister: CanisterHandle<'a>, - wallet: Option>, - api: Runtime, -} - -impl<'a> CanisterLocator<'a> { - fn new_from_ic( - canister: CanisterHandle<'a>, - wallet: Option>, - ic: &'a dyn Ic, - ) -> Self { - let subnet_id = ic.route(canister.canister_id().get()); - if let Some(subnet_id) = subnet_id { - let api = ic.subnet(subnet_id).node_by_idx(0).api(); - Self::new(canister, wallet, api) - } else { - panic!( - "Could not find subnet id for canister {}.", - canister.canister_id() - ); - } - } - - fn new(canister: CanisterHandle<'a>, wallet: Option>, api: Runtime) -> Self { - Self { - canister, - wallet, - api, - } - } - - fn canister(&self) -> Canister { - self.canister.canister(&self.api) - } - - fn wallet(&self) -> Option { - self.wallet - .as_ref() - .map(|handle| handle.canister(&self.api)) - } -} - -/// For all `canisters` concurrently attempts to first refund the remaining -/// cycles to the used wallet canisters, then attempts to stop the canisters -/// where refunding was successful, and finally attempts to delete the -/// successfully stopped canisters. If there are no wallets provided it -/// will directly stop and delete the given canisters. -pub async fn cleanup(canisters: Vec>, delete_canister_retries: u64) { - pub async fn return_cycles<'a, 'b>( - src: Canister<'a>, - dst: Canister<'b>, - ) -> Result, String> { - #[derive(Debug, CandidType, Deserialize)] - struct DepositCycleArgs { - canister_id: Principal, - } - - let res: Result = src - .update_( - "return_cycles", - candid, - (DepositCycleArgs { - canister_id: Principal::from_str(&dst.canister_id().to_string()).unwrap(), - },), - ) - .await; - - res.map(|_| src) - } - fn print_error<'a>( - action: &str, - i: usize, - result: Result, String>, - ) -> Result, String> { - if let Err(e) = result.as_ref() { - println!("{} {} failed: {}", action, i, e); - } - result - } - pub async fn stop_canister(canister: Canister<'_>) -> Result, String> { - canister.stop().await.map(|_| canister) - } - - pub async fn delete_canister( - canister: Canister<'_>, - max_retries: u64, - ) -> Result, String> { - let exponential_basis = 2; - - let mut sleep_duration = std::time::Duration::from_secs(1); - let mut retries_counter = 0; - while let Err(errmsg) = canister.delete().await { - if retries_counter == max_retries { - return Err(errmsg); - } - std::thread::sleep(sleep_duration); - sleep_duration *= exponential_basis; - retries_counter += 1; - } - Ok(canister) - } - - let canisters_to_wallets = canisters - .iter() - .map(|locator| (locator.canister(), locator.wallet())) - .filter(|(_, wallet)| wallet.is_some()) - .map(|(canister, wallet)| (canister, wallet.unwrap())) - .collect::>(); - - let canisters: Vec = if !canisters_to_wallets.is_empty() { - println!("Sleeping 120s to give canisters a chance to catch up with the backlog"); - std::thread::sleep(Duration::from_secs(120)); - println!("Refunding remaining cycles"); - let refunded: Vec> = parallel_async( - canisters_to_wallets, - |(canister, wallet)| return_cycles(canister, wallet), - |i, res| print_error("Refunding cycles for canister", i, res), - ) - .await; - - refunded.into_iter().filter_map(|x| x.ok()).collect() - } else { - canisters.iter().map(|locator| locator.canister()).collect() - }; - - println!("Stopping canisters"); - let canisters: Vec> = - parallel_async(canisters, stop_canister, |i, res| { - print_error("Stopping canister", i, res) - }) - .await; - - println!("Deleting canisters"); - let _: Vec> = parallel_async( - canisters - .into_iter() - .filter_map(|x| x.ok()) - .collect::>(), - |canister| delete_canister(canister, delete_canister_retries), - |i, res| print_error("Deleting canister", i, res), - ) - .await; -} - -/// Performs `cleanup` based on `canister_ids` represented as strings -pub async fn cleanup_canister_ids( - canister_ids: Vec, - wallet_canisters: Vec, - ic: &dyn Ic, - delete_canister_retries: u64, -) { - let canisters = locate_canister_ids(canister_ids, wallet_canisters, ic); - cleanup(canisters, delete_canister_retries).await; -} - -/// Locates the canisters on the network relative to the given wallets and -/// returns a vector of canister locators. -pub fn locate_canisters<'a>( - canisters: &[Canister<'a>], - wallets: &BTreeMap, - ic: &'a dyn Ic, -) -> Vec> { - locate_canisters_internal( - canisters - .iter() - .map(|canister| canister.clone().into()) - .collect(), - wallets - .iter() - .map(|(_, canister_id)| (*canister_id).into()) - .collect(), - ic, - ) -} - -/// Locates the canister ids on the network relative to the given wallet ids and -/// returns a vector of canister locators. -pub fn locate_canister_ids( - canister_ids: Vec, - wallet_ids: Vec, - ic: &dyn Ic, -) -> Vec { - fn into_canister_id(canister_id: &str) -> CanisterId { - CanisterId::unchecked_from_principal( - PrincipalId::from_str(canister_id) - .unwrap_or_else(|_| panic!("Could not create PrincipalId from {}", canister_id)), - ) - } - fn into_canister_ids<'a>(canister_ids: &'_ [String]) -> Vec> { - canister_ids - .iter() - .map(|id| into_canister_id(id).into()) - .collect() - } - - locate_canisters_internal( - into_canister_ids(&canister_ids), - into_canister_ids(&wallet_ids), - ic, - ) -} - -fn locate_canisters_internal<'a>( - canisters: Vec>, - wallets: Vec>, - ic: &'a dyn Ic, -) -> Vec> { - fn route(canister_id: CanisterId, ic: &dyn Ic) -> SubnetId { - ic.route(canister_id.get()) - .unwrap_or_else(|| panic!("Could not map canister {} to subnet.", canister_id)) - } - - let wallets = wallets - .into_iter() - .map(|wallet_handle| (route(wallet_handle.canister_id(), ic), wallet_handle)) - .collect::>(); - - canisters - .into_iter() - .map(|canister_handle| { - let canister_id = canister_handle.canister_id(); - CanisterLocator::new_from_ic( - canister_handle, - wallets.get(&route(canister_id, ic)).cloned(), - ic, - ) - }) - .collect() -} - -/// Concurrently executes the `call` async closure for every item in `targets`, -/// postprocessing each result with `post` and collecting them. -pub async fn parallel_async(targets: I, call: Pre, post: Post) -> O -where - I: IntoIterator, - F: Future, - Pre: Fn(I::Item) -> F, - Post: Fn(usize, F::Output) -> P, - O: FromIterator

, -{ - let futures = targets.into_iter().map(call); - join_all(futures) - .await - .into_iter() - .enumerate() - .map(|(i, res)| post(i, res)) - .collect() -} diff --git a/rs/scenario_tests/src/tests/e2e.rs b/rs/scenario_tests/src/tests/e2e.rs deleted file mode 100644 index 35dc192beeb..00000000000 --- a/rs/scenario_tests/src/tests/e2e.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod testcase_4_3_xnet_slo; -pub mod testcase_5_2_does_not_stop; diff --git a/rs/scenario_tests/src/tests/e2e/testcase_4_3_xnet_slo.rs b/rs/scenario_tests/src/tests/e2e/testcase_4_3_xnet_slo.rs deleted file mode 100644 index 957c8cdd2aa..00000000000 --- a/rs/scenario_tests/src/tests/e2e/testcase_4_3_xnet_slo.rs +++ /dev/null @@ -1,75 +0,0 @@ -use crate::api::e2e::{handle::IcHandle, testnet::Testnet}; -use crate::tests::testcase_4_3_xnet_slo::{stop_chatters, DEFAULT_DELETE_CANISTER_RETRIES}; -use crate::tests::{cleanup_canister_ids, locate_canister_ids, testcase_4_3_xnet_slo::test_impl}; - -/// Testcase 4.3 in its end-to-end test incarnation. -/// -/// Takes the IC instance to run on in the form of an already deployed testnet. -#[allow(clippy::too_many_arguments)] -pub async fn e2e_test( - testnet: Testnet, - subnets: Option, - runtime: Option, - rate: Option, - payload_size: Option, - targeted_latency: Option, - key_file: Option, - wallet_canisters: Option>, - cycles_per_subnet: Option, - canisters_to_cleanup: Option>, - skip_cleanup: bool, - delete_canister_retries: Option, - all_to_one: bool, - canisters_per_subnet: Option, - canister_to_subnet_rate: Option, -) { - let ic = match key_file { - Some(key_file) => IcHandle::from_testnet_with_principal_from_file(testnet, key_file), - _ => IcHandle::from_testnet(testnet), - }; - - if let Some(canisters_to_cleanup) = canisters_to_cleanup { - if skip_cleanup { - println!( - "Warning: --skip_cleanup flag is ignored when --canisters_to_cleanup is provided." - ); - } - let wallet_canisters = wallet_canisters.unwrap_or_default(); - // If there a some canisters left to be cleaned up we call stop() on them just - // to be sure that they don't keep consuming cycles while we are - // depositing them back onto the wallets. - stop_chatters( - &locate_canister_ids(canisters_to_cleanup.clone(), wallet_canisters.clone(), &ic) - .iter() - .map(|locator| locator.canister()) - .collect::>(), - |_| { /* do nothing */ }, - ) - .await; - cleanup_canister_ids( - canisters_to_cleanup, - wallet_canisters, - &ic, - delete_canister_retries.unwrap_or(DEFAULT_DELETE_CANISTER_RETRIES), - ) - .await; - return; - } - - test_impl( - &ic, - subnets, - runtime, - rate, - payload_size, - targeted_latency, - wallet_canisters, - cycles_per_subnet, - skip_cleanup, - delete_canister_retries, - all_to_one, - canisters_per_subnet, - canister_to_subnet_rate, - ) - .await -} diff --git a/rs/scenario_tests/src/tests/e2e/testcase_5_2_does_not_stop.rs b/rs/scenario_tests/src/tests/e2e/testcase_5_2_does_not_stop.rs deleted file mode 100644 index d96058fa861..00000000000 --- a/rs/scenario_tests/src/tests/e2e/testcase_5_2_does_not_stop.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::api::e2e::{handle::IcHandle, testnet::Testnet}; -use crate::tests::testcase_5_2_does_not_stop::test_impl; - -/// Testcase 5.2 in its end-to-end test incarnation. -/// -/// Takes the IC instance to run on in the form of an already deployed testnet. -pub async fn e2e_test( - testnet: Testnet, - sleeptime: Option, - num_canisters: Option, - size_level: Option, - random_seed: Option, -) { - let ic = IcHandle::from_testnet(testnet); - - test_impl(&ic, sleeptime, num_canisters, size_level, random_seed).await -} diff --git a/rs/scenario_tests/src/tests/testcase_4_3_xnet_slo.rs b/rs/scenario_tests/src/tests/testcase_4_3_xnet_slo.rs deleted file mode 100644 index 358e12c93f7..00000000000 --- a/rs/scenario_tests/src/tests/testcase_4_3_xnet_slo.rs +++ /dev/null @@ -1,490 +0,0 @@ -use crate::api::handle::Ic; -use crate::tests::{cleanup, locate_canisters, parallel_async}; -use candid::{CandidType, Deserialize}; -use canister_test::*; -use dfn_candid::candid; -use ic_base_types::SubnetId; -use std::{collections::BTreeMap, fmt::Display}; -use std::{str::FromStr, time::Duration}; -use xnet_test::{Metrics, NetworkTopology}; - -/// For how long to run the test after canisters have been installed and -/// `start()` was called. Not a `Duration` as we use it for computing how many -/// messages we expect to have gotten responses. -const DEFAULT_TEST_DURATION_SECONDS: u64 = 60; - -const DEFAULT_RATE: u64 = 10; -const DEFAULT_PAYLOAD_SIZE: u64 = 1024; - -/// Maximum messages a canister should send every round (in order to prevent it -/// filling up its output queue). This should be estimated as: -/// -/// `queue_capacity / 10 /* max_rounds roundtrip */` -const MAX_CANISTER_TO_CANISTER_RATE: usize = 30; - -const DEFAULT_TARGETED_LATENCY_SECONDS: u64 = 20; - -pub const DEFAULT_DELETE_CANISTER_RETRIES: u64 = 8; - -/// Testcase 4.3 implementation: installs `xnet-test-canister` onto the first -/// `subnets` subnets of `ic`; calls `start()` on each; sleeps for `runtime`; -/// calls `stop()` on each; and finally retrieves and validates the metrics -/// collected by each canister. -/// -/// Optionally a collection of `wallet_canisters` can be provided, one per -/// subnet, to serve as an alternative way of creating canisters if -/// `ProvisionalCreateCanisterWithCycles` is not available on the testnet. -#[allow(clippy::too_many_arguments)] -pub async fn test_impl( - ic: &dyn Ic, - subnets: Option, - runtime: Option, - rate: Option, - payload_size_bytes: Option, - targeted_latency_seconds: Option, - wallet_canisters: Option>, - cycles_per_subnet: Option, - skip_cleanup: bool, - delete_canister_retries: Option, - all_to_one: bool, - canisters_per_subnet: Option, - canister_to_subnet_rate: Option, -) { - if let Some(subnets) = subnets { - assert!( - subnets >= 2 , - "At least 2 subnets are required to test XNet messaging. Test was invoked with `--subnets {}`", - subnets - ); - } - if let Some(runtime) = runtime { - assert!( - runtime > 10, - "Test runtime must be more than 10 seconds. Test was invoked with `--runtime {}`", - runtime - ); - } - if let Some(rate) = rate { - assert!( - rate > 0, - "Message rate must be non-zero. Test was invoked with `--rate {}`", - rate - ); - } - if let Some(wallet_canisters) = wallet_canisters.as_ref() { - let subnets = subnets.unwrap_or_else(|| ic.subnet_ids().len() as u64); - assert!( - wallet_canisters.len() >= subnets as usize, - "One wallet canister is required for each of {} subnets", - subnets - ); - } - if wallet_canisters.is_none() && cycles_per_subnet.is_some() { - println!("Warning: cycles_per_subnet will be ignored when not specifying wallet_canisters"); - } - - // Load the xnet-test-canister. - let wasm = Project::cargo_bin_maybe_from_env("xnet-test-canister", &[]); - - // Map subnets to wallet canisters (if provided) and retain those subnets with - // wallet canisters only. - let wallet_canisters = - wallet_canisters.map(|canisters| subnets_to_wallet_canisters(canisters, ic)); - let subnet_ids = wallet_canisters - .as_ref() - .map(|wallet_canisters| wallet_canisters.keys().copied().collect()) - .unwrap_or_else(|| ic.subnet_ids()); - let wallet_canisters = wallet_canisters.unwrap_or_default(); - assert!( - subnet_ids.len() >= 2, - "At least 2 subnets are required to test XNet messaging. Provided topology has {} subnets.", - subnet_ids.len() - ); - let subnets = subnets.map(|s| s as usize).unwrap_or(subnet_ids.len()); - - // Nodes 0 on each subnet and the corresponding wallet canisters (if any). - let node_apis: Vec<_> = subnet_ids - .into_iter() - .map(|id| { - ( - ic.subnet(id).node_by_idx(0).api(), - wallet_canisters.get(&id), - ) - }) - .take(subnets) - .collect(); - let subnets = node_apis.len(); - - let (canisters_per_subnet, canister_to_subnet_rate, subnet_to_subnet_rate) = match ( - canisters_per_subnet, - canister_to_subnet_rate, - ) { - (Some(_), None) | (None, Some(_)) => { - panic!( - "`--canisters_per_subnet` and `--canister_to_subnet_rate` must be specified together." - ); - } - (Some(canisters_per_subnet), Some(canister_to_subnet_rate)) => { - // Using overwrites for `rate` to directly specify the number of canisters per subnet, - // the canister to subnet rate. The subnet to subnet rate is the product of the former. - assert!( - canisters_per_subnet > 0, - "Number of canisters per subnet must be non-zero. Test was invoked with '--canisters_per_subnet {}`.", - canisters_per_subnet, - ); - assert!( - canister_to_subnet_rate > 0, - "Canisters to subnet rate must be non-zero. Test was invoked with '--canister_to_subnet_rate {}`.", - canister_to_subnet_rate, - ); - if rate.is_some() { - println!("Warning: rate will be ignored when `canister_per_subnet` and `canister_to_subnet_rate` are specified."); - } - ( - canisters_per_subnet as usize, - canister_to_subnet_rate as usize, - (canisters_per_subnet * canister_to_subnet_rate) as usize, - ) - } - (None, None) => { - // Using `rate` to automatically compute the number of canisters per subnet, the - // canister to subnet rate and the subnet to subnet rate. - if let Some(rate) = rate { - assert!( - rate > 0, - "Message rate must be non-zero. Test was invoked with `--rate {}`", - rate - ); - } - let rate = rate.unwrap_or(DEFAULT_RATE); - // Subnet-to-subnet request rate: ceil(rate / (subnets - 1)). - let subnet_to_subnet_rate = (rate as usize - 1) / (subnets - 1) + 1; - // Minimum number of subnet-to-subnet queues needed to stay under - // `MAX_CANISTER_TO_CANISTER_RATE`. - let subnet_to_subnet_queues = - (subnet_to_subnet_rate - 1) / MAX_CANISTER_TO_CANISTER_RATE + 1; - // Minimum number of canisters required to send `subnet_to_subnet_rate` requests - // per round. - let canisters_per_subnet = (subnet_to_subnet_queues as f64).sqrt().ceil() as usize; - // A canister's outbound request rate to a given subnet. - let canister_to_subnet_rate = (subnet_to_subnet_rate - 1) / canisters_per_subnet + 1; - ( - canisters_per_subnet, - canister_to_subnet_rate, - subnet_to_subnet_rate, - ) - } - }; - - let cycles_per_canister = cycles_per_subnet - .map(|cycles| cycles / canisters_per_subnet as u64) - .unwrap_or(u64::MAX); - let payload_size_bytes = payload_size_bytes.unwrap_or(DEFAULT_PAYLOAD_SIZE); - let targeted_latency_seconds = - targeted_latency_seconds.unwrap_or(DEFAULT_TARGETED_LATENCY_SECONDS); - - // Install `canisters_per_subnet` canisters on every subnet. - println!( - "👉 Installing {} xnet-test-canister instance(s) onto each of {} subnets", - canisters_per_subnet, subnets - ); - let canisters: Vec<_> = parallel_async( - node_apis - .iter() - .cycle() - .take(canisters_per_subnet * subnets), - |(api, wallet_canister)| { - install( - wasm.clone(), - api, - wallet_canister.copied(), - cycles_per_canister, - ic.get_principal(), - ) - }, - |i, res| { - res.unwrap_or_else(|err| { - panic!( - "Failed to install canister onto subnet {}: {}", - i % subnets, - err - ) - }) - }, - ) - .await; - - // Call `start()` on all canisters to get them chatting to one another. - println!( - "👉 Starting chatter: {} messages/round * {} bytes = {} bytes/round", - canister_to_subnet_rate * canisters_per_subnet * (subnets - 1), - payload_size_bytes, - canister_to_subnet_rate - * canisters_per_subnet - * (subnets - 1) - * payload_size_bytes as usize - ); - let mut topology: NetworkTopology = vec![Vec::with_capacity(canisters_per_subnet); subnets]; - - if all_to_one { - topology - .get_mut(0) - .unwrap() - .push(canisters[0].canister_id_vec8()); - } else { - canisters.iter().enumerate().for_each(|(i, canister)| { - topology - .get_mut(i % subnets) - .unwrap() - .push(canister.canister_id_vec8()) - }); - } - let _: Vec = parallel_async( - &canisters, - |canister| { - canister.update_( - "start", - candid, - ( - topology.clone(), - canister_to_subnet_rate as u64, - payload_size_bytes, - ), - ) - }, - |i, res| { - res.unwrap_or_else(|e| { - panic!("Calling start() on subnet {} failed: {}", i % subnets, e) - }) - }, - ) - .await; - - // Let them run for a while. - let runtime = runtime.unwrap_or(DEFAULT_TEST_DURATION_SECONDS); - println!("👉 Sleeping for {} seconds", runtime); - let delay = Duration::from_secs(runtime); - std::thread::sleep(delay); - - // Stop the chatter (as a way of ensuring that subnets are still responsive). - println!("👉 Stopping chatter"); - stop_chatters(&canisters, |error| { - panic!("Calling stop() on canister failed: {}", error) - }) - .await; - - // Retrieve collected metrics. - println!("👉 Collecting metrics"); - let metrics: Vec = parallel_async( - &canisters, - |canister| canister.query_("metrics", candid, ()), - |i, res| { - res.unwrap_or_else(|e| { - panic!("Querying metrics() on subnet {} failed: {}", i % subnets, e) - }) - }, - ) - .await; - - if !skip_cleanup { - println!("👉 Cleaning up"); - cleanup( - locate_canisters(&canisters, &wallet_canisters, ic), - delete_canister_retries.unwrap_or(DEFAULT_DELETE_CANISTER_RETRIES), - ) - .await; - } - - let mut aggregated_metrics: Vec = Vec::with_capacity(subnets); - for _ in 0..subnets { - aggregated_metrics.push(Metrics::default()); - } - for (i, m) in metrics.iter().enumerate() { - println!( - "👉 Metrics for subnet {}, canister {}: {:?}", - i % subnets, - i / subnets, - m - ); - aggregated_metrics.get_mut(i % subnets).unwrap().merge(m); - } - - let mut success = true; - let mut expect = - |cond: bool, subnet: usize, ok_msg: &str, fail_msg: &str, val: &dyn Display| { - success &= cond; - println!( - "Subnet {}: {} {}: {} {}", - subnet, - if cond { "✅" } else { "❌" }, - if cond { ok_msg } else { fail_msg }, - val, - if cond { "🎉🎉🎉" } else { "😭😭😭" } - ); - }; - - for (i, m) in aggregated_metrics.iter().enumerate() { - let attempted_calls = m.requests_sent + m.call_errors; - if attempted_calls != 0 { - let failed_calls = m.call_errors + m.reject_responses; - let error_ratio = 100. * failed_calls as f64 / attempted_calls as f64; - expect( - error_ratio < 5., - i, - "Error ratio below 5%", - "Failed calls", - &format!("{}% ({}/{})", error_ratio, failed_calls, attempted_calls), - ); - } - - expect( - m.seq_errors == 0, - i, - "Sequence errors", - "Sequence errors", - &m.seq_errors, - ); - - let send_rate = attempted_calls as f64 - / (subnets - 1) as f64 - / runtime as f64 - / canisters_per_subnet as f64 - / canister_to_subnet_rate as f64; - expect( - send_rate >= 0.3, - i, - "Send rate at least 0.3", - "Send rate below 0.3", - &send_rate, - ); - - // Successful plus reject responses. - let responses_received = - m.latency_distribution.buckets().last().unwrap().1 + m.reject_responses; - // All messages sent more than `targeted_latency_seconds` before the end of the - // test should have gotten a response. - let responses_expected = (m.requests_sent as f64 - * (runtime - targeted_latency_seconds) as f64 - / runtime as f64) as usize; - // Account for requests enqueued this round (in case canister messages were - // executed before ingress messages, i.e. the heartbeat was executed before - // metrics collection) or uncounted responses (if ingress executed first). - let responses_expected = responses_expected - subnet_to_subnet_rate; - let actual = format!("{}/{}", responses_received, m.requests_sent); - let msg = format!( - "Expected requests sent more than {}s ago ({}/{}) to receive responses", - targeted_latency_seconds, responses_expected, m.requests_sent - ); - expect( - responses_received >= responses_expected, - i, - &msg, - &msg, - &actual, - ); - - if responses_received != 0 { - let avg_latency_millis = m.latency_distribution.sum_millis() / responses_received; - expect( - avg_latency_millis <= targeted_latency_seconds as usize * 1000, - i, - &format!( - "Mean response latency less than {}s", - targeted_latency_seconds - ), - &format!( - "Mean response latency was more than {}s", - targeted_latency_seconds - ), - &(avg_latency_millis as f64 * 1e-3), - ); - } - } - - assert!(success, "Test failed."); -} - -pub async fn stop_chatters(canisters: &[Canister<'_>], on_error: fn(&str) -> ()) { - let _: Vec<()> = parallel_async( - canisters, - |canister| canister.update_("stop", candid, ()), - |_, res| res.map(|_: String| ()).unwrap_or_else(|e| on_error(&e)), - ) - .await; -} - -/// Creates a map of subnets to wallet canisters, ensuring that every provided -/// canister maps to unique subnet. -fn subnets_to_wallet_canisters( - wallet_canisters: Vec, - ic: &dyn Ic, -) -> BTreeMap { - let wallet_canister_count = wallet_canisters.len(); - let subnet_wallet_canisters = wallet_canisters - .into_iter() - .map(|s| CanisterId::unchecked_from_principal(PrincipalId::from_str(&s).unwrap())) - .map(|c| (ic.route(c.get()).unwrap(), c)) - .collect::>(); - assert_eq!(wallet_canister_count, subnet_wallet_canisters.len()); - subnet_wallet_canisters -} - -/// Creates a canister on the given `Runtime` (via a wallet canister if -/// provided; else using `ProvisionalCreateCanisterWithCycles` as a whitelisted -/// principal) and installs the provided Wasm. -async fn install( - wasm: Wasm, - api: &Runtime, - wallet_canister_id: Option, - cycles: u64, - principal: Option, -) -> Result, String> { - if let Some(wallet_canister_id) = wallet_canister_id { - #[derive(Clone, CandidType, Deserialize)] - struct CanisterSettings { - controller: Option, - compute_allocation: Option, - memory_allocation: Option, - freezing_threshold: Option, - } - - #[derive(CandidType, Deserialize)] - struct CreateCanisterArgs { - cycles: u64, - settings: CanisterSettings, - } - #[derive(CandidType, Deserialize)] - struct CreateResult { - canister_id: PrincipalId, - } - - // Create canister via wallet canister. - let wallet_canister = Canister::new(api, wallet_canister_id); - let res: Result = wallet_canister - .update_( - "wallet_create_canister", - candid, - (CreateCanisterArgs { - cycles, - settings: CanisterSettings { - controller: principal, - compute_allocation: None, - memory_allocation: None, - freezing_threshold: None, - }, - },), - ) - .await?; - - // Install the Wasm. - let canister_id = CanisterId::unchecked_from_principal(res?.canister_id); - let mut canister = Canister::new(api, canister_id); - wasm.install_onto_canister(&mut canister, CanisterInstallMode::Install, None, None) - .await?; - - Ok(canister) - } else { - // Create and install canister via whitelisted principal. - wasm.install_(api, vec![]).await - } -} diff --git a/rs/scenario_tests/src/tests/testcase_5_2_does_not_stop.rs b/rs/scenario_tests/src/tests/testcase_5_2_does_not_stop.rs deleted file mode 100644 index 2f517b70989..00000000000 --- a/rs/scenario_tests/src/tests/testcase_5_2_does_not_stop.rs +++ /dev/null @@ -1,117 +0,0 @@ -use crate::api::handle::Ic; -use canister_test::{Canister, Project}; -use chrono::Utc; -use std::time::Duration; - -/// How long to run the test after canisters have been installed -const TEST_DURATION_SECONDS: usize = 120; -/// Number of canisters to be installed -const NUM_CANISTERS: usize = 2; - -/// `SIZE_LEVEL` is level of the size of canister state. -/// Each `statesync-test` canister's state size will be `SIZE_LEVEL` * -/// `VECTOR_LENGTH` bytes. `SIZE_LEVEL = 0` exercises the original testcase. -/// `SIZE_LEVEL > 0` is used for the state sync test of large size. -const SIZE_LEVEL: usize = 0; - -const RANDOM_SEED: usize = 0; -/// Testcase 5.2 implementation: installs copies of the -/// `statesync-test-canister` onto the second node of each `subnet` of `ic`; -/// calls `change()` on it and sleeps for `sleeptime / 8`; 8 times. -pub async fn test_impl( - ic: &dyn Ic, - sleeptime: Option, - num_canisters: Option, - size_level: Option, - random_seed: Option, -) { - // Load the statesync-test-canister. - let wasm = Project::cargo_bin_maybe_from_env("statesync-test-canister", &[]); - - println!("UTC before installing canisters is: {}", Utc::now()); - let num_canisters = num_canisters.unwrap_or(NUM_CANISTERS as u64) as usize; - let node1 = ic.subnet(ic.subnet_ids()[0]).node_by_idx(1); - let r = node1.api(); - let mut canisters: Vec = Vec::new(); - for i in 0..num_canisters { - canisters.push( - wasm.clone() - .install(&r) - .with_memory_allocation(1056 * 1024 * 1024) - .bytes(Vec::new()) - .await - .unwrap_or_else(|err| { - panic!("Failed to install canister {}: {}", i, err); - }), - ); - } - println!( - "UTC after installing {} canisters is: {}", - num_canisters, - Utc::now() - ); - - let size_level = size_level.unwrap_or(SIZE_LEVEL as u64) as usize; - let random_seed = random_seed.unwrap_or(RANDOM_SEED as u64) as usize; - if size_level == 0 { - // Call `change()` on all canisters 8 times, sleep inbetween - let total_changes: u8 = 8; - for x in 1..=total_changes { - println!("Start updating canisters, it is now {}", Utc::now()); - for canister in &canisters { - let seed = x + canister.canister_id_vec8()[0]; - let res: Result = canister - .update_("change_state", dfn_json::json, seed as u32) - .await - .unwrap_or_else(|e| { - panic!( - "Calling change_state() on canister {} failed: {}", - canister.canister_id_vec8()[0], - e - ) - }); - assert_eq!( - res, - Ok(x), - "Changed state {} times, result should have been Ok({}), was {:?}", - x, - x, - res - ); - } - println!("Updated canisters {} times, it is now {}", x, Utc::now()); - let time = sleeptime.unwrap_or(TEST_DURATION_SECONDS as u64) / (total_changes as u64); - let delay = Duration::from_secs(time); - std::thread::sleep(delay); // x: i32 - } - } else { - for x in 1..=size_level { - println!("Start expanding canisters, it is now {}", Utc::now()); - for (i, canister) in canisters.iter().enumerate() { - let seed = i + (x - 1) * num_canisters + random_seed; - let res: Result = canister - .update_("expand_state", dfn_json::json, (x as u32, seed as u32)) - .await - .unwrap_or_else(|e| { - panic!( - "Calling expand_state() on canister {} failed: {}", - canister.canister_id_vec8()[0], - e - ) - }); - assert_eq!( - res, - Ok(x as u8), - "Expanded state {} times, result should have been Ok({}), was {:?}", - x, - x, - res - ); - } - println!("Expanded canisters {} times, it is now {}", x, Utc::now()); - let time = sleeptime.unwrap_or(TEST_DURATION_SECONDS as u64) / size_level as u64; - let delay = Duration::from_secs(time); - std::thread::sleep(delay); // x: i32 - } - } -}