mirror of
https://github.com/mgree/ffs.git
synced 2024-10-05 15:18:20 +03:00
Exit status (#44)
Unify exit status (0=success, 1=fs error, 2=cli error). Documented and tested; one test is disabled due to an upstream error masking.
This commit is contained in:
parent
747301c815
commit
b651c9c1ac
@ -4,6 +4,8 @@
|
||||
|
||||
* Handle failed mounts better, with an appropriate message and error
|
||||
code.
|
||||
* Revise exit codes: 0 means success, 1 means FS error, 2 means CLI
|
||||
error.
|
||||
|
||||
## 0.1.1 - 2021-07-15
|
||||
|
||||
|
@ -20,7 +20,7 @@ _ffs() {
|
||||
|
||||
case "${cmd}" in
|
||||
ffs)
|
||||
opts=" -q -d -i -h -V -u -g -o -s -t -m --quiet --debug --exact --unpadded --readonly --no-output --in-place --help --version --completions --uid --gid --mode --dirmode --output --source --target --mount <INPUT> "
|
||||
opts=" -q -d -i -h -V -u -g -o -s -t -m --quiet --debug --exact --no-xattr --keep-macos-xattr --unpadded --readonly --no-output --in-place --pretty --help --version --completions --uid --gid --mode --dirmode --munge --output --source --target --mount --new <INPUT> "
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
@ -55,6 +55,10 @@ _ffs() {
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
--munge)
|
||||
COMPREPLY=($(compgen -W "filter rename" -- "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
--output)
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
@ -87,6 +91,10 @@ _ffs() {
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
--new)
|
||||
COMPREPLY=($(compgen -f "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
|
@ -1,18 +1,23 @@
|
||||
complete -c ffs -n "__fish_use_subcommand" -l completions -d 'Generate shell completions and exit' -r -f -a "bash fish zsh"
|
||||
complete -c ffs -n "__fish_use_subcommand" -l completions -d 'Generate shell completions (and exits)' -r -f -a "bash fish zsh"
|
||||
complete -c ffs -n "__fish_use_subcommand" -s u -l uid -d 'Sets the user id of the generated filesystem (defaults to current effective user id)'
|
||||
complete -c ffs -n "__fish_use_subcommand" -s g -l gid -d 'Sets the group id of the generated filesystem (defaults to current effective group id)'
|
||||
complete -c ffs -n "__fish_use_subcommand" -l mode -d 'Sets the default mode of files (parsed as octal; defaults to 644; if unspecified, directories will have this mode with execute bits set when read bits are set)'
|
||||
complete -c ffs -n "__fish_use_subcommand" -l dirmode -d 'Sets the default mode of directories (parsed as octal; defaults to 755; )'
|
||||
complete -c ffs -n "__fish_use_subcommand" -l mode -d 'Sets the default mode of files (parsed as octal)'
|
||||
complete -c ffs -n "__fish_use_subcommand" -l dirmode -d 'Sets the default mode of directories (parsed as octal; if unspecified, directories will have FILEMODE with execute bits set when read bits are set)'
|
||||
complete -c ffs -n "__fish_use_subcommand" -l munge -d 'Set the name munging policy; applies to \'.\', \'..\', and files with NUL and \'/\' in them' -r -f -a "filter rename"
|
||||
complete -c ffs -n "__fish_use_subcommand" -s o -l output -d 'Sets the output file for saving changes (defaults to stdout)'
|
||||
complete -c ffs -n "__fish_use_subcommand" -s s -l source -d 'Specify the source format explicitly (by default, automatically inferred from filename extension)' -r -f -a "json toml yaml"
|
||||
complete -c ffs -n "__fish_use_subcommand" -s t -l target -d 'Specify the target format explicitly (by default, automatically inferred from filename extension)' -r -f -a "json toml yaml"
|
||||
complete -c ffs -n "__fish_use_subcommand" -s m -l mount -d 'Sets the mountpoint; will be inferred when using a file, but must be specified when running on stdin'
|
||||
complete -c ffs -n "__fish_use_subcommand" -l new -d 'Mounts an empty filesystem, inferring a mountpoint and output format'
|
||||
complete -c ffs -n "__fish_use_subcommand" -s q -l quiet -d 'Quiet mode (turns off all errors and warnings, enables `--no-output`)'
|
||||
complete -c ffs -n "__fish_use_subcommand" -s d -l debug -d 'Give debug output on stderr'
|
||||
complete -c ffs -n "__fish_use_subcommand" -l exact -d 'Don\'t add newlines to the end of values that don\'t already have them (or strip them when loading)'
|
||||
complete -c ffs -n "__fish_use_subcommand" -l no-xattr -d 'Don\'t use extended attributes to track metadata (see `man xattr`)'
|
||||
complete -c ffs -n "__fish_use_subcommand" -l keep-macos-xattr -d 'Include ._* extended attribute/resource fork files on macOS'
|
||||
complete -c ffs -n "__fish_use_subcommand" -l unpadded -d 'Don\'t pad the numeric names of list elements with zeroes; will not sort properly'
|
||||
complete -c ffs -n "__fish_use_subcommand" -l readonly -d 'Mounted filesystem will be readonly'
|
||||
complete -c ffs -n "__fish_use_subcommand" -l no-output -d 'Disables output of filesystem (normally on stdout)'
|
||||
complete -c ffs -n "__fish_use_subcommand" -s i -l in-place -d 'Writes the output back over the input file'
|
||||
complete -c ffs -n "__fish_use_subcommand" -l pretty -d 'Pretty-print output (may increase size)'
|
||||
complete -c ffs -n "__fish_use_subcommand" -s h -l help -d 'Prints help information'
|
||||
complete -c ffs -n "__fish_use_subcommand" -s V -l version -d 'Prints version information'
|
||||
|
@ -15,13 +15,14 @@ _ffs() {
|
||||
|
||||
local context curcontext="$curcontext" state line
|
||||
_arguments "${_arguments_options[@]}" \
|
||||
'--completions=[Generate shell completions and exit]: :(bash fish zsh)' \
|
||||
'--completions=[Generate shell completions (and exits)]: :(bash fish zsh)' \
|
||||
'-u+[Sets the user id of the generated filesystem (defaults to current effective user id)]' \
|
||||
'--uid=[Sets the user id of the generated filesystem (defaults to current effective user id)]' \
|
||||
'-g+[Sets the group id of the generated filesystem (defaults to current effective group id)]' \
|
||||
'--gid=[Sets the group id of the generated filesystem (defaults to current effective group id)]' \
|
||||
'--mode=[Sets the default mode of files (parsed as octal; defaults to 644; if unspecified, directories will have this mode with execute bits set when read bits are set)]' \
|
||||
'--dirmode=[Sets the default mode of directories (parsed as octal; defaults to 755; )]' \
|
||||
'--mode=[Sets the default mode of files (parsed as octal)]' \
|
||||
'--dirmode=[Sets the default mode of directories (parsed as octal; if unspecified, directories will have FILEMODE with execute bits set when read bits are set)]' \
|
||||
'--munge=[Set the name munging policy; applies to '\''.'\'', '\''..'\'', and files with NUL and '\''/'\'' in them]: :(filter rename)' \
|
||||
'-o+[Sets the output file for saving changes (defaults to stdout)]' \
|
||||
'--output=[Sets the output file for saving changes (defaults to stdout)]' \
|
||||
'-s+[Specify the source format explicitly (by default, automatically inferred from filename extension)]: :(json toml yaml)' \
|
||||
@ -30,21 +31,25 @@ _ffs() {
|
||||
'--target=[Specify the target format explicitly (by default, automatically inferred from filename extension)]: :(json toml yaml)' \
|
||||
'-m+[Sets the mountpoint; will be inferred when using a file, but must be specified when running on stdin]' \
|
||||
'--mount=[Sets the mountpoint; will be inferred when using a file, but must be specified when running on stdin]' \
|
||||
'(-i --in-place -s --source -o --output)--new=[Mounts an empty filesystem, inferring a mountpoint and output format]' \
|
||||
'-q[Quiet mode (turns off all errors and warnings, enables `--no-output`)]' \
|
||||
'--quiet[Quiet mode (turns off all errors and warnings, enables `--no-output`)]' \
|
||||
'-d[Give debug output on stderr]' \
|
||||
'--debug[Give debug output on stderr]' \
|
||||
'--exact[Don'\''t add newlines to the end of values that don'\''t already have them (or strip them when loading)]' \
|
||||
'--no-xattr[Don'\''t use extended attributes to track metadata (see `man xattr`)]' \
|
||||
'--keep-macos-xattr[Include ._* extended attribute/resource fork files on macOS]' \
|
||||
'--unpadded[Don'\''t pad the numeric names of list elements with zeroes; will not sort properly]' \
|
||||
'--readonly[Mounted filesystem will be readonly]' \
|
||||
'--no-output[Disables output of filesystem (normally on stdout)]' \
|
||||
'-i[Writes the output back over the input file]' \
|
||||
'--in-place[Writes the output back over the input file]' \
|
||||
'(--no-output -q --quiet)--pretty[Pretty-print output (may increase size)]' \
|
||||
'-h[Prints help information]' \
|
||||
'--help[Prints help information]' \
|
||||
'-V[Prints version information]' \
|
||||
'--version[Prints version information]' \
|
||||
'::INPUT -- Sets the input file (defaults to '-', meaning STDIN):_files' \
|
||||
'::INPUT -- Sets the input file ('-' means STDIN):_files' \
|
||||
&& ret=0
|
||||
|
||||
}
|
||||
|
@ -238,6 +238,20 @@ RUST_LOG
|
||||
*ffs=warn*. Setting *-q* turns off all output; setting *-d* sets
|
||||
*ffs=debug*.
|
||||
|
||||
# EXIT STATUS
|
||||
|
||||
0
|
||||
|
||||
: Successfully unmounted.
|
||||
|
||||
1
|
||||
|
||||
: A FUSE or other filesystem error occurred.
|
||||
|
||||
2
|
||||
|
||||
: Command-line argument parsing error.
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
The general workflow is to run *ffs*, do some work, and then unmount
|
||||
|
10
man/ffs.1
10
man/ffs.1
@ -233,6 +233,16 @@ probably be \f[I]ffs\f[R] and \f[I]level\f[R] should be one of
|
||||
The default is \f[I]ffs=warn\f[R].
|
||||
Setting \f[I]-q\f[R] turns off all output; setting \f[I]-d\f[R] sets
|
||||
\f[I]ffs=debug\f[R].
|
||||
.SH EXIT STATUS
|
||||
.TP
|
||||
0
|
||||
Successfully unmounted.
|
||||
.TP
|
||||
1
|
||||
A FUSE or other filesystem error occurred.
|
||||
.TP
|
||||
2
|
||||
Command-line argument parsing error.
|
||||
.SH EXAMPLES
|
||||
.PP
|
||||
The general workflow is to run \f[I]ffs\f[R], do some work, and then
|
||||
|
@ -12,6 +12,9 @@ use super::format::Format;
|
||||
|
||||
use super::cli;
|
||||
|
||||
pub const ERROR_STATUS_FUSE: i32 = 1;
|
||||
pub const ERROR_STATUS_CLI: i32 = 2;
|
||||
|
||||
/// Configuration information
|
||||
///
|
||||
/// See `cli.rs` for information on the actual command-line options; see
|
||||
@ -100,7 +103,12 @@ impl FromStr for Munge {
|
||||
impl Config {
|
||||
/// Parses arguments from `std::env::Args`, via `cli::app().get_matches()`
|
||||
pub fn from_args() -> Self {
|
||||
let args = cli::app().get_matches();
|
||||
let args = cli::app()
|
||||
.get_matches_safe()
|
||||
.unwrap_or_else(|e| {
|
||||
eprintln!("{}", e.message);
|
||||
std::process::exit(ERROR_STATUS_CLI)
|
||||
});
|
||||
|
||||
let mut config = Config::default();
|
||||
// generate completions?
|
||||
@ -115,7 +123,7 @@ impl Config {
|
||||
clap::Shell::Zsh
|
||||
} else {
|
||||
eprintln!("Can't generate completions for '{}'.", shell);
|
||||
std::process::exit(1);
|
||||
std::process::exit(ERROR_STATUS_CLI);
|
||||
};
|
||||
cli::app().gen_completions_to("ffs", shell, &mut std::io::stdout());
|
||||
std::process::exit(0);
|
||||
@ -166,7 +174,7 @@ impl Config {
|
||||
args.value_of("FILEMODE").unwrap(),
|
||||
e
|
||||
);
|
||||
std::process::exit(1)
|
||||
std::process::exit(ERROR_STATUS_CLI)
|
||||
}
|
||||
};
|
||||
if args.occurrences_of("FILEMODE") > 0 && args.occurrences_of("DIRMODE") == 0 {
|
||||
@ -190,7 +198,7 @@ impl Config {
|
||||
args.value_of("DIRMODE").unwrap(),
|
||||
e
|
||||
);
|
||||
std::process::exit(1)
|
||||
std::process::exit(ERROR_STATUS_CLI)
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -234,12 +242,12 @@ impl Config {
|
||||
|
||||
if args.occurrences_of("INPUT") != 0 {
|
||||
error!("It doesn't make sense to set `--new` with a specified input file.");
|
||||
std::process::exit(1);
|
||||
std::process::exit(ERROR_STATUS_CLI);
|
||||
}
|
||||
let output = PathBuf::from(target_file);
|
||||
if output.exists() {
|
||||
error!("Output file {} already exists.", output.display());
|
||||
std::process::exit(1);
|
||||
std::process::exit(ERROR_STATUS_FUSE);
|
||||
}
|
||||
let format = match args
|
||||
.value_of("TARGET_FORMAT")
|
||||
@ -272,7 +280,7 @@ impl Config {
|
||||
"Unrecognized format '{}'; use --target or a known extension to specify a format.",
|
||||
output.display()
|
||||
);
|
||||
std::process::exit(1);
|
||||
std::process::exit(ERROR_STATUS_CLI);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -282,19 +290,23 @@ impl Config {
|
||||
let mount_point = PathBuf::from(mount_point);
|
||||
if !mount_point.exists() {
|
||||
error!("Mount point {} does not exist.", mount_point.display());
|
||||
std::process::exit(1);
|
||||
std::process::exit(ERROR_STATUS_FUSE);
|
||||
}
|
||||
config.cleanup_mount = false;
|
||||
Some(mount_point)
|
||||
}
|
||||
None => {
|
||||
// If the output is to a file foo.EXT, then try to make a directory foo.
|
||||
let mount_dir = output.with_extension("");
|
||||
let stem = output.file_stem().unwrap_or_else(|| {
|
||||
error!("Couldn't infer the mountpoint from output '{}'. Use `--mount MOUNT` to specify a mountpoint.", output.display());
|
||||
std::process::exit(ERROR_STATUS_FUSE);
|
||||
});
|
||||
let mount_dir = PathBuf::from(stem);
|
||||
// If that file already exists, give up and tell the user about --mount.
|
||||
if mount_dir.exists() {
|
||||
error!("Inferred mountpoint '{mount}' for output file '{file}', but '{mount}' already exists. Use `--mount MOUNT` to specify a mountpoint.",
|
||||
mount = mount_dir.display(), file = output.display());
|
||||
std::process::exit(1);
|
||||
std::process::exit(ERROR_STATUS_FUSE);
|
||||
}
|
||||
// If the mountpoint can't be created, give up and tell the user about --mount.
|
||||
if let Err(e) = std::fs::create_dir(&mount_dir) {
|
||||
@ -302,7 +314,7 @@ impl Config {
|
||||
mount_dir.display(),
|
||||
e
|
||||
);
|
||||
std::process::exit(1);
|
||||
std::process::exit(ERROR_STATUS_FUSE);
|
||||
}
|
||||
// We did it!
|
||||
config.cleanup_mount = true;
|
||||
@ -327,7 +339,7 @@ impl Config {
|
||||
let input_source = PathBuf::from(input_source);
|
||||
if !input_source.exists() {
|
||||
error!("Input file {} does not exist.", input_source.display());
|
||||
std::process::exit(1);
|
||||
std::process::exit(ERROR_STATUS_FUSE);
|
||||
}
|
||||
Input::File(input_source)
|
||||
}
|
||||
@ -366,7 +378,7 @@ impl Config {
|
||||
let mount_point = PathBuf::from(mount_point);
|
||||
if !mount_point.exists() {
|
||||
error!("Mount point {} does not exist.", mount_point.display());
|
||||
std::process::exit(1);
|
||||
std::process::exit(ERROR_STATUS_FUSE);
|
||||
}
|
||||
config.cleanup_mount = false;
|
||||
Some(mount_point)
|
||||
@ -375,22 +387,28 @@ impl Config {
|
||||
match &config.input {
|
||||
Input::Stdin => {
|
||||
error!("You must specify a mount point when reading from stdin.");
|
||||
std::process::exit(1);
|
||||
std::process::exit(ERROR_STATUS_CLI);
|
||||
}
|
||||
Input::Empty => {
|
||||
error!(
|
||||
"You must specify a mount point when reading an empty file."
|
||||
);
|
||||
std::process::exit(1);
|
||||
std::process::exit(ERROR_STATUS_CLI);
|
||||
}
|
||||
Input::File(file) => {
|
||||
// If the input is from a file foo.EXT, then try to make a directory foo.
|
||||
let mount_dir = file.with_extension("");
|
||||
let stem = file.file_stem().unwrap_or_else(|| {
|
||||
error!("Couldn't infer the mountpoint from input '{}'. Use `--mount MOUNT` to specify a mountpoint.", file.display());
|
||||
std::process::exit(ERROR_STATUS_FUSE);
|
||||
});
|
||||
let mount_dir = PathBuf::from(stem);
|
||||
debug!("inferred mount_dir {}", mount_dir.display());
|
||||
|
||||
// If that file already exists, give up and tell the user about --mount.
|
||||
if mount_dir.exists() {
|
||||
error!("Inferred mountpoint '{mount}' for input file '{file}', but '{mount}' already exists. Use `--mount MOUNT` to specify a mountpoint.",
|
||||
mount = mount_dir.display(), file = file.display());
|
||||
std::process::exit(1);
|
||||
std::process::exit(ERROR_STATUS_FUSE);
|
||||
}
|
||||
// If the mountpoint can't be created, give up and tell the user about --mount.
|
||||
if let Err(e) = std::fs::create_dir(&mount_dir) {
|
||||
@ -399,7 +417,7 @@ impl Config {
|
||||
mount_dir.display(),
|
||||
e
|
||||
);
|
||||
std::process::exit(1);
|
||||
std::process::exit(ERROR_STATUS_FUSE);
|
||||
}
|
||||
// We did it!
|
||||
config.cleanup_mount = true;
|
||||
|
@ -6,7 +6,7 @@ use tracing::{debug, error, info, instrument, warn};
|
||||
|
||||
use fuser::FileType;
|
||||
|
||||
use super::config::{Config, Input, Munge, Output};
|
||||
use super::config::{ERROR_STATUS_FUSE, Config, Input, Munge, Output};
|
||||
use super::fs::{DirEntry, DirType, Entry, Inode, FS};
|
||||
|
||||
use ::toml as serde_toml;
|
||||
@ -141,7 +141,7 @@ impl Format {
|
||||
let fmt = config.input_format;
|
||||
let file = std::fs::File::open(&file).unwrap_or_else(|e| {
|
||||
error!("Unable to open {} for {} input: {}", file.display(), fmt, e);
|
||||
std::process::exit(1);
|
||||
std::process::exit(ERROR_STATUS_FUSE);
|
||||
});
|
||||
Box::new(file)
|
||||
}
|
||||
@ -311,7 +311,7 @@ where
|
||||
|
||||
if v.kind() != FileType::Directory {
|
||||
error!("The root of the filesystem must be a directory, but '{}' only generates a single file.", v);
|
||||
std::process::exit(1);
|
||||
std::process::exit(ERROR_STATUS_FUSE);
|
||||
}
|
||||
|
||||
let mut filtered = 0;
|
||||
|
@ -5,7 +5,7 @@ mod config;
|
||||
mod format;
|
||||
mod fs;
|
||||
|
||||
use config::Config;
|
||||
use config::{Config, ERROR_STATUS_CLI, ERROR_STATUS_FUSE};
|
||||
|
||||
use fuser::MountOption;
|
||||
|
||||
@ -26,7 +26,7 @@ fn main() {
|
||||
error!(
|
||||
"No mount point specified; aborting. Use `--mount MOUNT` to specify a mountpoint."
|
||||
);
|
||||
std::process::exit(1);
|
||||
std::process::exit(ERROR_STATUS_CLI);
|
||||
}
|
||||
};
|
||||
let cleanup_mount = config.cleanup_mount;
|
||||
@ -41,7 +41,7 @@ fn main() {
|
||||
}
|
||||
Err(e) => {
|
||||
error!("I/O error: {}", e);
|
||||
2
|
||||
ERROR_STATUS_FUSE
|
||||
}
|
||||
};
|
||||
|
||||
|
178
tests/exit_status.sh
Executable file
178
tests/exit_status.sh
Executable file
@ -0,0 +1,178 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# from https://github.com/mgree/ffs/issues/42
|
||||
|
||||
fail() {
|
||||
echo FAILED: $1
|
||||
if [ "$MNT" ]
|
||||
then
|
||||
umount "$D"/single
|
||||
rm -r "$D"
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
TESTS="$(pwd)"
|
||||
|
||||
D=$(mktemp -d)
|
||||
|
||||
cp ../json/single.json "$D"/single.json
|
||||
cp ../json/false.json "$D"/false.json
|
||||
|
||||
cd "$D"
|
||||
|
||||
cp single.json unreadable.json
|
||||
chmod -r unreadable.json
|
||||
|
||||
mkdir unwriteable
|
||||
chmod -w unwriteable
|
||||
|
||||
# in place mount, mountpoint exists
|
||||
mkdir single
|
||||
"$TESTS"/timeout -t 2 ffs -i single.json 2>single.err
|
||||
[ $? -eq 1 ] || fail imountstatus
|
||||
[ -s single.err ] || fail imountmsg
|
||||
rmdir single
|
||||
rm single.err
|
||||
|
||||
# in place, can't make mountpoint
|
||||
cd unwriteable
|
||||
"$TESTS"/timeout -t 2 ffs -i single.json 2>../single.err
|
||||
[ $? -eq 1 ] || fail imkmountstatus
|
||||
cd ..
|
||||
[ -s single.err ] || fail imkmountmsg
|
||||
rm single.err
|
||||
|
||||
# new, mountpoint exists
|
||||
mkdir foo
|
||||
"$TESTS"/timeout -t 2 ffs --new foo.json 2>foo.err
|
||||
[ $? -eq 1 ] || fail newmountstatus
|
||||
[ -s foo.err ] || fail newmountmsg
|
||||
rmdir foo
|
||||
rm foo.err
|
||||
|
||||
# new, can't make mountpoint
|
||||
cd unwriteable
|
||||
"$TESTS"/timeout -t 2 ffs --new foo.json 2>../foo.err
|
||||
[ $? -eq 1 ] || fail newmkmountstatus
|
||||
cd ..
|
||||
[ -s foo.err ] || fail newmkmountmsg
|
||||
rm foo.err
|
||||
|
||||
# input file, can't infer mountpoint
|
||||
"$TESTS"/timeout -t 2 ffs --new .. 2>dotdot.err
|
||||
[ $? -eq 1 ] || fail newdotdotmountstatus
|
||||
[ -s dotdot.err ] || fail newdotdotmountmsg
|
||||
rm dotdot.err
|
||||
|
||||
# --new, output file exists
|
||||
touch foo.yaml
|
||||
"$TESTS"/timeout -t 2 ffs --new foo.yaml 2>foo.err
|
||||
[ $? -eq 1 ] || fail omountstatus
|
||||
[ -s foo.err ] || fail omountmsg
|
||||
rm foo.yaml
|
||||
rm foo.err
|
||||
|
||||
# --new, mountpoint doesn't exist
|
||||
"$TESTS"/timeout -t 2 ffs -m notthere --new foo.json 2>foo.err
|
||||
[ $? -eq 1 ] || fail mmountstatus1
|
||||
[ -s foo.err ] || fail mmountmsg1
|
||||
rm foo.err
|
||||
|
||||
# mountpoint doesn't exist
|
||||
"$TESTS"/timeout -t 2 ffs -m notthere single.json 2>single.err
|
||||
[ $? -eq 1 ] || fail mmountstatus2
|
||||
[ -s single.err ] || fail mmountmsg2
|
||||
rm single.err
|
||||
|
||||
# input file doesn't exists
|
||||
"$TESTS"/timeout -t 2 ffs nonesuch.toml 2>nonesuch.err
|
||||
[ $? -eq 1 ] || fail inputmountstatus1
|
||||
[ -s nonesuch.err ] || fail inputmountmsg1
|
||||
rm nonesuch.err
|
||||
|
||||
# input file, mountpoint exists
|
||||
mkdir single
|
||||
"$TESTS"/timeout -t 2 ffs single.json 2>single.err
|
||||
[ $? -eq 1 ] || fail inputmountstatus2
|
||||
[ -s single.err ] || fail inputmountmsg2
|
||||
rmdir single
|
||||
rm single.err
|
||||
|
||||
# input file, can't make mount point
|
||||
cd unwriteable
|
||||
"$TESTS"/timeout -t 2 ffs ../single.json 2>../single.err
|
||||
[ $? -eq 1 ] || fail inputmkmountstatus
|
||||
cd ..
|
||||
[ -s single.err ] || fail inputmkmountmsg
|
||||
rm single.err
|
||||
|
||||
# input file, can't infer mountpoint
|
||||
"$TESTS"/timeout -t 2 ffs .. 2>dotdot.err
|
||||
[ $? -eq 1 ] || fail inputdotdotmountstatus
|
||||
[ -s dotdot.err ] || fail inputdotdotmountmsg
|
||||
rm dotdot.err
|
||||
|
||||
# unreadable input
|
||||
"$TESTS"/timeout -t 2 ffs unreadable.json 2>ur.err
|
||||
[ $? -eq 1 ] || fail unreadablemountstatus
|
||||
[ -s ur.err ] || fail unreadablemountmsg
|
||||
rm ur.err
|
||||
|
||||
# plain value input
|
||||
"$TESTS"/timeout -t 2 ffs false.json 2>false.err
|
||||
[ $? -eq 1 ] || fail falsemountstatus
|
||||
[ -s false.err ] || fail falsemountmsg
|
||||
rm false.err
|
||||
|
||||
# bad mount point (fuser is masking this error)
|
||||
# "$TESTS"/timeout -t 2 ffs /etc single.json 2>etc.err
|
||||
# [ $? -eq 1 ] || fail etcmountstatus
|
||||
# [ -s etc.err ] || fail etcmountmsg
|
||||
# rm etc.err
|
||||
|
||||
# bad shell completion
|
||||
"$TESTS"/timeout -t 2 ffs --completions smoosh 2>comp.err
|
||||
[ $? -eq 2 ] || fail compmountstatus
|
||||
[ -s comp.err ] || fail compmountmsg
|
||||
rm comp.err
|
||||
|
||||
# bad mode
|
||||
"$TESTS"/timeout -t 2 ffs --mode 888 2>mode.err
|
||||
[ $? -eq 2 ] || fail modemountstatus
|
||||
[ -s mode.err ] || fail modemountmsg
|
||||
rm mode.err
|
||||
|
||||
# bad dirmode
|
||||
"$TESTS"/timeout -t 2 ffs --dirmode 888 2>dirmode.err
|
||||
[ $? -eq 2 ] || fail dirmodemountstatus
|
||||
[ -s dirmode.err ] || fail dirmodemountmsg
|
||||
rm dirmode.err
|
||||
|
||||
# new and input file
|
||||
"$TESTS"/timeout -t 2 ffs --new foo.json single.json 2>ni.err
|
||||
[ $? -eq 2 ] || fail nimountstatus
|
||||
[ -s ni.err ] || fail nimountmsg
|
||||
rm ni.err
|
||||
|
||||
# unknown --source
|
||||
"$TESTS"/timeout -t 2 ffs --source hieratic single.json 2>source.err
|
||||
[ $? -eq 2 ] || fail sourcemountstatus
|
||||
[ -s source.err ] || fail sourcemountmsg
|
||||
rm source.err
|
||||
|
||||
# unknown --target
|
||||
"$TESTS"/timeout -t 2 ffs --target hieratic single.json 2>target.err
|
||||
[ $? -eq 2 ] || fail targetmountstatus
|
||||
[ -s target.err ] || fail targetmountmsg
|
||||
rm target.err
|
||||
|
||||
# stdin read, no mountpoint
|
||||
"$TESTS"/timeout -t 2 ffs 2>im.err
|
||||
[ $? -eq 2 ] || fail immountstatus
|
||||
[ -s im.err ] || fail immountmsg
|
||||
rm im.err
|
||||
|
||||
chmod +w unwriteable
|
||||
cd "$TESTS"
|
||||
rm -r "$D" || fail cleanup
|
39
tests/infer_mount_relative.sh
Executable file
39
tests/infer_mount_relative.sh
Executable file
@ -0,0 +1,39 @@
|
||||
#!/bin/sh
|
||||
|
||||
fail() {
|
||||
echo FAILED: $1
|
||||
if [ "$MNT" ]
|
||||
then
|
||||
cd
|
||||
umount "$TMP"/object
|
||||
rm -r "$TMP"
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
TMP=$(mktemp -d)
|
||||
|
||||
cp ../json/object.json "$TMP"
|
||||
mkdir "$TMP"/nested
|
||||
cd "$TMP"/nested
|
||||
|
||||
ffs ../object.json &
|
||||
PID=$!
|
||||
sleep 2
|
||||
[ -d "object" ] || fail mountdir
|
||||
case $(ls object) in
|
||||
(eyes*fingernails*human*name) ;;
|
||||
(*) fail ls;;
|
||||
esac
|
||||
[ "$(cat object/name)" = "Michael Greenberg" ] || fail name
|
||||
[ "$(cat object/eyes)" -eq 2 ] || fail eyes
|
||||
[ "$(cat object/fingernails)" -eq 10 ] || fail fingernails
|
||||
[ "$(cat object/human)" = "true" ] || fail human
|
||||
umount object || fail unmount
|
||||
sleep 1
|
||||
|
||||
kill -0 $PID >/dev/null 2>&1 && fail process
|
||||
|
||||
[ -d "object" ] && fail cleanup
|
||||
cd -
|
||||
rm -r "$TMP"
|
15
tests/new.sh
15
tests/new.sh
@ -5,22 +5,21 @@ fail() {
|
||||
if [ "$MNT" ]
|
||||
then
|
||||
umount "$MNT"
|
||||
rmdir "$MNT"
|
||||
rm "$OUT" "$EXP"
|
||||
rm -r "$D"
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
# really, just for the name
|
||||
OUT=$(mktemp)
|
||||
rm "$OUT"
|
||||
MNT="$OUT"
|
||||
OUT="$OUT".json
|
||||
D=$(mktemp -d)
|
||||
|
||||
MNT=foo
|
||||
OUT=foo.json
|
||||
|
||||
EXP=$(mktemp)
|
||||
|
||||
printf '{"handles":{"github":"mgree","stevens":"mgreenbe","twitter":"mgrnbrg"},"problems":99}' >"$EXP"
|
||||
|
||||
cd "$D"
|
||||
ffs --new "$OUT" &
|
||||
PID=$!
|
||||
sleep 2
|
||||
@ -40,4 +39,4 @@ kill -0 $PID >/dev/null 2>&1 && fail process
|
||||
diff "$OUT" "$EXP" || fail diff
|
||||
|
||||
[ -e "$MNT" ] && fail mount
|
||||
rm "$OUT" "$EXP"
|
||||
rm -r "$D"
|
||||
|
@ -40,7 +40,7 @@ NESTEDSTATUS=$?
|
||||
[ -f single.timeout ] && fail timeout
|
||||
[ -s single.err ] || fail error
|
||||
rm single.err
|
||||
[ $NESTEDSTATUS -eq 2 ] || fail status
|
||||
[ $NESTEDSTATUS -eq 1 ] || fail status
|
||||
|
||||
case $(ls) in
|
||||
(onlyone*single.json) ;;
|
||||
|
Loading…
Reference in New Issue
Block a user