mirror of
https://github.com/fluencelabs/marine-rs-sdk-test
synced 2025-04-24 23:12:13 +00:00
Implement marine_test for services integration (first part) (#61)
This commit is contained in:
parent
715e2f0e7e
commit
631c6d9885
@ -15,10 +15,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use darling::FromMeta;
|
use darling::FromMeta;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
/// Describes attributes of `marine_test` macro.
|
/// Describes attributes of `marine_test` macro.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) enum MTestAttributes {
|
||||||
|
SingleService(ServiceDescription),
|
||||||
|
MultipleServices(HashMap<String, ServiceDescription>),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, FromMeta)]
|
#[derive(Debug, Default, Clone, FromMeta)]
|
||||||
pub(crate) struct MTestAttributes {
|
pub(crate) struct ServiceDescription {
|
||||||
/// Path to a config file of a tested service.
|
/// Path to a config file of a tested service.
|
||||||
pub(crate) config_path: String,
|
pub(crate) config_path: String,
|
||||||
|
|
||||||
@ -26,3 +33,24 @@ pub(crate) struct MTestAttributes {
|
|||||||
#[darling(default)]
|
#[darling(default)]
|
||||||
pub(crate) modules_dir: Option<String>,
|
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",
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -56,6 +56,9 @@ pub enum TestGeneratorError {
|
|||||||
|
|
||||||
#[error("Duplicate module: {0}")]
|
#[error("Duplicate module: {0}")]
|
||||||
DuplicateModuleName(String),
|
DuplicateModuleName(String),
|
||||||
|
|
||||||
|
#[error("No modules loaded for a service")]
|
||||||
|
NoModulesInService,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, ThisError)]
|
#[derive(Debug, ThisError)]
|
||||||
|
@ -14,13 +14,13 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::TResult;
|
use crate::{TResult, TestGeneratorError};
|
||||||
|
|
||||||
use fluence_app_service::TomlAppServiceConfig;
|
use fluence_app_service::TomlAppServiceConfig;
|
||||||
use marine_it_parser::module_it_interface;
|
use marine_it_parser::module_it_interface;
|
||||||
use marine_it_parser::it_interface::IModuleInterface;
|
use marine_it_parser::it_interface::IModuleInterface;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::{PathBuf, Path};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub(super) struct Module<'m> {
|
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.
|
/// Returns all modules the provided config consists of.
|
||||||
pub(super) fn collect_modules(
|
pub(super) fn collect_modules<'config>(
|
||||||
config: &TomlAppServiceConfig,
|
config: &'config TomlAppServiceConfig,
|
||||||
modules_dir: PathBuf,
|
modules_dir: &Path,
|
||||||
) -> TResult<Vec<Module<'_>>> {
|
) -> TResult<Vec<Module<'config>>> {
|
||||||
let module_paths = collect_module_paths(config, modules_dir);
|
let module_paths = collect_module_paths(config, &modules_dir);
|
||||||
|
|
||||||
module_paths
|
module_paths
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -48,10 +72,10 @@ pub(super) fn collect_modules(
|
|||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_module_paths(
|
fn collect_module_paths<'config>(
|
||||||
config: &TomlAppServiceConfig,
|
config: &'config TomlAppServiceConfig,
|
||||||
modules_dir: PathBuf,
|
modules_dir: &Path,
|
||||||
) -> Vec<(&str, PathBuf)> {
|
) -> Vec<(&'config str, PathBuf)> {
|
||||||
config
|
config
|
||||||
.toml_faas_config
|
.toml_faas_config
|
||||||
.module
|
.module
|
||||||
|
@ -14,20 +14,19 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::attributes::MTestAttributes;
|
use crate::attributes::{MTestAttributes, ServiceDescription};
|
||||||
use crate::TResult;
|
|
||||||
use crate::TestGeneratorError;
|
|
||||||
use crate::marine_test;
|
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 proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
|
|
||||||
use std::path::Path;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use syn::FnArg;
|
use syn::FnArg;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
/// Generates glue code for tests.
|
/// Generates glue code for tests.
|
||||||
/// F.e. for this test for the greeting service
|
/// F.e. for this test for the greeting service
|
||||||
@ -116,23 +115,35 @@ use syn::FnArg;
|
|||||||
pub(super) fn generate_test_glue_code(
|
pub(super) fn generate_test_glue_code(
|
||||||
func_item: syn::ItemFn,
|
func_item: syn::ItemFn,
|
||||||
attrs: MTestAttributes,
|
attrs: MTestAttributes,
|
||||||
file_path: PathBuf,
|
test_file_path: PathBuf,
|
||||||
) -> TResult<TokenStream> {
|
) -> 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)?;
|
fn generate_test_glue_code_single_service(
|
||||||
let modules_dir = match config_utils::resolve_modules_dir(&marine_config, attrs.modules_dir) {
|
func_item: syn::ItemFn,
|
||||||
Some(modules_dir) => modules_dir,
|
service: ServiceDescription,
|
||||||
None => return Err(TestGeneratorError::ModulesDirUnspecified),
|
test_file_path: PathBuf,
|
||||||
};
|
) -> TResult<TokenStream> {
|
||||||
|
let config_wrapper =
|
||||||
let app_service_ctor = generate_app_service_ctor(&attrs.config_path, &modules_dir)?;
|
config_utils::load_config(&service.config_path, service.modules_dir, &test_file_path)?;
|
||||||
let modules_dir = file_path.join(modules_dir);
|
let modules_dir_test_relative = test_file_path.join(&config_wrapper.resolved_modules_dir);
|
||||||
let module_interfaces =
|
let module_interfaces =
|
||||||
marine_test::config_utils::collect_modules(&marine_config, modules_dir)?;
|
config_utils::collect_modules(&config_wrapper.config, &modules_dir_test_relative)?;
|
||||||
let linked_modules = marine_test::modules_linker::link_modules(&module_interfaces)?;
|
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(),
|
module_interfaces.iter(),
|
||||||
&linked_modules,
|
&linked_modules,
|
||||||
)?;
|
)?;
|
||||||
@ -143,7 +154,10 @@ pub(super) fn generate_test_glue_code(
|
|||||||
let inputs = &signature.inputs;
|
let inputs = &signature.inputs;
|
||||||
let arg_names = generate_arg_names(inputs.iter())?;
|
let arg_names = generate_arg_names(inputs.iter())?;
|
||||||
let module_ctors = generate_module_ctors(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! {
|
let glue_code = quote! {
|
||||||
#[test]
|
#[test]
|
||||||
fn #name() {
|
fn #name() {
|
||||||
@ -170,62 +184,34 @@ pub(super) fn generate_test_glue_code(
|
|||||||
Ok(glue_code)
|
Ok(glue_code)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_app_service_ctor(config_path: &str, modules_dir: &Path) -> TResult<TokenStream> {
|
fn generate_test_glue_code_multiple_eservices(
|
||||||
let modules_dir = modules_dir
|
func_item: syn::ItemFn,
|
||||||
.to_str()
|
services: HashMap<String, ServiceDescription>,
|
||||||
.ok_or_else(|| TestGeneratorError::InvalidUTF8Path(modules_dir.to_path_buf()))?;
|
test_file_path: PathBuf,
|
||||||
|
) -> TResult<TokenStream> {
|
||||||
|
let service_definitions =
|
||||||
|
token_stream_generator::generate_service_definitions(services, &test_file_path)?;
|
||||||
|
|
||||||
let service_ctor = quote! {
|
let original_block = func_item.block;
|
||||||
let tmp_dir = std::env::temp_dir();
|
let signature = func_item.sig;
|
||||||
let service_id = marine_rs_sdk_test::internal::Uuid::new_v4().to_string();
|
let name = &signature.ident;
|
||||||
|
let glue_code = quote! {
|
||||||
let tmp_dir = tmp_dir.join(&service_id);
|
#[test]
|
||||||
let tmp_dir = tmp_dir.to_string_lossy().to_string();
|
fn #name() {
|
||||||
std::fs::create_dir(&tmp_dir).expect("can't create a directory for service in tmp");
|
// definitions for services specified in attributes
|
||||||
|
pub mod marine_test_env {
|
||||||
let mut module_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
#(#service_definitions)*
|
||||||
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 {
|
fn test_func() {
|
||||||
std::path::Component::Normal(_) | std::path::Component::CurDir | std::path::Component::ParentDir => Some((file_path, p)),
|
#original_block
|
||||||
_ => None,
|
}
|
||||||
}) {
|
|
||||||
Some(t) => t,
|
|
||||||
None => break,
|
|
||||||
};
|
|
||||||
file_path = file_path_;
|
|
||||||
|
|
||||||
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>(
|
fn generate_module_ctors<'inputs>(
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
mod config_utils;
|
mod config_utils;
|
||||||
mod marine_test_impl;
|
mod marine_test_impl;
|
||||||
mod glue_code_generator;
|
mod glue_code_generator;
|
||||||
mod module_generator;
|
mod token_stream_generator;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod modules_linker;
|
mod modules_linker;
|
||||||
|
|
||||||
|
@ -14,10 +14,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::marine_test::config_utils::Module;
|
|
||||||
use crate::{TResult, TestGeneratorError};
|
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 marine_it_parser::it_interface::it::{IType, IRecordType};
|
||||||
|
|
||||||
use itertools::zip;
|
use itertools::zip;
|
||||||
@ -28,16 +27,16 @@ use std::rc::Rc;
|
|||||||
use static_assertions::const_assert;
|
use static_assertions::const_assert;
|
||||||
|
|
||||||
pub(super) fn link_modules<'modules>(
|
pub(super) fn link_modules<'modules>(
|
||||||
modules: &'modules [Module<'_>],
|
modules: impl ExactSizeIterator<Item = (&'modules str, &'modules IModuleInterface)>,
|
||||||
) -> TResult<LinkedModules<'modules>> {
|
) -> TResult<LinkedModules<'modules>> {
|
||||||
let mut all_record_types = HashMap::<IRecordTypeClosed<'_>, &str>::new();
|
let mut all_record_types = HashMap::<IRecordTypeClosed<'_>, &str>::new();
|
||||||
let mut linked_modules = HashMap::<&str, LinkedModule<'_>>::new();
|
let mut linked_modules = HashMap::<&str, LinkedModule<'_>>::new();
|
||||||
|
|
||||||
for module in modules {
|
for (name, interface) in modules {
|
||||||
let mut linking_module = LinkedModule::default();
|
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 =
|
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) {
|
let entry = match all_record_types.get(&record_type_ex) {
|
||||||
Some(owner_module) => RecordEntry::Use(UseDescription {
|
Some(owner_module) => RecordEntry::Use(UseDescription {
|
||||||
@ -45,7 +44,7 @@ pub(super) fn link_modules<'modules>(
|
|||||||
name: &record_type.name,
|
name: &record_type.name,
|
||||||
}),
|
}),
|
||||||
None => {
|
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)
|
RecordEntry::Declare(record_type_ex)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -53,10 +52,8 @@ pub(super) fn link_modules<'modules>(
|
|||||||
linking_module.records.push(entry);
|
linking_module.records.push(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
if linked_modules.insert(module.name, linking_module).is_some() {
|
if linked_modules.insert(name, linking_module).is_some() {
|
||||||
return Err(TestGeneratorError::DuplicateModuleName(
|
return Err(TestGeneratorError::DuplicateModuleName(name.to_string()));
|
||||||
module.name.to_string(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,14 +17,20 @@
|
|||||||
mod methods_generator;
|
mod methods_generator;
|
||||||
mod methods_generator_utils;
|
mod methods_generator_utils;
|
||||||
mod record_type_generator;
|
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::config_utils::Module;
|
||||||
|
use crate::marine_test::modules_linker::{LinkedModule, LinkedModules, UseDescription};
|
||||||
|
use crate::marine_test::utils;
|
||||||
use crate::TResult;
|
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 proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
use crate::marine_test::utils::new_ident;
|
||||||
|
|
||||||
/// Generates definitions of modules and records of this modules.
|
/// Generates definitions of modules and records of this modules.
|
||||||
/// F.e. for the greeting service the following definitions would be generated:
|
/// 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>> {
|
) -> TResult<Vec<TokenStream>> {
|
||||||
modules
|
modules
|
||||||
.into_iter()
|
.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<_>>>()
|
.collect::<TResult<Vec<_>>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_module_definition(
|
fn generate_module_definition(
|
||||||
module: &Module<'_>,
|
module: &Module<'_>,
|
||||||
linked_module: &'_ LinkedModule<'_>,
|
linked_module: &'_ LinkedModule<'_>,
|
||||||
|
import_generator: fn(info: &UseDescription<'_>) -> TResult<TokenStream>,
|
||||||
) -> 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 struct_ident = utils::new_ident("ModuleInterface")?;
|
||||||
|
let module_records = record_type_generator::generate_records(linked_module, import_generator)?;
|
||||||
let module_interface = &module.interface;
|
|
||||||
let module_records = record_type_generator::generate_records(linked_module)?;
|
|
||||||
let module_functions = methods_generator::generate_module_methods(
|
let module_functions = methods_generator::generate_module_methods(
|
||||||
module_name,
|
module.name,
|
||||||
module_interface.function_signatures.iter(),
|
module.interface.function_signatures.iter(),
|
||||||
&module_interface.record_types,
|
&module.interface.record_types,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let module_definition = quote! {
|
let module_definition = quote! {
|
||||||
// it's a sort of hack: this module structure allows user to import structs by
|
#(#module_records)*
|
||||||
// using marine_env_test::module_name::StructName;
|
|
||||||
pub mod #module_ident {
|
|
||||||
#(#module_records)*
|
|
||||||
|
|
||||||
pub struct #struct_ident {
|
pub struct #struct_ident {
|
||||||
marine: std::rc::Rc<std::cell::RefCell<marine_rs_sdk_test::internal::AppService>, >,
|
marine: std::rc::Rc<std::cell::RefCell<marine_rs_sdk_test::internal::AppService>, >,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl #struct_ident {
|
impl #struct_ident {
|
||||||
pub fn new(marine: std::rc::Rc<std::cell::RefCell<marine_rs_sdk_test::internal::AppService>, >) -> Self {
|
pub fn new(marine: std::rc::Rc<std::cell::RefCell<marine_rs_sdk_test::internal::AppService>, >) -> Self {
|
||||||
Self { marine }
|
Self { marine }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl #struct_ident {
|
impl #struct_ident {
|
||||||
#(#module_functions)*
|
#(#module_functions)*
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(module_definition)
|
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;})
|
||||||
|
}
|
@ -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)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
@ -42,25 +42,28 @@ pub(super) fn generate_module_method(
|
|||||||
let output_type = generate_output_type(&signature.outputs, records)?;
|
let output_type = generate_output_type(&signature.outputs, records)?;
|
||||||
let mcall = generate_marine_call(module_name, cp_setting, &signature, records)?;
|
let mcall = generate_marine_call(module_name, cp_setting, &signature, records)?;
|
||||||
|
|
||||||
let (cp, func_name) = match cp_setting {
|
let (cp, func_name) = generate_call_parameters(&cp_setting, signature)?;
|
||||||
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 = quote! { #maybe_comma cp: marine_rs_sdk_test::CallParameters };
|
let module_method = quote! {
|
||||||
let func_name = format!("{}_cp", signature.name);
|
pub fn #func_name(&mut self, #(#arguments),* #cp) #output_type {
|
||||||
let func_name = new_ident(&func_name)?;
|
#mcall
|
||||||
(cp, func_name)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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! {
|
let module_method = quote! {
|
||||||
pub fn #func_name(&mut self, #(#arguments),* #cp) #output_type {
|
pub fn #func_name(&mut self, #(#arguments),* #cp) #output_type {
|
||||||
#mcall
|
#mcall
|
||||||
@ -100,6 +103,30 @@ fn generate_marine_call(
|
|||||||
Ok(function_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.
|
/// Generates type convertor to json because of AppService receives them in json.
|
||||||
fn generate_arguments_converter<'a>(
|
fn generate_arguments_converter<'a>(
|
||||||
args: impl ExactSizeIterator<Item = &'a str>,
|
args: impl ExactSizeIterator<Item = &'a str>,
|
||||||
@ -192,3 +219,27 @@ fn get_output_type(output_types: &[IType]) -> TResult<Option<&IType>> {
|
|||||||
_ => Err(ManyFnOutputsUnsupported),
|
_ => 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -23,21 +23,20 @@ use marine_it_parser::it_interface::IRecordTypes;
|
|||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
|
||||||
use crate::marine_test::modules_linker::{LinkedModule, RecordEntry};
|
use crate::marine_test::modules_linker::{LinkedModule, RecordEntry, UseDescription};
|
||||||
use itertools::Itertools;
|
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
|
linked_module.records
|
||||||
.iter()
|
.iter()
|
||||||
.sorted()
|
.sorted()
|
||||||
.map(|record| -> TResult<_> {
|
.map(|record| -> TResult<_> {
|
||||||
use RecordEntry::*;
|
use RecordEntry::*;
|
||||||
match record {
|
match record {
|
||||||
Use(use_info) => {
|
Use(use_info) => import_generator(use_info),
|
||||||
let from_module_ident = utils::new_ident(use_info.from)?;
|
|
||||||
let record_name_ident = utils::new_ident(use_info.name)?;
|
|
||||||
Ok(quote! {pub use super::#from_module_ident::#record_name_ident;})
|
|
||||||
},
|
|
||||||
Declare(record) => {
|
Declare(record) => {
|
||||||
let record_name_ident = utils::new_ident(&record.record_type.name)?;
|
let record_name_ident = utils::new_ident(&record.record_type.name)?;
|
||||||
let fields = prepare_field(record.record_type.fields.iter(), record.records)?;
|
let fields = prepare_field(record.record_type.fields.iter(), record.records)?;
|
@ -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)
|
||||||
|
}
|
@ -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())
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
modules_dir = "artifacts/"
|
||||||
|
|
||||||
|
[[module]]
|
||||||
|
name = "greeting"
|
||||||
|
mem_pages_count = 50
|
||||||
|
logger_enabled = false
|
Binary file not shown.
@ -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()
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
fn test() {
|
||||||
|
let mut greeting = marine_test_env::greeting::ServiceInterface::new();
|
||||||
|
let _ = greeting.download("duckduckgo.com");
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
modules_dir = "artifacts/"
|
||||||
|
|
||||||
|
[[module]]
|
||||||
|
name = "greeting"
|
||||||
|
mem_pages_count = 50
|
||||||
|
logger_enabled = false
|
||||||
|
[module.mounted_binaries]
|
||||||
|
echo = "/usr/bin/curl"
|
Binary file not shown.
@ -0,0 +1,6 @@
|
|||||||
|
modules_dir = "artifacts/"
|
||||||
|
|
||||||
|
[[module]]
|
||||||
|
name = "greeting"
|
||||||
|
mem_pages_count = 50
|
||||||
|
logger_enabled = false
|
Binary file not shown.
@ -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()
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
fn empty_test() {}
|
@ -17,6 +17,8 @@
|
|||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use utils::test_marine_test_token_streams;
|
use utils::test_marine_test_token_streams;
|
||||||
|
use utils::TestServiceDescription;
|
||||||
|
use utils::test_marine_test_token_streams_multiservice;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_empty_func() {
|
fn test_empty_func() {
|
||||||
@ -47,3 +49,38 @@ fn test_multiple_modules() {
|
|||||||
"artifacts"
|
"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
|
||||||
|
));
|
||||||
|
}
|
||||||
|
@ -33,7 +33,56 @@ where
|
|||||||
let marine_item = stream_from_file(&marine_path);
|
let marine_item = stream_from_file(&marine_path);
|
||||||
let test_token_stream = quote::quote! { #marine_item };
|
let test_token_stream = quote::quote! { #marine_item };
|
||||||
let buf = marine_path.as_ref().to_path_buf();
|
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(
|
let marine_token_streams = marine_test_impl(
|
||||||
attrs,
|
attrs,
|
||||||
test_token_stream,
|
test_token_stream,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user