diff --git a/wfassoc/src/highlevel/program.rs b/wfassoc/src/highlevel/program.rs index 4fd0048..34485a4 100644 --- a/wfassoc/src/highlevel/program.rs +++ b/wfassoc/src/highlevel/program.rs @@ -55,6 +55,7 @@ pub enum ProgramError { // region: Program /// Program is a complete and immutable program representer +#[derive(Debug)] pub struct Program { app_paths_key: lowlevel::AppPathsKey, applications_key: lowlevel::ApplicationsKey, @@ -640,6 +641,7 @@ struct ProgramProgIdExtKey { /// Exposed struct representing this program provided method for opening specific file extension. /// /// The data including the extension name, diaplay name and icon. +#[derive(Debug)] pub struct ProgramSelfExtStatus { ext: concept::Ext, name: String, @@ -680,6 +682,7 @@ impl ProgramSelfExtStatus { /// Exposed struct representing the default associated program of specific file extension. /// /// The data including the diaplay name and icon. +#[derive(Debug)] pub struct ProgramExtStatus { name: String, icon: concept::IconRc, diff --git a/wfassoc/tests/highlevel.rs b/wfassoc/tests/highlevel.rs index 28251e1..f983388 100644 --- a/wfassoc/tests/highlevel.rs +++ b/wfassoc/tests/highlevel.rs @@ -1,7 +1,169 @@ use wfassoc::highlevel::*; mod common; -#[test] -fn test() { - common::check_sandbox(); +static IDENTIFIER: &str = "Passoc"; +static APP_PATH: &str = r"C:\Passoc\passoc.exe"; +static CLSID: &str = "{59031a47-3f72-44a7-89c5-5595fe6b30ee}"; +static EXT_BODY: &str = "pacfg"; + +fn make_valid_schema() -> Schema { + let mut schema = Schema::new(); + schema.set_identifier(IDENTIFIER); + schema.set_path(APP_PATH); + schema.set_clsid(CLSID); + schema.add_str("main_name", "Passoc Application").unwrap(); + schema.add_str("ext_name", "Pacfg File").unwrap(); + schema.add_icon("main_icon", r"notepad.exe,0").unwrap(); + schema.add_icon("ext_icon", r"notepad.exe,0").unwrap(); + schema.add_behavior("main_behavior", "notepad.exe %1").unwrap(); + schema.add_behavior("ext_behavior", "notepad.exe %1").unwrap(); + schema.set_name(Some("main_name")); + schema.set_icon(Some("main_icon")); + schema.set_behavior(Some("main_behavior")); + schema.add_ext(EXT_BODY, "ext_name", "ext_icon", "ext_behavior").unwrap(); + schema } + +// region: Schema + +#[test] +fn test_schema() { + common::check_sandbox(); + + // valid schema -> valid program + let schema = make_valid_schema(); + let rv = schema.into_program(); + assert!(rv.is_ok()); + + // missing identifier + let mut schema = Schema::new(); + schema.set_path(APP_PATH); + let rv = schema.into_program(); + assert!(rv.is_err()); + + // invalid path + let mut schema = Schema::new(); + schema.set_identifier(IDENTIFIER); + schema.set_path(r"C:\"); + let rv = schema.into_program(); + assert!(rv.is_err()); + + // prepare a schema for following test + let mut schema = Schema::new(); + schema.set_identifier(IDENTIFIER); + schema.set_path(APP_PATH); + + // duplicate detection on add_str + let rv = schema.add_str("k", "v1"); + assert!(rv.is_ok()); + let rv = schema.add_str("k", "v2"); + assert!(rv.is_err()); + + // duplicate detection on add_icon + let rv = schema.add_icon("k", "v1"); + assert!(rv.is_ok()); + let rv = schema.add_icon("k", "v2"); + assert!(rv.is_err()); + + // duplicate detection on add_behavior + let rv = schema.add_behavior("k", "v1"); + assert!(rv.is_ok()); + let rv = schema.add_behavior("k", "v2"); + assert!(rv.is_err()); + + // duplicate detection on add_ext + let rv = schema.add_ext(EXT_BODY, "k", "k", "k"); + assert!(rv.is_ok()); + let rv = schema.add_ext(EXT_BODY, "k", "k", "k"); + assert!(rv.is_err()); + + // ext referencing non-existent map entry + schema.add_str("k2", "v").unwrap(); + schema.add_icon("k2", "v").unwrap(); + let rv = schema.add_ext("pacfg2", "nonexistent", "k2", "k2"); + assert!(rv.is_ok()); + let rv = schema.into_program(); + assert!(rv.is_err()); +} + +// endregion + +// region: Program + +#[test] +fn test_program() { + common::check_sandbox(); + + fn tester(scope: Scope, view: View) { + // build program + let schema = make_valid_schema(); + let rv = schema.into_program(); + assert!(rv.is_ok()); + let mut program = rv.unwrap(); + + // cleanup before test + let rv = program.unregister(scope); + assert!(rv.is_ok()); + + // initially not registered + let rv = program.is_registered(scope); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), false); + + // register + let rv = program.register(scope); + assert!(rv.is_ok()); + + // should be registered now + let rv = program.is_registered(scope); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), true); + + // link_ext first so ext key exists before register + let rv = program.link_ext(scope, 0); + assert!(rv.is_ok()); + + // query_ext after link + register + let rv = program.query_ext(view, 0); + assert!(rv.is_ok()); + assert!(rv.unwrap().is_some()); + + // unlink_ext + let rv = program.unlink_ext(scope, 0); + assert!(rv.is_ok()); + + // query_ext should return None + let rv = program.query_ext(view, 0); + assert!(rv.is_ok()); + assert!(rv.unwrap().is_none()); + + // resolve_name, resolve_icon + let rv = program.resolve_name(); + assert!(rv.is_ok()); + let rv = program.resolve_icon(); + assert!(rv.is_ok()); + + // exts_len, find_ext, resolve_ext + assert_eq!(program.exts_len(), 1); + assert_eq!(program.find_ext(EXT_BODY), Some(0)); + let rv = program.resolve_ext(0); + assert!(rv.is_ok()); + + // bad index + let rv = program.resolve_ext(1); + assert!(rv.is_err()); + let rv = program.link_ext(scope, 1); + assert!(rv.is_err()); + let rv = program.query_ext(view, 1); + assert!(rv.is_err()); + + // cleanup + let rv = program.unregister(scope); + assert!(rv.is_ok()); + } + + tester(Scope::User, View::User); + tester(Scope::System, View::System); +} + +// endregion diff --git a/wfassoc/tests/lowlevel.rs b/wfassoc/tests/lowlevel.rs index 51ce5b7..8b9322b 100644 --- a/wfassoc/tests/lowlevel.rs +++ b/wfassoc/tests/lowlevel.rs @@ -17,6 +17,161 @@ static VERB: LazyLock = LazyLock::new(|| { ShellVerb::new(verb, cmdline) }); +// region: AppPathsKey + +#[test] +fn test_app_paths_key() { + common::check_sandbox(); + + static APP_PATH: &str = r"C:\Program Files\Passoc\passoc.exe"; + static APP_DIR: &str = r"C:\Program Files\Passoc"; + + fn tester(scope: Scope) { + let mut key = AppPathsKey::new(APP_FILE.clone()); + + // delete and ensure + let rv = key.delete(scope); + assert!(rv.is_ok()); + let rv = key.is_exist(scope); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), false); + let rv = key.ensure(scope); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), true); + let rv = key.is_exist(scope); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), true); + let rv = key.ensure(scope); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), false); + + // get/set default + let rv = key.set_default(scope, APP_PATH); + assert!(rv.is_ok()); + let rv = key.get_default(scope); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), APP_PATH); + + // get/set path + let rv = key.set_path(scope, APP_DIR); + assert!(rv.is_ok()); + let rv = key.get_path(scope); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), APP_DIR); + + // clean up + let rv = key.delete(scope); + assert!(rv.is_ok()); + } + + tester(Scope::User); + tester(Scope::System); +} + +// endregion + +// region: ApplicationsKey + +#[test] +fn test_applications_key() { + common::check_sandbox(); + + static FRIENDLY_APP_NAME: LazyLock = + LazyLock::new(|| "Passoc Application".into()); + + fn tester(scope: Scope, view: View) { + let mut key = ApplicationsKey::new(APP_FILE.clone()); + + // delete and ensure + let rv = key.delete(scope); + assert!(rv.is_ok()); + let rv = key.is_exist(view); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), false); + let rv = key.ensure(scope); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), true); + let rv = key.is_exist(view); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), true); + let rv = key.ensure(scope); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), false); + + // get/set shell verb + let rv = key.set_shell_verb(scope, Some(&VERB)); + assert!(rv.is_ok()); + let rv = key.get_shell_verb(view); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), Some(VERB.clone())); + let rv = key.set_shell_verb(scope, None); + assert!(rv.is_ok()); + let rv = key.get_shell_verb(view); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), None); + + // get/set default icon + let rv = key.set_default_icon(scope, Some(&ICON)); + assert!(rv.is_ok()); + let rv = key.get_default_icon(view); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), Some(ICON.clone())); + let rv = key.set_default_icon(scope, None); + assert!(rv.is_ok()); + let rv = key.get_default_icon(view); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), None); + + // get/set friendly app name + let rv = key.set_friendly_app_name(scope, Some(&FRIENDLY_APP_NAME)); + assert!(rv.is_ok()); + let rv = key.get_friendly_app_name(view); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), Some(FRIENDLY_APP_NAME.clone())); + let rv = key.set_friendly_app_name(scope, None); + assert!(rv.is_ok()); + let rv = key.get_friendly_app_name(view); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), None); + + // get/set supported types + let rv = key.set_supported_types(scope, Some(&vec![EXT.deref()])); + assert!(rv.is_ok()); + let rv = key.get_supported_types(view); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), Some(vec![EXT.clone()])); + let rv = key.set_supported_types(scope, None); + assert!(rv.is_ok()); + let rv = key.get_supported_types(view); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), None); + + // get/set no open with + let rv = key.get_no_open_with(view); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), false); + let rv = key.set_no_open_with(scope, true); + assert!(rv.is_ok()); + let rv = key.get_no_open_with(view); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), true); + let rv = key.set_no_open_with(scope, false); + assert!(rv.is_ok()); + let rv = key.get_no_open_with(view); + assert!(rv.is_ok()); + assert_eq!(rv.unwrap(), false); + + // clean up + let rv = key.delete(scope); + assert!(rv.is_ok()); + } + + tester(Scope::User, View::User); + tester(Scope::System, View::System); +} + +// endregion + // region: ExtKey #[test] @@ -173,158 +328,3 @@ fn test_prog_id_key() { } // endregion - -// region: AppPathsKey - -#[test] -fn test_app_paths_key() { - common::check_sandbox(); - - static APP_PATH: &str = r"C:\Program Files\Passoc\passoc.exe"; - static APP_DIR: &str = r"C:\Program Files\Passoc"; - - fn tester(scope: Scope) { - let mut key = AppPathsKey::new(APP_FILE.clone()); - - // delete and ensure - let rv = key.delete(scope); - assert!(rv.is_ok()); - let rv = key.is_exist(scope); - assert!(rv.is_ok()); - assert_eq!(rv.unwrap(), false); - let rv = key.ensure(scope); - assert!(rv.is_ok()); - assert_eq!(rv.unwrap(), true); - let rv = key.is_exist(scope); - assert!(rv.is_ok()); - assert_eq!(rv.unwrap(), true); - let rv = key.ensure(scope); - assert!(rv.is_ok()); - assert_eq!(rv.unwrap(), false); - - // get/set default - let rv = key.set_default(scope, APP_PATH); - assert!(rv.is_ok()); - let rv = key.get_default(scope); - assert!(rv.is_ok()); - assert_eq!(rv.unwrap(), APP_PATH); - - // get/set path - let rv = key.set_path(scope, APP_DIR); - assert!(rv.is_ok()); - let rv = key.get_path(scope); - assert!(rv.is_ok()); - assert_eq!(rv.unwrap(), APP_DIR); - - // clean up - let rv = key.delete(scope); - assert!(rv.is_ok()); - } - - tester(Scope::User); - tester(Scope::System); -} - -// endregion - -// region: ApplicationsKey - -#[test] -fn test_applications_key() { - common::check_sandbox(); - - static FRIENDLY_APP_NAME: LazyLock = - LazyLock::new(|| "Passoc Application".into()); - - fn tester(scope: Scope, view: View) { - let mut key = ApplicationsKey::new(APP_FILE.clone()); - - // delete and ensure - let rv = key.delete(scope); - assert!(rv.is_ok()); - let rv = key.is_exist(view); - assert!(rv.is_ok()); - assert_eq!(rv.unwrap(), false); - let rv = key.ensure(scope); - assert!(rv.is_ok()); - assert_eq!(rv.unwrap(), true); - let rv = key.is_exist(view); - assert!(rv.is_ok()); - assert_eq!(rv.unwrap(), true); - let rv = key.ensure(scope); - assert!(rv.is_ok()); - assert_eq!(rv.unwrap(), false); - - // get/set shell verb - let rv = key.set_shell_verb(scope, Some(&VERB)); - assert!(rv.is_ok()); - let rv = key.get_shell_verb(view); - assert!(rv.is_ok()); - assert_eq!(rv.unwrap(), Some(VERB.clone())); - let rv = key.set_shell_verb(scope, None); - assert!(rv.is_ok()); - let rv = key.get_shell_verb(view); - assert!(rv.is_ok()); - assert_eq!(rv.unwrap(), None); - - // get/set default icon - let rv = key.set_default_icon(scope, Some(&ICON)); - assert!(rv.is_ok()); - let rv = key.get_default_icon(view); - assert!(rv.is_ok()); - assert_eq!(rv.unwrap(), Some(ICON.clone())); - let rv = key.set_default_icon(scope, None); - assert!(rv.is_ok()); - let rv = key.get_default_icon(view); - assert!(rv.is_ok()); - assert_eq!(rv.unwrap(), None); - - // get/set friendly app name - let rv = key.set_friendly_app_name(scope, Some(&FRIENDLY_APP_NAME)); - assert!(rv.is_ok()); - let rv = key.get_friendly_app_name(view); - assert!(rv.is_ok()); - assert_eq!(rv.unwrap(), Some(FRIENDLY_APP_NAME.clone())); - let rv = key.set_friendly_app_name(scope, None); - assert!(rv.is_ok()); - let rv = key.get_friendly_app_name(view); - assert!(rv.is_ok()); - assert_eq!(rv.unwrap(), None); - - // get/set supported types - let rv = key.set_supported_types(scope, Some(&vec![EXT.deref()])); - assert!(rv.is_ok()); - let rv = key.get_supported_types(view); - assert!(rv.is_ok()); - assert_eq!(rv.unwrap(), Some(vec![EXT.clone()])); - let rv = key.set_supported_types(scope, None); - assert!(rv.is_ok()); - let rv = key.get_supported_types(view); - assert!(rv.is_ok()); - assert_eq!(rv.unwrap(), None); - - // get/set no open with - let rv = key.get_no_open_with(view); - assert!(rv.is_ok()); - assert_eq!(rv.unwrap(), false); - let rv = key.set_no_open_with(scope, true); - assert!(rv.is_ok()); - let rv = key.get_no_open_with(view); - assert!(rv.is_ok()); - assert_eq!(rv.unwrap(), true); - let rv = key.set_no_open_with(scope, false); - assert!(rv.is_ok()); - let rv = key.get_no_open_with(view); - assert!(rv.is_ok()); - assert_eq!(rv.unwrap(), false); - - // clean up - let rv = key.delete(scope); - assert!(rv.is_ok()); - } - - tester(Scope::User, View::User); - tester(Scope::System, View::System); -} - -// endregion