summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2022-10-19 10:29:05 -0400
committerTavian Barnes <tavianator@tavianator.com>2022-10-19 11:50:03 -0400
commit3b387d81e63893ed3fe3b45e3721fbcfb1c5dde0 (patch)
tree513c32eda43d92a8ed977f394492ba198bba1f3b
parente5972621ffa8864b18d3e303ac714fdbe231be74 (diff)
downloadbfs-3b387d81e63893ed3fe3b45e3721fbcfb1c5dde0.tar.xz
tests: Split test cases into separate files
-rw-r--r--docs/BUILDING.md15
-rw-r--r--docs/HACKING.md20
-rw-r--r--docs/USAGE.md14
-rw-r--r--tests/bfs/D_all.out (renamed from tests/test_D_all.out)0
-rw-r--r--tests/bfs/D_all.sh1
-rw-r--r--tests/bfs/D_multi.out (renamed from tests/test_D_multi.out)0
-rw-r--r--tests/bfs/D_multi.sh1
-rw-r--r--tests/bfs/L_capable.out (renamed from tests/test_L_capable.out)0
-rw-r--r--tests/bfs/L_capable.sh12
-rw-r--r--tests/bfs/L_unique.out (renamed from tests/test_L_unique.out)0
-rw-r--r--tests/bfs/L_unique.sh1
-rw-r--r--tests/bfs/L_unique_depth.out (renamed from tests/test_L_unique_depth.out)0
-rw-r--r--tests/bfs/L_unique_depth.sh1
-rw-r--r--tests/bfs/L_unique_loops.out (renamed from tests/test_L_unique_loops.out)0
-rw-r--r--tests/bfs/L_unique_loops.sh1
-rw-r--r--tests/bfs/O0.out (renamed from tests/test_O0.out)0
-rw-r--r--tests/bfs/O0.sh1
-rw-r--r--tests/bfs/O1.out (renamed from tests/test_O1.out)0
-rw-r--r--tests/bfs/O1.sh1
-rw-r--r--tests/bfs/O2.out (renamed from tests/test_O2.out)0
-rw-r--r--tests/bfs/O2.sh1
-rw-r--r--tests/bfs/O3.out (renamed from tests/test_O3.out)0
-rw-r--r--tests/bfs/O3.sh1
-rw-r--r--tests/bfs/Ofast.out (renamed from tests/test_Ofast.out)0
-rw-r--r--tests/bfs/Ofast.sh1
-rw-r--r--tests/bfs/S_bfs.out (renamed from tests/test_S_bfs.out)0
-rw-r--r--tests/bfs/S_bfs.sh2
-rw-r--r--tests/bfs/S_dfs.out (renamed from tests/test_S_dfs.out)0
-rw-r--r--tests/bfs/S_dfs.sh2
-rw-r--r--tests/bfs/S_ids.out (renamed from tests/test_S_ids.out)0
-rw-r--r--tests/bfs/S_ids.sh2
-rw-r--r--tests/bfs/and_incomplete.sh1
-rw-r--r--tests/bfs/capable.out (renamed from tests/test_capable.out)0
-rw-r--r--tests/bfs/capable.sh12
-rw-r--r--tests/bfs/color.out (renamed from tests/test_color.out)0
-rw-r--r--tests/bfs/color.sh1
-rw-r--r--tests/bfs/color_L.out (renamed from tests/test_color_L.out)0
-rw-r--r--tests/bfs/color_L.sh1
-rw-r--r--tests/bfs/color_L_ln_target.out (renamed from tests/test_color_L_ln_target.out)0
-rw-r--r--tests/bfs/color_L_ln_target.sh1
-rw-r--r--tests/bfs/color_L_no_stat.out (renamed from tests/test_color_L_no_stat.out)0
-rw-r--r--tests/bfs/color_L_no_stat.sh1
-rw-r--r--tests/bfs/color_escapes.out (renamed from tests/test_color_escapes.out)0
-rw-r--r--tests/bfs/color_escapes.sh1
-rw-r--r--tests/bfs/color_ext.out (renamed from tests/test_color_ext.out)0
-rw-r--r--tests/bfs/color_ext.sh1
-rw-r--r--tests/bfs/color_ext0.out (renamed from tests/test_color_ext0.out)0
-rw-r--r--tests/bfs/color_ext0.sh1
-rw-r--r--tests/bfs/color_ext_override.out (renamed from tests/test_color_ext_override.out)0
-rw-r--r--tests/bfs/color_ext_override.sh1
-rw-r--r--tests/bfs/color_ext_underride.out (renamed from tests/test_color_ext_underride.out)0
-rw-r--r--tests/bfs/color_ext_underride.sh1
-rw-r--r--tests/bfs/color_ln_target.out (renamed from tests/test_color_ln_target.out)0
-rw-r--r--tests/bfs/color_ln_target.sh1
-rw-r--r--tests/bfs/color_ls.out (renamed from tests/test_color_ls.out)0
-rw-r--r--tests/bfs/color_ls.sh15
-rw-r--r--tests/bfs/color_mh.out (renamed from tests/test_color_mh.out)0
-rw-r--r--tests/bfs/color_mh.sh1
-rw-r--r--tests/bfs/color_mh0.out (renamed from tests/test_color_mh0.out)0
-rw-r--r--tests/bfs/color_mh0.sh1
-rw-r--r--tests/bfs/color_mi.out (renamed from tests/test_color_mi.out)0
-rw-r--r--tests/bfs/color_mi.sh1
-rw-r--r--tests/bfs/color_missing_colon.out (renamed from tests/test_color_missing_colon.out)0
-rw-r--r--tests/bfs/color_missing_colon.sh1
-rw-r--r--tests/bfs/color_no_stat.out (renamed from tests/test_color_no_stat.out)0
-rw-r--r--tests/bfs/color_no_stat.sh1
-rw-r--r--tests/bfs/color_nul.out (renamed from tests/test_color_nul.out)bin20 -> 20 bytes
-rw-r--r--tests/bfs/color_nul.sh2
-rw-r--r--tests/bfs/color_or.out (renamed from tests/test_color_or.out)0
-rw-r--r--tests/bfs/color_or.sh1
-rw-r--r--tests/bfs/color_or0_mi.out (renamed from tests/test_color_or0_mi.out)0
-rw-r--r--tests/bfs/color_or0_mi.sh1
-rw-r--r--tests/bfs/color_or0_mi0.out (renamed from tests/test_color_or0_mi0.out)0
-rw-r--r--tests/bfs/color_or0_mi0.sh1
-rw-r--r--tests/bfs/color_or_mi.out (renamed from tests/test_color_or_mi.out)0
-rw-r--r--tests/bfs/color_or_mi.sh1
-rw-r--r--tests/bfs/color_or_mi0.out (renamed from tests/test_color_or_mi0.out)0
-rw-r--r--tests/bfs/color_or_mi0.sh1
-rw-r--r--tests/bfs/color_rs_lc_rc_ec.out (renamed from tests/test_color_rs_lc_rc_ec.out)0
-rw-r--r--tests/bfs/color_rs_lc_rc_ec.sh1
-rw-r--r--tests/bfs/color_st0_tw0_ow.out (renamed from tests/test_color_st0_tw0_ow.out)0
-rw-r--r--tests/bfs/color_st0_tw0_ow.sh1
-rw-r--r--tests/bfs/color_st0_tw0_ow0.out (renamed from tests/test_color_st0_tw0_ow0.out)0
-rw-r--r--tests/bfs/color_st0_tw0_ow0.sh1
-rw-r--r--tests/bfs/color_st0_tw_ow.out (renamed from tests/test_color_st0_tw_ow.out)0
-rw-r--r--tests/bfs/color_st0_tw_ow.sh1
-rw-r--r--tests/bfs/color_st0_tw_ow0.out (renamed from tests/test_color_st0_tw_ow0.out)0
-rw-r--r--tests/bfs/color_st0_tw_ow0.sh1
-rw-r--r--tests/bfs/color_st_tw0_ow.out (renamed from tests/test_color_st_tw0_ow.out)0
-rw-r--r--tests/bfs/color_st_tw0_ow.sh1
-rw-r--r--tests/bfs/color_st_tw0_ow0.out (renamed from tests/test_color_st_tw0_ow0.out)0
-rw-r--r--tests/bfs/color_st_tw0_ow0.sh1
-rw-r--r--tests/bfs/color_st_tw_ow0.out (renamed from tests/test_color_st_tw_ow0.out)0
-rw-r--r--tests/bfs/color_st_tw_ow0.sh1
-rw-r--r--tests/bfs/color_star.out (renamed from tests/test_color_star.out)0
-rw-r--r--tests/bfs/color_star.sh2
-rw-r--r--tests/bfs/color_su0_sg.out (renamed from tests/test_color_su0_sg.out)0
-rw-r--r--tests/bfs/color_su0_sg.sh1
-rw-r--r--tests/bfs/color_su0_sg0.out (renamed from tests/test_color_su0_sg0.out)0
-rw-r--r--tests/bfs/color_su0_sg0.sh1
-rw-r--r--tests/bfs/color_su_sg0.out (renamed from tests/test_color_su_sg0.out)0
-rw-r--r--tests/bfs/color_su_sg0.sh1
-rw-r--r--tests/bfs/comma_incomplete.sh1
-rw-r--r--tests/bfs/data_flow_hidden.out (renamed from tests/test_basic.out)0
-rw-r--r--tests/bfs/data_flow_hidden.sh1
-rw-r--r--tests/bfs/deep_strict.out (renamed from tests/test_deep.out)0
-rw-r--r--tests/bfs/deep_strict.sh5
-rw-r--r--tests/bfs/exclude_depth.out (renamed from tests/test_exclude_depth.out)0
-rw-r--r--tests/bfs/exclude_depth.sh1
-rw-r--r--tests/bfs/exclude_exclude.sh1
-rw-r--r--tests/bfs/exclude_mindepth.out (renamed from tests/test_L_ilname.out)0
-rw-r--r--tests/bfs/exclude_mindepth.sh1
-rw-r--r--tests/bfs/exclude_name.out (renamed from tests/test_exclude_name.out)0
-rw-r--r--tests/bfs/exclude_name.sh1
-rw-r--r--tests/bfs/exclude_print.sh1
-rw-r--r--tests/bfs/exec_flush_fprint.out (renamed from tests/test_exec_flush_fprint.out)0
-rw-r--r--tests/bfs/exec_flush_fprint.sh3
-rw-r--r--tests/bfs/exec_flush_fprint_fail.sh2
-rw-r--r--tests/bfs/execdir_plus.out (renamed from tests/test_execdir_plus.out)0
-rw-r--r--tests/bfs/execdir_plus.sh7
-rw-r--r--tests/bfs/execdir_plus_nonexistent.out (renamed from tests/test_closed_stdin.out)0
-rw-r--r--tests/bfs/execdir_plus_nonexistent.sh5
-rw-r--r--tests/bfs/expr_flag_path.out (renamed from tests/test_H_type_l.out)0
-rw-r--r--tests/bfs/expr_flag_path.sh1
-rw-r--r--tests/bfs/expr_path_flag.out (renamed from tests/test_expr_flag_path.out)0
-rw-r--r--tests/bfs/expr_path_flag.sh1
-rw-r--r--tests/bfs/flag_expr_path.out (renamed from tests/test_expr_path_flag.out)0
-rw-r--r--tests/bfs/flag_expr_path.sh1
-rw-r--r--tests/bfs/fprint_duplicate_stdout.out (renamed from tests/test_fprint_append.out)0
-rw-r--r--tests/bfs/fprint_duplicate_stdout.sh3
-rw-r--r--tests/bfs/fprint_error_stderr.sh2
-rw-r--r--tests/bfs/fprint_error_stdout.sh2
-rw-r--r--tests/bfs/help.sh6
-rw-r--r--tests/bfs/hidden.out (renamed from tests/test_hidden.out)0
-rw-r--r--tests/bfs/hidden.sh1
-rw-r--r--tests/bfs/hidden_root.out (renamed from tests/test_hidden_root.out)0
-rw-r--r--tests/bfs/hidden_root.sh2
-rw-r--r--tests/bfs/links_empty.sh1
-rw-r--r--tests/bfs/links_invalid.sh1
-rw-r--r--tests/bfs/links_negative.sh1
-rw-r--r--tests/bfs/links_noarg.sh1
-rw-r--r--tests/bfs/newerma_nonexistent.sh1
-rw-r--r--tests/bfs/newermq.sh1
-rw-r--r--tests/bfs/newermt_invalid.sh1
-rw-r--r--tests/bfs/newerqm.sh1
-rw-r--r--tests/bfs/nohidden.out (renamed from tests/test_nohidden.out)0
-rw-r--r--tests/bfs/nohidden.sh1
-rw-r--r--tests/bfs/nohidden_depth.out (renamed from tests/test_nohidden_depth.out)0
-rw-r--r--tests/bfs/nohidden_depth.sh1
-rw-r--r--tests/bfs/nowarn.sh2
-rw-r--r--tests/bfs/ok_plus_semicolon.out (renamed from tests/test_ok_plus_semicolon.out)0
-rw-r--r--tests/bfs/ok_plus_semicolon.sh8
-rw-r--r--tests/bfs/okdir_plus_semicolon.out (renamed from tests/test_okdir_plus_semicolon.out)0
-rw-r--r--tests/bfs/okdir_plus_semicolon.sh1
-rw-r--r--tests/bfs/or_incomplete.sh1
-rw-r--r--tests/bfs/path_expr_flag.out (renamed from tests/test_flag_expr_path.out)0
-rw-r--r--tests/bfs/path_expr_flag.sh1
-rw-r--r--tests/bfs/path_flag_expr.out (renamed from tests/test_path_expr_flag.out)0
-rw-r--r--tests/bfs/path_flag_expr.sh1
-rw-r--r--tests/bfs/perm_leading_plus_symbolic.out (renamed from tests/test_L_lname.out)0
-rw-r--r--tests/bfs/perm_leading_plus_symbolic.sh1
-rw-r--r--tests/bfs/perm_symbolic_double_comma.sh1
-rw-r--r--tests/bfs/perm_symbolic_missing_action.sh1
-rw-r--r--tests/bfs/perm_symbolic_trailing_comma.sh1
-rw-r--r--tests/bfs/printf_color.out (renamed from tests/test_printf_color.out)0
-rw-r--r--tests/bfs/printf_color.sh1
-rw-r--r--tests/bfs/printf_duplicate_flag.sh1
-rw-r--r--tests/bfs/printf_everything.sh9
-rw-r--r--tests/bfs/printf_incomplete_escape.sh1
-rw-r--r--tests/bfs/printf_incomplete_format.sh1
-rw-r--r--tests/bfs/printf_invalid_escape.sh1
-rw-r--r--tests/bfs/printf_invalid_format.sh1
-rw-r--r--tests/bfs/printf_must_be_numeric.sh1
-rw-r--r--tests/bfs/printf_w.out (renamed from tests/test_and_purity.out)0
-rw-r--r--tests/bfs/printf_w.sh2
-rw-r--r--tests/bfs/stderr_fails_loudly.sh2
-rw-r--r--tests/bfs/stderr_fails_silently.out (renamed from tests/test_d_path.out)0
-rw-r--r--tests/bfs/stderr_fails_silently.sh2
-rw-r--r--tests/bfs/type_multi.out (renamed from tests/test_type_multi.out)0
-rw-r--r--tests/bfs/type_multi.sh1
-rw-r--r--tests/bfs/typo.sh1
-rw-r--r--tests/bfs/unexpected_operator.sh1
-rw-r--r--tests/bfs/unique.out (renamed from tests/test_unique.out)0
-rw-r--r--tests/bfs/unique.sh1
-rw-r--r--tests/bfs/unique_depth.out (renamed from tests/test_data_flow_group.out)0
-rw-r--r--tests/bfs/unique_depth.sh1
-rw-r--r--tests/bfs/version.sh1
-rw-r--r--tests/bfs/warn.sh2
-rw-r--r--tests/bfs/xtype_depth.sh2
-rw-r--r--tests/bfs/xtype_multi.out (renamed from tests/test_xtype_multi.out)0
-rw-r--r--tests/bfs/xtype_multi.sh1
-rw-r--r--tests/bfs/xtype_reorder.out (renamed from tests/test_data_flow_type.out)0
-rw-r--r--tests/bfs/xtype_reorder.sh4
-rw-r--r--tests/bsd/E.out (renamed from tests/test_E.out)0
-rw-r--r--tests/bsd/E.sh2
-rw-r--r--tests/bsd/H_mnewer.out (renamed from tests/test_H_mnewer.out)0
-rw-r--r--tests/bsd/H_mnewer.sh1
-rw-r--r--tests/bsd/L_acl.out (renamed from tests/test_L_acl.out)0
-rw-r--r--tests/bsd/L_acl.sh9
-rw-r--r--tests/bsd/L_xattr.out (renamed from tests/test_L_xattr.out)0
-rw-r--r--tests/bsd/L_xattr.sh3
-rw-r--r--tests/bsd/L_xattrname.out (renamed from tests/test_L_xattrname.out)0
-rw-r--r--tests/bsd/L_xattrname.sh11
-rw-r--r--tests/bsd/X.out (renamed from tests/test_X.out)0
-rw-r--r--tests/bsd/X.sh2
-rw-r--r--tests/bsd/acl.out (renamed from tests/test_acl.out)0
-rw-r--r--tests/bsd/acl.sh9
-rw-r--r--tests/bsd/asince.out (renamed from tests/test_asince.out)0
-rw-r--r--tests/bsd/asince.sh1
-rw-r--r--tests/bsd/d_path.out (renamed from tests/test_data_flow_hidden.out)0
-rw-r--r--tests/bsd/d_path.sh1
-rw-r--r--tests/bsd/data_flow_depth.out (renamed from tests/test_data_flow_depth.out)0
-rw-r--r--tests/bsd/data_flow_depth.sh1
-rw-r--r--tests/bsd/data_flow_sparse.out (renamed from tests/test_data_flow_sparse.out)0
-rw-r--r--tests/bsd/data_flow_sparse.sh1
-rw-r--r--tests/bsd/depth_depth_n.out (renamed from tests/test_depth_depth_n.out)0
-rw-r--r--tests/bsd/depth_depth_n.sh1
-rw-r--r--tests/bsd/depth_depth_n_minus.out (renamed from tests/test_depth_depth_n_minus.out)0
-rw-r--r--tests/bsd/depth_depth_n_minus.sh1
-rw-r--r--tests/bsd/depth_depth_n_plus.out (renamed from tests/test_depth_depth_n_plus.out)0
-rw-r--r--tests/bsd/depth_depth_n_plus.sh1
-rw-r--r--tests/bsd/depth_n.out (renamed from tests/test_depth_n.out)0
-rw-r--r--tests/bsd/depth_n.sh1
-rw-r--r--tests/bsd/depth_n_minus.out (renamed from tests/test_depth_maxdepth_1.out)0
-rw-r--r--tests/bsd/depth_n_minus.sh1
-rw-r--r--tests/bsd/depth_n_plus.out (renamed from tests/test_depth_n_plus.out)0
-rw-r--r--tests/bsd/depth_n_plus.sh1
-rw-r--r--tests/bsd/depth_overflow.out (renamed from tests/test_data_flow_user.out)0
-rw-r--r--tests/bsd/depth_overflow.sh1
-rw-r--r--tests/bsd/exit.out (renamed from tests/test_exit.out)0
-rw-r--r--tests/bsd/exit.sh11
-rw-r--r--tests/bsd/exit_no_implicit_print.out (renamed from tests/test_exclude_mindepth.out)0
-rw-r--r--tests/bsd/exit_no_implicit_print.sh1
-rw-r--r--tests/bsd/f.out (renamed from tests/test_f.out)0
-rw-r--r--tests/bsd/f.sh2
-rw-r--r--tests/bsd/flags.out (renamed from tests/test_flags.out)0
-rw-r--r--tests/bsd/flags.sh8
-rw-r--r--tests/bsd/gid_name.out (renamed from tests/test_daystart.out)0
-rw-r--r--tests/bsd/gid_name.sh1
-rw-r--r--tests/bsd/mnewer.out (renamed from tests/test_H_newer.out)0
-rw-r--r--tests/bsd/mnewer.sh1
-rw-r--r--tests/bsd/msince.out (renamed from tests/test_msince.out)0
-rw-r--r--tests/bsd/msince.sh1
-rw-r--r--tests/bsd/mtime_units.out (renamed from tests/test_mtime_units.out)0
-rw-r--r--tests/bsd/mtime_units.sh1
-rw-r--r--tests/bsd/okdir_stdin.out (renamed from tests/test_okdir_stdin.out)0
-rw-r--r--tests/bsd/okdir_stdin.sh2
-rw-r--r--tests/bsd/perm_000_plus.out (renamed from tests/test_perm_000_minus.out)0
-rw-r--r--tests/bsd/perm_000_plus.sh1
-rw-r--r--tests/bsd/perm_222_plus.out (renamed from tests/test_perm_222_plus.out)0
-rw-r--r--tests/bsd/perm_222_plus.sh1
-rw-r--r--tests/bsd/perm_644_plus.out (renamed from tests/test_perm_644_plus.out)0
-rw-r--r--tests/bsd/perm_644_plus.sh1
-rw-r--r--tests/bsd/printx.out (renamed from tests/test_printx.out)0
-rw-r--r--tests/bsd/printx.sh1
-rw-r--r--tests/bsd/quit_implicit_print.out (renamed from tests/test_and_false_or_true.out)0
-rw-r--r--tests/bsd/quit_implicit_print.sh1
-rw-r--r--tests/bsd/rm.out (renamed from tests/test_delete.out)0
-rw-r--r--tests/bsd/rm.sh6
-rw-r--r--tests/bsd/s.out (renamed from tests/test_s.out)0
-rw-r--r--tests/bsd/s.sh2
-rw-r--r--tests/bsd/size_T.out (renamed from tests/test_size_T.out)0
-rw-r--r--tests/bsd/size_T.sh1
-rw-r--r--tests/bsd/uid_name.out (renamed from tests/test_daystart_twice.out)0
-rw-r--r--tests/bsd/uid_name.sh1
-rw-r--r--tests/bsd/xattr.out (renamed from tests/test_xattr.out)0
-rw-r--r--tests/bsd/xattr.sh3
-rw-r--r--tests/bsd/xattrname.out (renamed from tests/test_xattrname.out)0
-rw-r--r--tests/bsd/xattrname.sh11
-rw-r--r--tests/common/H_newer.out (renamed from tests/test_anewer.out)0
-rw-r--r--tests/common/H_newer.sh1
-rw-r--r--tests/common/H_samefile_broken.out (renamed from tests/test_H_broken.out)0
-rw-r--r--tests/common/H_samefile_broken.sh1
-rw-r--r--tests/common/H_samefile_notdir.out (renamed from tests/test_H_notdir.out)0
-rw-r--r--tests/common/H_samefile_notdir.sh1
-rw-r--r--tests/common/H_samefile_symlink.out (renamed from tests/test_H_samefile_symlink.out)0
-rw-r--r--tests/common/H_samefile_symlink.sh1
-rw-r--r--tests/common/L_ilname.out (renamed from tests/test_exit_no_implicit_print.out)0
-rw-r--r--tests/common/L_ilname.sh2
-rw-r--r--tests/common/L_lname.out (renamed from tests/test_false.out)0
-rw-r--r--tests/common/L_lname.sh1
-rw-r--r--tests/common/L_ls.sh1
-rw-r--r--tests/common/L_mount.out (renamed from tests/test_L_mount.out)0
-rw-r--r--tests/common/L_mount.sh15
-rw-r--r--tests/common/L_samefile_broken.out (renamed from tests/test_H_samefile_broken.out)0
-rw-r--r--tests/common/L_samefile_broken.sh1
-rw-r--r--tests/common/L_samefile_notdir.out (renamed from tests/test_H_samefile_notdir.out)0
-rw-r--r--tests/common/L_samefile_notdir.sh1
-rw-r--r--tests/common/L_samefile_symlink.out (renamed from tests/test_L_samefile_symlink.out)0
-rw-r--r--tests/common/L_samefile_symlink.sh1
-rw-r--r--tests/common/P.out (renamed from tests/test_H.out)0
-rw-r--r--tests/common/P.sh1
-rw-r--r--tests/common/P_slash.out (renamed from tests/test_H_slash.out)0
-rw-r--r--tests/common/P_slash.sh1
-rw-r--r--tests/common/anewer.out (renamed from tests/test_mnewer.out)0
-rw-r--r--tests/common/anewer.sh1
-rw-r--r--tests/common/delete.out (renamed from tests/test_delete_many.out)0
-rw-r--r--tests/common/delete.sh7
-rw-r--r--tests/common/delete_many.out (renamed from tests/test_rm.out)0
-rw-r--r--tests/common/delete_many.sh8
-rw-r--r--tests/common/depth_maxdepth_1.out (renamed from tests/test_depth_n_minus.out)0
-rw-r--r--tests/common/depth_maxdepth_1.sh1
-rw-r--r--tests/common/depth_maxdepth_2.out (renamed from tests/test_depth_maxdepth_2.out)0
-rw-r--r--tests/common/depth_maxdepth_2.sh1
-rw-r--r--tests/common/depth_mindepth_1.out (renamed from tests/test_depth_mindepth_1.out)0
-rw-r--r--tests/common/depth_mindepth_1.sh1
-rw-r--r--tests/common/depth_mindepth_2.out (renamed from tests/test_depth_mindepth_2.out)0
-rw-r--r--tests/common/depth_mindepth_2.sh1
-rw-r--r--tests/common/double_dash.out (renamed from tests/test_double_dash.out)0
-rw-r--r--tests/common/double_dash.sh2
-rw-r--r--tests/common/exec_substring.out (renamed from tests/test_exec_substring.out)0
-rw-r--r--tests/common/exec_substring.sh1
-rw-r--r--tests/common/execdir_nonexistent.out (renamed from tests/test_depth.out)0
-rw-r--r--tests/common/execdir_nonexistent.sh5
-rw-r--r--tests/common/execdir_pwd.out (renamed from tests/test_execdir_pwd.out)0
-rw-r--r--tests/common/execdir_pwd.sh3
-rw-r--r--tests/common/execdir_slash.out (renamed from tests/test_execdir_slash.out)0
-rw-r--r--tests/common/execdir_slash.sh2
-rw-r--r--tests/common/execdir_slash_pwd.out (renamed from tests/test_execdir_slash_pwd.out)0
-rw-r--r--tests/common/execdir_slash_pwd.sh1
-rw-r--r--tests/common/execdir_slashes.out (renamed from tests/test_execdir_slashes.out)0
-rw-r--r--tests/common/execdir_slashes.sh1
-rw-r--r--tests/common/execdir_ulimit.out (renamed from tests/test_execdir_ulimit.out)0
-rw-r--r--tests/common/execdir_ulimit.sh7
-rw-r--r--tests/common/flag_double_dash.out (renamed from tests/test_flag_double_dash.out)0
-rw-r--r--tests/common/flag_double_dash.sh2
-rw-r--r--tests/common/follow.out (renamed from tests/test_L.out)0
-rw-r--r--tests/common/follow.sh1
-rw-r--r--tests/common/ilname.out (renamed from tests/test_ilname.out)0
-rw-r--r--tests/common/ilname.sh2
-rw-r--r--tests/common/iname.out (renamed from tests/test_iname.out)0
-rw-r--r--tests/common/iname.sh2
-rw-r--r--tests/common/inum.out (renamed from tests/test_inum.out)0
-rw-r--r--tests/common/inum.sh1
-rw-r--r--tests/common/inum_bind_mount.out (renamed from tests/test_inum_bind_mount.out)0
-rw-r--r--tests/common/inum_bind_mount.sh12
-rw-r--r--tests/common/inum_mount.out (renamed from tests/test_inum_automount.out)0
-rw-r--r--tests/common/inum_mount.sh12
-rw-r--r--tests/common/ipath.out (renamed from tests/test_ipath.out)0
-rw-r--r--tests/common/ipath.sh2
-rw-r--r--tests/common/iregex.out (renamed from tests/test_iregex.out)0
-rw-r--r--tests/common/iregex.sh1
-rw-r--r--tests/common/lname.out (renamed from tests/test_lname.out)0
-rw-r--r--tests/common/lname.sh1
-rw-r--r--tests/common/ls.sh1
-rw-r--r--tests/common/maxdepth.out (renamed from tests/test_maxdepth.out)0
-rw-r--r--tests/common/maxdepth.sh1
-rw-r--r--tests/common/mindepth.out (renamed from tests/test_mindepth.out)0
-rw-r--r--tests/common/mindepth.sh1
-rw-r--r--tests/common/mount.out (renamed from tests/test_mount.out)0
-rw-r--r--tests/common/mount.sh13
-rw-r--r--tests/common/name_slash.out (renamed from tests/test_name_slash.out)0
-rw-r--r--tests/common/name_slash.sh1
-rw-r--r--tests/common/name_slashes.out (renamed from tests/test_name_slashes.out)0
-rw-r--r--tests/common/name_slashes.sh1
-rw-r--r--tests/common/newerma.out (renamed from tests/test_newer.out)0
-rw-r--r--tests/common/newerma.sh1
-rw-r--r--tests/common/newermt.out (renamed from tests/test_newermt.out)0
-rw-r--r--tests/common/newermt.sh1
-rw-r--r--tests/common/newermt_epoch_minus_one.out (renamed from tests/test_newermt_epoch_minus_one.out)0
-rw-r--r--tests/common/newermt_epoch_minus_one.sh1
-rw-r--r--tests/common/ok_closed_stdin.out (renamed from tests/test_name_backslash.out)0
-rw-r--r--tests/common/ok_closed_stdin.sh1
-rw-r--r--tests/common/okdir_closed_stdin.out (renamed from tests/test_nogroup.out)0
-rw-r--r--tests/common/okdir_closed_stdin.sh1
-rw-r--r--tests/common/quit.out (renamed from tests/test_name_root_depth.out)0
-rw-r--r--tests/common/quit.sh1
-rw-r--r--tests/common/quit_after_print.out (renamed from tests/test_comma_reachability.out)0
-rw-r--r--tests/common/quit_after_print.sh1
-rw-r--r--tests/common/quit_before_print.out (renamed from tests/test_nogroup_ulimit.out)0
-rw-r--r--tests/common/quit_before_print.sh1
-rw-r--r--tests/common/quit_child.out (renamed from tests/test_quit_child.out)0
-rw-r--r--tests/common/quit_child.sh1
-rw-r--r--tests/common/quit_depth.out (renamed from tests/test_quit_depth.out)0
-rw-r--r--tests/common/quit_depth.sh1
-rw-r--r--tests/common/quit_depth_child.out (renamed from tests/test_quit_depth_child.out)0
-rw-r--r--tests/common/quit_depth_child.sh1
-rw-r--r--tests/common/regex.out (renamed from tests/test_regex.out)0
-rw-r--r--tests/common/regex.sh1
-rw-r--r--tests/common/regex_parens.out (renamed from tests/test_regex_parens.out)0
-rw-r--r--tests/common/regex_parens.sh2
-rw-r--r--tests/common/samefile.out (renamed from tests/test_links.out)0
-rw-r--r--tests/common/samefile.sh1
-rw-r--r--tests/common/samefile_broken.out (renamed from tests/test_L_broken.out)0
-rw-r--r--tests/common/samefile_broken.sh1
-rw-r--r--tests/common/samefile_notdir.out (renamed from tests/test_L_notdir.out)0
-rw-r--r--tests/common/samefile_notdir.sh1
-rw-r--r--tests/common/samefile_symlink.out (renamed from tests/test_samefile_symlink.out)0
-rw-r--r--tests/common/samefile_symlink.sh1
-rw-r--r--tests/common/size_big.out (renamed from tests/test_nouser.out)0
-rw-r--r--tests/common/size_big.sh1
-rw-r--r--tests/gnu/L_delete.out (renamed from tests/test_L_delete.out)0
-rw-r--r--tests/gnu/L_delete.sh9
-rw-r--r--tests/gnu/L_loops_continue.out (renamed from tests/test_L_loops_continue.out)0
-rw-r--r--tests/gnu/L_loops_continue.sh2
-rw-r--r--tests/gnu/L_xtype_f.out (renamed from tests/test_L_xtype_f.out)0
-rw-r--r--tests/gnu/L_xtype_f.sh1
-rw-r--r--tests/gnu/L_xtype_l.out (renamed from tests/test_L_xtype_l.out)0
-rw-r--r--tests/gnu/L_xtype_l.sh1
-rw-r--r--tests/gnu/and.out (renamed from tests/test_a.out)0
-rw-r--r--tests/gnu/and.sh1
-rw-r--r--tests/gnu/and_false_or_true.out (renamed from tests/test_comma_redundant_false.out)0
-rw-r--r--tests/gnu/and_false_or_true.sh3
-rw-r--r--tests/gnu/and_purity.out (renamed from tests/test_nouser_ulimit.out)0
-rw-r--r--tests/gnu/and_purity.sh2
-rw-r--r--tests/gnu/comma.out (renamed from tests/test_comma.out)0
-rw-r--r--tests/gnu/comma.sh1
-rw-r--r--tests/gnu/comma_reachability.out (renamed from tests/test_comma_redundant_true.out)0
-rw-r--r--tests/gnu/comma_reachability.sh1
-rw-r--r--tests/gnu/comma_redundant_false.out (renamed from tests/test_fprint_truncate.out)0
-rw-r--r--tests/gnu/comma_redundant_false.sh2
-rw-r--r--tests/gnu/comma_redundant_true.out (renamed from tests/test_not_reachability.out)0
-rw-r--r--tests/gnu/comma_redundant_true.sh2
-rw-r--r--tests/gnu/daystart.out (renamed from tests/test_depth_overflow.out)0
-rw-r--r--tests/gnu/daystart.sh1
-rw-r--r--tests/gnu/daystart_twice.out (renamed from tests/test_exec.out)0
-rw-r--r--tests/gnu/daystart_twice.sh1
-rw-r--r--tests/gnu/empty.out (renamed from tests/test_empty.out)0
-rw-r--r--tests/gnu/empty.sh1
-rw-r--r--tests/gnu/empty_special.out (renamed from tests/test_empty_special.out)0
-rw-r--r--tests/gnu/empty_special.sh1
-rw-r--r--tests/gnu/exec_flush.out (renamed from tests/test_exec_flush.out)0
-rw-r--r--tests/gnu/exec_flush.sh4
-rw-r--r--tests/gnu/exec_flush_fail.sh3
-rw-r--r--tests/gnu/exec_nothing.sh2
-rw-r--r--tests/gnu/exec_plus_flush.out (renamed from tests/test_exec_plus_flush.out)bin22 -> 22 bytes
-rw-r--r--tests/gnu/exec_plus_flush.sh2
-rw-r--r--tests/gnu/exec_plus_flush_fail.sh2
-rw-r--r--tests/gnu/execdir.out (renamed from tests/test_execdir.out)0
-rw-r--r--tests/gnu/execdir.sh1
-rw-r--r--tests/gnu/execdir_plus_semicolon.out (renamed from tests/test_execdir_plus_semicolon.out)0
-rw-r--r--tests/gnu/execdir_plus_semicolon.sh1
-rw-r--r--tests/gnu/execdir_substring.out (renamed from tests/test_execdir_substring.out)0
-rw-r--r--tests/gnu/execdir_substring.sh1
-rw-r--r--tests/gnu/executable.out (renamed from tests/test_executable.out)0
-rw-r--r--tests/gnu/executable.sh1
-rw-r--r--tests/gnu/false.out (renamed from tests/test_ok_closed_stdin.out)0
-rw-r--r--tests/gnu/false.sh1
-rw-r--r--tests/gnu/files0_from_empty.sh1
-rw-r--r--tests/gnu/files0_from_file.out (renamed from tests/test_files0_from_file.out)0
-rw-r--r--tests/gnu/files0_from_file.sh3
-rw-r--r--tests/gnu/files0_from_none.sh1
-rw-r--r--tests/gnu/files0_from_nothing.sh1
-rw-r--r--tests/gnu/files0_from_nowhere.sh1
-rw-r--r--tests/gnu/files0_from_ok.sh1
-rw-r--r--tests/gnu/files0_from_stdin.out (renamed from tests/test_files0_from_stdin.out)0
-rw-r--r--tests/gnu/files0_from_stdin.sh2
-rw-r--r--tests/gnu/fls.sh1
-rw-r--r--tests/gnu/follow_comma.out (renamed from tests/test_follow_comma.out)0
-rw-r--r--tests/gnu/follow_comma.sh3
-rw-r--r--tests/gnu/fprint.out (renamed from tests/test_exec_nonexistent.out)0
-rw-r--r--tests/gnu/fprint.sh3
-rw-r--r--tests/gnu/fprint0.out (renamed from tests/test_fprint0.out)bin16 -> 16 bytes
-rw-r--r--tests/gnu/fprint0.sh2
-rw-r--r--tests/gnu/fprint_duplicate.out (renamed from tests/test_fprint_duplicate.out)0
-rw-r--r--tests/gnu/fprint_duplicate.sh7
-rw-r--r--tests/gnu/fprint_error.sh2
-rw-r--r--tests/gnu/fprint_noarg.sh1
-rw-r--r--tests/gnu/fprint_noerror.sh3
-rw-r--r--tests/gnu/fprint_nonexistent.sh1
-rw-r--r--tests/gnu/fprint_truncate.out (renamed from tests/test_printf_leak.out)0
-rw-r--r--tests/gnu/fprint_truncate.sh5
-rw-r--r--tests/gnu/fprintf.out (renamed from tests/test_fprintf.out)0
-rw-r--r--tests/gnu/fprintf.sh3
-rw-r--r--tests/gnu/fprintf_nofile.sh1
-rw-r--r--tests/gnu/fprintf_noformat.sh1
-rw-r--r--tests/gnu/fstype.out (renamed from tests/test_exec_nopath.out)0
-rw-r--r--tests/gnu/fstype.sh2
-rw-r--r--tests/gnu/gid.out (renamed from tests/test_exec_plus_nonexistent.out)0
-rw-r--r--tests/gnu/gid.sh1
-rw-r--r--tests/gnu/gid_minus.out (renamed from tests/test_exec_plus_status.out)0
-rw-r--r--tests/gnu/gid_minus.sh1
-rw-r--r--tests/gnu/gid_minus_plus.out (renamed from tests/test_execdir_nonexistent.out)0
-rw-r--r--tests/gnu/gid_minus_plus.sh1
-rw-r--r--tests/gnu/gid_plus.out (renamed from tests/test_execdir_plus_nonexistent.out)0
-rw-r--r--tests/gnu/gid_plus.sh2
-rw-r--r--tests/gnu/gid_plus_plus.out (renamed from tests/test_fprint.out)0
-rw-r--r--tests/gnu/gid_plus_plus.sh2
-rw-r--r--tests/gnu/ignore_readdir_race.sh5
-rw-r--r--tests/gnu/ignore_readdir_race_notdir.sh5
-rw-r--r--tests/gnu/ignore_readdir_race_root.sh2
-rw-r--r--tests/gnu/inum_automount.out (renamed from tests/test_inum_mount.out)0
-rw-r--r--tests/gnu/inum_automount.sh17
-rw-r--r--tests/gnu/iwholename.out (renamed from tests/test_iwholename.out)0
-rw-r--r--tests/gnu/iwholename.sh2
-rw-r--r--tests/gnu/not.out (renamed from tests/test_bang.out)0
-rw-r--r--tests/gnu/not.sh1
-rw-r--r--tests/gnu/not_reachability.out (renamed from tests/test_quit_after_print.out)0
-rw-r--r--tests/gnu/not_reachability.sh1
-rw-r--r--tests/gnu/ok_nothing.sh2
-rw-r--r--tests/gnu/or.out (renamed from tests/test_o.out)0
-rw-r--r--tests/gnu/or.sh1
-rw-r--r--tests/gnu/path_d.out (renamed from tests/test_fstype.out)0
-rw-r--r--tests/gnu/path_d.sh1
-rw-r--r--tests/gnu/perm_000_slash.out (renamed from tests/test_perm_000_plus.out)0
-rw-r--r--tests/gnu/perm_000_slash.sh1
-rw-r--r--tests/gnu/perm_222_slash.out (renamed from tests/test_perm_222_slash.out)0
-rw-r--r--tests/gnu/perm_222_slash.sh1
-rw-r--r--tests/gnu/perm_644_slash.out (renamed from tests/test_perm_644_slash.out)0
-rw-r--r--tests/gnu/perm_644_slash.sh1
-rw-r--r--tests/gnu/perm_leading_plus_symbolic_slash.out (renamed from tests/test_perm_leading_plus_symbolic_slash.out)0
-rw-r--r--tests/gnu/perm_leading_plus_symbolic_slash.sh1
-rw-r--r--tests/gnu/perm_symbolic_slash.out (renamed from tests/test_perm_symbolic_slash.out)0
-rw-r--r--tests/gnu/perm_symbolic_slash.sh1
-rw-r--r--tests/gnu/precedence.out (renamed from tests/test_precedence.out)0
-rw-r--r--tests/gnu/precedence.sh1
-rw-r--r--tests/gnu/print0.out (renamed from tests/test_print0.out)bin16 -> 16 bytes
-rw-r--r--tests/gnu/print0.sh2
-rw-r--r--tests/gnu/print_error.sh2
-rw-r--r--tests/gnu/printf.out (renamed from tests/test_printf.out)0
-rw-r--r--tests/gnu/printf.sh1
-rw-r--r--tests/gnu/printf_H.out (renamed from tests/test_printf_H.out)0
-rw-r--r--tests/gnu/printf_H.sh1
-rw-r--r--tests/gnu/printf_Y_error.out (renamed from tests/test_printf_Y_error.out)0
-rw-r--r--tests/gnu/printf_Y_error.sh12
-rw-r--r--tests/gnu/printf_empty.out (renamed from tests/test_okdir_closed_stdin.out)0
-rw-r--r--tests/gnu/printf_empty.sh1
-rw-r--r--tests/gnu/printf_escapes.out (renamed from tests/test_printf_escapes.out)0
-rw-r--r--tests/gnu/printf_escapes.sh1
-rw-r--r--tests/gnu/printf_flags.out (renamed from tests/test_printf_flags.out)0
-rw-r--r--tests/gnu/printf_flags.sh1
-rw-r--r--tests/gnu/printf_l_nonlink.out (renamed from tests/test_printf_l_nonlink.out)0
-rw-r--r--tests/gnu/printf_l_nonlink.sh1
-rw-r--r--tests/gnu/printf_leak.out (renamed from tests/test_quit_implicit_print.out)0
-rw-r--r--tests/gnu/printf_leak.sh2
-rw-r--r--tests/gnu/printf_nul.out (renamed from tests/test_printf_nul.out)bin16 -> 16 bytes
-rw-r--r--tests/gnu/printf_nul.sh3
-rw-r--r--tests/gnu/printf_slash.out (renamed from tests/test_printf_slash.out)0
-rw-r--r--tests/gnu/printf_slash.sh1
-rw-r--r--tests/gnu/printf_slashes.out (renamed from tests/test_printf_slashes.out)0
-rw-r--r--tests/gnu/printf_slashes.sh1
-rw-r--r--tests/gnu/printf_times.out (renamed from tests/test_printf_times.out)0
-rw-r--r--tests/gnu/printf_times.sh1
-rw-r--r--tests/gnu/printf_trailing_slash.out (renamed from tests/test_printf_trailing_slash.out)0
-rw-r--r--tests/gnu/printf_trailing_slash.sh1
-rw-r--r--tests/gnu/printf_trailing_slashes.out (renamed from tests/test_printf_trailing_slashes.out)0
-rw-r--r--tests/gnu/printf_trailing_slashes.sh1
-rw-r--r--tests/gnu/printf_types.out (renamed from tests/test_printf_types.out)0
-rw-r--r--tests/gnu/printf_types.sh1
-rw-r--r--tests/gnu/printf_u_g_ulimit.sh3
-rw-r--r--tests/gnu/readable.out (renamed from tests/test_readable.out)0
-rw-r--r--tests/gnu/readable.sh1
-rw-r--r--tests/gnu/regex_error.sh1
-rw-r--r--tests/gnu/regex_invalid_utf8.out (renamed from tests/test_regex_invalid_utf8.out)0
-rw-r--r--tests/gnu/regex_invalid_utf8.sh8
-rw-r--r--tests/gnu/regextype_ed.out (renamed from tests/test_regextype_ed.out)0
-rw-r--r--tests/gnu/regextype_ed.sh2
-rw-r--r--tests/gnu/regextype_emacs.out (renamed from tests/test_regextype_emacs.out)0
-rw-r--r--tests/gnu/regextype_emacs.sh3
-rw-r--r--tests/gnu/regextype_grep.out (renamed from tests/test_name.out)0
-rw-r--r--tests/gnu/regextype_grep.sh3
-rw-r--r--tests/gnu/regextype_posix_basic.out (renamed from tests/test_regextype_posix_basic.out)0
-rw-r--r--tests/gnu/regextype_posix_basic.sh2
-rw-r--r--tests/gnu/regextype_posix_extended.out (renamed from tests/test_regextype_posix_extended.out)0
-rw-r--r--tests/gnu/regextype_posix_extended.sh2
-rw-r--r--tests/gnu/regextype_sed.out (renamed from tests/test_regextype_sed.out)0
-rw-r--r--tests/gnu/regextype_sed.sh2
-rw-r--r--tests/gnu/true.out (renamed from tests/test_gid.out)0
-rw-r--r--tests/gnu/true.sh1
-rw-r--r--tests/gnu/uid.out (renamed from tests/test_gid_minus.out)0
-rw-r--r--tests/gnu/uid.sh1
-rw-r--r--tests/gnu/uid_minus.out (renamed from tests/test_gid_minus_plus.out)0
-rw-r--r--tests/gnu/uid_minus.sh1
-rw-r--r--tests/gnu/uid_minus_plus.out (renamed from tests/test_gid_name.out)0
-rw-r--r--tests/gnu/uid_minus_plus.sh1
-rw-r--r--tests/gnu/uid_plus.out (renamed from tests/test_gid_plus.out)0
-rw-r--r--tests/gnu/uid_plus.sh2
-rw-r--r--tests/gnu/uid_plus_plus.out (renamed from tests/test_gid_plus_plus.out)0
-rw-r--r--tests/gnu/uid_plus_plus.sh2
-rw-r--r--tests/gnu/wholename.out (renamed from tests/test_path.out)0
-rw-r--r--tests/gnu/wholename.sh1
-rw-r--r--tests/gnu/writable.out (renamed from tests/test_writable.out)0
-rw-r--r--tests/gnu/writable.sh1
-rw-r--r--tests/gnu/xtype_bind_mount.out (renamed from tests/test_xtype_bind_mount.out)0
-rw-r--r--tests/gnu/xtype_bind_mount.sh13
-rw-r--r--tests/gnu/xtype_f.out (renamed from tests/test_xtype_f.out)0
-rw-r--r--tests/gnu/xtype_f.sh1
-rw-r--r--tests/gnu/xtype_l.out (renamed from tests/test_xtype_l.out)0
-rw-r--r--tests/gnu/xtype_l.sh1
-rw-r--r--tests/posix/H.out (renamed from tests/test_P.out)0
-rw-r--r--tests/posix/H.sh1
-rw-r--r--tests/posix/H_broken.out (renamed from tests/test_L_samefile_broken.out)0
-rw-r--r--tests/posix/H_broken.sh1
-rw-r--r--tests/posix/H_loops.out (renamed from tests/test_H_loops.out)0
-rw-r--r--tests/posix/H_loops.sh1
-rw-r--r--tests/posix/H_notdir.out (renamed from tests/test_L_samefile_notdir.out)0
-rw-r--r--tests/posix/H_notdir.sh1
-rw-r--r--tests/posix/H_slash.out (renamed from tests/test_P_slash.out)0
-rw-r--r--tests/posix/H_slash.sh1
-rw-r--r--tests/posix/H_type_l.out (renamed from tests/test_path_flag_expr.out)0
-rw-r--r--tests/posix/H_type_l.sh1
-rw-r--r--tests/posix/L.out (renamed from tests/test_L_depth.out)0
-rw-r--r--tests/posix/L.sh1
-rw-r--r--tests/posix/L_broken.out (renamed from tests/test_samefile_broken.out)0
-rw-r--r--tests/posix/L_broken.sh1
-rw-r--r--tests/posix/L_depth.out (renamed from tests/test_follow.out)0
-rw-r--r--tests/posix/L_depth.sh1
-rw-r--r--tests/posix/L_loops.sh4
-rw-r--r--tests/posix/L_notdir.out (renamed from tests/test_samefile_notdir.out)0
-rw-r--r--tests/posix/L_notdir.sh1
-rw-r--r--tests/posix/L_type_l.out (renamed from tests/test_L_type_l.out)0
-rw-r--r--tests/posix/L_type_l.sh1
-rw-r--r--tests/posix/L_xdev.out (renamed from tests/test_L_xdev.out)0
-rw-r--r--tests/posix/L_xdev.sh15
-rw-r--r--tests/posix/a.out (renamed from tests/test_and.out)0
-rw-r--r--tests/posix/a.sh1
-rw-r--r--tests/posix/bang.out (renamed from tests/test_not.out)0
-rw-r--r--tests/posix/bang.sh1
-rw-r--r--tests/posix/basic.out (renamed from tests/test_group_id.out)0
-rw-r--r--tests/posix/basic.sh1
-rw-r--r--tests/posix/closed_stderr.sh1
-rw-r--r--tests/posix/closed_stdin.out (renamed from tests/test_group_name.out)0
-rw-r--r--tests/posix/closed_stdin.sh1
-rw-r--r--tests/posix/closed_stdout.sh1
-rw-r--r--tests/posix/data_flow_and_swap.out (renamed from tests/test_data_flow_and_swap.out)0
-rw-r--r--tests/posix/data_flow_and_swap.sh1
-rw-r--r--tests/posix/data_flow_group.out (renamed from tests/test_group_nogroup.out)0
-rw-r--r--tests/posix/data_flow_group.sh1
-rw-r--r--tests/posix/data_flow_or_swap.out (renamed from tests/test_data_flow_or_swap.out)0
-rw-r--r--tests/posix/data_flow_or_swap.sh1
-rw-r--r--tests/posix/data_flow_type.out (renamed from tests/test_or_purity.out)0
-rw-r--r--tests/posix/data_flow_type.sh1
-rw-r--r--tests/posix/data_flow_user.out (renamed from tests/test_path_d.out)0
-rw-r--r--tests/posix/data_flow_user.sh1
-rw-r--r--tests/posix/de_morgan_and.out (renamed from tests/test_de_morgan_and.out)0
-rw-r--r--tests/posix/de_morgan_and.sh1
-rw-r--r--tests/posix/de_morgan_not.out (renamed from tests/test_de_morgan_not.out)0
-rw-r--r--tests/posix/de_morgan_not.sh1
-rw-r--r--tests/posix/de_morgan_or.out (renamed from tests/test_de_morgan_or.out)0
-rw-r--r--tests/posix/de_morgan_or.sh1
-rw-r--r--tests/posix/deep.out (renamed from tests/test_deep_strict.out)0
-rw-r--r--tests/posix/deep.sh4
-rw-r--r--tests/posix/depth.out (renamed from tests/test_stderr_fails_silently.out)0
-rw-r--r--tests/posix/depth.sh1
-rw-r--r--tests/posix/depth_error.out (renamed from tests/test_depth_error.out)0
-rw-r--r--tests/posix/depth_error.sh11
-rw-r--r--tests/posix/depth_slash.out (renamed from tests/test_depth_slash.out)0
-rw-r--r--tests/posix/depth_slash.sh1
-rw-r--r--tests/posix/double_negation.out (renamed from tests/test_double_negation.out)0
-rw-r--r--tests/posix/double_negation.sh1
-rw-r--r--tests/posix/exec.out (renamed from tests/test_true.out)0
-rw-r--r--tests/posix/exec.sh1
-rw-r--r--tests/posix/exec_nonexistent.out (renamed from tests/test_uid.out)0
-rw-r--r--tests/posix/exec_nonexistent.sh8
-rw-r--r--tests/posix/exec_nopath.out (renamed from tests/test_uid_minus.out)0
-rw-r--r--tests/posix/exec_nopath.sh7
-rw-r--r--tests/posix/exec_plus.out (renamed from tests/test_exec_plus.out)0
-rw-r--r--tests/posix/exec_plus.sh1
-rw-r--r--tests/posix/exec_plus_nonexistent.out (renamed from tests/test_uid_minus_plus.out)0
-rw-r--r--tests/posix/exec_plus_nonexistent.sh5
-rw-r--r--tests/posix/exec_plus_semicolon.out (renamed from tests/test_exec_plus_semicolon.out)0
-rw-r--r--tests/posix/exec_plus_semicolon.sh5
-rw-r--r--tests/posix/exec_plus_status.out (renamed from tests/test_uid_name.out)0
-rw-r--r--tests/posix/exec_plus_status.sh4
-rw-r--r--tests/posix/extra_paren.sh1
-rw-r--r--tests/posix/flag_comma.out (renamed from tests/test_flag_comma.out)0
-rw-r--r--tests/posix/flag_comma.sh3
-rw-r--r--tests/posix/flag_weird_names.out (renamed from tests/test_flag_weird_names.out)0
-rw-r--r--tests/posix/flag_weird_names.sh2
-rw-r--r--tests/posix/group_id.out (renamed from tests/test_uid_plus.out)0
-rw-r--r--tests/posix/group_id.sh1
-rw-r--r--tests/posix/group_name.out (renamed from tests/test_uid_plus_plus.out)0
-rw-r--r--tests/posix/group_name.sh1
-rw-r--r--tests/posix/group_nogroup.out (renamed from tests/test_unique_depth.out)0
-rw-r--r--tests/posix/group_nogroup.sh2
-rw-r--r--tests/posix/implicit_and.out (renamed from tests/test_implicit_and.out)0
-rw-r--r--tests/posix/implicit_and.sh1
-rw-r--r--tests/posix/incomplete.sh1
-rw-r--r--tests/posix/links.out (renamed from tests/test_links_plus.out)0
-rw-r--r--tests/posix/links.sh1
-rw-r--r--tests/posix/links_minus.out (renamed from tests/test_links_minus.out)0
-rw-r--r--tests/posix/links_minus.sh1
-rw-r--r--tests/posix/links_plus.out (renamed from tests/test_samefile.out)0
-rw-r--r--tests/posix/links_plus.sh1
-rw-r--r--tests/posix/missing_paren.sh1
-rw-r--r--tests/posix/name.out (renamed from tests/test_name_star_star.out)0
-rw-r--r--tests/posix/name.sh1
-rw-r--r--tests/posix/name_backslash.out (renamed from tests/test_perm_leading_plus_symbolic.out)0
-rw-r--r--tests/posix/name_backslash.sh2
-rw-r--r--tests/posix/name_bracket.out (renamed from tests/test_name_bracket.out)0
-rw-r--r--tests/posix/name_bracket.sh5
-rw-r--r--tests/posix/name_character_class.out (renamed from tests/test_name_character_class.out)0
-rw-r--r--tests/posix/name_character_class.sh1
-rw-r--r--tests/posix/name_double_backslash.out (renamed from tests/test_name_double_backslash.out)0
-rw-r--r--tests/posix/name_double_backslash.sh2
-rw-r--r--tests/posix/name_root.out (renamed from tests/test_name_root.out)0
-rw-r--r--tests/posix/name_root.sh1
-rw-r--r--tests/posix/name_root_depth.out (renamed from tests/test_quit.out)0
-rw-r--r--tests/posix/name_root_depth.sh1
-rw-r--r--tests/posix/name_star_star.out (renamed from tests/test_parens.out)0
-rw-r--r--tests/posix/name_star_star.sh1
-rw-r--r--tests/posix/name_trailing_slash.out (renamed from tests/test_name_trailing_slash.out)0
-rw-r--r--tests/posix/name_trailing_slash.sh1
-rw-r--r--tests/posix/newer.out (renamed from tests/test_newerma.out)0
-rw-r--r--tests/posix/newer.sh1
-rw-r--r--tests/posix/newer_link.out (renamed from tests/test_newer_link.out)0
-rw-r--r--tests/posix/newer_link.sh1
-rw-r--r--tests/posix/nogroup.out (renamed from tests/test_perm_leading_plus_symbolic_minus.out)0
-rw-r--r--tests/posix/nogroup.sh1
-rw-r--r--tests/posix/nogroup_ulimit.out (renamed from tests/test_perm_symbolic.out)0
-rw-r--r--tests/posix/nogroup_ulimit.sh3
-rw-r--r--tests/posix/not_prune.out (renamed from tests/test_not_prune.out)0
-rw-r--r--tests/posix/not_prune.sh1
-rw-r--r--tests/posix/nouser.out (renamed from tests/test_printf_empty.out)0
-rw-r--r--tests/posix/nouser.sh1
-rw-r--r--tests/posix/nouser_ulimit.out (renamed from tests/test_printf_w.out)0
-rw-r--r--tests/posix/nouser_ulimit.sh3
-rw-r--r--tests/posix/o.out (renamed from tests/test_or.out)0
-rw-r--r--tests/posix/o.sh1
-rw-r--r--tests/posix/ok_stdin.out (renamed from tests/test_ok_stdin.out)0
-rw-r--r--tests/posix/ok_stdin.sh3
-rw-r--r--tests/posix/or_purity.out (renamed from tests/test_quit_before_print.out)0
-rw-r--r--tests/posix/or_purity.sh2
-rw-r--r--tests/posix/parens.out (renamed from tests/test_regextype_grep.out)0
-rw-r--r--tests/posix/parens.sh1
-rw-r--r--tests/posix/path.out (renamed from tests/test_wholename.out)0
-rw-r--r--tests/posix/path.sh1
-rw-r--r--tests/posix/perm_000.out (renamed from tests/test_perm_000.out)0
-rw-r--r--tests/posix/perm_000.sh1
-rw-r--r--tests/posix/perm_000_minus.out (renamed from tests/test_perm_000_slash.out)0
-rw-r--r--tests/posix/perm_000_minus.sh1
-rw-r--r--tests/posix/perm_222.out (renamed from tests/test_perm_222.out)0
-rw-r--r--tests/posix/perm_222.sh1
-rw-r--r--tests/posix/perm_222_minus.out (renamed from tests/test_perm_222_minus.out)0
-rw-r--r--tests/posix/perm_222_minus.sh1
-rw-r--r--tests/posix/perm_644.out (renamed from tests/test_perm_644.out)0
-rw-r--r--tests/posix/perm_644.sh1
-rw-r--r--tests/posix/perm_644_minus.out (renamed from tests/test_perm_644_minus.out)0
-rw-r--r--tests/posix/perm_644_minus.sh1
-rw-r--r--tests/posix/perm_leading_plus_symbolic_minus.out (renamed from tests/test_size_big.out)0
-rw-r--r--tests/posix/perm_leading_plus_symbolic_minus.sh1
-rw-r--r--tests/posix/perm_setid.out (renamed from tests/test_perm_setid.out)0
-rw-r--r--tests/posix/perm_setid.sh1
-rw-r--r--tests/posix/perm_sticky.out (renamed from tests/test_perm_sticky.out)0
-rw-r--r--tests/posix/perm_sticky.sh1
-rw-r--r--tests/posix/perm_symbolic.out (renamed from tests/test_xtype_reorder.out)0
-rw-r--r--tests/posix/perm_symbolic.sh1
-rw-r--r--tests/posix/perm_symbolic_minus.out (renamed from tests/test_perm_symbolic_minus.out)0
-rw-r--r--tests/posix/perm_symbolic_minus.sh1
-rw-r--r--tests/posix/permcopy.out (renamed from tests/test_permcopy.out)0
-rw-r--r--tests/posix/permcopy.sh1
-rw-r--r--tests/posix/prune.out (renamed from tests/test_prune.out)0
-rw-r--r--tests/posix/prune.sh1
-rw-r--r--tests/posix/prune_file.out (renamed from tests/test_prune_file.out)0
-rw-r--r--tests/posix/prune_file.sh1
-rw-r--r--tests/posix/prune_or_print.out (renamed from tests/test_prune_or_print.out)0
-rw-r--r--tests/posix/prune_or_print.sh1
-rw-r--r--tests/posix/size.out (renamed from tests/test_size.out)0
-rw-r--r--tests/posix/size.sh1
-rw-r--r--tests/posix/size_bytes.out (renamed from tests/test_size_bytes.out)0
-rw-r--r--tests/posix/size_bytes.sh1
-rw-r--r--tests/posix/size_plus.out (renamed from tests/test_size_plus.out)0
-rw-r--r--tests/posix/size_plus.sh1
-rw-r--r--tests/posix/type_bind_mount.out (renamed from tests/test_type_bind_mount.out)0
-rw-r--r--tests/posix/type_bind_mount.sh12
-rw-r--r--tests/posix/type_d.out (renamed from tests/test_type_d.out)0
-rw-r--r--tests/posix/type_d.sh1
-rw-r--r--tests/posix/type_f.out (renamed from tests/test_type_f.out)0
-rw-r--r--tests/posix/type_f.sh1
-rw-r--r--tests/posix/type_l.out (renamed from tests/test_type_l.out)0
-rw-r--r--tests/posix/type_l.sh1
-rw-r--r--tests/posix/user_id.out (renamed from tests/test_user_id.out)0
-rw-r--r--tests/posix/user_id.sh1
-rw-r--r--tests/posix/user_name.out (renamed from tests/test_user_name.out)0
-rw-r--r--tests/posix/user_name.sh1
-rw-r--r--tests/posix/user_nouser.out (renamed from tests/test_user_nouser.out)0
-rw-r--r--tests/posix/user_nouser.sh2
-rw-r--r--tests/posix/weird_names.out (renamed from tests/test_weird_names.out)0
-rw-r--r--tests/posix/weird_names.sh2
-rw-r--r--tests/posix/xdev.out (renamed from tests/test_xdev.out)0
-rw-r--r--tests/posix/xdev.sh13
-rw-r--r--tests/test_fprint_duplicate_stdout.out38
-rwxr-xr-xtests/tests.sh2847
773 files changed, 931 insertions, 2877 deletions
diff --git a/docs/BUILDING.md b/docs/BUILDING.md
index 932845b..5219160 100644
--- a/docs/BUILDING.md
+++ b/docs/BUILDING.md
@@ -107,27 +107,28 @@ Testing
$ make check
-Most of the testsuite is implemented in the file [`tests.sh`](/tests.sh).
-This script contains hundreds of separate test cases.
-Most of them are *snapshot tests* which compare `bfs`'s output to a known-good copy saved under [`tests`](/tests).
+The test harness is implemented in the file [`tests/tests.sh`](/tests/tests.sh).
+Individual test cases are found in `tests/*/*.sh`.
+Most of them are *snapshot tests* which compare `bfs`'s output to a known-good copy saved under the matching `tests/*/*.out`.
You can pass the name of a particular test case (or a few) to run just those tests.
For example:
- $ ./tests/tests.sh test_basic
+ $ ./tests/tests.sh posix/basic
If you need to update the reference snapshot, pass `--update`.
It can be handy to generate the snapshot with a different `find` implementation to ensure the output is correct, for example:
- $ ./tests/tests.sh test_basic --bfs=find --update
+ $ ./tests/tests.sh posix/basic --bfs=find --update
But keep in mind, other `find` implementations may not be correct.
To my knowledge, no other implementation passes even the POSIX-compatible subset of the tests:
$ ./tests/tests.sh --bfs=find --posix
...
- tests passed: 89
- tests failed: 5
+ tests passed: 90
+ tests skipped: 3
+ tests failed: 6
Run
diff --git a/docs/HACKING.md b/docs/HACKING.md
index 08ddac2..c9bbe14 100644
--- a/docs/HACKING.md
+++ b/docs/HACKING.md
@@ -30,18 +30,26 @@ Tests
`bfs` includes an extensive test suite.
See the [build documentation](BUILDING.md#testing) for details on running the tests.
+Test cases are grouped by the standard or `find` implementation that supports the tested feature(s):
+
+| Group | Description |
+|---------------------------------|---------------------------------------|
+| [`tests/posix`](/tests/posix) | POSIX compatibility tests |
+| [`tests/bsd`](/tests/bsd) | BSD `find` features |
+| [`tests/gnu`](/tests/gnu) | GNU `find` features |
+| [`tests/common`](/tests/common) | Features common to BSD and GNU `find` |
+| [`tests/bfs`](/tests/bfs) | `bfs`-specific tests |
+
Both new features and bug fixes should have associated tests.
-To add a test, create a new function in `tests.sh` called `test_<something>`.
+To add a test, create a new `*.sh` file in the appropriate group.
Snapshot tests use the `bfs_diff` function to automatically compare the generated and expected outputs.
For example,
```bash
-function test_something() {
- bfs_diff basic -name something
-}
+# posix/something.sh
+bfs_diff basic -name something
```
`basic` is one of the directory trees generated for test cases; others include `links`, `loops`, `deep`, and `rainbow`.
-Run `./tests/tests.sh test_something --update` to generate the reference snapshot (and don't forget to `git add` it).
-Finally, add the test case to one of the arrays `posix_tests`, `bsd_tests`, `gnu_tests`, or `bfs_tests`, depending on which `find` implementations it should be compatible with.
+Run `./tests/tests.sh posix/something --update` to generate the reference snapshot (and don't forget to `git add` it).
diff --git a/docs/USAGE.md b/docs/USAGE.md
index e2cff44..4f5db98 100644
--- a/docs/USAGE.md
+++ b/docs/USAGE.md
@@ -78,17 +78,17 @@ $ bfs -name '*.md' -and -name '*ING'`
There are other operators like `-or`:
```console
-$ bfs -name '*.md' -or -name '*.sh'
+$ bfs -name '*.md' -or -name 'bfs.*'
./README.md
-./tests/find-color.sh
-./tests/ls-color.sh
-./tests/remove-sibling.sh
-./tests/sort-args.sh
-./tests/tests.sh
+./completions/bfs.bash
+./completions/bfs.fish
+./completions/bfs.zsh
+./docs/BUILDING.md
./docs/CHANGELOG.md
./docs/HACKING.md
-./docs/BUILDING.md
./docs/USAGE.md
+./docs/bfs.1
+./src/bfs.h
```
and `-not`:
diff --git a/tests/test_D_all.out b/tests/bfs/D_all.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_D_all.out
+++ b/tests/bfs/D_all.out
diff --git a/tests/bfs/D_all.sh b/tests/bfs/D_all.sh
new file mode 100644
index 0000000..170698a
--- /dev/null
+++ b/tests/bfs/D_all.sh
@@ -0,0 +1 @@
+bfs_diff -D all basic
diff --git a/tests/test_D_multi.out b/tests/bfs/D_multi.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_D_multi.out
+++ b/tests/bfs/D_multi.out
diff --git a/tests/bfs/D_multi.sh b/tests/bfs/D_multi.sh
new file mode 100644
index 0000000..08a8ca6
--- /dev/null
+++ b/tests/bfs/D_multi.sh
@@ -0,0 +1 @@
+bfs_diff -D opt,tree,unknown basic
diff --git a/tests/test_L_capable.out b/tests/bfs/L_capable.out
index e5ba3c7..e5ba3c7 100644
--- a/tests/test_L_capable.out
+++ b/tests/bfs/L_capable.out
diff --git a/tests/bfs/L_capable.sh b/tests/bfs/L_capable.sh
new file mode 100644
index 0000000..e1806b7
--- /dev/null
+++ b/tests/bfs/L_capable.sh
@@ -0,0 +1,12 @@
+skip_unless test "$SUDO"
+skip_unless test "$UNAME" = "Linux"
+
+rm -rf scratch/*
+
+skip_unless invoke_bfs scratch -quit -capable
+
+$TOUCH scratch/{normal,capable}
+sudo setcap all+ep scratch/capable
+ln -s capable scratch/link
+
+bfs_diff -L scratch -capable
diff --git a/tests/test_L_unique.out b/tests/bfs/L_unique.out
index c94c48e..c94c48e 100644
--- a/tests/test_L_unique.out
+++ b/tests/bfs/L_unique.out
diff --git a/tests/bfs/L_unique.sh b/tests/bfs/L_unique.sh
new file mode 100644
index 0000000..c804526
--- /dev/null
+++ b/tests/bfs/L_unique.sh
@@ -0,0 +1 @@
+bfs_diff -L links/{file,symlink,hardlink} -unique
diff --git a/tests/test_L_unique_depth.out b/tests/bfs/L_unique_depth.out
index dad0a98..dad0a98 100644
--- a/tests/test_L_unique_depth.out
+++ b/tests/bfs/L_unique_depth.out
diff --git a/tests/bfs/L_unique_depth.sh b/tests/bfs/L_unique_depth.sh
new file mode 100644
index 0000000..fb9aca1
--- /dev/null
+++ b/tests/bfs/L_unique_depth.sh
@@ -0,0 +1 @@
+bfs_diff -L loops/deeply/nested -unique -depth
diff --git a/tests/test_L_unique_loops.out b/tests/bfs/L_unique_loops.out
index dad0a98..dad0a98 100644
--- a/tests/test_L_unique_loops.out
+++ b/tests/bfs/L_unique_loops.out
diff --git a/tests/bfs/L_unique_loops.sh b/tests/bfs/L_unique_loops.sh
new file mode 100644
index 0000000..2bdd94e
--- /dev/null
+++ b/tests/bfs/L_unique_loops.sh
@@ -0,0 +1 @@
+bfs_diff -L loops/deeply/nested -unique
diff --git a/tests/test_O0.out b/tests/bfs/O0.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_O0.out
+++ b/tests/bfs/O0.out
diff --git a/tests/bfs/O0.sh b/tests/bfs/O0.sh
new file mode 100644
index 0000000..0f92d71
--- /dev/null
+++ b/tests/bfs/O0.sh
@@ -0,0 +1 @@
+bfs_diff -O0 basic -not \( -type f -not -type f \)
diff --git a/tests/test_O1.out b/tests/bfs/O1.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_O1.out
+++ b/tests/bfs/O1.out
diff --git a/tests/bfs/O1.sh b/tests/bfs/O1.sh
new file mode 100644
index 0000000..924b410
--- /dev/null
+++ b/tests/bfs/O1.sh
@@ -0,0 +1 @@
+bfs_diff -O1 basic -not \( -type f -not -type f \)
diff --git a/tests/test_O2.out b/tests/bfs/O2.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_O2.out
+++ b/tests/bfs/O2.out
diff --git a/tests/bfs/O2.sh b/tests/bfs/O2.sh
new file mode 100644
index 0000000..9382456
--- /dev/null
+++ b/tests/bfs/O2.sh
@@ -0,0 +1 @@
+bfs_diff -O2 basic -not \( -type f -not -type f \)
diff --git a/tests/test_O3.out b/tests/bfs/O3.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_O3.out
+++ b/tests/bfs/O3.out
diff --git a/tests/bfs/O3.sh b/tests/bfs/O3.sh
new file mode 100644
index 0000000..5bdf2bc
--- /dev/null
+++ b/tests/bfs/O3.sh
@@ -0,0 +1 @@
+bfs_diff -O3 basic -not \( -type f -not -type f \)
diff --git a/tests/test_Ofast.out b/tests/bfs/Ofast.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_Ofast.out
+++ b/tests/bfs/Ofast.out
diff --git a/tests/bfs/Ofast.sh b/tests/bfs/Ofast.sh
new file mode 100644
index 0000000..87c1d8d
--- /dev/null
+++ b/tests/bfs/Ofast.sh
@@ -0,0 +1 @@
+bfs_diff -Ofast basic -not \( -xtype f -not -xtype f \)
diff --git a/tests/test_S_bfs.out b/tests/bfs/S_bfs.out
index bb3cd8d..bb3cd8d 100644
--- a/tests/test_S_bfs.out
+++ b/tests/bfs/S_bfs.out
diff --git a/tests/bfs/S_bfs.sh b/tests/bfs/S_bfs.sh
new file mode 100644
index 0000000..76976de
--- /dev/null
+++ b/tests/bfs/S_bfs.sh
@@ -0,0 +1,2 @@
+invoke_bfs -S bfs -s basic >"$OUT"
+diff_output
diff --git a/tests/test_S_dfs.out b/tests/bfs/S_dfs.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_S_dfs.out
+++ b/tests/bfs/S_dfs.out
diff --git a/tests/bfs/S_dfs.sh b/tests/bfs/S_dfs.sh
new file mode 100644
index 0000000..7dd7a46
--- /dev/null
+++ b/tests/bfs/S_dfs.sh
@@ -0,0 +1,2 @@
+invoke_bfs -S dfs -s basic >"$OUT"
+diff_output
diff --git a/tests/test_S_ids.out b/tests/bfs/S_ids.out
index bb3cd8d..bb3cd8d 100644
--- a/tests/test_S_ids.out
+++ b/tests/bfs/S_ids.out
diff --git a/tests/bfs/S_ids.sh b/tests/bfs/S_ids.sh
new file mode 100644
index 0000000..3995cf0
--- /dev/null
+++ b/tests/bfs/S_ids.sh
@@ -0,0 +1,2 @@
+invoke_bfs -S ids -s basic >"$OUT"
+diff_output
diff --git a/tests/bfs/and_incomplete.sh b/tests/bfs/and_incomplete.sh
new file mode 100644
index 0000000..f7bc2c3
--- /dev/null
+++ b/tests/bfs/and_incomplete.sh
@@ -0,0 +1 @@
+fail invoke_bfs -print -a
diff --git a/tests/test_capable.out b/tests/bfs/capable.out
index 78b5bd9..78b5bd9 100644
--- a/tests/test_capable.out
+++ b/tests/bfs/capable.out
diff --git a/tests/bfs/capable.sh b/tests/bfs/capable.sh
new file mode 100644
index 0000000..964f37f
--- /dev/null
+++ b/tests/bfs/capable.sh
@@ -0,0 +1,12 @@
+skip_unless test "$SUDO"
+skip_unless test "$UNAME" = "Linux"
+
+rm -rf scratch/*
+
+skip_unless invoke_bfs scratch -quit -capable
+
+$TOUCH scratch/{normal,capable}
+sudo setcap all+ep scratch/capable
+ln -s capable scratch/link
+
+bfs_diff scratch -capable
diff --git a/tests/test_color.out b/tests/bfs/color.out
index 77fc8a8..77fc8a8 100644
--- a/tests/test_color.out
+++ b/tests/bfs/color.out
diff --git a/tests/bfs/color.sh b/tests/bfs/color.sh
new file mode 100644
index 0000000..23f05a3
--- /dev/null
+++ b/tests/bfs/color.sh
@@ -0,0 +1 @@
+bfs_diff rainbow -color
diff --git a/tests/test_color_L.out b/tests/bfs/color_L.out
index b60dd4a..b60dd4a 100644
--- a/tests/test_color_L.out
+++ b/tests/bfs/color_L.out
diff --git a/tests/bfs/color_L.sh b/tests/bfs/color_L.sh
new file mode 100644
index 0000000..823db62
--- /dev/null
+++ b/tests/bfs/color_L.sh
@@ -0,0 +1 @@
+bfs_diff -L rainbow -color
diff --git a/tests/test_color_L_ln_target.out b/tests/bfs/color_L_ln_target.out
index cd4ec5e..cd4ec5e 100644
--- a/tests/test_color_L_ln_target.out
+++ b/tests/bfs/color_L_ln_target.out
diff --git a/tests/bfs/color_L_ln_target.sh b/tests/bfs/color_L_ln_target.sh
new file mode 100644
index 0000000..cc5991d
--- /dev/null
+++ b/tests/bfs/color_L_ln_target.sh
@@ -0,0 +1 @@
+LS_COLORS="ln=target:or=01;31:mi=01;33:" bfs_diff -L rainbow -color
diff --git a/tests/test_color_L_no_stat.out b/tests/bfs/color_L_no_stat.out
index c0bb1be..c0bb1be 100644
--- a/tests/test_color_L_no_stat.out
+++ b/tests/bfs/color_L_no_stat.out
diff --git a/tests/bfs/color_L_no_stat.sh b/tests/bfs/color_L_no_stat.sh
new file mode 100644
index 0000000..0a2caf0
--- /dev/null
+++ b/tests/bfs/color_L_no_stat.sh
@@ -0,0 +1 @@
+LS_COLORS="mh=0:ex=0:sg=0:su=0:st=0:ow=0:tw=0:*.txt=01:" bfs_diff -L rainbow -color
diff --git a/tests/test_color_escapes.out b/tests/bfs/color_escapes.out
index b71e138..b71e138 100644
--- a/tests/test_color_escapes.out
+++ b/tests/bfs/color_escapes.out
diff --git a/tests/bfs/color_escapes.sh b/tests/bfs/color_escapes.sh
new file mode 100644
index 0000000..eb5817f
--- /dev/null
+++ b/tests/bfs/color_escapes.sh
@@ -0,0 +1 @@
+LS_COLORS="lc=\e[:rc=\155\::ec=^[\x5B\x6d:" bfs_diff rainbow -color
diff --git a/tests/test_color_ext.out b/tests/bfs/color_ext.out
index cf26e73..cf26e73 100644
--- a/tests/test_color_ext.out
+++ b/tests/bfs/color_ext.out
diff --git a/tests/bfs/color_ext.sh b/tests/bfs/color_ext.sh
new file mode 100644
index 0000000..c9f6d46
--- /dev/null
+++ b/tests/bfs/color_ext.sh
@@ -0,0 +1 @@
+LS_COLORS="*.txt=01:" bfs_diff rainbow -color
diff --git a/tests/test_color_ext0.out b/tests/bfs/color_ext0.out
index e764a6b..e764a6b 100644
--- a/tests/test_color_ext0.out
+++ b/tests/bfs/color_ext0.out
diff --git a/tests/bfs/color_ext0.sh b/tests/bfs/color_ext0.sh
new file mode 100644
index 0000000..371a9c5
--- /dev/null
+++ b/tests/bfs/color_ext0.sh
@@ -0,0 +1 @@
+LS_COLORS="*.txt=00:" bfs_diff rainbow -color
diff --git a/tests/test_color_ext_override.out b/tests/bfs/color_ext_override.out
index 1377b65..1377b65 100644
--- a/tests/test_color_ext_override.out
+++ b/tests/bfs/color_ext_override.out
diff --git a/tests/bfs/color_ext_override.sh b/tests/bfs/color_ext_override.sh
new file mode 100644
index 0000000..ac4c7fb
--- /dev/null
+++ b/tests/bfs/color_ext_override.sh
@@ -0,0 +1 @@
+LS_COLORS="*.tar.gz=01;31:*.TAR=01;32:*.gz=01;33:" bfs_diff rainbow -color
diff --git a/tests/test_color_ext_underride.out b/tests/bfs/color_ext_underride.out
index 787248a..787248a 100644
--- a/tests/test_color_ext_underride.out
+++ b/tests/bfs/color_ext_underride.out
diff --git a/tests/bfs/color_ext_underride.sh b/tests/bfs/color_ext_underride.sh
new file mode 100644
index 0000000..fb12e01
--- /dev/null
+++ b/tests/bfs/color_ext_underride.sh
@@ -0,0 +1 @@
+LS_COLORS="*.gz=01;33:*.TAR=01;32:*.tar.gz=01;31:" bfs_diff rainbow -color
diff --git a/tests/test_color_ln_target.out b/tests/bfs/color_ln_target.out
index cd4ec5e..cd4ec5e 100644
--- a/tests/test_color_ln_target.out
+++ b/tests/bfs/color_ln_target.out
diff --git a/tests/bfs/color_ln_target.sh b/tests/bfs/color_ln_target.sh
new file mode 100644
index 0000000..707d25e
--- /dev/null
+++ b/tests/bfs/color_ln_target.sh
@@ -0,0 +1 @@
+LS_COLORS="ln=target:or=01;31:mi=01;33:" bfs_diff rainbow -color
diff --git a/tests/test_color_ls.out b/tests/bfs/color_ls.out
index b08d894..b08d894 100644
--- a/tests/test_color_ls.out
+++ b/tests/bfs/color_ls.out
diff --git a/tests/bfs/color_ls.sh b/tests/bfs/color_ls.sh
new file mode 100644
index 0000000..79e1db2
--- /dev/null
+++ b/tests/bfs/color_ls.sh
@@ -0,0 +1,15 @@
+rm -rf scratch/*
+touchp scratch/foo/bar/baz
+ln -s foo/bar/baz scratch/link
+ln -s foo/bar/nowhere scratch/broken
+ln -s foo/bar/nowhere/nothing scratch/nested
+ln -s foo/bar/baz/qux scratch/notdir
+ln -s scratch/foo/bar scratch/relative
+mkdir scratch/__bfs__
+ln -s /__bfs__/nowhere scratch/absolute
+
+LS_COLORS="or=01;31:" invoke_bfs scratch/{,link,broken,nested,notdir,relative,absolute} -color -type l -ls \
+ | sed 's/.* -> //' \
+ | sort >"$OUT"
+
+diff_output
diff --git a/tests/test_color_mh.out b/tests/bfs/color_mh.out
index 757a6a1..757a6a1 100644
--- a/tests/test_color_mh.out
+++ b/tests/bfs/color_mh.out
diff --git a/tests/bfs/color_mh.sh b/tests/bfs/color_mh.sh
new file mode 100644
index 0000000..aff1845
--- /dev/null
+++ b/tests/bfs/color_mh.sh
@@ -0,0 +1 @@
+LS_COLORS="mh=01:" bfs_diff rainbow -color
diff --git a/tests/test_color_mh0.out b/tests/bfs/color_mh0.out
index 77fc8a8..77fc8a8 100644
--- a/tests/test_color_mh0.out
+++ b/tests/bfs/color_mh0.out
diff --git a/tests/bfs/color_mh0.sh b/tests/bfs/color_mh0.sh
new file mode 100644
index 0000000..7de880d
--- /dev/null
+++ b/tests/bfs/color_mh0.sh
@@ -0,0 +1 @@
+LS_COLORS="mh=00:" bfs_diff rainbow -color
diff --git a/tests/test_color_mi.out b/tests/bfs/color_mi.out
index 77fc8a8..77fc8a8 100644
--- a/tests/test_color_mi.out
+++ b/tests/bfs/color_mi.out
diff --git a/tests/bfs/color_mi.sh b/tests/bfs/color_mi.sh
new file mode 100644
index 0000000..06dd8c6
--- /dev/null
+++ b/tests/bfs/color_mi.sh
@@ -0,0 +1 @@
+LS_COLORS="mi=01:" bfs_diff rainbow -color
diff --git a/tests/test_color_missing_colon.out b/tests/bfs/color_missing_colon.out
index cf26e73..cf26e73 100644
--- a/tests/test_color_missing_colon.out
+++ b/tests/bfs/color_missing_colon.out
diff --git a/tests/bfs/color_missing_colon.sh b/tests/bfs/color_missing_colon.sh
new file mode 100644
index 0000000..afa3763
--- /dev/null
+++ b/tests/bfs/color_missing_colon.sh
@@ -0,0 +1 @@
+LS_COLORS="*.txt=01" bfs_diff rainbow -color
diff --git a/tests/test_color_no_stat.out b/tests/bfs/color_no_stat.out
index 1fc5324..1fc5324 100644
--- a/tests/test_color_no_stat.out
+++ b/tests/bfs/color_no_stat.out
diff --git a/tests/bfs/color_no_stat.sh b/tests/bfs/color_no_stat.sh
new file mode 100644
index 0000000..0bc2520
--- /dev/null
+++ b/tests/bfs/color_no_stat.sh
@@ -0,0 +1 @@
+LS_COLORS="mh=0:ex=0:sg=0:su=0:st=0:ow=0:tw=0:*.txt=01:" bfs_diff rainbow -color
diff --git a/tests/test_color_nul.out b/tests/bfs/color_nul.out
index c328f82..c328f82 100644
--- a/tests/test_color_nul.out
+++ b/tests/bfs/color_nul.out
Binary files differ
diff --git a/tests/bfs/color_nul.sh b/tests/bfs/color_nul.sh
new file mode 100644
index 0000000..4979569
--- /dev/null
+++ b/tests/bfs/color_nul.sh
@@ -0,0 +1,2 @@
+LS_COLORS="ec=\33[m\0:" invoke_bfs rainbow -color -maxdepth 0 >"$OUT"
+diff_output
diff --git a/tests/test_color_or.out b/tests/bfs/color_or.out
index 9e1fe5c..9e1fe5c 100644
--- a/tests/test_color_or.out
+++ b/tests/bfs/color_or.out
diff --git a/tests/bfs/color_or.sh b/tests/bfs/color_or.sh
new file mode 100644
index 0000000..bccb400
--- /dev/null
+++ b/tests/bfs/color_or.sh
@@ -0,0 +1 @@
+LS_COLORS="or=01:" bfs_diff rainbow -color
diff --git a/tests/test_color_or0_mi.out b/tests/bfs/color_or0_mi.out
index 77fc8a8..77fc8a8 100644
--- a/tests/test_color_or0_mi.out
+++ b/tests/bfs/color_or0_mi.out
diff --git a/tests/bfs/color_or0_mi.sh b/tests/bfs/color_or0_mi.sh
new file mode 100644
index 0000000..a362cf1
--- /dev/null
+++ b/tests/bfs/color_or0_mi.sh
@@ -0,0 +1 @@
+LS_COLORS="or=00:mi=01;33:" bfs_diff rainbow -color
diff --git a/tests/test_color_or0_mi0.out b/tests/bfs/color_or0_mi0.out
index 77fc8a8..77fc8a8 100644
--- a/tests/test_color_or0_mi0.out
+++ b/tests/bfs/color_or0_mi0.out
diff --git a/tests/bfs/color_or0_mi0.sh b/tests/bfs/color_or0_mi0.sh
new file mode 100644
index 0000000..d7c00f6
--- /dev/null
+++ b/tests/bfs/color_or0_mi0.sh
@@ -0,0 +1 @@
+LS_COLORS="or=00:mi=00:" bfs_diff rainbow -color
diff --git a/tests/test_color_or_mi.out b/tests/bfs/color_or_mi.out
index 5667f56..5667f56 100644
--- a/tests/test_color_or_mi.out
+++ b/tests/bfs/color_or_mi.out
diff --git a/tests/bfs/color_or_mi.sh b/tests/bfs/color_or_mi.sh
new file mode 100644
index 0000000..467ce6b
--- /dev/null
+++ b/tests/bfs/color_or_mi.sh
@@ -0,0 +1 @@
+LS_COLORS="or=01;31:mi=01;33:" bfs_diff rainbow -color
diff --git a/tests/test_color_or_mi0.out b/tests/bfs/color_or_mi0.out
index 5667f56..5667f56 100644
--- a/tests/test_color_or_mi0.out
+++ b/tests/bfs/color_or_mi0.out
diff --git a/tests/bfs/color_or_mi0.sh b/tests/bfs/color_or_mi0.sh
new file mode 100644
index 0000000..a9c36bf
--- /dev/null
+++ b/tests/bfs/color_or_mi0.sh
@@ -0,0 +1 @@
+LS_COLORS="or=01;31:mi=00:" bfs_diff rainbow -color
diff --git a/tests/test_color_rs_lc_rc_ec.out b/tests/bfs/color_rs_lc_rc_ec.out
index d39bbe7..d39bbe7 100644
--- a/tests/test_color_rs_lc_rc_ec.out
+++ b/tests/bfs/color_rs_lc_rc_ec.out
diff --git a/tests/bfs/color_rs_lc_rc_ec.sh b/tests/bfs/color_rs_lc_rc_ec.sh
new file mode 100644
index 0000000..467b2da
--- /dev/null
+++ b/tests/bfs/color_rs_lc_rc_ec.sh
@@ -0,0 +1 @@
+LS_COLORS="rs=RS:lc=LC:rc=RC:ec=EC:" bfs_diff rainbow -color
diff --git a/tests/test_color_st0_tw0_ow.out b/tests/bfs/color_st0_tw0_ow.out
index 9a47ef2..9a47ef2 100644
--- a/tests/test_color_st0_tw0_ow.out
+++ b/tests/bfs/color_st0_tw0_ow.out
diff --git a/tests/bfs/color_st0_tw0_ow.sh b/tests/bfs/color_st0_tw0_ow.sh
new file mode 100644
index 0000000..8e2b8e3
--- /dev/null
+++ b/tests/bfs/color_st0_tw0_ow.sh
@@ -0,0 +1 @@
+LS_COLORS="st=00:tw=00:ow=34;42:" bfs_diff rainbow -color
diff --git a/tests/test_color_st0_tw0_ow0.out b/tests/bfs/color_st0_tw0_ow0.out
index 2b86fe4..2b86fe4 100644
--- a/tests/test_color_st0_tw0_ow0.out
+++ b/tests/bfs/color_st0_tw0_ow0.out
diff --git a/tests/bfs/color_st0_tw0_ow0.sh b/tests/bfs/color_st0_tw0_ow0.sh
new file mode 100644
index 0000000..c5d5fe7
--- /dev/null
+++ b/tests/bfs/color_st0_tw0_ow0.sh
@@ -0,0 +1 @@
+LS_COLORS="st=00:tw=00:ow=00:" bfs_diff rainbow -color
diff --git a/tests/test_color_st0_tw_ow.out b/tests/bfs/color_st0_tw_ow.out
index 42549a1..42549a1 100644
--- a/tests/test_color_st0_tw_ow.out
+++ b/tests/bfs/color_st0_tw_ow.out
diff --git a/tests/bfs/color_st0_tw_ow.sh b/tests/bfs/color_st0_tw_ow.sh
new file mode 100644
index 0000000..8fd9605
--- /dev/null
+++ b/tests/bfs/color_st0_tw_ow.sh
@@ -0,0 +1 @@
+LS_COLORS="st=00:tw=40;32:ow=34;42:" bfs_diff rainbow -color
diff --git a/tests/test_color_st0_tw_ow0.out b/tests/bfs/color_st0_tw_ow0.out
index 535b8ae..535b8ae 100644
--- a/tests/test_color_st0_tw_ow0.out
+++ b/tests/bfs/color_st0_tw_ow0.out
diff --git a/tests/bfs/color_st0_tw_ow0.sh b/tests/bfs/color_st0_tw_ow0.sh
new file mode 100644
index 0000000..68c63dc
--- /dev/null
+++ b/tests/bfs/color_st0_tw_ow0.sh
@@ -0,0 +1 @@
+LS_COLORS="st=00:tw=40;32:ow=00:" bfs_diff rainbow -color
diff --git a/tests/test_color_st_tw0_ow.out b/tests/bfs/color_st_tw0_ow.out
index c9a86f4..c9a86f4 100644
--- a/tests/test_color_st_tw0_ow.out
+++ b/tests/bfs/color_st_tw0_ow.out
diff --git a/tests/bfs/color_st_tw0_ow.sh b/tests/bfs/color_st_tw0_ow.sh
new file mode 100644
index 0000000..be16251
--- /dev/null
+++ b/tests/bfs/color_st_tw0_ow.sh
@@ -0,0 +1 @@
+LS_COLORS="st=37;44:tw=00:ow=34;42:" bfs_diff rainbow -color
diff --git a/tests/test_color_st_tw0_ow0.out b/tests/bfs/color_st_tw0_ow0.out
index 2d94f3a..2d94f3a 100644
--- a/tests/test_color_st_tw0_ow0.out
+++ b/tests/bfs/color_st_tw0_ow0.out
diff --git a/tests/bfs/color_st_tw0_ow0.sh b/tests/bfs/color_st_tw0_ow0.sh
new file mode 100644
index 0000000..f869e7c
--- /dev/null
+++ b/tests/bfs/color_st_tw0_ow0.sh
@@ -0,0 +1 @@
+LS_COLORS="st=37;44:tw=00:ow=00:" bfs_diff rainbow -color
diff --git a/tests/test_color_st_tw_ow0.out b/tests/bfs/color_st_tw_ow0.out
index 317ef90..317ef90 100644
--- a/tests/test_color_st_tw_ow0.out
+++ b/tests/bfs/color_st_tw_ow0.out
diff --git a/tests/bfs/color_st_tw_ow0.sh b/tests/bfs/color_st_tw_ow0.sh
new file mode 100644
index 0000000..99a17a6
--- /dev/null
+++ b/tests/bfs/color_st_tw_ow0.sh
@@ -0,0 +1 @@
+LS_COLORS="st=37;44:tw=40;32:ow=00:" bfs_diff rainbow -color
diff --git a/tests/test_color_star.out b/tests/bfs/color_star.out
index 77fc8a8..77fc8a8 100644
--- a/tests/test_color_star.out
+++ b/tests/bfs/color_star.out
diff --git a/tests/bfs/color_star.sh b/tests/bfs/color_star.sh
new file mode 100644
index 0000000..3ada4fd
--- /dev/null
+++ b/tests/bfs/color_star.sh
@@ -0,0 +1,2 @@
+# Regression test: don't segfault on LS_COLORS="*"
+LS_COLORS="*" bfs_diff rainbow -color
diff --git a/tests/test_color_su0_sg.out b/tests/bfs/color_su0_sg.out
index 8b8c8b8..8b8c8b8 100644
--- a/tests/test_color_su0_sg.out
+++ b/tests/bfs/color_su0_sg.out
diff --git a/tests/bfs/color_su0_sg.sh b/tests/bfs/color_su0_sg.sh
new file mode 100644
index 0000000..f5f57b4
--- /dev/null
+++ b/tests/bfs/color_su0_sg.sh
@@ -0,0 +1 @@
+LS_COLORS="su=00:sg=30;43:" bfs_diff rainbow -color
diff --git a/tests/test_color_su0_sg0.out b/tests/bfs/color_su0_sg0.out
index 0cd5f9a..0cd5f9a 100644
--- a/tests/test_color_su0_sg0.out
+++ b/tests/bfs/color_su0_sg0.out
diff --git a/tests/bfs/color_su0_sg0.sh b/tests/bfs/color_su0_sg0.sh
new file mode 100644
index 0000000..0198383
--- /dev/null
+++ b/tests/bfs/color_su0_sg0.sh
@@ -0,0 +1 @@
+LS_COLORS="su=00:sg=00:" bfs_diff rainbow -color
diff --git a/tests/test_color_su_sg0.out b/tests/bfs/color_su_sg0.out
index a9e8c5d..a9e8c5d 100644
--- a/tests/test_color_su_sg0.out
+++ b/tests/bfs/color_su_sg0.out
diff --git a/tests/bfs/color_su_sg0.sh b/tests/bfs/color_su_sg0.sh
new file mode 100644
index 0000000..8dc6984
--- /dev/null
+++ b/tests/bfs/color_su_sg0.sh
@@ -0,0 +1 @@
+LS_COLORS="su=37;41:sg=00:" bfs_diff rainbow -color
diff --git a/tests/bfs/comma_incomplete.sh b/tests/bfs/comma_incomplete.sh
new file mode 100644
index 0000000..07cf505
--- /dev/null
+++ b/tests/bfs/comma_incomplete.sh
@@ -0,0 +1 @@
+fail invoke_bfs -print ,
diff --git a/tests/test_basic.out b/tests/bfs/data_flow_hidden.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_basic.out
+++ b/tests/bfs/data_flow_hidden.out
diff --git a/tests/bfs/data_flow_hidden.sh b/tests/bfs/data_flow_hidden.sh
new file mode 100644
index 0000000..6afaab2
--- /dev/null
+++ b/tests/bfs/data_flow_hidden.sh
@@ -0,0 +1 @@
+bfs_diff basic \( -hidden -not -hidden \) -o \( -hidden -o -not -hidden \)
diff --git a/tests/test_deep.out b/tests/bfs/deep_strict.out
index c385fce..c385fce 100644
--- a/tests/test_deep.out
+++ b/tests/bfs/deep_strict.out
diff --git a/tests/bfs/deep_strict.sh b/tests/bfs/deep_strict.sh
new file mode 100644
index 0000000..e057310
--- /dev/null
+++ b/tests/bfs/deep_strict.sh
@@ -0,0 +1,5 @@
+closefrom 4
+
+# Not even enough fds to keep the root open
+ulimit -n 7
+bfs_diff deep -type f -exec bash -c 'echo "${1:0:6}/.../${1##*/} (${#1})"' bash {} \;
diff --git a/tests/test_exclude_depth.out b/tests/bfs/exclude_depth.out
index 59e3c42..59e3c42 100644
--- a/tests/test_exclude_depth.out
+++ b/tests/bfs/exclude_depth.out
diff --git a/tests/bfs/exclude_depth.sh b/tests/bfs/exclude_depth.sh
new file mode 100644
index 0000000..437b4dd
--- /dev/null
+++ b/tests/bfs/exclude_depth.sh
@@ -0,0 +1 @@
+bfs_diff basic -depth -exclude -name foo
diff --git a/tests/bfs/exclude_exclude.sh b/tests/bfs/exclude_exclude.sh
new file mode 100644
index 0000000..c687623
--- /dev/null
+++ b/tests/bfs/exclude_exclude.sh
@@ -0,0 +1 @@
+fail invoke_bfs basic -exclude -exclude -name foo
diff --git a/tests/test_L_ilname.out b/tests/bfs/exclude_mindepth.out
index e69de29..e69de29 100644
--- a/tests/test_L_ilname.out
+++ b/tests/bfs/exclude_mindepth.out
diff --git a/tests/bfs/exclude_mindepth.sh b/tests/bfs/exclude_mindepth.sh
new file mode 100644
index 0000000..c8f70f9
--- /dev/null
+++ b/tests/bfs/exclude_mindepth.sh
@@ -0,0 +1 @@
+bfs_diff basic -mindepth 3 -exclude -name foo
diff --git a/tests/test_exclude_name.out b/tests/bfs/exclude_name.out
index 59e3c42..59e3c42 100644
--- a/tests/test_exclude_name.out
+++ b/tests/bfs/exclude_name.out
diff --git a/tests/bfs/exclude_name.sh b/tests/bfs/exclude_name.sh
new file mode 100644
index 0000000..7cf9f33
--- /dev/null
+++ b/tests/bfs/exclude_name.sh
@@ -0,0 +1 @@
+bfs_diff basic -exclude -name foo
diff --git a/tests/bfs/exclude_print.sh b/tests/bfs/exclude_print.sh
new file mode 100644
index 0000000..52ff0fd
--- /dev/null
+++ b/tests/bfs/exclude_print.sh
@@ -0,0 +1 @@
+fail invoke_bfs basic -exclude -print
diff --git a/tests/test_exec_flush_fprint.out b/tests/bfs/exec_flush_fprint.out
index 511198f..511198f 100644
--- a/tests/test_exec_flush_fprint.out
+++ b/tests/bfs/exec_flush_fprint.out
diff --git a/tests/bfs/exec_flush_fprint.sh b/tests/bfs/exec_flush_fprint.sh
new file mode 100644
index 0000000..9756b6a
--- /dev/null
+++ b/tests/bfs/exec_flush_fprint.sh
@@ -0,0 +1,3 @@
+# Even non-stdstreams should be flushed
+rm -rf scratch/*
+bfs_diff basic/a -fprint scratch/foo -exec cat scratch/foo \;
diff --git a/tests/bfs/exec_flush_fprint_fail.sh b/tests/bfs/exec_flush_fprint_fail.sh
new file mode 100644
index 0000000..5da944a
--- /dev/null
+++ b/tests/bfs/exec_flush_fprint_fail.sh
@@ -0,0 +1,2 @@
+skip_unless test -e /dev/full
+fail invoke_bfs basic/a -fprint /dev/full -exec true \;
diff --git a/tests/test_execdir_plus.out b/tests/bfs/execdir_plus.out
index 8866a8f..8866a8f 100644
--- a/tests/test_execdir_plus.out
+++ b/tests/bfs/execdir_plus.out
diff --git a/tests/bfs/execdir_plus.sh b/tests/bfs/execdir_plus.sh
new file mode 100644
index 0000000..9ae7764
--- /dev/null
+++ b/tests/bfs/execdir_plus.sh
@@ -0,0 +1,7 @@
+tree=$(invoke_bfs -D tree 2>&1 -quit)
+
+if [[ "$tree" == *"-S dfs"* ]]; then
+ skip
+fi
+
+bfs_diff basic -execdir "$TESTS/sort-args.sh" {} +
diff --git a/tests/test_closed_stdin.out b/tests/bfs/execdir_plus_nonexistent.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_closed_stdin.out
+++ b/tests/bfs/execdir_plus_nonexistent.out
diff --git a/tests/bfs/execdir_plus_nonexistent.sh b/tests/bfs/execdir_plus_nonexistent.sh
new file mode 100644
index 0000000..8436953
--- /dev/null
+++ b/tests/bfs/execdir_plus_nonexistent.sh
@@ -0,0 +1,5 @@
+stderr=$(invoke_bfs basic -execdir "$TESTS/nonexistent" {} + 2>&1 >/dev/null)
+[ -n "$stderr" ] || return 1
+
+bfs_diff basic -execdir "$TESTS/nonexistent" {} + -print
+(($? == EX_BFS))
diff --git a/tests/test_H_type_l.out b/tests/bfs/expr_flag_path.out
index e67f10b..e67f10b 100644
--- a/tests/test_H_type_l.out
+++ b/tests/bfs/expr_flag_path.out
diff --git a/tests/bfs/expr_flag_path.sh b/tests/bfs/expr_flag_path.sh
new file mode 100644
index 0000000..bb89d92
--- /dev/null
+++ b/tests/bfs/expr_flag_path.sh
@@ -0,0 +1 @@
+bfs_diff -type l -H links/skip
diff --git a/tests/test_expr_flag_path.out b/tests/bfs/expr_path_flag.out
index e67f10b..e67f10b 100644
--- a/tests/test_expr_flag_path.out
+++ b/tests/bfs/expr_path_flag.out
diff --git a/tests/bfs/expr_path_flag.sh b/tests/bfs/expr_path_flag.sh
new file mode 100644
index 0000000..818e5d1
--- /dev/null
+++ b/tests/bfs/expr_path_flag.sh
@@ -0,0 +1 @@
+bfs_diff -type l links/skip -H
diff --git a/tests/test_expr_path_flag.out b/tests/bfs/flag_expr_path.out
index e67f10b..e67f10b 100644
--- a/tests/test_expr_path_flag.out
+++ b/tests/bfs/flag_expr_path.out
diff --git a/tests/bfs/flag_expr_path.sh b/tests/bfs/flag_expr_path.sh
new file mode 100644
index 0000000..a414e10
--- /dev/null
+++ b/tests/bfs/flag_expr_path.sh
@@ -0,0 +1 @@
+bfs_diff -H -type l links/skip
diff --git a/tests/test_fprint_append.out b/tests/bfs/fprint_duplicate_stdout.out
index 6c21751..6c21751 100644
--- a/tests/test_fprint_append.out
+++ b/tests/bfs/fprint_duplicate_stdout.out
diff --git a/tests/bfs/fprint_duplicate_stdout.sh b/tests/bfs/fprint_duplicate_stdout.sh
new file mode 100644
index 0000000..4e95e30
--- /dev/null
+++ b/tests/bfs/fprint_duplicate_stdout.sh
@@ -0,0 +1,3 @@
+invoke_bfs basic -fprint "$OUT" -print >"$OUT"
+sort_output
+diff_output
diff --git a/tests/bfs/fprint_error_stderr.sh b/tests/bfs/fprint_error_stderr.sh
new file mode 100644
index 0000000..427808f
--- /dev/null
+++ b/tests/bfs/fprint_error_stderr.sh
@@ -0,0 +1,2 @@
+skip_unless test -e /dev/full
+fail invoke_bfs basic -maxdepth 0 -fprint /dev/full 2>/dev/full
diff --git a/tests/bfs/fprint_error_stdout.sh b/tests/bfs/fprint_error_stdout.sh
new file mode 100644
index 0000000..fbdc1d0
--- /dev/null
+++ b/tests/bfs/fprint_error_stdout.sh
@@ -0,0 +1,2 @@
+skip_unless test -e /dev/full
+fail invoke_bfs basic -maxdepth 0 -fprint /dev/full >/dev/full
diff --git a/tests/bfs/help.sh b/tests/bfs/help.sh
new file mode 100644
index 0000000..5e5c684
--- /dev/null
+++ b/tests/bfs/help.sh
@@ -0,0 +1,6 @@
+invoke_bfs -help | grep -E '\{...?\}' && return 1
+invoke_bfs -D help | grep -E '\{...?\}' && return 1
+invoke_bfs -S help | grep -E '\{...?\}' && return 1
+invoke_bfs -regextype help | grep -E '\{...?\}' && return 1
+
+return 0
diff --git a/tests/test_hidden.out b/tests/bfs/hidden.out
index e65ede9..e65ede9 100644
--- a/tests/test_hidden.out
+++ b/tests/bfs/hidden.out
diff --git a/tests/bfs/hidden.sh b/tests/bfs/hidden.sh
new file mode 100644
index 0000000..b0413c5
--- /dev/null
+++ b/tests/bfs/hidden.sh
@@ -0,0 +1 @@
+bfs_diff weirdnames -hidden
diff --git a/tests/test_hidden_root.out b/tests/bfs/hidden_root.out
index 8c1371b..8c1371b 100644
--- a/tests/test_hidden_root.out
+++ b/tests/bfs/hidden_root.out
diff --git a/tests/bfs/hidden_root.sh b/tests/bfs/hidden_root.sh
new file mode 100644
index 0000000..905c5b5
--- /dev/null
+++ b/tests/bfs/hidden_root.sh
@@ -0,0 +1,2 @@
+cd weirdnames
+bfs_diff . ./. ... ./... .../.. -hidden
diff --git a/tests/bfs/links_empty.sh b/tests/bfs/links_empty.sh
new file mode 100644
index 0000000..34c7c25
--- /dev/null
+++ b/tests/bfs/links_empty.sh
@@ -0,0 +1 @@
+fail invoke_bfs links -links ''
diff --git a/tests/bfs/links_invalid.sh b/tests/bfs/links_invalid.sh
new file mode 100644
index 0000000..ff69fa6
--- /dev/null
+++ b/tests/bfs/links_invalid.sh
@@ -0,0 +1 @@
+fail invoke_bfs links -links ASDF
diff --git a/tests/bfs/links_negative.sh b/tests/bfs/links_negative.sh
new file mode 100644
index 0000000..b5d9c58
--- /dev/null
+++ b/tests/bfs/links_negative.sh
@@ -0,0 +1 @@
+fail invoke_bfs links -links +-1
diff --git a/tests/bfs/links_noarg.sh b/tests/bfs/links_noarg.sh
new file mode 100644
index 0000000..5dede5f
--- /dev/null
+++ b/tests/bfs/links_noarg.sh
@@ -0,0 +1 @@
+fail invoke_bfs links -links
diff --git a/tests/bfs/newerma_nonexistent.sh b/tests/bfs/newerma_nonexistent.sh
new file mode 100644
index 0000000..7f3695f
--- /dev/null
+++ b/tests/bfs/newerma_nonexistent.sh
@@ -0,0 +1 @@
+fail invoke_bfs times -newerma basic/nonexistent
diff --git a/tests/bfs/newermq.sh b/tests/bfs/newermq.sh
new file mode 100644
index 0000000..2a22586
--- /dev/null
+++ b/tests/bfs/newermq.sh
@@ -0,0 +1 @@
+fail invoke_bfs times -newermq times/a
diff --git a/tests/bfs/newermt_invalid.sh b/tests/bfs/newermt_invalid.sh
new file mode 100644
index 0000000..61d2485
--- /dev/null
+++ b/tests/bfs/newermt_invalid.sh
@@ -0,0 +1 @@
+fail invoke_bfs times -newermt not_a_date_time
diff --git a/tests/bfs/newerqm.sh b/tests/bfs/newerqm.sh
new file mode 100644
index 0000000..da84350
--- /dev/null
+++ b/tests/bfs/newerqm.sh
@@ -0,0 +1 @@
+fail invoke_bfs times -newerqm times/a
diff --git a/tests/test_nohidden.out b/tests/bfs/nohidden.out
index d3ec901..d3ec901 100644
--- a/tests/test_nohidden.out
+++ b/tests/bfs/nohidden.out
diff --git a/tests/bfs/nohidden.sh b/tests/bfs/nohidden.sh
new file mode 100644
index 0000000..e3a3e4a
--- /dev/null
+++ b/tests/bfs/nohidden.sh
@@ -0,0 +1 @@
+bfs_diff weirdnames -nohidden
diff --git a/tests/test_nohidden_depth.out b/tests/bfs/nohidden_depth.out
index d3ec901..d3ec901 100644
--- a/tests/test_nohidden_depth.out
+++ b/tests/bfs/nohidden_depth.out
diff --git a/tests/bfs/nohidden_depth.sh b/tests/bfs/nohidden_depth.sh
new file mode 100644
index 0000000..9fd7017
--- /dev/null
+++ b/tests/bfs/nohidden_depth.sh
@@ -0,0 +1 @@
+bfs_diff weirdnames -depth -nohidden
diff --git a/tests/bfs/nowarn.sh b/tests/bfs/nowarn.sh
new file mode 100644
index 0000000..d9f9ab3
--- /dev/null
+++ b/tests/bfs/nowarn.sh
@@ -0,0 +1,2 @@
+stderr=$(invoke_bfs basic -nowarn -depth -prune 2>&1 >/dev/null)
+[ -z "$stderr" ]
diff --git a/tests/test_ok_plus_semicolon.out b/tests/bfs/ok_plus_semicolon.out
index 2a3e14f..2a3e14f 100644
--- a/tests/test_ok_plus_semicolon.out
+++ b/tests/bfs/ok_plus_semicolon.out
diff --git a/tests/bfs/ok_plus_semicolon.sh b/tests/bfs/ok_plus_semicolon.sh
new file mode 100644
index 0000000..57d6103
--- /dev/null
+++ b/tests/bfs/ok_plus_semicolon.sh
@@ -0,0 +1,8 @@
+# The -ok primary shall be equivalent to -exec, except that the use of a
+# <plus-sign> to punctuate the end of the primary expression need not be
+# supported, ...
+#
+# bfs chooses not to support it, for compatibility with most other find
+# implementations.
+
+yes | bfs_diff basic -ok echo {} + \;
diff --git a/tests/test_okdir_plus_semicolon.out b/tests/bfs/okdir_plus_semicolon.out
index 1909d27..1909d27 100644
--- a/tests/test_okdir_plus_semicolon.out
+++ b/tests/bfs/okdir_plus_semicolon.out
diff --git a/tests/bfs/okdir_plus_semicolon.sh b/tests/bfs/okdir_plus_semicolon.sh
new file mode 100644
index 0000000..d316bd7
--- /dev/null
+++ b/tests/bfs/okdir_plus_semicolon.sh
@@ -0,0 +1 @@
+yes | bfs_diff basic -okdir echo {} + \;
diff --git a/tests/bfs/or_incomplete.sh b/tests/bfs/or_incomplete.sh
new file mode 100644
index 0000000..c941b95
--- /dev/null
+++ b/tests/bfs/or_incomplete.sh
@@ -0,0 +1 @@
+fail invoke_bfs -print -o
diff --git a/tests/test_flag_expr_path.out b/tests/bfs/path_expr_flag.out
index e67f10b..e67f10b 100644
--- a/tests/test_flag_expr_path.out
+++ b/tests/bfs/path_expr_flag.out
diff --git a/tests/bfs/path_expr_flag.sh b/tests/bfs/path_expr_flag.sh
new file mode 100644
index 0000000..7cfa1cd
--- /dev/null
+++ b/tests/bfs/path_expr_flag.sh
@@ -0,0 +1 @@
+bfs_diff links/skip -type l -H
diff --git a/tests/test_path_expr_flag.out b/tests/bfs/path_flag_expr.out
index e67f10b..e67f10b 100644
--- a/tests/test_path_expr_flag.out
+++ b/tests/bfs/path_flag_expr.out
diff --git a/tests/bfs/path_flag_expr.sh b/tests/bfs/path_flag_expr.sh
new file mode 100644
index 0000000..ca00c8c
--- /dev/null
+++ b/tests/bfs/path_flag_expr.sh
@@ -0,0 +1 @@
+bfs_diff links/skip -H -type l
diff --git a/tests/test_L_lname.out b/tests/bfs/perm_leading_plus_symbolic.out
index e69de29..e69de29 100644
--- a/tests/test_L_lname.out
+++ b/tests/bfs/perm_leading_plus_symbolic.out
diff --git a/tests/bfs/perm_leading_plus_symbolic.sh b/tests/bfs/perm_leading_plus_symbolic.sh
new file mode 100644
index 0000000..4202ac1
--- /dev/null
+++ b/tests/bfs/perm_leading_plus_symbolic.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm +rwx
diff --git a/tests/bfs/perm_symbolic_double_comma.sh b/tests/bfs/perm_symbolic_double_comma.sh
new file mode 100644
index 0000000..66db0ac
--- /dev/null
+++ b/tests/bfs/perm_symbolic_double_comma.sh
@@ -0,0 +1 @@
+fail invoke_bfs perms -perm a+r,,u+w
diff --git a/tests/bfs/perm_symbolic_missing_action.sh b/tests/bfs/perm_symbolic_missing_action.sh
new file mode 100644
index 0000000..3b18721
--- /dev/null
+++ b/tests/bfs/perm_symbolic_missing_action.sh
@@ -0,0 +1 @@
+fail invoke_bfs perms -perm a
diff --git a/tests/bfs/perm_symbolic_trailing_comma.sh b/tests/bfs/perm_symbolic_trailing_comma.sh
new file mode 100644
index 0000000..c52ebe6
--- /dev/null
+++ b/tests/bfs/perm_symbolic_trailing_comma.sh
@@ -0,0 +1 @@
+fail invoke_bfs perms -perm a+r,
diff --git a/tests/test_printf_color.out b/tests/bfs/printf_color.out
index d9cd1a4..d9cd1a4 100644
--- a/tests/test_printf_color.out
+++ b/tests/bfs/printf_color.out
diff --git a/tests/bfs/printf_color.sh b/tests/bfs/printf_color.sh
new file mode 100644
index 0000000..7bb38c2
--- /dev/null
+++ b/tests/bfs/printf_color.sh
@@ -0,0 +1 @@
+bfs_diff -color -path './rainbow*' -printf '%H %h %f %p %P %l\n'
diff --git a/tests/bfs/printf_duplicate_flag.sh b/tests/bfs/printf_duplicate_flag.sh
new file mode 100644
index 0000000..77650d0
--- /dev/null
+++ b/tests/bfs/printf_duplicate_flag.sh
@@ -0,0 +1 @@
+fail invoke_bfs basic -printf '%--p'
diff --git a/tests/bfs/printf_everything.sh b/tests/bfs/printf_everything.sh
new file mode 100644
index 0000000..58996ab
--- /dev/null
+++ b/tests/bfs/printf_everything.sh
@@ -0,0 +1,9 @@
+everything=(%{a,b,c,d,D,f,F,g,G,h,H,i,k,l,m,M,n,p,P,s,S,t,u,U,y,Y})
+everything+=(%{A,C,T}{%,+,@,a,A,b,B,c,C,d,D,e,F,g,G,h,H,I,j,k,l,m,M,n,p,r,R,s,S,t,T,u,U,V,w,W,x,X,y,Y,z,Z})
+
+# Check if we have birth times
+if ! fail invoke_bfs basic -printf '%w' -quit >/dev/null; then
+ everything+=(%w %{B,W}{%,+,@,a,A,b,B,c,C,d,D,e,F,g,G,h,H,I,j,k,l,m,M,n,p,r,R,s,S,t,T,u,U,V,w,W,x,X,y,Y,z,Z})
+fi
+
+invoke_bfs rainbow -printf "${everything[*]}\n" >/dev/null
diff --git a/tests/bfs/printf_incomplete_escape.sh b/tests/bfs/printf_incomplete_escape.sh
new file mode 100644
index 0000000..144add5
--- /dev/null
+++ b/tests/bfs/printf_incomplete_escape.sh
@@ -0,0 +1 @@
+fail invoke_bfs basic -printf '\'
diff --git a/tests/bfs/printf_incomplete_format.sh b/tests/bfs/printf_incomplete_format.sh
new file mode 100644
index 0000000..347a0f4
--- /dev/null
+++ b/tests/bfs/printf_incomplete_format.sh
@@ -0,0 +1 @@
+fail invoke_bfs basic -printf '%'
diff --git a/tests/bfs/printf_invalid_escape.sh b/tests/bfs/printf_invalid_escape.sh
new file mode 100644
index 0000000..ce12233
--- /dev/null
+++ b/tests/bfs/printf_invalid_escape.sh
@@ -0,0 +1 @@
+fail invoke_bfs basic -printf '\!'
diff --git a/tests/bfs/printf_invalid_format.sh b/tests/bfs/printf_invalid_format.sh
new file mode 100644
index 0000000..1717615
--- /dev/null
+++ b/tests/bfs/printf_invalid_format.sh
@@ -0,0 +1 @@
+fail invoke_bfs basic -printf '%!'
diff --git a/tests/bfs/printf_must_be_numeric.sh b/tests/bfs/printf_must_be_numeric.sh
new file mode 100644
index 0000000..eabb3d6
--- /dev/null
+++ b/tests/bfs/printf_must_be_numeric.sh
@@ -0,0 +1 @@
+fail invoke_bfs basic -printf '%+p'
diff --git a/tests/test_and_purity.out b/tests/bfs/printf_w.out
index e69de29..e69de29 100644
--- a/tests/test_and_purity.out
+++ b/tests/bfs/printf_w.out
diff --git a/tests/bfs/printf_w.sh b/tests/bfs/printf_w.sh
new file mode 100644
index 0000000..3b27ee7
--- /dev/null
+++ b/tests/bfs/printf_w.sh
@@ -0,0 +1,2 @@
+# Birth times may not be supported, so just check that %w/%W/%B can be parsed
+bfs_diff times -false -printf '%w %WY %BY\n'
diff --git a/tests/bfs/stderr_fails_loudly.sh b/tests/bfs/stderr_fails_loudly.sh
new file mode 100644
index 0000000..d8b3861
--- /dev/null
+++ b/tests/bfs/stderr_fails_loudly.sh
@@ -0,0 +1,2 @@
+skip_unless test -e /dev/full
+fail invoke_bfs -D all basic -false -fprint /dev/full 2>/dev/full
diff --git a/tests/test_d_path.out b/tests/bfs/stderr_fails_silently.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_d_path.out
+++ b/tests/bfs/stderr_fails_silently.out
diff --git a/tests/bfs/stderr_fails_silently.sh b/tests/bfs/stderr_fails_silently.sh
new file mode 100644
index 0000000..731cb02
--- /dev/null
+++ b/tests/bfs/stderr_fails_silently.sh
@@ -0,0 +1,2 @@
+skip_unless test -e /dev/full
+bfs_diff -D all basic 2>/dev/full
diff --git a/tests/test_type_multi.out b/tests/bfs/type_multi.out
index 3cae08a..3cae08a 100644
--- a/tests/test_type_multi.out
+++ b/tests/bfs/type_multi.out
diff --git a/tests/bfs/type_multi.sh b/tests/bfs/type_multi.sh
new file mode 100644
index 0000000..59992c7
--- /dev/null
+++ b/tests/bfs/type_multi.sh
@@ -0,0 +1 @@
+bfs_diff links -type f,d,c
diff --git a/tests/bfs/typo.sh b/tests/bfs/typo.sh
new file mode 100644
index 0000000..459e9fe
--- /dev/null
+++ b/tests/bfs/typo.sh
@@ -0,0 +1 @@
+invoke_bfs -dikkiq 2>&1 | grep follow >/dev/null
diff --git a/tests/bfs/unexpected_operator.sh b/tests/bfs/unexpected_operator.sh
new file mode 100644
index 0000000..b3658f6
--- /dev/null
+++ b/tests/bfs/unexpected_operator.sh
@@ -0,0 +1 @@
+fail invoke_bfs \! -o -print
diff --git a/tests/test_unique.out b/tests/bfs/unique.out
index 289cbde..289cbde 100644
--- a/tests/test_unique.out
+++ b/tests/bfs/unique.out
diff --git a/tests/bfs/unique.sh b/tests/bfs/unique.sh
new file mode 100644
index 0000000..ea8adfd
--- /dev/null
+++ b/tests/bfs/unique.sh
@@ -0,0 +1 @@
+bfs_diff links/{file,symlink,hardlink} -unique
diff --git a/tests/test_data_flow_group.out b/tests/bfs/unique_depth.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_data_flow_group.out
+++ b/tests/bfs/unique_depth.out
diff --git a/tests/bfs/unique_depth.sh b/tests/bfs/unique_depth.sh
new file mode 100644
index 0000000..c1d9716
--- /dev/null
+++ b/tests/bfs/unique_depth.sh
@@ -0,0 +1 @@
+bfs_diff basic -unique -depth
diff --git a/tests/bfs/version.sh b/tests/bfs/version.sh
new file mode 100644
index 0000000..e0417ca
--- /dev/null
+++ b/tests/bfs/version.sh
@@ -0,0 +1 @@
+invoke_bfs -version >/dev/null
diff --git a/tests/bfs/warn.sh b/tests/bfs/warn.sh
new file mode 100644
index 0000000..0f613c8
--- /dev/null
+++ b/tests/bfs/warn.sh
@@ -0,0 +1,2 @@
+stderr=$(invoke_bfs basic -warn -depth -prune 2>&1 >/dev/null)
+[ -n "$stderr" ]
diff --git a/tests/bfs/xtype_depth.sh b/tests/bfs/xtype_depth.sh
new file mode 100644
index 0000000..cd478af
--- /dev/null
+++ b/tests/bfs/xtype_depth.sh
@@ -0,0 +1,2 @@
+# Make sure -xtype is considered side-effecting for facts_when_impure
+fail invoke_bfs loops -xtype l -depth 100
diff --git a/tests/test_xtype_multi.out b/tests/bfs/xtype_multi.out
index 558e89c..558e89c 100644
--- a/tests/test_xtype_multi.out
+++ b/tests/bfs/xtype_multi.out
diff --git a/tests/bfs/xtype_multi.sh b/tests/bfs/xtype_multi.sh
new file mode 100644
index 0000000..ed20955
--- /dev/null
+++ b/tests/bfs/xtype_multi.sh
@@ -0,0 +1 @@
+bfs_diff links -xtype f,d,c
diff --git a/tests/test_data_flow_type.out b/tests/bfs/xtype_reorder.out
index e69de29..e69de29 100644
--- a/tests/test_data_flow_type.out
+++ b/tests/bfs/xtype_reorder.out
diff --git a/tests/bfs/xtype_reorder.sh b/tests/bfs/xtype_reorder.sh
new file mode 100644
index 0000000..8d75d7d
--- /dev/null
+++ b/tests/bfs/xtype_reorder.sh
@@ -0,0 +1,4 @@
+# Make sure -xtype is not reordered in front of anything -- if -xtype runs
+# before -links 100, it will report an ELOOP error
+bfs_diff loops -links 100 -xtype l
+invoke_bfs loops -links 100 -xtype l
diff --git a/tests/test_E.out b/tests/bsd/E.out
index 0f0971e..0f0971e 100644
--- a/tests/test_E.out
+++ b/tests/bsd/E.out
diff --git a/tests/bsd/E.sh b/tests/bsd/E.sh
new file mode 100644
index 0000000..5d97178
--- /dev/null
+++ b/tests/bsd/E.sh
@@ -0,0 +1,2 @@
+cd weirdnames
+bfs_diff -E . -regex '\./(\()'
diff --git a/tests/test_H_mnewer.out b/tests/bsd/H_mnewer.out
index 7f6c0dd..7f6c0dd 100644
--- a/tests/test_H_mnewer.out
+++ b/tests/bsd/H_mnewer.out
diff --git a/tests/bsd/H_mnewer.sh b/tests/bsd/H_mnewer.sh
new file mode 100644
index 0000000..94fe08b
--- /dev/null
+++ b/tests/bsd/H_mnewer.sh
@@ -0,0 +1 @@
+bfs_diff -H times -mnewer times/l
diff --git a/tests/test_L_acl.out b/tests/bsd/L_acl.out
index 1dae00a..1dae00a 100644
--- a/tests/test_L_acl.out
+++ b/tests/bsd/L_acl.out
diff --git a/tests/bsd/L_acl.sh b/tests/bsd/L_acl.sh
new file mode 100644
index 0000000..d8f5a7d
--- /dev/null
+++ b/tests/bsd/L_acl.sh
@@ -0,0 +1,9 @@
+rm -rf scratch/*
+
+skip_unless invoke_bfs scratch -quit -acl
+
+$TOUCH scratch/{normal,acl}
+skip_unless set_acl scratch/acl
+ln -s acl scratch/link
+
+bfs_diff -L scratch -acl
diff --git a/tests/test_L_xattr.out b/tests/bsd/L_xattr.out
index 12fac95..12fac95 100644
--- a/tests/test_L_xattr.out
+++ b/tests/bsd/L_xattr.out
diff --git a/tests/bsd/L_xattr.sh b/tests/bsd/L_xattr.sh
new file mode 100644
index 0000000..7c27e0d
--- /dev/null
+++ b/tests/bsd/L_xattr.sh
@@ -0,0 +1,3 @@
+skip_unless invoke_bfs scratch -quit -xattr
+skip_unless make_xattrs
+bfs_diff -L scratch -xattr
diff --git a/tests/test_L_xattrname.out b/tests/bsd/L_xattrname.out
index 4dc4836..4dc4836 100644
--- a/tests/test_L_xattrname.out
+++ b/tests/bsd/L_xattrname.out
diff --git a/tests/bsd/L_xattrname.sh b/tests/bsd/L_xattrname.sh
new file mode 100644
index 0000000..39d6a77
--- /dev/null
+++ b/tests/bsd/L_xattrname.sh
@@ -0,0 +1,11 @@
+skip_unless invoke_bfs scratch -quit -xattr
+skip_unless make_xattrs
+
+case "$UNAME" in
+ Darwin|FreeBSD)
+ bfs_diff -L scratch -xattrname bfs_test
+ ;;
+ *)
+ bfs_diff -L scratch -xattrname security.bfs_test
+ ;;
+esac
diff --git a/tests/test_X.out b/tests/bsd/X.out
index afa84f7..afa84f7 100644
--- a/tests/test_X.out
+++ b/tests/bsd/X.out
diff --git a/tests/bsd/X.sh b/tests/bsd/X.sh
new file mode 100644
index 0000000..03d9eee
--- /dev/null
+++ b/tests/bsd/X.sh
@@ -0,0 +1,2 @@
+bfs_diff -X weirdnames
+[ $? -eq $EX_BFS ]
diff --git a/tests/test_acl.out b/tests/bsd/acl.out
index ddf8446..ddf8446 100644
--- a/tests/test_acl.out
+++ b/tests/bsd/acl.out
diff --git a/tests/bsd/acl.sh b/tests/bsd/acl.sh
new file mode 100644
index 0000000..5bbfccc
--- /dev/null
+++ b/tests/bsd/acl.sh
@@ -0,0 +1,9 @@
+rm -rf scratch/*
+
+skip_unless invoke_bfs scratch -quit -acl
+
+$TOUCH scratch/{normal,acl}
+skip_unless set_acl scratch/acl
+ln -s acl scratch/link
+
+bfs_diff scratch -acl
diff --git a/tests/test_asince.out b/tests/bsd/asince.out
index 650e550..650e550 100644
--- a/tests/test_asince.out
+++ b/tests/bsd/asince.out
diff --git a/tests/bsd/asince.sh b/tests/bsd/asince.sh
new file mode 100644
index 0000000..32d5228
--- /dev/null
+++ b/tests/bsd/asince.sh
@@ -0,0 +1 @@
+bfs_diff times -asince 1991-12-14T00:01
diff --git a/tests/test_data_flow_hidden.out b/tests/bsd/d_path.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_data_flow_hidden.out
+++ b/tests/bsd/d_path.out
diff --git a/tests/bsd/d_path.sh b/tests/bsd/d_path.sh
new file mode 100644
index 0000000..010f76f
--- /dev/null
+++ b/tests/bsd/d_path.sh
@@ -0,0 +1 @@
+bfs_diff -d basic
diff --git a/tests/test_data_flow_depth.out b/tests/bsd/data_flow_depth.out
index ab127ec..ab127ec 100644
--- a/tests/test_data_flow_depth.out
+++ b/tests/bsd/data_flow_depth.out
diff --git a/tests/bsd/data_flow_depth.sh b/tests/bsd/data_flow_depth.sh
new file mode 100644
index 0000000..cd5d6b2
--- /dev/null
+++ b/tests/bsd/data_flow_depth.sh
@@ -0,0 +1 @@
+bfs_diff basic -depth +1 -depth -4
diff --git a/tests/test_data_flow_sparse.out b/tests/bsd/data_flow_sparse.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_data_flow_sparse.out
+++ b/tests/bsd/data_flow_sparse.out
diff --git a/tests/bsd/data_flow_sparse.sh b/tests/bsd/data_flow_sparse.sh
new file mode 100644
index 0000000..bd7e1f7
--- /dev/null
+++ b/tests/bsd/data_flow_sparse.sh
@@ -0,0 +1 @@
+bfs_diff basic \( -sparse -not -sparse \) -o \( -sparse -o -not -sparse \)
diff --git a/tests/test_depth_depth_n.out b/tests/bsd/depth_depth_n.out
index 3bfc1d3..3bfc1d3 100644
--- a/tests/test_depth_depth_n.out
+++ b/tests/bsd/depth_depth_n.out
diff --git a/tests/bsd/depth_depth_n.sh b/tests/bsd/depth_depth_n.sh
new file mode 100644
index 0000000..5989b3c
--- /dev/null
+++ b/tests/bsd/depth_depth_n.sh
@@ -0,0 +1 @@
+bfs_diff basic -depth -depth 2
diff --git a/tests/test_depth_depth_n_minus.out b/tests/bsd/depth_depth_n_minus.out
index 7575ae4..7575ae4 100644
--- a/tests/test_depth_depth_n_minus.out
+++ b/tests/bsd/depth_depth_n_minus.out
diff --git a/tests/bsd/depth_depth_n_minus.sh b/tests/bsd/depth_depth_n_minus.sh
new file mode 100644
index 0000000..1d8ac79
--- /dev/null
+++ b/tests/bsd/depth_depth_n_minus.sh
@@ -0,0 +1 @@
+bfs_diff basic -depth -depth -2
diff --git a/tests/test_depth_depth_n_plus.out b/tests/bsd/depth_depth_n_plus.out
index 847995d..847995d 100644
--- a/tests/test_depth_depth_n_plus.out
+++ b/tests/bsd/depth_depth_n_plus.out
diff --git a/tests/bsd/depth_depth_n_plus.sh b/tests/bsd/depth_depth_n_plus.sh
new file mode 100644
index 0000000..64e392b
--- /dev/null
+++ b/tests/bsd/depth_depth_n_plus.sh
@@ -0,0 +1 @@
+bfs_diff basic -depth -depth +2
diff --git a/tests/test_depth_n.out b/tests/bsd/depth_n.out
index 3bfc1d3..3bfc1d3 100644
--- a/tests/test_depth_n.out
+++ b/tests/bsd/depth_n.out
diff --git a/tests/bsd/depth_n.sh b/tests/bsd/depth_n.sh
new file mode 100644
index 0000000..4852952
--- /dev/null
+++ b/tests/bsd/depth_n.sh
@@ -0,0 +1 @@
+bfs_diff basic -depth 2
diff --git a/tests/test_depth_maxdepth_1.out b/tests/bsd/depth_n_minus.out
index 7575ae4..7575ae4 100644
--- a/tests/test_depth_maxdepth_1.out
+++ b/tests/bsd/depth_n_minus.out
diff --git a/tests/bsd/depth_n_minus.sh b/tests/bsd/depth_n_minus.sh
new file mode 100644
index 0000000..192bf8a
--- /dev/null
+++ b/tests/bsd/depth_n_minus.sh
@@ -0,0 +1 @@
+bfs_diff basic -depth -2
diff --git a/tests/test_depth_n_plus.out b/tests/bsd/depth_n_plus.out
index 847995d..847995d 100644
--- a/tests/test_depth_n_plus.out
+++ b/tests/bsd/depth_n_plus.out
diff --git a/tests/bsd/depth_n_plus.sh b/tests/bsd/depth_n_plus.sh
new file mode 100644
index 0000000..858e1c4
--- /dev/null
+++ b/tests/bsd/depth_n_plus.sh
@@ -0,0 +1 @@
+bfs_diff basic -depth +2
diff --git a/tests/test_data_flow_user.out b/tests/bsd/depth_overflow.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_data_flow_user.out
+++ b/tests/bsd/depth_overflow.out
diff --git a/tests/bsd/depth_overflow.sh b/tests/bsd/depth_overflow.sh
new file mode 100644
index 0000000..4685d03
--- /dev/null
+++ b/tests/bsd/depth_overflow.sh
@@ -0,0 +1 @@
+bfs_diff basic -depth -4294967296
diff --git a/tests/test_exit.out b/tests/bsd/exit.out
index cf4d5a9..cf4d5a9 100644
--- a/tests/test_exit.out
+++ b/tests/bsd/exit.out
diff --git a/tests/bsd/exit.sh b/tests/bsd/exit.sh
new file mode 100644
index 0000000..524a75f
--- /dev/null
+++ b/tests/bsd/exit.sh
@@ -0,0 +1,11 @@
+invoke_bfs basic -name foo -exit 42
+if [ $? -ne 42 ]; then
+ return 1
+fi
+
+invoke_bfs basic -name qux -exit 42
+if [ $? -ne 0 ]; then
+ return 1
+fi
+
+bfs_diff basic/g -print -name g -exit
diff --git a/tests/test_exclude_mindepth.out b/tests/bsd/exit_no_implicit_print.out
index e69de29..e69de29 100644
--- a/tests/test_exclude_mindepth.out
+++ b/tests/bsd/exit_no_implicit_print.out
diff --git a/tests/bsd/exit_no_implicit_print.sh b/tests/bsd/exit_no_implicit_print.sh
new file mode 100644
index 0000000..c48b43c
--- /dev/null
+++ b/tests/bsd/exit_no_implicit_print.sh
@@ -0,0 +1 @@
+bfs_diff basic -not -name foo -o -exit
diff --git a/tests/test_f.out b/tests/bsd/f.out
index 77eac77..77eac77 100644
--- a/tests/test_f.out
+++ b/tests/bsd/f.out
diff --git a/tests/bsd/f.sh b/tests/bsd/f.sh
new file mode 100644
index 0000000..42d2dfd
--- /dev/null
+++ b/tests/bsd/f.sh
@@ -0,0 +1,2 @@
+cd weirdnames
+bfs_diff -f '-' -f '('
diff --git a/tests/test_flags.out b/tests/bsd/flags.out
index 11998ed..11998ed 100644
--- a/tests/test_flags.out
+++ b/tests/bsd/flags.out
diff --git a/tests/bsd/flags.sh b/tests/bsd/flags.sh
new file mode 100644
index 0000000..99d0fdb
--- /dev/null
+++ b/tests/bsd/flags.sh
@@ -0,0 +1,8 @@
+skip_unless invoke_bfs scratch -quit -flags offline
+
+rm -rf scratch/*
+
+$TOUCH scratch/{foo,bar}
+skip_unless chflags offline scratch/bar
+
+bfs_diff scratch -flags -offline,nohidden
diff --git a/tests/test_daystart.out b/tests/bsd/gid_name.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_daystart.out
+++ b/tests/bsd/gid_name.out
diff --git a/tests/bsd/gid_name.sh b/tests/bsd/gid_name.sh
new file mode 100644
index 0000000..c7e0e71
--- /dev/null
+++ b/tests/bsd/gid_name.sh
@@ -0,0 +1 @@
+bfs_diff basic -gid "$(id -gn)"
diff --git a/tests/test_H_newer.out b/tests/bsd/mnewer.out
index 7f6c0dd..7f6c0dd 100644
--- a/tests/test_H_newer.out
+++ b/tests/bsd/mnewer.out
diff --git a/tests/bsd/mnewer.sh b/tests/bsd/mnewer.sh
new file mode 100644
index 0000000..5d9f1a7
--- /dev/null
+++ b/tests/bsd/mnewer.sh
@@ -0,0 +1 @@
+bfs_diff times -mnewer times/a
diff --git a/tests/test_msince.out b/tests/bsd/msince.out
index 650e550..650e550 100644
--- a/tests/test_msince.out
+++ b/tests/bsd/msince.out
diff --git a/tests/bsd/msince.sh b/tests/bsd/msince.sh
new file mode 100644
index 0000000..ec22f02
--- /dev/null
+++ b/tests/bsd/msince.sh
@@ -0,0 +1 @@
+bfs_diff times -msince 1991-12-14T00:01
diff --git a/tests/test_mtime_units.out b/tests/bsd/mtime_units.out
index f7f63b0..f7f63b0 100644
--- a/tests/test_mtime_units.out
+++ b/tests/bsd/mtime_units.out
diff --git a/tests/bsd/mtime_units.sh b/tests/bsd/mtime_units.sh
new file mode 100644
index 0000000..a1e587e
--- /dev/null
+++ b/tests/bsd/mtime_units.sh
@@ -0,0 +1 @@
+bfs_diff times -mtime +500w400d300h200m100s
diff --git a/tests/test_okdir_stdin.out b/tests/bsd/okdir_stdin.out
index ef2a68b..ef2a68b 100644
--- a/tests/test_okdir_stdin.out
+++ b/tests/bsd/okdir_stdin.out
diff --git a/tests/bsd/okdir_stdin.sh b/tests/bsd/okdir_stdin.sh
new file mode 100644
index 0000000..7908ac0
--- /dev/null
+++ b/tests/bsd/okdir_stdin.sh
@@ -0,0 +1,2 @@
+# -okdir should *not* close stdin
+yes | bfs_diff basic -okdir bash -c 'printf "%s? " "$1" && head -n1' bash {} \;
diff --git a/tests/test_perm_000_minus.out b/tests/bsd/perm_000_plus.out
index d7494b8..d7494b8 100644
--- a/tests/test_perm_000_minus.out
+++ b/tests/bsd/perm_000_plus.out
diff --git a/tests/bsd/perm_000_plus.sh b/tests/bsd/perm_000_plus.sh
new file mode 100644
index 0000000..9ab3146
--- /dev/null
+++ b/tests/bsd/perm_000_plus.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm +000
diff --git a/tests/test_perm_222_plus.out b/tests/bsd/perm_222_plus.out
index 9a5b95a..9a5b95a 100644
--- a/tests/test_perm_222_plus.out
+++ b/tests/bsd/perm_222_plus.out
diff --git a/tests/bsd/perm_222_plus.sh b/tests/bsd/perm_222_plus.sh
new file mode 100644
index 0000000..ac3c4eb
--- /dev/null
+++ b/tests/bsd/perm_222_plus.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm +222
diff --git a/tests/test_perm_644_plus.out b/tests/bsd/perm_644_plus.out
index 7e5ae98..7e5ae98 100644
--- a/tests/test_perm_644_plus.out
+++ b/tests/bsd/perm_644_plus.out
diff --git a/tests/bsd/perm_644_plus.sh b/tests/bsd/perm_644_plus.sh
new file mode 100644
index 0000000..b3f5bc6
--- /dev/null
+++ b/tests/bsd/perm_644_plus.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm +644
diff --git a/tests/test_printx.out b/tests/bsd/printx.out
index 04bf9a9..04bf9a9 100644
--- a/tests/test_printx.out
+++ b/tests/bsd/printx.out
diff --git a/tests/bsd/printx.sh b/tests/bsd/printx.sh
new file mode 100644
index 0000000..cb24aab
--- /dev/null
+++ b/tests/bsd/printx.sh
@@ -0,0 +1 @@
+bfs_diff weirdnames -printx
diff --git a/tests/test_and_false_or_true.out b/tests/bsd/quit_implicit_print.out
index 15a13db..15a13db 100644
--- a/tests/test_and_false_or_true.out
+++ b/tests/bsd/quit_implicit_print.out
diff --git a/tests/bsd/quit_implicit_print.sh b/tests/bsd/quit_implicit_print.sh
new file mode 100644
index 0000000..ea8fd5d
--- /dev/null
+++ b/tests/bsd/quit_implicit_print.sh
@@ -0,0 +1 @@
+bfs_diff basic -name basic -o -quit
diff --git a/tests/test_delete.out b/tests/bsd/rm.out
index fb188b9..fb188b9 100644
--- a/tests/test_delete.out
+++ b/tests/bsd/rm.out
diff --git a/tests/bsd/rm.sh b/tests/bsd/rm.sh
new file mode 100644
index 0000000..2e8b613
--- /dev/null
+++ b/tests/bsd/rm.sh
@@ -0,0 +1,6 @@
+rm -rf scratch/*
+touchp scratch/foo/bar/baz
+
+(cd scratch && invoke_bfs . -rm)
+
+bfs_diff scratch
diff --git a/tests/test_s.out b/tests/bsd/s.out
index 6b790c2..6b790c2 100644
--- a/tests/test_s.out
+++ b/tests/bsd/s.out
diff --git a/tests/bsd/s.sh b/tests/bsd/s.sh
new file mode 100644
index 0000000..52f8eb3
--- /dev/null
+++ b/tests/bsd/s.sh
@@ -0,0 +1,2 @@
+invoke_bfs -s weirdnames -maxdepth 1 >"$OUT"
+diff_output
diff --git a/tests/test_size_T.out b/tests/bsd/size_T.out
index 279f3f1..279f3f1 100644
--- a/tests/test_size_T.out
+++ b/tests/bsd/size_T.out
diff --git a/tests/bsd/size_T.sh b/tests/bsd/size_T.sh
new file mode 100644
index 0000000..1023a10
--- /dev/null
+++ b/tests/bsd/size_T.sh
@@ -0,0 +1 @@
+bfs_diff basic -type f -size 1T
diff --git a/tests/test_daystart_twice.out b/tests/bsd/uid_name.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_daystart_twice.out
+++ b/tests/bsd/uid_name.out
diff --git a/tests/bsd/uid_name.sh b/tests/bsd/uid_name.sh
new file mode 100644
index 0000000..7d3ba82
--- /dev/null
+++ b/tests/bsd/uid_name.sh
@@ -0,0 +1 @@
+bfs_diff basic -uid "$(id -un)"
diff --git a/tests/test_xattr.out b/tests/bsd/xattr.out
index 109e7c9..109e7c9 100644
--- a/tests/test_xattr.out
+++ b/tests/bsd/xattr.out
diff --git a/tests/bsd/xattr.sh b/tests/bsd/xattr.sh
new file mode 100644
index 0000000..727c220
--- /dev/null
+++ b/tests/bsd/xattr.sh
@@ -0,0 +1,3 @@
+skip_unless invoke_bfs scratch -quit -xattr
+skip_unless make_xattrs
+bfs_diff scratch -xattr
diff --git a/tests/test_xattrname.out b/tests/bsd/xattrname.out
index 0285ac1..0285ac1 100644
--- a/tests/test_xattrname.out
+++ b/tests/bsd/xattrname.out
diff --git a/tests/bsd/xattrname.sh b/tests/bsd/xattrname.sh
new file mode 100644
index 0000000..6a0fe7e
--- /dev/null
+++ b/tests/bsd/xattrname.sh
@@ -0,0 +1,11 @@
+skip_unless invoke_bfs scratch -quit -xattr
+skip_unless make_xattrs
+
+case "$UNAME" in
+ Darwin|FreeBSD)
+ bfs_diff scratch -xattrname bfs_test
+ ;;
+ *)
+ bfs_diff scratch -xattrname security.bfs_test
+ ;;
+esac
diff --git a/tests/test_anewer.out b/tests/common/H_newer.out
index 7f6c0dd..7f6c0dd 100644
--- a/tests/test_anewer.out
+++ b/tests/common/H_newer.out
diff --git a/tests/common/H_newer.sh b/tests/common/H_newer.sh
new file mode 100644
index 0000000..c72bff7
--- /dev/null
+++ b/tests/common/H_newer.sh
@@ -0,0 +1 @@
+bfs_diff -H times -newer times/l
diff --git a/tests/test_H_broken.out b/tests/common/H_samefile_broken.out
index 21d6316..21d6316 100644
--- a/tests/test_H_broken.out
+++ b/tests/common/H_samefile_broken.out
diff --git a/tests/common/H_samefile_broken.sh b/tests/common/H_samefile_broken.sh
new file mode 100644
index 0000000..7a3366d
--- /dev/null
+++ b/tests/common/H_samefile_broken.sh
@@ -0,0 +1 @@
+bfs_diff -H links -samefile links/broken
diff --git a/tests/test_H_notdir.out b/tests/common/H_samefile_notdir.out
index 6e6658d..6e6658d 100644
--- a/tests/test_H_notdir.out
+++ b/tests/common/H_samefile_notdir.out
diff --git a/tests/common/H_samefile_notdir.sh b/tests/common/H_samefile_notdir.sh
new file mode 100644
index 0000000..25ad88d
--- /dev/null
+++ b/tests/common/H_samefile_notdir.sh
@@ -0,0 +1 @@
+bfs_diff -H links -samefile links/notdir
diff --git a/tests/test_H_samefile_symlink.out b/tests/common/H_samefile_symlink.out
index 996ffc8..996ffc8 100644
--- a/tests/test_H_samefile_symlink.out
+++ b/tests/common/H_samefile_symlink.out
diff --git a/tests/common/H_samefile_symlink.sh b/tests/common/H_samefile_symlink.sh
new file mode 100644
index 0000000..c73ddb2
--- /dev/null
+++ b/tests/common/H_samefile_symlink.sh
@@ -0,0 +1 @@
+bfs_diff -H links -samefile links/symlink
diff --git a/tests/test_exit_no_implicit_print.out b/tests/common/L_ilname.out
index e69de29..e69de29 100644
--- a/tests/test_exit_no_implicit_print.out
+++ b/tests/common/L_ilname.out
diff --git a/tests/common/L_ilname.sh b/tests/common/L_ilname.sh
new file mode 100644
index 0000000..cfb15a8
--- /dev/null
+++ b/tests/common/L_ilname.sh
@@ -0,0 +1,2 @@
+skip_unless invoke_bfs -quit -ilname PATTERN
+bfs_diff -L links -ilname '[AQ]'
diff --git a/tests/test_false.out b/tests/common/L_lname.out
index e69de29..e69de29 100644
--- a/tests/test_false.out
+++ b/tests/common/L_lname.out
diff --git a/tests/common/L_lname.sh b/tests/common/L_lname.sh
new file mode 100644
index 0000000..65b9da5
--- /dev/null
+++ b/tests/common/L_lname.sh
@@ -0,0 +1 @@
+bfs_diff -L links -lname '[aq]'
diff --git a/tests/common/L_ls.sh b/tests/common/L_ls.sh
new file mode 100644
index 0000000..d886791
--- /dev/null
+++ b/tests/common/L_ls.sh
@@ -0,0 +1 @@
+invoke_bfs -L rainbow -ls >scratch/L_ls.out
diff --git a/tests/test_L_mount.out b/tests/common/L_mount.out
index 2e80082..2e80082 100644
--- a/tests/test_L_mount.out
+++ b/tests/common/L_mount.out
diff --git a/tests/common/L_mount.sh b/tests/common/L_mount.sh
new file mode 100644
index 0000000..d43f95a
--- /dev/null
+++ b/tests/common/L_mount.sh
@@ -0,0 +1,15 @@
+skip_unless test "$SUDO"
+skip_if test "$UNAME" = "Darwin"
+
+rm -rf scratch/*
+mkdir scratch/{foo,mnt}
+sudo mount -t tmpfs tmpfs scratch/mnt
+ln -s ../mnt scratch/foo/bar
+$TOUCH scratch/mnt/baz
+ln -s ../mnt/baz scratch/foo/qux
+
+bfs_diff -L scratch -mount
+ret=$?
+
+sudo umount scratch/mnt
+return $ret
diff --git a/tests/test_H_samefile_broken.out b/tests/common/L_samefile_broken.out
index 21d6316..21d6316 100644
--- a/tests/test_H_samefile_broken.out
+++ b/tests/common/L_samefile_broken.out
diff --git a/tests/common/L_samefile_broken.sh b/tests/common/L_samefile_broken.sh
new file mode 100644
index 0000000..5f860cc
--- /dev/null
+++ b/tests/common/L_samefile_broken.sh
@@ -0,0 +1 @@
+bfs_diff -L links -samefile links/broken
diff --git a/tests/test_H_samefile_notdir.out b/tests/common/L_samefile_notdir.out
index 6e6658d..6e6658d 100644
--- a/tests/test_H_samefile_notdir.out
+++ b/tests/common/L_samefile_notdir.out
diff --git a/tests/common/L_samefile_notdir.sh b/tests/common/L_samefile_notdir.sh
new file mode 100644
index 0000000..9b63429
--- /dev/null
+++ b/tests/common/L_samefile_notdir.sh
@@ -0,0 +1 @@
+bfs_diff -L links -samefile links/notdir
diff --git a/tests/test_L_samefile_symlink.out b/tests/common/L_samefile_symlink.out
index 222ac78..222ac78 100644
--- a/tests/test_L_samefile_symlink.out
+++ b/tests/common/L_samefile_symlink.out
diff --git a/tests/common/L_samefile_symlink.sh b/tests/common/L_samefile_symlink.sh
new file mode 100644
index 0000000..4a7a8dd
--- /dev/null
+++ b/tests/common/L_samefile_symlink.sh
@@ -0,0 +1 @@
+bfs_diff -L links -samefile links/symlink
diff --git a/tests/test_H.out b/tests/common/P.out
index ff635ff..ff635ff 100644
--- a/tests/test_H.out
+++ b/tests/common/P.out
diff --git a/tests/common/P.sh b/tests/common/P.sh
new file mode 100644
index 0000000..a7a09d1
--- /dev/null
+++ b/tests/common/P.sh
@@ -0,0 +1 @@
+bfs_diff -P links/deeply/nested/dir
diff --git a/tests/test_H_slash.out b/tests/common/P_slash.out
index df7701b..df7701b 100644
--- a/tests/test_H_slash.out
+++ b/tests/common/P_slash.out
diff --git a/tests/common/P_slash.sh b/tests/common/P_slash.sh
new file mode 100644
index 0000000..9b9ffa0
--- /dev/null
+++ b/tests/common/P_slash.sh
@@ -0,0 +1 @@
+bfs_diff -P links/deeply/nested/dir/
diff --git a/tests/test_mnewer.out b/tests/common/anewer.out
index 7f6c0dd..7f6c0dd 100644
--- a/tests/test_mnewer.out
+++ b/tests/common/anewer.out
diff --git a/tests/common/anewer.sh b/tests/common/anewer.sh
new file mode 100644
index 0000000..0cdd759
--- /dev/null
+++ b/tests/common/anewer.sh
@@ -0,0 +1 @@
+bfs_diff times -anewer times/a
diff --git a/tests/test_delete_many.out b/tests/common/delete.out
index fb188b9..fb188b9 100644
--- a/tests/test_delete_many.out
+++ b/tests/common/delete.out
diff --git a/tests/common/delete.sh b/tests/common/delete.sh
new file mode 100644
index 0000000..4855d60
--- /dev/null
+++ b/tests/common/delete.sh
@@ -0,0 +1,7 @@
+rm -rf scratch/*
+touchp scratch/foo/bar/baz
+
+# Don't try to delete '.'
+(cd scratch && invoke_bfs . -delete)
+
+bfs_diff scratch
diff --git a/tests/test_rm.out b/tests/common/delete_many.out
index fb188b9..fb188b9 100644
--- a/tests/test_rm.out
+++ b/tests/common/delete_many.out
diff --git a/tests/common/delete_many.sh b/tests/common/delete_many.sh
new file mode 100644
index 0000000..6446f93
--- /dev/null
+++ b/tests/common/delete_many.sh
@@ -0,0 +1,8 @@
+# Test for https://github.com/tavianator/bfs/issues/67
+
+rm -rf scratch/*
+mkdir scratch/foo
+$TOUCH scratch/foo/{1..256}
+
+invoke_bfs scratch/foo -delete
+bfs_diff scratch
diff --git a/tests/test_depth_n_minus.out b/tests/common/depth_maxdepth_1.out
index 7575ae4..7575ae4 100644
--- a/tests/test_depth_n_minus.out
+++ b/tests/common/depth_maxdepth_1.out
diff --git a/tests/common/depth_maxdepth_1.sh b/tests/common/depth_maxdepth_1.sh
new file mode 100644
index 0000000..4b7e538
--- /dev/null
+++ b/tests/common/depth_maxdepth_1.sh
@@ -0,0 +1 @@
+bfs_diff basic -maxdepth 1 -depth
diff --git a/tests/test_depth_maxdepth_2.out b/tests/common/depth_maxdepth_2.out
index c53041e..c53041e 100644
--- a/tests/test_depth_maxdepth_2.out
+++ b/tests/common/depth_maxdepth_2.out
diff --git a/tests/common/depth_maxdepth_2.sh b/tests/common/depth_maxdepth_2.sh
new file mode 100644
index 0000000..2c49a65
--- /dev/null
+++ b/tests/common/depth_maxdepth_2.sh
@@ -0,0 +1 @@
+bfs_diff basic -maxdepth 2 -depth
diff --git a/tests/test_depth_mindepth_1.out b/tests/common/depth_mindepth_1.out
index 3b461cf..3b461cf 100644
--- a/tests/test_depth_mindepth_1.out
+++ b/tests/common/depth_mindepth_1.out
diff --git a/tests/common/depth_mindepth_1.sh b/tests/common/depth_mindepth_1.sh
new file mode 100644
index 0000000..868d9e1
--- /dev/null
+++ b/tests/common/depth_mindepth_1.sh
@@ -0,0 +1 @@
+bfs_diff basic -mindepth 1 -depth
diff --git a/tests/test_depth_mindepth_2.out b/tests/common/depth_mindepth_2.out
index 6ccd80a..6ccd80a 100644
--- a/tests/test_depth_mindepth_2.out
+++ b/tests/common/depth_mindepth_2.out
diff --git a/tests/common/depth_mindepth_2.sh b/tests/common/depth_mindepth_2.sh
new file mode 100644
index 0000000..2031b2c
--- /dev/null
+++ b/tests/common/depth_mindepth_2.sh
@@ -0,0 +1 @@
+bfs_diff basic -mindepth 2 -depth
diff --git a/tests/test_double_dash.out b/tests/common/double_dash.out
index 774cc7c..774cc7c 100644
--- a/tests/test_double_dash.out
+++ b/tests/common/double_dash.out
diff --git a/tests/common/double_dash.sh b/tests/common/double_dash.sh
new file mode 100644
index 0000000..1a7a118
--- /dev/null
+++ b/tests/common/double_dash.sh
@@ -0,0 +1,2 @@
+cd basic
+bfs_diff -- . -type f
diff --git a/tests/test_exec_substring.out b/tests/common/exec_substring.out
index 32a6353..32a6353 100644
--- a/tests/test_exec_substring.out
+++ b/tests/common/exec_substring.out
diff --git a/tests/common/exec_substring.sh b/tests/common/exec_substring.sh
new file mode 100644
index 0000000..5cf8e85
--- /dev/null
+++ b/tests/common/exec_substring.sh
@@ -0,0 +1 @@
+bfs_diff basic -exec echo '-{}-' \;
diff --git a/tests/test_depth.out b/tests/common/execdir_nonexistent.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_depth.out
+++ b/tests/common/execdir_nonexistent.out
diff --git a/tests/common/execdir_nonexistent.sh b/tests/common/execdir_nonexistent.sh
new file mode 100644
index 0000000..5d116e5
--- /dev/null
+++ b/tests/common/execdir_nonexistent.sh
@@ -0,0 +1,5 @@
+stderr=$(invoke_bfs basic -execdir "$TESTS/nonexistent" {} \; 2>&1 >/dev/null)
+[ -n "$stderr" ] || return 1
+
+bfs_diff basic -print -execdir "$TESTS/nonexistent" {} \; -print
+(($? == EX_BFS))
diff --git a/tests/test_execdir_pwd.out b/tests/common/execdir_pwd.out
index a11cd25..a11cd25 100644
--- a/tests/test_execdir_pwd.out
+++ b/tests/common/execdir_pwd.out
diff --git a/tests/common/execdir_pwd.sh b/tests/common/execdir_pwd.sh
new file mode 100644
index 0000000..1c0165a
--- /dev/null
+++ b/tests/common/execdir_pwd.sh
@@ -0,0 +1,3 @@
+TMP_REAL=$(cd "$TMP" && pwd)
+OFFSET=$((${#TMP_REAL} + 2))
+bfs_diff basic -execdir bash -c "pwd | cut -b$OFFSET-" \;
diff --git a/tests/test_execdir_slash.out b/tests/common/execdir_slash.out
index b498fd4..b498fd4 100644
--- a/tests/test_execdir_slash.out
+++ b/tests/common/execdir_slash.out
diff --git a/tests/common/execdir_slash.sh b/tests/common/execdir_slash.sh
new file mode 100644
index 0000000..965f679
--- /dev/null
+++ b/tests/common/execdir_slash.sh
@@ -0,0 +1,2 @@
+# Don't prepend ./ for absolute paths in -execdir
+bfs_diff / -maxdepth 0 -execdir echo {} \;
diff --git a/tests/test_execdir_slash_pwd.out b/tests/common/execdir_slash_pwd.out
index b498fd4..b498fd4 100644
--- a/tests/test_execdir_slash_pwd.out
+++ b/tests/common/execdir_slash_pwd.out
diff --git a/tests/common/execdir_slash_pwd.sh b/tests/common/execdir_slash_pwd.sh
new file mode 100644
index 0000000..9c82e09
--- /dev/null
+++ b/tests/common/execdir_slash_pwd.sh
@@ -0,0 +1 @@
+bfs_diff / -maxdepth 0 -execdir pwd \;
diff --git a/tests/test_execdir_slashes.out b/tests/common/execdir_slashes.out
index b498fd4..b498fd4 100644
--- a/tests/test_execdir_slashes.out
+++ b/tests/common/execdir_slashes.out
diff --git a/tests/common/execdir_slashes.sh b/tests/common/execdir_slashes.sh
new file mode 100644
index 0000000..4e2b327
--- /dev/null
+++ b/tests/common/execdir_slashes.sh
@@ -0,0 +1 @@
+bfs_diff /// -maxdepth 0 -execdir echo {} \;
diff --git a/tests/test_execdir_ulimit.out b/tests/common/execdir_ulimit.out
index 7f53982..7f53982 100644
--- a/tests/test_execdir_ulimit.out
+++ b/tests/common/execdir_ulimit.out
diff --git a/tests/common/execdir_ulimit.sh b/tests/common/execdir_ulimit.sh
new file mode 100644
index 0000000..6da8783
--- /dev/null
+++ b/tests/common/execdir_ulimit.sh
@@ -0,0 +1,7 @@
+rm -rf scratch/*
+mkdir -p scratch/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z
+mkdir -p scratch/a/b/c/d/e/f/g/h/i/j/k/l/m/0/1/2/3/4/5/6/7/8/9/A/B/C
+
+closefrom 4
+ulimit -n 13
+bfs_diff scratch -execdir echo {} \;
diff --git a/tests/test_flag_double_dash.out b/tests/common/flag_double_dash.out
index 774cc7c..774cc7c 100644
--- a/tests/test_flag_double_dash.out
+++ b/tests/common/flag_double_dash.out
diff --git a/tests/common/flag_double_dash.sh b/tests/common/flag_double_dash.sh
new file mode 100644
index 0000000..1075b06
--- /dev/null
+++ b/tests/common/flag_double_dash.sh
@@ -0,0 +1,2 @@
+cd basic
+bfs_diff -L -- . -type f
diff --git a/tests/test_L.out b/tests/common/follow.out
index ec9e861..ec9e861 100644
--- a/tests/test_L.out
+++ b/tests/common/follow.out
diff --git a/tests/common/follow.sh b/tests/common/follow.sh
new file mode 100644
index 0000000..b5a2ae1
--- /dev/null
+++ b/tests/common/follow.sh
@@ -0,0 +1 @@
+bfs_diff links -follow
diff --git a/tests/test_ilname.out b/tests/common/ilname.out
index e69de29..e69de29 100644
--- a/tests/test_ilname.out
+++ b/tests/common/ilname.out
diff --git a/tests/common/ilname.sh b/tests/common/ilname.sh
new file mode 100644
index 0000000..7ab0793
--- /dev/null
+++ b/tests/common/ilname.sh
@@ -0,0 +1,2 @@
+skip_unless invoke_bfs -quit -ilname PATTERN
+bfs_diff links -ilname '[AQ]'
diff --git a/tests/test_iname.out b/tests/common/iname.out
index a9e5d42..a9e5d42 100644
--- a/tests/test_iname.out
+++ b/tests/common/iname.out
diff --git a/tests/common/iname.sh b/tests/common/iname.sh
new file mode 100644
index 0000000..8fcc443
--- /dev/null
+++ b/tests/common/iname.sh
@@ -0,0 +1,2 @@
+skip_unless invoke_bfs -quit -iname PATTERN
+bfs_diff basic -iname '*F*'
diff --git a/tests/test_inum.out b/tests/common/inum.out
index ebcaf79..ebcaf79 100644
--- a/tests/test_inum.out
+++ b/tests/common/inum.out
diff --git a/tests/common/inum.sh b/tests/common/inum.sh
new file mode 100644
index 0000000..ca63bbb
--- /dev/null
+++ b/tests/common/inum.sh
@@ -0,0 +1 @@
+bfs_diff basic -inum "$(inum basic/k/foo/bar)"
diff --git a/tests/test_inum_bind_mount.out b/tests/common/inum_bind_mount.out
index a520de3..a520de3 100644
--- a/tests/test_inum_bind_mount.out
+++ b/tests/common/inum_bind_mount.out
diff --git a/tests/common/inum_bind_mount.sh b/tests/common/inum_bind_mount.sh
new file mode 100644
index 0000000..654e762
--- /dev/null
+++ b/tests/common/inum_bind_mount.sh
@@ -0,0 +1,12 @@
+skip_unless test "$SUDO"
+skip_unless test "$UNAME" = "Linux"
+
+rm -rf scratch/*
+$TOUCH scratch/{foo,bar}
+sudo mount --bind scratch/{foo,bar}
+
+bfs_diff scratch -inum "$(inum scratch/bar)"
+ret=$?
+
+sudo umount scratch/bar
+return $ret
diff --git a/tests/test_inum_automount.out b/tests/common/inum_mount.out
index 99c7511..99c7511 100644
--- a/tests/test_inum_automount.out
+++ b/tests/common/inum_mount.out
diff --git a/tests/common/inum_mount.sh b/tests/common/inum_mount.sh
new file mode 100644
index 0000000..8f5c317
--- /dev/null
+++ b/tests/common/inum_mount.sh
@@ -0,0 +1,12 @@
+skip_unless test "$SUDO"
+skip_if test "$UNAME" = "Darwin"
+
+rm -rf scratch/*
+mkdir scratch/{foo,mnt}
+sudo mount -t tmpfs tmpfs scratch/mnt
+
+bfs_diff scratch -inum "$(inum scratch/mnt)"
+ret=$?
+
+sudo umount scratch/mnt
+return $ret
diff --git a/tests/test_ipath.out b/tests/common/ipath.out
index ae1ae21..ae1ae21 100644
--- a/tests/test_ipath.out
+++ b/tests/common/ipath.out
diff --git a/tests/common/ipath.sh b/tests/common/ipath.sh
new file mode 100644
index 0000000..9e6f8c5
--- /dev/null
+++ b/tests/common/ipath.sh
@@ -0,0 +1,2 @@
+skip_unless invoke_bfs -quit -ipath PATTERN
+bfs_diff basic -ipath 'basic/*F*'
diff --git a/tests/test_iregex.out b/tests/common/iregex.out
index cfc113b..cfc113b 100644
--- a/tests/test_iregex.out
+++ b/tests/common/iregex.out
diff --git a/tests/common/iregex.sh b/tests/common/iregex.sh
new file mode 100644
index 0000000..fc782f5
--- /dev/null
+++ b/tests/common/iregex.sh
@@ -0,0 +1 @@
+bfs_diff basic -iregex 'basic/[A-Z]/[a-z]'
diff --git a/tests/test_lname.out b/tests/common/lname.out
index e69de29..e69de29 100644
--- a/tests/test_lname.out
+++ b/tests/common/lname.out
diff --git a/tests/common/lname.sh b/tests/common/lname.sh
new file mode 100644
index 0000000..cf8a2a1
--- /dev/null
+++ b/tests/common/lname.sh
@@ -0,0 +1 @@
+bfs_diff links -lname '[aq]'
diff --git a/tests/common/ls.sh b/tests/common/ls.sh
new file mode 100644
index 0000000..b49a2d5
--- /dev/null
+++ b/tests/common/ls.sh
@@ -0,0 +1 @@
+invoke_bfs rainbow -ls >scratch/ls.out
diff --git a/tests/test_maxdepth.out b/tests/common/maxdepth.out
index 7575ae4..7575ae4 100644
--- a/tests/test_maxdepth.out
+++ b/tests/common/maxdepth.out
diff --git a/tests/common/maxdepth.sh b/tests/common/maxdepth.sh
new file mode 100644
index 0000000..bb47cc9
--- /dev/null
+++ b/tests/common/maxdepth.sh
@@ -0,0 +1 @@
+bfs_diff basic -maxdepth 1
diff --git a/tests/test_mindepth.out b/tests/common/mindepth.out
index 3b461cf..3b461cf 100644
--- a/tests/test_mindepth.out
+++ b/tests/common/mindepth.out
diff --git a/tests/common/mindepth.sh b/tests/common/mindepth.sh
new file mode 100644
index 0000000..22d7770
--- /dev/null
+++ b/tests/common/mindepth.sh
@@ -0,0 +1 @@
+bfs_diff basic -mindepth 1
diff --git a/tests/test_mount.out b/tests/common/mount.out
index f7839fb..f7839fb 100644
--- a/tests/test_mount.out
+++ b/tests/common/mount.out
diff --git a/tests/common/mount.sh b/tests/common/mount.sh
new file mode 100644
index 0000000..6e20d90
--- /dev/null
+++ b/tests/common/mount.sh
@@ -0,0 +1,13 @@
+skip_unless test "$SUDO"
+skip_if test "$UNAME" = "Darwin"
+
+rm -rf scratch/*
+mkdir scratch/{foo,mnt}
+sudo mount -t tmpfs tmpfs scratch/mnt
+$TOUCH scratch/foo/bar scratch/mnt/baz
+
+bfs_diff scratch -mount
+ret=$?
+
+sudo umount scratch/mnt
+return $ret
diff --git a/tests/test_name_slash.out b/tests/common/name_slash.out
index b498fd4..b498fd4 100644
--- a/tests/test_name_slash.out
+++ b/tests/common/name_slash.out
diff --git a/tests/common/name_slash.sh b/tests/common/name_slash.sh
new file mode 100644
index 0000000..8d89623
--- /dev/null
+++ b/tests/common/name_slash.sh
@@ -0,0 +1 @@
+bfs_diff / -maxdepth 0 -name /
diff --git a/tests/test_name_slashes.out b/tests/common/name_slashes.out
index 187b81f..187b81f 100644
--- a/tests/test_name_slashes.out
+++ b/tests/common/name_slashes.out
diff --git a/tests/common/name_slashes.sh b/tests/common/name_slashes.sh
new file mode 100644
index 0000000..78d0a84
--- /dev/null
+++ b/tests/common/name_slashes.sh
@@ -0,0 +1 @@
+bfs_diff /// -maxdepth 0 -name /
diff --git a/tests/test_newer.out b/tests/common/newerma.out
index 7f6c0dd..7f6c0dd 100644
--- a/tests/test_newer.out
+++ b/tests/common/newerma.out
diff --git a/tests/common/newerma.sh b/tests/common/newerma.sh
new file mode 100644
index 0000000..b05af8d
--- /dev/null
+++ b/tests/common/newerma.sh
@@ -0,0 +1 @@
+bfs_diff times -newerma times/a
diff --git a/tests/test_newermt.out b/tests/common/newermt.out
index 650e550..650e550 100644
--- a/tests/test_newermt.out
+++ b/tests/common/newermt.out
diff --git a/tests/common/newermt.sh b/tests/common/newermt.sh
new file mode 100644
index 0000000..3c5be68
--- /dev/null
+++ b/tests/common/newermt.sh
@@ -0,0 +1 @@
+bfs_diff times -newermt 1991-12-14T00:01
diff --git a/tests/test_newermt_epoch_minus_one.out b/tests/common/newermt_epoch_minus_one.out
index f7f63b0..f7f63b0 100644
--- a/tests/test_newermt_epoch_minus_one.out
+++ b/tests/common/newermt_epoch_minus_one.out
diff --git a/tests/common/newermt_epoch_minus_one.sh b/tests/common/newermt_epoch_minus_one.sh
new file mode 100644
index 0000000..568e2f3
--- /dev/null
+++ b/tests/common/newermt_epoch_minus_one.sh
@@ -0,0 +1 @@
+bfs_diff times -newermt 1969-12-31T23:59:59Z
diff --git a/tests/test_name_backslash.out b/tests/common/ok_closed_stdin.out
index e69de29..e69de29 100644
--- a/tests/test_name_backslash.out
+++ b/tests/common/ok_closed_stdin.out
diff --git a/tests/common/ok_closed_stdin.sh b/tests/common/ok_closed_stdin.sh
new file mode 100644
index 0000000..687e998
--- /dev/null
+++ b/tests/common/ok_closed_stdin.sh
@@ -0,0 +1 @@
+bfs_diff basic -ok echo \; <&-
diff --git a/tests/test_nogroup.out b/tests/common/okdir_closed_stdin.out
index e69de29..e69de29 100644
--- a/tests/test_nogroup.out
+++ b/tests/common/okdir_closed_stdin.out
diff --git a/tests/common/okdir_closed_stdin.sh b/tests/common/okdir_closed_stdin.sh
new file mode 100644
index 0000000..a515298
--- /dev/null
+++ b/tests/common/okdir_closed_stdin.sh
@@ -0,0 +1 @@
+bfs_diff basic -okdir echo {} \; <&-
diff --git a/tests/test_name_root_depth.out b/tests/common/quit.out
index cf4d5a9..cf4d5a9 100644
--- a/tests/test_name_root_depth.out
+++ b/tests/common/quit.out
diff --git a/tests/common/quit.sh b/tests/common/quit.sh
new file mode 100644
index 0000000..46b60c5
--- /dev/null
+++ b/tests/common/quit.sh
@@ -0,0 +1 @@
+bfs_diff basic/g -print -name g -quit
diff --git a/tests/test_comma_reachability.out b/tests/common/quit_after_print.out
index 15a13db..15a13db 100644
--- a/tests/test_comma_reachability.out
+++ b/tests/common/quit_after_print.out
diff --git a/tests/common/quit_after_print.sh b/tests/common/quit_after_print.sh
new file mode 100644
index 0000000..ee5653a
--- /dev/null
+++ b/tests/common/quit_after_print.sh
@@ -0,0 +1 @@
+bfs_diff basic basic -print -quit
diff --git a/tests/test_nogroup_ulimit.out b/tests/common/quit_before_print.out
index e69de29..e69de29 100644
--- a/tests/test_nogroup_ulimit.out
+++ b/tests/common/quit_before_print.out
diff --git a/tests/common/quit_before_print.sh b/tests/common/quit_before_print.sh
new file mode 100644
index 0000000..cda3a2c
--- /dev/null
+++ b/tests/common/quit_before_print.sh
@@ -0,0 +1 @@
+bfs_diff basic basic -quit -print
diff --git a/tests/test_quit_child.out b/tests/common/quit_child.out
index fb683c7..fb683c7 100644
--- a/tests/test_quit_child.out
+++ b/tests/common/quit_child.out
diff --git a/tests/common/quit_child.sh b/tests/common/quit_child.sh
new file mode 100644
index 0000000..bd27eff
--- /dev/null
+++ b/tests/common/quit_child.sh
@@ -0,0 +1 @@
+bfs_diff basic/g -print -name h -quit
diff --git a/tests/test_quit_depth.out b/tests/common/quit_depth.out
index fb683c7..fb683c7 100644
--- a/tests/test_quit_depth.out
+++ b/tests/common/quit_depth.out
diff --git a/tests/common/quit_depth.sh b/tests/common/quit_depth.sh
new file mode 100644
index 0000000..f5f82ba
--- /dev/null
+++ b/tests/common/quit_depth.sh
@@ -0,0 +1 @@
+bfs_diff basic/g -depth -print -name g -quit
diff --git a/tests/test_quit_depth_child.out b/tests/common/quit_depth_child.out
index 72b086d..72b086d 100644
--- a/tests/test_quit_depth_child.out
+++ b/tests/common/quit_depth_child.out
diff --git a/tests/common/quit_depth_child.sh b/tests/common/quit_depth_child.sh
new file mode 100644
index 0000000..dd09d5b
--- /dev/null
+++ b/tests/common/quit_depth_child.sh
@@ -0,0 +1 @@
+bfs_diff basic/g -depth -print -name h -quit
diff --git a/tests/test_regex.out b/tests/common/regex.out
index cfc113b..cfc113b 100644
--- a/tests/test_regex.out
+++ b/tests/common/regex.out
diff --git a/tests/common/regex.sh b/tests/common/regex.sh
new file mode 100644
index 0000000..a3bdae8
--- /dev/null
+++ b/tests/common/regex.sh
@@ -0,0 +1 @@
+bfs_diff basic -regex 'basic/./.'
diff --git a/tests/test_regex_parens.out b/tests/common/regex_parens.out
index 0f0971e..0f0971e 100644
--- a/tests/test_regex_parens.out
+++ b/tests/common/regex_parens.out
diff --git a/tests/common/regex_parens.sh b/tests/common/regex_parens.sh
new file mode 100644
index 0000000..fe0abf6
--- /dev/null
+++ b/tests/common/regex_parens.sh
@@ -0,0 +1,2 @@
+cd weirdnames
+bfs_diff . -regex '\./\((\)'
diff --git a/tests/test_links.out b/tests/common/samefile.out
index 996ffc8..996ffc8 100644
--- a/tests/test_links.out
+++ b/tests/common/samefile.out
diff --git a/tests/common/samefile.sh b/tests/common/samefile.sh
new file mode 100644
index 0000000..8e51966
--- /dev/null
+++ b/tests/common/samefile.sh
@@ -0,0 +1 @@
+bfs_diff links -samefile links/file
diff --git a/tests/test_L_broken.out b/tests/common/samefile_broken.out
index 21d6316..21d6316 100644
--- a/tests/test_L_broken.out
+++ b/tests/common/samefile_broken.out
diff --git a/tests/common/samefile_broken.sh b/tests/common/samefile_broken.sh
new file mode 100644
index 0000000..1cb52db
--- /dev/null
+++ b/tests/common/samefile_broken.sh
@@ -0,0 +1 @@
+bfs_diff links -samefile links/broken
diff --git a/tests/test_L_notdir.out b/tests/common/samefile_notdir.out
index 6e6658d..6e6658d 100644
--- a/tests/test_L_notdir.out
+++ b/tests/common/samefile_notdir.out
diff --git a/tests/common/samefile_notdir.sh b/tests/common/samefile_notdir.sh
new file mode 100644
index 0000000..f274ef6
--- /dev/null
+++ b/tests/common/samefile_notdir.sh
@@ -0,0 +1 @@
+bfs_diff links -samefile links/notdir
diff --git a/tests/test_samefile_symlink.out b/tests/common/samefile_symlink.out
index 299a572..299a572 100644
--- a/tests/test_samefile_symlink.out
+++ b/tests/common/samefile_symlink.out
diff --git a/tests/common/samefile_symlink.sh b/tests/common/samefile_symlink.sh
new file mode 100644
index 0000000..55ccf5c
--- /dev/null
+++ b/tests/common/samefile_symlink.sh
@@ -0,0 +1 @@
+bfs_diff links -samefile links/symlink
diff --git a/tests/test_nouser.out b/tests/common/size_big.out
index e69de29..e69de29 100644
--- a/tests/test_nouser.out
+++ b/tests/common/size_big.out
diff --git a/tests/common/size_big.sh b/tests/common/size_big.sh
new file mode 100644
index 0000000..6c100eb
--- /dev/null
+++ b/tests/common/size_big.sh
@@ -0,0 +1 @@
+bfs_diff basic -size 9223372036854775807
diff --git a/tests/test_L_delete.out b/tests/gnu/L_delete.out
index ed0e9a1..ed0e9a1 100644
--- a/tests/test_L_delete.out
+++ b/tests/gnu/L_delete.out
diff --git a/tests/gnu/L_delete.sh b/tests/gnu/L_delete.sh
new file mode 100644
index 0000000..08f39af
--- /dev/null
+++ b/tests/gnu/L_delete.sh
@@ -0,0 +1,9 @@
+rm -rf scratch/*
+mkdir scratch/foo
+mkdir scratch/bar
+ln -s ../foo scratch/bar/baz
+
+# Don't try to rmdir() a symlink
+invoke_bfs -L scratch/bar -delete || return 1
+
+bfs_diff scratch
diff --git a/tests/test_L_loops_continue.out b/tests/gnu/L_loops_continue.out
index a514555..a514555 100644
--- a/tests/test_L_loops_continue.out
+++ b/tests/gnu/L_loops_continue.out
diff --git a/tests/gnu/L_loops_continue.sh b/tests/gnu/L_loops_continue.sh
new file mode 100644
index 0000000..0244137
--- /dev/null
+++ b/tests/gnu/L_loops_continue.sh
@@ -0,0 +1,2 @@
+bfs_diff -L loops
+[ $? -eq $EX_BFS ]
diff --git a/tests/test_L_xtype_f.out b/tests/gnu/L_xtype_f.out
index 8b95397..8b95397 100644
--- a/tests/test_L_xtype_f.out
+++ b/tests/gnu/L_xtype_f.out
diff --git a/tests/gnu/L_xtype_f.sh b/tests/gnu/L_xtype_f.sh
new file mode 100644
index 0000000..47f7be7
--- /dev/null
+++ b/tests/gnu/L_xtype_f.sh
@@ -0,0 +1 @@
+bfs_diff -L links -xtype f
diff --git a/tests/test_L_xtype_l.out b/tests/gnu/L_xtype_l.out
index 973864f..973864f 100644
--- a/tests/test_L_xtype_l.out
+++ b/tests/gnu/L_xtype_l.out
diff --git a/tests/gnu/L_xtype_l.sh b/tests/gnu/L_xtype_l.sh
new file mode 100644
index 0000000..afe52ef
--- /dev/null
+++ b/tests/gnu/L_xtype_l.sh
@@ -0,0 +1 @@
+bfs_diff -L links -xtype l
diff --git a/tests/test_a.out b/tests/gnu/and.out
index 722962c..722962c 100644
--- a/tests/test_a.out
+++ b/tests/gnu/and.out
diff --git a/tests/gnu/and.sh b/tests/gnu/and.sh
new file mode 100644
index 0000000..1606455
--- /dev/null
+++ b/tests/gnu/and.sh
@@ -0,0 +1 @@
+bfs_diff basic -name foo -and -type d
diff --git a/tests/test_comma_redundant_false.out b/tests/gnu/and_false_or_true.out
index 15a13db..15a13db 100644
--- a/tests/test_comma_redundant_false.out
+++ b/tests/gnu/and_false_or_true.out
diff --git a/tests/gnu/and_false_or_true.sh b/tests/gnu/and_false_or_true.sh
new file mode 100644
index 0000000..e500722
--- /dev/null
+++ b/tests/gnu/and_false_or_true.sh
@@ -0,0 +1,3 @@
+# Test (-a lhs(always_true) -false) <==> (! lhs),
+# (-o lhs(always_false) -true) <==> (! lhs)
+bfs_diff basic -prune -false -o -true
diff --git a/tests/test_nouser_ulimit.out b/tests/gnu/and_purity.out
index e69de29..e69de29 100644
--- a/tests/test_nouser_ulimit.out
+++ b/tests/gnu/and_purity.out
diff --git a/tests/gnu/and_purity.sh b/tests/gnu/and_purity.sh
new file mode 100644
index 0000000..55e2cfc
--- /dev/null
+++ b/tests/gnu/and_purity.sh
@@ -0,0 +1,2 @@
+# Regression test: (-a lhs(pure) rhs(always_false)) <==> rhs is only valid if rhs is pure
+bfs_diff basic -name nonexistent \( -print , -false \)
diff --git a/tests/test_comma.out b/tests/gnu/comma.out
index 740eefc..740eefc 100644
--- a/tests/test_comma.out
+++ b/tests/gnu/comma.out
diff --git a/tests/gnu/comma.sh b/tests/gnu/comma.sh
new file mode 100644
index 0000000..cdcebf8
--- /dev/null
+++ b/tests/gnu/comma.sh
@@ -0,0 +1 @@
+bfs_diff basic -name '*f*' -print , -print
diff --git a/tests/test_comma_redundant_true.out b/tests/gnu/comma_reachability.out
index 15a13db..15a13db 100644
--- a/tests/test_comma_redundant_true.out
+++ b/tests/gnu/comma_reachability.out
diff --git a/tests/gnu/comma_reachability.sh b/tests/gnu/comma_reachability.sh
new file mode 100644
index 0000000..60c26bc
--- /dev/null
+++ b/tests/gnu/comma_reachability.sh
@@ -0,0 +1 @@
+bfs_diff basic -print -quit , -print
diff --git a/tests/test_fprint_truncate.out b/tests/gnu/comma_redundant_false.out
index 15a13db..15a13db 100644
--- a/tests/test_fprint_truncate.out
+++ b/tests/gnu/comma_redundant_false.out
diff --git a/tests/gnu/comma_redundant_false.sh b/tests/gnu/comma_redundant_false.sh
new file mode 100644
index 0000000..f35d9b8
--- /dev/null
+++ b/tests/gnu/comma_redundant_false.sh
@@ -0,0 +1,2 @@
+# Test (, lhs(always_false) -false) <==> lhs
+bfs_diff basic -print -not -prune , -false
diff --git a/tests/test_not_reachability.out b/tests/gnu/comma_redundant_true.out
index 15a13db..15a13db 100644
--- a/tests/test_not_reachability.out
+++ b/tests/gnu/comma_redundant_true.out
diff --git a/tests/gnu/comma_redundant_true.sh b/tests/gnu/comma_redundant_true.sh
new file mode 100644
index 0000000..f9eef57
--- /dev/null
+++ b/tests/gnu/comma_redundant_true.sh
@@ -0,0 +1,2 @@
+# Test (, lhs(always_true) -true) <==> lhs
+bfs_diff basic -prune , -true
diff --git a/tests/test_depth_overflow.out b/tests/gnu/daystart.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_depth_overflow.out
+++ b/tests/gnu/daystart.out
diff --git a/tests/gnu/daystart.sh b/tests/gnu/daystart.sh
new file mode 100644
index 0000000..9799bca
--- /dev/null
+++ b/tests/gnu/daystart.sh
@@ -0,0 +1 @@
+bfs_diff basic -daystart -mtime 0
diff --git a/tests/test_exec.out b/tests/gnu/daystart_twice.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_exec.out
+++ b/tests/gnu/daystart_twice.out
diff --git a/tests/gnu/daystart_twice.sh b/tests/gnu/daystart_twice.sh
new file mode 100644
index 0000000..21b2c0f
--- /dev/null
+++ b/tests/gnu/daystart_twice.sh
@@ -0,0 +1 @@
+bfs_diff basic -daystart -daystart -mtime 0
diff --git a/tests/test_empty.out b/tests/gnu/empty.out
index a0f4b76..a0f4b76 100644
--- a/tests/test_empty.out
+++ b/tests/gnu/empty.out
diff --git a/tests/gnu/empty.sh b/tests/gnu/empty.sh
new file mode 100644
index 0000000..95ee988
--- /dev/null
+++ b/tests/gnu/empty.sh
@@ -0,0 +1 @@
+bfs_diff basic -empty
diff --git a/tests/test_empty_special.out b/tests/gnu/empty_special.out
index 3927f2b..3927f2b 100644
--- a/tests/test_empty_special.out
+++ b/tests/gnu/empty_special.out
diff --git a/tests/gnu/empty_special.sh b/tests/gnu/empty_special.sh
new file mode 100644
index 0000000..31e9d2e
--- /dev/null
+++ b/tests/gnu/empty_special.sh
@@ -0,0 +1 @@
+bfs_diff rainbow -empty
diff --git a/tests/test_exec_flush.out b/tests/gnu/exec_flush.out
index fdd7b16..fdd7b16 100644
--- a/tests/test_exec_flush.out
+++ b/tests/gnu/exec_flush.out
diff --git a/tests/gnu/exec_flush.sh b/tests/gnu/exec_flush.sh
new file mode 100644
index 0000000..aaeef6a
--- /dev/null
+++ b/tests/gnu/exec_flush.sh
@@ -0,0 +1,4 @@
+# IO streams should be flushed before executing programs
+invoke_bfs basic -print0 -exec echo found \; | tr '\0' ' ' >"$OUT"
+sort_output
+diff_output
diff --git a/tests/gnu/exec_flush_fail.sh b/tests/gnu/exec_flush_fail.sh
new file mode 100644
index 0000000..4772a14
--- /dev/null
+++ b/tests/gnu/exec_flush_fail.sh
@@ -0,0 +1,3 @@
+# Failure to flush streams before exec should be caught
+skip_unless test -e /dev/full
+fail invoke_bfs basic -print0 -exec true \; >/dev/full
diff --git a/tests/gnu/exec_nothing.sh b/tests/gnu/exec_nothing.sh
new file mode 100644
index 0000000..9d613e8
--- /dev/null
+++ b/tests/gnu/exec_nothing.sh
@@ -0,0 +1,2 @@
+# Regression test: don't segfault on missing command
+fail invoke_bfs basic -exec \;
diff --git a/tests/test_exec_plus_flush.out b/tests/gnu/exec_plus_flush.out
index 3e276be..3e276be 100644
--- a/tests/test_exec_plus_flush.out
+++ b/tests/gnu/exec_plus_flush.out
Binary files differ
diff --git a/tests/gnu/exec_plus_flush.sh b/tests/gnu/exec_plus_flush.sh
new file mode 100644
index 0000000..0c03837
--- /dev/null
+++ b/tests/gnu/exec_plus_flush.sh
@@ -0,0 +1,2 @@
+invoke_bfs basic/a -print0 -exec echo found {} + >"$OUT"
+diff_output
diff --git a/tests/gnu/exec_plus_flush_fail.sh b/tests/gnu/exec_plus_flush_fail.sh
new file mode 100644
index 0000000..5c74fd8
--- /dev/null
+++ b/tests/gnu/exec_plus_flush_fail.sh
@@ -0,0 +1,2 @@
+skip_unless test -e /dev/full
+fail invoke_bfs basic/a -print0 -exec echo found {} + >/dev/full
diff --git a/tests/test_execdir.out b/tests/gnu/execdir.out
index 62b31f6..62b31f6 100644
--- a/tests/test_execdir.out
+++ b/tests/gnu/execdir.out
diff --git a/tests/gnu/execdir.sh b/tests/gnu/execdir.sh
new file mode 100644
index 0000000..5a3a95a
--- /dev/null
+++ b/tests/gnu/execdir.sh
@@ -0,0 +1 @@
+bfs_diff basic -execdir echo {} \;
diff --git a/tests/test_execdir_plus_semicolon.out b/tests/gnu/execdir_plus_semicolon.out
index e39f452..e39f452 100644
--- a/tests/test_execdir_plus_semicolon.out
+++ b/tests/gnu/execdir_plus_semicolon.out
diff --git a/tests/gnu/execdir_plus_semicolon.sh b/tests/gnu/execdir_plus_semicolon.sh
new file mode 100644
index 0000000..c5cdafe
--- /dev/null
+++ b/tests/gnu/execdir_plus_semicolon.sh
@@ -0,0 +1 @@
+bfs_diff basic -execdir echo foo {} bar + baz \;
diff --git a/tests/test_execdir_substring.out b/tests/gnu/execdir_substring.out
index f7a9ac0..f7a9ac0 100644
--- a/tests/test_execdir_substring.out
+++ b/tests/gnu/execdir_substring.out
diff --git a/tests/gnu/execdir_substring.sh b/tests/gnu/execdir_substring.sh
new file mode 100644
index 0000000..feeabc4
--- /dev/null
+++ b/tests/gnu/execdir_substring.sh
@@ -0,0 +1 @@
+bfs_diff basic -execdir echo '-{}-' \;
diff --git a/tests/test_executable.out b/tests/gnu/executable.out
index 49c1b21..49c1b21 100644
--- a/tests/test_executable.out
+++ b/tests/gnu/executable.out
diff --git a/tests/gnu/executable.sh b/tests/gnu/executable.sh
new file mode 100644
index 0000000..f7f6633
--- /dev/null
+++ b/tests/gnu/executable.sh
@@ -0,0 +1 @@
+bfs_diff perms -executable
diff --git a/tests/test_ok_closed_stdin.out b/tests/gnu/false.out
index e69de29..e69de29 100644
--- a/tests/test_ok_closed_stdin.out
+++ b/tests/gnu/false.out
diff --git a/tests/gnu/false.sh b/tests/gnu/false.sh
new file mode 100644
index 0000000..89d86c2
--- /dev/null
+++ b/tests/gnu/false.sh
@@ -0,0 +1 @@
+bfs_diff basic -false
diff --git a/tests/gnu/files0_from_empty.sh b/tests/gnu/files0_from_empty.sh
new file mode 100644
index 0000000..bd4fbf4
--- /dev/null
+++ b/tests/gnu/files0_from_empty.sh
@@ -0,0 +1 @@
+printf "\0" | fail invoke_bfs -files0-from -
diff --git a/tests/test_files0_from_file.out b/tests/gnu/files0_from_file.out
index 1d87e6b..1d87e6b 100644
--- a/tests/test_files0_from_file.out
+++ b/tests/gnu/files0_from_file.out
diff --git a/tests/gnu/files0_from_file.sh b/tests/gnu/files0_from_file.sh
new file mode 100644
index 0000000..7563149
--- /dev/null
+++ b/tests/gnu/files0_from_file.sh
@@ -0,0 +1,3 @@
+cd weirdnames
+invoke_bfs -mindepth 1 -fprintf ../scratch/files0.in "%P\0"
+bfs_diff -files0-from ../scratch/files0.in
diff --git a/tests/gnu/files0_from_none.sh b/tests/gnu/files0_from_none.sh
new file mode 100644
index 0000000..c6e5b97
--- /dev/null
+++ b/tests/gnu/files0_from_none.sh
@@ -0,0 +1 @@
+printf "" | fail invoke_bfs -files0-from -
diff --git a/tests/gnu/files0_from_nothing.sh b/tests/gnu/files0_from_nothing.sh
new file mode 100644
index 0000000..5fdae60
--- /dev/null
+++ b/tests/gnu/files0_from_nothing.sh
@@ -0,0 +1 @@
+fail invoke_bfs -files0-from basic/nonexistent
diff --git a/tests/gnu/files0_from_nowhere.sh b/tests/gnu/files0_from_nowhere.sh
new file mode 100644
index 0000000..2337613
--- /dev/null
+++ b/tests/gnu/files0_from_nowhere.sh
@@ -0,0 +1 @@
+fail invoke_bfs -files0-from
diff --git a/tests/gnu/files0_from_ok.sh b/tests/gnu/files0_from_ok.sh
new file mode 100644
index 0000000..5387e5c
--- /dev/null
+++ b/tests/gnu/files0_from_ok.sh
@@ -0,0 +1 @@
+printf "basic\0" | fail invoke_bfs -files0-from - -ok echo {} \;
diff --git a/tests/test_files0_from_stdin.out b/tests/gnu/files0_from_stdin.out
index 1d87e6b..1d87e6b 100644
--- a/tests/test_files0_from_stdin.out
+++ b/tests/gnu/files0_from_stdin.out
diff --git a/tests/gnu/files0_from_stdin.sh b/tests/gnu/files0_from_stdin.sh
new file mode 100644
index 0000000..9df7736
--- /dev/null
+++ b/tests/gnu/files0_from_stdin.sh
@@ -0,0 +1,2 @@
+cd weirdnames
+invoke_bfs -mindepth 1 -printf "%P\0" | bfs_diff -files0-from -
diff --git a/tests/gnu/fls.sh b/tests/gnu/fls.sh
new file mode 100644
index 0000000..94e2c16
--- /dev/null
+++ b/tests/gnu/fls.sh
@@ -0,0 +1 @@
+invoke_bfs rainbow -fls scratch/fls.out
diff --git a/tests/test_follow_comma.out b/tests/gnu/follow_comma.out
index 920b3d3..920b3d3 100644
--- a/tests/test_follow_comma.out
+++ b/tests/gnu/follow_comma.out
diff --git a/tests/gnu/follow_comma.sh b/tests/gnu/follow_comma.sh
new file mode 100644
index 0000000..f57b932
--- /dev/null
+++ b/tests/gnu/follow_comma.sh
@@ -0,0 +1,3 @@
+# , is an operator after a non-flag is seen
+cd weirdnames
+bfs_diff -follow ',' -print
diff --git a/tests/test_exec_nonexistent.out b/tests/gnu/fprint.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_exec_nonexistent.out
+++ b/tests/gnu/fprint.out
diff --git a/tests/gnu/fprint.sh b/tests/gnu/fprint.sh
new file mode 100644
index 0000000..04b50fa
--- /dev/null
+++ b/tests/gnu/fprint.sh
@@ -0,0 +1,3 @@
+invoke_bfs basic -fprint "$OUT"
+sort_output
+diff_output
diff --git a/tests/test_fprint0.out b/tests/gnu/fprint0.out
index 1347444..1347444 100644
--- a/tests/test_fprint0.out
+++ b/tests/gnu/fprint0.out
Binary files differ
diff --git a/tests/gnu/fprint0.sh b/tests/gnu/fprint0.sh
new file mode 100644
index 0000000..dd10b5f
--- /dev/null
+++ b/tests/gnu/fprint0.sh
@@ -0,0 +1,2 @@
+invoke_bfs basic/a basic/b -fprint0 "$OUT"
+diff_output
diff --git a/tests/test_fprint_duplicate.out b/tests/gnu/fprint_duplicate.out
index 2575f35..2575f35 100644
--- a/tests/test_fprint_duplicate.out
+++ b/tests/gnu/fprint_duplicate.out
diff --git a/tests/gnu/fprint_duplicate.sh b/tests/gnu/fprint_duplicate.sh
new file mode 100644
index 0000000..b4eb047
--- /dev/null
+++ b/tests/gnu/fprint_duplicate.sh
@@ -0,0 +1,7 @@
+touchp scratch/foo.out
+ln scratch/foo.out scratch/foo.hard
+ln -s foo.out scratch/foo.soft
+
+invoke_bfs basic -fprint scratch/foo.out -fprint scratch/foo.hard -fprint scratch/foo.soft
+sort scratch/foo.out >"$OUT"
+diff_output
diff --git a/tests/gnu/fprint_error.sh b/tests/gnu/fprint_error.sh
new file mode 100644
index 0000000..e7f2394
--- /dev/null
+++ b/tests/gnu/fprint_error.sh
@@ -0,0 +1,2 @@
+skip_unless test -e /dev/full
+fail invoke_bfs basic -maxdepth 0 -fprint /dev/full
diff --git a/tests/gnu/fprint_noarg.sh b/tests/gnu/fprint_noarg.sh
new file mode 100644
index 0000000..bf772f3
--- /dev/null
+++ b/tests/gnu/fprint_noarg.sh
@@ -0,0 +1 @@
+fail invoke_bfs basic -fprint
diff --git a/tests/gnu/fprint_noerror.sh b/tests/gnu/fprint_noerror.sh
new file mode 100644
index 0000000..142e935
--- /dev/null
+++ b/tests/gnu/fprint_noerror.sh
@@ -0,0 +1,3 @@
+# Regression test: /dev/full should not fail until actually written to
+skip_unless test -e /dev/full
+invoke_bfs basic -false -fprint /dev/full
diff --git a/tests/gnu/fprint_nonexistent.sh b/tests/gnu/fprint_nonexistent.sh
new file mode 100644
index 0000000..b6dac8a
--- /dev/null
+++ b/tests/gnu/fprint_nonexistent.sh
@@ -0,0 +1 @@
+fail invoke_bfs basic -fprint scratch/nonexistent/path
diff --git a/tests/test_printf_leak.out b/tests/gnu/fprint_truncate.out
index 15a13db..15a13db 100644
--- a/tests/test_printf_leak.out
+++ b/tests/gnu/fprint_truncate.out
diff --git a/tests/gnu/fprint_truncate.sh b/tests/gnu/fprint_truncate.sh
new file mode 100644
index 0000000..db58a7a
--- /dev/null
+++ b/tests/gnu/fprint_truncate.sh
@@ -0,0 +1,5 @@
+printf "basic\nbasic\n" >"$OUT"
+
+invoke_bfs basic -maxdepth 0 -fprint "$OUT"
+sort_output
+diff_output
diff --git a/tests/test_fprintf.out b/tests/gnu/fprintf.out
index 77ce17a..77ce17a 100644
--- a/tests/test_fprintf.out
+++ b/tests/gnu/fprintf.out
diff --git a/tests/gnu/fprintf.sh b/tests/gnu/fprintf.sh
new file mode 100644
index 0000000..9c6355a
--- /dev/null
+++ b/tests/gnu/fprintf.sh
@@ -0,0 +1,3 @@
+invoke_bfs basic -fprintf "$OUT" '%%p(%p) %%d(%d) %%f(%f) %%h(%h) %%H(%H) %%P(%P) %%m(%m) %%M(%M) %%y(%y)\n'
+sort_output
+diff_output
diff --git a/tests/gnu/fprintf_nofile.sh b/tests/gnu/fprintf_nofile.sh
new file mode 100644
index 0000000..c2c48a5
--- /dev/null
+++ b/tests/gnu/fprintf_nofile.sh
@@ -0,0 +1 @@
+fail invoke_bfs basic -fprintf
diff --git a/tests/gnu/fprintf_noformat.sh b/tests/gnu/fprintf_noformat.sh
new file mode 100644
index 0000000..0d285c3
--- /dev/null
+++ b/tests/gnu/fprintf_noformat.sh
@@ -0,0 +1 @@
+fail invoke_bfs basic -fprintf /dev/null
diff --git a/tests/test_exec_nopath.out b/tests/gnu/fstype.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_exec_nopath.out
+++ b/tests/gnu/fstype.out
diff --git a/tests/gnu/fstype.sh b/tests/gnu/fstype.sh
new file mode 100644
index 0000000..176688d
--- /dev/null
+++ b/tests/gnu/fstype.sh
@@ -0,0 +1,2 @@
+fstype=$(invoke_bfs basic -maxdepth 0 -printf '%F\n')
+bfs_diff basic -fstype "$fstype"
diff --git a/tests/test_exec_plus_nonexistent.out b/tests/gnu/gid.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_exec_plus_nonexistent.out
+++ b/tests/gnu/gid.out
diff --git a/tests/gnu/gid.sh b/tests/gnu/gid.sh
new file mode 100644
index 0000000..2707b4a
--- /dev/null
+++ b/tests/gnu/gid.sh
@@ -0,0 +1 @@
+bfs_diff basic -gid "$(id -g)"
diff --git a/tests/test_exec_plus_status.out b/tests/gnu/gid_minus.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_exec_plus_status.out
+++ b/tests/gnu/gid_minus.out
diff --git a/tests/gnu/gid_minus.sh b/tests/gnu/gid_minus.sh
new file mode 100644
index 0000000..e3822f0
--- /dev/null
+++ b/tests/gnu/gid_minus.sh
@@ -0,0 +1 @@
+bfs_diff basic -gid "-$(($(id -g) + 1))"
diff --git a/tests/test_execdir_nonexistent.out b/tests/gnu/gid_minus_plus.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_execdir_nonexistent.out
+++ b/tests/gnu/gid_minus_plus.out
diff --git a/tests/gnu/gid_minus_plus.sh b/tests/gnu/gid_minus_plus.sh
new file mode 100644
index 0000000..4ff0877
--- /dev/null
+++ b/tests/gnu/gid_minus_plus.sh
@@ -0,0 +1 @@
+bfs_diff basic -gid "-+$(($(id -g) + 1))"
diff --git a/tests/test_execdir_plus_nonexistent.out b/tests/gnu/gid_plus.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_execdir_plus_nonexistent.out
+++ b/tests/gnu/gid_plus.out
diff --git a/tests/gnu/gid_plus.sh b/tests/gnu/gid_plus.sh
new file mode 100644
index 0000000..8ad493b
--- /dev/null
+++ b/tests/gnu/gid_plus.sh
@@ -0,0 +1,2 @@
+skip_if test "$(id -g)" -eq 0
+bfs_diff basic -gid +0
diff --git a/tests/test_fprint.out b/tests/gnu/gid_plus_plus.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_fprint.out
+++ b/tests/gnu/gid_plus_plus.out
diff --git a/tests/gnu/gid_plus_plus.sh b/tests/gnu/gid_plus_plus.sh
new file mode 100644
index 0000000..7982633
--- /dev/null
+++ b/tests/gnu/gid_plus_plus.sh
@@ -0,0 +1,2 @@
+skip_if test "$(id -g)" -eq 0
+bfs_diff basic -gid ++0
diff --git a/tests/gnu/ignore_readdir_race.sh b/tests/gnu/ignore_readdir_race.sh
new file mode 100644
index 0000000..429933c
--- /dev/null
+++ b/tests/gnu/ignore_readdir_race.sh
@@ -0,0 +1,5 @@
+rm -rf scratch/*
+$TOUCH scratch/{foo,bar}
+
+# -links 1 forces a stat() call, which will fail for the second file
+invoke_bfs scratch -mindepth 1 -ignore_readdir_race -links 1 -exec "$TESTS/remove-sibling.sh" {} \;
diff --git a/tests/gnu/ignore_readdir_race_notdir.sh b/tests/gnu/ignore_readdir_race_notdir.sh
new file mode 100644
index 0000000..0c3ff5d
--- /dev/null
+++ b/tests/gnu/ignore_readdir_race_notdir.sh
@@ -0,0 +1,5 @@
+# Check -ignore_readdir_race handling when a directory is replaced with a file
+rm -rf scratch/*
+touchp scratch/foo/bar
+
+invoke_bfs scratch -mindepth 1 -ignore_readdir_race -execdir rm -r {} \; -execdir $TOUCH {} \;
diff --git a/tests/gnu/ignore_readdir_race_root.sh b/tests/gnu/ignore_readdir_race_root.sh
new file mode 100644
index 0000000..bad7391
--- /dev/null
+++ b/tests/gnu/ignore_readdir_race_root.sh
@@ -0,0 +1,2 @@
+# Make sure -ignore_readdir_race doesn't suppress ENOENT at the root
+fail invoke_bfs basic/nonexistent -ignore_readdir_race
diff --git a/tests/test_inum_mount.out b/tests/gnu/inum_automount.out
index 99c7511..99c7511 100644
--- a/tests/test_inum_mount.out
+++ b/tests/gnu/inum_automount.out
diff --git a/tests/gnu/inum_automount.sh b/tests/gnu/inum_automount.sh
new file mode 100644
index 0000000..0149043
--- /dev/null
+++ b/tests/gnu/inum_automount.sh
@@ -0,0 +1,17 @@
+# bfs shouldn't trigger automounts unless it descends into them
+
+skip_unless test "$SUDO"
+skip_unless command -v systemd-mount &>/dev/null
+
+rm -rf scratch/*
+mkdir scratch/{foo,mnt}
+skip_unless sudo systemd-mount -A -o bind basic scratch/mnt
+
+before=$(inum scratch/mnt)
+bfs_diff scratch -inum "$before" -prune
+ret=$?
+after=$(inum scratch/mnt)
+
+sudo systemd-umount scratch/mnt
+
+((ret == 0 && before == after))
diff --git a/tests/test_iwholename.out b/tests/gnu/iwholename.out
index ae1ae21..ae1ae21 100644
--- a/tests/test_iwholename.out
+++ b/tests/gnu/iwholename.out
diff --git a/tests/gnu/iwholename.sh b/tests/gnu/iwholename.sh
new file mode 100644
index 0000000..67e9630
--- /dev/null
+++ b/tests/gnu/iwholename.sh
@@ -0,0 +1,2 @@
+skip_unless invoke_bfs -quit -iwholename PATTERN
+bfs_diff basic -iwholename 'basic/*F*'
diff --git a/tests/test_bang.out b/tests/gnu/not.out
index b286454..b286454 100644
--- a/tests/test_bang.out
+++ b/tests/gnu/not.out
diff --git a/tests/gnu/not.sh b/tests/gnu/not.sh
new file mode 100644
index 0000000..9fa9edc
--- /dev/null
+++ b/tests/gnu/not.sh
@@ -0,0 +1 @@
+bfs_diff basic -not -name foo
diff --git a/tests/test_quit_after_print.out b/tests/gnu/not_reachability.out
index 15a13db..15a13db 100644
--- a/tests/test_quit_after_print.out
+++ b/tests/gnu/not_reachability.out
diff --git a/tests/gnu/not_reachability.sh b/tests/gnu/not_reachability.sh
new file mode 100644
index 0000000..7fd3c74
--- /dev/null
+++ b/tests/gnu/not_reachability.sh
@@ -0,0 +1 @@
+bfs_diff basic -print \! -quit -print
diff --git a/tests/gnu/ok_nothing.sh b/tests/gnu/ok_nothing.sh
new file mode 100644
index 0000000..439687b
--- /dev/null
+++ b/tests/gnu/ok_nothing.sh
@@ -0,0 +1,2 @@
+# Regression test: don't segfault on missing command
+fail invoke_bfs basic -ok \;
diff --git a/tests/test_o.out b/tests/gnu/or.out
index 1650c4d..1650c4d 100644
--- a/tests/test_o.out
+++ b/tests/gnu/or.out
diff --git a/tests/gnu/or.sh b/tests/gnu/or.sh
new file mode 100644
index 0000000..eb28030
--- /dev/null
+++ b/tests/gnu/or.sh
@@ -0,0 +1 @@
+bfs_diff basic -name foo -or -type d
diff --git a/tests/test_fstype.out b/tests/gnu/path_d.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_fstype.out
+++ b/tests/gnu/path_d.out
diff --git a/tests/gnu/path_d.sh b/tests/gnu/path_d.sh
new file mode 100644
index 0000000..e546ff3
--- /dev/null
+++ b/tests/gnu/path_d.sh
@@ -0,0 +1 @@
+bfs_diff basic -d
diff --git a/tests/test_perm_000_plus.out b/tests/gnu/perm_000_slash.out
index d7494b8..d7494b8 100644
--- a/tests/test_perm_000_plus.out
+++ b/tests/gnu/perm_000_slash.out
diff --git a/tests/gnu/perm_000_slash.sh b/tests/gnu/perm_000_slash.sh
new file mode 100644
index 0000000..f4b2665
--- /dev/null
+++ b/tests/gnu/perm_000_slash.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm /000
diff --git a/tests/test_perm_222_slash.out b/tests/gnu/perm_222_slash.out
index 9a5b95a..9a5b95a 100644
--- a/tests/test_perm_222_slash.out
+++ b/tests/gnu/perm_222_slash.out
diff --git a/tests/gnu/perm_222_slash.sh b/tests/gnu/perm_222_slash.sh
new file mode 100644
index 0000000..f4be665
--- /dev/null
+++ b/tests/gnu/perm_222_slash.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm /222
diff --git a/tests/test_perm_644_slash.out b/tests/gnu/perm_644_slash.out
index 7e5ae98..7e5ae98 100644
--- a/tests/test_perm_644_slash.out
+++ b/tests/gnu/perm_644_slash.out
diff --git a/tests/gnu/perm_644_slash.sh b/tests/gnu/perm_644_slash.sh
new file mode 100644
index 0000000..e883f17
--- /dev/null
+++ b/tests/gnu/perm_644_slash.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm /644
diff --git a/tests/test_perm_leading_plus_symbolic_slash.out b/tests/gnu/perm_leading_plus_symbolic_slash.out
index 7e5ae98..7e5ae98 100644
--- a/tests/test_perm_leading_plus_symbolic_slash.out
+++ b/tests/gnu/perm_leading_plus_symbolic_slash.out
diff --git a/tests/gnu/perm_leading_plus_symbolic_slash.sh b/tests/gnu/perm_leading_plus_symbolic_slash.sh
new file mode 100644
index 0000000..3db27bd
--- /dev/null
+++ b/tests/gnu/perm_leading_plus_symbolic_slash.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm /+rwx
diff --git a/tests/test_perm_symbolic_slash.out b/tests/gnu/perm_symbolic_slash.out
index 7e5ae98..7e5ae98 100644
--- a/tests/test_perm_symbolic_slash.out
+++ b/tests/gnu/perm_symbolic_slash.out
diff --git a/tests/gnu/perm_symbolic_slash.sh b/tests/gnu/perm_symbolic_slash.sh
new file mode 100644
index 0000000..253b14e
--- /dev/null
+++ b/tests/gnu/perm_symbolic_slash.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm /a+r,u=wX,g+wX-w
diff --git a/tests/test_precedence.out b/tests/gnu/precedence.out
index 7f589f2..7f589f2 100644
--- a/tests/test_precedence.out
+++ b/tests/gnu/precedence.out
diff --git a/tests/gnu/precedence.sh b/tests/gnu/precedence.sh
new file mode 100644
index 0000000..b35d160
--- /dev/null
+++ b/tests/gnu/precedence.sh
@@ -0,0 +1 @@
+bfs_diff basic \( -name foo -type d -o -name bar -a -type f \) -print , \! -empty -type f -print
diff --git a/tests/test_print0.out b/tests/gnu/print0.out
index 1347444..1347444 100644
--- a/tests/test_print0.out
+++ b/tests/gnu/print0.out
Binary files differ
diff --git a/tests/gnu/print0.sh b/tests/gnu/print0.sh
new file mode 100644
index 0000000..b916172
--- /dev/null
+++ b/tests/gnu/print0.sh
@@ -0,0 +1,2 @@
+invoke_bfs basic/a basic/b -print0 >"$OUT"
+diff_output
diff --git a/tests/gnu/print_error.sh b/tests/gnu/print_error.sh
new file mode 100644
index 0000000..9fd5af5
--- /dev/null
+++ b/tests/gnu/print_error.sh
@@ -0,0 +1,2 @@
+skip_unless test -e /dev/full
+fail invoke_bfs basic -maxdepth 0 >/dev/full
diff --git a/tests/test_printf.out b/tests/gnu/printf.out
index 77ce17a..77ce17a 100644
--- a/tests/test_printf.out
+++ b/tests/gnu/printf.out
diff --git a/tests/gnu/printf.sh b/tests/gnu/printf.sh
new file mode 100644
index 0000000..4dd48e8
--- /dev/null
+++ b/tests/gnu/printf.sh
@@ -0,0 +1 @@
+bfs_diff basic -printf '%%p(%p) %%d(%d) %%f(%f) %%h(%h) %%H(%H) %%P(%P) %%m(%m) %%M(%M) %%y(%y)\n'
diff --git a/tests/test_printf_H.out b/tests/gnu/printf_H.out
index 6b605ff..6b605ff 100644
--- a/tests/test_printf_H.out
+++ b/tests/gnu/printf_H.out
diff --git a/tests/gnu/printf_H.sh b/tests/gnu/printf_H.sh
new file mode 100644
index 0000000..ddef7e2
--- /dev/null
+++ b/tests/gnu/printf_H.sh
@@ -0,0 +1 @@
+bfs_diff basic links -printf '%%p(%p) %%d(%d) %%f(%f) %%h(%h) %%H(%H) %%P(%P) %%y(%y)\n'
diff --git a/tests/test_printf_Y_error.out b/tests/gnu/printf_Y_error.out
index 410a9b5..410a9b5 100644
--- a/tests/test_printf_Y_error.out
+++ b/tests/gnu/printf_Y_error.out
diff --git a/tests/gnu/printf_Y_error.sh b/tests/gnu/printf_Y_error.sh
new file mode 100644
index 0000000..933c734
--- /dev/null
+++ b/tests/gnu/printf_Y_error.sh
@@ -0,0 +1,12 @@
+rm -rf scratch/*
+mkdir scratch/foo
+chmod -x scratch/foo
+ln -s foo/bar scratch/bar
+
+bfs_diff scratch -printf '(%p) (%l) %y %Y\n'
+ret=$?
+
+chmod +x scratch/foo
+rm -rf scratch/*
+
+[ $ret -eq $EX_BFS ]
diff --git a/tests/test_okdir_closed_stdin.out b/tests/gnu/printf_empty.out
index e69de29..e69de29 100644
--- a/tests/test_okdir_closed_stdin.out
+++ b/tests/gnu/printf_empty.out
diff --git a/tests/gnu/printf_empty.sh b/tests/gnu/printf_empty.sh
new file mode 100644
index 0000000..ed5eb04
--- /dev/null
+++ b/tests/gnu/printf_empty.sh
@@ -0,0 +1 @@
+bfs_diff basic -printf ''
diff --git a/tests/test_printf_escapes.out b/tests/gnu/printf_escapes.out
index 20ea120..20ea120 100644
--- a/tests/test_printf_escapes.out
+++ b/tests/gnu/printf_escapes.out
diff --git a/tests/gnu/printf_escapes.sh b/tests/gnu/printf_escapes.sh
new file mode 100644
index 0000000..ece7c0e
--- /dev/null
+++ b/tests/gnu/printf_escapes.sh
@@ -0,0 +1 @@
+bfs_diff basic -maxdepth 0 -printf '\18\118\1118\11118\n\cfoo'
diff --git a/tests/test_printf_flags.out b/tests/gnu/printf_flags.out
index c2c1f0a..c2c1f0a 100644
--- a/tests/test_printf_flags.out
+++ b/tests/gnu/printf_flags.out
diff --git a/tests/gnu/printf_flags.sh b/tests/gnu/printf_flags.sh
new file mode 100644
index 0000000..2ef37ad
--- /dev/null
+++ b/tests/gnu/printf_flags.sh
@@ -0,0 +1 @@
+bfs_diff basic -printf '|%- 10.10p| %+03d %#4m\n'
diff --git a/tests/test_printf_l_nonlink.out b/tests/gnu/printf_l_nonlink.out
index 30df155..30df155 100644
--- a/tests/test_printf_l_nonlink.out
+++ b/tests/gnu/printf_l_nonlink.out
diff --git a/tests/gnu/printf_l_nonlink.sh b/tests/gnu/printf_l_nonlink.sh
new file mode 100644
index 0000000..1c66442
--- /dev/null
+++ b/tests/gnu/printf_l_nonlink.sh
@@ -0,0 +1 @@
+bfs_diff links -printf '| %26p -> %-26l |\n'
diff --git a/tests/test_quit_implicit_print.out b/tests/gnu/printf_leak.out
index 15a13db..15a13db 100644
--- a/tests/test_quit_implicit_print.out
+++ b/tests/gnu/printf_leak.out
diff --git a/tests/gnu/printf_leak.sh b/tests/gnu/printf_leak.sh
new file mode 100644
index 0000000..c4092c7
--- /dev/null
+++ b/tests/gnu/printf_leak.sh
@@ -0,0 +1,2 @@
+# Memory leak regression test
+bfs_diff basic -maxdepth 0 -printf '%p'
diff --git a/tests/test_printf_nul.out b/tests/gnu/printf_nul.out
index fdb6c6b..fdb6c6b 100644
--- a/tests/test_printf_nul.out
+++ b/tests/gnu/printf_nul.out
Binary files differ
diff --git a/tests/gnu/printf_nul.sh b/tests/gnu/printf_nul.sh
new file mode 100644
index 0000000..0b8b928
--- /dev/null
+++ b/tests/gnu/printf_nul.sh
@@ -0,0 +1,3 @@
+# NUL byte regression test
+invoke_bfs basic/a basic/b -maxdepth 0 -printf '%h\0%f\n' >"$OUT"
+diff_output
diff --git a/tests/test_printf_slash.out b/tests/gnu/printf_slash.out
index 5571971..5571971 100644
--- a/tests/test_printf_slash.out
+++ b/tests/gnu/printf_slash.out
diff --git a/tests/gnu/printf_slash.sh b/tests/gnu/printf_slash.sh
new file mode 100644
index 0000000..b64ff10
--- /dev/null
+++ b/tests/gnu/printf_slash.sh
@@ -0,0 +1 @@
+bfs_diff / -maxdepth 0 -printf '(%h)/(%f)\n'
diff --git a/tests/test_printf_slashes.out b/tests/gnu/printf_slashes.out
index 5571971..5571971 100644
--- a/tests/test_printf_slashes.out
+++ b/tests/gnu/printf_slashes.out
diff --git a/tests/gnu/printf_slashes.sh b/tests/gnu/printf_slashes.sh
new file mode 100644
index 0000000..d56a287
--- /dev/null
+++ b/tests/gnu/printf_slashes.sh
@@ -0,0 +1 @@
+bfs_diff /// -maxdepth 0 -printf '(%h)/(%f)\n'
diff --git a/tests/test_printf_times.out b/tests/gnu/printf_times.out
index 7e7d256..7e7d256 100644
--- a/tests/test_printf_times.out
+++ b/tests/gnu/printf_times.out
diff --git a/tests/gnu/printf_times.sh b/tests/gnu/printf_times.sh
new file mode 100644
index 0000000..d952620
--- /dev/null
+++ b/tests/gnu/printf_times.sh
@@ -0,0 +1 @@
+bfs_diff times -type f -printf '%p | %a %AY-%Am-%Ad %AH:%AI:%AS %T@ | %t %TY-%Tm-%Td %TH:%TI:%TS %T@\n'
diff --git a/tests/test_printf_trailing_slash.out b/tests/gnu/printf_trailing_slash.out
index 017ac0d..017ac0d 100644
--- a/tests/test_printf_trailing_slash.out
+++ b/tests/gnu/printf_trailing_slash.out
diff --git a/tests/gnu/printf_trailing_slash.sh b/tests/gnu/printf_trailing_slash.sh
new file mode 100644
index 0000000..2df818d
--- /dev/null
+++ b/tests/gnu/printf_trailing_slash.sh
@@ -0,0 +1 @@
+bfs_diff basic/ -printf '(%h)/(%f)\n'
diff --git a/tests/test_printf_trailing_slashes.out b/tests/gnu/printf_trailing_slashes.out
index fd27101..fd27101 100644
--- a/tests/test_printf_trailing_slashes.out
+++ b/tests/gnu/printf_trailing_slashes.out
diff --git a/tests/gnu/printf_trailing_slashes.sh b/tests/gnu/printf_trailing_slashes.sh
new file mode 100644
index 0000000..6dc532c
--- /dev/null
+++ b/tests/gnu/printf_trailing_slashes.sh
@@ -0,0 +1 @@
+bfs_diff basic/// -printf '(%h)/(%f)\n'
diff --git a/tests/test_printf_types.out b/tests/gnu/printf_types.out
index 8144c7c..8144c7c 100644
--- a/tests/test_printf_types.out
+++ b/tests/gnu/printf_types.out
diff --git a/tests/gnu/printf_types.sh b/tests/gnu/printf_types.sh
new file mode 100644
index 0000000..6ed1d75
--- /dev/null
+++ b/tests/gnu/printf_types.sh
@@ -0,0 +1 @@
+bfs_diff loops -printf '(%p) (%l) %y %Y\n'
diff --git a/tests/gnu/printf_u_g_ulimit.sh b/tests/gnu/printf_u_g_ulimit.sh
new file mode 100644
index 0000000..a84ee29
--- /dev/null
+++ b/tests/gnu/printf_u_g_ulimit.sh
@@ -0,0 +1,3 @@
+closefrom 4
+ulimit -n 16
+[ "$(invoke_bfs deep -printf '%u %g\n' | uniq)" = "$(id -un) $(id -gn)" ]
diff --git a/tests/test_readable.out b/tests/gnu/readable.out
index 386feba..386feba 100644
--- a/tests/test_readable.out
+++ b/tests/gnu/readable.out
diff --git a/tests/gnu/readable.sh b/tests/gnu/readable.sh
new file mode 100644
index 0000000..a496667
--- /dev/null
+++ b/tests/gnu/readable.sh
@@ -0,0 +1 @@
+bfs_diff perms -readable
diff --git a/tests/gnu/regex_error.sh b/tests/gnu/regex_error.sh
new file mode 100644
index 0000000..9bd4c8d
--- /dev/null
+++ b/tests/gnu/regex_error.sh
@@ -0,0 +1 @@
+fail invoke_bfs basic -regex '['
diff --git a/tests/test_regex_invalid_utf8.out b/tests/gnu/regex_invalid_utf8.out
index 03f3f58..03f3f58 100644
--- a/tests/test_regex_invalid_utf8.out
+++ b/tests/gnu/regex_invalid_utf8.out
diff --git a/tests/gnu/regex_invalid_utf8.sh b/tests/gnu/regex_invalid_utf8.sh
new file mode 100644
index 0000000..85f1897
--- /dev/null
+++ b/tests/gnu/regex_invalid_utf8.sh
@@ -0,0 +1,8 @@
+rm -rf scratch/*
+
+# Incomplete UTF-8 sequences
+skip_unless touch scratch/$'\xC3'
+skip_unless touch scratch/$'\xE2\x84'
+skip_unless touch scratch/$'\xF0\x9F\x92'
+
+bfs_diff scratch -regex 'scratch/..'
diff --git a/tests/test_regextype_ed.out b/tests/gnu/regextype_ed.out
index 0f0971e..0f0971e 100644
--- a/tests/test_regextype_ed.out
+++ b/tests/gnu/regextype_ed.out
diff --git a/tests/gnu/regextype_ed.sh b/tests/gnu/regextype_ed.sh
new file mode 100644
index 0000000..0e92db3
--- /dev/null
+++ b/tests/gnu/regextype_ed.sh
@@ -0,0 +1,2 @@
+cd weirdnames
+bfs_diff -regextype ed -regex '\./\((\)'
diff --git a/tests/test_regextype_emacs.out b/tests/gnu/regextype_emacs.out
index 95942b4..95942b4 100644
--- a/tests/test_regextype_emacs.out
+++ b/tests/gnu/regextype_emacs.out
diff --git a/tests/gnu/regextype_emacs.sh b/tests/gnu/regextype_emacs.sh
new file mode 100644
index 0000000..d0f68cc
--- /dev/null
+++ b/tests/gnu/regextype_emacs.sh
@@ -0,0 +1,3 @@
+skip_unless invoke_bfs -regextype emacs -quit
+
+bfs_diff basic -regextype emacs -regex '.*/\(f+o?o?\|bar\)'
diff --git a/tests/test_name.out b/tests/gnu/regextype_grep.out
index a9e5d42..a9e5d42 100644
--- a/tests/test_name.out
+++ b/tests/gnu/regextype_grep.out
diff --git a/tests/gnu/regextype_grep.sh b/tests/gnu/regextype_grep.sh
new file mode 100644
index 0000000..0136700
--- /dev/null
+++ b/tests/gnu/regextype_grep.sh
@@ -0,0 +1,3 @@
+skip_unless invoke_bfs -regextype grep -quit
+
+bfs_diff basic -regextype grep -regex '.*/f\+o\?o\?'
diff --git a/tests/test_regextype_posix_basic.out b/tests/gnu/regextype_posix_basic.out
index 0f0971e..0f0971e 100644
--- a/tests/test_regextype_posix_basic.out
+++ b/tests/gnu/regextype_posix_basic.out
diff --git a/tests/gnu/regextype_posix_basic.sh b/tests/gnu/regextype_posix_basic.sh
new file mode 100644
index 0000000..fa2254c
--- /dev/null
+++ b/tests/gnu/regextype_posix_basic.sh
@@ -0,0 +1,2 @@
+cd weirdnames
+bfs_diff -regextype posix-basic -regex '\./\((\)'
diff --git a/tests/test_regextype_posix_extended.out b/tests/gnu/regextype_posix_extended.out
index 0f0971e..0f0971e 100644
--- a/tests/test_regextype_posix_extended.out
+++ b/tests/gnu/regextype_posix_extended.out
diff --git a/tests/gnu/regextype_posix_extended.sh b/tests/gnu/regextype_posix_extended.sh
new file mode 100644
index 0000000..f82ed65
--- /dev/null
+++ b/tests/gnu/regextype_posix_extended.sh
@@ -0,0 +1,2 @@
+cd weirdnames
+bfs_diff -regextype posix-extended -regex '\./(\()'
diff --git a/tests/test_regextype_sed.out b/tests/gnu/regextype_sed.out
index 0f0971e..0f0971e 100644
--- a/tests/test_regextype_sed.out
+++ b/tests/gnu/regextype_sed.out
diff --git a/tests/gnu/regextype_sed.sh b/tests/gnu/regextype_sed.sh
new file mode 100644
index 0000000..9ce6f4e
--- /dev/null
+++ b/tests/gnu/regextype_sed.sh
@@ -0,0 +1,2 @@
+cd weirdnames
+bfs_diff -regextype sed -regex '\./\((\)'
diff --git a/tests/test_gid.out b/tests/gnu/true.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_gid.out
+++ b/tests/gnu/true.out
diff --git a/tests/gnu/true.sh b/tests/gnu/true.sh
new file mode 100644
index 0000000..65f3254
--- /dev/null
+++ b/tests/gnu/true.sh
@@ -0,0 +1 @@
+bfs_diff basic -true
diff --git a/tests/test_gid_minus.out b/tests/gnu/uid.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_gid_minus.out
+++ b/tests/gnu/uid.out
diff --git a/tests/gnu/uid.sh b/tests/gnu/uid.sh
new file mode 100644
index 0000000..fb3cd93
--- /dev/null
+++ b/tests/gnu/uid.sh
@@ -0,0 +1 @@
+bfs_diff basic -uid "$(id -u)"
diff --git a/tests/test_gid_minus_plus.out b/tests/gnu/uid_minus.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_gid_minus_plus.out
+++ b/tests/gnu/uid_minus.out
diff --git a/tests/gnu/uid_minus.sh b/tests/gnu/uid_minus.sh
new file mode 100644
index 0000000..6d371f2
--- /dev/null
+++ b/tests/gnu/uid_minus.sh
@@ -0,0 +1 @@
+bfs_diff basic -uid "-$(($(id -u) + 1))"
diff --git a/tests/test_gid_name.out b/tests/gnu/uid_minus_plus.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_gid_name.out
+++ b/tests/gnu/uid_minus_plus.out
diff --git a/tests/gnu/uid_minus_plus.sh b/tests/gnu/uid_minus_plus.sh
new file mode 100644
index 0000000..e7a0496
--- /dev/null
+++ b/tests/gnu/uid_minus_plus.sh
@@ -0,0 +1 @@
+bfs_diff basic -uid "-+$(($(id -u) + 1))"
diff --git a/tests/test_gid_plus.out b/tests/gnu/uid_plus.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_gid_plus.out
+++ b/tests/gnu/uid_plus.out
diff --git a/tests/gnu/uid_plus.sh b/tests/gnu/uid_plus.sh
new file mode 100644
index 0000000..fc4bce3
--- /dev/null
+++ b/tests/gnu/uid_plus.sh
@@ -0,0 +1,2 @@
+skip_if test "$(id -u)" -eq 0
+bfs_diff basic -uid +0
diff --git a/tests/test_gid_plus_plus.out b/tests/gnu/uid_plus_plus.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_gid_plus_plus.out
+++ b/tests/gnu/uid_plus_plus.out
diff --git a/tests/gnu/uid_plus_plus.sh b/tests/gnu/uid_plus_plus.sh
new file mode 100644
index 0000000..5d5e086
--- /dev/null
+++ b/tests/gnu/uid_plus_plus.sh
@@ -0,0 +1,2 @@
+skip_if test "$(id -u)" -eq 0
+bfs_diff basic -uid ++0
diff --git a/tests/test_path.out b/tests/gnu/wholename.out
index ae1ae21..ae1ae21 100644
--- a/tests/test_path.out
+++ b/tests/gnu/wholename.out
diff --git a/tests/gnu/wholename.sh b/tests/gnu/wholename.sh
new file mode 100644
index 0000000..4c641b8
--- /dev/null
+++ b/tests/gnu/wholename.sh
@@ -0,0 +1 @@
+bfs_diff basic -wholename 'basic/*f*'
diff --git a/tests/test_writable.out b/tests/gnu/writable.out
index 9a5b95a..9a5b95a 100644
--- a/tests/test_writable.out
+++ b/tests/gnu/writable.out
diff --git a/tests/gnu/writable.sh b/tests/gnu/writable.sh
new file mode 100644
index 0000000..93c666f
--- /dev/null
+++ b/tests/gnu/writable.sh
@@ -0,0 +1 @@
+bfs_diff perms -writable
diff --git a/tests/test_xtype_bind_mount.out b/tests/gnu/xtype_bind_mount.out
index 16804ea..16804ea 100644
--- a/tests/test_xtype_bind_mount.out
+++ b/tests/gnu/xtype_bind_mount.out
diff --git a/tests/gnu/xtype_bind_mount.sh b/tests/gnu/xtype_bind_mount.sh
new file mode 100644
index 0000000..56f9c5c
--- /dev/null
+++ b/tests/gnu/xtype_bind_mount.sh
@@ -0,0 +1,13 @@
+skip_unless test "$SUDO"
+skip_unless test "$UNAME" = "Linux"
+
+rm -rf scratch/*
+$TOUCH scratch/{file,null}
+sudo mount --bind /dev/null scratch/null
+ln -s /dev/null scratch/link
+
+bfs_diff -L scratch -type c
+ret=$?
+
+sudo umount scratch/null
+return $ret
diff --git a/tests/test_xtype_f.out b/tests/gnu/xtype_f.out
index e6ba322..e6ba322 100644
--- a/tests/test_xtype_f.out
+++ b/tests/gnu/xtype_f.out
diff --git a/tests/gnu/xtype_f.sh b/tests/gnu/xtype_f.sh
new file mode 100644
index 0000000..0ea27bb
--- /dev/null
+++ b/tests/gnu/xtype_f.sh
@@ -0,0 +1 @@
+bfs_diff links -xtype f
diff --git a/tests/test_xtype_l.out b/tests/gnu/xtype_l.out
index f29c978..f29c978 100644
--- a/tests/test_xtype_l.out
+++ b/tests/gnu/xtype_l.out
diff --git a/tests/gnu/xtype_l.sh b/tests/gnu/xtype_l.sh
new file mode 100644
index 0000000..39c8ea4
--- /dev/null
+++ b/tests/gnu/xtype_l.sh
@@ -0,0 +1 @@
+bfs_diff links -xtype l
diff --git a/tests/test_P.out b/tests/posix/H.out
index ff635ff..ff635ff 100644
--- a/tests/test_P.out
+++ b/tests/posix/H.out
diff --git a/tests/posix/H.sh b/tests/posix/H.sh
new file mode 100644
index 0000000..5bae1be
--- /dev/null
+++ b/tests/posix/H.sh
@@ -0,0 +1 @@
+bfs_diff -H links/deeply/nested/dir
diff --git a/tests/test_L_samefile_broken.out b/tests/posix/H_broken.out
index 21d6316..21d6316 100644
--- a/tests/test_L_samefile_broken.out
+++ b/tests/posix/H_broken.out
diff --git a/tests/posix/H_broken.sh b/tests/posix/H_broken.sh
new file mode 100644
index 0000000..9ff761c
--- /dev/null
+++ b/tests/posix/H_broken.sh
@@ -0,0 +1 @@
+bfs_diff -H links/broken
diff --git a/tests/test_H_loops.out b/tests/posix/H_loops.out
index 1fc8f8f..1fc8f8f 100644
--- a/tests/test_H_loops.out
+++ b/tests/posix/H_loops.out
diff --git a/tests/posix/H_loops.sh b/tests/posix/H_loops.sh
new file mode 100644
index 0000000..90383b8
--- /dev/null
+++ b/tests/posix/H_loops.sh
@@ -0,0 +1 @@
+bfs_diff -H loops/deeply/nested/loop
diff --git a/tests/test_L_samefile_notdir.out b/tests/posix/H_notdir.out
index 6e6658d..6e6658d 100644
--- a/tests/test_L_samefile_notdir.out
+++ b/tests/posix/H_notdir.out
diff --git a/tests/posix/H_notdir.sh b/tests/posix/H_notdir.sh
new file mode 100644
index 0000000..68d7be7
--- /dev/null
+++ b/tests/posix/H_notdir.sh
@@ -0,0 +1 @@
+bfs_diff -H links/notdir
diff --git a/tests/test_P_slash.out b/tests/posix/H_slash.out
index df7701b..df7701b 100644
--- a/tests/test_P_slash.out
+++ b/tests/posix/H_slash.out
diff --git a/tests/posix/H_slash.sh b/tests/posix/H_slash.sh
new file mode 100644
index 0000000..b44d756
--- /dev/null
+++ b/tests/posix/H_slash.sh
@@ -0,0 +1 @@
+bfs_diff -H links/deeply/nested/dir/
diff --git a/tests/test_path_flag_expr.out b/tests/posix/H_type_l.out
index e67f10b..e67f10b 100644
--- a/tests/test_path_flag_expr.out
+++ b/tests/posix/H_type_l.out
diff --git a/tests/posix/H_type_l.sh b/tests/posix/H_type_l.sh
new file mode 100644
index 0000000..416a53e
--- /dev/null
+++ b/tests/posix/H_type_l.sh
@@ -0,0 +1 @@
+bfs_diff -H links/skip -type l
diff --git a/tests/test_L_depth.out b/tests/posix/L.out
index ec9e861..ec9e861 100644
--- a/tests/test_L_depth.out
+++ b/tests/posix/L.out
diff --git a/tests/posix/L.sh b/tests/posix/L.sh
new file mode 100644
index 0000000..d8aebe6
--- /dev/null
+++ b/tests/posix/L.sh
@@ -0,0 +1 @@
+bfs_diff -L links
diff --git a/tests/test_samefile_broken.out b/tests/posix/L_broken.out
index 21d6316..21d6316 100644
--- a/tests/test_samefile_broken.out
+++ b/tests/posix/L_broken.out
diff --git a/tests/posix/L_broken.sh b/tests/posix/L_broken.sh
new file mode 100644
index 0000000..9ff761c
--- /dev/null
+++ b/tests/posix/L_broken.sh
@@ -0,0 +1 @@
+bfs_diff -H links/broken
diff --git a/tests/test_follow.out b/tests/posix/L_depth.out
index ec9e861..ec9e861 100644
--- a/tests/test_follow.out
+++ b/tests/posix/L_depth.out
diff --git a/tests/posix/L_depth.sh b/tests/posix/L_depth.sh
new file mode 100644
index 0000000..59d7ee9
--- /dev/null
+++ b/tests/posix/L_depth.sh
@@ -0,0 +1 @@
+bfs_diff -L links -depth
diff --git a/tests/posix/L_loops.sh b/tests/posix/L_loops.sh
new file mode 100644
index 0000000..f737cea
--- /dev/null
+++ b/tests/posix/L_loops.sh
@@ -0,0 +1,4 @@
+# POSIX says it's okay to either stop or keep going on seeing a filesystem
+# loop, as long as a diagnostic is printed
+errors=$(invoke_bfs -L loops 2>&1 >/dev/null)
+[ -n "$errors" ]
diff --git a/tests/test_samefile_notdir.out b/tests/posix/L_notdir.out
index 6e6658d..6e6658d 100644
--- a/tests/test_samefile_notdir.out
+++ b/tests/posix/L_notdir.out
diff --git a/tests/posix/L_notdir.sh b/tests/posix/L_notdir.sh
new file mode 100644
index 0000000..68d7be7
--- /dev/null
+++ b/tests/posix/L_notdir.sh
@@ -0,0 +1 @@
+bfs_diff -H links/notdir
diff --git a/tests/test_L_type_l.out b/tests/posix/L_type_l.out
index 725d398..725d398 100644
--- a/tests/test_L_type_l.out
+++ b/tests/posix/L_type_l.out
diff --git a/tests/posix/L_type_l.sh b/tests/posix/L_type_l.sh
new file mode 100644
index 0000000..ee9e563
--- /dev/null
+++ b/tests/posix/L_type_l.sh
@@ -0,0 +1 @@
+bfs_diff -L links/skip -type l
diff --git a/tests/test_L_xdev.out b/tests/posix/L_xdev.out
index 2e80082..2e80082 100644
--- a/tests/test_L_xdev.out
+++ b/tests/posix/L_xdev.out
diff --git a/tests/posix/L_xdev.sh b/tests/posix/L_xdev.sh
new file mode 100644
index 0000000..f0ba120
--- /dev/null
+++ b/tests/posix/L_xdev.sh
@@ -0,0 +1,15 @@
+skip_unless test "$SUDO"
+skip_if test "$UNAME" = "Darwin"
+
+rm -rf scratch/*
+mkdir scratch/{foo,mnt}
+sudo mount -t tmpfs tmpfs scratch/mnt
+ln -s ../mnt scratch/foo/bar
+$TOUCH scratch/mnt/baz
+ln -s ../mnt/baz scratch/foo/qux
+
+bfs_diff -L scratch -xdev
+ret=$?
+
+sudo umount scratch/mnt
+return $ret
diff --git a/tests/test_and.out b/tests/posix/a.out
index 722962c..722962c 100644
--- a/tests/test_and.out
+++ b/tests/posix/a.out
diff --git a/tests/posix/a.sh b/tests/posix/a.sh
new file mode 100644
index 0000000..7d82d88
--- /dev/null
+++ b/tests/posix/a.sh
@@ -0,0 +1 @@
+bfs_diff basic -name foo -a -type d
diff --git a/tests/test_not.out b/tests/posix/bang.out
index b286454..b286454 100644
--- a/tests/test_not.out
+++ b/tests/posix/bang.out
diff --git a/tests/posix/bang.sh b/tests/posix/bang.sh
new file mode 100644
index 0000000..27840cd
--- /dev/null
+++ b/tests/posix/bang.sh
@@ -0,0 +1 @@
+bfs_diff basic \! -name foo
diff --git a/tests/test_group_id.out b/tests/posix/basic.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_group_id.out
+++ b/tests/posix/basic.out
diff --git a/tests/posix/basic.sh b/tests/posix/basic.sh
new file mode 100644
index 0000000..3d43529
--- /dev/null
+++ b/tests/posix/basic.sh
@@ -0,0 +1 @@
+bfs_diff basic
diff --git a/tests/posix/closed_stderr.sh b/tests/posix/closed_stderr.sh
new file mode 100644
index 0000000..cc746ef
--- /dev/null
+++ b/tests/posix/closed_stderr.sh
@@ -0,0 +1 @@
+fail invoke_bfs basic >&- 2>&-
diff --git a/tests/test_group_name.out b/tests/posix/closed_stdin.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_group_name.out
+++ b/tests/posix/closed_stdin.out
diff --git a/tests/posix/closed_stdin.sh b/tests/posix/closed_stdin.sh
new file mode 100644
index 0000000..6932be8
--- /dev/null
+++ b/tests/posix/closed_stdin.sh
@@ -0,0 +1 @@
+bfs_diff basic <&-
diff --git a/tests/posix/closed_stdout.sh b/tests/posix/closed_stdout.sh
new file mode 100644
index 0000000..446bf56
--- /dev/null
+++ b/tests/posix/closed_stdout.sh
@@ -0,0 +1 @@
+fail invoke_bfs basic >&-
diff --git a/tests/test_data_flow_and_swap.out b/tests/posix/data_flow_and_swap.out
index e604709..e604709 100644
--- a/tests/test_data_flow_and_swap.out
+++ b/tests/posix/data_flow_and_swap.out
diff --git a/tests/posix/data_flow_and_swap.sh b/tests/posix/data_flow_and_swap.sh
new file mode 100644
index 0000000..9a141af
--- /dev/null
+++ b/tests/posix/data_flow_and_swap.sh
@@ -0,0 +1 @@
+bfs_diff basic \! -type f -a -type d
diff --git a/tests/test_group_nogroup.out b/tests/posix/data_flow_group.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_group_nogroup.out
+++ b/tests/posix/data_flow_group.out
diff --git a/tests/posix/data_flow_group.sh b/tests/posix/data_flow_group.sh
new file mode 100644
index 0000000..453dc3e
--- /dev/null
+++ b/tests/posix/data_flow_group.sh
@@ -0,0 +1 @@
+bfs_diff basic \( -group "$(id -g)" -nogroup \) -o \( -group "$(id -g)" -o -nogroup \)
diff --git a/tests/test_data_flow_or_swap.out b/tests/posix/data_flow_or_swap.out
index e604709..e604709 100644
--- a/tests/test_data_flow_or_swap.out
+++ b/tests/posix/data_flow_or_swap.out
diff --git a/tests/posix/data_flow_or_swap.sh b/tests/posix/data_flow_or_swap.sh
new file mode 100644
index 0000000..e8f504b
--- /dev/null
+++ b/tests/posix/data_flow_or_swap.sh
@@ -0,0 +1 @@
+bfs_diff basic \! \( -type f -o \! -type d \)
diff --git a/tests/test_or_purity.out b/tests/posix/data_flow_type.out
index e69de29..e69de29 100644
--- a/tests/test_or_purity.out
+++ b/tests/posix/data_flow_type.out
diff --git a/tests/posix/data_flow_type.sh b/tests/posix/data_flow_type.sh
new file mode 100644
index 0000000..33339df
--- /dev/null
+++ b/tests/posix/data_flow_type.sh
@@ -0,0 +1 @@
+bfs_diff basic \! \( -type f -o \! -type f \)
diff --git a/tests/test_path_d.out b/tests/posix/data_flow_user.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_path_d.out
+++ b/tests/posix/data_flow_user.out
diff --git a/tests/posix/data_flow_user.sh b/tests/posix/data_flow_user.sh
new file mode 100644
index 0000000..44b6e1f
--- /dev/null
+++ b/tests/posix/data_flow_user.sh
@@ -0,0 +1 @@
+bfs_diff basic \( -user "$(id -u)" -nouser \) -o \( -user "$(id -u)" -o -nouser \)
diff --git a/tests/test_de_morgan_and.out b/tests/posix/de_morgan_and.out
index 7b7afd2..7b7afd2 100644
--- a/tests/test_de_morgan_and.out
+++ b/tests/posix/de_morgan_and.out
diff --git a/tests/posix/de_morgan_and.sh b/tests/posix/de_morgan_and.sh
new file mode 100644
index 0000000..d52975e
--- /dev/null
+++ b/tests/posix/de_morgan_and.sh
@@ -0,0 +1 @@
+bfs_diff basic \( \! -name 'foo' -a \! -type f \)
diff --git a/tests/test_de_morgan_not.out b/tests/posix/de_morgan_not.out
index 5916da3..5916da3 100644
--- a/tests/test_de_morgan_not.out
+++ b/tests/posix/de_morgan_not.out
diff --git a/tests/posix/de_morgan_not.sh b/tests/posix/de_morgan_not.sh
new file mode 100644
index 0000000..7393ce0
--- /dev/null
+++ b/tests/posix/de_morgan_not.sh
@@ -0,0 +1 @@
+bfs_diff basic \! \( -name 'foo' -o \! -type f \)
diff --git a/tests/test_de_morgan_or.out b/tests/posix/de_morgan_or.out
index 2a57066..2a57066 100644
--- a/tests/test_de_morgan_or.out
+++ b/tests/posix/de_morgan_or.out
diff --git a/tests/posix/de_morgan_or.sh b/tests/posix/de_morgan_or.sh
new file mode 100644
index 0000000..378aab2
--- /dev/null
+++ b/tests/posix/de_morgan_or.sh
@@ -0,0 +1 @@
+bfs_diff basic \( \! -name 'foo' -o \! -type f \)
diff --git a/tests/test_deep_strict.out b/tests/posix/deep.out
index c385fce..c385fce 100644
--- a/tests/test_deep_strict.out
+++ b/tests/posix/deep.out
diff --git a/tests/posix/deep.sh b/tests/posix/deep.sh
new file mode 100644
index 0000000..3d1cd60
--- /dev/null
+++ b/tests/posix/deep.sh
@@ -0,0 +1,4 @@
+closefrom 4
+
+ulimit -n 16
+bfs_diff deep -type f -exec bash -c 'echo "${1:0:6}/.../${1##*/} (${#1})"' bash {} \;
diff --git a/tests/test_stderr_fails_silently.out b/tests/posix/depth.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_stderr_fails_silently.out
+++ b/tests/posix/depth.out
diff --git a/tests/posix/depth.sh b/tests/posix/depth.sh
new file mode 100644
index 0000000..444eba5
--- /dev/null
+++ b/tests/posix/depth.sh
@@ -0,0 +1 @@
+bfs_diff basic -depth
diff --git a/tests/test_depth_error.out b/tests/posix/depth_error.out
index ed0e9a1..ed0e9a1 100644
--- a/tests/test_depth_error.out
+++ b/tests/posix/depth_error.out
diff --git a/tests/posix/depth_error.sh b/tests/posix/depth_error.sh
new file mode 100644
index 0000000..9acf976
--- /dev/null
+++ b/tests/posix/depth_error.sh
@@ -0,0 +1,11 @@
+rm -rf scratch/*
+touchp scratch/foo/bar
+chmod a-r scratch/foo
+
+bfs_diff scratch -depth
+ret=$?
+
+chmod +r scratch/foo
+rm -rf scratch/*
+
+[ $ret -eq $EX_BFS ]
diff --git a/tests/test_depth_slash.out b/tests/posix/depth_slash.out
index 77526d5..77526d5 100644
--- a/tests/test_depth_slash.out
+++ b/tests/posix/depth_slash.out
diff --git a/tests/posix/depth_slash.sh b/tests/posix/depth_slash.sh
new file mode 100644
index 0000000..f73e9f1
--- /dev/null
+++ b/tests/posix/depth_slash.sh
@@ -0,0 +1 @@
+bfs_diff basic/ -depth
diff --git a/tests/test_double_negation.out b/tests/posix/double_negation.out
index e9d47b1..e9d47b1 100644
--- a/tests/test_double_negation.out
+++ b/tests/posix/double_negation.out
diff --git a/tests/posix/double_negation.sh b/tests/posix/double_negation.sh
new file mode 100644
index 0000000..eefe464
--- /dev/null
+++ b/tests/posix/double_negation.sh
@@ -0,0 +1 @@
+bfs_diff basic \! \! -name 'foo'
diff --git a/tests/test_true.out b/tests/posix/exec.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_true.out
+++ b/tests/posix/exec.out
diff --git a/tests/posix/exec.sh b/tests/posix/exec.sh
new file mode 100644
index 0000000..96c897b
--- /dev/null
+++ b/tests/posix/exec.sh
@@ -0,0 +1 @@
+bfs_diff basic -exec echo {} \;
diff --git a/tests/test_uid.out b/tests/posix/exec_nonexistent.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_uid.out
+++ b/tests/posix/exec_nonexistent.out
diff --git a/tests/posix/exec_nonexistent.sh b/tests/posix/exec_nonexistent.sh
new file mode 100644
index 0000000..b4e08e0
--- /dev/null
+++ b/tests/posix/exec_nonexistent.sh
@@ -0,0 +1,8 @@
+# Failure to execute the command should lead to an error message and
+# non-zero exit status. See https://unix.stackexchange.com/q/704522/56202
+
+stderr=$(invoke_bfs basic -exec "$TESTS/nonexistent" {} \; 2>&1 >/dev/null)
+[ -n "$stderr" ] || return 1
+
+bfs_diff basic -print -exec "$TESTS/nonexistent" {} \; -print
+(($? == EX_BFS))
diff --git a/tests/test_uid_minus.out b/tests/posix/exec_nopath.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_uid_minus.out
+++ b/tests/posix/exec_nopath.out
diff --git a/tests/posix/exec_nopath.sh b/tests/posix/exec_nopath.sh
new file mode 100644
index 0000000..6e05d2e
--- /dev/null
+++ b/tests/posix/exec_nopath.sh
@@ -0,0 +1,7 @@
+(
+ unset PATH
+ invoke_bfs basic -exec echo {} \; >"$OUT"
+)
+
+sort_output
+diff_output
diff --git a/tests/test_exec_plus.out b/tests/posix/exec_plus.out
index f6b423b..f6b423b 100644
--- a/tests/test_exec_plus.out
+++ b/tests/posix/exec_plus.out
diff --git a/tests/posix/exec_plus.sh b/tests/posix/exec_plus.sh
new file mode 100644
index 0000000..56a93f1
--- /dev/null
+++ b/tests/posix/exec_plus.sh
@@ -0,0 +1 @@
+bfs_diff basic -exec "$TESTS/sort-args.sh" {} +
diff --git a/tests/test_uid_minus_plus.out b/tests/posix/exec_plus_nonexistent.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_uid_minus_plus.out
+++ b/tests/posix/exec_plus_nonexistent.out
diff --git a/tests/posix/exec_plus_nonexistent.sh b/tests/posix/exec_plus_nonexistent.sh
new file mode 100644
index 0000000..f96099e
--- /dev/null
+++ b/tests/posix/exec_plus_nonexistent.sh
@@ -0,0 +1,5 @@
+stderr=$(invoke_bfs basic -exec "$TESTS/nonexistent" {} + 2>&1 >/dev/null)
+[ -n "$stderr" ] || return 1
+
+bfs_diff basic -exec "$TESTS/nonexistent" {} + -print
+(($? == EX_BFS))
diff --git a/tests/test_exec_plus_semicolon.out b/tests/posix/exec_plus_semicolon.out
index f33c48f..f33c48f 100644
--- a/tests/test_exec_plus_semicolon.out
+++ b/tests/posix/exec_plus_semicolon.out
diff --git a/tests/posix/exec_plus_semicolon.sh b/tests/posix/exec_plus_semicolon.sh
new file mode 100644
index 0000000..449a3f9
--- /dev/null
+++ b/tests/posix/exec_plus_semicolon.sh
@@ -0,0 +1,5 @@
+# POSIX says:
+# Only a <plus-sign> that immediately follows an argument containing only the two characters "{}"
+# shall punctuate the end of the primary expression. Other uses of the <plus-sign> shall not be
+# treated as special.
+bfs_diff basic -exec echo foo {} bar + baz \;
diff --git a/tests/test_uid_name.out b/tests/posix/exec_plus_status.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_uid_name.out
+++ b/tests/posix/exec_plus_status.out
diff --git a/tests/posix/exec_plus_status.sh b/tests/posix/exec_plus_status.sh
new file mode 100644
index 0000000..ea9e5ef
--- /dev/null
+++ b/tests/posix/exec_plus_status.sh
@@ -0,0 +1,4 @@
+# -exec ... {} + should always return true, but if the command fails, bfs
+# should exit with a non-zero status
+bfs_diff basic -exec false {} + -print
+(($? == EX_BFS))
diff --git a/tests/posix/extra_paren.sh b/tests/posix/extra_paren.sh
new file mode 100644
index 0000000..cd8e8f8
--- /dev/null
+++ b/tests/posix/extra_paren.sh
@@ -0,0 +1 @@
+fail invoke_bfs basic -print \)
diff --git a/tests/test_flag_comma.out b/tests/posix/flag_comma.out
index 3574388..3574388 100644
--- a/tests/test_flag_comma.out
+++ b/tests/posix/flag_comma.out
diff --git a/tests/posix/flag_comma.sh b/tests/posix/flag_comma.sh
new file mode 100644
index 0000000..cec87e7
--- /dev/null
+++ b/tests/posix/flag_comma.sh
@@ -0,0 +1,3 @@
+# , is a filename until a non-flag is seen
+cd weirdnames
+bfs_diff -L ',' -print
diff --git a/tests/test_flag_weird_names.out b/tests/posix/flag_weird_names.out
index c395659..c395659 100644
--- a/tests/test_flag_weird_names.out
+++ b/tests/posix/flag_weird_names.out
diff --git a/tests/posix/flag_weird_names.sh b/tests/posix/flag_weird_names.sh
new file mode 100644
index 0000000..f6596e9
--- /dev/null
+++ b/tests/posix/flag_weird_names.sh
@@ -0,0 +1,2 @@
+cd weirdnames
+bfs_diff -L '-' '(-' '!-' ',' ')' './(' './!' \( \! -print -o -print \)
diff --git a/tests/test_uid_plus.out b/tests/posix/group_id.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_uid_plus.out
+++ b/tests/posix/group_id.out
diff --git a/tests/posix/group_id.sh b/tests/posix/group_id.sh
new file mode 100644
index 0000000..2ff7bb3
--- /dev/null
+++ b/tests/posix/group_id.sh
@@ -0,0 +1 @@
+bfs_diff basic -group "$(id -g)"
diff --git a/tests/test_uid_plus_plus.out b/tests/posix/group_name.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_uid_plus_plus.out
+++ b/tests/posix/group_name.out
diff --git a/tests/posix/group_name.sh b/tests/posix/group_name.sh
new file mode 100644
index 0000000..36799d9
--- /dev/null
+++ b/tests/posix/group_name.sh
@@ -0,0 +1 @@
+bfs_diff basic -group "$(id -gn)"
diff --git a/tests/test_unique_depth.out b/tests/posix/group_nogroup.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_unique_depth.out
+++ b/tests/posix/group_nogroup.out
diff --git a/tests/posix/group_nogroup.sh b/tests/posix/group_nogroup.sh
new file mode 100644
index 0000000..cbd1ffc
--- /dev/null
+++ b/tests/posix/group_nogroup.sh
@@ -0,0 +1,2 @@
+# Regression test: this was wrongly optimized to -false
+bfs_diff basic -group "$(id -g)" \! -nogroup
diff --git a/tests/test_implicit_and.out b/tests/posix/implicit_and.out
index 722962c..722962c 100644
--- a/tests/test_implicit_and.out
+++ b/tests/posix/implicit_and.out
diff --git a/tests/posix/implicit_and.sh b/tests/posix/implicit_and.sh
new file mode 100644
index 0000000..161ab0b
--- /dev/null
+++ b/tests/posix/implicit_and.sh
@@ -0,0 +1 @@
+bfs_diff basic -name foo -type d
diff --git a/tests/posix/incomplete.sh b/tests/posix/incomplete.sh
new file mode 100644
index 0000000..07b1b61
--- /dev/null
+++ b/tests/posix/incomplete.sh
@@ -0,0 +1 @@
+fail invoke_bfs basic \(
diff --git a/tests/test_links_plus.out b/tests/posix/links.out
index 996ffc8..996ffc8 100644
--- a/tests/test_links_plus.out
+++ b/tests/posix/links.out
diff --git a/tests/posix/links.sh b/tests/posix/links.sh
new file mode 100644
index 0000000..3d8ad80
--- /dev/null
+++ b/tests/posix/links.sh
@@ -0,0 +1 @@
+bfs_diff links -type f -links 2
diff --git a/tests/test_links_minus.out b/tests/posix/links_minus.out
index eda26f1..eda26f1 100644
--- a/tests/test_links_minus.out
+++ b/tests/posix/links_minus.out
diff --git a/tests/posix/links_minus.sh b/tests/posix/links_minus.sh
new file mode 100644
index 0000000..3ee0803
--- /dev/null
+++ b/tests/posix/links_minus.sh
@@ -0,0 +1 @@
+bfs_diff links -type f -links -2
diff --git a/tests/test_samefile.out b/tests/posix/links_plus.out
index 996ffc8..996ffc8 100644
--- a/tests/test_samefile.out
+++ b/tests/posix/links_plus.out
diff --git a/tests/posix/links_plus.sh b/tests/posix/links_plus.sh
new file mode 100644
index 0000000..375834b
--- /dev/null
+++ b/tests/posix/links_plus.sh
@@ -0,0 +1 @@
+bfs_diff links -type f -links +1
diff --git a/tests/posix/missing_paren.sh b/tests/posix/missing_paren.sh
new file mode 100644
index 0000000..ac8dd60
--- /dev/null
+++ b/tests/posix/missing_paren.sh
@@ -0,0 +1 @@
+fail invoke_bfs basic \( -print
diff --git a/tests/test_name_star_star.out b/tests/posix/name.out
index a9e5d42..a9e5d42 100644
--- a/tests/test_name_star_star.out
+++ b/tests/posix/name.out
diff --git a/tests/posix/name.sh b/tests/posix/name.sh
new file mode 100644
index 0000000..a673ad0
--- /dev/null
+++ b/tests/posix/name.sh
@@ -0,0 +1 @@
+bfs_diff basic -name '*f*'
diff --git a/tests/test_perm_leading_plus_symbolic.out b/tests/posix/name_backslash.out
index e69de29..e69de29 100644
--- a/tests/test_perm_leading_plus_symbolic.out
+++ b/tests/posix/name_backslash.out
diff --git a/tests/posix/name_backslash.sh b/tests/posix/name_backslash.sh
new file mode 100644
index 0000000..ff9b539
--- /dev/null
+++ b/tests/posix/name_backslash.sh
@@ -0,0 +1,2 @@
+# An unescaped \ doesn't match
+bfs_diff weirdnames -name '\'
diff --git a/tests/test_name_bracket.out b/tests/posix/name_bracket.out
index 5ff3c0c..5ff3c0c 100644
--- a/tests/test_name_bracket.out
+++ b/tests/posix/name_bracket.out
diff --git a/tests/posix/name_bracket.sh b/tests/posix/name_bracket.sh
new file mode 100644
index 0000000..84a417f
--- /dev/null
+++ b/tests/posix/name_bracket.sh
@@ -0,0 +1,5 @@
+# fnmatch() is broken on macOS
+skip_if test "$UNAME" = "Darwin"
+
+# An unclosed [ should be matched literally
+bfs_diff weirdnames -name '['
diff --git a/tests/test_name_character_class.out b/tests/posix/name_character_class.out
index e9d47b1..e9d47b1 100644
--- a/tests/test_name_character_class.out
+++ b/tests/posix/name_character_class.out
diff --git a/tests/posix/name_character_class.sh b/tests/posix/name_character_class.sh
new file mode 100644
index 0000000..ecda190
--- /dev/null
+++ b/tests/posix/name_character_class.sh
@@ -0,0 +1 @@
+bfs_diff basic -name '[e-g][!a-n][!p-z]'
diff --git a/tests/test_name_double_backslash.out b/tests/posix/name_double_backslash.out
index 45ceda0..45ceda0 100644
--- a/tests/test_name_double_backslash.out
+++ b/tests/posix/name_double_backslash.out
diff --git a/tests/posix/name_double_backslash.sh b/tests/posix/name_double_backslash.sh
new file mode 100644
index 0000000..009553a
--- /dev/null
+++ b/tests/posix/name_double_backslash.sh
@@ -0,0 +1,2 @@
+# An escaped \\ matches
+bfs_diff weirdnames -name '\\'
diff --git a/tests/test_name_root.out b/tests/posix/name_root.out
index 511198f..511198f 100644
--- a/tests/test_name_root.out
+++ b/tests/posix/name_root.out
diff --git a/tests/posix/name_root.sh b/tests/posix/name_root.sh
new file mode 100644
index 0000000..785861e
--- /dev/null
+++ b/tests/posix/name_root.sh
@@ -0,0 +1 @@
+bfs_diff basic/a -name a
diff --git a/tests/test_quit.out b/tests/posix/name_root_depth.out
index cf4d5a9..cf4d5a9 100644
--- a/tests/test_quit.out
+++ b/tests/posix/name_root_depth.out
diff --git a/tests/posix/name_root_depth.sh b/tests/posix/name_root_depth.sh
new file mode 100644
index 0000000..dc3b8bb
--- /dev/null
+++ b/tests/posix/name_root_depth.sh
@@ -0,0 +1 @@
+bfs_diff basic/g -depth -name g
diff --git a/tests/test_parens.out b/tests/posix/name_star_star.out
index a9e5d42..a9e5d42 100644
--- a/tests/test_parens.out
+++ b/tests/posix/name_star_star.out
diff --git a/tests/posix/name_star_star.sh b/tests/posix/name_star_star.sh
new file mode 100644
index 0000000..035f635
--- /dev/null
+++ b/tests/posix/name_star_star.sh
@@ -0,0 +1 @@
+bfs_diff basic -name '**f**'
diff --git a/tests/test_name_trailing_slash.out b/tests/posix/name_trailing_slash.out
index daff2f5..daff2f5 100644
--- a/tests/test_name_trailing_slash.out
+++ b/tests/posix/name_trailing_slash.out
diff --git a/tests/posix/name_trailing_slash.sh b/tests/posix/name_trailing_slash.sh
new file mode 100644
index 0000000..ab058d1
--- /dev/null
+++ b/tests/posix/name_trailing_slash.sh
@@ -0,0 +1 @@
+bfs_diff basic/g/ -name g
diff --git a/tests/test_newerma.out b/tests/posix/newer.out
index 7f6c0dd..7f6c0dd 100644
--- a/tests/test_newerma.out
+++ b/tests/posix/newer.out
diff --git a/tests/posix/newer.sh b/tests/posix/newer.sh
new file mode 100644
index 0000000..860623a
--- /dev/null
+++ b/tests/posix/newer.sh
@@ -0,0 +1 @@
+bfs_diff times -newer times/a
diff --git a/tests/test_newer_link.out b/tests/posix/newer_link.out
index d2dcdd1..d2dcdd1 100644
--- a/tests/test_newer_link.out
+++ b/tests/posix/newer_link.out
diff --git a/tests/posix/newer_link.sh b/tests/posix/newer_link.sh
new file mode 100644
index 0000000..685ac78
--- /dev/null
+++ b/tests/posix/newer_link.sh
@@ -0,0 +1 @@
+bfs_diff times -newer times/l
diff --git a/tests/test_perm_leading_plus_symbolic_minus.out b/tests/posix/nogroup.out
index e69de29..e69de29 100644
--- a/tests/test_perm_leading_plus_symbolic_minus.out
+++ b/tests/posix/nogroup.out
diff --git a/tests/posix/nogroup.sh b/tests/posix/nogroup.sh
new file mode 100644
index 0000000..60ffd68
--- /dev/null
+++ b/tests/posix/nogroup.sh
@@ -0,0 +1 @@
+bfs_diff basic -nogroup
diff --git a/tests/test_perm_symbolic.out b/tests/posix/nogroup_ulimit.out
index e69de29..e69de29 100644
--- a/tests/test_perm_symbolic.out
+++ b/tests/posix/nogroup_ulimit.out
diff --git a/tests/posix/nogroup_ulimit.sh b/tests/posix/nogroup_ulimit.sh
new file mode 100644
index 0000000..e7231d9
--- /dev/null
+++ b/tests/posix/nogroup_ulimit.sh
@@ -0,0 +1,3 @@
+closefrom 4
+ulimit -n 16
+bfs_diff deep -nogroup
diff --git a/tests/test_not_prune.out b/tests/posix/not_prune.out
index 59e3c42..59e3c42 100644
--- a/tests/test_not_prune.out
+++ b/tests/posix/not_prune.out
diff --git a/tests/posix/not_prune.sh b/tests/posix/not_prune.sh
new file mode 100644
index 0000000..6d7b092
--- /dev/null
+++ b/tests/posix/not_prune.sh
@@ -0,0 +1 @@
+bfs_diff basic \! \( -name foo -prune \)
diff --git a/tests/test_printf_empty.out b/tests/posix/nouser.out
index e69de29..e69de29 100644
--- a/tests/test_printf_empty.out
+++ b/tests/posix/nouser.out
diff --git a/tests/posix/nouser.sh b/tests/posix/nouser.sh
new file mode 100644
index 0000000..e7c48c0
--- /dev/null
+++ b/tests/posix/nouser.sh
@@ -0,0 +1 @@
+bfs_diff basic -nouser
diff --git a/tests/test_printf_w.out b/tests/posix/nouser_ulimit.out
index e69de29..e69de29 100644
--- a/tests/test_printf_w.out
+++ b/tests/posix/nouser_ulimit.out
diff --git a/tests/posix/nouser_ulimit.sh b/tests/posix/nouser_ulimit.sh
new file mode 100644
index 0000000..1d0dd65
--- /dev/null
+++ b/tests/posix/nouser_ulimit.sh
@@ -0,0 +1,3 @@
+closefrom 4
+ulimit -n 16
+bfs_diff deep -nouser
diff --git a/tests/test_or.out b/tests/posix/o.out
index 1650c4d..1650c4d 100644
--- a/tests/test_or.out
+++ b/tests/posix/o.out
diff --git a/tests/posix/o.sh b/tests/posix/o.sh
new file mode 100644
index 0000000..6dcd442
--- /dev/null
+++ b/tests/posix/o.sh
@@ -0,0 +1 @@
+bfs_diff basic -name foo -o -type d
diff --git a/tests/test_ok_stdin.out b/tests/posix/ok_stdin.out
index 7acf711..7acf711 100644
--- a/tests/test_ok_stdin.out
+++ b/tests/posix/ok_stdin.out
diff --git a/tests/posix/ok_stdin.sh b/tests/posix/ok_stdin.sh
new file mode 100644
index 0000000..a190d81
--- /dev/null
+++ b/tests/posix/ok_stdin.sh
@@ -0,0 +1,3 @@
+# -ok should *not* close stdin
+# See https://savannah.gnu.org/bugs/?24561
+yes | bfs_diff basic -ok bash -c 'printf "%s? " "$1" && head -n1' bash {} \;
diff --git a/tests/test_quit_before_print.out b/tests/posix/or_purity.out
index e69de29..e69de29 100644
--- a/tests/test_quit_before_print.out
+++ b/tests/posix/or_purity.out
diff --git a/tests/posix/or_purity.sh b/tests/posix/or_purity.sh
new file mode 100644
index 0000000..277b18b
--- /dev/null
+++ b/tests/posix/or_purity.sh
@@ -0,0 +1,2 @@
+# Regression test: (-o lhs(pure) rhs(always_true)) <==> rhs is only valid if rhs is pure
+bfs_diff basic -name '*' -o -print
diff --git a/tests/test_regextype_grep.out b/tests/posix/parens.out
index a9e5d42..a9e5d42 100644
--- a/tests/test_regextype_grep.out
+++ b/tests/posix/parens.out
diff --git a/tests/posix/parens.sh b/tests/posix/parens.sh
new file mode 100644
index 0000000..abbb20f
--- /dev/null
+++ b/tests/posix/parens.sh
@@ -0,0 +1 @@
+bfs_diff basic \( -name '*f*' \)
diff --git a/tests/test_wholename.out b/tests/posix/path.out
index ae1ae21..ae1ae21 100644
--- a/tests/test_wholename.out
+++ b/tests/posix/path.out
diff --git a/tests/posix/path.sh b/tests/posix/path.sh
new file mode 100644
index 0000000..04606eb
--- /dev/null
+++ b/tests/posix/path.sh
@@ -0,0 +1 @@
+bfs_diff basic -path 'basic/*f*'
diff --git a/tests/test_perm_000.out b/tests/posix/perm_000.out
index 5fd30bc..5fd30bc 100644
--- a/tests/test_perm_000.out
+++ b/tests/posix/perm_000.out
diff --git a/tests/posix/perm_000.sh b/tests/posix/perm_000.sh
new file mode 100644
index 0000000..ee25f23
--- /dev/null
+++ b/tests/posix/perm_000.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm 000
diff --git a/tests/test_perm_000_slash.out b/tests/posix/perm_000_minus.out
index d7494b8..d7494b8 100644
--- a/tests/test_perm_000_slash.out
+++ b/tests/posix/perm_000_minus.out
diff --git a/tests/posix/perm_000_minus.sh b/tests/posix/perm_000_minus.sh
new file mode 100644
index 0000000..5027b91
--- /dev/null
+++ b/tests/posix/perm_000_minus.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm -000
diff --git a/tests/test_perm_222.out b/tests/posix/perm_222.out
index 1690e43..1690e43 100644
--- a/tests/test_perm_222.out
+++ b/tests/posix/perm_222.out
diff --git a/tests/posix/perm_222.sh b/tests/posix/perm_222.sh
new file mode 100644
index 0000000..40f5804
--- /dev/null
+++ b/tests/posix/perm_222.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm 222
diff --git a/tests/test_perm_222_minus.out b/tests/posix/perm_222_minus.out
index 1690e43..1690e43 100644
--- a/tests/test_perm_222_minus.out
+++ b/tests/posix/perm_222_minus.out
diff --git a/tests/posix/perm_222_minus.sh b/tests/posix/perm_222_minus.sh
new file mode 100644
index 0000000..4e7ad5a
--- /dev/null
+++ b/tests/posix/perm_222_minus.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm -222
diff --git a/tests/test_perm_644.out b/tests/posix/perm_644.out
index 4e64e49..4e64e49 100644
--- a/tests/test_perm_644.out
+++ b/tests/posix/perm_644.out
diff --git a/tests/posix/perm_644.sh b/tests/posix/perm_644.sh
new file mode 100644
index 0000000..9a4f41d
--- /dev/null
+++ b/tests/posix/perm_644.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm 644
diff --git a/tests/test_perm_644_minus.out b/tests/posix/perm_644_minus.out
index 2e2576b..2e2576b 100644
--- a/tests/test_perm_644_minus.out
+++ b/tests/posix/perm_644_minus.out
diff --git a/tests/posix/perm_644_minus.sh b/tests/posix/perm_644_minus.sh
new file mode 100644
index 0000000..6464f84
--- /dev/null
+++ b/tests/posix/perm_644_minus.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm -644
diff --git a/tests/test_size_big.out b/tests/posix/perm_leading_plus_symbolic_minus.out
index e69de29..e69de29 100644
--- a/tests/test_size_big.out
+++ b/tests/posix/perm_leading_plus_symbolic_minus.out
diff --git a/tests/posix/perm_leading_plus_symbolic_minus.sh b/tests/posix/perm_leading_plus_symbolic_minus.sh
new file mode 100644
index 0000000..60389c0
--- /dev/null
+++ b/tests/posix/perm_leading_plus_symbolic_minus.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm -+rwx
diff --git a/tests/test_perm_setid.out b/tests/posix/perm_setid.out
index 865a74e..865a74e 100644
--- a/tests/test_perm_setid.out
+++ b/tests/posix/perm_setid.out
diff --git a/tests/posix/perm_setid.sh b/tests/posix/perm_setid.sh
new file mode 100644
index 0000000..3b98647
--- /dev/null
+++ b/tests/posix/perm_setid.sh
@@ -0,0 +1 @@
+bfs_diff rainbow -perm -u+s -o -perm -g+s
diff --git a/tests/test_perm_sticky.out b/tests/posix/perm_sticky.out
index c07eb61..c07eb61 100644
--- a/tests/test_perm_sticky.out
+++ b/tests/posix/perm_sticky.out
diff --git a/tests/posix/perm_sticky.sh b/tests/posix/perm_sticky.sh
new file mode 100644
index 0000000..6bdf8e9
--- /dev/null
+++ b/tests/posix/perm_sticky.sh
@@ -0,0 +1 @@
+bfs_diff rainbow -perm -a+t
diff --git a/tests/test_xtype_reorder.out b/tests/posix/perm_symbolic.out
index e69de29..e69de29 100644
--- a/tests/test_xtype_reorder.out
+++ b/tests/posix/perm_symbolic.out
diff --git a/tests/posix/perm_symbolic.sh b/tests/posix/perm_symbolic.sh
new file mode 100644
index 0000000..5cfddb6
--- /dev/null
+++ b/tests/posix/perm_symbolic.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm a+r,u=wX,g+wX-w
diff --git a/tests/test_perm_symbolic_minus.out b/tests/posix/perm_symbolic_minus.out
index 2e2576b..2e2576b 100644
--- a/tests/test_perm_symbolic_minus.out
+++ b/tests/posix/perm_symbolic_minus.out
diff --git a/tests/posix/perm_symbolic_minus.sh b/tests/posix/perm_symbolic_minus.sh
new file mode 100644
index 0000000..b6ba3a5
--- /dev/null
+++ b/tests/posix/perm_symbolic_minus.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm -a+r,u=wX,g+wX-w
diff --git a/tests/test_permcopy.out b/tests/posix/permcopy.out
index 4e64e49..4e64e49 100644
--- a/tests/test_permcopy.out
+++ b/tests/posix/permcopy.out
diff --git a/tests/posix/permcopy.sh b/tests/posix/permcopy.sh
new file mode 100644
index 0000000..3c85cce
--- /dev/null
+++ b/tests/posix/permcopy.sh
@@ -0,0 +1 @@
+bfs_diff perms -perm u+rw,g+u-w,o=g
diff --git a/tests/test_prune.out b/tests/posix/prune.out
index e9d47b1..e9d47b1 100644
--- a/tests/test_prune.out
+++ b/tests/posix/prune.out
diff --git a/tests/posix/prune.sh b/tests/posix/prune.sh
new file mode 100644
index 0000000..b48ab48
--- /dev/null
+++ b/tests/posix/prune.sh
@@ -0,0 +1 @@
+bfs_diff basic -name foo -prune
diff --git a/tests/test_prune_file.out b/tests/posix/prune_file.out
index 7575ae4..7575ae4 100644
--- a/tests/test_prune_file.out
+++ b/tests/posix/prune_file.out
diff --git a/tests/posix/prune_file.sh b/tests/posix/prune_file.sh
new file mode 100644
index 0000000..29a3a33
--- /dev/null
+++ b/tests/posix/prune_file.sh
@@ -0,0 +1 @@
+bfs_diff basic -print -name '?' -prune
diff --git a/tests/test_prune_or_print.out b/tests/posix/prune_or_print.out
index 59e3c42..59e3c42 100644
--- a/tests/test_prune_or_print.out
+++ b/tests/posix/prune_or_print.out
diff --git a/tests/posix/prune_or_print.sh b/tests/posix/prune_or_print.sh
new file mode 100644
index 0000000..85b97fd
--- /dev/null
+++ b/tests/posix/prune_or_print.sh
@@ -0,0 +1 @@
+bfs_diff basic -name foo -prune -o -print
diff --git a/tests/test_size.out b/tests/posix/size.out
index eeabbd7..eeabbd7 100644
--- a/tests/test_size.out
+++ b/tests/posix/size.out
diff --git a/tests/posix/size.sh b/tests/posix/size.sh
new file mode 100644
index 0000000..1e7528a
--- /dev/null
+++ b/tests/posix/size.sh
@@ -0,0 +1 @@
+bfs_diff basic -type f -size 0
diff --git a/tests/test_size_bytes.out b/tests/posix/size_bytes.out
index 279f3f1..279f3f1 100644
--- a/tests/test_size_bytes.out
+++ b/tests/posix/size_bytes.out
diff --git a/tests/posix/size_bytes.sh b/tests/posix/size_bytes.sh
new file mode 100644
index 0000000..6a68321
--- /dev/null
+++ b/tests/posix/size_bytes.sh
@@ -0,0 +1 @@
+bfs_diff basic -type f -size +0c
diff --git a/tests/test_size_plus.out b/tests/posix/size_plus.out
index 279f3f1..279f3f1 100644
--- a/tests/test_size_plus.out
+++ b/tests/posix/size_plus.out
diff --git a/tests/posix/size_plus.sh b/tests/posix/size_plus.sh
new file mode 100644
index 0000000..01853d5
--- /dev/null
+++ b/tests/posix/size_plus.sh
@@ -0,0 +1 @@
+bfs_diff basic -type f -size +0
diff --git a/tests/test_type_bind_mount.out b/tests/posix/type_bind_mount.out
index 6435159..6435159 100644
--- a/tests/test_type_bind_mount.out
+++ b/tests/posix/type_bind_mount.out
diff --git a/tests/posix/type_bind_mount.sh b/tests/posix/type_bind_mount.sh
new file mode 100644
index 0000000..87b1549
--- /dev/null
+++ b/tests/posix/type_bind_mount.sh
@@ -0,0 +1,12 @@
+skip_unless test "$SUDO"
+skip_unless test "$UNAME" = "Linux"
+
+rm -rf scratch/*
+$TOUCH scratch/{file,null}
+sudo mount --bind /dev/null scratch/null
+
+bfs_diff scratch -type c
+ret=$?
+
+sudo umount scratch/null
+return $ret
diff --git a/tests/test_type_d.out b/tests/posix/type_d.out
index e604709..e604709 100644
--- a/tests/test_type_d.out
+++ b/tests/posix/type_d.out
diff --git a/tests/posix/type_d.sh b/tests/posix/type_d.sh
new file mode 100644
index 0000000..8d06b73
--- /dev/null
+++ b/tests/posix/type_d.sh
@@ -0,0 +1 @@
+bfs_diff basic -type d
diff --git a/tests/test_type_f.out b/tests/posix/type_f.out
index 6218a0c..6218a0c 100644
--- a/tests/test_type_f.out
+++ b/tests/posix/type_f.out
diff --git a/tests/posix/type_f.sh b/tests/posix/type_f.sh
new file mode 100644
index 0000000..1fd0c8c
--- /dev/null
+++ b/tests/posix/type_f.sh
@@ -0,0 +1 @@
+bfs_diff basic -type f
diff --git a/tests/test_type_l.out b/tests/posix/type_l.out
index f2c8b19..f2c8b19 100644
--- a/tests/test_type_l.out
+++ b/tests/posix/type_l.out
diff --git a/tests/posix/type_l.sh b/tests/posix/type_l.sh
new file mode 100644
index 0000000..457f74d
--- /dev/null
+++ b/tests/posix/type_l.sh
@@ -0,0 +1 @@
+bfs_diff links/skip -type l
diff --git a/tests/test_user_id.out b/tests/posix/user_id.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_user_id.out
+++ b/tests/posix/user_id.out
diff --git a/tests/posix/user_id.sh b/tests/posix/user_id.sh
new file mode 100644
index 0000000..c3e4b31
--- /dev/null
+++ b/tests/posix/user_id.sh
@@ -0,0 +1 @@
+bfs_diff basic -user "$(id -u)"
diff --git a/tests/test_user_name.out b/tests/posix/user_name.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_user_name.out
+++ b/tests/posix/user_name.out
diff --git a/tests/posix/user_name.sh b/tests/posix/user_name.sh
new file mode 100644
index 0000000..8599249
--- /dev/null
+++ b/tests/posix/user_name.sh
@@ -0,0 +1 @@
+bfs_diff basic -user "$(id -un)"
diff --git a/tests/test_user_nouser.out b/tests/posix/user_nouser.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/test_user_nouser.out
+++ b/tests/posix/user_nouser.out
diff --git a/tests/posix/user_nouser.sh b/tests/posix/user_nouser.sh
new file mode 100644
index 0000000..e72bd45
--- /dev/null
+++ b/tests/posix/user_nouser.sh
@@ -0,0 +1,2 @@
+# Regression test: this was wrongly optimized to -false
+bfs_diff basic -user "$(id -u)" \! -nouser
diff --git a/tests/test_weird_names.out b/tests/posix/weird_names.out
index c395659..c395659 100644
--- a/tests/test_weird_names.out
+++ b/tests/posix/weird_names.out
diff --git a/tests/posix/weird_names.sh b/tests/posix/weird_names.sh
new file mode 100644
index 0000000..8a9a8cd
--- /dev/null
+++ b/tests/posix/weird_names.sh
@@ -0,0 +1,2 @@
+cd weirdnames
+bfs_diff '-' '(-' '!-' ',' ')' './(' './!' \( \! -print -o -print \)
diff --git a/tests/test_xdev.out b/tests/posix/xdev.out
index f7839fb..f7839fb 100644
--- a/tests/test_xdev.out
+++ b/tests/posix/xdev.out
diff --git a/tests/posix/xdev.sh b/tests/posix/xdev.sh
new file mode 100644
index 0000000..cbb3347
--- /dev/null
+++ b/tests/posix/xdev.sh
@@ -0,0 +1,13 @@
+skip_unless test "$SUDO"
+skip_if test "$UNAME" = "Darwin"
+
+rm -rf scratch/*
+mkdir scratch/{foo,mnt}
+sudo mount -t tmpfs tmpfs scratch/mnt
+$TOUCH scratch/foo/bar scratch/mnt/baz
+
+bfs_diff scratch -xdev
+ret=$?
+
+sudo umount scratch/mnt
+return $ret
diff --git a/tests/test_fprint_duplicate_stdout.out b/tests/test_fprint_duplicate_stdout.out
deleted file mode 100644
index 6c21751..0000000
--- a/tests/test_fprint_duplicate_stdout.out
+++ /dev/null
@@ -1,38 +0,0 @@
-basic
-basic
-basic/a
-basic/a
-basic/b
-basic/b
-basic/c
-basic/c
-basic/c/d
-basic/c/d
-basic/e
-basic/e
-basic/e/f
-basic/e/f
-basic/g
-basic/g
-basic/g/h
-basic/g/h
-basic/i
-basic/i
-basic/j
-basic/j
-basic/j/foo
-basic/j/foo
-basic/k
-basic/k
-basic/k/foo
-basic/k/foo
-basic/k/foo/bar
-basic/k/foo/bar
-basic/l
-basic/l
-basic/l/foo
-basic/l/foo
-basic/l/foo/bar
-basic/l/foo/bar
-basic/l/foo/bar/baz
-basic/l/foo/bar/baz
diff --git a/tests/tests.sh b/tests/tests.sh
index 64bd04d..d957cf1 100755
--- a/tests/tests.sh
+++ b/tests/tests.sh
@@ -16,6 +16,8 @@
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #
############################################################################
+shopt -s extdebug
+
set -euP
umask 022
@@ -92,7 +94,7 @@ function usage() {
cat <<EOF
Usage: ${GRN}$0${RST} [${BLU}--bfs${RST}=${MAG}path/to/bfs${RST}] [${BLU}--posix${RST}] [${BLU}--bsd${RST}] [${BLU}--gnu${RST}] [${BLU}--all${RST}] [${BLU}--sudo${RST}]
$pad [${BLU}--stop${RST}] [${BLU}--noclean${RST}] [${BLU}--update${RST}] [${BLU}--verbose${RST}[=${BLD}LEVEL${RST}]] [${BLU}--help${RST}]
- $pad [${BLD}test_*${RST} [${BLD}test_*${RST} ...]]
+ $pad [${BLD}TEST${RST} [${BLD}TEST${RST} ...]]
${BLU}--bfs${RST}=${MAG}path/to/bfs${RST}
Set the path to the bfs executable to test (default: ${MAG}./bin/bfs${RST})
@@ -126,13 +128,14 @@ Usage: ${GRN}$0${RST} [${BLU}--bfs${RST}=${MAG}path/to/bfs${RST}] [${BLU}--posix
${BLU}--help${RST}
This message
- ${BLD}test_*${RST}
- Select individual test cases to run
+ ${BLD}TEST${RST}
+ Select individual test cases to run (e.g. ${BLD}posix/basic${RST})
EOF
}
DEFAULT=yes
POSIX=
+COMMON=
BSD=
GNU=
ALL=
@@ -160,16 +163,19 @@ for arg; do
--bsd)
DEFAULT=
POSIX=yes
+ COMMON=yes
BSD=yes
;;
--gnu)
DEFAULT=
POSIX=yes
+ COMMON=yes
GNU=yes
;;
--all)
DEFAULT=
POSIX=yes
+ COMMON=yes
BSD=yes
GNU=yes
ALL=yes
@@ -209,7 +215,7 @@ for arg; do
usage
exit 0
;;
- test_*)
+ */*)
EXPLICIT=yes
SUDO=yes
enabled_tests+=("$arg")
@@ -222,697 +228,14 @@ for arg; do
esac
done
-posix_tests=(
- # General parsing
- test_basic
-
- test_parens
- test_bang
- test_implicit_and
- test_a
- test_o
-
- test_weird_names
-
- test_incomplete
- test_missing_paren
- test_extra_paren
-
- # Flags
-
- test_H
- test_H_slash
- test_H_broken
- test_H_notdir
- test_H_loops
-
- test_L
- test_L_broken
- test_L_notdir
- test_L_loops
-
- test_flag_weird_names
- test_flag_comma
-
- # Primaries
-
- test_depth
- test_depth_slash
- test_depth_error
- test_L_depth
-
- test_exec
- test_exec_nonexistent
- test_exec_nopath
- test_exec_plus
- test_exec_plus_nonexistent
- test_exec_plus_status
- test_exec_plus_semicolon
-
- test_group_name
- test_group_id
- test_group_nogroup
-
- test_links
- test_links_plus
- test_links_minus
-
- test_name
- test_name_root
- test_name_root_depth
- test_name_trailing_slash
- test_name_star_star
- test_name_character_class
- test_name_bracket
- test_name_backslash
- test_name_double_backslash
-
- test_newer
- test_newer_link
-
- test_nogroup
- test_nogroup_ulimit
-
- test_nouser
- test_nouser_ulimit
-
- test_ok_stdin
-
- test_path
-
- test_perm_000
- test_perm_000_minus
- test_perm_222
- test_perm_222_minus
- test_perm_644
- test_perm_644_minus
- test_perm_symbolic
- test_perm_symbolic_minus
- test_perm_leading_plus_symbolic_minus
- test_permcopy
- test_perm_setid
- test_perm_sticky
-
- test_prune
- test_prune_file
- test_prune_or_print
- test_not_prune
-
- test_size
- test_size_plus
- test_size_bytes
-
- test_type_d
- test_type_f
- test_type_l
- test_H_type_l
- test_L_type_l
- test_type_bind_mount
-
- test_user_name
- test_user_id
- test_user_nouser
-
- test_xdev
- test_L_xdev
-
- # Closed file descriptors
- test_closed_stdin
- test_closed_stdout
- test_closed_stderr
-
- # PATH_MAX handling
- test_deep
-
- # Optimizer tests
- test_or_purity
- test_double_negation
- test_de_morgan_not
- test_de_morgan_and
- test_de_morgan_or
- test_data_flow_group
- test_data_flow_user
- test_data_flow_type
- test_data_flow_and_swap
- test_data_flow_or_swap
-)
-
-bsd_tests=(
- # Flags
-
- test_E
-
- test_P
- test_P_slash
-
- test_X
-
- test_d_path
-
- test_f
-
- test_s
-
- test_double_dash
- test_flag_double_dash
-
- # Primaries
-
- test_acl
- test_L_acl
-
- test_anewer
- test_asince
-
- test_delete
- test_delete_many
-
- test_depth_maxdepth_1
- test_depth_maxdepth_2
- test_depth_mindepth_1
- test_depth_mindepth_2
-
- test_depth_n
- test_depth_n_plus
- test_depth_n_minus
- test_depth_depth_n
- test_depth_depth_n_plus
- test_depth_depth_n_minus
- test_depth_overflow
- test_data_flow_depth
-
- test_exec_substring
-
- test_execdir_nonexistent
- test_execdir_pwd
- test_execdir_slash
- test_execdir_slash_pwd
- test_execdir_slashes
- test_execdir_ulimit
-
- test_exit
- test_exit_no_implicit_print
-
- test_flags
-
- test_follow
-
- test_gid_name
-
- test_ilname
- test_L_ilname
-
- test_iname
-
- test_inum
- test_inum_mount
- test_inum_bind_mount
-
- test_ipath
-
- test_iregex
-
- test_lname
- test_L_lname
-
- test_ls
- test_L_ls
-
- test_maxdepth
-
- test_mindepth
-
- test_mnewer
- test_H_mnewer
-
- test_mount
- test_L_mount
-
- test_msince
-
- test_mtime_units
-
- test_name_slash
- test_name_slashes
-
- test_H_newer
-
- test_newerma
- test_newermt
- test_newermt_epoch_minus_one
-
- test_ok_stdin
- test_ok_closed_stdin
-
- test_okdir_stdin
- test_okdir_closed_stdin
-
- test_perm_000_plus
- test_perm_222_plus
- test_perm_644_plus
-
- test_printx
-
- test_quit
- test_quit_child
- test_quit_depth
- test_quit_depth_child
- test_quit_after_print
- test_quit_before_print
- test_quit_implicit_print
-
- test_rm
-
- test_regex
- test_regex_parens
-
- test_samefile
- test_samefile_symlink
- test_H_samefile_symlink
- test_L_samefile_symlink
- test_samefile_broken
- test_H_samefile_broken
- test_L_samefile_broken
- test_samefile_notdir
- test_H_samefile_notdir
- test_L_samefile_notdir
-
- test_size_T
- test_size_big
-
- test_uid_name
-
- test_xattr
- test_L_xattr
-
- test_xattrname
- test_L_xattrname
-
- # Optimizer tests
- test_data_flow_sparse
-)
-
-gnu_tests=(
- # General parsing
-
- test_not
- test_and
- test_or
- test_comma
- test_precedence
-
- test_follow_comma
-
- # Flags
-
- test_P
- test_P_slash
-
- test_L_loops_continue
-
- test_double_dash
- test_flag_double_dash
-
- # Primaries
-
- test_anewer
-
- test_path_d
-
- test_daystart
- test_daystart_twice
-
- test_delete
- test_delete_many
- test_L_delete
-
- test_depth_mindepth_1
- test_depth_mindepth_2
- test_depth_maxdepth_1
- test_depth_maxdepth_2
-
- test_empty
- test_empty_special
-
- test_exec_nothing
- test_exec_substring
- test_exec_flush
- test_exec_flush_fail
- test_exec_plus_flush
- test_exec_plus_flush_fail
-
- test_execdir
- test_execdir_nonexistent
- test_execdir_substring
- test_execdir_plus_semicolon
- test_execdir_pwd
- test_execdir_slash
- test_execdir_slash_pwd
- test_execdir_slashes
- test_execdir_ulimit
-
- test_executable
-
- test_false
-
- test_files0_from_file
- test_files0_from_stdin
- test_files0_from_none
- test_files0_from_empty
- test_files0_from_nowhere
- test_files0_from_nothing
- test_files0_from_ok
-
- test_fls
-
- test_follow
-
- test_fprint
- test_fprint_duplicate
- test_fprint_error
- test_fprint_noerror
- test_fprint_noarg
- test_fprint_nonexistent
- test_fprint_truncate
-
- test_fprint0
-
- test_fprintf
- test_fprintf_nofile
- test_fprintf_noformat
-
- test_fstype
-
- test_gid
- test_gid_plus
- test_gid_plus_plus
- test_gid_minus
- test_gid_minus_plus
-
- test_ignore_readdir_race
- test_ignore_readdir_race_root
- test_ignore_readdir_race_notdir
-
- test_ilname
- test_L_ilname
-
- test_iname
-
- test_inum
- test_inum_mount
- test_inum_bind_mount
- test_inum_automount
-
- test_ipath
-
- test_iregex
-
- test_iwholename
-
- test_lname
- test_L_lname
-
- test_ls
- test_L_ls
-
- test_maxdepth
-
- test_mindepth
-
- test_mount
- test_L_mount
-
- test_name_slash
- test_name_slashes
-
- test_H_newer
-
- test_newerma
- test_newermt
- test_newermt_epoch_minus_one
-
- test_ok_closed_stdin
- test_ok_nothing
-
- test_okdir_closed_stdin
-
- test_perm_000_slash
- test_perm_222_slash
- test_perm_644_slash
- test_perm_symbolic_slash
- test_perm_leading_plus_symbolic_slash
-
- test_print_error
-
- test_print0
-
- test_printf
- test_printf_empty
- test_printf_slash
- test_printf_slashes
- test_printf_trailing_slash
- test_printf_trailing_slashes
- test_printf_flags
- test_printf_types
- test_printf_escapes
- test_printf_times
- test_printf_leak
- test_printf_nul
- test_printf_Y_error
- test_printf_H
- test_printf_u_g_ulimit
- test_printf_l_nonlink
-
- test_quit
- test_quit_child
- test_quit_depth
- test_quit_depth_child
- test_quit_after_print
- test_quit_before_print
-
- test_readable
-
- test_regex
- test_regex_parens
- test_regex_error
- test_regex_invalid_utf8
-
- test_regextype_posix_basic
- test_regextype_posix_extended
- test_regextype_ed
- test_regextype_emacs
- test_regextype_grep
- test_regextype_sed
-
- test_samefile
- test_samefile_symlink
- test_H_samefile_symlink
- test_L_samefile_symlink
- test_samefile_broken
- test_H_samefile_broken
- test_L_samefile_broken
- test_samefile_notdir
- test_H_samefile_notdir
- test_L_samefile_notdir
-
- test_size_big
-
- test_true
-
- test_uid
- test_uid_plus
- test_uid_plus_plus
- test_uid_minus
- test_uid_minus_plus
-
- test_wholename
-
- test_writable
-
- test_xtype_l
- test_xtype_f
- test_L_xtype_l
- test_L_xtype_f
- test_xtype_bind_mount
-
- # Optimizer tests
- test_and_purity
- test_not_reachability
- test_comma_reachability
- test_and_false_or_true
- test_comma_redundant_true
- test_comma_redundant_false
-)
-
-bfs_tests=(
- # General parsing
- test_path_flag_expr
- test_path_expr_flag
- test_flag_expr_path
- test_expr_flag_path
- test_expr_path_flag
-
- test_unexpected_operator
- test_and_incomplete
- test_or_incomplete
- test_comma_incomplete
-
- test_typo
-
- # Flags
-
- test_D_multi
- test_D_all
-
- test_O0
- test_O1
- test_O2
- test_O3
- test_Ofast
-
- test_S_bfs
- test_S_dfs
- test_S_ids
-
- # Special forms
-
- test_exclude_name
- test_exclude_depth
- test_exclude_mindepth
- test_exclude_print
- test_exclude_exclude
-
- # Primaries
-
- test_capable
- test_L_capable
-
- test_color
- test_color_L
- test_color_rs_lc_rc_ec
- test_color_escapes
- test_color_nul
- test_color_ln_target
- test_color_L_ln_target
- test_color_mh
- test_color_mh0
- test_color_or
- test_color_mi
- test_color_or_mi
- test_color_or_mi0
- test_color_or0_mi
- test_color_or0_mi0
- test_color_su_sg0
- test_color_su0_sg
- test_color_su0_sg0
- test_color_st_tw_ow0
- test_color_st_tw0_ow
- test_color_st_tw0_ow0
- test_color_st0_tw_ow
- test_color_st0_tw_ow0
- test_color_st0_tw0_ow
- test_color_st0_tw0_ow0
- test_color_ext
- test_color_ext0
- test_color_ext_override
- test_color_ext_underride
- test_color_missing_colon
- test_color_no_stat
- test_color_L_no_stat
- test_color_star
- test_color_ls
-
- test_exec_flush_fprint
- test_exec_flush_fprint_fail
-
- test_execdir_plus
- test_execdir_plus_nonexistent
-
- test_fprint_duplicate_stdout
- test_fprint_error_stdout
- test_fprint_error_stderr
-
- test_help
-
- test_hidden
- test_hidden_root
-
- test_links_noarg
- test_links_empty
- test_links_negative
- test_links_invalid
-
- test_newerma_nonexistent
- test_newermt_invalid
- test_newermq
- test_newerqm
-
- test_nohidden
- test_nohidden_depth
-
- test_ok_plus_semicolon
-
- test_okdir_plus_semicolon
-
- test_perm_symbolic_trailing_comma
- test_perm_symbolic_double_comma
- test_perm_symbolic_missing_action
- test_perm_leading_plus_symbolic
-
- test_printf_w
- test_printf_incomplete_escape
- test_printf_invalid_escape
- test_printf_incomplete_format
- test_printf_invalid_format
- test_printf_duplicate_flag
- test_printf_must_be_numeric
- test_printf_color
- test_printf_everything
-
- test_type_multi
-
- test_unique
- test_unique_depth
- test_L_unique
- test_L_unique_loops
- test_L_unique_depth
-
- test_version
-
- test_warn
- test_nowarn
-
- test_xtype_multi
-
- # Optimizer tests
- test_data_flow_hidden
- test_xtype_reorder
- test_xtype_depth
-
- # PATH_MAX handling
- test_deep_strict
-
- # Error handling
- test_stderr_fails_silently
- test_stderr_fails_loudly
-)
-
if [ "$DEFAULT" ]; then
POSIX=yes
+ COMMON=yes
BSD=yes
GNU=yes
ALL=yes
fi
-if [ ! "$EXPLICIT" ]; then
- [ "$POSIX" ] && enabled_tests+=("${posix_tests[@]}")
- [ "$BSD" ] && enabled_tests+=("${bsd_tests[@]}")
- [ "$GNU" ] && enabled_tests+=("${gnu_tests[@]}")
- [ "$ALL" ] && enabled_tests+=("${bfs_tests[@]}")
-fi
-
-eval "enabled_tests=($(printf '%q\n' "${enabled_tests[@]}" | sort -u))"
-
function _realpath() {
(
cd "$(dirname -- "$1")"
@@ -937,6 +260,18 @@ BFS[0]=$(_realpath "$(command -v "${BFS[0]}")")
TMP=$(mktemp -d "${TMPDIR:-/tmp}"/bfs.XXXXXXXXXX)
chown "$(id -u):$(id -g)" "$TMP"
+cd "$TESTS"
+
+if [ ! "$EXPLICIT" ]; then
+ [ "$POSIX" ] && enabled_tests+=(posix/*.sh)
+ [ "$COMMON" ] && enabled_tests+=(common/*.sh)
+ [ "$BSD" ] && enabled_tests+=(bsd/*.sh)
+ [ "$GNU" ] && enabled_tests+=(gnu/*.sh)
+ [ "$ALL" ] && enabled_tests+=(bfs/*.sh)
+
+ enabled_tests=("${enabled_tests[@]%.sh}")
+fi
+
# Clean up temporary directories on exit
function cleanup() {
# Don't force rm to deal with long paths
@@ -1265,1881 +600,6 @@ function inum() {
ls -id "$@" | awk '{ print $1 }'
}
-
-cd "$TMP"
-set +e
-
-# Test cases
-
-function test_basic() {
- bfs_diff basic
-}
-
-function test_type_d() {
- bfs_diff basic -type d
-}
-
-function test_type_f() {
- bfs_diff basic -type f
-}
-
-function test_type_l() {
- bfs_diff links/skip -type l
-}
-
-function test_H_type_l() {
- bfs_diff -H links/skip -type l
-}
-
-function test_L_type_l() {
- bfs_diff -L links/skip -type l
-}
-
-function test_type_multi() {
- bfs_diff links -type f,d,c
-}
-
-function test_mindepth() {
- bfs_diff basic -mindepth 1
-}
-
-function test_maxdepth() {
- bfs_diff basic -maxdepth 1
-}
-
-function test_depth() {
- bfs_diff basic -depth
-}
-
-function test_depth_slash() {
- bfs_diff basic/ -depth
-}
-
-function test_depth_mindepth_1() {
- bfs_diff basic -mindepth 1 -depth
-}
-
-function test_depth_mindepth_2() {
- bfs_diff basic -mindepth 2 -depth
-}
-
-function test_depth_maxdepth_1() {
- bfs_diff basic -maxdepth 1 -depth
-}
-
-function test_depth_maxdepth_2() {
- bfs_diff basic -maxdepth 2 -depth
-}
-
-function test_depth_error() {
- rm -rf scratch/*
- touchp scratch/foo/bar
- chmod a-r scratch/foo
-
- bfs_diff scratch -depth
- local ret=$?
-
- chmod +r scratch/foo
- rm -rf scratch/*
-
- [ $ret -eq $EX_BFS ]
-}
-
-function test_name() {
- bfs_diff basic -name '*f*'
-}
-
-function test_name_root() {
- bfs_diff basic/a -name a
-}
-
-function test_name_root_depth() {
- bfs_diff basic/g -depth -name g
-}
-
-function test_name_trailing_slash() {
- bfs_diff basic/g/ -name g
-}
-
-function test_name_slash() {
- bfs_diff / -maxdepth 0 -name /
-}
-
-function test_name_slashes() {
- bfs_diff /// -maxdepth 0 -name /
-}
-
-function test_name_star_star() {
- bfs_diff basic -name '**f**'
-}
-
-function test_name_character_class() {
- bfs_diff basic -name '[e-g][!a-n][!p-z]'
-}
-
-function test_name_bracket() {
- # fnmatch() is broken on macOS
- skip_if test "$UNAME" = "Darwin"
-
- # An unclosed [ should be matched literally
- bfs_diff weirdnames -name '['
-}
-
-function test_name_backslash() {
- # An unescaped \ doesn't match
- bfs_diff weirdnames -name '\'
-}
-
-function test_name_double_backslash() {
- # An escaped \\ matches
- bfs_diff weirdnames -name '\\'
-}
-
-function test_path() {
- bfs_diff basic -path 'basic/*f*'
-}
-
-function test_wholename() {
- bfs_diff basic -wholename 'basic/*f*'
-}
-
-function test_true() {
- bfs_diff basic -true
-}
-
-function test_false() {
- bfs_diff basic -false
-}
-
-function test_executable() {
- bfs_diff perms -executable
-}
-
-function test_readable() {
- bfs_diff perms -readable
-}
-
-function test_writable() {
- bfs_diff perms -writable
-}
-
-function test_empty() {
- bfs_diff basic -empty
-}
-
-function test_empty_special() {
- bfs_diff rainbow -empty
-}
-
-function test_gid() {
- bfs_diff basic -gid "$(id -g)"
-}
-
-function test_gid_plus() {
- skip_if test "$(id -g)" -eq 0
- bfs_diff basic -gid +0
-}
-
-function test_gid_plus_plus() {
- skip_if test "$(id -g)" -eq 0
- bfs_diff basic -gid ++0
-}
-
-function test_gid_minus() {
- bfs_diff basic -gid "-$(($(id -g) + 1))"
-}
-
-function test_gid_minus_plus() {
- bfs_diff basic -gid "-+$(($(id -g) + 1))"
-}
-
-function test_uid() {
- bfs_diff basic -uid "$(id -u)"
-}
-
-function test_uid_plus() {
- skip_if test "$(id -u)" -eq 0
- bfs_diff basic -uid +0
-}
-
-function test_uid_plus_plus() {
- skip_if test "$(id -u)" -eq 0
- bfs_diff basic -uid ++0
-}
-
-function test_uid_minus() {
- bfs_diff basic -uid "-$(($(id -u) + 1))"
-}
-
-function test_uid_minus_plus() {
- bfs_diff basic -uid "-+$(($(id -u) + 1))"
-}
-
-function test_newer() {
- bfs_diff times -newer times/a
-}
-
-function test_newer_link() {
- bfs_diff times -newer times/l
-}
-
-function test_anewer() {
- bfs_diff times -anewer times/a
-}
-
-function test_asince() {
- bfs_diff times -asince 1991-12-14T00:01
-}
-
-function test_links() {
- bfs_diff links -type f -links 2
-}
-
-function test_links_plus() {
- bfs_diff links -type f -links +1
-}
-
-function test_links_minus() {
- bfs_diff links -type f -links -2
-}
-
-function test_links_noarg() {
- fail invoke_bfs links -links
-}
-
-function test_links_empty() {
- fail invoke_bfs links -links ''
-}
-
-function test_links_negative() {
- fail invoke_bfs links -links +-1
-}
-
-function test_links_invalid() {
- fail invoke_bfs links -links ASDF
-}
-
-function test_P() {
- bfs_diff -P links/deeply/nested/dir
-}
-
-function test_P_slash() {
- bfs_diff -P links/deeply/nested/dir/
-}
-
-function test_H() {
- bfs_diff -H links/deeply/nested/dir
-}
-
-function test_H_slash() {
- bfs_diff -H links/deeply/nested/dir/
-}
-
-function test_H_broken() {
- bfs_diff -H links/broken
-}
-
-function test_H_notdir() {
- bfs_diff -H links/notdir
-}
-
-function test_H_newer() {
- bfs_diff -H times -newer times/l
-}
-
-function test_H_loops() {
- bfs_diff -H loops/deeply/nested/loop
-}
-
-function test_L() {
- bfs_diff -L links
-}
-
-function test_L_broken() {
- bfs_diff -H links/broken
-}
-
-function test_L_notdir() {
- bfs_diff -H links/notdir
-}
-
-function test_L_loops() {
- # POSIX says it's okay to either stop or keep going on seeing a filesystem
- # loop, as long as a diagnostic is printed
- local errors=$(invoke_bfs -L loops 2>&1 >/dev/null)
- [ -n "$errors" ]
-}
-
-function test_L_loops_continue() {
- bfs_diff -L loops
- [ $? -eq $EX_BFS ]
-}
-
-function test_X() {
- bfs_diff -X weirdnames
- [ $? -eq $EX_BFS ]
-}
-
-function test_follow() {
- bfs_diff links -follow
-}
-
-function test_L_depth() {
- bfs_diff -L links -depth
-}
-
-function test_samefile() {
- bfs_diff links -samefile links/file
-}
-
-function test_samefile_symlink() {
- bfs_diff links -samefile links/symlink
-}
-
-function test_H_samefile_symlink() {
- bfs_diff -H links -samefile links/symlink
-}
-
-function test_L_samefile_symlink() {
- bfs_diff -L links -samefile links/symlink
-}
-
-function test_samefile_broken() {
- bfs_diff links -samefile links/broken
-}
-
-function test_H_samefile_broken() {
- bfs_diff -H links -samefile links/broken
-}
-
-function test_L_samefile_broken() {
- bfs_diff -L links -samefile links/broken
-}
-
-function test_samefile_notdir() {
- bfs_diff links -samefile links/notdir
-}
-
-function test_H_samefile_notdir() {
- bfs_diff -H links -samefile links/notdir
-}
-
-function test_L_samefile_notdir() {
- bfs_diff -L links -samefile links/notdir
-}
-
-function test_xtype_l() {
- bfs_diff links -xtype l
-}
-
-function test_xtype_f() {
- bfs_diff links -xtype f
-}
-
-function test_L_xtype_l() {
- bfs_diff -L links -xtype l
-}
-
-function test_L_xtype_f() {
- bfs_diff -L links -xtype f
-}
-
-function test_xtype_multi() {
- bfs_diff links -xtype f,d,c
-}
-
-function test_xtype_reorder() {
- # Make sure -xtype is not reordered in front of anything -- if -xtype runs
- # before -links 100, it will report an ELOOP error
- bfs_diff loops -links 100 -xtype l
- invoke_bfs loops -links 100 -xtype l
-}
-
-function test_xtype_depth() {
- # Make sure -xtype is considered side-effecting for facts_when_impure
- fail invoke_bfs loops -xtype l -depth 100
-}
-
-function test_iname() {
- skip_unless invoke_bfs -quit -iname PATTERN
- bfs_diff basic -iname '*F*'
-}
-
-function test_ipath() {
- skip_unless invoke_bfs -quit -ipath PATTERN
- bfs_diff basic -ipath 'basic/*F*'
-}
-
-function test_iwholename() {
- skip_unless invoke_bfs -quit -iwholename PATTERN
- bfs_diff basic -iwholename 'basic/*F*'
-}
-
-function test_lname() {
- bfs_diff links -lname '[aq]'
-}
-
-function test_ilname() {
- skip_unless invoke_bfs -quit -ilname PATTERN
- bfs_diff links -ilname '[AQ]'
-}
-
-function test_L_lname() {
- bfs_diff -L links -lname '[aq]'
-}
-
-function test_L_ilname() {
- skip_unless invoke_bfs -quit -ilname PATTERN
- bfs_diff -L links -ilname '[AQ]'
-}
-
-function test_user_name() {
- bfs_diff basic -user "$(id -un)"
-}
-
-function test_user_id() {
- bfs_diff basic -user "$(id -u)"
-}
-
-function test_user_nouser() {
- # Regression test: this was wrongly optimized to -false
- bfs_diff basic -user "$(id -u)" \! -nouser
-}
-
-function test_group_name() {
- bfs_diff basic -group "$(id -gn)"
-}
-
-function test_group_id() {
- bfs_diff basic -group "$(id -g)"
-}
-
-function test_group_nogroup() {
- # Regression test: this was wrongly optimized to -false
- bfs_diff basic -group "$(id -g)" \! -nogroup
-}
-
-function test_daystart() {
- bfs_diff basic -daystart -mtime 0
-}
-
-function test_daystart_twice() {
- bfs_diff basic -daystart -daystart -mtime 0
-}
-
-function test_newerma() {
- bfs_diff times -newerma times/a
-}
-
-function test_newermt() {
- bfs_diff times -newermt 1991-12-14T00:01
-}
-
-function test_newermt_epoch_minus_one() {
- bfs_diff times -newermt 1969-12-31T23:59:59Z
-}
-
-function test_newermt_invalid() {
- fail invoke_bfs times -newermt not_a_date_time
-}
-
-function test_newerma_nonexistent() {
- fail invoke_bfs times -newerma basic/nonexistent
-}
-
-function test_newermq() {
- fail invoke_bfs times -newermq times/a
-}
-
-function test_newerqm() {
- fail invoke_bfs times -newerqm times/a
-}
-
-function test_size() {
- bfs_diff basic -type f -size 0
-}
-
-function test_size_plus() {
- bfs_diff basic -type f -size +0
-}
-
-function test_size_bytes() {
- bfs_diff basic -type f -size +0c
-}
-
-function test_size_big() {
- bfs_diff basic -size 9223372036854775807
-}
-
-function test_exec() {
- bfs_diff basic -exec echo {} \;
-}
-
-function test_exec_nonexistent() {
- # Failure to execute the command should lead to an error message and
- # non-zero exit status. See https://unix.stackexchange.com/q/704522/56202
-
- local stderr=$(invoke_bfs basic -exec "$TESTS/nonexistent" {} \; 2>&1 >/dev/null)
- [ -n "$stderr" ] || return 1
-
- bfs_diff basic -print -exec "$TESTS/nonexistent" {} \; -print
- (($? == EX_BFS))
-}
-
-function test_exec_nopath() {
- (
- unset PATH
- invoke_bfs basic -exec echo {} \; >"$OUT"
- )
-
- sort_output
- diff_output
-}
-
-function test_exec_nothing() {
- # Regression test: don't segfault on missing command
- fail invoke_bfs basic -exec \;
-}
-
-function test_exec_plus() {
- bfs_diff basic -exec "$TESTS/sort-args.sh" {} +
-}
-
-function test_exec_plus_nonexistent() {
- local stderr=$(invoke_bfs basic -exec "$TESTS/nonexistent" {} + 2>&1 >/dev/null)
- [ -n "$stderr" ] || return 1
-
- bfs_diff basic -exec "$TESTS/nonexistent" {} + -print
- (($? == EX_BFS))
-}
-
-function test_exec_plus_status() {
- # -exec ... {} + should always return true, but if the command fails, bfs
- # should exit with a non-zero status
- bfs_diff basic -exec false {} + -print
- (($? == EX_BFS))
-}
-
-function test_exec_plus_semicolon() {
- # POSIX says:
- # Only a <plus-sign> that immediately follows an argument containing only the two characters "{}"
- # shall punctuate the end of the primary expression. Other uses of the <plus-sign> shall not be
- # treated as special.
- bfs_diff basic -exec echo foo {} bar + baz \;
-}
-
-function test_exec_substring() {
- bfs_diff basic -exec echo '-{}-' \;
-}
-
-function test_exec_flush() {
- # IO streams should be flushed before executing programs
- invoke_bfs basic -print0 -exec echo found \; | tr '\0' ' ' >"$OUT"
- sort_output
- diff_output
-}
-
-function test_exec_flush_fail() {
- # Failure to flush streams before exec should be caught
- skip_unless test -e /dev/full
- fail invoke_bfs basic -print0 -exec true \; >/dev/full
-}
-
-function test_exec_flush_fprint() {
- # Even non-stdstreams should be flushed
- bfs_diff basic/a -fprint scratch/foo -exec cat scratch/foo \;
-}
-
-function test_exec_flush_fprint_fail() {
- skip_unless test -e /dev/full
- fail invoke_bfs basic/a -fprint /dev/full -exec true \;
-}
-
-function test_exec_plus_flush() {
- invoke_bfs basic/a -print0 -exec echo found {} + >"$OUT"
- diff_output
-}
-
-function test_exec_plus_flush_fail() {
- skip_unless test -e /dev/full
- fail invoke_bfs basic/a -print0 -exec echo found {} + >/dev/full
-}
-
-function test_execdir() {
- bfs_diff basic -execdir echo {} \;
-}
-
-function test_execdir_nonexistent() {
- local stderr=$(invoke_bfs basic -execdir "$TESTS/nonexistent" {} \; 2>&1 >/dev/null)
- [ -n "$stderr" ] || return 1
-
- bfs_diff basic -print -execdir "$TESTS/nonexistent" {} \; -print
- (($? == EX_BFS))
-}
-
-function test_execdir_plus() {
- local tree=$(invoke_bfs -D tree 2>&1 -quit)
-
- if [[ "$tree" == *"-S dfs"* ]]; then
- skip
- fi
-
- bfs_diff basic -execdir "$TESTS/sort-args.sh" {} +
-}
-
-function test_execdir_plus_nonexistent() {
- local stderr=$(invoke_bfs basic -execdir "$TESTS/nonexistent" {} + 2>&1 >/dev/null)
- [ -n "$stderr" ] || return 1
-
- bfs_diff basic -execdir "$TESTS/nonexistent" {} + -print
- (($? == EX_BFS))
-}
-
-function test_execdir_substring() {
- bfs_diff basic -execdir echo '-{}-' \;
-}
-
-function test_execdir_plus_semicolon() {
- bfs_diff basic -execdir echo foo {} bar + baz \;
-}
-
-function test_execdir_pwd() {
- local TMP_REAL=$(cd "$TMP" && pwd)
- local OFFSET=$((${#TMP_REAL} + 2))
- bfs_diff basic -execdir bash -c "pwd | cut -b$OFFSET-" \;
-}
-
-function test_execdir_slash() {
- # Don't prepend ./ for absolute paths in -execdir
- bfs_diff / -maxdepth 0 -execdir echo {} \;
-}
-
-function test_execdir_slash_pwd() {
- bfs_diff / -maxdepth 0 -execdir pwd \;
-}
-
-function test_execdir_slashes() {
- bfs_diff /// -maxdepth 0 -execdir echo {} \;
-}
-
-function test_execdir_ulimit() {
- rm -rf scratch/*
- mkdir -p scratch/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z
- mkdir -p scratch/a/b/c/d/e/f/g/h/i/j/k/l/m/0/1/2/3/4/5/6/7/8/9/A/B/C
-
- closefrom 4
- ulimit -n 13
- bfs_diff scratch -execdir echo {} \;
-}
-
-function test_weird_names() {
- cd weirdnames
- bfs_diff '-' '(-' '!-' ',' ')' './(' './!' \( \! -print -o -print \)
-}
-
-function test_flag_weird_names() {
- cd weirdnames
- bfs_diff -L '-' '(-' '!-' ',' ')' './(' './!' \( \! -print -o -print \)
-}
-
-function test_flag_comma() {
- # , is a filename until a non-flag is seen
- cd weirdnames
- bfs_diff -L ',' -print
-}
-
-function test_follow_comma() {
- # , is an operator after a non-flag is seen
- cd weirdnames
- bfs_diff -follow ',' -print
-}
-
-function test_fprint() {
- invoke_bfs basic -fprint "$OUT"
- sort_output
- diff_output
-}
-
-function test_fprint_duplicate() {
- touchp scratch/$TEST.out
- ln scratch/$TEST.out scratch/$TEST.hard
- ln -s $TEST.out scratch/$TEST.soft
-
- invoke_bfs basic -fprint scratch/$TEST.out -fprint scratch/$TEST.hard -fprint scratch/$TEST.soft
- sort scratch/$TEST.out >"$OUT"
- diff_output
-}
-
-function test_fprint_duplicate_stdout() {
- invoke_bfs basic -fprint "$OUT" -print >"$OUT"
- sort_output
- diff_output
-}
-
-function test_fprint_noarg() {
- fail invoke_bfs basic -fprint
-}
-
-function test_fprint_nonexistent() {
- fail invoke_bfs basic -fprint scratch/nonexistent/path
-}
-
-function test_fprint_truncate() {
- printf "basic\nbasic\n" >"$OUT"
-
- invoke_bfs basic -maxdepth 0 -fprint "$OUT"
- sort_output
- diff_output
-}
-
-function test_double_dash() {
- cd basic
- bfs_diff -- . -type f
-}
-
-function test_flag_double_dash() {
- cd basic
- bfs_diff -L -- . -type f
-}
-
-function test_ignore_readdir_race() {
- rm -rf scratch/*
- $TOUCH scratch/{foo,bar}
-
- # -links 1 forces a stat() call, which will fail for the second file
- invoke_bfs scratch -mindepth 1 -ignore_readdir_race -links 1 -exec "$TESTS/remove-sibling.sh" {} \;
-}
-
-function test_ignore_readdir_race_root() {
- # Make sure -ignore_readdir_race doesn't suppress ENOENT at the root
- fail invoke_bfs basic/nonexistent -ignore_readdir_race
-}
-
-function test_ignore_readdir_race_notdir() {
- # Check -ignore_readdir_race handling when a directory is replaced with a file
- rm -rf scratch/*
- touchp scratch/foo/bar
-
- invoke_bfs scratch -mindepth 1 -ignore_readdir_race -execdir rm -r {} \; -execdir $TOUCH {} \;
-}
-
-function test_perm_000() {
- bfs_diff perms -perm 000
-}
-
-function test_perm_000_minus() {
- bfs_diff perms -perm -000
-}
-
-function test_perm_000_slash() {
- bfs_diff perms -perm /000
-}
-
-function test_perm_000_plus() {
- bfs_diff perms -perm +000
-}
-
-function test_perm_222() {
- bfs_diff perms -perm 222
-}
-
-function test_perm_222_minus() {
- bfs_diff perms -perm -222
-}
-
-function test_perm_222_slash() {
- bfs_diff perms -perm /222
-}
-
-function test_perm_222_plus() {
- bfs_diff perms -perm +222
-}
-
-function test_perm_644() {
- bfs_diff perms -perm 644
-}
-
-function test_perm_644_minus() {
- bfs_diff perms -perm -644
-}
-
-function test_perm_644_slash() {
- bfs_diff perms -perm /644
-}
-
-function test_perm_644_plus() {
- bfs_diff perms -perm +644
-}
-
-function test_perm_symbolic() {
- bfs_diff perms -perm a+r,u=wX,g+wX-w
-}
-
-function test_perm_symbolic_minus() {
- bfs_diff perms -perm -a+r,u=wX,g+wX-w
-}
-
-function test_perm_symbolic_slash() {
- bfs_diff perms -perm /a+r,u=wX,g+wX-w
-}
-
-function test_perm_symbolic_trailing_comma() {
- fail invoke_bfs perms -perm a+r,
-}
-
-function test_perm_symbolic_double_comma() {
- fail invoke_bfs perms -perm a+r,,u+w
-}
-
-function test_perm_symbolic_missing_action() {
- fail invoke_bfs perms -perm a
-}
-
-function test_perm_leading_plus_symbolic() {
- bfs_diff perms -perm +rwx
-}
-
-function test_perm_leading_plus_symbolic_minus() {
- bfs_diff perms -perm -+rwx
-}
-
-function test_perm_leading_plus_symbolic_slash() {
- bfs_diff perms -perm /+rwx
-}
-
-function test_permcopy() {
- bfs_diff perms -perm u+rw,g+u-w,o=g
-}
-
-function test_perm_setid() {
- bfs_diff rainbow -perm -u+s -o -perm -g+s
-}
-
-function test_perm_sticky() {
- bfs_diff rainbow -perm -a+t
-}
-
-function test_prune() {
- bfs_diff basic -name foo -prune
-}
-
-function test_prune_file() {
- bfs_diff basic -print -name '?' -prune
-}
-
-function test_prune_or_print() {
- bfs_diff basic -name foo -prune -o -print
-}
-
-function test_not_prune() {
- bfs_diff basic \! \( -name foo -prune \)
-}
-
-function test_ok_nothing() {
- # Regression test: don't segfault on missing command
- fail invoke_bfs basic -ok \;
-}
-
-function test_ok_stdin() {
- # -ok should *not* close stdin
- # See https://savannah.gnu.org/bugs/?24561
- yes | bfs_diff basic -ok bash -c 'printf "%s? " "$1" && head -n1' bash {} \;
-}
-
-function test_okdir_stdin() {
- # -okdir should *not* close stdin
- yes | bfs_diff basic -okdir bash -c 'printf "%s? " "$1" && head -n1' bash {} \;
-}
-
-function test_ok_plus_semicolon() {
- # The -ok primary shall be equivalent to -exec, except that the use of a
- # <plus-sign> to punctuate the end of the primary expression need not be
- # supported, ...
- #
- # bfs chooses not to support it, for compatibility with most other find
- # implementations.
-
- yes | bfs_diff basic -ok echo {} + \;
-}
-
-function test_okdir_plus_semicolon() {
- yes | bfs_diff basic -okdir echo {} + \;
-}
-
-function test_delete() {
- rm -rf scratch/*
- touchp scratch/foo/bar/baz
-
- # Don't try to delete '.'
- (cd scratch && invoke_bfs . -delete)
-
- bfs_diff scratch
-}
-
-function test_delete_many() {
- # Test for https://github.com/tavianator/bfs/issues/67
-
- rm -rf scratch/*
- mkdir scratch/foo
- $TOUCH scratch/foo/{1..256}
-
- invoke_bfs scratch/foo -delete
- bfs_diff scratch
-}
-
-function test_L_delete() {
- rm -rf scratch/*
- mkdir scratch/foo
- mkdir scratch/bar
- ln -s ../foo scratch/bar/baz
-
- # Don't try to rmdir() a symlink
- invoke_bfs -L scratch/bar -delete || return 1
-
- bfs_diff scratch
-}
-
-function test_rm() {
- rm -rf scratch/*
- touchp scratch/foo/bar/baz
-
- (cd scratch && invoke_bfs . -rm)
-
- bfs_diff scratch
-}
-
-function test_regex() {
- bfs_diff basic -regex 'basic/./.'
-}
-
-function test_iregex() {
- bfs_diff basic -iregex 'basic/[A-Z]/[a-z]'
-}
-
-function test_regex_parens() {
- cd weirdnames
- bfs_diff . -regex '\./\((\)'
-}
-
-function test_regex_error() {
- fail invoke_bfs basic -regex '['
-}
-
-function test_regex_invalid_utf8() {
- rm -rf scratch/*
-
- # Incomplete UTF-8 sequences
- skip_unless touch scratch/$'\xC3'
- skip_unless touch scratch/$'\xE2\x84'
- skip_unless touch scratch/$'\xF0\x9F\x92'
-
- bfs_diff scratch -regex 'scratch/..'
-}
-
-function test_E() {
- cd weirdnames
- bfs_diff -E . -regex '\./(\()'
-}
-
-function test_regextype_posix_basic() {
- cd weirdnames
- bfs_diff -regextype posix-basic -regex '\./\((\)'
-}
-
-function test_regextype_posix_extended() {
- cd weirdnames
- bfs_diff -regextype posix-extended -regex '\./(\()'
-}
-
-function test_regextype_ed() {
- cd weirdnames
- bfs_diff -regextype ed -regex '\./\((\)'
-}
-
-function test_regextype_emacs() {
- skip_unless invoke_bfs -regextype emacs -quit
-
- bfs_diff basic -regextype emacs -regex '.*/\(f+o?o?\|bar\)'
-}
-
-function test_regextype_grep() {
- skip_unless invoke_bfs -regextype grep -quit
-
- bfs_diff basic -regextype grep -regex '.*/f\+o\?o\?'
-}
-
-function test_regextype_sed() {
- cd weirdnames
- bfs_diff -regextype sed -regex '\./\((\)'
-}
-
-function test_d_path() {
- bfs_diff -d basic
-}
-
-function test_path_d() {
- bfs_diff basic -d
-}
-
-function test_f() {
- cd weirdnames
- bfs_diff -f '-' -f '('
-}
-
-function test_s() {
- invoke_bfs -s weirdnames -maxdepth 1 >"$OUT"
- diff_output
-}
-
-function test_hidden() {
- bfs_diff weirdnames -hidden
-}
-
-function test_hidden_root() {
- cd weirdnames
- bfs_diff . ./. ... ./... .../.. -hidden
-}
-
-function test_nohidden() {
- bfs_diff weirdnames -nohidden
-}
-
-function test_nohidden_depth() {
- bfs_diff weirdnames -depth -nohidden
-}
-
-function test_depth_n() {
- bfs_diff basic -depth 2
-}
-
-function test_depth_n_plus() {
- bfs_diff basic -depth +2
-}
-
-function test_depth_n_minus() {
- bfs_diff basic -depth -2
-}
-
-function test_depth_depth_n() {
- bfs_diff basic -depth -depth 2
-}
-
-function test_depth_depth_n_plus() {
- bfs_diff basic -depth -depth +2
-}
-
-function test_depth_depth_n_minus() {
- bfs_diff basic -depth -depth -2
-}
-
-function test_depth_overflow() {
- bfs_diff basic -depth -4294967296
-}
-
-function test_gid_name() {
- bfs_diff basic -gid "$(id -gn)"
-}
-
-function test_uid_name() {
- bfs_diff basic -uid "$(id -un)"
-}
-
-function test_mnewer() {
- bfs_diff times -mnewer times/a
-}
-
-function test_H_mnewer() {
- bfs_diff -H times -mnewer times/l
-}
-
-function test_msince() {
- bfs_diff times -msince 1991-12-14T00:01
-}
-
-function test_mtime_units() {
- bfs_diff times -mtime +500w400d300h200m100s
-}
-
-function test_size_T() {
- bfs_diff basic -type f -size 1T
-}
-
-function test_quit() {
- bfs_diff basic/g -print -name g -quit
-}
-
-function test_quit_child() {
- bfs_diff basic/g -print -name h -quit
-}
-
-function test_quit_depth() {
- bfs_diff basic/g -depth -print -name g -quit
-}
-
-function test_quit_depth_child() {
- bfs_diff basic/g -depth -print -name h -quit
-}
-
-function test_quit_after_print() {
- bfs_diff basic basic -print -quit
-}
-
-function test_quit_before_print() {
- bfs_diff basic basic -quit -print
-}
-
-function test_quit_implicit_print() {
- bfs_diff basic -name basic -o -quit
-}
-
-function test_inum() {
- bfs_diff basic -inum "$(inum basic/k/foo/bar)"
-}
-
-function test_nogroup() {
- bfs_diff basic -nogroup
-}
-
-function test_nogroup_ulimit() {
- closefrom 4
- ulimit -n 16
- bfs_diff deep -nogroup
-}
-
-function test_nouser() {
- bfs_diff basic -nouser
-}
-
-function test_nouser_ulimit() {
- closefrom 4
- ulimit -n 16
- bfs_diff deep -nouser
-}
-
-function test_ls() {
- invoke_bfs rainbow -ls >scratch/test_ls.out
-}
-
-function test_L_ls() {
- invoke_bfs -L rainbow -ls >scratch/test_L_ls.out
-}
-
-function test_fls() {
- invoke_bfs rainbow -fls scratch/test_fls.out
-}
-
-function test_printf() {
- bfs_diff basic -printf '%%p(%p) %%d(%d) %%f(%f) %%h(%h) %%H(%H) %%P(%P) %%m(%m) %%M(%M) %%y(%y)\n'
-}
-
-function test_printf_empty() {
- bfs_diff basic -printf ''
-}
-
-function test_printf_slash() {
- bfs_diff / -maxdepth 0 -printf '(%h)/(%f)\n'
-}
-
-function test_printf_slashes() {
- bfs_diff /// -maxdepth 0 -printf '(%h)/(%f)\n'
-}
-
-function test_printf_trailing_slash() {
- bfs_diff basic/ -printf '(%h)/(%f)\n'
-}
-
-function test_printf_trailing_slashes() {
- bfs_diff basic/// -printf '(%h)/(%f)\n'
-}
-
-function test_printf_flags() {
- bfs_diff basic -printf '|%- 10.10p| %+03d %#4m\n'
-}
-
-function test_printf_types() {
- bfs_diff loops -printf '(%p) (%l) %y %Y\n'
-}
-
-function test_printf_escapes() {
- bfs_diff basic -maxdepth 0 -printf '\18\118\1118\11118\n\cfoo'
-}
-
-function test_printf_times() {
- bfs_diff times -type f -printf '%p | %a %AY-%Am-%Ad %AH:%AI:%AS %T@ | %t %TY-%Tm-%Td %TH:%TI:%TS %T@\n'
-}
-
-function test_printf_leak() {
- # Memory leak regression test
- bfs_diff basic -maxdepth 0 -printf '%p'
-}
-
-function test_printf_nul() {
- # NUL byte regression test
- invoke_bfs basic/a basic/b -maxdepth 0 -printf '%h\0%f\n' >"$OUT"
- diff_output
-}
-
-function test_printf_w() {
- # Birth times may not be supported, so just check that %w/%W/%B can be parsed
- bfs_diff times -false -printf '%w %WY %BY\n'
-}
-
-function test_printf_Y_error() {
- rm -rf scratch/*
- mkdir scratch/foo
- chmod -x scratch/foo
- ln -s foo/bar scratch/bar
-
- bfs_diff scratch -printf '(%p) (%l) %y %Y\n'
- local ret=$?
-
- chmod +x scratch/foo
- rm -rf scratch/*
-
- [ $ret -eq $EX_BFS ]
-}
-
-function test_printf_H() {
- bfs_diff basic links -printf '%%p(%p) %%d(%d) %%f(%f) %%h(%h) %%H(%H) %%P(%P) %%y(%y)\n'
-}
-
-function test_printf_u_g_ulimit() {
- closefrom 4
- ulimit -n 16
- [ "$(invoke_bfs deep -printf '%u %g\n' | uniq)" = "$(id -un) $(id -gn)" ]
-}
-
-function test_printf_l_nonlink() {
- bfs_diff links -printf '| %26p -> %-26l |\n'
-}
-
-function test_printf_incomplete_escape() {
- fail invoke_bfs basic -printf '\'
-}
-
-function test_printf_invalid_escape() {
- fail invoke_bfs basic -printf '\!'
-}
-
-function test_printf_incomplete_format() {
- fail invoke_bfs basic -printf '%'
-}
-
-function test_printf_invalid_format() {
- fail invoke_bfs basic -printf '%!'
-}
-
-function test_printf_duplicate_flag() {
- fail invoke_bfs basic -printf '%--p'
-}
-
-function test_printf_must_be_numeric() {
- fail invoke_bfs basic -printf '%+p'
-}
-
-function test_printf_color() {
- bfs_diff -color -path './rainbow*' -printf '%H %h %f %p %P %l\n'
-}
-
-function test_printf_everything() {
- local everything=(%{a,b,c,d,D,f,F,g,G,h,H,i,k,l,m,M,n,p,P,s,S,t,u,U,y,Y})
- everything+=(%{A,C,T}{%,+,@,a,A,b,B,c,C,d,D,e,F,g,G,h,H,I,j,k,l,m,M,n,p,r,R,s,S,t,T,u,U,V,w,W,x,X,y,Y,z,Z})
-
- # Check if we have birth times
- if ! fail invoke_bfs basic -printf '%w' -quit >/dev/null; then
- everything+=(%w %{B,W}{%,+,@,a,A,b,B,c,C,d,D,e,F,g,G,h,H,I,j,k,l,m,M,n,p,r,R,s,S,t,T,u,U,V,w,W,x,X,y,Y,z,Z})
- fi
-
- invoke_bfs rainbow -printf "${everything[*]}\n" >/dev/null
-}
-
-function test_fprintf() {
- invoke_bfs basic -fprintf "$OUT" '%%p(%p) %%d(%d) %%f(%f) %%h(%h) %%H(%H) %%P(%P) %%m(%m) %%M(%M) %%y(%y)\n'
- sort_output
- diff_output
-}
-
-function test_fprintf_nofile() {
- fail invoke_bfs basic -fprintf
-}
-
-function test_fprintf_noformat() {
- fail invoke_bfs basic -fprintf /dev/null
-}
-
-function test_fstype() {
- fstype=$(invoke_bfs basic -maxdepth 0 -printf '%F\n')
- bfs_diff basic -fstype "$fstype"
-}
-
-function test_path_flag_expr() {
- bfs_diff links/skip -H -type l
-}
-
-function test_path_expr_flag() {
- bfs_diff links/skip -type l -H
-}
-
-function test_flag_expr_path() {
- bfs_diff -H -type l links/skip
-}
-
-function test_expr_flag_path() {
- bfs_diff -type l -H links/skip
-}
-
-function test_expr_path_flag() {
- bfs_diff -type l links/skip -H
-}
-
-function test_parens() {
- bfs_diff basic \( -name '*f*' \)
-}
-
-function test_bang() {
- bfs_diff basic \! -name foo
-}
-
-function test_not() {
- bfs_diff basic -not -name foo
-}
-
-function test_implicit_and() {
- bfs_diff basic -name foo -type d
-}
-
-function test_a() {
- bfs_diff basic -name foo -a -type d
-}
-
-function test_and() {
- bfs_diff basic -name foo -and -type d
-}
-
-function test_o() {
- bfs_diff basic -name foo -o -type d
-}
-
-function test_or() {
- bfs_diff basic -name foo -or -type d
-}
-
-function test_comma() {
- bfs_diff basic -name '*f*' -print , -print
-}
-
-function test_precedence() {
- bfs_diff basic \( -name foo -type d -o -name bar -a -type f \) -print , \! -empty -type f -print
-}
-
-function test_incomplete() {
- fail invoke_bfs basic \(
-}
-
-function test_missing_paren() {
- fail invoke_bfs basic \( -print
-}
-
-function test_extra_paren() {
- fail invoke_bfs basic -print \)
-}
-
-function test_color() {
- bfs_diff rainbow -color
-}
-
-function test_color_L() {
- bfs_diff -L rainbow -color
-}
-
-function test_color_rs_lc_rc_ec() {
- LS_COLORS="rs=RS:lc=LC:rc=RC:ec=EC:" bfs_diff rainbow -color
-}
-
-function test_color_escapes() {
- LS_COLORS="lc=\e[:rc=\155\::ec=^[\x5B\x6d:" bfs_diff rainbow -color
-}
-
-function test_color_nul() {
- LS_COLORS="ec=\33[m\0:" invoke_bfs rainbow -color -maxdepth 0 >"$OUT"
- diff_output
-}
-
-function test_color_ln_target() {
- LS_COLORS="ln=target:or=01;31:mi=01;33:" bfs_diff rainbow -color
-}
-
-function test_color_L_ln_target() {
- LS_COLORS="ln=target:or=01;31:mi=01;33:" bfs_diff -L rainbow -color
-}
-
-function test_color_mh() {
- LS_COLORS="mh=01:" bfs_diff rainbow -color
-}
-
-function test_color_mh0() {
- LS_COLORS="mh=00:" bfs_diff rainbow -color
-}
-
-function test_color_or() {
- LS_COLORS="or=01:" bfs_diff rainbow -color
-}
-
-function test_color_mi() {
- LS_COLORS="mi=01:" bfs_diff rainbow -color
-}
-
-function test_color_or_mi() {
- LS_COLORS="or=01;31:mi=01;33:" bfs_diff rainbow -color
-}
-
-function test_color_or_mi0() {
- LS_COLORS="or=01;31:mi=00:" bfs_diff rainbow -color
-}
-
-function test_color_or0_mi() {
- LS_COLORS="or=00:mi=01;33:" bfs_diff rainbow -color
-}
-
-function test_color_or0_mi0() {
- LS_COLORS="or=00:mi=00:" bfs_diff rainbow -color
-}
-
-function test_color_su_sg0() {
- LS_COLORS="su=37;41:sg=00:" bfs_diff rainbow -color
-}
-
-function test_color_su0_sg() {
- LS_COLORS="su=00:sg=30;43:" bfs_diff rainbow -color
-}
-
-function test_color_su0_sg0() {
- LS_COLORS="su=00:sg=00:" bfs_diff rainbow -color
-}
-
-function test_color_st_tw_ow0() {
- LS_COLORS="st=37;44:tw=40;32:ow=00:" bfs_diff rainbow -color
-}
-
-function test_color_st_tw0_ow() {
- LS_COLORS="st=37;44:tw=00:ow=34;42:" bfs_diff rainbow -color
-}
-
-function test_color_st_tw0_ow0() {
- LS_COLORS="st=37;44:tw=00:ow=00:" bfs_diff rainbow -color
-}
-
-function test_color_st0_tw_ow() {
- LS_COLORS="st=00:tw=40;32:ow=34;42:" bfs_diff rainbow -color
-}
-
-function test_color_st0_tw_ow0() {
- LS_COLORS="st=00:tw=40;32:ow=00:" bfs_diff rainbow -color
-}
-
-function test_color_st0_tw0_ow() {
- LS_COLORS="st=00:tw=00:ow=34;42:" bfs_diff rainbow -color
-}
-
-function test_color_st0_tw0_ow0() {
- LS_COLORS="st=00:tw=00:ow=00:" bfs_diff rainbow -color
-}
-
-function test_color_ext() {
- LS_COLORS="*.txt=01:" bfs_diff rainbow -color
-}
-
-function test_color_ext0() {
- LS_COLORS="*.txt=00:" bfs_diff rainbow -color
-}
-
-function test_color_ext_override() {
- LS_COLORS="*.tar.gz=01;31:*.TAR=01;32:*.gz=01;33:" bfs_diff rainbow -color
-}
-
-function test_color_ext_underride() {
- LS_COLORS="*.gz=01;33:*.TAR=01;32:*.tar.gz=01;31:" bfs_diff rainbow -color
-}
-
-function test_color_missing_colon() {
- LS_COLORS="*.txt=01" bfs_diff rainbow -color
-}
-
-function test_color_no_stat() {
- LS_COLORS="mh=0:ex=0:sg=0:su=0:st=0:ow=0:tw=0:*.txt=01:" bfs_diff rainbow -color
-}
-
-function test_color_L_no_stat() {
- LS_COLORS="mh=0:ex=0:sg=0:su=0:st=0:ow=0:tw=0:*.txt=01:" bfs_diff -L rainbow -color
-}
-
-function test_color_star() {
- # Regression test: don't segfault on LS_COLORS="*"
- LS_COLORS="*" bfs_diff rainbow -color
-}
-
-function test_color_ls() {
- rm -rf scratch/*
- touchp scratch/foo/bar/baz
- ln -s foo/bar/baz scratch/link
- ln -s foo/bar/nowhere scratch/broken
- ln -s foo/bar/nowhere/nothing scratch/nested
- ln -s foo/bar/baz/qux scratch/notdir
- ln -s scratch/foo/bar scratch/relative
- mkdir scratch/__bfs__
- ln -s /__bfs__/nowhere scratch/absolute
-
- LS_COLORS="or=01;31:" invoke_bfs scratch/{,link,broken,nested,notdir,relative,absolute} -color -type l -ls \
- | sed 's/.* -> //' \
- | sort >"$OUT"
-
- diff_output
-}
-
-function test_deep() {
- closefrom 4
-
- ulimit -n 16
- bfs_diff deep -type f -exec bash -c 'echo "${1:0:6}/.../${1##*/} (${#1})"' bash {} \;
-}
-
-function test_deep_strict() {
- closefrom 4
-
- # Not even enough fds to keep the root open
- ulimit -n 7
- bfs_diff deep -type f -exec bash -c 'echo "${1:0:6}/.../${1##*/} (${#1})"' bash {} \;
-}
-
-function test_exit() {
- invoke_bfs basic -name foo -exit 42
- if [ $? -ne 42 ]; then
- return 1
- fi
-
- invoke_bfs basic -name qux -exit 42
- if [ $? -ne 0 ]; then
- return 1
- fi
-
- bfs_diff basic/g -print -name g -exit
-}
-
-function test_exit_no_implicit_print() {
- bfs_diff basic -not -name foo -o -exit
-}
-
-function test_printx() {
- bfs_diff weirdnames -printx
-}
-
-function test_and_purity() {
- # Regression test: (-a lhs(pure) rhs(always_false)) <==> rhs is only valid if rhs is pure
- bfs_diff basic -name nonexistent \( -print , -false \)
-}
-
-function test_or_purity() {
- # Regression test: (-o lhs(pure) rhs(always_true)) <==> rhs is only valid if rhs is pure
- bfs_diff basic -name '*' -o -print
-}
-
-function test_double_negation() {
- bfs_diff basic \! \! -name 'foo'
-}
-
-function test_not_reachability() {
- bfs_diff basic -print \! -quit -print
-}
-
-function test_comma_reachability() {
- bfs_diff basic -print -quit , -print
-}
-
-function test_de_morgan_not() {
- bfs_diff basic \! \( -name 'foo' -o \! -type f \)
-}
-
-function test_de_morgan_and() {
- bfs_diff basic \( \! -name 'foo' -a \! -type f \)
-}
-
-function test_de_morgan_or() {
- bfs_diff basic \( \! -name 'foo' -o \! -type f \)
-}
-
-function test_and_false_or_true() {
- # Test (-a lhs(always_true) -false) <==> (! lhs),
- # (-o lhs(always_false) -true) <==> (! lhs)
- bfs_diff basic -prune -false -o -true
-}
-
-function test_comma_redundant_true() {
- # Test (, lhs(always_true) -true) <==> lhs
- bfs_diff basic -prune , -true
-}
-
-function test_comma_redundant_false() {
- # Test (, lhs(always_false) -false) <==> lhs
- bfs_diff basic -print -not -prune , -false
-}
-
-function test_data_flow_depth() {
- bfs_diff basic -depth +1 -depth -4
-}
-
-function test_data_flow_group() {
- bfs_diff basic \( -group "$(id -g)" -nogroup \) -o \( -group "$(id -g)" -o -nogroup \)
-}
-
-function test_data_flow_user() {
- bfs_diff basic \( -user "$(id -u)" -nouser \) -o \( -user "$(id -u)" -o -nouser \)
-}
-
-function test_data_flow_hidden() {
- bfs_diff basic \( -hidden -not -hidden \) -o \( -hidden -o -not -hidden \)
-}
-
-function test_data_flow_sparse() {
- bfs_diff basic \( -sparse -not -sparse \) -o \( -sparse -o -not -sparse \)
-}
-
-function test_data_flow_type() {
- bfs_diff basic \! \( -type f -o \! -type f \)
-}
-
-function test_data_flow_and_swap() {
- bfs_diff basic \! -type f -a -type d
-}
-
-function test_data_flow_or_swap() {
- bfs_diff basic \! \( -type f -o \! -type d \)
-}
-
-function test_print_error() {
- skip_unless test -e /dev/full
- fail invoke_bfs basic -maxdepth 0 >/dev/full
-}
-
-function test_fprint_error() {
- skip_unless test -e /dev/full
- fail invoke_bfs basic -maxdepth 0 -fprint /dev/full
-}
-
-function test_fprint_noerror() {
- # Regression test: /dev/full should not fail until actually written to
- skip_unless test -e /dev/full
- invoke_bfs basic -false -fprint /dev/full
-}
-
-function test_fprint_error_stdout() {
- skip_unless test -e /dev/full
- fail invoke_bfs basic -maxdepth 0 -fprint /dev/full >/dev/full
-}
-
-function test_fprint_error_stderr() {
- skip_unless test -e /dev/full
- fail invoke_bfs basic -maxdepth 0 -fprint /dev/full 2>/dev/full
-}
-
-function test_print0() {
- invoke_bfs basic/a basic/b -print0 >"$OUT"
- diff_output
-}
-
-function test_fprint0() {
- invoke_bfs basic/a basic/b -fprint0 "$OUT"
- diff_output
-}
-
-function test_closed_stdin() {
- bfs_diff basic <&-
-}
-
-function test_ok_closed_stdin() {
- bfs_diff basic -ok echo \; <&-
-}
-
-function test_okdir_closed_stdin() {
- bfs_diff basic -okdir echo {} \; <&-
-}
-
-function test_closed_stdout() {
- fail invoke_bfs basic >&-
-}
-
-function test_closed_stderr() {
- fail invoke_bfs basic >&- 2>&-
-}
-
-function test_unique() {
- bfs_diff links/{file,symlink,hardlink} -unique
-}
-
-function test_unique_depth() {
- bfs_diff basic -unique -depth
-}
-
-function test_L_unique() {
- bfs_diff -L links/{file,symlink,hardlink} -unique
-}
-
-function test_L_unique_loops() {
- bfs_diff -L loops/deeply/nested -unique
-}
-
-function test_L_unique_depth() {
- bfs_diff -L loops/deeply/nested -unique -depth
-}
-
-function test_mount() {
- skip_unless test "$SUDO"
- skip_if test "$UNAME" = "Darwin"
-
- rm -rf scratch/*
- mkdir scratch/{foo,mnt}
- sudo mount -t tmpfs tmpfs scratch/mnt
- $TOUCH scratch/foo/bar scratch/mnt/baz
-
- bfs_diff scratch -mount
- local ret=$?
-
- sudo umount scratch/mnt
- return $ret
-}
-
-function test_L_mount() {
- skip_unless test "$SUDO"
- skip_if test "$UNAME" = "Darwin"
-
- rm -rf scratch/*
- mkdir scratch/{foo,mnt}
- sudo mount -t tmpfs tmpfs scratch/mnt
- ln -s ../mnt scratch/foo/bar
- $TOUCH scratch/mnt/baz
- ln -s ../mnt/baz scratch/foo/qux
-
- bfs_diff -L scratch -mount
- local ret=$?
-
- sudo umount scratch/mnt
- return $ret
-}
-
-function test_xdev() {
- skip_unless test "$SUDO"
- skip_if test "$UNAME" = "Darwin"
-
- rm -rf scratch/*
- mkdir scratch/{foo,mnt}
- sudo mount -t tmpfs tmpfs scratch/mnt
- $TOUCH scratch/foo/bar scratch/mnt/baz
-
- bfs_diff scratch -xdev
- local ret=$?
-
- sudo umount scratch/mnt
- return $ret
-}
-
-function test_L_xdev() {
- skip_unless test "$SUDO"
- skip_if test "$UNAME" = "Darwin"
-
- rm -rf scratch/*
- mkdir scratch/{foo,mnt}
- sudo mount -t tmpfs tmpfs scratch/mnt
- ln -s ../mnt scratch/foo/bar
- $TOUCH scratch/mnt/baz
- ln -s ../mnt/baz scratch/foo/qux
-
- bfs_diff -L scratch -xdev
- local ret=$?
-
- sudo umount scratch/mnt
- return $ret
-}
-
-function test_inum_mount() {
- skip_unless test "$SUDO"
- skip_if test "$UNAME" = "Darwin"
-
- rm -rf scratch/*
- mkdir scratch/{foo,mnt}
- sudo mount -t tmpfs tmpfs scratch/mnt
-
- bfs_diff scratch -inum "$(inum scratch/mnt)"
- local ret=$?
-
- sudo umount scratch/mnt
- return $ret
-}
-
-function test_inum_bind_mount() {
- skip_unless test "$SUDO"
- skip_unless test "$UNAME" = "Linux"
-
- rm -rf scratch/*
- $TOUCH scratch/{foo,bar}
- sudo mount --bind scratch/{foo,bar}
-
- bfs_diff scratch -inum "$(inum scratch/bar)"
- local ret=$?
-
- sudo umount scratch/bar
- return $ret
-}
-
-function test_type_bind_mount() {
- skip_unless test "$SUDO"
- skip_unless test "$UNAME" = "Linux"
-
- rm -rf scratch/*
- $TOUCH scratch/{file,null}
- sudo mount --bind /dev/null scratch/null
-
- bfs_diff scratch -type c
- local ret=$?
-
- sudo umount scratch/null
- return $ret
-}
-
-function test_xtype_bind_mount() {
- skip_unless test "$SUDO"
- skip_unless test "$UNAME" = "Linux"
-
- rm -rf scratch/*
- $TOUCH scratch/{file,null}
- sudo mount --bind /dev/null scratch/null
- ln -s /dev/null scratch/link
-
- bfs_diff -L scratch -type c
- local ret=$?
-
- sudo umount scratch/null
- return $ret
-}
-
-function test_inum_automount() {
- # bfs shouldn't trigger automounts unless it descends into them
-
- skip_unless test "$SUDO"
- skip_unless command -v systemd-mount &>/dev/null
-
- rm -rf scratch/*
- mkdir scratch/{foo,mnt}
- skip_unless sudo systemd-mount -A -o bind basic scratch/mnt
-
- local before=$(inum scratch/mnt)
- bfs_diff scratch -inum "$before" -prune
- local ret=$?
- local after=$(inum scratch/mnt)
-
- sudo systemd-umount scratch/mnt
-
- ((ret == 0 && before == after))
-}
-
function set_acl() {
case "$UNAME" in
Darwin)
@@ -3158,60 +618,6 @@ function set_acl() {
esac
}
-function test_acl() {
- rm -rf scratch/*
-
- skip_unless invoke_bfs scratch -quit -acl
-
- $TOUCH scratch/{normal,acl}
- skip_unless set_acl scratch/acl
- ln -s acl scratch/link
-
- bfs_diff scratch -acl
-}
-
-function test_L_acl() {
- rm -rf scratch/*
-
- skip_unless invoke_bfs scratch -quit -acl
-
- $TOUCH scratch/{normal,acl}
- skip_unless set_acl scratch/acl
- ln -s acl scratch/link
-
- bfs_diff -L scratch -acl
-}
-
-function test_capable() {
- skip_unless test "$SUDO"
- skip_unless test "$UNAME" = "Linux"
-
- rm -rf scratch/*
-
- skip_unless invoke_bfs scratch -quit -capable
-
- $TOUCH scratch/{normal,capable}
- sudo setcap all+ep scratch/capable
- ln -s capable scratch/link
-
- bfs_diff scratch -capable
-}
-
-function test_L_capable() {
- skip_unless test "$SUDO"
- skip_unless test "$UNAME" = "Linux"
-
- rm -rf scratch/*
-
- skip_unless invoke_bfs scratch -quit -capable
-
- $TOUCH scratch/{normal,capable}
- sudo setcap all+ep scratch/capable
- ln -s capable scratch/link
-
- bfs_diff -L scratch -capable
-}
-
function make_xattrs() {
rm -rf scratch/*
@@ -3241,205 +647,8 @@ function make_xattrs() {
esac
}
-function test_xattr() {
- skip_unless invoke_bfs scratch -quit -xattr
- skip_unless make_xattrs
- bfs_diff scratch -xattr
-}
-
-function test_L_xattr() {
- skip_unless invoke_bfs scratch -quit -xattr
- skip_unless make_xattrs
- bfs_diff -L scratch -xattr
-}
-
-function test_xattrname() {
- skip_unless invoke_bfs scratch -quit -xattr
- skip_unless make_xattrs
-
- case "$UNAME" in
- Darwin|FreeBSD)
- bfs_diff scratch -xattrname bfs_test
- ;;
- *)
- bfs_diff scratch -xattrname security.bfs_test
- ;;
- esac
-}
-
-function test_L_xattrname() {
- skip_unless invoke_bfs scratch -quit -xattr
- skip_unless make_xattrs
-
- case "$UNAME" in
- Darwin|FreeBSD)
- bfs_diff -L scratch -xattrname bfs_test
- ;;
- *)
- bfs_diff -L scratch -xattrname security.bfs_test
- ;;
- esac
-}
-
-function test_help() {
- invoke_bfs -help | grep -E '\{...?\}' && return 1
- invoke_bfs -D help | grep -E '\{...?\}' && return 1
- invoke_bfs -S help | grep -E '\{...?\}' && return 1
- invoke_bfs -regextype help | grep -E '\{...?\}' && return 1
-
- return 0
-}
-
-function test_version() {
- invoke_bfs -version >/dev/null
-}
-
-function test_warn() {
- local stderr=$(invoke_bfs basic -warn -depth -prune 2>&1 >/dev/null)
- [ -n "$stderr" ]
-}
-
-function test_nowarn() {
- local stderr=$(invoke_bfs basic -nowarn -depth -prune 2>&1 >/dev/null)
- [ -z "$stderr" ]
-}
-
-function test_typo() {
- invoke_bfs -dikkiq 2>&1 | grep follow >/dev/null
-}
-
-function test_D_multi() {
- bfs_diff -D opt,tree,unknown basic
-}
-
-function test_D_all() {
- bfs_diff -D all basic
-}
-
-function test_O0() {
- bfs_diff -O0 basic -not \( -type f -not -type f \)
-}
-
-function test_O1() {
- bfs_diff -O1 basic -not \( -type f -not -type f \)
-}
-
-function test_O2() {
- bfs_diff -O2 basic -not \( -type f -not -type f \)
-}
-
-function test_O3() {
- bfs_diff -O3 basic -not \( -type f -not -type f \)
-}
-
-function test_Ofast() {
- bfs_diff -Ofast basic -not \( -xtype f -not -xtype f \)
-}
-
-function test_S() {
- invoke_bfs -S "$1" -s basic >"$OUT"
- diff_output
-}
-
-function test_S_bfs() {
- test_S bfs
-}
-
-function test_S_dfs() {
- test_S dfs
-}
-
-function test_S_ids() {
- test_S ids
-}
-
-function test_exclude_name() {
- bfs_diff basic -exclude -name foo
-}
-
-function test_exclude_depth() {
- bfs_diff basic -depth -exclude -name foo
-}
-
-function test_exclude_mindepth() {
- bfs_diff basic -mindepth 3 -exclude -name foo
-}
-
-function test_exclude_print() {
- fail invoke_bfs basic -exclude -print
-}
-
-function test_exclude_exclude() {
- fail invoke_bfs basic -exclude -exclude -name foo
-}
-
-function test_flags() {
- skip_unless invoke_bfs scratch -quit -flags offline
-
- rm -rf scratch/*
-
- $TOUCH scratch/{foo,bar}
- skip_unless chflags offline scratch/bar
-
- bfs_diff scratch -flags -offline,nohidden
-}
-
-function test_files0_from_file() {
- cd weirdnames
- invoke_bfs -mindepth 1 -fprintf ../scratch/files0.in "%P\0"
- bfs_diff -files0-from ../scratch/files0.in
-}
-
-function test_files0_from_stdin() {
- cd weirdnames
- invoke_bfs -mindepth 1 -printf "%P\0" | bfs_diff -files0-from -
-}
-
-function test_files0_from_none() {
- printf "" | fail invoke_bfs -files0-from -
-}
-
-function test_files0_from_empty() {
- printf "\0" | fail invoke_bfs -files0-from -
-}
-
-function test_files0_from_nowhere() {
- fail invoke_bfs -files0-from
-}
-
-function test_files0_from_nothing() {
- fail invoke_bfs -files0-from basic/nonexistent
-}
-
-function test_files0_from_ok() {
- printf "basic\0" | fail invoke_bfs -files0-from - -ok echo {} \;
-}
-
-function test_stderr_fails_silently() {
- skip_unless test -e /dev/full
- bfs_diff -D all basic 2>/dev/full
-}
-
-function test_stderr_fails_loudly() {
- skip_unless test -e /dev/full
- fail invoke_bfs -D all basic -false -fprint /dev/full 2>/dev/full
-}
-
-function test_unexpected_operator() {
- fail invoke_bfs \! -o -print
-}
-
-function test_and_incomplete() {
- fail invoke_bfs -print -a
-}
-
-function test_or_incomplete() {
- fail invoke_bfs -print -o
-}
-
-function test_comma_incomplete() {
- fail invoke_bfs -print ,
-}
+cd "$TMP"
+set +e
BOL='\n'
EOL='\n'
@@ -3450,7 +659,6 @@ function update_eol() {
EOL="\\033[${COLUMNS}G "
}
-
if [ "$VERBOSE_TESTS" ]; then
BOL=''
elif [ -t 1 ]; then
@@ -3478,11 +686,12 @@ for TEST in "${enabled_tests[@]}"; do
fi
OUT="$TMP/$TEST.out"
+ mkdir -p "${OUT%/*}"
if [ "$VERBOSE_ERRORS" ]; then
- ("$TEST")
+ (. "$TESTS/$TEST.sh")
else
- ("$TEST") 2>"$TMP/stderr"
+ (. "$TESTS/$TEST.sh") 2>"$TMP/stderr"
fi
status=$?