Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use smithy-bindgen to gen interface, re-export as 'testing' #24

Merged
merged 6 commits into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions wasmcloud-test-util/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "wasmcloud-test-util"
version = "0.6.4"
version = "0.6.5"
edition = "2021"
authors = [ "wasmcloud Team" ]
license = "Apache-2.0"
Expand All @@ -10,8 +10,9 @@ repository = "https://github.com/wasmcloud/wasmcloud-test"
readme = "README.md"

[dependencies]
wasmcloud-interface-testing = "0.7.1"
wasmbus-rpc = "0.11.2"
smithy-bindgen = { git="https://github.com/wasmcloud/weld", branch="feat/smithy-bindgen" }
wasmbus-rpc = { git="https://github.com/wasmcloud/weld", rev="4faec462d1dd41efbfe95c9e2d2061a9a40f2fcc", features = [ "otel" ] }
serde_bytes = "0.11"
regex = "1"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
Expand Down
2 changes: 1 addition & 1 deletion wasmcloud-test-util/src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//! cli utilities
use std::io::Write;

use crate::testing::TestResult;
use serde::Deserialize;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
use wasmcloud_interface_testing::TestResult;

// structure for deserializing error results
#[derive(Deserialize)]
Expand Down
65 changes: 62 additions & 3 deletions wasmcloud-test-util/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,72 @@ pub mod provider_test;
#[cfg(not(target_arch = "wasm32"))]
pub mod cli;

// re-export testing interface
pub use wasmcloud_interface_testing as testing;

// re-export regex and nkeys
pub use nkeys;
pub use regex;

pub mod testing {

smithy_bindgen::smithy_bindgen!("testing/testing.smithy", "org.wasmcloud.interface.testing");

// after sdk is split we won't have to duplicate this code
impl Default for TestOptions {
fn default() -> TestOptions {
TestOptions {
patterns: vec![".*".to_string()],
options: std::collections::HashMap::default(),
}
}
}

/// A NamedResult, generated inside a `run_selected!`` or `run_selected_spawn!`` macro,
/// contains a tuple of a test case name and its result. The implementation of From here
/// makes it easy to use the `into()` function to turn the NamedResult into a TestResult.
pub type NamedResult<'name, T> = (&'name str, RpcResult<T>);
stevelr marked this conversation as resolved.
Show resolved Hide resolved
stevelr marked this conversation as resolved.
Show resolved Hide resolved

// convert empty RpcResult into a testResult
impl<'name, T: Serialize> From<NamedResult<'name, T>> for TestResult {
fn from(name_res: NamedResult<'name, T>) -> TestResult {
let test_case_name = name_res.0.to_string();
let test_case_result = name_res.1;
match test_case_result {
Ok(res) => {
// test passed. Serialize the data to json
let data = match serde_json::to_vec(&res) {
Ok(v) => serde_json::to_vec(&serde_json::json!({ "data": v }))
.unwrap_or_default(),
// if serialization of data fails, it doesn't change
// the test result, but serialization errors should be logged.
// Logging requires us to have logging set up, but since we might be running as an actor,
// and we can't force the user to add a logging dependency and set up logging,
// so there isn't much we can do here. Not even println!().
Err(_) => b"".to_vec(),
};
TestResult {
name: test_case_name,
passed: true,
snap_data: Some(data),
}
}
Err(e) => {
// test failed: generate an error message
let data = serde_json::to_vec(&serde_json::json!(
{
"error": e.to_string(),
}
))
.ok();
TestResult {
name: test_case_name,
stevelr marked this conversation as resolved.
Show resolved Hide resolved
passed: false,
snap_data: data,
}
}
}
}
}
}

// these macros are supported on all build targets (wasm32 et. al.)

/// check that the two expressions are equal, returning RpcError if they are not
Expand Down
55 changes: 35 additions & 20 deletions wasmcloud-test-util/src/provider_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@ use async_trait::async_trait;
use futures::future::BoxFuture;
use nkeys::{KeyPair, KeyPairType};
use serde::Serialize;
use std::{fs, io::Write, ops::Deref, path::PathBuf, sync::Arc};
use std::{
fs,
io::Write,
ops::Deref,
path::{Path, PathBuf},
process,
sync::{Arc, Mutex},
time::Duration,
};
use tokio::sync::OnceCell;
use toml::value::Value as TomlValue;
use wasmbus_rpc::{
Expand Down Expand Up @@ -51,7 +59,7 @@ fn to_value_map(data: &toml::map::Map<String, TomlValue>) -> RpcResult<SimpleVal
// copy the entire map as base64-encoded json with value "config_b64"
let json = serde_json::to_string(data)
.map_err(|e| RpcError::Ser(format!("invalid 'values' map: {}", e)))?;
let b64 = base64::encode_config(&json, base64::STANDARD_NO_PAD);
let b64 = base64::encode_config(json, base64::STANDARD_NO_PAD);
map.insert("config_b64".to_string(), b64);
Ok(map)
}
Expand All @@ -71,18 +79,23 @@ struct ShutdownMessage {
/// the provider will exit
#[derive(Debug)]
pub struct ProviderProcess {
pub file: std::fs::File,
pub file: fs::File,
pub host_data: HostData,
pub actor_id: String,
pub path: PathBuf,
pub proc: std::process::Child,
pub proc: process::Child,
pub config: TomlMap,
pub nats_client: async_nats::Client,
pub rpc_client: RpcClient,
pub timeout_ms: std::sync::Mutex<u64>,
pub timeout_ms: Mutex<u64>,
}

impl ProviderProcess {
/// Returns the nats topic used by a mock actor
pub fn mock_actor_rpc_topic(&self) -> String {
wasmbus_rpc::rpc_client::rpc_topic(&self.origin(), &self.host_data.lattice_rpc_prefix)
}

/// generate the `origin` field for an Invocation. To the receiving provider,
/// the origin field looks like an actor
pub fn origin(&self) -> WasmCloudEntity {
Expand Down Expand Up @@ -210,7 +223,7 @@ impl ProviderProcess {
if !resp.is_empty() {
eprintln!("shutdown response: {}", String::from_utf8_lossy(&resp));
}
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
tokio::time::sleep(Duration::from_secs(2)).await;
Ok(())
}
}
Expand All @@ -222,13 +235,13 @@ impl Transport for Provider {
_ctx: &Context,
message: Message<'_>,
_opts: Option<SendOpts>,
) -> std::result::Result<Vec<u8>, RpcError> {
) -> Result<Vec<u8>, RpcError> {
self.inner.send_rpc(message).await
}

/// sets the time period for an expected response to rpc messages,
/// after which an RpcError::Timeout will occur.
fn set_timeout(&self, interval: std::time::Duration) {
fn set_timeout(&self, interval: Duration) {
let lock = self.timeout_ms.try_lock();
if let Ok(mut rg) = lock {
*rg = interval.as_millis() as u64
Expand Down Expand Up @@ -296,7 +309,7 @@ pub fn load_config() -> Result<TomlMap, RpcError> {
/// or in the config file as "par_file"
pub async fn start_provider_test(
config: TomlMap,
exe_path: &std::path::Path,
exe_path: &Path,
ld: LinkDefinition,
) -> Result<Provider, RpcError> {
let exe_file = fs::File::open(exe_path)?;
Expand Down Expand Up @@ -343,9 +356,9 @@ pub async fn start_provider_test(
encoded.push_str("\r\n");

// provider's stdout is piped through our stdout
let mut child_proc = std::process::Command::new(exe_path)
.stdout(std::process::Stdio::piped())
.stdin(std::process::Stdio::piped())
let mut child_proc = process::Command::new(exe_path)
.stdout(process::Stdio::piped())
.stdin(process::Stdio::piped())
.env("RUST_LOG", &log_level)
.env("RUST_BACKTRACE", enable_backtrace)
.spawn()
Expand Down Expand Up @@ -379,13 +392,13 @@ pub async fn start_provider_test(
rpc_client: RpcClient::new(
nats_client,
host_key.public_key(),
Some(std::time::Duration::from_millis(
Some(Duration::from_millis(
host_data.default_rpc_timeout_ms.unwrap_or(2000),
)),
Arc::new(host_key),
),
host_data,
timeout_ms: std::sync::Mutex::new(DEFAULT_RPC_TIMEOUT_MILLIS),
timeout_ms: Mutex::new(DEFAULT_RPC_TIMEOUT_MILLIS),
}),
})
}
Expand All @@ -397,14 +410,15 @@ pub async fn start_provider_test(
/// This is like the `run_selected!` macro, except that it spawns
/// a thread for running the test case, so it can handle panics
/// (and failed assertions, which panic).
/// Users of this macro must have a the testing interface package in scope by using `use wasmcloud_test_util::testing`;
stevelr marked this conversation as resolved.
Show resolved Hide resolved
#[macro_export]
macro_rules! run_selected_spawn {
( $opt:expr, $($tname:ident),* $(,)? ) => {{
let mut unique = std::collections::BTreeSet::new();
let handle = tokio::runtime::Handle::current();
let all_tests = vec![".*".to_string()];
let pats : &Vec<String> = $opt.patterns.as_ref();
let mut results: Vec<TestResult> = Vec::new();
let mut results = Vec::new();

// Each test case regex (pats) is checked against all test names (tname).
// This would be simpler to use a RegexSet, but then the tests would
Expand Down Expand Up @@ -432,7 +446,7 @@ macro_rules! run_selected_spawn {
$tname(&opts).await
}
).await;
let tr:TestResult = match join {
let tr: testing::TestResult = match join {
Ok(res) => (name, res).into(),
Err(e) => (name, Err::<(),RpcError>(
RpcError::Other(format!("join error: {}", e.to_string()))
Expand Down Expand Up @@ -460,10 +474,11 @@ macro_rules! run_selected_spawn {
// all test cases in the current thread (async executor).
// The reason I had put the spawn in was to catch panics from assert
// calls that fail.
/// Users of this macro must have a the testing interface package in scope by using `use wasmcloud_test_util::testing`;
pub async fn run_tests(
tests: Vec<(&'static str, TestFunc)>,
) -> std::result::Result<Vec<TestResult>, Box<dyn std::error::Error>> {
let mut results: Vec<TestResult> = Vec::new();
) -> Result<Vec<TestResult>, Box<dyn std::error::Error>> {
let mut results = Vec::new();
let handle = tokio::runtime::Handle::current();
for (name, tfunc) in tests.into_iter() {
let rc: RpcResult<()> = handle.spawn(tfunc()).await?;
Expand Down Expand Up @@ -543,14 +558,14 @@ pub async fn load_provider() -> Result<Provider, RpcError> {
}
None => DEFAULT_START_DELAY_SEC,
};
tokio::time::sleep(std::time::Duration::from_secs(delay_time_sec)).await;
tokio::time::sleep(Duration::from_secs(delay_time_sec)).await;

// optionally, allow extra time to handle put_link
if let Some(n) = prov.config.get("link_delay_sec") {
if let Some(n) = n.as_integer() {
if n > 0 {
eprintln!("Pausing {} secs after put_link", n);
tokio::time::sleep(std::time::Duration::from_secs(n as u64)).await;
tokio::time::sleep(Duration::from_secs(n as u64)).await;
}
} else {
return Err(RpcError::InvalidParameter(format!(
Expand Down