Implement marine_test for services integration (first part) (#61)

This commit is contained in:
Valery Antopol 2021-10-04 19:06:58 +03:00 committed by GitHub
parent 715e2f0e7e
commit 631c6d9885
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1443 additions and 143 deletions

View File

@ -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<String, ServiceDescription>),
}
#[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<String>,
}
impl FromMeta for MTestAttributes {
fn from_list(items: &[syn::NestedMeta]) -> darling::Result<Self> {
let single_service = ServiceDescription::from_list(items);
let multiple_services = HashMap::<String, ServiceDescription>::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",
)),
}
}
}

View File

@ -56,6 +56,9 @@ pub enum TestGeneratorError {
#[error("Duplicate module: {0}")]
DuplicateModuleName(String),
#[error("No modules loaded for a service")]
NoModulesInService,
}
#[derive(Debug, ThisError)]

View File

@ -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<String>,
file_path: &Path,
) -> TResult<ConfigWrapper> {
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<Vec<Module<'_>>> {
let module_paths = collect_module_paths(config, modules_dir);
pub(super) fn collect_modules<'config>(
config: &'config TomlAppServiceConfig,
modules_dir: &Path,
) -> TResult<Vec<Module<'config>>> {
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

View File

@ -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<TokenStream> {
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<TokenStream> {
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<TokenStream> {
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<String, ServiceDescription>,
test_file_path: PathBuf,
) -> TResult<TokenStream> {
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>(

View File

@ -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;

View File

@ -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<Item = (&'modules str, &'modules IModuleInterface)>,
) -> TResult<LinkedModules<'modules>> {
let mut all_record_types = HashMap::<IRecordTypeClosed<'_>, &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()));
}
}

View File

@ -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<Vec<TokenStream>> {
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::<TResult<Vec<_>>>()
}
fn generate_module_definition(
module: &Module<'_>,
linked_module: &'_ LinkedModule<'_>,
import_generator: fn(info: &UseDescription<'_>) -> TResult<TokenStream>,
) -> TResult<TokenStream> {
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<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)*
}
};
Ok(module_definition)
}
fn module_import_generator(info: &UseDescription<'_>) -> TResult<TokenStream> {
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;})
}

View File

@ -46,3 +46,26 @@ pub(super) fn generate_module_methods<'m, 'r>(
},
)
}
pub fn generate_facade_methods<'m, 'r>(
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
.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)
},
)
}

View File

@ -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<TokenStream> {
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<TokenStream> {
let mut args = method_signature
.arguments
.iter()
.map(|a| new_ident(a.name.as_str()))
.collect::<TResult<Vec<syn::Ident>>>()?;
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<Item = &'a str>,
@ -192,3 +219,27 @@ fn get_output_type(output_types: &[IType]) -> TResult<Option<&IType>> {
_ => 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))
}
}
}

View File

@ -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<Vec<TokenStream>> {
pub(super) fn generate_records(
linked_module: &LinkedModule<'_>,
import_generator: fn(info: &UseDescription<'_>) -> TResult<TokenStream>,
) -> 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;})
},
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)?;

View File

@ -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<TokenStream> {
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<TokenStream> {
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<std::cell::RefCell<marine_rs_sdk_test::internal::AppService>, >
}
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<TokenStream> {
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<Vec<TokenStream>> {
module
.interface
.record_types
.iter()
.sorted_by_key(|(_, record)| &record.name)
.map(|(_, record)| -> TResult<TokenStream> {
let record_name = new_ident(&record.name)?;
let result = quote! {pub use #module_name::#record_name;};
Ok(result)
})
.collect::<TResult<Vec<TokenStream>>>()
}
fn generate_modules_type(module_interfaces: &[Module<'_>]) -> TResult<TokenStream> {
let fields = module_interfaces
.iter()
.map(|module| -> TResult<TokenStream> {
let name = new_ident(module.name)?;
Ok(quote! {pub #name: modules::#name::ModuleInterface})
})
.collect::<TResult<Vec<TokenStream>>>()?;
let ctors = module_interfaces
.iter()
.map(|module| -> TResult<TokenStream> {
let name = new_ident(module.name)?;
Ok(quote! {#name: modules::#name::ModuleInterface::new(marine.clone())})
})
.collect::<TResult<Vec<TokenStream>>>()?;
let ty = quote! {
pub struct __GeneratedModules {
#(#fields,)*
}
impl __GeneratedModules {
fn new(marine: std::rc::Rc<std::cell::RefCell<marine_rs_sdk_test::internal::AppService>, >) -> Self {
Self {
#(#ctors,)*
}
}
}
};
Ok(ty)
}

View File

@ -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<String, ServiceDescription>,
file_path: &Path,
) -> TResult<Vec<TokenStream>> {
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::<TResult<Vec<ProcessedService>>>()?;
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::<TResult<Vec<Vec<Module<'_>>>>>()?;
let link_info = link_services(zip(&services, &service_modules))?;
services
.iter()
.map(|service| -> TResult<TokenStream> {
// entry with service.name was added in link_services(...), so unwrap is safe
generate_service_definition(
&service,
file_path,
link_info.get::<str>(&service.name).unwrap(),
)
})
.collect::<TResult<Vec<TokenStream>>>()
}
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<Self> {
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<Module<'modules>>),
>,
) -> TResult<LinkedModules<'modules>> {
let facade_modules = services
.map(|(service, modules)| {
let facade = get_facace(modules)?;
Ok((service.name.as_str(), &facade.interface))
})
.collect::<TResult<Vec<(&str, &IModuleInterface)>>>()?;
modules_linker::link_modules(facade_modules.iter().copied())
}

View File

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

View File

@ -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<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 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<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 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<marine_rs_sdk_test::internal::AppService>,
>
) -> Self {
Self {
greeting: modules::greeting::ModuleInterface::new(marine.clone()),
}
}
}
pub struct ServiceInterface {
pub modules: __GeneratedModules,
__facade: __facade_override::ModuleInterface,
marine: std::rc::Rc<std::cell::RefCell<marine_rs_sdk_test::internal::AppService>, >
}
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<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
}
}
}
}
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<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
}
}
}
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<marine_rs_sdk_test::internal::AppService>,
>
) -> Self {
Self {
greeting: modules::greeting::ModuleInterface::new(marine.clone()),
}
}
}
pub struct ServiceInterface {
pub modules: __GeneratedModules,
__facade: __facade_override::ModuleInterface,
marine: std::rc::Rc<std::cell::RefCell<marine_rs_sdk_test::internal::AppService>, >
}
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()
}

View File

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

View File

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

View File

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

View File

@ -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<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 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<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 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<marine_rs_sdk_test::internal::AppService>,
>
) -> Self {
Self {
greeting: modules::greeting::ModuleInterface::new(marine.clone()),
}
}
}
pub struct ServiceInterface {
pub modules: __GeneratedModules,
__facade: __facade_override::ModuleInterface,
marine: std::rc::Rc<std::cell::RefCell<marine_rs_sdk_test::internal::AppService>, >
}
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()
}

View File

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

View File

@ -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
));
}

View File

@ -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<FP, EP>(
marine_path: FP,
expanded_path: EP,
services: Vec<TestServiceDescription>,
) -> 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 service_declarations = services
.iter()
.map(|desc| {
let config_path = desc.config_path;
let modules_dir = desc.modules_dir;
let name = syn::parse_str::<syn::Ident>(desc.name)?;
Ok(quote::quote! {#name(config_path = #config_path, modules_dir = #modules_dir)})
})
.collect::<Result<Vec<_>, 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,