diff --git a/.github/workflows/ledger-emulator.yml b/.github/workflows/ledger-emulator.yml index 87df86b94..2bf371b63 100644 --- a/.github/workflows/ledger-emulator.yml +++ b/.github/workflows/ledger-emulator.yml @@ -25,4 +25,4 @@ jobs: run: | sudo apt install -y libudev-dev - run: | - cargo test --manifest-path cmd/crates/stellar-ledger/Cargo.toml --features "emulator-tests" \ No newline at end of file + cargo test --manifest-path cmd/crates/stellar-ledger/Cargo.toml --features "emulator-tests" -- --nocapture \ No newline at end of file diff --git a/.github/workflows/temp-ledger-emulator.yml b/.github/workflows/temp-ledger-emulator.yml new file mode 100644 index 000000000..9a08ae5d8 --- /dev/null +++ b/.github/workflows/temp-ledger-emulator.yml @@ -0,0 +1,30 @@ +name: Temporary Ledger Emulator Tests +on: + push: + branches: + - AhaLabs:fix/troubleshooting-emulator-tests + pull_request_target: + branches: + - main + +concurrency: + group: ${{ github.workflow }}-${{ github.ref_protected == 'true' && github.sha || github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +jobs: + emulator-tests: + runs-on: ubuntu-latest + env: + CI_TESTS: true + steps: + - uses: actions/checkout@v3 + - uses: stellar/actions/rust-cache@main + - name: install libudev-dev + run: | + sudo apt install -y libudev-dev + - run: | + cargo test --manifest-path cmd/crates/stellar-ledger/Cargo.toml --features "emulator-tests" -- --nocapture diff --git a/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs b/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs index d22830089..4483296bb 100644 --- a/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs +++ b/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs @@ -4,6 +4,7 @@ use soroban_env_host::xdr::{self, Operation, OperationBody, Uint256}; use soroban_env_host::xdr::{Hash, Transaction}; use std::vec; +use std::net::TcpListener; use stellar_ledger::hd_path::HdPath; use stellar_ledger::{Blob, Error, LedgerSigner}; @@ -14,7 +15,7 @@ use stellar_xdr::curr::{ Memo, MuxedAccount, PaymentOp, Preconditions, SequenceNumber, TransactionExt, }; -use testcontainers::clients; +use testcontainers::{clients, core::Port, RunnableImage}; use tokio::time::sleep; pub const TEST_NETWORK_PASSPHRASE: &[u8] = b"Test SDF Network ; September 2015"; @@ -44,11 +45,9 @@ use test_helpers::test::{ #[test_case("nanosp".to_string() ; "when the device is NanoS Plus")] #[tokio::test] async fn test_get_public_key(ledger_device_model: String) { - let args = Args { - ledger_device_model, - }; + let runnable_image = get_runnable_image(ledger_device_model.clone()); let docker = clients::Cli::default(); - let node = docker.run((Speculos::new(), args)); + 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); wait_for_emulator_start_text(ui_host_port).await; @@ -78,11 +77,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 args = Args { - ledger_device_model, - }; + let runnable_image = get_runnable_image(ledger_device_model.clone()); let docker = clients::Cli::default(); - let node = docker.run((Speculos::new(), args)); + 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); wait_for_emulator_start_text(ui_host_port).await; @@ -108,11 +105,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 args = Args { - ledger_device_model, - }; + let runnable_image = get_runnable_image(ledger_device_model.clone()); let docker = clients::Cli::default(); - let node = docker.run((Speculos::new(), args.clone())); + 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); wait_for_emulator_start_text(ui_host_port).await; @@ -175,7 +170,7 @@ async fn test_sign_tx(ledger_device_model: String) { let ledger = Arc::clone(&ledger); async move { ledger.sign_transaction(path, tx, test_network_hash()).await } }); - let approve = tokio::task::spawn(approve_tx_signature(ui_host_port, args.ledger_device_model)); + let approve = tokio::task::spawn(approve_tx_signature(ui_host_port, ledger_device_model)); let result = sign.await.unwrap(); let _ = approve.await.unwrap(); @@ -199,12 +194,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 args = Args { - ledger_device_model, - }; - + let runnable_image = get_runnable_image(ledger_device_model.clone()); let docker = clients::Cli::default(); - let node = docker.run((Speculos::new(), args)); + 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); wait_for_emulator_start_text(ui_host_port).await; @@ -231,11 +223,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 args = Args { - ledger_device_model, - }; + let runnable_image = get_runnable_image(ledger_device_model.clone()); let docker = clients::Cli::default(); - let node = docker.run((Speculos::new(), args.clone())); + 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); @@ -262,10 +252,7 @@ async fn test_sign_tx_hash_when_hash_signing_is_enabled(ledger_device_model: Str let ledger = Arc::clone(&ledger); async move { ledger.sign_transaction_hash(path, &test_hash).await } }); - let approve = tokio::task::spawn(approve_tx_hash_signature( - ui_host_port, - args.ledger_device_model, - )); + let approve = tokio::task::spawn(approve_tx_hash_signature(ui_host_port, ledger_device_model)); let response = sign.await.unwrap(); let _ = approve.await.unwrap(); @@ -338,6 +325,37 @@ 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 + let (tcp_port_1, tcp_port_2) = get_available_ports(); + runnable_image + .with_mapped_port(Port { + local: tcp_port_1, + internal: 9998, + }) + .with_mapped_port(Port { + local: tcp_port_2, + internal: 5000, + }) +} + +fn get_available_ports() -> (u16, u16) { + let listener1 = TcpListener::bind("127.0.0.1:0").unwrap(); + let listener2 = TcpListener::bind("127.0.0.1:0").unwrap(); + println!("listener1: {:?}", listener1); + let port_1 = listener1.local_addr().unwrap().port(); + let port_2 = listener2.local_addr().unwrap().port(); + drop(listener1); + drop(listener2); + + (port_1, port_2) +} + fn get_http_transport(host: &str, port: u16) -> Result { Ok(EmulatorHttpTransport::new(host, port)) } @@ -379,9 +397,11 @@ async fn get_emulator_events_with_retries( return resp.events; } Err(e) => { - println!("this many retries: {retries}"); + println!("Retry count: {retries}"); + println!("Wait time: {wait_time:?}"); retries += 1; if retries >= max_retries { + println!("Exeeded max retries"); panic!("Failed to get emulator events: {e}"); } sleep(wait_time).await;