From 631c6d98857b1e402d5d409094fc84b40aa98c58 Mon Sep 17 00:00:00 2001 From: Valery Antopol Date: Mon, 4 Oct 2021 19:06:58 +0300 Subject: [PATCH] Implement marine_test for services integration (first part) (#61) --- .../marine-test-macro-impl/src/attributes.rs | 30 +- crates/marine-test-macro-impl/src/errors.rs | 3 + .../src/marine_test/config_utils.rs | 46 +- .../src/marine_test/glue_code_generator.rs | 128 ++--- .../src/marine_test/mod.rs | 2 +- .../src/marine_test/modules_linker.rs | 19 +- ...generator.rs => token_stream_generator.rs} | 68 ++- .../methods_generator.rs | 23 + .../methods_generator_utils.rs | 81 ++- .../record_type_generator.rs | 13 +- .../service_generation_utils.rs | 204 +++++++ .../service_generator.rs | 110 ++++ .../empty_func/Config.toml | 6 + .../empty_func/artifacts/greeting.wasm | Bin 0 -> 56806 bytes .../multi-service-multiple/expanded.rs | 504 ++++++++++++++++++ .../multi-service-multiple/marine_test.rs | 4 + .../mounted_binary/Config.toml | 8 + .../mounted_binary/artifacts/greeting.wasm | Bin 0 -> 64072 bytes .../empty_func/Config.toml | 6 + .../empty_func/artifacts/greeting.wasm | Bin 0 -> 56806 bytes .../multi-service-single/expanded.rs | 242 +++++++++ .../multi-service-single/marine_test.rs | 1 + .../tests/generation_tests_runner.rs | 37 ++ crates/marine-test-macro-impl/tests/utils.rs | 51 +- 24 files changed, 1443 insertions(+), 143 deletions(-) rename crates/marine-test-macro-impl/src/marine_test/{module_generator.rs => token_stream_generator.rs} (62%) rename crates/marine-test-macro-impl/src/marine_test/{module_generator => token_stream_generator}/methods_generator.rs (67%) rename crates/marine-test-macro-impl/src/marine_test/{module_generator => token_stream_generator}/methods_generator_utils.rs (78%) rename crates/marine-test-macro-impl/src/marine_test/{module_generator => token_stream_generator}/record_type_generator.rs (85%) create mode 100644 crates/marine-test-macro-impl/src/marine_test/token_stream_generator/service_generation_utils.rs create mode 100644 crates/marine-test-macro-impl/src/marine_test/token_stream_generator/service_generator.rs create mode 100644 crates/marine-test-macro-impl/tests/generation_tests/multi-service-multiple/empty_func/Config.toml create mode 100755 crates/marine-test-macro-impl/tests/generation_tests/multi-service-multiple/empty_func/artifacts/greeting.wasm create mode 100644 crates/marine-test-macro-impl/tests/generation_tests/multi-service-multiple/expanded.rs create mode 100644 crates/marine-test-macro-impl/tests/generation_tests/multi-service-multiple/marine_test.rs create mode 100644 crates/marine-test-macro-impl/tests/generation_tests/multi-service-multiple/mounted_binary/Config.toml create mode 100755 crates/marine-test-macro-impl/tests/generation_tests/multi-service-multiple/mounted_binary/artifacts/greeting.wasm create mode 100644 crates/marine-test-macro-impl/tests/generation_tests/multi-service-single/empty_func/Config.toml create mode 100755 crates/marine-test-macro-impl/tests/generation_tests/multi-service-single/empty_func/artifacts/greeting.wasm create mode 100644 crates/marine-test-macro-impl/tests/generation_tests/multi-service-single/expanded.rs create mode 100644 crates/marine-test-macro-impl/tests/generation_tests/multi-service-single/marine_test.rs diff --git a/crates/marine-test-macro-impl/src/attributes.rs b/crates/marine-test-macro-impl/src/attributes.rs index e999473..17a83f9 100644 --- a/crates/marine-test-macro-impl/src/attributes.rs +++ b/crates/marine-test-macro-impl/src/attributes.rs @@ -15,10 +15,17 @@ */ use darling::FromMeta; +use std::collections::HashMap; /// Describes attributes of `marine_test` macro. +#[derive(Debug, Clone)] +pub(crate) enum MTestAttributes { + SingleService(ServiceDescription), + MultipleServices(HashMap), +} + #[derive(Debug, Default, Clone, FromMeta)] -pub(crate) struct MTestAttributes { +pub(crate) struct ServiceDescription { /// Path to a config file of a tested service. pub(crate) config_path: String, @@ -26,3 +33,24 @@ pub(crate) struct MTestAttributes { #[darling(default)] pub(crate) modules_dir: Option, } + +impl FromMeta for MTestAttributes { + fn from_list(items: &[syn::NestedMeta]) -> darling::Result { + let single_service = ServiceDescription::from_list(items); + let multiple_services = HashMap::::from_list(items); + match (single_service, multiple_services) { + (Ok(modules), Err(_)) => Ok(Self::SingleService(modules)), + (Err(_), Ok(services)) if !services.is_empty() => Ok(Self::MultipleServices(services)), + (Err(_), Ok(_)) => Err(darling::Error::custom( + r#"Need to specify "config_path" and "modules_dir" or several named services with these fields "#, + )), + (Err(error_single), Err(error_multiple)) => Err(darling::error::Error::multiple(vec![ + error_single, + error_multiple, + ])), + (Ok(_), Ok(_)) => Err(darling::Error::custom( + "internal sdk error: marine_test attributes are ambiguous", + )), + } + } +} diff --git a/crates/marine-test-macro-impl/src/errors.rs b/crates/marine-test-macro-impl/src/errors.rs index a84b7d2..48402db 100644 --- a/crates/marine-test-macro-impl/src/errors.rs +++ b/crates/marine-test-macro-impl/src/errors.rs @@ -56,6 +56,9 @@ pub enum TestGeneratorError { #[error("Duplicate module: {0}")] DuplicateModuleName(String), + + #[error("No modules loaded for a service")] + NoModulesInService, } #[derive(Debug, ThisError)] diff --git a/crates/marine-test-macro-impl/src/marine_test/config_utils.rs b/crates/marine-test-macro-impl/src/marine_test/config_utils.rs index 9750952..7300ba7 100644 --- a/crates/marine-test-macro-impl/src/marine_test/config_utils.rs +++ b/crates/marine-test-macro-impl/src/marine_test/config_utils.rs @@ -14,13 +14,13 @@ * limitations under the License. */ -use crate::TResult; +use crate::{TResult, TestGeneratorError}; use fluence_app_service::TomlAppServiceConfig; use marine_it_parser::module_it_interface; use marine_it_parser::it_interface::IModuleInterface; -use std::path::PathBuf; +use std::path::{PathBuf, Path}; #[derive(Debug, Clone, PartialEq, Eq)] pub(super) struct Module<'m> { @@ -34,12 +34,36 @@ impl<'m> Module<'m> { } } +pub(crate) struct ConfigWrapper { + pub config: TomlAppServiceConfig, + pub resolved_modules_dir: PathBuf, +} + +pub(crate) fn load_config( + config_path: &str, + modules_dir: Option, + file_path: &Path, +) -> TResult { + let config_path_buf = file_path.join(&config_path); + + let marine_config = TomlAppServiceConfig::load(&config_path_buf)?; + let modules_dir = match resolve_modules_dir(&marine_config, modules_dir) { + Some(modules_dir) => modules_dir, + None => return Err(TestGeneratorError::ModulesDirUnspecified), + }; + + Ok(ConfigWrapper { + config: marine_config, + resolved_modules_dir: modules_dir, + }) +} + /// Returns all modules the provided config consists of. -pub(super) fn collect_modules( - config: &TomlAppServiceConfig, - modules_dir: PathBuf, -) -> TResult>> { - let module_paths = collect_module_paths(config, modules_dir); +pub(super) fn collect_modules<'config>( + config: &'config TomlAppServiceConfig, + modules_dir: &Path, +) -> TResult>> { + let module_paths = collect_module_paths(config, &modules_dir); module_paths .into_iter() @@ -48,10 +72,10 @@ pub(super) fn collect_modules( .map_err(Into::into) } -fn collect_module_paths( - config: &TomlAppServiceConfig, - modules_dir: PathBuf, -) -> Vec<(&str, PathBuf)> { +fn collect_module_paths<'config>( + config: &'config TomlAppServiceConfig, + modules_dir: &Path, +) -> Vec<(&'config str, PathBuf)> { config .toml_faas_config .module diff --git a/crates/marine-test-macro-impl/src/marine_test/glue_code_generator.rs b/crates/marine-test-macro-impl/src/marine_test/glue_code_generator.rs index c775e67..70b12b3 100644 --- a/crates/marine-test-macro-impl/src/marine_test/glue_code_generator.rs +++ b/crates/marine-test-macro-impl/src/marine_test/glue_code_generator.rs @@ -14,20 +14,19 @@ * limitations under the License. */ -use crate::attributes::MTestAttributes; -use crate::TResult; -use crate::TestGeneratorError; +use crate::attributes::{MTestAttributes, ServiceDescription}; use crate::marine_test; -use crate::marine_test::config_utils; +use crate::marine_test::{config_utils, token_stream_generator}; +use crate::TestGeneratorError; +use crate::TResult; + +use std::path::PathBuf; -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; +use std::collections::HashMap; /// Generates glue code for tests. /// F.e. for this test for the greeting service @@ -116,23 +115,35 @@ use syn::FnArg; pub(super) fn generate_test_glue_code( func_item: syn::ItemFn, attrs: MTestAttributes, - file_path: PathBuf, + test_file_path: PathBuf, ) -> TResult { - let config_path = file_path.join(&attrs.config_path); + match attrs { + MTestAttributes::MultipleServices(services) => { + generate_test_glue_code_multiple_eservices(func_item, services, test_file_path) + } + MTestAttributes::SingleService(service) => { + generate_test_glue_code_single_service(func_item, service, test_file_path) + } + } +} - let marine_config = TomlAppServiceConfig::load(&config_path)?; - let modules_dir = match config_utils::resolve_modules_dir(&marine_config, attrs.modules_dir) { - Some(modules_dir) => modules_dir, - None => return Err(TestGeneratorError::ModulesDirUnspecified), - }; - - let app_service_ctor = generate_app_service_ctor(&attrs.config_path, &modules_dir)?; - let modules_dir = file_path.join(modules_dir); +fn generate_test_glue_code_single_service( + func_item: syn::ItemFn, + service: ServiceDescription, + test_file_path: PathBuf, +) -> TResult { + let config_wrapper = + config_utils::load_config(&service.config_path, service.modules_dir, &test_file_path)?; + let modules_dir_test_relative = test_file_path.join(&config_wrapper.resolved_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)?; + config_utils::collect_modules(&config_wrapper.config, &modules_dir_test_relative)?; + let linked_modules = marine_test::modules_linker::link_modules( + module_interfaces + .iter() + .map(|module| (module.name, &module.interface)), + )?; - let module_definitions = marine_test::module_generator::generate_module_definitions( + let module_definitions = token_stream_generator::generate_module_definitions( module_interfaces.iter(), &linked_modules, )?; @@ -143,7 +154,10 @@ pub(super) fn generate_test_glue_code( let inputs = &signature.inputs; let arg_names = generate_arg_names(inputs.iter())?; let module_ctors = generate_module_ctors(inputs.iter())?; - + let app_service_ctor = token_stream_generator::generate_app_service_ctor( + &service.config_path, + &config_wrapper.resolved_modules_dir, + )?; let glue_code = quote! { #[test] fn #name() { @@ -170,62 +184,34 @@ pub(super) fn generate_test_glue_code( Ok(glue_code) } -fn generate_app_service_ctor(config_path: &str, modules_dir: &Path) -> TResult { - let modules_dir = modules_dir - .to_str() - .ok_or_else(|| TestGeneratorError::InvalidUTF8Path(modules_dir.to_path_buf()))?; +fn generate_test_glue_code_multiple_eservices( + func_item: syn::ItemFn, + services: HashMap, + test_file_path: PathBuf, +) -> TResult { + let service_definitions = + token_stream_generator::generate_service_definitions(services, &test_file_path)?; - let service_ctor = quote! { - 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 original_block = func_item.block; + let signature = func_item.sig; + let name = &signature.ident; + let glue_code = quote! { + #[test] + fn #name() { + // definitions for services specified in attributes + pub mod marine_test_env { + #(#service_definitions)* } - 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_; + fn test_func() { + #original_block + } - truncated_file_path.push(remainder); + test_func() } - - for path in truncated_file_path.iter().rev() { - module_path.push(path); - } - - let _ = module_path.pop(); - - let config_path = module_path.join(#config_path); - let modules_dir = module_path.join(#modules_dir); - 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)); }; - Ok(service_ctor) + Ok(glue_code) } fn generate_module_ctors<'inputs>( diff --git a/crates/marine-test-macro-impl/src/marine_test/mod.rs b/crates/marine-test-macro-impl/src/marine_test/mod.rs index 8b98009..7ca8eb2 100644 --- a/crates/marine-test-macro-impl/src/marine_test/mod.rs +++ b/crates/marine-test-macro-impl/src/marine_test/mod.rs @@ -17,7 +17,7 @@ mod config_utils; mod marine_test_impl; mod glue_code_generator; -mod module_generator; +mod token_stream_generator; mod utils; mod modules_linker; diff --git a/crates/marine-test-macro-impl/src/marine_test/modules_linker.rs b/crates/marine-test-macro-impl/src/marine_test/modules_linker.rs index 23894e6..19b06eb 100644 --- a/crates/marine-test-macro-impl/src/marine_test/modules_linker.rs +++ b/crates/marine-test-macro-impl/src/marine_test/modules_linker.rs @@ -14,10 +14,9 @@ * 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::{IRecordTypes, IModuleInterface}; use marine_it_parser::it_interface::it::{IType, IRecordType}; use itertools::zip; @@ -28,16 +27,16 @@ use std::rc::Rc; use static_assertions::const_assert; pub(super) fn link_modules<'modules>( - modules: &'modules [Module<'_>], + modules: impl ExactSizeIterator, ) -> TResult> { let mut all_record_types = HashMap::, &str>::new(); let mut linked_modules = HashMap::<&str, LinkedModule<'_>>::new(); - for module in modules { + for (name, interface) in modules { let mut linking_module = LinkedModule::default(); - for (_, record_type) in &module.interface.record_types { + for record_type in interface.record_types.values() { let record_type_ex = - IRecordTypeClosed::new(record_type.clone(), &module.interface.record_types); + IRecordTypeClosed::new(record_type.clone(), &interface.record_types); let entry = match all_record_types.get(&record_type_ex) { Some(owner_module) => RecordEntry::Use(UseDescription { @@ -45,7 +44,7 @@ pub(super) fn link_modules<'modules>( name: &record_type.name, }), None => { - all_record_types.insert(record_type_ex.clone(), module.name); + all_record_types.insert(record_type_ex.clone(), name); RecordEntry::Declare(record_type_ex) } }; @@ -53,10 +52,8 @@ pub(super) fn link_modules<'modules>( linking_module.records.push(entry); } - if linked_modules.insert(module.name, linking_module).is_some() { - return Err(TestGeneratorError::DuplicateModuleName( - module.name.to_string(), - )); + if linked_modules.insert(name, linking_module).is_some() { + return Err(TestGeneratorError::DuplicateModuleName(name.to_string())); } } diff --git a/crates/marine-test-macro-impl/src/marine_test/module_generator.rs b/crates/marine-test-macro-impl/src/marine_test/token_stream_generator.rs similarity index 62% rename from crates/marine-test-macro-impl/src/marine_test/module_generator.rs rename to crates/marine-test-macro-impl/src/marine_test/token_stream_generator.rs index a41022a..5396301 100644 --- a/crates/marine-test-macro-impl/src/marine_test/module_generator.rs +++ b/crates/marine-test-macro-impl/src/marine_test/token_stream_generator.rs @@ -17,14 +17,20 @@ mod methods_generator; mod methods_generator_utils; mod record_type_generator; +mod service_generator; +mod service_generation_utils; -use crate::marine_test::utils; use crate::marine_test::config_utils::Module; +use crate::marine_test::modules_linker::{LinkedModule, LinkedModules, UseDescription}; +use crate::marine_test::utils; use crate::TResult; -use crate::marine_test::modules_linker::{LinkedModules, LinkedModule}; + +pub(super) use service_generator::generate_service_definitions; +pub(super) use service_generation_utils::generate_app_service_ctor; use proc_macro2::TokenStream; use quote::quote; +use crate::marine_test::utils::new_ident; /// Generates definitions of modules and records of this modules. /// F.e. for the greeting service the following definitions would be generated: @@ -61,47 +67,59 @@ pub(super) fn generate_module_definitions<'i>( ) -> TResult> { modules .into_iter() - .map(|value| generate_module_definition(value, linked_modules.get(&value.name).unwrap())) // linked_modules are built from modules + .map(|value| { + // linked_modules are built from modules, so unwrap is safe + let content = generate_module_definition( + value, + linked_modules.get(&value.name).unwrap(), + module_import_generator, + )?; + let module_ident = new_ident(&value.name)?; + Ok(quote! { + pub mod #module_ident { + #content + } + }) + }) .collect::>>() } fn generate_module_definition( module: &Module<'_>, linked_module: &'_ LinkedModule<'_>, + import_generator: fn(info: &UseDescription<'_>) -> TResult, ) -> TResult { - let module_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(linked_module)?; + let module_records = record_type_generator::generate_records(linked_module, import_generator)?; let module_functions = methods_generator::generate_module_methods( - module_name, - module_interface.function_signatures.iter(), - &module_interface.record_types, + module.name, + module.interface.function_signatures.iter(), + &module.interface.record_types, )?; let module_definition = quote! { - // it's a sort of hack: this module structure allows user to import structs by - // using marine_env_test::module_name::StructName; - pub mod #module_ident { - #(#module_records)* + #(#module_records)* - pub struct #struct_ident { - marine: std::rc::Rc, >, - } + pub struct #struct_ident { + marine: std::rc::Rc, >, + } - impl #struct_ident { - pub fn new(marine: std::rc::Rc, >) -> Self { - Self { marine } - } + impl #struct_ident { + pub fn new(marine: std::rc::Rc, >) -> Self { + Self { marine } } + } - impl #struct_ident { - #(#module_functions)* - } + impl #struct_ident { + #(#module_functions)* } }; Ok(module_definition) } + +fn module_import_generator(info: &UseDescription<'_>) -> TResult { + let from_module_ident = utils::new_ident(info.from)?; + let record_name_ident = utils::new_ident(info.name)?; + Ok(quote! {pub use super::#from_module_ident::#record_name_ident;}) +} diff --git a/crates/marine-test-macro-impl/src/marine_test/module_generator/methods_generator.rs b/crates/marine-test-macro-impl/src/marine_test/token_stream_generator/methods_generator.rs similarity index 67% rename from crates/marine-test-macro-impl/src/marine_test/module_generator/methods_generator.rs rename to crates/marine-test-macro-impl/src/marine_test/token_stream_generator/methods_generator.rs index 893cffa..c458958 100644 --- a/crates/marine-test-macro-impl/src/marine_test/module_generator/methods_generator.rs +++ b/crates/marine-test-macro-impl/src/marine_test/token_stream_generator/methods_generator.rs @@ -46,3 +46,26 @@ pub(super) fn generate_module_methods<'m, 'r>( }, ) } + +pub fn generate_facade_methods<'m, 'r>( + method_signatures: impl ExactSizeIterator, + records: &'r IRecordTypes, +) -> TResult> { + use CallParametersSettings::*; + + let methods_count = 2 * method_signatures.len(); + 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_forward(&signature, Default, records)?; + let user_cp = generate_module_method_forward(&signature, UserDefined, records)?; + + methods.push(default_cp); + methods.push(user_cp); + + Ok(methods) + }, + ) +} diff --git a/crates/marine-test-macro-impl/src/marine_test/module_generator/methods_generator_utils.rs b/crates/marine-test-macro-impl/src/marine_test/token_stream_generator/methods_generator_utils.rs similarity index 78% rename from crates/marine-test-macro-impl/src/marine_test/module_generator/methods_generator_utils.rs rename to crates/marine-test-macro-impl/src/marine_test/token_stream_generator/methods_generator_utils.rs index 9156381..91def5c 100644 --- a/crates/marine-test-macro-impl/src/marine_test/module_generator/methods_generator_utils.rs +++ b/crates/marine-test-macro-impl/src/marine_test/token_stream_generator/methods_generator_utils.rs @@ -42,25 +42,28 @@ pub(super) fn generate_module_method( let output_type = generate_output_type(&signature.outputs, records)?; let mcall = generate_marine_call(module_name, cp_setting, &signature, records)?; - let (cp, func_name) = match cp_setting { - CallParametersSettings::Default => { - let func_name = new_ident(&signature.name)?; - (TokenStream::new(), func_name) - } - CallParametersSettings::UserDefined => { - let maybe_comma = if signature.arguments.is_empty() { - TokenStream::new() - } else { - quote! { , } - }; + let (cp, func_name) = generate_call_parameters(&cp_setting, signature)?; - let cp = quote! { #maybe_comma cp: marine_rs_sdk_test::CallParameters }; - let func_name = format!("{}_cp", signature.name); - let func_name = new_ident(&func_name)?; - (cp, func_name) + let module_method = quote! { + pub fn #func_name(&mut self, #(#arguments),* #cp) #output_type { + #mcall } }; + Ok(module_method) +} + +pub(super) fn generate_module_method_forward( + signature: &IFunctionSignature, + cp_setting: CallParametersSettings, + records: &IRecordTypes, +) -> TResult { + let arguments = generate_arguments(signature.arguments.iter(), records)?; + let output_type = generate_output_type(&signature.outputs, records)?; + let mcall = generate_forward_call(cp_setting, &signature)?; + + let (cp, func_name) = generate_call_parameters(&cp_setting, signature)?; + let module_method = quote! { pub fn #func_name(&mut self, #(#arguments),* #cp) #output_type { #mcall @@ -100,6 +103,30 @@ fn generate_marine_call( Ok(function_call) } +fn generate_forward_call( + cp_settings: CallParametersSettings, + method_signature: &IFunctionSignature, +) -> TResult { + let mut args = method_signature + .arguments + .iter() + .map(|a| new_ident(a.name.as_str())) + .collect::>>()?; + + let method_name = if let CallParametersSettings::UserDefined = cp_settings { + args.push(new_ident("cp")?); + new_ident(format!("{}_cp", method_signature.name.as_str()).as_str())? + } else { + new_ident(method_signature.name.as_str())? + }; + + let function_call = quote! { + self.__facade.#method_name(#(#args,)*) + }; + + Ok(function_call) +} + /// Generates type convertor to json because of AppService receives them in json. fn generate_arguments_converter<'a>( args: impl ExactSizeIterator, @@ -192,3 +219,27 @@ fn get_output_type(output_types: &[IType]) -> TResult> { _ => Err(ManyFnOutputsUnsupported), } } + +fn generate_call_parameters( + cp_setting: &CallParametersSettings, + signature: &IFunctionSignature, +) -> TResult<(TokenStream, syn::Ident)> { + match cp_setting { + CallParametersSettings::Default => { + let func_name = new_ident(&signature.name)?; + Ok((TokenStream::new(), func_name)) + } + CallParametersSettings::UserDefined => { + let maybe_comma = if signature.arguments.is_empty() { + TokenStream::new() + } else { + quote! { , } + }; + + let cp = quote! { #maybe_comma cp: marine_rs_sdk_test::CallParameters }; + let func_name = format!("{}_cp", signature.name); + let func_name = new_ident(&func_name)?; + Ok((cp, func_name)) + } + } +} diff --git a/crates/marine-test-macro-impl/src/marine_test/module_generator/record_type_generator.rs b/crates/marine-test-macro-impl/src/marine_test/token_stream_generator/record_type_generator.rs similarity index 85% rename from crates/marine-test-macro-impl/src/marine_test/module_generator/record_type_generator.rs rename to crates/marine-test-macro-impl/src/marine_test/token_stream_generator/record_type_generator.rs index 1e7bd16..6dd0400 100644 --- a/crates/marine-test-macro-impl/src/marine_test/module_generator/record_type_generator.rs +++ b/crates/marine-test-macro-impl/src/marine_test/token_stream_generator/record_type_generator.rs @@ -23,21 +23,20 @@ use marine_it_parser::it_interface::IRecordTypes; use proc_macro2::TokenStream; use quote::quote; -use crate::marine_test::modules_linker::{LinkedModule, RecordEntry}; +use crate::marine_test::modules_linker::{LinkedModule, RecordEntry, UseDescription}; use itertools::Itertools; -pub(super) fn generate_records(linked_module: &LinkedModule<'_>) -> TResult> { +pub(super) fn generate_records( + linked_module: &LinkedModule<'_>, + import_generator: fn(info: &UseDescription<'_>) -> TResult, +) -> TResult> { 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;}) - }, + Use(use_info) => import_generator(use_info), Declare(record) => { let record_name_ident = utils::new_ident(&record.record_type.name)?; let fields = prepare_field(record.record_type.fields.iter(), record.records)?; diff --git a/crates/marine-test-macro-impl/src/marine_test/token_stream_generator/service_generation_utils.rs b/crates/marine-test-macro-impl/src/marine_test/token_stream_generator/service_generation_utils.rs new file mode 100644 index 0000000..6d09490 --- /dev/null +++ b/crates/marine-test-macro-impl/src/marine_test/token_stream_generator/service_generation_utils.rs @@ -0,0 +1,204 @@ +use crate::TResult; +use crate::TestGeneratorError; +use crate::marine_test::config_utils::Module; +use crate::marine_test::utils::new_ident; +use crate::marine_test::{modules_linker, config_utils}; +use crate::marine_test::modules_linker::{LinkedModule, UseDescription}; +use super::service_generator::{ProcessedService, get_facace}; + +use proc_macro2::TokenStream; +use quote::quote; + +use std::path::Path; +use itertools::Itertools; + +pub(crate) fn generate_app_service_ctor( + config_path: &str, + modules_dir: &Path, +) -> TResult { + let modules_dir = modules_dir + .to_str() + .ok_or_else(|| TestGeneratorError::InvalidUTF8Path(modules_dir.to_path_buf()))?; + + let service_ctor = quote! { + 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_path); + let modules_dir = module_path.join(#modules_dir); + 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)); + }; + + Ok(service_ctor) +} + +pub(super) fn generate_service_definition( + service: &ProcessedService, + test_file_path: &Path, + linked_facade: &LinkedModule<'_>, +) -> TResult { + let modules_dir_test_relative = test_file_path.join(&service.config.resolved_modules_dir); + let modules = + config_utils::collect_modules(&service.config.config, &modules_dir_test_relative)?; + let linked_modules = modules_linker::link_modules( + modules + .iter() + .map(|module| (module.name, &module.interface)), + )?; + + let service_mod = new_ident(&service.name)?; + let module_definitions = super::generate_module_definitions(modules.iter(), &linked_modules)?; + + let facade = get_facace(&modules)?; + + let facade_interface = super::methods_generator::generate_facade_methods( + facade.interface.function_signatures.iter(), + &facade.interface.record_types, + )?; + + let facade_override = + super::generate_module_definition(facade, linked_facade, service_import_generator)?; + let facade_override_ident = new_ident("__facade_override")?; + let facade_structs = generate_facade_structs(facade, &facade_override_ident)?; + + let app_service_ctor = + generate_app_service_ctor(&service.config_path, &service.config.resolved_modules_dir)?; + let modules_type = generate_modules_type(&modules)?; + + let service_definition = quote! { + pub mod #service_mod { + pub mod modules { + #(#module_definitions)* + } + + pub mod #facade_override_ident { + #facade_override + } + + #(#facade_structs)* + + #modules_type + + pub struct ServiceInterface { + pub modules: __GeneratedModules, + __facade: #facade_override_ident::ModuleInterface, + marine: std::rc::Rc, > + } + + impl ServiceInterface { + pub fn new() -> Self { + #app_service_ctor + let modules = __GeneratedModules::new(marine.clone()); + let __facade = #facade_override_ident::ModuleInterface::new(marine.clone()); + Self { + marine, + modules, + __facade + } + } + + #(#facade_interface)* + } + } + }; + + Ok(service_definition) +} + +fn service_import_generator(info: &UseDescription<'_>) -> TResult { + let from_module_ident = new_ident(info.from)?; + let record_name_ident = new_ident(info.name)?; + Ok(quote! {pub use super::super::#from_module_ident::#record_name_ident;}) +} + +fn generate_facade_structs( + module: &Module<'_>, + module_name: &syn::Ident, +) -> TResult> { + module + .interface + .record_types + .iter() + .sorted_by_key(|(_, record)| &record.name) + .map(|(_, record)| -> TResult { + let record_name = new_ident(&record.name)?; + let result = quote! {pub use #module_name::#record_name;}; + Ok(result) + }) + .collect::>>() +} + +fn generate_modules_type(module_interfaces: &[Module<'_>]) -> TResult { + let fields = module_interfaces + .iter() + .map(|module| -> TResult { + let name = new_ident(module.name)?; + Ok(quote! {pub #name: modules::#name::ModuleInterface}) + }) + .collect::>>()?; + + let ctors = module_interfaces + .iter() + .map(|module| -> TResult { + let name = new_ident(module.name)?; + Ok(quote! {#name: modules::#name::ModuleInterface::new(marine.clone())}) + }) + .collect::>>()?; + + let ty = quote! { + pub struct __GeneratedModules { + #(#fields,)* + } + + impl __GeneratedModules { + fn new(marine: std::rc::Rc, >) -> Self { + Self { + #(#ctors,)* + } + } + } + }; + + Ok(ty) +} diff --git a/crates/marine-test-macro-impl/src/marine_test/token_stream_generator/service_generator.rs b/crates/marine-test-macro-impl/src/marine_test/token_stream_generator/service_generator.rs new file mode 100644 index 0000000..ee2cf63 --- /dev/null +++ b/crates/marine-test-macro-impl/src/marine_test/token_stream_generator/service_generator.rs @@ -0,0 +1,110 @@ +/* + * 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::attributes::{ServiceDescription}; +use crate::TResult; +use crate::TestGeneratorError; +use crate::marine_test::config_utils::{Module, ConfigWrapper, load_config}; +use crate::marine_test::{modules_linker, config_utils}; +use crate::marine_test::modules_linker::LinkedModules; +use super::service_generation_utils::generate_service_definition; + +use marine_it_parser::it_interface::IModuleInterface; +use proc_macro2::TokenStream; +use itertools::{Itertools, zip}; + +use std::path::Path; +use std::collections::HashMap; + +pub(crate) fn generate_service_definitions( + services: HashMap, + file_path: &Path, +) -> TResult> { + let services = services + .into_iter() + .sorted_by(|lhs, rhs| lhs.0.cmp(&rhs.0)) // deterministic output required for tests + .map(|(name, service)| ProcessedService::new(service, name, file_path)) + .collect::>>()?; + + let service_modules = services + .iter() + .map(|service| { + let modules_dir_test_relative = file_path.join(&service.config.resolved_modules_dir); + let modules = + config_utils::collect_modules(&service.config.config, &modules_dir_test_relative)?; + Ok(modules) + }) + .collect::>>>>()?; + + let link_info = link_services(zip(&services, &service_modules))?; + services + .iter() + .map(|service| -> TResult { + // entry with service.name was added in link_services(...), so unwrap is safe + generate_service_definition( + &service, + file_path, + link_info.get::(&service.name).unwrap(), + ) + }) + .collect::>>() +} + +pub(super) fn get_facace<'modules, 'm>( + modules: &'modules [Module<'m>], +) -> TResult<&'modules Module<'m>> { + match modules.last() { + Some(module) => Ok(module), + None => Err(TestGeneratorError::NoModulesInService), + } +} + +pub(super) struct ProcessedService { + pub(super) config: ConfigWrapper, + pub(super) config_path: String, + pub(super) name: String, +} + +impl ProcessedService { + pub(crate) fn new( + service: ServiceDescription, + name: String, + file_path: &Path, + ) -> TResult { + let config_wrapper = load_config(&service.config_path, service.modules_dir, &file_path)?; + + Ok(Self { + config: config_wrapper, + config_path: service.config_path, + name, + }) + } +} + +fn link_services<'modules>( + services: impl ExactSizeIterator< + Item = (&'modules ProcessedService, &'modules Vec>), + >, +) -> TResult> { + let facade_modules = services + .map(|(service, modules)| { + let facade = get_facace(modules)?; + Ok((service.name.as_str(), &facade.interface)) + }) + .collect::>>()?; + + modules_linker::link_modules(facade_modules.iter().copied()) +} diff --git a/crates/marine-test-macro-impl/tests/generation_tests/multi-service-multiple/empty_func/Config.toml b/crates/marine-test-macro-impl/tests/generation_tests/multi-service-multiple/empty_func/Config.toml new file mode 100644 index 0000000..cd6d314 --- /dev/null +++ b/crates/marine-test-macro-impl/tests/generation_tests/multi-service-multiple/empty_func/Config.toml @@ -0,0 +1,6 @@ +modules_dir = "artifacts/" + +[[module]] + name = "greeting" + mem_pages_count = 50 + logger_enabled = false diff --git a/crates/marine-test-macro-impl/tests/generation_tests/multi-service-multiple/empty_func/artifacts/greeting.wasm b/crates/marine-test-macro-impl/tests/generation_tests/multi-service-multiple/empty_func/artifacts/greeting.wasm new file mode 100755 index 0000000000000000000000000000000000000000..20cd5a21c399164aeb704b39ac9869f2c400597c GIT binary patch literal 56806 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 NAMoGXEjlm6{sp^<9NGW? literal 0 HcmV?d00001 diff --git a/crates/marine-test-macro-impl/tests/generation_tests/multi-service-multiple/expanded.rs b/crates/marine-test-macro-impl/tests/generation_tests/multi-service-multiple/expanded.rs new file mode 100644 index 0000000..b9381a7 --- /dev/null +++ b/crates/marine-test-macro-impl/tests/generation_tests/multi-service-multiple/expanded.rs @@ -0,0 +1,504 @@ +#[test] +fn test() { + pub mod marine_test_env { + pub mod empty_func { + pub mod modules { + 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> + } + #[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, + pub stderr: Vec + } + #[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, + >, + } + impl ModuleInterface { + pub fn new( + marine: std::rc::Rc< + std::cell::RefCell, + > + ) -> Self { + Self { marine } + } + } + impl ModuleInterface {} + } + } + pub mod __facade_override { + #[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> + } + #[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, + pub stderr: Vec + } + #[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, + >, + } + impl ModuleInterface { + pub fn new( + marine: std::rc::Rc< + std::cell::RefCell, + > + ) -> Self { + Self { marine } + } + } + impl ModuleInterface {} + } + pub use __facade_override::CallParameters; + pub use __facade_override::MountedBinaryResult; + pub use __facade_override::MountedBinaryStringResult; + pub use __facade_override::SecurityTetraplet; + pub struct __GeneratedModules { + pub greeting: modules::greeting::ModuleInterface, + } + impl __GeneratedModules { + fn new( + marine: std::rc::Rc< + std::cell::RefCell, + > + ) -> Self { + Self { + greeting: modules::greeting::ModuleInterface::new(marine.clone()), + } + } + } + pub struct ServiceInterface { + pub modules: __GeneratedModules, + __facade: __facade_override::ModuleInterface, + marine: std::rc::Rc, > + } + impl ServiceInterface { + pub fn new() -> Self { + 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("empty_func/Config.toml"); + let modules_dir = module_path.join("empty_func/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 modules = __GeneratedModules::new(marine.clone()); + let __facade = __facade_override::ModuleInterface::new(marine.clone()); + Self { + marine, + modules, + __facade + } + } + } + } + pub mod mounted_binary { + pub mod modules { + 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> + } + #[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, + pub stderr: Vec + } + #[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, + >, + } + impl ModuleInterface { + pub fn new( + marine: std::rc::Rc< + std::cell::RefCell, + > + ) -> 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 + } + } + } + } + pub mod __facade_override { + pub use super::super::empty_func::CallParameters; + pub use super::super::empty_func::MountedBinaryResult; + pub use super::super::empty_func::MountedBinaryStringResult; + pub use super::super::empty_func::SecurityTetraplet; + pub struct ModuleInterface { + marine: + std::rc::Rc, + >, + } + impl ModuleInterface { + pub fn new( + marine: std::rc::Rc< + std::cell::RefCell, + > + ) -> 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 + } + } + } + pub use __facade_override::CallParameters; + pub use __facade_override::MountedBinaryResult; + pub use __facade_override::MountedBinaryStringResult; + pub use __facade_override::SecurityTetraplet; + pub struct __GeneratedModules { + pub greeting: modules::greeting::ModuleInterface, + } + impl __GeneratedModules { + fn new( + marine: std::rc::Rc< + std::cell::RefCell, + > + ) -> Self { + Self { + greeting: modules::greeting::ModuleInterface::new(marine.clone()), + } + } + } + pub struct ServiceInterface { + pub modules: __GeneratedModules, + __facade: __facade_override::ModuleInterface, + marine: std::rc::Rc, > + } + impl ServiceInterface { + pub fn new() -> Self { + 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("mounted_binary/Config.toml"); + let modules_dir = module_path.join("mounted_binary/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 modules = __GeneratedModules::new(marine.clone()); + let __facade = __facade_override::ModuleInterface::new(marine.clone()); + Self { + marine, + modules, + __facade + } + } + pub fn download(&mut self, url: String) -> String { + self.__facade.download(url,) + } + pub fn download_cp( + &mut self, + url: String, + cp: marine_rs_sdk_test::CallParameters + ) -> String { + self.__facade.download_cp(url, cp,) + } + } + } + } + fn test_func() { + { + let mut greeting = marine_test_env::greeting::ServiceInterface::new(); + let _ = greeting.download("duckduckgo.com"); + } + } + test_func() +} diff --git a/crates/marine-test-macro-impl/tests/generation_tests/multi-service-multiple/marine_test.rs b/crates/marine-test-macro-impl/tests/generation_tests/multi-service-multiple/marine_test.rs new file mode 100644 index 0000000..afff316 --- /dev/null +++ b/crates/marine-test-macro-impl/tests/generation_tests/multi-service-multiple/marine_test.rs @@ -0,0 +1,4 @@ +fn test() { + let mut greeting = marine_test_env::greeting::ServiceInterface::new(); + let _ = greeting.download("duckduckgo.com"); +} diff --git a/crates/marine-test-macro-impl/tests/generation_tests/multi-service-multiple/mounted_binary/Config.toml b/crates/marine-test-macro-impl/tests/generation_tests/multi-service-multiple/mounted_binary/Config.toml new file mode 100644 index 0000000..f642154 --- /dev/null +++ b/crates/marine-test-macro-impl/tests/generation_tests/multi-service-multiple/mounted_binary/Config.toml @@ -0,0 +1,8 @@ +modules_dir = "artifacts/" + +[[module]] + name = "greeting" + mem_pages_count = 50 + logger_enabled = false + [module.mounted_binaries] + echo = "/usr/bin/curl" diff --git a/crates/marine-test-macro-impl/tests/generation_tests/multi-service-multiple/mounted_binary/artifacts/greeting.wasm b/crates/marine-test-macro-impl/tests/generation_tests/multi-service-multiple/mounted_binary/artifacts/greeting.wasm new file mode 100755 index 0000000000000000000000000000000000000000..7f605b6be4eff3044ccf3ebc5a193c85167ea40e GIT binary patch literal 64072 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 Pwt&zcASb0ux$OS{7*d%7 literal 0 HcmV?d00001 diff --git a/crates/marine-test-macro-impl/tests/generation_tests/multi-service-single/empty_func/Config.toml b/crates/marine-test-macro-impl/tests/generation_tests/multi-service-single/empty_func/Config.toml new file mode 100644 index 0000000..cd6d314 --- /dev/null +++ b/crates/marine-test-macro-impl/tests/generation_tests/multi-service-single/empty_func/Config.toml @@ -0,0 +1,6 @@ +modules_dir = "artifacts/" + +[[module]] + name = "greeting" + mem_pages_count = 50 + logger_enabled = false diff --git a/crates/marine-test-macro-impl/tests/generation_tests/multi-service-single/empty_func/artifacts/greeting.wasm b/crates/marine-test-macro-impl/tests/generation_tests/multi-service-single/empty_func/artifacts/greeting.wasm new file mode 100755 index 0000000000000000000000000000000000000000..20cd5a21c399164aeb704b39ac9869f2c400597c GIT binary patch literal 56806 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 NAMoGXEjlm6{sp^<9NGW? literal 0 HcmV?d00001 diff --git a/crates/marine-test-macro-impl/tests/generation_tests/multi-service-single/expanded.rs b/crates/marine-test-macro-impl/tests/generation_tests/multi-service-single/expanded.rs new file mode 100644 index 0000000..4c3c7cc --- /dev/null +++ b/crates/marine-test-macro-impl/tests/generation_tests/multi-service-single/expanded.rs @@ -0,0 +1,242 @@ +#[test] +fn empty_test() { + pub mod marine_test_env { + pub mod empty_func { + pub mod modules { + 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> + } + #[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, + pub stderr: Vec + } + #[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, + >, + } + impl ModuleInterface { + pub fn new( + marine: std::rc::Rc< + std::cell::RefCell, + > + ) -> Self { + Self { marine } + } + } + impl ModuleInterface {} + } + } + pub mod __facade_override { + #[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> + } + #[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, + pub stderr: Vec + } + #[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, + >, + } + impl ModuleInterface { + pub fn new( + marine: std::rc::Rc< + std::cell::RefCell, + > + ) -> Self { + Self { marine } + } + } + impl ModuleInterface {} + } + pub use __facade_override::CallParameters; + pub use __facade_override::MountedBinaryResult; + pub use __facade_override::MountedBinaryStringResult; + pub use __facade_override::SecurityTetraplet; + pub struct __GeneratedModules { + pub greeting: modules::greeting::ModuleInterface, + } + impl __GeneratedModules { + fn new( + marine: std::rc::Rc< + std::cell::RefCell, + > + ) -> Self { + Self { + greeting: modules::greeting::ModuleInterface::new(marine.clone()), + } + } + } + pub struct ServiceInterface { + pub modules: __GeneratedModules, + __facade: __facade_override::ModuleInterface, + marine: std::rc::Rc, > + } + impl ServiceInterface { + pub fn new() -> Self { + 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("empty_func/Config.toml"); + let modules_dir = module_path.join("empty_func/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 modules = __GeneratedModules::new(marine.clone()); + let __facade = __facade_override::ModuleInterface::new(marine.clone()); + Self { + marine, + modules, + __facade + } + } + } + } + } + fn test_func() { + {} + } + test_func() +} diff --git a/crates/marine-test-macro-impl/tests/generation_tests/multi-service-single/marine_test.rs b/crates/marine-test-macro-impl/tests/generation_tests/multi-service-single/marine_test.rs new file mode 100644 index 0000000..1444025 --- /dev/null +++ b/crates/marine-test-macro-impl/tests/generation_tests/multi-service-single/marine_test.rs @@ -0,0 +1 @@ +fn empty_test() {} diff --git a/crates/marine-test-macro-impl/tests/generation_tests_runner.rs b/crates/marine-test-macro-impl/tests/generation_tests_runner.rs index e10d6c1..53221ea 100644 --- a/crates/marine-test-macro-impl/tests/generation_tests_runner.rs +++ b/crates/marine-test-macro-impl/tests/generation_tests_runner.rs @@ -17,6 +17,8 @@ mod utils; use utils::test_marine_test_token_streams; +use utils::TestServiceDescription; +use utils::test_marine_test_token_streams_multiservice; #[test] fn test_empty_func() { @@ -47,3 +49,38 @@ fn test_multiple_modules() { "artifacts" )); } + +#[test] +fn test_multiservice_single() { + let descriptions = vec![TestServiceDescription { + modules_dir: "empty_func/artifacts", + config_path: "empty_func/Config.toml", + name: "empty_func", + }]; + assert!(test_marine_test_token_streams_multiservice( + "tests/generation_tests/multi-service-single/marine_test.rs", + "tests/generation_tests/multi-service-single/expanded.rs", + descriptions + )); +} + +#[test] +fn test_multiservice_multiple() { + let descriptions = vec![ + TestServiceDescription { + modules_dir: "empty_func/artifacts", + config_path: "empty_func/Config.toml", + name: "empty_func", + }, + TestServiceDescription { + modules_dir: "mounted_binary/artifacts", + config_path: "mounted_binary/Config.toml", + name: "mounted_binary", + }, + ]; + assert!(test_marine_test_token_streams_multiservice( + "tests/generation_tests/multi-service-multiple/marine_test.rs", + "tests/generation_tests/multi-service-multiple/expanded.rs", + descriptions + )); +} diff --git a/crates/marine-test-macro-impl/tests/utils.rs b/crates/marine-test-macro-impl/tests/utils.rs index 68a3384..1139659 100644 --- a/crates/marine-test-macro-impl/tests/utils.rs +++ b/crates/marine-test-macro-impl/tests/utils.rs @@ -33,7 +33,56 @@ where 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 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 +} + +pub struct TestServiceDescription { + pub config_path: &'static str, + pub modules_dir: &'static str, + pub name: &'static str, +} + +pub fn test_marine_test_token_streams_multiservice( + marine_path: FP, + expanded_path: EP, + services: Vec, +) -> bool +where + FP: AsRef, + EP: AsRef, +{ + 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 service_declarations = services + .iter() + .map(|desc| { + let config_path = desc.config_path; + let modules_dir = desc.modules_dir; + let name = syn::parse_str::(desc.name)?; + Ok(quote::quote! {#name(config_path = #config_path, modules_dir = #modules_dir)}) + }) + .collect::, syn::Error>>() + .unwrap_or_else(|e| panic!("failed to parse test arguments due to {}", e)); + + let attrs = quote::quote! { + #(#service_declarations,)* + }; + let marine_token_streams = marine_test_impl( attrs, test_token_stream,