<commit_before>use super::common::*;
use crate::prelude::*;

use crate::idents;
use crate::model;
use crate::utils;
use std::iter::FromIterator;

use syn::spanned::Spanned;

/// Expands the `com_library` macro.
///
/// The macro expansion results in the following items:
///
/// - `DllGetClassObject` extern function implementation.
/// - `IntercomListClassObjects` extern function implementation.
pub fn expand_com_module(
    arg_tokens: TokenStreamNightly,
    com_library: bool,
) -> Result<TokenStreamNightly, model::ParseError>
{
    let mut output = vec![];
    let lib = model::ComLibrary::parse(&lib_name(), arg_tokens.into())?;

    // Create the match-statmeent patterns for each supposedly visible COM class.
    let mut match_arms = vec![];
    for struct_path in &lib.coclasses {
        // Construct the match pattern.
        let clsid_path = idents::clsid_path(struct_path);
        match_arms.push(quote_spanned!(struct_path.span() =>
            #clsid_path =>
                return Some(intercom::ClassFactory::<#struct_path>::create(riid, pout))
        ));
    }

    let try_submodule_class_factory = lib.submodules.iter().map(|submod| {
        quote!(
            if let Some(hr) = #submod::__get_module_class_factory(rclsid, riid, pout) {
                return Some(hr);
            }
        )
    });

    // Implement the __get_module_class_factory function.
    output.push(quote!(
        #[allow(dead_code)]
        #[doc(hidden)]
        pub unsafe fn __get_module_class_factory(
            rclsid : intercom::REFCLSID,
            riid : intercom::REFIID,
            pout : *mut intercom::raw::RawComPtr
        ) -> Option<intercom::raw::HRESULT>
        {
            // Create new class factory.
            // Specify a create function that is able to create all the
            // contained coclasses.
            match *rclsid {
                #( #match_arms, )*
                _ => {},
            };

            // Try each sub-module
            #( #try_submodule_class_factory )*

            None
        }
    ));

    // Implement DllGetClassObject and DllMain.
    //
    // This is more or less the only symbolic entry point that the COM
    // infrastructure uses. The COM client uses this method to acquire
    // the IClassFactory interfaces that are then used to construct the
    // actual coclasses.
    if com_library {
        let dll_get_class_object = get_dll_get_class_object_function();
        output.push(dll_get_class_object);
        output.push(quote!(
            static mut DLL_INSTANCE: *mut std::os::raw::c_void = 0 as _;

            #[cfg(windows)]
            #[no_mangle]
            #[allow(non_camel_case_types)]
            #[deprecated]
            #[doc(hidden)]
            pub extern "stdcall" fn DllMain(
                dll_instance: *mut std::os::raw::c_void,
                reason: u32,
                _reserved: *mut std::os::raw::c_void,
            ) -> bool
            {
                match reason {
                    // DLL_PROCESS_ATTACH
                    1 => unsafe {
                        DLL_INSTANCE = dll_instance;
                    },
                    _ => {}
                }
                true
            }
        ));
    }

    // Implement get_intercom_typelib()
    output.push(create_gather_module_types(&lib).map_err(model::ParseError::ComLibrary)?);
    if com_library {
        output.push(create_get_typelib_function(&lib).map_err(model::ParseError::ComLibrary)?);
    }

    // Implement the global DLL entry points
    if com_library {
        // DllListClassObjects returns all CLSIDs implemented in the crate.
        let list_class_objects = get_intercom_list_class_objects_function();
        output.push(list_class_objects);

        // DllListClassObjects returns all CLSIDs implemented in the crate.
        let dll_register_server = get_register_server_function(&lib);
        output.push(dll_register_server);
    }

    Ok(TokenStream::from_iter(output.into_iter()).into())
}

fn get_dll_get_class_object_function() -> TokenStream
{
    quote!(
        #[no_mangle]
        #[allow(non_snake_case)]
        #[allow(dead_code)]
        #[doc(hidden)]
        pub unsafe extern "system" fn DllGetClassObject(
            rclsid: intercom::REFCLSID,
            riid: intercom::REFIID,
            pout: *mut intercom::raw::RawComPtr,
        ) -> intercom::raw::HRESULT
        {
            // Delegate to the module implementation.
            if let Some(hr) = __get_module_class_factory(rclsid, riid, pout) {
                return hr;
            }

            // Try intercom built in types.
            if let Some(hr) = intercom::__get_module_class_factory(rclsid, riid, pout) {
                return hr;
            }

            intercom::raw::E_CLASSNOTAVAILABLE
        }
    )
}

fn create_gather_module_types(lib: &model::ComLibrary) -> Result<TokenStream, String>
{
    let create_class_typeinfo = lib.coclasses.iter().map(|path| {
        quote!(
            <#path as intercom::attributes::ComClassTypeInfo>::gather_type_info()
        )
    });
    let gather_submodule_types = lib
        .submodules
        .iter()
        .map(|path| quote!( #path::__gather_module_types()));
    Ok(quote!(
        pub fn __gather_module_types() -> Vec<intercom::typelib::TypeInfo>
        {
            vec![
                #( #create_class_typeinfo, )*
                #( #gather_submodule_types, )*
            ]
            .into_iter()
            .flatten()
            .collect()
        }
    ))
}

fn create_get_typelib_function(lib: &model::ComLibrary) -> Result<TokenStream, String>
{
    let lib_name = lib_name();
    let libid = utils::get_guid_tokens(&lib.libid, Span::call_site());
    Ok(quote!(
        #[no_mangle]
        pub unsafe extern "system" fn IntercomTypeLib(
            type_system: intercom::type_system::TypeSystemName,
            out: *mut intercom::raw::RawComPtr,
        ) -> intercom::raw::HRESULT
        {
            let mut tlib = intercom::ComBox::new(intercom::typelib::TypeLib::__new(
                    #lib_name.into(),
                    #libid,
                    "0.1".into(),
                    intercom::__gather_module_types()
                        .into_iter().chain(__gather_module_types())
                        .collect()
            ));
            let rc = intercom::ComRc::<intercom::typelib::IIntercomTypeLib>::from( &tlib );
            let itf = intercom::ComRc::detach(rc);
            *out = type_system.get_ptr(&itf);

            intercom::raw::S_OK
        }
    ))
}

fn get_intercom_list_class_objects_function() -> TokenStream
{
    quote!(
        #[no_mangle]
        #[allow(non_snake_case)]
        #[allow(dead_code)]
        #[doc(hidden)]
        pub unsafe extern "system" fn IntercomListClassObjects(
            pcount: *mut usize,
            pclsids: *mut *const intercom::CLSID,
        ) -> intercom::raw::HRESULT
        {
            // Do not crash due to invalid parameters.
            if pcount.is_null() {
                return intercom::raw::E_POINTER;
            }
            if pclsids.is_null() {
                return intercom::raw::E_POINTER;
            }

            // Store the available CLSID in a static variable so that we can
            // pass them as-is to the caller.
            static mut AVAILABLE_CLASSES: Option<Vec<intercom::CLSID>> = None;
            static INIT_AVAILABLE_CLASSES: std::sync::Once = std::sync::Once::new();
            INIT_AVAILABLE_CLASSES.call_once(|| unsafe {
                AVAILABLE_CLASSES = Some(
                    __gather_module_types()
                        .into_iter()
                        .chain(intercom::__gather_module_types())
                        .filter_map(|ty| match ty {
                            intercom::typelib::TypeInfo::Class(cls) => Some(cls.clsid.clone()),
                            _ => None,
                        })
                        .collect(),
                );
            });

            // com_struct will drop here and decrement the referenc ecount.
            // This is okay, as the query_interface incremented it, leaving
            // it at two at this point.
            let available_classes = AVAILABLE_CLASSES
                .as_ref()
                .expect("AVAILABLE_CLASSES was not initialized");
            *pcount = available_classes.len();
            *pclsids = available_classes.as_ptr();

            intercom::raw::S_OK
        }
    )
}

fn get_register_server_function(lib: &model::ComLibrary) -> TokenStream
{
    let lib_name = lib_name();
    let libid = utils::get_guid_tokens(&lib.libid, Span::call_site());
    quote!(
        #[cfg(windows)]
        #[no_mangle]
        #[allow(non_snake_case)]
        #[allow(dead_code)]
        #[doc(hidden)]
        pub unsafe extern "system" fn DllRegisterServer() -> intercom::raw::HRESULT
        {
            let mut tlib = intercom::typelib::TypeLib::__new(
                    #lib_name.into(),
                    #libid,
                    "0.1".into(),
                    intercom::__gather_module_types()
                        .into_iter().chain(__gather_module_types())
                        .collect()
            );
            match intercom::registry::register(DLL_INSTANCE, tlib) {
                Ok(_) => intercom::raw::S_OK,
                Err(hr) => hr,
            }
        }

        #[cfg(windows)]
        #[no_mangle]
        #[allow(non_snake_case)]
        #[allow(dead_code)]
        #[doc(hidden)]
        pub unsafe extern "system" fn DllUnregisterServer() -> intercom::raw::HRESULT
        {
            let mut tlib = intercom::typelib::TypeLib::__new(
                    #lib_name.into(),
                    #libid,
                    "0.1".into(),
                    intercom::__gather_module_types()
                        .into_iter().chain(__gather_module_types())
                        .collect()
            );
            match intercom::registry::unregister(DLL_INSTANCE, tlib) {
                Ok(_) => intercom::raw::S_OK,
                Err(hr) => hr,
            }
        }
    )
}
<commit_msg>Rename DLL_INSTANCE -> __INTERCOM_DLL_INSTANCE<commit_after>use super::common::*;
use crate::prelude::*;

use crate::idents;
use crate::model;
use crate::utils;
use std::iter::FromIterator;

use syn::spanned::Spanned;

/// Expands the `com_library` macro.
///
/// The macro expansion results in the following items:
///
/// - `DllGetClassObject` extern function implementation.
/// - `IntercomListClassObjects` extern function implementation.
pub fn expand_com_module(
    arg_tokens: TokenStreamNightly,
    com_library: bool,
) -> Result<TokenStreamNightly, model::ParseError>
{
    let mut output = vec![];
    let lib = model::ComLibrary::parse(&lib_name(), arg_tokens.into())?;

    // Create the match-statmeent patterns for each supposedly visible COM class.
    let mut match_arms = vec![];
    for struct_path in &lib.coclasses {
        // Construct the match pattern.
        let clsid_path = idents::clsid_path(struct_path);
        match_arms.push(quote_spanned!(struct_path.span() =>
            #clsid_path =>
                return Some(intercom::ClassFactory::<#struct_path>::create(riid, pout))
        ));
    }

    let try_submodule_class_factory = lib.submodules.iter().map(|submod| {
        quote!(
            if let Some(hr) = #submod::__get_module_class_factory(rclsid, riid, pout) {
                return Some(hr);
            }
        )
    });

    // Implement the __get_module_class_factory function.
    output.push(quote!(
        #[allow(dead_code)]
        #[doc(hidden)]
        pub unsafe fn __get_module_class_factory(
            rclsid : intercom::REFCLSID,
            riid : intercom::REFIID,
            pout : *mut intercom::raw::RawComPtr
        ) -> Option<intercom::raw::HRESULT>
        {
            // Create new class factory.
            // Specify a create function that is able to create all the
            // contained coclasses.
            match *rclsid {
                #( #match_arms, )*
                _ => {},
            };

            // Try each sub-module
            #( #try_submodule_class_factory )*

            None
        }
    ));

    // Implement DllGetClassObject and DllMain.
    //
    // This is more or less the only symbolic entry point that the COM
    // infrastructure uses. The COM client uses this method to acquire
    // the IClassFactory interfaces that are then used to construct the
    // actual coclasses.
    if com_library {
        let dll_get_class_object = get_dll_get_class_object_function();
        output.push(dll_get_class_object);
        output.push(quote!(
            #[doc(hidden)]
            static mut __INTERCOM_DLL_INSTANCE: *mut std::os::raw::c_void = 0 as _;

            #[cfg(windows)]
            #[no_mangle]
            #[allow(non_camel_case_types)]
            #[deprecated]
            #[doc(hidden)]
            pub extern "stdcall" fn DllMain(
                dll_instance: *mut std::os::raw::c_void,
                reason: u32,
                _reserved: *mut std::os::raw::c_void,
            ) -> bool
            {
                match reason {
                    // DLL_PROCESS_ATTACH
                    1 => unsafe {
                        __INTERCOM_DLL_INSTANCE = dll_instance;
                    },
                    _ => {}
                }
                true
            }
        ));
    }

    // Implement get_intercom_typelib()
    output.push(create_gather_module_types(&lib).map_err(model::ParseError::ComLibrary)?);
    if com_library {
        output.push(create_get_typelib_function(&lib).map_err(model::ParseError::ComLibrary)?);
    }

    // Implement the global DLL entry points
    if com_library {
        // DllListClassObjects returns all CLSIDs implemented in the crate.
        let list_class_objects = get_intercom_list_class_objects_function();
        output.push(list_class_objects);

        // DllListClassObjects returns all CLSIDs implemented in the crate.
        let dll_register_server = get_register_server_function(&lib);
        output.push(dll_register_server);
    }

    Ok(TokenStream::from_iter(output.into_iter()).into())
}

fn get_dll_get_class_object_function() -> TokenStream
{
    quote!(
        #[no_mangle]
        #[allow(non_snake_case)]
        #[allow(dead_code)]
        #[doc(hidden)]
        pub unsafe extern "system" fn DllGetClassObject(
            rclsid: intercom::REFCLSID,
            riid: intercom::REFIID,
            pout: *mut intercom::raw::RawComPtr,
        ) -> intercom::raw::HRESULT
        {
            // Delegate to the module implementation.
            if let Some(hr) = __get_module_class_factory(rclsid, riid, pout) {
                return hr;
            }

            // Try intercom built in types.
            if let Some(hr) = intercom::__get_module_class_factory(rclsid, riid, pout) {
                return hr;
            }

            intercom::raw::E_CLASSNOTAVAILABLE
        }
    )
}

fn create_gather_module_types(lib: &model::ComLibrary) -> Result<TokenStream, String>
{
    let create_class_typeinfo = lib.coclasses.iter().map(|path| {
        quote!(
            <#path as intercom::attributes::ComClassTypeInfo>::gather_type_info()
        )
    });
    let gather_submodule_types = lib
        .submodules
        .iter()
        .map(|path| quote!( #path::__gather_module_types()));
    Ok(quote!(
        pub fn __gather_module_types() -> Vec<intercom::typelib::TypeInfo>
        {
            vec![
                #( #create_class_typeinfo, )*
                #( #gather_submodule_types, )*
            ]
            .into_iter()
            .flatten()
            .collect()
        }
    ))
}

fn create_get_typelib_function(lib: &model::ComLibrary) -> Result<TokenStream, String>
{
    let lib_name = lib_name();
    let libid = utils::get_guid_tokens(&lib.libid, Span::call_site());
    Ok(quote!(
        #[no_mangle]
        pub unsafe extern "system" fn IntercomTypeLib(
            type_system: intercom::type_system::TypeSystemName,
            out: *mut intercom::raw::RawComPtr,
        ) -> intercom::raw::HRESULT
        {
            let mut tlib = intercom::ComBox::new(intercom::typelib::TypeLib::__new(
                    #lib_name.into(),
                    #libid,
                    "0.1".into(),
                    intercom::__gather_module_types()
                        .into_iter().chain(__gather_module_types())
                        .collect()
            ));
            let rc = intercom::ComRc::<intercom::typelib::IIntercomTypeLib>::from( &tlib );
            let itf = intercom::ComRc::detach(rc);
            *out = type_system.get_ptr(&itf);

            intercom::raw::S_OK
        }
    ))
}

fn get_intercom_list_class_objects_function() -> TokenStream
{
    quote!(
        #[no_mangle]
        #[allow(non_snake_case)]
        #[allow(dead_code)]
        #[doc(hidden)]
        pub unsafe extern "system" fn IntercomListClassObjects(
            pcount: *mut usize,
            pclsids: *mut *const intercom::CLSID,
        ) -> intercom::raw::HRESULT
        {
            // Do not crash due to invalid parameters.
            if pcount.is_null() {
                return intercom::raw::E_POINTER;
            }
            if pclsids.is_null() {
                return intercom::raw::E_POINTER;
            }

            // Store the available CLSID in a static variable so that we can
            // pass them as-is to the caller.
            static mut AVAILABLE_CLASSES: Option<Vec<intercom::CLSID>> = None;
            static INIT_AVAILABLE_CLASSES: std::sync::Once = std::sync::Once::new();
            INIT_AVAILABLE_CLASSES.call_once(|| unsafe {
                AVAILABLE_CLASSES = Some(
                    __gather_module_types()
                        .into_iter()
                        .chain(intercom::__gather_module_types())
                        .filter_map(|ty| match ty {
                            intercom::typelib::TypeInfo::Class(cls) => Some(cls.clsid.clone()),
                            _ => None,
                        })
                        .collect(),
                );
            });

            // com_struct will drop here and decrement the referenc ecount.
            // This is okay, as the query_interface incremented it, leaving
            // it at two at this point.
            let available_classes = AVAILABLE_CLASSES
                .as_ref()
                .expect("AVAILABLE_CLASSES was not initialized");
            *pcount = available_classes.len();
            *pclsids = available_classes.as_ptr();

            intercom::raw::S_OK
        }
    )
}

fn get_register_server_function(lib: &model::ComLibrary) -> TokenStream
{
    let lib_name = lib_name();
    let libid = utils::get_guid_tokens(&lib.libid, Span::call_site());
    quote!(
        #[cfg(windows)]
        #[no_mangle]
        #[allow(non_snake_case)]
        #[allow(dead_code)]
        #[doc(hidden)]
        pub unsafe extern "system" fn DllRegisterServer() -> intercom::raw::HRESULT
        {
            let mut tlib = intercom::typelib::TypeLib::__new(
                    #lib_name.into(),
                    #libid,
                    "0.1".into(),
                    intercom::__gather_module_types()
                        .into_iter().chain(__gather_module_types())
                        .collect()
            );
            match intercom::registry::register(__INTERCOM_DLL_INSTANCE, tlib) {
                Ok(_) => intercom::raw::S_OK,
                Err(hr) => hr,
            }
        }

        #[cfg(windows)]
        #[no_mangle]
        #[allow(non_snake_case)]
        #[allow(dead_code)]
        #[doc(hidden)]
        pub unsafe extern "system" fn DllUnregisterServer() -> intercom::raw::HRESULT
        {
            let mut tlib = intercom::typelib::TypeLib::__new(
                    #lib_name.into(),
                    #libid,
                    "0.1".into(),
                    intercom::__gather_module_types()
                        .into_iter().chain(__gather_module_types())
                        .collect()
            );
            match intercom::registry::unregister(__INTERCOM_DLL_INSTANCE, tlib) {
                Ok(_) => intercom::raw::S_OK,
                Err(hr) => hr,
            }
        }
    )
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate regex;

include!("../clap-test.rs");

use clap::{App, Arg, ErrorKind};

#[test]
fn stdin_char() {
    let r = App::new("opts")
        .arg( Arg::from_usage("-f [flag] 'some flag'") )
        .get_matches_from_safe(vec!["", "-f", "-"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("f"));
    assert_eq!(m.value_of("f").unwrap(), "-");
}

#[test]
fn opts_using_short() {
    let r = App::new("opts")
        .args(&[
            Arg::from_usage("-f [flag] 'some flag'"),
            Arg::from_usage("-c [color] 'some other flag'")
            ])
        .get_matches_from_safe(vec!["", "-f", "some", "-c", "other"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("f"));
    assert_eq!(m.value_of("f").unwrap(), "some");
    assert!(m.is_present("c"));
    assert_eq!(m.value_of("c").unwrap(), "other");
}

#[test]
fn lots_o_vals() {
    let r = App::new("opts")
        .arg(
            Arg::from_usage("-o [opt]... 'some opt'"),
            )
        .get_matches_from_safe(vec!["", "-o",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            ]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.values_of("o").unwrap().collect::<Vec<_>>().len(), 297); // i.e. more than u8
}

#[test]
fn opts_using_long_space() {
    let r = App::new("opts")
        .args(&[
            Arg::from_usage("--flag [flag] 'some flag'"),
            Arg::from_usage("--color [color] 'some other flag'")
            ])
        .get_matches_from_safe(vec!["", "--flag", "some", "--color", "other"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("flag"));
    assert_eq!(m.value_of("flag").unwrap(), "some");
    assert!(m.is_present("color"));
    assert_eq!(m.value_of("color").unwrap(), "other");
}

#[test]
fn opts_with_empty_values() {
    let r = App::new("opts")
        .arg_from_usage("--flag [flag]... 'some flag'")
        .get_matches_from_safe(vec!["", "--flag", "", "test"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("flag"));
    assert_eq!(m.values_of("flag").unwrap().collect::<Vec<_>>(), ["", "test"]);
}

#[test]
fn opts_using_long_equals() {
    let r = App::new("opts")
        .args(&[
            Arg::from_usage("--flag [flag] 'some flag'"),
            Arg::from_usage("--color [color] 'some other flag'")
            ])
        .get_matches_from_safe(vec!["", "--flag=some", "--color=other"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("flag"));
    assert_eq!(m.value_of("flag").unwrap(), "some");
    assert!(m.is_present("color"));
    assert_eq!(m.value_of("color").unwrap(), "other");
}

#[test]
fn opts_using_mixed() {
    let r = App::new("opts")
        .args(&[
            Arg::from_usage("-f, --flag [flag] 'some flag'"),
            Arg::from_usage("-c, --color [color] 'some other flag'")
            ])
        .get_matches_from_safe(vec!["", "-f", "some", "--color", "other"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("flag"));
    assert_eq!(m.value_of("flag").unwrap(), "some");
    assert!(m.is_present("color"));
    assert_eq!(m.value_of("color").unwrap(), "other");
}

#[test]
fn opts_using_mixed2() {
    let r = App::new("opts")
        .args(&[
            Arg::from_usage("-f, --flag [flag] 'some flag'"),
            Arg::from_usage("-c, --color [color] 'some other flag'")
            ])
        .get_matches_from_safe(vec!["", "--flag=some", "-c", "other"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("flag"));
    assert_eq!(m.value_of("flag").unwrap(), "some");
    assert!(m.is_present("color"));
    assert_eq!(m.value_of("color").unwrap(), "other");
}

#[test]
fn default_values_user_value() {
    let r = App::new("df")
        .arg( Arg::from_usage("-o [opt] 'some opt'")
            .default_value("default"))
        .get_matches_from_safe(vec!["", "-o", "value"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.value_of("o").unwrap(), "value");
}

#[test]
fn multiple_vals_pos_arg_equals() {
    let r = App::new("mvae")
        .arg( Arg::from_usage("-o [opt]... 'some opt'") )
        .arg( Arg::from_usage("[file] 'some file'") )
        .get_matches_from_safe(vec!["", "-o=1", "some"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.value_of("o").unwrap(), "1");
    assert!(m.is_present("file"));
    assert_eq!(m.value_of("file").unwrap(), "some");
}

#[test]
fn multiple_vals_pos_arg_delim() {
    let r = App::new("mvae")
        .arg( Arg::from_usage("-o [opt]... 'some opt'") )
        .arg( Arg::from_usage("[file] 'some file'") )
        .get_matches_from_safe(vec!["", "-o", "1,2", "some"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.values_of("o").unwrap().collect::<Vec<_>>(), &["1", "2"]);
    assert!(m.is_present("file"));
    assert_eq!(m.value_of("file").unwrap(), "some");
}

#[test]
fn require_delims_no_delim() {
    let r = App::new("mvae")
        .arg( Arg::from_usage("-o [opt]... 'some opt'").require_delimiter(true) )
        .arg( Arg::from_usage("[file] 'some file'") )
        .get_matches_from_safe(vec!["mvae", "-o", "1", "2", "some"]);
    assert!(r.is_err());
    let err = r.unwrap_err();
    assert_eq!(err.kind, ErrorKind::UnknownArgument);
}

#[test]
fn require_delims() {
    let r = App::new("mvae")
        .arg( Arg::from_usage("-o [opt]... 'some opt'").require_delimiter(true) )
        .arg( Arg::from_usage("[file] 'some file'") )
        .get_matches_from_safe(vec!["", "-o", "1,2", "some"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.values_of("o").unwrap().collect::<Vec<_>>(), &["1", "2"]);
    assert!(m.is_present("file"));
    assert_eq!(m.value_of("file").unwrap(), "some");
}

#[test]
fn leading_hyphen_pass() {
    let r = App::new("mvae")
        .arg( Arg::from_usage("-o [opt]... 'some opt'").allow_hyphen_values(true))
        .get_matches_from_safe(vec!["", "-o", "-2", "3"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.values_of("o").unwrap().collect::<Vec<_>>(), &["-2", "3"]);
}

#[test]
fn leading_hyphen_fail() {
    let r = App::new("mvae")
        .arg( Arg::from_usage("-o [opt] 'some opt'"))
        .get_matches_from_safe(vec!["", "-o", "-2"]);
    assert!(r.is_err());
    let m = r.unwrap_err();
    assert_eq!(m.kind, ErrorKind::UnknownArgument);
}

#[test]
fn leading_hyphen_with_flag_after() {
    let r = App::new("mvae")
        .arg( Arg::from_usage("-o [opt]... 'some opt'").allow_hyphen_values(true))
        .arg_from_usage("-f 'some flag'")
        .get_matches_from_safe(vec!["", "-o", "-2", "-f"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.values_of("o").unwrap().collect::<Vec<_>>(), &["-2", "-f"]);
    assert!(!m.is_present("f"));
}

#[test]
fn leading_hyphen_with_flag_before() {
    let r = App::new("mvae")
        .arg( Arg::from_usage("-o [opt]... 'some opt'").allow_hyphen_values(true))
        .arg_from_usage("-f 'some flag'")
        .get_matches_from_safe(vec!["", "-f", "-o", "-2"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.values_of("o").unwrap().collect::<Vec<_>>(), &["-2"]);
    assert!(m.is_present("f"));
}

#[test]
fn leading_hyphen_with_only_pos_follows() {
    let r = App::new("mvae")
        .arg( Arg::from_usage("-o [opt]... 'some opt'").allow_hyphen_values(true))
        .arg_from_usage("[arg] 'some arg'")
        .get_matches_from_safe(vec!["", "-o", "-2", "--", "val"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.values_of("o").unwrap().collect::<Vec<_>>(), &["-2"]);
    assert_eq!(m.value_of("arg"), Some("val"));
}

#[test]
#[cfg(feature="suggestions")]
fn did_you_mean() {
    test::check_err_output(test::complex_app(), "clap-test --optio=foo",
"error: Found argument '--optio' which wasn't expected, or isn't valid in this context
\tDid you mean --option?

USAGE:
    clap-test --option <opt>...

For more information try --help", true);
}
<commit_msg>test(Options): adds a test against issue 665<commit_after>extern crate clap;
extern crate regex;

include!("../clap-test.rs");

use clap::{App, Arg, ErrorKind};

#[test]
fn stdin_char() {
    let r = App::new("opts")
        .arg(Arg::from_usage("-f [flag] 'some flag'"))
        .get_matches_from_safe(vec!["", "-f", "-"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("f"));
    assert_eq!(m.value_of("f").unwrap(), "-");
}

#[test]
fn opts_using_short() {
    let r = App::new("opts")
        .args(&[Arg::from_usage("-f [flag] 'some flag'"),
                Arg::from_usage("-c [color] 'some other flag'")])
        .get_matches_from_safe(vec!["", "-f", "some", "-c", "other"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("f"));
    assert_eq!(m.value_of("f").unwrap(), "some");
    assert!(m.is_present("c"));
    assert_eq!(m.value_of("c").unwrap(), "other");
}

#[test]
fn lots_o_vals() {
    let r = App::new("opts")
        .arg(Arg::from_usage("-o [opt]... 'some opt'"))
        .get_matches_from_safe(vec!["", "-o", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some", "some", "some", "some",
                                    "some", "some", "some", "some"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.values_of("o").unwrap().collect::<Vec<_>>().len(), 297); // i.e. more than u8
}

#[test]
fn opts_using_long_space() {
    let r = App::new("opts")
        .args(&[Arg::from_usage("--flag [flag] 'some flag'"),
                Arg::from_usage("--color [color] 'some other flag'")])
        .get_matches_from_safe(vec!["", "--flag", "some", "--color", "other"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("flag"));
    assert_eq!(m.value_of("flag").unwrap(), "some");
    assert!(m.is_present("color"));
    assert_eq!(m.value_of("color").unwrap(), "other");
}

#[test]
fn opts_with_empty_values() {
    let r = App::new("opts")
        .arg_from_usage("--flag [flag]... 'some flag'")
        .get_matches_from_safe(vec!["", "--flag", "", "test"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("flag"));
    assert_eq!(m.values_of("flag").unwrap().collect::<Vec<_>>(),
               ["", "test"]);
}

#[test]
fn opts_using_long_equals() {
    let r = App::new("opts")
        .args(&[Arg::from_usage("--flag [flag] 'some flag'"),
                Arg::from_usage("--color [color] 'some other flag'")])
        .get_matches_from_safe(vec!["", "--flag=some", "--color=other"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("flag"));
    assert_eq!(m.value_of("flag").unwrap(), "some");
    assert!(m.is_present("color"));
    assert_eq!(m.value_of("color").unwrap(), "other");
}

#[test]
fn opts_using_mixed() {
    let r = App::new("opts")
        .args(&[Arg::from_usage("-f, --flag [flag] 'some flag'"),
                Arg::from_usage("-c, --color [color] 'some other flag'")])
        .get_matches_from_safe(vec!["", "-f", "some", "--color", "other"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("flag"));
    assert_eq!(m.value_of("flag").unwrap(), "some");
    assert!(m.is_present("color"));
    assert_eq!(m.value_of("color").unwrap(), "other");
}

#[test]
fn opts_using_mixed2() {
    let r = App::new("opts")
        .args(&[Arg::from_usage("-f, --flag [flag] 'some flag'"),
                Arg::from_usage("-c, --color [color] 'some other flag'")])
        .get_matches_from_safe(vec!["", "--flag=some", "-c", "other"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("flag"));
    assert_eq!(m.value_of("flag").unwrap(), "some");
    assert!(m.is_present("color"));
    assert_eq!(m.value_of("color").unwrap(), "other");
}

#[test]
fn default_values_user_value() {
    let r = App::new("df")
        .arg(Arg::from_usage("-o [opt] 'some opt'").default_value("default"))
        .get_matches_from_safe(vec!["", "-o", "value"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.value_of("o").unwrap(), "value");
}

#[test]
fn multiple_vals_pos_arg_equals() {
    let r = App::new("mvae")
        .arg(Arg::from_usage("-o [opt]... 'some opt'"))
        .arg(Arg::from_usage("[file] 'some file'"))
        .get_matches_from_safe(vec!["", "-o=1", "some"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.value_of("o").unwrap(), "1");
    assert!(m.is_present("file"));
    assert_eq!(m.value_of("file").unwrap(), "some");
}

#[test]
fn multiple_vals_pos_arg_delim() {
    let r = App::new("mvae")
        .arg(Arg::from_usage("-o [opt]... 'some opt'"))
        .arg(Arg::from_usage("[file] 'some file'"))
        .get_matches_from_safe(vec!["", "-o", "1,2", "some"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.values_of("o").unwrap().collect::<Vec<_>>(), &["1", "2"]);
    assert!(m.is_present("file"));
    assert_eq!(m.value_of("file").unwrap(), "some");
}

#[test]
fn require_delims_no_delim() {
    let r = App::new("mvae")
        .arg(Arg::from_usage("-o [opt]... 'some opt'").require_delimiter(true))
        .arg(Arg::from_usage("[file] 'some file'"))
        .get_matches_from_safe(vec!["mvae", "-o", "1", "2", "some"]);
    assert!(r.is_err());
    let err = r.unwrap_err();
    assert_eq!(err.kind, ErrorKind::UnknownArgument);
}

#[test]
fn require_delims() {
    let r = App::new("mvae")
        .arg(Arg::from_usage("-o [opt]... 'some opt'").require_delimiter(true))
        .arg(Arg::from_usage("[file] 'some file'"))
        .get_matches_from_safe(vec!["", "-o", "1,2", "some"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.values_of("o").unwrap().collect::<Vec<_>>(), &["1", "2"]);
    assert!(m.is_present("file"));
    assert_eq!(m.value_of("file").unwrap(), "some");
}

#[test]
fn leading_hyphen_pass() {
    let r = App::new("mvae")
        .arg(Arg::from_usage("-o [opt]... 'some opt'").allow_hyphen_values(true))
        .get_matches_from_safe(vec!["", "-o", "-2", "3"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.values_of("o").unwrap().collect::<Vec<_>>(), &["-2", "3"]);
}

#[test]
fn leading_hyphen_fail() {
    let r = App::new("mvae")
        .arg(Arg::from_usage("-o [opt] 'some opt'"))
        .get_matches_from_safe(vec!["", "-o", "-2"]);
    assert!(r.is_err());
    let m = r.unwrap_err();
    assert_eq!(m.kind, ErrorKind::UnknownArgument);
}

#[test]
fn leading_hyphen_with_flag_after() {
    let r = App::new("mvae")
        .arg(Arg::from_usage("-o [opt]... 'some opt'").allow_hyphen_values(true))
        .arg_from_usage("-f 'some flag'")
        .get_matches_from_safe(vec!["", "-o", "-2", "-f"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.values_of("o").unwrap().collect::<Vec<_>>(), &["-2", "-f"]);
    assert!(!m.is_present("f"));
}

#[test]
fn leading_hyphen_with_flag_before() {
    let r = App::new("mvae")
        .arg(Arg::from_usage("-o [opt]... 'some opt'").allow_hyphen_values(true))
        .arg_from_usage("-f 'some flag'")
        .get_matches_from_safe(vec!["", "-f", "-o", "-2"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.values_of("o").unwrap().collect::<Vec<_>>(), &["-2"]);
    assert!(m.is_present("f"));
}

#[test]
fn leading_hyphen_with_only_pos_follows() {
    let r = App::new("mvae")
        .arg(Arg::from_usage("-o [opt]... 'some opt'").allow_hyphen_values(true))
        .arg_from_usage("[arg] 'some arg'")
        .get_matches_from_safe(vec!["", "-o", "-2", "--", "val"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.values_of("o").unwrap().collect::<Vec<_>>(), &["-2"]);
    assert_eq!(m.value_of("arg"), Some("val"));
}

#[test]
#[cfg(feature="suggestions")]
fn did_you_mean() {
    test::check_err_output(test::complex_app(),
                           "clap-test --optio=foo",
                           "error: Found argument '--optio' which wasn't expected, or isn't valid in this context
\tDid you mean --option?

USAGE:
    clap-test --option <opt>...

For more information try --help",
                           true);
}

#[test]
fn issue_665() {
    let res = App::new("tester")
        .arg_from_usage("-v, --reroll-count=[N] 'Mark the patch series as PATCH vN'")
        .arg(Arg::from_usage(
"--subject-prefix [Subject-Prefix] 'Use [Subject-Prefix] instead of the standard [PATCH] prefix'")
            .empty_values(false))
        .get_matches_from_safe(vec!["test", "--subject-prefix", "-v", "2"]);

    assert!(res.is_err());
    assert_eq!(res.unwrap_err().kind, ErrorKind::EmptyValue);
}<|endoftext|>


<commit_before>extern crate indy;

// Workaround to share some utils code based on indy sdk types between tests and indy sdk
use indy::api as api;

#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;

#[macro_use]
mod utils;

#[cfg(feature = "local_nodes_pool")]
use indy::api::ErrorCode;

use utils::environment::EnvironmentUtils;
use utils::pool::PoolUtils;
use utils::test::TestUtils;

mod high_cases {
    use super::*;

    mod create {
        use super::*;

        #[test]
        fn create_pool_ledger_config_works() {
            TestUtils::cleanup_storage();

            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool("pool_create", None, None);
            let pool_confog = PoolUtils::pool_config_json(txn_file_path.as_path());
            PoolUtils::create_pool_ledger_config("pool_create", Some(pool_confog.as_str())).unwrap();

            TestUtils::cleanup_storage();
        }

        #[test]
        fn create_pool_ledger_config_works_for_empty_name() {
            TestUtils::cleanup_storage();

            let pool_name = "";
            let res = PoolUtils::create_pool_ledger_config(pool_name, None);
            assert_eq!(res.unwrap_err(), ErrorCode::CommonInvalidParam2);

            TestUtils::cleanup_storage();
        }

        #[test]
        fn create_pool_ledger_config_works_for_config_json() {
            TestUtils::cleanup_storage();

            let pool_name = "create_pool_ledger_config_works_for_config_json";
            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool(pool_name, None, None);
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());

            PoolUtils::create_pool_ledger_config(pool_name, Some(pool_config.as_str())).unwrap();

            TestUtils::cleanup_storage();
        }

        #[test]
        fn create_pool_ledger_config_works_for_specific_config() {
            TestUtils::cleanup_storage();

            let pool_name = "create_pool_ledger_config_works_for_specific_config";
            let txn_file_path = EnvironmentUtils::tmp_file_path("specific_filename.txn");
            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool(pool_name, None, Some(txn_file_path.as_path()));
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());

            PoolUtils::create_pool_ledger_config(pool_name, Some(pool_config.as_str())).unwrap();

            TestUtils::cleanup_storage();
        }
    }

    mod open {
        use super::*;

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn open_pool_ledger_works() {
            TestUtils::cleanup_storage();

            let pool_name = "pool_open";
            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool(pool_name, None, None);
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());
            PoolUtils::create_pool_ledger_config(pool_name, Some(pool_config.as_str())).unwrap();

            PoolUtils::open_pool_ledger(pool_name, None).unwrap();

            TestUtils::cleanup_storage();
        }

        #[test]
        #[cfg(feature = "local_nodes_pool")] //TODO Not implemented yet
        fn open_pool_ledger_works_for_config() {
            TestUtils::cleanup_storage();

            let pool_name = "open_pool_ledger_works_for_config";
            let config = r#"{"refreshOnOpen": true}"#;

            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool(pool_name, None, None);
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());
            PoolUtils::create_pool_ledger_config(pool_name, Some(pool_config.as_str())).unwrap();

            PoolUtils::open_pool_ledger(pool_name, Some(config)).unwrap();

            TestUtils::cleanup_storage();
        }

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn open_pool_ledger_works_for_twice() {
            TestUtils::cleanup_storage();

            let pool_name = "pool_open_twice";
            PoolUtils::create_and_open_pool_ledger(pool_name).unwrap();

            let res = PoolUtils::open_pool_ledger(pool_name, None);
            assert_match!(Err(ErrorCode::PoolLedgerInvalidPoolHandle), res);

            TestUtils::cleanup_storage();
        }

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn open_pool_ledger_works_for_two_nodes() {
            TestUtils::cleanup_storage();

            let pool_name = "open_pool_ledger_works_for_two_nodes";
            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool(pool_name, Some(2), None);
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());
            PoolUtils::create_pool_ledger_config(pool_name, Some(pool_config.as_str())).unwrap();

            PoolUtils::open_pool_ledger(pool_name, None).unwrap();

            TestUtils::cleanup_storage();
        }

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn open_pool_ledger_works_for_three_nodes() {
            TestUtils::cleanup_storage();

            let pool_name = "open_pool_ledger_works_for_three_nodes";
            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool(pool_name, Some(3), None);
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());
            PoolUtils::create_pool_ledger_config(pool_name, Some(pool_config.as_str())).unwrap();

            PoolUtils::open_pool_ledger(pool_name, None).unwrap();

            TestUtils::cleanup_storage();
        }
    }

    mod refresh {
        use super::*;

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn indy_refresh_pool_ledger_works() {
            TestUtils::cleanup_storage();

            let pool_handle = PoolUtils::create_and_open_pool_ledger("indy_refresh_pool_ledger_works").unwrap();

            PoolUtils::refresh(pool_handle).unwrap();

            TestUtils::cleanup_storage();
        }
    }

    mod close {
        use super::*;

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn indy_close_pool_ledger_works() {
            TestUtils::cleanup_storage();

            let pool_name = "indy_close_pool_ledger_works";
            let pool_handle = PoolUtils::create_and_open_pool_ledger(pool_name).unwrap();

            PoolUtils::close(pool_handle).unwrap();

            TestUtils::cleanup_storage();
        }

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn indy_close_pool_ledger_works_for_twice() {
            TestUtils::cleanup_storage();

            let pool_name = "indy_close_pool_ledger_works_twice";
            let pool_handle = PoolUtils::create_and_open_pool_ledger(pool_name).unwrap();

            PoolUtils::close(pool_handle).unwrap();
            assert_eq!(PoolUtils::close(pool_handle).unwrap_err(), ErrorCode::PoolLedgerInvalidPoolHandle);

            TestUtils::cleanup_storage();
        }

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn indy_close_pool_ledger_works_for_reopen_after_close() {
            TestUtils::cleanup_storage();

            let pool_name = "indy_close_pool_ledger_works";
            let pool_handle = PoolUtils::create_and_open_pool_ledger(pool_name).unwrap();

            PoolUtils::close(pool_handle).unwrap();
            PoolUtils::open_pool_ledger(pool_name, None).unwrap();

            TestUtils::cleanup_storage();
        }
    }

    mod delete {
        use super::*;

        #[test]
        fn indy_delete_pool_ledger_config_works() {
            TestUtils::cleanup_storage();

            let pool_name = "indy_remove_pool_ledger_config_works";
            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool(pool_name, None, None);
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());
            PoolUtils::create_pool_ledger_config(pool_name, Some(pool_config.as_str())).unwrap();

            PoolUtils::delete(pool_name).unwrap();

            TestUtils::cleanup_storage();
        }

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn indy_delete_pool_ledger_config_works_for_opened() {
            TestUtils::cleanup_storage();

            let pool_name = "indy_remove_pool_ledger_config_works_for_opened";
            PoolUtils::create_and_open_pool_ledger(pool_name).unwrap();

            assert_eq!(PoolUtils::delete(pool_name).unwrap_err(), ErrorCode::CommonInvalidState);

            TestUtils::cleanup_storage();
        }
    }
}

mod medium_cases {
    use super::*;

    mod create {
        use super::*;

        #[test]
        fn create_pool_ledger_config_works_for_invalid_config_json() {
            TestUtils::cleanup_storage();

            let pool_name = "create_pool_ledger_config_works_for_invalid_config";
            let config = r#"{}"#.to_string();

            let res = PoolUtils::create_pool_ledger_config(pool_name, Some(config.as_str()));
            assert_eq!(res.unwrap_err(), ErrorCode::CommonInvalidStructure);

            TestUtils::cleanup_storage();
        }

        #[test]
        fn create_pool_ledger_config_works_for_invalid_genesis_txn_path() {
            TestUtils::cleanup_storage();

            let pool_name = "create_pool_ledger_config_works_for_invalid_genesis_txn_path";
            let config = r#"{"genesis_txn": "path"}"#.to_string();

            let res = PoolUtils::create_pool_ledger_config(pool_name, Some(config.as_str()));
            assert_eq!(res.unwrap_err(), ErrorCode::CommonIOError);

            TestUtils::cleanup_storage();
        }

        #[test]
        fn create_pool_ledger_config_works_for_twice() {
            TestUtils::cleanup_storage();

            let pool_name = "pool_create";
            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool(pool_name, None, None);
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());

            PoolUtils::create_pool_ledger_config(pool_name, Some(pool_config.as_str())).unwrap();
            let res = PoolUtils::create_pool_ledger_config(pool_name, Some(pool_config.as_str()));

            assert_eq!(res.unwrap_err(), ErrorCode::PoolLedgerConfigAlreadyExistsError);

            TestUtils::cleanup_storage();
        }
    }

    mod open {
        use super::*;

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn open_pool_ledger_works_for_invalid_name() {
            TestUtils::cleanup_storage();
            let pool_name = "open_pool_ledger_works_for_invalid_name";

            let res = PoolUtils::open_pool_ledger(pool_name, None);
            assert_eq!(res.unwrap_err(), ErrorCode::PoolLedgerTerminated);//TODO change it on IOError

            TestUtils::cleanup_storage();
        }

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn open_pool_ledger_works_for_invalid_nodes_file() {
            TestUtils::cleanup_storage();

            let pool_name = "open_pool_ledger_works_for_invalid_nodes_file";
            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool_with_invalid_nodes(pool_name, None);
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());
            PoolUtils::create_pool_ledger_config(pool_name, Some(pool_config.as_str())).unwrap();

            let res = PoolUtils::open_pool_ledger(pool_name, Some(pool_config.as_str()));
            assert_eq!(res.unwrap_err(), ErrorCode::PoolLedgerTerminated);//TODO Replace on InvalidState Error

            TestUtils::cleanup_storage();
        }

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn open_pool_ledger_works_for_wrong_alias() {
            TestUtils::cleanup_storage();

            let pool_name = "open_pool_ledger_works_for_wrong_alias";
            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool_with_wrong_alias(pool_name, None);
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());
            PoolUtils::create_pool_ledger_config(pool_name, Some(pool_config.as_str())).unwrap();

            let res = PoolUtils::open_pool_ledger(pool_name, None);
            assert_eq!(res.unwrap_err(), ErrorCode::PoolLedgerTerminated);//TODO Replace on InvalidState Error

            TestUtils::cleanup_storage();
        }

        #[test]
        #[ignore]
        #[cfg(feature = "local_nodes_pool")] //TODO Not implemented yet
        fn open_pool_ledger_works_for_invalid_config() {
            TestUtils::cleanup_storage();
            let name = "pool_open";
            let config = r#"{"refreshOnOpen": "true"}"#;

            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool(name, None, None);
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());
            PoolUtils::create_pool_ledger_config(name, Some(pool_config.as_str())).unwrap();

            let res = PoolUtils::open_pool_ledger(name, Some(config));
            assert_eq!(res.unwrap_err(), ErrorCode::CommonInvalidStructure);

            TestUtils::cleanup_storage();
        }
    }
}<commit_msg>* Misspelling fixed<commit_after>extern crate indy;

// Workaround to share some utils code based on indy sdk types between tests and indy sdk
use indy::api as api;

#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;

#[macro_use]
mod utils;

#[cfg(feature = "local_nodes_pool")]
use indy::api::ErrorCode;

use utils::environment::EnvironmentUtils;
use utils::pool::PoolUtils;
use utils::test::TestUtils;

mod high_cases {
    use super::*;

    mod create {
        use super::*;

        #[test]
        fn create_pool_ledger_config_works() {
            TestUtils::cleanup_storage();

            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool("pool_create", None, None);
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());
            PoolUtils::create_pool_ledger_config("pool_create", Some(pool_config.as_str())).unwrap();

            TestUtils::cleanup_storage();
        }

        #[test]
        fn create_pool_ledger_config_works_for_empty_name() {
            TestUtils::cleanup_storage();

            let pool_name = "";
            let res = PoolUtils::create_pool_ledger_config(pool_name, None);
            assert_eq!(res.unwrap_err(), ErrorCode::CommonInvalidParam2);

            TestUtils::cleanup_storage();
        }

        #[test]
        fn create_pool_ledger_config_works_for_config_json() {
            TestUtils::cleanup_storage();

            let pool_name = "create_pool_ledger_config_works_for_config_json";
            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool(pool_name, None, None);
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());

            PoolUtils::create_pool_ledger_config(pool_name, Some(pool_config.as_str())).unwrap();

            TestUtils::cleanup_storage();
        }

        #[test]
        fn create_pool_ledger_config_works_for_specific_config() {
            TestUtils::cleanup_storage();

            let pool_name = "create_pool_ledger_config_works_for_specific_config";
            let txn_file_path = EnvironmentUtils::tmp_file_path("specific_filename.txn");
            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool(pool_name, None, Some(txn_file_path.as_path()));
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());

            PoolUtils::create_pool_ledger_config(pool_name, Some(pool_config.as_str())).unwrap();

            TestUtils::cleanup_storage();
        }
    }

    mod open {
        use super::*;

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn open_pool_ledger_works() {
            TestUtils::cleanup_storage();

            let pool_name = "pool_open";
            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool(pool_name, None, None);
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());
            PoolUtils::create_pool_ledger_config(pool_name, Some(pool_config.as_str())).unwrap();

            PoolUtils::open_pool_ledger(pool_name, None).unwrap();

            TestUtils::cleanup_storage();
        }

        #[test]
        #[cfg(feature = "local_nodes_pool")] //TODO Not implemented yet
        fn open_pool_ledger_works_for_config() {
            TestUtils::cleanup_storage();

            let pool_name = "open_pool_ledger_works_for_config";
            let config = r#"{"refreshOnOpen": true}"#;

            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool(pool_name, None, None);
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());
            PoolUtils::create_pool_ledger_config(pool_name, Some(pool_config.as_str())).unwrap();

            PoolUtils::open_pool_ledger(pool_name, Some(config)).unwrap();

            TestUtils::cleanup_storage();
        }

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn open_pool_ledger_works_for_twice() {
            TestUtils::cleanup_storage();

            let pool_name = "pool_open_twice";
            PoolUtils::create_and_open_pool_ledger(pool_name).unwrap();

            let res = PoolUtils::open_pool_ledger(pool_name, None);
            assert_match!(Err(ErrorCode::PoolLedgerInvalidPoolHandle), res);

            TestUtils::cleanup_storage();
        }

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn open_pool_ledger_works_for_two_nodes() {
            TestUtils::cleanup_storage();

            let pool_name = "open_pool_ledger_works_for_two_nodes";
            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool(pool_name, Some(2), None);
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());
            PoolUtils::create_pool_ledger_config(pool_name, Some(pool_config.as_str())).unwrap();

            PoolUtils::open_pool_ledger(pool_name, None).unwrap();

            TestUtils::cleanup_storage();
        }

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn open_pool_ledger_works_for_three_nodes() {
            TestUtils::cleanup_storage();

            let pool_name = "open_pool_ledger_works_for_three_nodes";
            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool(pool_name, Some(3), None);
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());
            PoolUtils::create_pool_ledger_config(pool_name, Some(pool_config.as_str())).unwrap();

            PoolUtils::open_pool_ledger(pool_name, None).unwrap();

            TestUtils::cleanup_storage();
        }
    }

    mod refresh {
        use super::*;

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn indy_refresh_pool_ledger_works() {
            TestUtils::cleanup_storage();

            let pool_handle = PoolUtils::create_and_open_pool_ledger("indy_refresh_pool_ledger_works").unwrap();

            PoolUtils::refresh(pool_handle).unwrap();

            TestUtils::cleanup_storage();
        }
    }

    mod close {
        use super::*;

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn indy_close_pool_ledger_works() {
            TestUtils::cleanup_storage();

            let pool_name = "indy_close_pool_ledger_works";
            let pool_handle = PoolUtils::create_and_open_pool_ledger(pool_name).unwrap();

            PoolUtils::close(pool_handle).unwrap();

            TestUtils::cleanup_storage();
        }

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn indy_close_pool_ledger_works_for_twice() {
            TestUtils::cleanup_storage();

            let pool_name = "indy_close_pool_ledger_works_twice";
            let pool_handle = PoolUtils::create_and_open_pool_ledger(pool_name).unwrap();

            PoolUtils::close(pool_handle).unwrap();
            assert_eq!(PoolUtils::close(pool_handle).unwrap_err(), ErrorCode::PoolLedgerInvalidPoolHandle);

            TestUtils::cleanup_storage();
        }

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn indy_close_pool_ledger_works_for_reopen_after_close() {
            TestUtils::cleanup_storage();

            let pool_name = "indy_close_pool_ledger_works";
            let pool_handle = PoolUtils::create_and_open_pool_ledger(pool_name).unwrap();

            PoolUtils::close(pool_handle).unwrap();
            PoolUtils::open_pool_ledger(pool_name, None).unwrap();

            TestUtils::cleanup_storage();
        }
    }

    mod delete {
        use super::*;

        #[test]
        fn indy_delete_pool_ledger_config_works() {
            TestUtils::cleanup_storage();

            let pool_name = "indy_remove_pool_ledger_config_works";
            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool(pool_name, None, None);
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());
            PoolUtils::create_pool_ledger_config(pool_name, Some(pool_config.as_str())).unwrap();

            PoolUtils::delete(pool_name).unwrap();

            TestUtils::cleanup_storage();
        }

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn indy_delete_pool_ledger_config_works_for_opened() {
            TestUtils::cleanup_storage();

            let pool_name = "indy_remove_pool_ledger_config_works_for_opened";
            PoolUtils::create_and_open_pool_ledger(pool_name).unwrap();

            assert_eq!(PoolUtils::delete(pool_name).unwrap_err(), ErrorCode::CommonInvalidState);

            TestUtils::cleanup_storage();
        }
    }
}

mod medium_cases {
    use super::*;

    mod create {
        use super::*;

        #[test]
        fn create_pool_ledger_config_works_for_invalid_config_json() {
            TestUtils::cleanup_storage();

            let pool_name = "create_pool_ledger_config_works_for_invalid_config";
            let config = r#"{}"#.to_string();

            let res = PoolUtils::create_pool_ledger_config(pool_name, Some(config.as_str()));
            assert_eq!(res.unwrap_err(), ErrorCode::CommonInvalidStructure);

            TestUtils::cleanup_storage();
        }

        #[test]
        fn create_pool_ledger_config_works_for_invalid_genesis_txn_path() {
            TestUtils::cleanup_storage();

            let pool_name = "create_pool_ledger_config_works_for_invalid_genesis_txn_path";
            let config = r#"{"genesis_txn": "path"}"#.to_string();

            let res = PoolUtils::create_pool_ledger_config(pool_name, Some(config.as_str()));
            assert_eq!(res.unwrap_err(), ErrorCode::CommonIOError);

            TestUtils::cleanup_storage();
        }

        #[test]
        fn create_pool_ledger_config_works_for_twice() {
            TestUtils::cleanup_storage();

            let pool_name = "pool_create";
            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool(pool_name, None, None);
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());

            PoolUtils::create_pool_ledger_config(pool_name, Some(pool_config.as_str())).unwrap();
            let res = PoolUtils::create_pool_ledger_config(pool_name, Some(pool_config.as_str()));

            assert_eq!(res.unwrap_err(), ErrorCode::PoolLedgerConfigAlreadyExistsError);

            TestUtils::cleanup_storage();
        }
    }

    mod open {
        use super::*;

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn open_pool_ledger_works_for_invalid_name() {
            TestUtils::cleanup_storage();
            let pool_name = "open_pool_ledger_works_for_invalid_name";

            let res = PoolUtils::open_pool_ledger(pool_name, None);
            assert_eq!(res.unwrap_err(), ErrorCode::PoolLedgerTerminated);//TODO change it on IOError

            TestUtils::cleanup_storage();
        }

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn open_pool_ledger_works_for_invalid_nodes_file() {
            TestUtils::cleanup_storage();

            let pool_name = "open_pool_ledger_works_for_invalid_nodes_file";
            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool_with_invalid_nodes(pool_name, None);
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());
            PoolUtils::create_pool_ledger_config(pool_name, Some(pool_config.as_str())).unwrap();

            let res = PoolUtils::open_pool_ledger(pool_name, Some(pool_config.as_str()));
            assert_eq!(res.unwrap_err(), ErrorCode::PoolLedgerTerminated);//TODO Replace on InvalidState Error

            TestUtils::cleanup_storage();
        }

        #[test]
        #[cfg(feature = "local_nodes_pool")]
        fn open_pool_ledger_works_for_wrong_alias() {
            TestUtils::cleanup_storage();

            let pool_name = "open_pool_ledger_works_for_wrong_alias";
            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool_with_wrong_alias(pool_name, None);
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());
            PoolUtils::create_pool_ledger_config(pool_name, Some(pool_config.as_str())).unwrap();

            let res = PoolUtils::open_pool_ledger(pool_name, None);
            assert_eq!(res.unwrap_err(), ErrorCode::PoolLedgerTerminated);//TODO Replace on InvalidState Error

            TestUtils::cleanup_storage();
        }

        #[test]
        #[ignore]
        #[cfg(feature = "local_nodes_pool")] //TODO Not implemented yet
        fn open_pool_ledger_works_for_invalid_config() {
            TestUtils::cleanup_storage();
            let name = "pool_open";
            let config = r#"{"refreshOnOpen": "true"}"#;

            let txn_file_path = PoolUtils::create_genesis_txn_file_for_test_pool(name, None, None);
            let pool_config = PoolUtils::pool_config_json(txn_file_path.as_path());
            PoolUtils::create_pool_ledger_config(name, Some(pool_config.as_str())).unwrap();

            let res = PoolUtils::open_pool_ledger(name, Some(config));
            assert_eq!(res.unwrap_err(), ErrorCode::CommonInvalidStructure);

            TestUtils::cleanup_storage();
        }
    }
}<|endoftext|>


<commit_before>extern crate mosquitto;
use mosquitto::{Client, Qos};

/* The linked code creates a client that connects to a broker at
 * localhost:1883, subscribes to the topics "tick", "control".
 * When received a message on 'tick', it'll be forwarded to tock
 */

/* 1. Start the broker --> mosquitto -c /etc/mosquitto/mosquitto.conf -d
   2. cargo run
   3. mosquitto_sub -t "tock"
   4. mosquitto_pub -t "tick" -m "Hello World"
   5. mosquitto_pub -t "control" -m "halt" --> stop
*/

//TODO: Check why cargo test some times is not waiting at loop_forever()
#[test]
fn all_ok() {
	//Client::init();
	/* Set before connect */
	let client = Client::new("test").keep_alive(30).clean_session(true).auth("root", "admin");

	let mut clients: Vec<Client> = vec![];

	for i in 0..1000{

		let mut client = Client::new("test".to_string())
					.keep_alive(5)
					.clean_session(false)
					.will("goodbye", "my last words");
		clients.push(client);
	}

	let mut client = Client::new("test")
					.keep_alive(5)
					.clean_session(false)
					.will("goodbye", "my last words");
				//	.auth("admin", "admin");

	let i = 100;

	client.onconnect_callback(move |a: i32|{
									println!("i = {:?}", i);
									println!("@@@ On connect callback {}@@@", a)
						  		});

	match client.connect("ec2-52-77-220-182.ap-southeast-1.compute.amazonaws.com"){
		Ok(_) => println!("Connection successful --> {:?}", "client"),
		Err(n) => panic!("Connection error = {:?}", n)
	}

	client.onsubscribe_callback(move|mid|println!("@@@ Subscribe request received for message mid = {:?}", mid));
	client.subscribe("hello/world", Qos::AtMostOnce);

	let mut count = 0; //TODO: Weird count print in closure callback
	client.onmesssage_callback(move |s|{
									println!("@@@ Message = {:?}, Count = {:?}", s, count);
								   });

	client.onpublish_callback(move |mid|println!("@@@ Publish request received for message mid = {:?}", mid));

	for i in 0..5{
	 	client.publish("hello/world".to_string(), "Hello".to_string(), Qos::AtMostOnce);
	}

	client.loop_forever();
	Client::cleanup();
}

 // #[test]
/* Tests idle connections */
// fn connect_stress(){
// 	Client::init();
// 	let id_prefix: String = "ath".to_string();
// 	let mut clients: Vec<Client> = Vec::new();

// 	for i in 0..10{
// 		let id = format!("{}-{}", id_prefix, i);
// 		//println!("{:?}", id);
// 		let mut client = Client::new(id)
// 					.keep_alive(30)
// 					.clean_session(true)
// 					.will("goodbye", "my last words");
// 		clients.push(client);
// 		match clients[i].connect("ec2-52-77-220-182.ap-southeast-1.compute.amazonaws.com"){
// 			Ok(_) => println!("Connection successful --> {:?}", "client"),
// 			Err(n) => panic!("Connection error = {:?}", n)
// 		}
// 	}
// 	clients[0].loop_forever();
// 	Client::cleanup();

// /************************ Stress observations **************************
//   Broker should receive a PINGREQ message every 'keep alive' time if no other messages are exchanged in this time
//   Network loop should kick in at right time and send PINGREQ or else broker will disconnect the client. If there are
//   a lot of connections, choose 'keep alive' in such a way that all the network threads (per client) are hit in
//   'keep alive' seconds

//   - [ ] 'keep alive' of 5 seems to be ok for 100 client connections
//   - [ ] 'keep alive' of 30 seems to be ok for 300 client connections
// */
// }
<commit_msg>Adding client persistence test<commit_after>extern crate mosquitto;
use mosquitto::{Client, Qos};

// The linked code creates a client that connects to a broker at
// localhost:1883, subscribes to the topics "tick", "control".
// When received a message on 'tick', it'll be forwarded to tock
//

// 1. Start the broker --> mosquitto -c /etc/mosquitto/mosquitto.conf -d
// 2. cargo run
// 3. mosquitto_sub -t "tock"
// 4. mosquitto_pub -t "tick" -m "Hello World"
// 5. mosquitto_pub -t "control" -m "halt" --> stop
//

// TODO: Check why cargo test some times is not waiting at loop_forever()
// #[test]
fn all_ok() {
    // Client::init();
    // Set before connect

    let client = Client::new("test").keep_alive(30).clean_session(true).auth("root", "admin");

    let mut clients: Vec<Client> = vec![];

    for i in 0..1000 {

        let mut client = Client::new("test".to_string())
                             .keep_alive(5)
                             .clean_session(false)
                             .will("goodbye", "my last words");
        clients.push(client);
    }

    let mut client = Client::new("test")
                         .keep_alive(5)
                         .clean_session(false)
                         .will("goodbye", "my last words");
    // .auth("admin", "admin");

    let i = 100;

    client.onconnect_callback(move |a: i32| {
        println!("i = {:?}", i);
        println!("@@@ On connect callback {}@@@", a)
    });

    match client.connect("ec2-52-77-220-182.ap-southeast-1.compute.amazonaws.com") {
        Ok(_) => println!("Connection successful --> {:?}", "client"),
        Err(n) => panic!("Connection error = {:?}", n),
    }

    client.onsubscribe_callback(move |mid| {
        println!("@@@ Subscribe request received for message mid = {:?}", mid)
    });
    client.subscribe("hello/world", Qos::AtMostOnce);

    let mut count = 0; //TODO: Weird count print in closure callback
    client.onmesssage_callback(move |s| {
        println!("@@@ Message = {:?}, Count = {:?}", s, count);
    });

    client.onpublish_callback(move |mid| {
        println!("@@@ Publish request received for message mid = {:?}", mid)
    });

    for i in 0..5 {
        client.publish("hello/world".to_string(),
                       "Hello".to_string(),
                       Qos::AtMostOnce);
    }

    client.loop_forever();
    Client::cleanup();
}

#[test]
fn client_persistance() {
    let client_test = Client::new("test").keep_alive(30).clean_session(true);

    let mut clients: Vec<Client> = vec![];

    for i in 0..1000 {
        let id = format!("client-{}", i);
        let mut client = Client::new(id)
                             .keep_alive(5)
                             .clean_session(false)
                             .will("goodbye", "my last words");
        clients.push(client);
    }

    for client in clients.iter_mut() {
        match client.connect("ec2-52-77-220-182.ap-southeast-1.compute.amazonaws.com") {
            Ok(_) => println!("Connection successful --> {:?}", client.id),
            Err(n) => panic!("Connection error = {:?}", n),
        }
    }

    client_test.loop_forever();
}

#[test]
fn idle_connect() {
    Client::init();
    let id_prefix: String = "ath".to_string();
    let mut clients: Vec<Client> = Vec::new();

    for i in 0..10 {
        let id = format!("{}-{}", id_prefix, i);
        // println!("{:?}", id);
        let mut client = Client::new(id)
                             .keep_alive(30)
                             .clean_session(true)
                             .will("goodbye", "my last words");
        clients.push(client);
        match clients[i].connect("ec2-52-77-220-182.ap-southeast-1.compute.amazonaws.com") {
            Ok(_) => println!("Connection successful --> {:?}", "client"),
            Err(n) => panic!("Connection error = {:?}", n),
        }
    }
    clients[0].loop_forever();
    Client::cleanup();
}

// /************************ Stress observations **************************
//   Broker should receive a PINGREQ message every 'keep alive' time if no other messages are exchanged in this time
//   Network loop should kick in at right time and send PINGREQ or else broker will disconnect the client. If there are
//   a lot of connections, choose 'keep alive' in such a way that all the network threads (per client) are hit in
//   'keep alive' seconds

//   - [ ] 'keep alive' of 5 seems to be ok for 100 client connections
//   - [ ] 'keep alive' of 30 seems to be ok for 300 client connections
// */
// }
<|endoftext|>


<commit_before>extern crate mosquitto;

use mosquitto::{Client, Qos};
use std::thread;
use std::time::Duration;

///#TESTCASES TO CHECK
///
///- [ ] Clent ram persistence. When broker goes down, client should keep track of all its publishes (with QoS 1,2)
///- [ ] Client disk persistance. Broker went down/ Scooter internet down. Client crashed. Broker up. Client up.
///      Now client should resend all the publishes after broker crash - before client crash.
///- [ ] Broker ram persistence. Broker should save messages that are supposed to be sent to disconnected persistent clients
///      (one's connected with clean_session = false).
///- [ ] Broker disk persistence. If broker went down before publishing all the messages (let's say a persisent client
///      which is supposed to receive the publish is down), it should retry sending that message when it is back up again.
///      I.e all the broker state should be written to permanent storage
///      Disconnect client, publish message, disconnect broker, connect broker, connect client.
///      Check is there is a way to periodically update the disk database incase of unexpected broker crashes.
///- [ ] Broker should remember client subscriptions for persistent clients (clean_session = false)even after disconnections
///      and should directly handle publishes to them after reconnections.
///      Disconnect and connect back broker and see if subscriptions persist.
///      Note: Set 'persist = true' in mosquitto.conf
///- [ ] Disconnection handling. If broker goes down, client should reconnect automatically when broker comes up
///- [ ] Reconnection handling when both broker and client are crashed.
// TODO: Check why cargo test some times is not waiting at loop_forever()
// #[test]
// fn all_ok() {
//     // Set before connect
//     let client = Client::new("test", true)
//                      .unwrap()
//                      .keep_alive(30);
//
//     let mut clients: Vec<Client> = vec![];
//
//     for i in 0..1000 {
//
//         let mut client = Client::new(&"test".to_string(), true)
//                              .unwrap()
//                              .keep_alive(5)
//                              .will("goodbye", "my last words");
//         clients.push(client);
//     }
//
//     let mut client = Client::new("test", true)
//                          .unwrap()
//                          .keep_alive(5)
//                          .will("goodbye", "my last words");
//
//
//
//
//     let i = 100;
//
//     client.onconnect_callback(move |a: i32| {
//         println!("i = {:?}", i);
//         println!("@@@ On connect callback {}@@@", a)
//     });
//
//     match client.connect("ec2-52-77-220-182.ap-southeast-1.compute.amazonaws.com") {
//         Ok(_) => println!("Connection successful --> {:?}", "client"),
//         Err(n) => panic!("Connection error = {:?}", n),
//     }
//
//     client.onsubscribe_callback(move |mid| {
//         println!("@@@ Subscribe request received for message mid = {:?}", mid)
//     });
//     client.subscribe("hello/world", Qos::AtMostOnce);
//
//     let mut count = 0; //TODO: Weird count print in closure callback
//     client.onmesssage_callback(move |s| {
//         println!("@@@ Message = {:?}, Count = {:?}", s, count);
//     });
//
//
//     client.onpublish_callback(move |mid| {
//         println!("@@@ Publish request received for message mid = {:?}", mid)
//     });
//
//     for i in 0..5 {
//         client.publish("hello/world".to_string(),
//                        "Hello".to_string(),
//                        Qos::AtMostOnce);
//     }
//
//     client.loop_forever();
// }
///###ANALYSIS
/// -[X] Auto reconnect working
///
/// -[X] Client RAM persistance working
/// Testcase: All the scooter clients will start publishing and AWS client receives it.
///           At some point in between, broker goes down and hence AWS client will stop receiving
///           AWS client should receive all the messages when broker comes up again. Total count should be = 100
///
#[test]
fn client_persistance() {
    let mut clients: Vec<Client> = vec![];

    for i in 0..10 {
        let id = format!("client-{}", i);
        let mut client = Client::new(&id, true)
                             .unwrap()
                             .keep_alive(5)
                             .will("goodbye", "my last words");
        clients.push(client);
    }

    for client in clients.iter_mut() {
        client.onconnect_callback(move |a: i32| {
            println!("@@@ connect callback ret = {} @@@", a);
        });
    }

    // for client in clients.iter_mut() {
    //     match client.connect("ec2-52-77-220-182.ap-southeast-1.compute.amazonaws.com") {
    //         Ok(_) => println!("Connection successful --> {:?}", client.id),
    //         Err(n) => panic!("Connection error = {:?}", n),
    //     }
    // }

    for client in clients.iter_mut() {
        match client.secure_connect("localhost",
                                    "/home/raviteja/Desktop/certs/ca.crt",
                                    Some(("/home/raviteja/Desktop/certs/scooter.crt",
                                          "/home/raviteja/Desktop/certs/scooter.key"))) {
            Ok(_) => println!("Connection successful --> {:?}", client.id),
            Err(n) => panic!("Connection error = {:?}", n),
        }
    }

    let mut count = 0;
    for client in clients.iter_mut() {
        for i in 0..10 {
            // thread::sleep(Duration::from_millis(100));
            let message = format!("{}...{:?} - Message {}", count, client.id, i);
            client.publish("ather/log-ship", &message, Qos::AtLeastOnce);
            count += 1;
        }
    }
    thread::sleep(Duration::from_millis(100));
}

// // #[test]
// fn idle_connect() {
//     let id_prefix: String = "ath".to_string();
//     let mut clients: Vec<Client> = Vec::new();

//     for i in 0..10 {
//         let id = format!("{}-{}", id_prefix, i);
//         // println!("{:?}", id);
//         let mut client = Client::new(&id, true)
//                              .unwrap()
//                              .keep_alive(30)
//                              .will("goodbye", "my last words");
//         clients.push(client);
//         match clients[i].connect("ec2-52-77-220-182.ap-southeast-1.compute.amazonaws.com") {
//             Ok(_) => println!("Connection successful --> {:?}", "client"),
//             Err(n) => panic!("Connection error = {:?}", n),
//         }
//     }
//     clients[0].loop_forever();
//     mosquitto::cleanup();
// }

// /************************ Stress observations **************************
//   Broker should receive a PINGREQ message every 'keep alive' time if no other messages are exchanged in this time
//   Network loop should kick in at right time and send PINGREQ or else broker will disconnect the client. If there are
//   a lot of connections, choose 'keep alive' in such a way that all the network threads (per client) are hit in
//   'keep alive' seconds

//   - [ ] 'keep alive' of 5 seems to be ok for 100 client connections
//   - [ ] 'keep alive' of 30 seems to be ok for 300 client connections
// */
// }
<commit_msg>Aws testing<commit_after>extern crate mosquitto;

use mosquitto::{Client, Qos};
use std::thread;
use std::time::Duration;

///#TESTCASES TO CHECK
///
///- [ ] Clent ram persistence. When broker goes down, client should keep track of all its publishes (with QoS 1,2)
///- [ ] Client disk persistance. Broker went down/ Scooter internet down. Client crashed. Broker up. Client up.
///      Now client should resend all the publishes after broker crash - before client crash.
///- [ ] Broker ram persistence. Broker should save messages that are supposed to be sent to disconnected persistent clients
///      (one's connected with clean_session = false).
///- [ ] Broker disk persistence. If broker went down before publishing all the messages (let's say a persisent client
///      which is supposed to receive the publish is down), it should retry sending that message when it is back up again.
///      I.e all the broker state should be written to permanent storage
///      Disconnect client, publish message, disconnect broker, connect broker, connect client.
///      Check is there is a way to periodically update the disk database incase of unexpected broker crashes.
///- [ ] Broker should remember client subscriptions for persistent clients (clean_session = false)even after disconnections
///      and should directly handle publishes to them after reconnections.
///      Disconnect and connect back broker and see if subscriptions persist.
///      Note: Set 'persist = true' in mosquitto.conf
///- [ ] Disconnection handling. If broker goes down, client should reconnect automatically when broker comes up
///- [ ] Reconnection handling when both broker and client are crashed.
// TODO: Check why cargo test some times is not waiting at loop_forever()
// #[test]
// fn all_ok() {
//     // Set before connect
//     let client = Client::new("test", true)
//                      .unwrap()
//                      .keep_alive(30);
//
//     let mut clients: Vec<Client> = vec![];
//
//     for i in 0..1000 {
//
//         let mut client = Client::new(&"test".to_string(), true)
//                              .unwrap()
//                              .keep_alive(5)
//                              .will("goodbye", "my last words");
//         clients.push(client);
//     }
//
//     let mut client = Client::new("test", true)
//                          .unwrap()
//                          .keep_alive(5)
//                          .will("goodbye", "my last words");
//
//
//
//
//     let i = 100;
//
//     client.onconnect_callback(move |a: i32| {
//         println!("i = {:?}", i);
//         println!("@@@ On connect callback {}@@@", a)
//     });
//
//     match client.connect("ec2-52-77-220-182.ap-southeast-1.compute.amazonaws.com") {
//         Ok(_) => println!("Connection successful --> {:?}", "client"),
//         Err(n) => panic!("Connection error = {:?}", n),
//     }
//
//     client.onsubscribe_callback(move |mid| {
//         println!("@@@ Subscribe request received for message mid = {:?}", mid)
//     });
//     client.subscribe("hello/world", Qos::AtMostOnce);
//
//     let mut count = 0; //TODO: Weird count print in closure callback
//     client.onmesssage_callback(move |s| {
//         println!("@@@ Message = {:?}, Count = {:?}", s, count);
//     });
//
//
//     client.onpublish_callback(move |mid| {
//         println!("@@@ Publish request received for message mid = {:?}", mid)
//     });
//
//     for i in 0..5 {
//         client.publish("hello/world".to_string(),
//                        "Hello".to_string(),
//                        Qos::AtMostOnce);
//     }
//
//     client.loop_forever();
// }
///###ANALYSIS
/// -[X] Auto reconnect working
///
/// -[X] Client RAM persistance working
/// Testcase: All the scooter clients will start publishing and AWS client receives it.
///           At some point in between, broker goes down and hence AWS client will stop receiving
///           AWS client should receive all the messages when broker comes up again. Total count should be = 100
///
#[test]
fn client_persistance() {
    let mut clients: Vec<Client> = vec![];

    for i in 0..10 {
        let id = format!("client-{}", i);
        let mut client = Client::new(&id, true)
                             .unwrap()
                             .keep_alive(5)
                             .will("goodbye", "my last words");
        clients.push(client);
    }

    for client in clients.iter_mut() {
        client.onconnect_callback(move |a: i32| {
            println!("@@@ connect callback ret = {} @@@", a);
        });
    }

    // for client in clients.iter_mut() {
    //     match client.connect("ec2-52-77-220-182.ap-southeast-1.compute.amazonaws.com") {
    //         Ok(_) => println!("Connection successful --> {:?}", client.id),
    //         Err(n) => panic!("Connection error = {:?}", n),
    //     }
    // }

    println!("@@@@@@@@@");
    for client in clients.iter_mut() {
        match client.secure_connect("ec2-52-77-220-182.ap-southeast-1.compute.amazonaws.com",
                                    "/home/raviteja/Desktop/certs/ca.crt",
                                    Some(("/home/raviteja/Desktop/certs/scooter.crt",
                                          "/home/raviteja/Desktop/certs/scooter.key"))) {
            Ok(_) => println!("Connection successful --> {:?}", client.id),
            Err(n) => panic!("Connection error = {:?}", n),
        }
    }

    let mut count = 0;
    for client in clients.iter_mut() {
        for i in 0..10 {
            // thread::sleep(Duration::from_millis(100));
            let message = format!("{}...{:?} - Message {}", count, client.id, i);
            client.publish("ather/log-ship", &message, Qos::AtLeastOnce);
            count += 1;
        }
    }
    thread::sleep(Duration::from_millis(100));
}

// // #[test]
// fn idle_connect() {
//     let id_prefix: String = "ath".to_string();
//     let mut clients: Vec<Client> = Vec::new();

//     for i in 0..10 {
//         let id = format!("{}-{}", id_prefix, i);
//         // println!("{:?}", id);
//         let mut client = Client::new(&id, true)
//                              .unwrap()
//                              .keep_alive(30)
//                              .will("goodbye", "my last words");
//         clients.push(client);
//         match clients[i].connect("ec2-52-77-220-182.ap-southeast-1.compute.amazonaws.com") {
//             Ok(_) => println!("Connection successful --> {:?}", "client"),
//             Err(n) => panic!("Connection error = {:?}", n),
//         }
//     }
//     clients[0].loop_forever();
//     mosquitto::cleanup();
// }

// /************************ Stress observations **************************
//   Broker should receive a PINGREQ message every 'keep alive' time if no other messages are exchanged in this time
//   Network loop should kick in at right time and send PINGREQ or else broker will disconnect the client. If there are
//   a lot of connections, choose 'keep alive' in such a way that all the network threads (per client) are hit in
//   'keep alive' seconds

//   - [ ] 'keep alive' of 5 seems to be ok for 100 client connections
//   - [ ] 'keep alive' of 30 seems to be ok for 300 client connections
// */
// }
<|endoftext|>