aboutsummaryrefslogtreecommitdiff
path: root/src/tests
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests')
-rw-r--r--src/tests/extension_var_test.py61
-rw-r--r--src/tests/extensions.rs43
-rw-r--r--src/tests/mod.rs10
-rw-r--r--src/tests/run.rs115
4 files changed, 229 insertions, 0 deletions
diff --git a/src/tests/extension_var_test.py b/src/tests/extension_var_test.py
new file mode 100644
index 0000000..af7ec84
--- /dev/null
+++ b/src/tests/extension_var_test.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2020 The Nitrocli Developers
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from argparse import (
+ ArgumentParser,
+)
+from enum import (
+ Enum,
+)
+from os import (
+ environ,
+)
+from sys import (
+ argv,
+ exit,
+)
+
+
+class Action(Enum):
+ """An action to perform."""
+ BINARY = "binary"
+ MODEL = "model"
+ NO_CACHE = "no-cache"
+ SERIAL_NUMBERS = "serial-numbers"
+ USB_PATH = "usb-path"
+ 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:])
+ # We create a "reverse" mapping from string to variant (e.g., model ->
+ # MODEL).
+ options = {v.value: k for k, v in Action.__members__.items()}
+ try:
+ var = options[namespace.what]
+ print(environ[f"NITROCLI_{var}"])
+ except KeyError:
+ 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..a295949
--- /dev/null
+++ b/src/tests/extensions.rs
@@ -0,0 +1,43 @@
+// extensions.rs
+
+// Copyright (C) 2020 The Nitrocli Developers
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+use std::env;
+use std::fs;
+
+use super::*;
+
+#[test]
+fn resolve_extensions() -> anyhow::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()])?;
+ assert_eq!(
+ crate::commands::resolve_extension(&path, ffi::OsStr::new("ext1"))?,
+ ext1_path
+ );
+ assert_eq!(
+ crate::commands::resolve_extension(&path, ffi::OsStr::new("ext2"))?,
+ ext2_path
+ );
+ assert_eq!(
+ crate::commands::resolve_extension(&path, ffi::OsStr::new("super-1337-extensions111one"))?,
+ ext3_path
+ );
+
+ let err = crate::commands::resolve_extension(&ffi::OsStr::new(""), ffi::OsStr::new("ext1"))
+ .unwrap_err();
+ assert_eq!(err.to_string(), "Extension nitrocli-ext1 not found");
+ }
+ Ok(())
+}
diff --git a/src/tests/mod.rs b/src/tests/mod.rs
index 65983bb..5e47520 100644
--- a/src/tests/mod.rs
+++ b/src/tests/mod.rs
@@ -9,6 +9,7 @@ use nitrokey_test::test as test_device;
mod config;
mod encrypted;
+mod extensions;
mod fill;
mod hidden;
mod list;
@@ -23,6 +24,7 @@ mod unencrypted;
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>,
@@ -34,6 +36,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,
@@ -54,6 +57,12 @@ impl Nitrocli {
self
}
+ /// Set the `PATH` used for looking up extensions.
+ fn path(mut self, path: impl Into<ffi::OsString>) -> Self {
+ self.path = Some(path.into());
+ self
+ }
+
pub fn admin_pin(mut self, pin: impl Into<ffi::OsString>) -> Self {
self.admin_pin = Some(pin.into());
self
@@ -102,6 +111,7 @@ impl Nitrocli {
stdout: &mut stdout,
stderr: &mut stderr,
is_tty: false,
+ 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 33191d3..e4bbb28 100644
--- a/src/tests/run.rs
+++ b/src/tests/run.rs
@@ -5,8 +5,12 @@
use std::collections;
use std::convert;
+use std::convert::TryFrom as _;
use std::convert::TryInto as _;
+use std::fs;
+use std::io::Write;
use std::ops;
+use std::os::unix::fs::OpenOptionsExt;
use std::path;
use super::*;
@@ -277,3 +281,114 @@ fn connect_usb_path_model_wrong_serial(_model: nitrokey::Model) -> anyhow::Resul
}
Ok(())
}
+
+#[test]
+fn extension() -> anyhow::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::new().path(path).handle(&["ext"])?;
+ assert_eq!(out, "success\n");
+ Ok(())
+}
+
+#[test]
+fn extension_failure() -> anyhow::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 mut ncli = Nitrocli::new().path(path);
+
+ let err = ncli.handle(&["ext"]).unwrap_err();
+ // The extension is responsible for printing any error messages.
+ // Nitrocli is expected not to mess with them, including adding
+ // additional information.
+ if let Some(crate::DirectExitError(rc)) = err.downcast_ref::<crate::DirectExitError>() {
+ assert_eq!(*rc, 42)
+ } else {
+ panic!("encountered unexpected error: {:#}", err)
+ }
+
+ let (rc, out, err) = ncli.run(&["ext"]);
+ assert_eq!(rc, 42);
+ assert_eq!(out, b"");
+ assert_eq!(err, b"");
+ Ok(())
+}
+
+#[test_device]
+fn extension_arguments(model: nitrokey::Model) -> anyhow::Result<()> {
+ fn test<F>(model: nitrokey::Model, what: &str, args: &[&str], check: F) -> anyhow::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_var_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::new().model(model).path(path).handle(&args)?;
+
+ assert!(check(&out), out);
+ Ok(())
+ }
+
+ test(model, "binary", &[], |out| {
+ path::Path::new(out)
+ .file_stem()
+ .unwrap()
+ .to_str()
+ .unwrap()
+ .trim()
+ .contains("nitrocli")
+ })?;
+ test(model, "model", &[], |out| {
+ out == args::DeviceModel::try_from(model).unwrap().to_string() + "\n"
+ })?;
+ test(model, "no-cache", &[], |out| out == "true\n")?;
+ test(model, "serial-numbers", &[], |out| out == "\n")?;
+ test(model, "verbosity", &[], |out| out == "0\n")?;
+ test(model, "verbosity", &["-v"], |out| out == "1\n")?;
+ test(model, "verbosity", &["-v", "--verbose"], |out| out == "2\n")?;
+
+ // NITROCLI_USB_PATH should not be set, so the program errors out.
+ let _ = test(model, "usb-path", &[], |out| out == "\n").unwrap_err();
+ Ok(())
+}