Pass module interfaces as args in marine-test macro (#54)

This commit is contained in:
Valery Antopol 2021-09-01 20:34:35 +03:00 committed by GitHub
parent 84d5c2af66
commit 2ee2cf3a2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 1262 additions and 225 deletions

130
Cargo.lock generated
View File

@ -13,9 +13,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.42"
version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486"
checksum = "28ae2b3dec75a406790005a200b1bd89785afc02517a00ca99ecfe093ee9e6cf"
[[package]]
name = "arrayref"
@ -57,9 +57,9 @@ dependencies = [
[[package]]
name = "bitflags"
version = "1.2.1"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "blake3"
@ -101,9 +101,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.0.69"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2"
checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0"
[[package]]
name = "cfg-if"
@ -278,9 +278,9 @@ dependencies = [
[[package]]
name = "ctor"
version = "0.1.20"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d"
checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa"
dependencies = [
"quote",
"syn",
@ -425,13 +425,14 @@ dependencies = [
[[package]]
name = "fluence-it-types"
version = "0.3.0"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5006d09553345421af5dd2334cc945fc34dc2a73d7c1ed842a39a3803699619d"
checksum = "047f670b4807cab8872550a607b1515daff08b3e3bb7576ce8f45971fd811a4e"
dependencies = [
"it-to-bytes",
"nom",
"serde",
"variant_count",
"wast",
]
@ -638,9 +639,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "0.4.7"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "lazy_static"
@ -669,9 +670,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.98"
version = "0.2.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790"
checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21"
[[package]]
name = "lock_api"
@ -705,7 +706,7 @@ checksum = "e62f29b16bbdb0763a04f8561c954624ee9cd9f558af4e67b95eb00880da11ec"
dependencies = [
"cargo_toml",
"it-lilo",
"marine-it-parser 0.6.5",
"marine-it-parser",
"marine-macro-impl 0.6.10",
"once_cell",
"serde",
@ -715,16 +716,6 @@ dependencies = [
"wasmer-interface-types-fl",
]
[[package]]
name = "marine-it-interfaces"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97c533789e72808630cc35b5d14d286382236282525f82ddce8fb47eb9d659e8"
dependencies = [
"multimap",
"wasmer-interface-types-fl",
]
[[package]]
name = "marine-it-interfaces"
version = "0.4.0"
@ -735,23 +726,6 @@ dependencies = [
"wasmer-interface-types-fl",
]
[[package]]
name = "marine-it-parser"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e59c7067a18b9e4aebe67bee033638fae97d6fe4fb00f70f9a509eb5d03d1c5d"
dependencies = [
"anyhow",
"marine-it-interfaces 0.3.0",
"nom",
"semver 0.11.0",
"serde",
"thiserror",
"walrus",
"wasmer-interface-types-fl",
"wasmer-runtime-core-fl",
]
[[package]]
name = "marine-it-parser"
version = "0.6.5"
@ -760,7 +734,7 @@ checksum = "19a6606e472587b2e7b759b16d037a4ea951facc2a6650f668f22403978c2442"
dependencies = [
"anyhow",
"itertools 0.10.1",
"marine-it-interfaces 0.4.0",
"marine-it-interfaces",
"marine-module-interface",
"nom",
"semver 0.11.0",
@ -805,6 +779,7 @@ dependencies = [
name = "marine-macro-impl"
version = "0.6.11"
dependencies = [
"marine-macro-testing-utils",
"pretty_assertions",
"proc-macro2",
"quote",
@ -814,6 +789,15 @@ dependencies = [
"uuid",
]
[[package]]
name = "marine-macro-testing-utils"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "marine-module-info-parser"
version = "0.2.0"
@ -838,7 +822,7 @@ checksum = "d8a5936273bebb523ed169863282dbc19fc66bb983c7031c5b8b0556584f2401"
dependencies = [
"anyhow",
"itertools 0.10.1",
"marine-it-interfaces 0.4.0",
"marine-it-interfaces",
"nom",
"semver 0.11.0",
"serde",
@ -895,12 +879,13 @@ dependencies = [
[[package]]
name = "marine-rs-sdk-test"
version = "0.1.11"
version = "0.2.0"
dependencies = [
"fluence-app-service",
"marine-test-macro",
"serde",
"serde_json",
"trybuild",
"uuid",
]
@ -915,8 +900,8 @@ dependencies = [
"it-lilo",
"log",
"marine-it-generator",
"marine-it-interfaces 0.4.0",
"marine-it-parser 0.6.5",
"marine-it-interfaces",
"marine-it-parser",
"marine-module-info-parser",
"marine-module-interface",
"marine-utils",
@ -936,7 +921,7 @@ dependencies = [
[[package]]
name = "marine-test-macro"
version = "0.1.11"
version = "0.2.0"
dependencies = [
"marine-test-macro-impl",
"proc-macro-error",
@ -947,14 +932,17 @@ dependencies = [
[[package]]
name = "marine-test-macro-impl"
version = "0.1.11"
version = "0.2.0"
dependencies = [
"darling",
"fluence-app-service",
"marine-it-parser 0.5.0",
"itertools 0.10.1",
"marine-it-parser",
"marine-macro-testing-utils",
"proc-macro-error",
"proc-macro2",
"quote",
"static_assertions",
"syn",
"thiserror",
]
@ -985,9 +973,9 @@ checksum = "8dc5838acba84ce4d802d672afd0814fae0ae7098021ae5b06d975e70d09f812"
[[package]]
name = "memchr"
version = "2.4.0"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "memmap"
@ -1177,9 +1165,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.28"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612"
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
dependencies = [
"unicode-xid",
]
@ -1347,9 +1335,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.66"
version = "1.0.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "336b10da19a12ad094b59d870ebde26a45402e5b470add4b5fd03c5048a32127"
checksum = "a7f9e390c27c3c0ce8bc5d725f6e4d30a29d26659494aa4b17535f7522c5c950"
dependencies = [
"itoa",
"ryu",
@ -1358,9 +1346,9 @@ dependencies = [
[[package]]
name = "simple_logger"
version = "1.12.1"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28475b72d7e5da6ad80c6d284aa557821bb4f7f788b9f607632635e3783a8608"
checksum = "b7de33c687404ec3045d4a0d437580455257c0436f858d702f244e7d652f9f07"
dependencies = [
"atty",
"chrono",
@ -1395,9 +1383,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "syn"
version = "1.0.74"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c"
checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7"
dependencies = [
"proc-macro2",
"quote",
@ -1421,18 +1409,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.26"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2"
checksum = "283d5230e63df9608ac7d9691adc1dfb6e701225436eb64d0b9a7f0a5a04f6ec"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.26"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745"
checksum = "fa3884228611f5cd3608e2d409bf7dce832e4eb3135e3f11addbd7e41bd68e71"
dependencies = [
"proc-macro2",
"quote",
@ -1461,9 +1449,9 @@ dependencies = [
[[package]]
name = "trybuild"
version = "1.0.43"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c02c413315329fc96167f922b46fd0caa3a43f4697b7a7896b183c7142635832"
checksum = "5bdaf2a1d317f3d58b44b31c7f6436b9b9acafe7bddfeace50897c2b804d7792"
dependencies = [
"glob",
"lazy_static",
@ -1530,6 +1518,16 @@ dependencies = [
"getrandom 0.2.3",
]
[[package]]
name = "variant_count"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae2faf80ac463422992abf4de234731279c058aaf33171ca70277c98406b124"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "version_check"
version = "0.9.3"

View File

@ -0,0 +1,21 @@
[package]
name = "marine-macro-testing-utils"
version = "0.1.0"
edition = "2018"
description = "Some functions for testing procedural macros"
documentation = "https://docs.rs/fluence/marine-macro-testing-utils"
repository = "https://github.com/fluencelabs/marine-rs-sdk/tree/master/crates/marine-macro-testing-utils"
authors = ["Fluence Labs"]
keywords = ["fluence", "marine", "sdk", "webassembly"]
categories = ["development-tools::testing"]
license = "Apache-2.0"
[lib]
path = "src/lib.rs"
crate-type = ["rlib"]
doctest = false
[dependencies]
quote = "1.0.9"
proc-macro2 = "1.0.26"
syn = { version = '1.0.64', features = ['full'] }

View File

@ -0,0 +1,44 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use std::io::Read;
use std::path::Path;
pub fn stream_from_file<P>(path: P) -> proc_macro2::TokenStream
where
P: AsRef<Path>,
{
let items = items_from_file(path);
quote::quote! { #(#items)* }
}
pub fn items_from_file<P>(path: P) -> Vec<syn::Item>
where
P: AsRef<Path>,
{
let mut file = std::fs::File::open(path).expect("Unable to open file");
let mut src = String::new();
file.read_to_string(&mut src).expect("Unable to read file");
let token_file = syn::parse_file(&src).expect("Unable to parse file");
token_file.items
}
pub fn to_syn_item(token_stream: proc_macro2::TokenStream) -> Vec<syn::Item> {
let file: syn::File = syn::parse2(token_stream).expect("token stream should be parsed");
file.items
}

View File

@ -23,3 +23,4 @@ uuid = { version = "0.8.2", features = ["v4"] }
[dev-dependencies]
pretty_assertions = "0.7.1"
marine-macro-testing-utils = {path = "../macro-testing-utils"}

View File

@ -16,7 +16,8 @@
use marine_macro_impl::marine;
use std::io::Read;
use marine_macro_testing_utils::{items_from_file, stream_from_file, to_syn_item};
use std::path::Path;
pub fn test_marine_token_streams<FP, EP>(marine_path: FP, expanded_path: EP) -> bool
@ -34,29 +35,3 @@ where
marine_item == expanded_item
}
fn stream_from_file<P>(path: P) -> proc_macro2::TokenStream
where
P: AsRef<Path>,
{
let items = items_from_file(path);
quote::quote! { #(#items)* }
}
fn items_from_file<P>(path: P) -> Vec<syn::Item>
where
P: AsRef<Path>,
{
let mut file = std::fs::File::open(path).expect("Unable to open file");
let mut src = String::new();
file.read_to_string(&mut src).expect("Unable to read file");
let token_file = syn::parse_file(&src).expect("Unable to parse file");
token_file.items
}
fn to_syn_item(token_stream: proc_macro2::TokenStream) -> Vec<syn::Item> {
let file: syn::File = syn::parse2(token_stream).expect("token stream should be parsed");
file.items
}

View File

@ -1,6 +1,6 @@
[package]
name = "marine-test-macro-impl"
version = "0.1.11" # remember to update html_root_url
version = "0.2.0" # remember to update html_root_url
edition = "2018"
description = "Implementation of the `#[marine_test]` macro"
documentation = "https://docs.rs/fluence/marine-test-macro-impl"
@ -15,11 +15,15 @@ all-features = true
[dependencies]
fluence-app-service = { version = "0.9.0", features = ["raw-module-api"] }
marine-it-parser = "0.5.0"
marine-it-parser = "0.6.5"
itertools = "0.10.1"
darling = "0.12.2"
quote = "1.0.9"
proc-macro2 = "1.0.26"
proc-macro-error = { version = "1.0.4", default-features = false }
syn = { version = '1.0.64', features = ['full'] }
thiserror = "1.0.24"
static_assertions = "1.1.0"
[dev-dependencies]
marine-macro-testing-utils = {path = "../macro-testing-utils"}

View File

@ -50,6 +50,12 @@ pub enum TestGeneratorError {
#[error("{0} is invalid UTF8 path")]
InvalidUTF8Path(PathBuf),
#[error(r#"a "self" argument found and it is not supported in test function"#)]
UnexpectedSelf,
#[error("Duplicate module: {0}")]
DuplicateModuleName(String),
}
#[derive(Debug, ThisError)]

View File

@ -17,19 +17,19 @@
use crate::TResult;
use fluence_app_service::TomlAppServiceConfig;
use marine_it_parser::module_raw_interface;
use marine_it_parser::interface::MModuleInterface;
use marine_it_parser::module_it_interface;
use marine_it_parser::it_interface::IModuleInterface;
use std::path::PathBuf;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) struct Module<'m> {
pub name: &'m str,
pub interface: MModuleInterface,
pub interface: IModuleInterface,
}
impl<'m> Module<'m> {
fn new(name: &'m str, interface: MModuleInterface) -> Self {
fn new(name: &'m str, interface: IModuleInterface) -> Self {
Self { name, interface }
}
}
@ -43,9 +43,7 @@ pub(super) fn collect_modules(
module_paths
.into_iter()
.map(|(name, path)| {
module_raw_interface(path).map(|interface| Module::new(name, interface))
})
.map(|(name, path)| module_it_interface(path).map(|interface| Module::new(name, interface)))
.collect::<Result<Vec<_>, _>>()
.map_err(Into::into)
}

View File

@ -23,9 +23,11 @@ use crate::marine_test::config_utils;
use fluence_app_service::TomlAppServiceConfig;
use proc_macro2::TokenStream;
use quote::quote;
use quote::ToTokens;
use std::path::Path;
use std::path::PathBuf;
use syn::FnArg;
/// Generates glue code for tests.
/// F.e. for this test for the greeting service
@ -128,30 +130,40 @@ pub(super) fn generate_test_glue_code(
let modules_dir = file_path.join(modules_dir);
let module_interfaces =
marine_test::config_utils::collect_modules(&marine_config, modules_dir)?;
let linked_modules = marine_test::modules_linker::link_modules(&module_interfaces)?;
let module_definitions =
marine_test::module_generator::generate_module_definitions(module_interfaces.iter())?;
let module_iter = module_interfaces.iter().map(|module| module.name);
let module_ctors = generate_module_ctors(module_iter)?;
let module_definitions = marine_test::module_generator::generate_module_definitions(
module_interfaces.iter(),
&linked_modules,
)?;
let original_block = func_item.block;
let signature = func_item.sig;
let name = &signature.ident;
let inputs = &signature.inputs;
let arg_names = generate_arg_names(inputs.iter())?;
let module_ctors = generate_module_ctors(inputs.iter())?;
let glue_code = quote! {
#[test]
#signature {
fn #name() {
// definitions for wasm modules specified in config
#(#module_definitions)*
pub mod marine_test_env {
#(#module_definitions)*
}
// AppService constructor and instantiation to implicit `marine` variable
#app_service_ctor
// constructors of all modules of the tested service
#(#module_ctors)*
// original test function as is
#original_block
fn test_func(#inputs) {
#(let mut #arg_names = #arg_names;)*
// original test function as is
#original_block
}
test_func(#(#arg_names,)*)
}
};
@ -181,9 +193,7 @@ fn generate_app_service_ctor(config_path: &str, modules_dir: &Path) -> TResult<T
}
let (file_path_, remainder) = match file_path.next_back().and_then(|p| match p {
std::path::Component::Normal(_) | std::path::Component::CurDir | std::path::Component::ParentDir => {
Some((file_path, p))
}
std::path::Component::Normal(_) | std::path::Component::CurDir | std::path::Component::ParentDir => Some((file_path, p)),
_ => None,
}) {
Some(t) => t,
@ -218,21 +228,32 @@ fn generate_app_service_ctor(config_path: &str, modules_dir: &Path) -> TResult<T
Ok(service_ctor)
}
fn generate_module_ctors<'n>(
module_names: impl ExactSizeIterator<Item = &'n str>,
fn generate_module_ctors<'inputs>(
inputs: impl Iterator<Item = &'inputs FnArg>,
) -> TResult<Vec<TokenStream>> {
module_names
.map(|name| -> TResult<_> {
// TODO: optimize these two call because they are called twice for each module name
// and internally allocate memory in format call.
let module_name = marine_test::utils::generate_structs_module_ident(&name)?;
let struct_name = marine_test::utils::generate_struct_name(&name)?;
let name_for_user = marine_test::utils::new_ident(&name)?;
let module_ctor =
quote! { let mut #name_for_user = #module_name::#struct_name::new(marine.clone()); };
Ok(module_ctor)
inputs
.map(|x| -> TResult<_> {
match x {
FnArg::Receiver(_) => Err(TestGeneratorError::UnexpectedSelf),
FnArg::Typed(x) => {
let pat = &x.pat;
let ty = &x.ty;
Ok(quote! {let mut #pat = #ty::new(marine.clone());})
}
}
})
.collect::<TResult<_>>()
}
fn generate_arg_names<'inputs>(
inputs: impl Iterator<Item = &'inputs FnArg>,
) -> TResult<Vec<TokenStream>> {
inputs
.map(|x| -> TResult<_> {
match x {
FnArg::Receiver(_) => Err(TestGeneratorError::UnexpectedSelf),
FnArg::Typed(x) => Ok(x.pat.to_token_stream()),
}
})
.collect::<TResult<_>>()
}

View File

@ -19,5 +19,6 @@ mod marine_test_impl;
mod glue_code_generator;
mod module_generator;
mod utils;
mod modules_linker;
pub use marine_test_impl::marine_test_impl;

View File

@ -21,6 +21,7 @@ mod record_type_generator;
use crate::marine_test::utils;
use crate::marine_test::config_utils::Module;
use crate::TResult;
use crate::marine_test::modules_linker::{LinkedModules, LinkedModule};
use proc_macro2::TokenStream;
use quote::quote;
@ -56,21 +57,24 @@ use quote::quote;
///```
pub(super) fn generate_module_definitions<'i>(
modules: impl ExactSizeIterator<Item = &'i Module<'i>>,
linked_modules: &'i LinkedModules<'_>,
) -> TResult<Vec<TokenStream>> {
modules
.into_iter()
.map(generate_module_definition)
.map(|value| generate_module_definition(value, linked_modules.get(&value.name).unwrap())) // linked_modules are built from modules
.collect::<TResult<Vec<_>>>()
}
fn generate_module_definition(module: &Module<'_>) -> TResult<TokenStream> {
fn generate_module_definition(
module: &Module<'_>,
linked_module: &'_ LinkedModule<'_>,
) -> TResult<TokenStream> {
let module_name = module.name;
let module_ident = utils::generate_module_ident(module_name)?;
let structs_module_ident = utils::generate_structs_module_ident(module_name)?;
let struct_ident = utils::generate_struct_name(module_name)?;
let module_ident = utils::new_ident(module_name)?;
let struct_ident = utils::new_ident("ModuleInterface")?;
let module_interface = &module.interface;
let module_records = record_type_generator::generate_records(&module_interface.record_types)?;
let module_records = record_type_generator::generate_records(linked_module)?;
let module_functions = methods_generator::generate_module_methods(
module_name,
module_interface.function_signatures.iter(),
@ -79,26 +83,22 @@ fn generate_module_definition(module: &Module<'_>) -> TResult<TokenStream> {
let module_definition = quote! {
// it's a sort of hack: this module structure allows user to import structs by
// use module_name_structs::StructName;
pub mod #structs_module_ident {
pub use #module_ident::*;
// using marine_env_test::module_name::StructName;
pub mod #module_ident {
#(#module_records)*
pub mod #module_ident {
#(#module_records)*
pub struct #struct_ident {
marine: std::rc::Rc<std::cell::RefCell<marine_rs_sdk_test::internal::AppService>, >,
}
pub struct #struct_ident {
marine: std::rc::Rc<std::cell::RefCell<marine_rs_sdk_test::internal::AppService>>,
impl #struct_ident {
pub fn new(marine: std::rc::Rc<std::cell::RefCell<marine_rs_sdk_test::internal::AppService>, >) -> Self {
Self { marine }
}
}
impl #struct_ident {
pub fn new(marine: std::rc::Rc<std::cell::RefCell<marine_rs_sdk_test::internal::AppService>>) -> Self {
Self { marine }
}
}
impl #struct_ident {
#(#module_functions)*
}
impl #struct_ident {
#(#module_functions)*
}
}
};

View File

@ -17,27 +17,32 @@
use super::methods_generator_utils::*;
use crate::TResult;
use marine_it_parser::interface::MRecordTypes;
use marine_it_parser::interface::MFunctionSignature;
use marine_it_parser::it_interface::IFunctionSignature;
use marine_it_parser::it_interface::IRecordTypes;
use itertools::Itertools;
pub(super) fn generate_module_methods<'m, 'r>(
module_name: &str,
mut method_signatures: impl ExactSizeIterator<Item = &'m MFunctionSignature>,
records: &'r MRecordTypes,
method_signatures: impl ExactSizeIterator<Item = &'m IFunctionSignature>,
records: &'r IRecordTypes,
) -> TResult<Vec<proc_macro2::TokenStream>> {
use CallParametersSettings::*;
let methods_count = 2 * method_signatures.len();
method_signatures.try_fold::<_, _, TResult<_>>(
Vec::with_capacity(methods_count),
|mut methods, signature| {
let default_cp = generate_module_method(module_name, &signature, Default, records)?;
let user_cp = generate_module_method(module_name, &signature, UserDefined, records)?;
method_signatures
.sorted_by(|lhs, rhs| lhs.name.cmp(&rhs.name))
.try_fold::<_, _, TResult<_>>(
Vec::with_capacity(methods_count),
|mut methods, signature| {
let default_cp = generate_module_method(module_name, &signature, Default, records)?;
let user_cp =
generate_module_method(module_name, &signature, UserDefined, records)?;
methods.push(default_cp);
methods.push(user_cp);
methods.push(default_cp);
methods.push(user_cp);
Ok(methods)
},
)
Ok(methods)
},
)
}

View File

@ -18,10 +18,10 @@ use crate::marine_test::utils::new_ident;
use crate::marine_test::utils::itype_to_tokens;
use crate::TResult;
use marine_it_parser::interface::it::IType;
use marine_it_parser::interface::it::IFunctionArg;
use marine_it_parser::interface::MRecordTypes;
use marine_it_parser::interface::MFunctionSignature;
use marine_it_parser::it_interface::it::IType;
use marine_it_parser::it_interface::it::IFunctionArg;
use marine_it_parser::it_interface::IRecordTypes;
use marine_it_parser::it_interface::IFunctionSignature;
use proc_macro2::TokenStream;
use quote::quote;
@ -34,9 +34,9 @@ pub(super) enum CallParametersSettings {
pub(super) fn generate_module_method(
module_name: &str,
signature: &MFunctionSignature,
signature: &IFunctionSignature,
cp_setting: CallParametersSettings,
records: &MRecordTypes,
records: &IRecordTypes,
) -> TResult<TokenStream> {
let arguments = generate_arguments(signature.arguments.iter(), records)?;
let output_type = generate_output_type(&signature.outputs, records)?;
@ -73,8 +73,8 @@ pub(super) fn generate_module_method(
fn generate_marine_call(
module_name: &str,
cp_settings: CallParametersSettings,
method_signature: &MFunctionSignature,
records: &MRecordTypes,
method_signature: &IFunctionSignature,
records: &IRecordTypes,
) -> TResult<TokenStream> {
let args = method_signature.arguments.iter().map(|a| a.name.as_str());
let convert_arguments = generate_arguments_converter(args)?;
@ -133,7 +133,7 @@ fn generate_set_result(output_type: &Option<&IType>) -> TokenStream {
fn generate_convert_to_output(
output_type: &Option<&IType>,
records: &MRecordTypes,
records: &IRecordTypes,
) -> TResult<TokenStream> {
let result_stream = match output_type {
Some(ty) => {
@ -157,7 +157,7 @@ fn generate_ret(output_type: &Option<&IType>) -> TokenStream {
fn generate_arguments<'a, 'r>(
arguments: impl ExactSizeIterator<Item = &'a IFunctionArg>,
records: &'r MRecordTypes,
records: &'r IRecordTypes,
) -> TResult<Vec<TokenStream>> {
arguments
.map(|argument| -> TResult<_> {
@ -170,7 +170,7 @@ fn generate_arguments<'a, 'r>(
.collect::<TResult<Vec<_>>>()
}
fn generate_output_type(output_types: &[IType], records: &MRecordTypes) -> TResult<TokenStream> {
fn generate_output_type(output_types: &[IType], records: &IRecordTypes) -> TResult<TokenStream> {
let output_type = get_output_type(output_types)?;
match output_type {
None => Ok(TokenStream::new()),

View File

@ -17,35 +17,47 @@
use crate::marine_test::utils;
use crate::TResult;
use marine_it_parser::interface::it::IRecordFieldType;
use marine_it_parser::interface::MRecordTypes;
use marine_it_parser::it_interface::it::IRecordFieldType;
use marine_it_parser::it_interface::IRecordTypes;
use proc_macro2::TokenStream;
use quote::quote;
pub(super) fn generate_records(records: &MRecordTypes) -> TResult<Vec<TokenStream>> {
use std::ops::Deref;
use crate::marine_test::modules_linker::{LinkedModule, RecordEntry};
use itertools::Itertools;
records.iter().map(|(_, record)| -> TResult<_> {
let record_name_ident = utils::generate_record_name(&record.name)?;
let fields = prepare_field(record.fields.deref().iter(), records)?;
pub(super) fn generate_records(linked_module: &LinkedModule<'_>) -> TResult<Vec<TokenStream>> {
linked_module.records
.iter()
.sorted()
.map(|record| -> TResult<_> {
use RecordEntry::*;
match record {
Use(use_info) => {
let from_module_ident = utils::new_ident(use_info.from)?;
let record_name_ident = utils::new_ident(use_info.name)?;
Ok(quote! {pub use super::#from_module_ident::#record_name_ident;})
},
Declare(record) => {
let record_name_ident = utils::new_ident(&record.record_type.name)?;
let fields = prepare_field(record.record_type.fields.iter(), record.records)?;
let generated_record = quote! {
#[derive(Clone, Debug, marine_rs_sdk_test::internal::serde::Serialize, marine_rs_sdk_test::internal::serde::Deserialize)]
#[serde(crate = "marine_rs_sdk_test::internal::serde")]
pub struct #record_name_ident {
#(pub #fields),*
Ok(quote! {
#[derive(Clone, Debug, marine_rs_sdk_test::internal::serde::Serialize, marine_rs_sdk_test::internal::serde::Deserialize)]
#[serde(crate = "marine_rs_sdk_test::internal::serde")]
pub struct #record_name_ident {
#(pub #fields),*
}
})
}
}
};
Ok(generated_record)
}
).collect::<TResult<Vec<_>>>()
})
.collect::<TResult<Vec<_>>>()
}
fn prepare_field<'f>(
fields: impl ExactSizeIterator<Item = &'f IRecordFieldType>,
records: &MRecordTypes,
records: &IRecordTypes,
) -> TResult<Vec<TokenStream>> {
fields
.map(|field| -> TResult<_> {

View File

@ -0,0 +1,198 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::marine_test::config_utils::Module;
use crate::{TResult, TestGeneratorError};
use marine_it_parser::it_interface::IRecordTypes;
use marine_it_parser::it_interface::it::{IType, IRecordType};
use itertools::zip;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::hash::Hasher;
use std::rc::Rc;
use static_assertions::const_assert;
pub(super) fn link_modules<'modules>(
modules: &'modules [Module<'_>],
) -> TResult<LinkedModules<'modules>> {
let mut all_record_types = HashMap::<IRecordTypeClosed<'_>, &str>::new();
let mut linked_modules = HashMap::<&str, LinkedModule<'_>>::new();
for module in modules {
let mut linking_module = LinkedModule::default();
for (_, record_type) in &module.interface.record_types {
let record_type_ex =
IRecordTypeClosed::new(record_type.clone(), &module.interface.record_types);
let entry = match all_record_types.get(&record_type_ex) {
Some(owner_module) => RecordEntry::Use(UseDescription {
from: owner_module,
name: &record_type.name,
}),
None => {
all_record_types.insert(record_type_ex.clone(), module.name);
RecordEntry::Declare(record_type_ex)
}
};
linking_module.records.push(entry);
}
if linked_modules.insert(module.name, linking_module).is_some() {
return Err(TestGeneratorError::DuplicateModuleName(
module.name.to_string(),
));
}
}
Ok(linked_modules)
}
struct ITypeClosed<'r> {
ty: &'r IType,
records: &'r IRecordTypes,
}
impl<'r> ITypeClosed<'r> {
fn new(ty: &'r IType, records: &'r IRecordTypes) -> Self {
Self { ty, records }
}
}
impl PartialEq for ITypeClosed<'_> {
fn eq(&self, other: &Self) -> bool {
use IType::*;
// check if new variants require special handling in the match below
#[allow(unused)]
const LAST_VERIFIED_ITYPE_SIZE: usize = 17;
const_assert!(IType::VARIANT_COUNT == LAST_VERIFIED_ITYPE_SIZE);
match (&self.ty, &other.ty) {
(Array(self_ty), Array(other_ty)) => {
ITypeClosed::new(self_ty, self.records) == ITypeClosed::new(other_ty, other.records)
}
(Record(self_record), Record(other_record)) => {
let self_record = self.records.get(self_record);
let other_record = other.records.get(other_record);
// ID from Record(ID) potentially may not be in .records, if it happens comparision is always FALSE
match (self_record, other_record) {
(None, _) => false,
(_, None) => false,
(Some(self_record), Some(other_record)) => {
IRecordTypeClosed::new(self_record.clone(), self.records)
== IRecordTypeClosed::new(other_record.clone(), other.records)
}
}
}
(lhs, rhs) if lhs == rhs => true,
_ => false,
}
}
}
#[derive(Clone)]
pub struct IRecordTypeClosed<'r> {
pub record_type: Rc<IRecordType>,
pub records: &'r IRecordTypes,
}
impl<'r> IRecordTypeClosed<'r> {
fn new(record_type: Rc<IRecordType>, records: &'r IRecordTypes) -> Self {
Self {
record_type,
records,
}
}
}
impl PartialEq for IRecordTypeClosed<'_> {
fn eq(&self, other: &Self) -> bool {
let names_are_equal = self.record_type.name == other.record_type.name;
names_are_equal && fields_are_equal(self, other)
}
}
fn fields_are_equal(lhs: &IRecordTypeClosed<'_>, rhs: &IRecordTypeClosed<'_>) -> bool {
let same_fields_count = lhs.record_type.fields.len() == rhs.record_type.fields.len();
same_fields_count
&& zip(lhs.record_type.fields.iter(), rhs.record_type.fields.iter()).all(
|(lhs_field, rhs_field)| -> bool {
lhs_field.name == rhs_field.name
&& ITypeClosed::new(&lhs_field.ty, lhs.records)
== ITypeClosed::new(&rhs_field.ty, rhs.records)
},
)
}
impl PartialOrd for IRecordTypeClosed<'_> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for IRecordTypeClosed<'_> {
fn cmp(&self, other: &Self) -> Ordering {
self.record_type.name.cmp(&other.record_type.name)
}
}
impl Eq for IRecordTypeClosed<'_> {}
impl std::hash::Hash for IRecordTypeClosed<'_> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.record_type.name.hash(state);
}
}
pub type LinkedModules<'r> = HashMap<&'r str, LinkedModule<'r>>;
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct UseDescription<'r> {
pub from: &'r str,
pub name: &'r str,
}
#[derive(PartialEq, Eq)]
pub enum RecordEntry<'r> {
Use(UseDescription<'r>),
Declare(IRecordTypeClosed<'r>),
}
impl PartialOrd for RecordEntry<'_> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for RecordEntry<'_> {
fn cmp(&self, other: &Self) -> Ordering {
use RecordEntry::*;
match (self, other) {
(Use(_), Declare(_)) => Ordering::Less,
(Declare(_), Use(_)) => Ordering::Greater,
(Use(lhs), Use(rhs)) => lhs.cmp(rhs),
(Declare(lhs), Declare(rhs)) => lhs.record_type.name.cmp(&rhs.record_type.name),
}
}
}
#[derive(Default)]
pub struct LinkedModule<'all> {
pub records: Vec<RecordEntry<'all>>,
}

View File

@ -15,38 +15,18 @@
*/
use crate::TResult;
use marine_it_parser::interface::MRecordTypes;
use marine_it_parser::interface::it::IType;
use marine_it_parser::it_interface::IRecordTypes;
use marine_it_parser::it_interface::it::IType;
use proc_macro2::TokenStream;
use quote::quote;
pub(super) fn generate_module_ident(module_name: &str) -> TResult<syn::Ident> {
let generated_module_name = format!("__m_generated_{}", module_name);
new_ident(&generated_module_name)
}
pub(super) fn generate_structs_module_ident(module_name: &str) -> TResult<syn::Ident> {
let generated_module_name = format!("{}_structs", module_name);
new_ident(&generated_module_name)
}
pub(super) fn generate_record_name(record_name: &str) -> TResult<syn::Ident> {
let extended_record_name = record_name.to_string();
new_ident(&extended_record_name)
}
pub(super) fn generate_struct_name(struct_name: &str) -> TResult<syn::Ident> {
let extended_struct_name = format!("MGeneratedStruct{}", struct_name);
new_ident(&extended_struct_name)
}
pub(super) fn new_ident(ident_str: &str) -> TResult<syn::Ident> {
let ident_str = ident_str.replace('-', "_");
syn::parse_str::<syn::Ident>(&ident_str).map_err(Into::into)
}
pub(super) fn itype_to_tokens(itype: &IType, records: &MRecordTypes) -> TResult<TokenStream> {
pub(super) fn itype_to_tokens(itype: &IType, records: &IRecordTypes) -> TResult<TokenStream> {
let token_stream = match itype {
IType::Record(record_id) => {
let record = records

View File

@ -0,0 +1,6 @@
modules_dir = "artifacts/"
[[module]]
name = "greeting"
mem_pages_count = 1
logger_enabled = false

View File

@ -0,0 +1,128 @@
#[test]
fn empty_string() {
pub mod marine_test_env {
pub mod greeting {
#[derive(
Clone,
Debug,
marine_rs_sdk_test :: internal :: serde :: Serialize,
marine_rs_sdk_test :: internal :: serde :: Deserialize
)]
#[serde(crate = "marine_rs_sdk_test::internal::serde")]
pub struct CallParameters {
pub init_peer_id: String,
pub service_id: String,
pub service_creator_peer_id: String,
pub host_id: String,
pub particle_id: String,
pub tetraplets: Vec<Vec<SecurityTetraplet>>
}
#[derive(
Clone,
Debug,
marine_rs_sdk_test :: internal :: serde :: Serialize,
marine_rs_sdk_test :: internal :: serde :: Deserialize
)]
#[serde(crate = "marine_rs_sdk_test::internal::serde")]
pub struct MountedBinaryResult {
pub ret_code: i32,
pub error: String,
pub stdout: Vec<u8>,
pub stderr: Vec<u8>
}
#[derive(
Clone,
Debug,
marine_rs_sdk_test :: internal :: serde :: Serialize,
marine_rs_sdk_test :: internal :: serde :: Deserialize
)]
#[serde(crate = "marine_rs_sdk_test::internal::serde")]
pub struct MountedBinaryStringResult {
pub ret_code: i32,
pub error: String,
pub stdout: String,
pub stderr: String
}
#[derive(
Clone,
Debug,
marine_rs_sdk_test :: internal :: serde :: Serialize,
marine_rs_sdk_test :: internal :: serde :: Deserialize
)]
#[serde(crate = "marine_rs_sdk_test::internal::serde")]
pub struct SecurityTetraplet {
pub peer_pk: String,
pub service_id: String,
pub function_name: String,
pub json_path: String
}
pub struct ModuleInterface {
marine:
std::rc::Rc<std::cell::RefCell<marine_rs_sdk_test::internal::AppService>, >,
}
impl ModuleInterface {
pub fn new(
marine: std::rc::Rc<
std::cell::RefCell<marine_rs_sdk_test::internal::AppService>,>
) -> Self {
Self { marine }
}
}
impl ModuleInterface {}
}
}
let tmp_dir = std::env::temp_dir();
let service_id = marine_rs_sdk_test::internal::Uuid::new_v4().to_string();
let tmp_dir = tmp_dir.join(&service_id);
let tmp_dir = tmp_dir.to_string_lossy().to_string();
std::fs::create_dir(&tmp_dir).expect("can't create a directory for service in tmp");
let mut module_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let mut file_path = std::path::Path::new(file!()).components();
let mut truncated_file_path = Vec::new();
loop {
if module_path.ends_with(file_path.as_path()) {
break;
}
let (file_path_, remainder) = match file_path.next_back().and_then(|p| match p {
std::path::Component::Normal(_)
| std::path::Component::CurDir
| std::path::Component::ParentDir => Some((file_path, p)),
_ => None,
}) {
Some(t) => t,
None => break,
};
file_path = file_path_;
truncated_file_path.push(remainder);
}
for path in truncated_file_path.iter().rev() {
module_path.push(path);
}
let _ = module_path.pop();
let config_path = module_path.join("Config.toml");
let modules_dir = module_path.join("artifacts");
let modules_dir = modules_dir
.to_str()
.expect("modules_dir contains invalid UTF8 string");
let mut __m_generated_marine_config = marine_rs_sdk_test::internal::TomlAppServiceConfig::load(
&config_path
)
.unwrap_or_else(|e|
panic!(
"app service config located at `{:?}` can't be loaded: {}",
config_path, e
));
__m_generated_marine_config.service_base_dir = Some(tmp_dir);
__m_generated_marine_config.toml_faas_config.modules_dir = Some(modules_dir.to_string());
let marine = marine_rs_sdk_test::internal::AppService::new_with_empty_facade(
__m_generated_marine_config,
service_id,
std::collections::HashMap::new()
)
.unwrap_or_else(|e| panic!("app service can't be created: {}", e));
let marine = std::rc::Rc::new(std::cell::RefCell::new(marine));
fn test_func() {
{}
}
test_func()
}

View File

@ -0,0 +1 @@
fn empty_string() {}

View File

@ -0,0 +1,8 @@
modules_dir = "artifacts/"
[[module]]
name = "greeting"
mem_pages_count = 1
logger_enabled = false
[module.mounted_binaries]
echo = "/usr/bin/curl"

View File

@ -0,0 +1,167 @@
#[test]
fn test() {
pub mod marine_test_env {
pub mod greeting {
#[derive(
Clone,
Debug,
marine_rs_sdk_test :: internal :: serde :: Serialize,
marine_rs_sdk_test :: internal :: serde :: Deserialize
)]
#[serde(crate = "marine_rs_sdk_test::internal::serde")]
pub struct CallParameters {
pub init_peer_id: String,
pub service_id: String,
pub service_creator_peer_id: String,
pub host_id: String,
pub particle_id: String,
pub tetraplets: Vec<Vec<SecurityTetraplet>>
}
#[derive(
Clone,
Debug,
marine_rs_sdk_test :: internal :: serde :: Serialize,
marine_rs_sdk_test :: internal :: serde :: Deserialize
)]
#[serde(crate = "marine_rs_sdk_test::internal::serde")]
pub struct MountedBinaryResult {
pub ret_code: i32,
pub error: String,
pub stdout: Vec<u8>,
pub stderr: Vec<u8>
}
#[derive(
Clone,
Debug,
marine_rs_sdk_test :: internal :: serde :: Serialize,
marine_rs_sdk_test :: internal :: serde :: Deserialize
)]
#[serde(crate = "marine_rs_sdk_test::internal::serde")]
pub struct MountedBinaryStringResult {
pub ret_code: i32,
pub error: String,
pub stdout: String,
pub stderr: String
}
#[derive(
Clone,
Debug,
marine_rs_sdk_test :: internal :: serde :: Serialize,
marine_rs_sdk_test :: internal :: serde :: Deserialize
)]
#[serde(crate = "marine_rs_sdk_test::internal::serde")]
pub struct SecurityTetraplet {
pub peer_pk: String,
pub service_id: String,
pub function_name: String,
pub json_path: String
}
pub struct ModuleInterface {
marine:
std::rc::Rc<std::cell::RefCell<marine_rs_sdk_test::internal::AppService>, >,
}
impl ModuleInterface {
pub fn new(
marine: std::rc::Rc<
std::cell::RefCell<marine_rs_sdk_test::internal::AppService>,
>
) -> Self {
Self { marine }
}
}
impl ModuleInterface {
pub fn download(&mut self, url: String) -> String {
use std::ops::DerefMut;
let arguments = marine_rs_sdk_test::internal::serde_json::json!([url]);
let result = self
.marine
.as_ref()
.borrow_mut()
.call_module("greeting", "download", arguments, <_>::default())
.expect("call to Marine failed");
let result: String =
marine_rs_sdk_test::internal::serde_json::from_value(result)
.expect("the default deserializer shouldn't fail");
result
}
pub fn download_cp(
&mut self,
url: String,
cp: marine_rs_sdk_test::CallParameters
) -> String {
use std::ops::DerefMut;
let arguments = marine_rs_sdk_test::internal::serde_json::json!([url]);
let result = self
.marine
.as_ref()
.borrow_mut()
.call_module("greeting", "download", arguments, cp)
.expect("call to Marine failed");
let result: String =
marine_rs_sdk_test::internal::serde_json::from_value(result)
.expect("the default deserializer shouldn't fail");
result
}
}
}
}
let tmp_dir = std::env::temp_dir();
let service_id = marine_rs_sdk_test::internal::Uuid::new_v4().to_string();
let tmp_dir = tmp_dir.join(&service_id);
let tmp_dir = tmp_dir.to_string_lossy().to_string();
std::fs::create_dir(&tmp_dir).expect("can't create a directory for service in tmp");
let mut module_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let mut file_path = std::path::Path::new(file!()).components();
let mut truncated_file_path = Vec::new();
loop {
if module_path.ends_with(file_path.as_path()) {
break;
}
let (file_path_, remainder) = match file_path.next_back().and_then(|p| match p {
std::path::Component::Normal(_)
| std::path::Component::CurDir
| std::path::Component::ParentDir => Some((file_path, p)),
_ => None,
}) {
Some(t) => t,
None => break,
};
file_path = file_path_;
truncated_file_path.push(remainder);
}
for path in truncated_file_path.iter().rev() {
module_path.push(path);
}
let _ = module_path.pop();
let config_path = module_path.join("Config.toml");
let modules_dir = module_path.join("artifacts");
let modules_dir = modules_dir
.to_str()
.expect("modules_dir contains invalid UTF8 string");
let mut __m_generated_marine_config = marine_rs_sdk_test::internal::TomlAppServiceConfig::load(
&config_path
)
.unwrap_or_else(|e|
panic!(
"app service config located at `{:?}` can't be loaded: {}",
config_path, e
)
);
__m_generated_marine_config.service_base_dir = Some(tmp_dir);
__m_generated_marine_config.toml_faas_config.modules_dir = Some(modules_dir.to_string());
let marine = marine_rs_sdk_test::internal::AppService::new_with_empty_facade(
__m_generated_marine_config,
service_id,
std::collections::HashMap::new()
)
.unwrap_or_else(|e| panic!("app service can't be created: {}", e));
let marine = std::rc::Rc::new(std::cell::RefCell::new(marine));
let mut greeting = marine_test_env::greeting::ModuleInterface::new(marine.clone());
fn test_func(greeting: marine_test_env::greeting::ModuleInterface) {
let mut greeting = greeting;
{
let _ = greeting.download("duckduckgo.com");
}
}
test_func(greeting,)
}

View File

@ -0,0 +1,3 @@
fn test(greeting: marine_test_env::greeting::ModuleInterface) {
let _ = greeting.download("duckduckgo.com");
}

View File

@ -0,0 +1,10 @@
modules_dir = "artifacts/"
[[module]]
name = "greeting"
mem_pages_count = 1
logger_enabled = false
[[module]]
name = "call_parameters"
mem_pages_count = 1
logger_enabled = false

View File

@ -0,0 +1,321 @@
#[test]
fn empty_string() {
pub mod marine_test_env {
pub mod greeting {
#[derive(
Clone,
Debug,
marine_rs_sdk_test :: internal :: serde :: Serialize,
marine_rs_sdk_test :: internal :: serde :: Deserialize
)]
#[serde(crate = "marine_rs_sdk_test::internal::serde")]
pub struct CallParameters {
pub init_peer_id: String,
pub service_id: String,
pub service_creator_peer_id: String,
pub host_id: String,
pub particle_id: String,
pub tetraplets: Vec<Vec<SecurityTetraplet>>
}
#[derive(
Clone,
Debug,
marine_rs_sdk_test :: internal :: serde :: Serialize,
marine_rs_sdk_test :: internal :: serde :: Deserialize
)]
#[serde(crate = "marine_rs_sdk_test::internal::serde")]
pub struct MountedBinaryResult {
pub ret_code: i32,
pub error: String,
pub stdout: Vec<u8>,
pub stderr: Vec<u8>
}
#[derive(
Clone,
Debug,
marine_rs_sdk_test :: internal :: serde :: Serialize,
marine_rs_sdk_test :: internal :: serde :: Deserialize
)]
#[serde(crate = "marine_rs_sdk_test::internal::serde")]
pub struct MountedBinaryStringResult {
pub ret_code: i32,
pub error: String,
pub stdout: String,
pub stderr: String
}
#[derive(
Clone,
Debug,
marine_rs_sdk_test :: internal :: serde :: Serialize,
marine_rs_sdk_test :: internal :: serde :: Deserialize
)]
#[serde(crate = "marine_rs_sdk_test::internal::serde")]
pub struct SecurityTetraplet {
pub peer_pk: String,
pub service_id: String,
pub function_name: String,
pub json_path: String
}
pub struct ModuleInterface {
marine: std::rc::Rc<std::cell::RefCell<marine_rs_sdk_test::internal::AppService>, >,
}
impl ModuleInterface {
pub fn new(
marine: std::rc::Rc<
std::cell::RefCell<marine_rs_sdk_test::internal::AppService>,
>
) -> Self {
Self { marine }
}
}
impl ModuleInterface {
pub fn greeting(&mut self, name: String) -> String {
use std::ops::DerefMut;
let arguments = marine_rs_sdk_test::internal::serde_json::json!([name]);
let result = self
.marine
.as_ref()
.borrow_mut()
.call_module("greeting", "greeting", arguments, <_>::default())
.expect("call to Marine failed");
let result: String =
marine_rs_sdk_test::internal::serde_json::from_value(result)
.expect("the default deserializer shouldn't fail");
result
}
pub fn greeting_cp(
&mut self,
name: String,
cp: marine_rs_sdk_test::CallParameters
) -> String {
use std::ops::DerefMut;
let arguments = marine_rs_sdk_test::internal::serde_json::json!([name]);
let result = self
.marine
.as_ref()
.borrow_mut()
.call_module("greeting", "greeting", arguments, cp)
.expect("call to Marine failed");
let result: String =
marine_rs_sdk_test::internal::serde_json::from_value(result)
.expect("the default deserializer shouldn't fail");
result
}
}
}
pub mod call_parameters {
pub use super::greeting::CallParameters;
pub use super::greeting::SecurityTetraplet;
pub struct ModuleInterface {
marine: std::rc::Rc<std::cell::RefCell<marine_rs_sdk_test::internal::AppService>, >,
}
impl ModuleInterface {
pub fn new(
marine: std::rc::Rc<
std::cell::RefCell<marine_rs_sdk_test::internal::AppService>,
>
) -> Self {
Self { marine }
}
}
impl ModuleInterface {
pub fn call_parameters(&mut self, ) -> String {
use std::ops::DerefMut;
let arguments = marine_rs_sdk_test::internal::serde_json::json!([]);
let result = self
.marine
.as_ref()
.borrow_mut()
.call_module(
"call_parameters",
"call_parameters",
arguments,
<_>::default()
)
.expect("call to Marine failed");
let result: String =
marine_rs_sdk_test::internal::serde_json::from_value(result)
.expect("the default deserializer shouldn't fail");
result
}
pub fn call_parameters_cp(
&mut self,
cp: marine_rs_sdk_test::CallParameters
) -> String {
use std::ops::DerefMut;
let arguments = marine_rs_sdk_test::internal::serde_json::json!([]);
let result = self
.marine
.as_ref()
.borrow_mut()
.call_module("call_parameters", "call_parameters", arguments, cp)
.expect("call to Marine failed");
let result: String =
marine_rs_sdk_test::internal::serde_json::from_value(result)
.expect("the default deserializer shouldn't fail");
result
}
pub fn return_string(&mut self,) -> String {
use std::ops::DerefMut;
let arguments = marine_rs_sdk_test::internal::serde_json::json!([]);
let result = self
.marine
.as_ref()
.borrow_mut()
.call_module(
"call_parameters",
"return_string",
arguments,
<_>::default()
)
.expect("call to Marine failed");
let result: String =
marine_rs_sdk_test::internal::serde_json::from_value(result)
.expect("the default deserializer shouldn't fail");
result
}
pub fn return_string_cp(
&mut self,
cp: marine_rs_sdk_test::CallParameters
) -> String {
use std::ops::DerefMut;
let arguments = marine_rs_sdk_test::internal::serde_json::json!([]);
let result = self
.marine
.as_ref()
.borrow_mut()
.call_module("call_parameters", "return_string", arguments, cp)
.expect("call to Marine failed");
let result: String =
marine_rs_sdk_test::internal::serde_json::from_value(result)
.expect("the default deserializer shouldn't fail");
result
}
pub fn test_array_refs(&mut self, ) -> Vec<String> {
use std::ops::DerefMut;
let arguments = marine_rs_sdk_test::internal::serde_json::json!([]);
let result = self
.marine
.as_ref()
.borrow_mut()
.call_module(
"call_parameters",
"test_array_refs",
arguments,
<_>::default()
)
.expect("call to Marine failed");
let result: Vec<String> =
marine_rs_sdk_test::internal::serde_json::from_value(result)
.expect("the default deserializer shouldn't fail");
result
}
pub fn test_array_refs_cp(
&mut self,
cp: marine_rs_sdk_test::CallParameters
) -> Vec<String> {
use std::ops::DerefMut;
let arguments = marine_rs_sdk_test::internal::serde_json::json!([]);
let result = self
.marine
.as_ref()
.borrow_mut()
.call_module("call_parameters", "test_array_refs", arguments, cp)
.expect("call to Marine failed");
let result: Vec<String> =
marine_rs_sdk_test::internal::serde_json::from_value(result)
.expect("the default deserializer shouldn't fail");
result
}
}
}
}
let tmp_dir = std::env::temp_dir();
let service_id = marine_rs_sdk_test::internal::Uuid::new_v4().to_string();
let tmp_dir = tmp_dir.join(&service_id);
let tmp_dir = tmp_dir.to_string_lossy().to_string();
std::fs::create_dir(&tmp_dir).expect("can't create a directory for service in tmp");
let mut module_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let mut file_path = std::path::Path::new(file!()).components();
let mut truncated_file_path = Vec::new();
loop {
if module_path.ends_with(file_path.as_path()) {
break;
}
let (file_path_, remainder) = match file_path.next_back().and_then(|p| match p {
std::path::Component::Normal(_)
| std::path::Component::CurDir
| std::path::Component::ParentDir => Some((file_path, p)),
_ => None,
}) {
Some(t) => t,
None => break,
};
file_path = file_path_;
truncated_file_path.push(remainder);
}
for path in truncated_file_path.iter().rev() {
module_path.push(path);
}
let _ = module_path.pop();
let config_path = module_path.join("Config.toml");
let modules_dir = module_path.join("artifacts");
let modules_dir = modules_dir
.to_str()
.expect("modules_dir contains invalid UTF8 string");
let mut __m_generated_marine_config = marine_rs_sdk_test::internal::TomlAppServiceConfig::load(
&config_path
)
.unwrap_or_else(|e|
panic!(
"app service config located at `{:?}` can't be loaded: {}",
config_path, e
)
);
__m_generated_marine_config.service_base_dir = Some(tmp_dir);
__m_generated_marine_config.toml_faas_config.modules_dir = Some(modules_dir.to_string());
let marine = marine_rs_sdk_test::internal::AppService::new_with_empty_facade(
__m_generated_marine_config,
service_id,
std::collections::HashMap::new()
)
.unwrap_or_else(|e| panic!("app service can't be created: {}", e));
let marine = std::rc::Rc::new(std::cell::RefCell::new(marine));
let mut greeting_m = marine_test_env::greeting::ModuleInterface::new(marine.clone());
let mut call_parameters_m =
marine_test_env::call_parameters::ModuleInterface::new(marine.clone());
fn test_func(
greeting_m: marine_test_env::greeting::ModuleInterface,
call_parameters_m: marine_test_env::call_parameters::ModuleInterface
) {
let mut greeting_m = greeting_m;
let mut call_parameters_m = call_parameters_m;
{
let init_peer_id = "init_peer_id";
let service_id = "service_id";
let service_creator_peer_id = "service_creator_peer_id";
let host_id = "host_id";
let particle_id = "particle_id";
let greeting = greeting_m.greeting("asd");
let mut tetraplet = SecurityTetraplet::default();
tetraplet.function_name = "some_func_name".to_string();
tetraplet.json_path = "some_json_path".to_string();
let tetraplets = vec![vec![tetraplet]];
let cp = CallParameters {
init_peer_id: init_peer_id.to_string(),
service_id: service_id.to_string(),
service_creator_peer_id: service_creator_peer_id.to_string(),
host_id: host_id.to_string(),
particle_id: particle_id.to_string(),
tetraplets: tetraplets.clone(),
};
let actual = call_parameters_m.call_parameters_cp(cp);
let expected = format!(
"{}\n{}\n{}\n{}\n{}\n{:?}",
init_peer_id, service_id, service_creator_peer_id, host_id, particle_id, tetraplets
);
assert_eq!(actual, expected);
}
}
test_func(greeting_m, call_parameters_m, )
}

View File

@ -0,0 +1,29 @@
fn empty_string(greeting_m: marine_test_env::greeting::ModuleInterface, call_parameters_m: marine_test_env::call_parameters::ModuleInterface) {
let init_peer_id = "init_peer_id";
let service_id = "service_id";
let service_creator_peer_id = "service_creator_peer_id";
let host_id = "host_id";
let particle_id = "particle_id";
let greeting = greeting_m.greeting("asd");
let mut tetraplet = SecurityTetraplet::default();
tetraplet.function_name = "some_func_name".to_string();
tetraplet.json_path = "some_json_path".to_string();
let tetraplets = vec![vec![tetraplet]];
let cp = CallParameters {
init_peer_id: init_peer_id.to_string(),
service_id: service_id.to_string(),
service_creator_peer_id: service_creator_peer_id.to_string(),
host_id: host_id.to_string(),
particle_id: particle_id.to_string(),
tetraplets: tetraplets.clone(),
};
let actual = call_parameters_m.call_parameters_cp(cp);
let expected = format!(
"{}\n{}\n{}\n{}\n{}\n{:?}",
init_peer_id, service_id, service_creator_peer_id, host_id, particle_id, tetraplets
);
assert_eq!(actual, expected);
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
mod utils;
use utils::test_marine_test_token_streams;
#[test]
fn test_empty_func() {
assert!(test_marine_test_token_streams(
"tests/generation_tests/empty_func/marine_test.rs",
"tests/generation_tests/empty_func/expanded.rs",
"Config.toml",
"artifacts"
));
}
#[test]
fn test_mounted_binary() {
assert!(test_marine_test_token_streams(
"tests/generation_tests/mounted_binary/marine_test.rs",
"tests/generation_tests/mounted_binary/expanded.rs",
"Config.toml",
"artifacts"
));
}
#[test]
fn test_multiple_modules() {
assert!(test_marine_test_token_streams(
"tests/generation_tests/multiple_modules/marine_test.rs",
"tests/generation_tests/multiple_modules/expanded.rs",
"Config.toml",
"artifacts"
));
}

View File

@ -0,0 +1,48 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use marine_test_macro_impl::marine_test_impl;
use marine_macro_testing_utils::{items_from_file, stream_from_file, to_syn_item};
use std::path::Path;
pub fn test_marine_test_token_streams<FP, EP>(
marine_path: FP,
expanded_path: EP,
config_path: &str,
modules_dir: &str,
) -> bool
where
FP: AsRef<Path>,
EP: AsRef<Path>,
{
let marine_item = stream_from_file(&marine_path);
let test_token_stream = quote::quote! { #marine_item };
let buf = marine_path.as_ref().to_path_buf();
let attrs = quote::quote! {config_path = #config_path, modules_dir = #modules_dir};
let marine_token_streams = marine_test_impl(
attrs,
test_token_stream,
buf.parent().unwrap().to_path_buf(),
)
.unwrap_or_else(|e| panic!("failed to apply the marine macro due {}", e));
let expanded_item = items_from_file(&expanded_path);
let marine_item = to_syn_item(marine_token_streams.clone());
marine_item == expanded_item
}

View File

@ -1,6 +1,6 @@
[package]
name = "marine-test-macro"
version = "0.1.11" # remember to update html_root_url
version = "0.2.0" # remember to update html_root_url
edition = "2018"
description = "Definition of the `#[marine_test]` macro"
documentation = "https://docs.rs/fluence/marine-test-macro"
@ -18,7 +18,7 @@ proc-macro = true
doctest = false
[dependencies]
marine-test-macro-impl = { path = "../marine-test-macro-impl", version = "=0.1.11" }
marine-test-macro-impl = { path = "../marine-test-macro-impl", version = "=0.2.0" }
quote = "1.0.9"
proc-macro2 = "1.0.24"

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
#![doc(html_root_url = "https://docs.rs/sdk-test-macro/0.1.11")]
#![doc(html_root_url = "https://docs.rs/sdk-test-macro/0.2.0")]
#![deny(
dead_code,
nonstandard_style,
@ -36,7 +36,7 @@ use syn::spanned::Spanned;
/// This macro allows user to write tests for services in the following form:
///```rust
/// #[marine_test(config = "/path/to/Config.toml", modules_dir = "path/to/service/modules")]
/// fn test() {
/// fn test(greeting: marine_test_env::greeting::ModuleInterface) {
/// let service_result = greeting.greeting("John".to_string());
/// assert_eq!(&service_result, "Hi, name!");
/// }

View File

@ -1,6 +1,6 @@
[package]
name = "marine-rs-sdk-test"
version = "0.1.11" # remember to update html_root_url
version = "0.2.0" # remember to update html_root_url
description = "Backend SDK that allows testing modules for the Marine runtime"
documentation = "https://docs.rs/marine-rs-sdk-test"
repository = "https://github.com/fluencelabs/marine-rs-sdk/tree/master/fluence-test"
@ -17,8 +17,11 @@ all-features = true
path = "src/lib.rs"
doctest = false
[dev-dependencies]
trybuild = "1.0"
[dependencies]
marine-test-macro = { path = "../crates/marine-test-macro", version = "=0.1.11" }
marine-test-macro = { path = "../crates/marine-test-macro", version = "=0.2.0" }
fluence-app-service = { version = "0.9.0", features = ["raw-module-api"] }
serde = { version = "1.0.118", features = ["derive"] }

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
#![doc(html_root_url = "https://docs.rs/sdk-test/0.1.11")]
#![doc(html_root_url = "https://docs.rs/sdk-test/0.2.0")]
#![deny(
dead_code,
nonstandard_style,