diff --git a/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs b/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs index 6e9235bb4..d45981eef 100644 --- a/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs +++ b/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs @@ -17,8 +17,7 @@ use std::{collections::HashMap, time::Duration}; use stellar_xdr::curr::{ Memo, MuxedAccount, PaymentOp, Preconditions, SequenceNumber, TransactionExt, }; - -use testcontainers::{clients, core::Port, RunnableImage}; +use testcontainers::{core::ContainerPort, runners::AsyncRunner, ContainerAsync, ImageExt}; use tokio::time::sleep; static PORT_RANGE: Lazy>> = Lazy::new(|| Mutex::new(40000..50000)); @@ -40,21 +39,16 @@ mod test_helpers { } use test_case::test_case; -use test_helpers::test::{ - emulator_http_transport::EmulatorHttpTransport, - speculos::{Args, Speculos}, -}; +use test_helpers::test::{emulator_http_transport::EmulatorHttpTransport, speculos::Speculos}; #[test_case("nanos".to_string() ; "when the device is NanoS")] #[test_case("nanox".to_string() ; "when the device is NanoX")] #[test_case("nanosp".to_string() ; "when the device is NanoS Plus")] #[tokio::test] async fn test_get_public_key(ledger_device_model: String) { - let runnable_image = get_runnable_image(ledger_device_model.clone()); - let docker = clients::Cli::default(); - let node = docker.run(runnable_image); - let host_port = node.get_host_port_ipv4(9998); - let ui_host_port: u16 = node.get_host_port_ipv4(5000); + let container = get_container(ledger_device_model.clone()).await; + let host_port = container.get_host_port_ipv4(9998).await.unwrap(); + let ui_host_port: u16 = container.get_host_port_ipv4(5000).await.unwrap(); wait_for_emulator_start_text(ui_host_port).await; let ledger = ledger(host_port).await; @@ -68,13 +62,10 @@ async fn test_get_public_key(ledger_device_model: String) { assert_eq!(public_key_string, expected_public_key); } Err(e) => { - node.stop(); println!("{e}"); assert!(false); } } - - node.stop(); } #[test_case("nanos".to_string() ; "when the device is NanoS")] @@ -82,11 +73,9 @@ async fn test_get_public_key(ledger_device_model: String) { #[test_case("nanosp".to_string() ; "when the device is NanoS Plus")] #[tokio::test] async fn test_get_app_configuration(ledger_device_model: String) { - let runnable_image = get_runnable_image(ledger_device_model.clone()); - let docker = clients::Cli::default(); - let node = docker.run(runnable_image); - let host_port = node.get_host_port_ipv4(9998); - let ui_host_port: u16 = node.get_host_port_ipv4(5000); + let container = get_container(ledger_device_model.clone()).await; + let host_port = container.get_host_port_ipv4(9998).await.unwrap(); + let ui_host_port: u16 = container.get_host_port_ipv4(5000).await.unwrap(); wait_for_emulator_start_text(ui_host_port).await; let ledger = ledger(host_port).await; @@ -96,13 +85,10 @@ async fn test_get_app_configuration(ledger_device_model: String) { assert_eq!(config, vec![0, 5, 0, 3]); } Err(e) => { - node.stop(); println!("{e}"); assert!(false); } }; - - node.stop(); } #[test_case("nanos".to_string() ; "when the device is NanoS")] @@ -110,11 +96,9 @@ async fn test_get_app_configuration(ledger_device_model: String) { #[test_case("nanosp".to_string() ; "when the device is NanoS Plus")] #[tokio::test] async fn test_sign_tx(ledger_device_model: String) { - let runnable_image = get_runnable_image(ledger_device_model.clone()); - let docker = clients::Cli::default(); - let node = docker.run(runnable_image); - let host_port = node.get_host_port_ipv4(9998); - let ui_host_port: u16 = node.get_host_port_ipv4(5000); + let container = get_container(ledger_device_model.clone()).await; + let host_port = container.get_host_port_ipv4(9998).await.unwrap(); + let ui_host_port: u16 = container.get_host_port_ipv4(5000).await.unwrap(); wait_for_emulator_start_text(ui_host_port).await; let ledger = Arc::new(ledger(host_port).await); @@ -185,13 +169,10 @@ async fn test_sign_tx(ledger_device_model: String) { assert_eq!( hex::encode(response), "5c2f8eb41e11ab922800071990a25cf9713cc6e7c43e50e0780ddc4c0c6da50c784609ef14c528a12f520d8ea9343b49083f59c51e3f28af8c62b3edeaade60e"); } Err(e) => { - node.stop(); println!("{e}"); assert!(false); } }; - - node.stop(); } #[test_case("nanos".to_string() ; "when the device is NanoS")] @@ -199,11 +180,9 @@ async fn test_sign_tx(ledger_device_model: String) { #[test_case("nanosp".to_string() ; "when the device is NanoS Plus")] #[tokio::test] async fn test_sign_tx_hash_when_hash_signing_is_not_enabled(ledger_device_model: String) { - let runnable_image = get_runnable_image(ledger_device_model.clone()); - let docker = clients::Cli::default(); - let node = docker.run(runnable_image); - let host_port = node.get_host_port_ipv4(9998); - let ui_host_port: u16 = node.get_host_port_ipv4(5000); + let container = get_container(ledger_device_model.clone()).await; + let host_port = container.get_host_port_ipv4(9998).await.unwrap(); + let ui_host_port: u16 = container.get_host_port_ipv4(5000).await.unwrap(); wait_for_emulator_start_text(ui_host_port).await; let ledger = ledger(host_port).await; @@ -216,11 +195,8 @@ async fn test_sign_tx_hash_when_hash_signing_is_not_enabled(ledger_device_model: assert_eq!(msg, "Ledger APDU retcode: 0x6C66"); // this error code is SW_TX_HASH_SIGNING_MODE_NOT_ENABLED https://github.com/LedgerHQ/app-stellar/blob/develop/docs/COMMANDS.md } else { - node.stop(); panic!("Unexpected result: {:?}", result); } - - node.stop(); } #[test_case("nanos".to_string() ; "when the device is NanoS")] @@ -228,11 +204,9 @@ async fn test_sign_tx_hash_when_hash_signing_is_not_enabled(ledger_device_model: #[test_case("nanosp".to_string() ; "when the device is NanoS Plus")] #[tokio::test] async fn test_sign_tx_hash_when_hash_signing_is_enabled(ledger_device_model: String) { - let runnable_image = get_runnable_image(ledger_device_model.clone()); - let docker = clients::Cli::default(); - let node = docker.run(runnable_image); - let host_port = node.get_host_port_ipv4(9998); - let ui_host_port: u16 = node.get_host_port_ipv4(5000); + let container = get_container(ledger_device_model.clone()).await; + let host_port = container.get_host_port_ipv4(9998).await.unwrap(); + let ui_host_port: u16 = container.get_host_port_ipv4(5000).await.unwrap(); wait_for_emulator_start_text(ui_host_port).await; enable_hash_signing(ui_host_port).await; @@ -248,7 +222,6 @@ async fn test_sign_tx_hash_when_hash_signing_is_enabled(ledger_device_model: Str ) { Ok(()) => {} Err(e) => { - node.stop(); panic!("Unexpected result: {e}"); } } @@ -267,12 +240,9 @@ async fn test_sign_tx_hash_when_hash_signing_is_enabled(ledger_device_model: Str assert_eq!( hex::encode(response), "e0fa9d19f34ddd494bbb794645fc82eb5ebab29e74160f1b1d5697e749aada7c6b367236df87326b0fdc921ed39702242fc8b14414f4e0ee3e775f1fd0208101"); } Err(e) => { - node.stop(); panic!("Unexpected result: {e}"); } } - - node.stop(); } async fn click(ui_host_port: u16, url: &str) { @@ -330,23 +300,14 @@ struct EventsResponse { events: Vec, } -fn get_runnable_image(ledger_device_model: String) -> RunnableImage { - let args = Args { - ledger_device_model, - }; - let runnable_image: RunnableImage = (Speculos::new(), args).into(); - - // doing this to randomize the ports on the host so that parallel tests don't clobber each other +async fn get_container(ledger_device_model: String) -> ContainerAsync { let (tcp_port_1, tcp_port_2) = get_available_ports(2); - runnable_image - .with_mapped_port(Port { - local: tcp_port_1, - internal: 9998, - }) - .with_mapped_port(Port { - local: tcp_port_2, - internal: 5000, - }) + Speculos::new(ledger_device_model) + .with_mapped_port(tcp_port_1, ContainerPort::Tcp(9998)) + .with_mapped_port(tcp_port_2, ContainerPort::Tcp(5000)) + .start() + .await + .unwrap() } fn get_available_ports(n: usize) -> (u16, u16) { diff --git a/cmd/crates/stellar-ledger/tests/utils/speculos.rs b/cmd/crates/stellar-ledger/tests/utils/speculos.rs index caccdfc38..57439b0af 100644 --- a/cmd/crates/stellar-ledger/tests/utils/speculos.rs +++ b/cmd/crates/stellar-ledger/tests/utils/speculos.rs @@ -1,5 +1,8 @@ -use std::{collections::HashMap, path::PathBuf}; -use testcontainers::{core::WaitFor, Image, ImageArgs}; +use std::{borrow::Cow, collections::HashMap, path::PathBuf}; +use testcontainers::{ + core::{Mount, WaitFor}, + Image, +}; const NAME: &str = "docker.io/zondax/builder-zemu"; const TAG: &str = "speculos-3a3439f6b45eca7f56395673caaf434c202e7005"; @@ -24,72 +27,69 @@ impl From<&Map> for HashMap { } #[derive(Debug, Default)] -pub struct Speculos(HashMap, HashMap); +pub struct Speculos { + env: HashMap, + volumes: Vec, + cmd: String, +} + const DEFAULT_APP_PATH: &str = "/project/app/bin"; impl Speculos { #[allow(dead_code)] - pub fn new() -> Self { + pub fn new(ledger_device_model: String) -> Self { #[allow(unused_mut)] let apps_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("tests") .join("test_fixtures") .join("apps"); - let mut volumes = HashMap::new(); - volumes.insert( - apps_dir.to_str().unwrap().to_string(), - DEFAULT_APP_PATH.to_string(), - ); - Speculos(ENV.into(), volumes) - } -} - -#[derive(Debug, Clone)] -pub struct Args { - pub ledger_device_model: String, -} - -impl Default for Args { - fn default() -> Self { - Self { - ledger_device_model: "nanos".to_string(), + let volumes = vec![Mount::bind_mount( + apps_dir.to_str().unwrap(), + DEFAULT_APP_PATH, + )]; + let cmd = Self::get_cmd(ledger_device_model); + Speculos { + env: ENV.into(), + volumes, + cmd, } } -} -impl ImageArgs for Args { - fn into_iterator(self) -> Box> { - let device_model = self.ledger_device_model.clone(); + fn get_cmd(ledger_device_model: String) -> String { + let device_model = ledger_device_model.clone(); let container_elf_path = match device_model.as_str() { "nanos" => format!("{DEFAULT_APP_PATH}/stellarNanoSApp.elf"), "nanosp" => format!("{DEFAULT_APP_PATH}/stellarNanoSPApp.elf"), "nanox" => format!("{DEFAULT_APP_PATH}/stellarNanoXApp.elf"), _ => panic!("Unsupported device model"), }; - let command_string = format!("/home/zondax/speculos/speculos.py --log-level speculos:DEBUG --color JADE_GREEN --display headless -s {TEST_SEED_PHRASE} -m {device_model} {container_elf_path}"); - Box::new(vec![command_string].into_iter()) + format!("/home/zondax/speculos/speculos.py --log-level speculos:DEBUG --color JADE_GREEN --display headless -s {TEST_SEED_PHRASE} -m {device_model} {container_elf_path}") } } impl Image for Speculos { - type Args = Args; - - fn name(&self) -> String { - NAME.to_owned() + fn name(&self) -> &str { + NAME } - fn tag(&self) -> String { - TAG.to_owned() + fn tag(&self) -> &str { + TAG } fn ready_conditions(&self) -> Vec { vec![WaitFor::message_on_stdout("HTTP proxy started...")] } - fn env_vars(&self) -> Box + '_> { - Box::new(self.0.iter()) + fn env_vars( + &self, + ) -> impl IntoIterator>, impl Into>)> { + self.env.clone().into_iter().collect::>() } - fn volumes(&self) -> Box + '_> { - Box::new(self.1.iter()) + fn mounts(&self) -> impl IntoIterator { + self.volumes.iter() } -} + + fn cmd(&self) -> impl IntoIterator>> { + vec![self.cmd.clone()].into_iter() + } +} \ No newline at end of file