diff options
Diffstat (limited to 'src/tests')
-rw-r--r-- | src/tests/extension_model_test.py | 52 | ||||
-rw-r--r-- | src/tests/extensions.rs | 65 | ||||
-rw-r--r-- | src/tests/mod.rs | 13 | ||||
-rw-r--r-- | src/tests/run.rs | 112 |
4 files changed, 242 insertions, 0 deletions
diff --git a/src/tests/extension_model_test.py b/src/tests/extension_model_test.py new file mode 100644 index 0000000..651c8e7 --- /dev/null +++ b/src/tests/extension_model_test.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python + +from argparse import ( + ArgumentParser, +) +from enum import ( + Enum, +) +from sys import ( + argv, + exit, +) + + +class Action(Enum): + """An action to perform.""" + NITROCLI = "nitrocli" + MODEL = "model" + VERBOSITY = "verbosity" + + @classmethod + def all(cls): + """Return the list of all the enum members' values.""" + return [x.value for x in cls.__members__.values()] + + +def main(args): + """The extension's main function.""" + parser = ArgumentParser() + parser.add_argument(choices=Action.all(), dest="what") + parser.add_argument("--nitrocli", action="store", default=None) + parser.add_argument("--model", action="store", default=None) + # We deliberately store the argument to this option as a string + # because we can differentiate between None and a valid value, in + # order to verify that it really is supplied. + parser.add_argument("--verbosity", action="store", default=None) + + namespace = parser.parse_args(args[1:]) + if namespace.what == Action.NITROCLI.value: + print(namespace.nitrocli) + elif namespace.what == Action.MODEL.value: + print(namespace.model) + elif namespace.what == Action.VERBOSITY.value: + print(namespace.verbosity) + else: + return 1 + + return 0 + + +if __name__ == "__main__": + exit(main(argv)) diff --git a/src/tests/extensions.rs b/src/tests/extensions.rs new file mode 100644 index 0000000..9e70181 --- /dev/null +++ b/src/tests/extensions.rs @@ -0,0 +1,65 @@ +// extensions.rs + +// ************************************************************************* +// * Copyright (C) 2020 Daniel Mueller (deso@posteo.net) * +// * * +// * This program is free software: you can redistribute it and/or modify * +// * it under the terms of the GNU General Public License as published by * +// * the Free Software Foundation, either version 3 of the License, or * +// * (at your option) any later version. * +// * * +// * This program is distributed in the hope that it will be useful, * +// * but WITHOUT ANY WARRANTY; without even the implied warranty of * +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +// * GNU General Public License for more details. * +// * * +// * You should have received a copy of the GNU General Public License * +// * along with this program. If not, see <http://www.gnu.org/licenses/>. * +// ************************************************************************* + +use std::env; +use std::fs; +use std::io; + +use super::*; + +use crate::commands::resolve_extension; +use crate::Error; + +#[test] +fn discover_extensions() -> crate::Result<()> { + let dir1 = tempfile::tempdir()?; + let dir2 = tempfile::tempdir()?; + + { + let ext1_path = dir1.path().join("nitrocli-ext1"); + let ext2_path = dir1.path().join("nitrocli-ext2"); + let ext3_path = dir2.path().join("nitrocli-super-1337-extensions111one"); + let _ext1 = fs::File::create(&ext1_path)?; + let _ext2 = fs::File::create(&ext2_path)?; + let _ext3 = fs::File::create(&ext3_path)?; + + let path = env::join_paths(&[dir1.path(), dir2.path()]).map_err(|err| err.to_string())?; + assert_eq!( + resolve_extension(&path, ffi::OsStr::new("ext1"))?, + ext1_path + ); + assert_eq!( + resolve_extension(&path, ffi::OsStr::new("ext2"))?, + ext2_path + ); + assert_eq!( + resolve_extension(&path, ffi::OsStr::new("super-1337-extensions111one"))?, + ext3_path + ); + + match resolve_extension(&ffi::OsStr::new(""), ffi::OsStr::new("ext1")) { + Err(Error::IoError(err)) if err.kind() == io::ErrorKind::NotFound => { + let expected = io::Error::new(io::ErrorKind::NotFound, "extension nitrocli-ext1 not found"); + assert_eq!(err.to_string(), expected.to_string()) + } + r => panic!("Unexpected variant found: {:?}", r), + } + } + Ok(()) +} diff --git a/src/tests/mod.rs b/src/tests/mod.rs index e86f42f..e0ee876 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -24,6 +24,7 @@ use nitrokey_test::test as test_device; mod config; mod encrypted; +mod extensions; mod hidden; mod list; mod lock; @@ -93,6 +94,15 @@ impl Builder { self } + /// Set the `PATH` used for looking up extensions. + fn path<P>(mut self, path: P) -> Self + where + P: Into<ffi::OsString>, + { + self.0.path = Some(path.into()); + self + } + /// Set the password to use for certain operations. fn password<P>(mut self, password: P) -> Self where @@ -110,6 +120,7 @@ impl Builder { struct Nitrocli { model: Option<nitrokey::Model>, + path: Option<ffi::OsString>, admin_pin: Option<ffi::OsString>, user_pin: Option<ffi::OsString>, new_admin_pin: Option<ffi::OsString>, @@ -121,6 +132,7 @@ impl Nitrocli { pub fn new() -> Self { Self { model: None, + path: None, admin_pin: Some(nitrokey::DEFAULT_ADMIN_PIN.into()), user_pin: Some(nitrokey::DEFAULT_USER_PIN.into()), new_admin_pin: None, @@ -174,6 +186,7 @@ impl Nitrocli { let ctx = &mut crate::RunCtx { stdout: &mut stdout, stderr: &mut stderr, + path: self.path.clone(), admin_pin: self.admin_pin.clone(), user_pin: self.user_pin.clone(), new_admin_pin: self.new_admin_pin.clone(), diff --git a/src/tests/run.rs b/src/tests/run.rs index 22e7004..f8470ad 100644 --- a/src/tests/run.rs +++ b/src/tests/run.rs @@ -17,6 +17,11 @@ // * along with this program. If not, see <http://www.gnu.org/licenses/>. * // ************************************************************************* +use std::fs; +use std::io::Write; +use std::os::unix::fs::OpenOptionsExt; +use std::path; + use super::*; #[test] @@ -108,3 +113,110 @@ fn version_option() { test(&re, "--version"); test(&re, "-V"); } + +#[test] +fn extension() -> crate::Result<()> { + let ext_dir = tempfile::tempdir()?; + { + let mut ext = fs::OpenOptions::new() + .create(true) + .mode(0o755) + .write(true) + .open(ext_dir.path().join("nitrocli-ext"))?; + + ext.write_all( + br#"#!/usr/bin/env python +print("success") +"#, + )?; + } + + let path = ext_dir.path().as_os_str().to_os_string(); + let out = Nitrocli::make().path(path).build().handle(&["ext"])?; + assert_eq!(out, "success\n"); + Ok(()) +} + +#[test] +fn extension_failure() -> crate::Result<()> { + let ext_dir = tempfile::tempdir()?; + { + let mut ext = fs::OpenOptions::new() + .create(true) + .mode(0o755) + .write(true) + .open(ext_dir.path().join("nitrocli-ext"))?; + + ext.write_all( + br#"#!/usr/bin/env python +import sys +sys.exit(42); +"#, + )?; + } + + let path = ext_dir.path().as_os_str().to_os_string(); + let err = Nitrocli::make() + .path(path) + .build() + .handle(&["ext"]) + .unwrap_err(); + + match err { + crate::Error::ExtensionFailed(ext, rc) => { + assert_eq!(ext, ext_dir.path().join("nitrocli-ext")); + assert_eq!(rc, Some(42)); + } + _ => panic!("Unexpected error variant found: {:?}", err), + }; + Ok(()) +} + +#[test_device] +fn extension_arguments(model: nitrokey::Model) -> crate::Result<()> { + fn test<F>(model: nitrokey::Model, what: &str, args: &[&str], check: F) -> crate::Result<()> + where + F: FnOnce(&str) -> bool, + { + let ext_dir = tempfile::tempdir()?; + { + let mut ext = fs::OpenOptions::new() + .create(true) + .mode(0o755) + .write(true) + .open(ext_dir.path().join("nitrocli-ext"))?; + + ext.write_all(include_bytes!("extension_model_test.py"))?; + } + + let mut args = args.to_vec(); + args.append(&mut vec!["ext", what]); + + let path = ext_dir.path().as_os_str().to_os_string(); + let out = Nitrocli::make() + .model(model) + .path(path) + .build() + .handle(&args)?; + + assert!(check(&out), out); + Ok(()) + } + + test(model, "model", &[], |out| { + out == model.to_string().to_lowercase() + "\n" + })?; + test(model, "nitrocli", &[], |out| { + path::Path::new(out) + .file_stem() + .unwrap() + .to_str() + .unwrap() + .trim() + .contains("nitrocli") + })?; + test(model, "verbosity", &[], |out| out == "0\n")?; + test(model, "verbosity", &["-v"], |out| out == "1\n")?; + test(model, "verbosity", &["-v", "--verbose"], |out| out == "2\n")?; + Ok(()) +} |