# SPDX-License-Identifier: BSD-2-Clause # Copyright (c) 2025 Baptiste Daroussin # Helper: create standard 5-line data file create_std_data() { cat > "$1" <<'EOF' line 1 line 2 line 3 line 4 line5 EOF } # --------------------------------------------------------------------------- # Append (a) # --------------------------------------------------------------------------- atf_test_case append append_head() { atf_set "descr" "Test append command (a)" } append_body() { create_std_data input.txt ed -s - <<'CMDS' H r input.txt 0a hello world . 2a hello world! . $a hello world!! . w output.txt CMDS cat > expected.txt <<'EOF' hello world line 1 hello world! line 2 line 3 line 4 line5 hello world!! EOF atf_check cmp output.txt expected.txt } # --------------------------------------------------------------------------- # Address parsing (addr) # --------------------------------------------------------------------------- atf_test_case address address_head() { atf_set "descr" "Test complex address parsing" } address_body() { cat > input.txt <<'EOF' line 1 line 2 line 3 line 4 line5 1ine6 line7 line8 line9 EOF ed -s - <<'CMDS' H r input.txt 1 d 1 1 d 1,2,d 1;+ + ,d 1,2;., + 2d w output.txt CMDS cat > expected.txt <<'EOF' line 2 line9 EOF atf_check cmp output.txt expected.txt } # --------------------------------------------------------------------------- # Change (c) # --------------------------------------------------------------------------- atf_test_case change change_head() { atf_set "descr" "Test change command (c)" } change_body() { create_std_data input.txt ed -s - <<'CMDS' H r input.txt 1c at the top . 4c in the middle . $c at the bottom . 2,3c between top/middle . w output.txt CMDS cat > expected.txt <<'EOF' at the top between top/middle in the middle at the bottom EOF atf_check cmp output.txt expected.txt } # --------------------------------------------------------------------------- # Delete (d) # --------------------------------------------------------------------------- atf_test_case delete delete_head() { atf_set "descr" "Test delete command (d)" } delete_body() { create_std_data input.txt ed -s - <<'CMDS' H r input.txt 1d 2;+1d $d w output.txt CMDS printf 'line 2\n' > expected.txt atf_check cmp output.txt expected.txt } # --------------------------------------------------------------------------- # Insert (i) # --------------------------------------------------------------------------- atf_test_case insert insert_head() { atf_set "descr" "Test insert command (i)" } insert_body() { create_std_data input.txt ed -s - <<'CMDS' H r input.txt 1i hello world . 2i hello world! . $i hello world!! . w output.txt CMDS cat > expected.txt <<'EOF' hello world hello world! line 1 line 2 line 3 line 4 hello world!! line5 EOF atf_check cmp output.txt expected.txt } # --------------------------------------------------------------------------- # Join (j) # --------------------------------------------------------------------------- atf_test_case join join_head() { atf_set "descr" "Test join command (j)" } join_body() { create_std_data input.txt ed -s - <<'CMDS' H r input.txt 1,1j 2,3j w output.txt CMDS cat > expected.txt <<'EOF' line 1 line 2line 3 line 4 line5 EOF atf_check cmp output.txt expected.txt } # --------------------------------------------------------------------------- # Mark (k) # --------------------------------------------------------------------------- atf_test_case mark mark_head() { atf_set "descr" "Test mark and reference commands (k, ')" } mark_body() { create_std_data input.txt ed -s - <<'CMDS' H r input.txt 2ka 1d 'am$ 1ka 0a hello world . 'ad u 'am0 w output.txt CMDS cat > expected.txt <<'EOF' line 3 hello world line 4 line5 line 2 EOF atf_check cmp output.txt expected.txt } # --------------------------------------------------------------------------- # Move (m) # --------------------------------------------------------------------------- atf_test_case move move_head() { atf_set "descr" "Test move command (m)"; } move_body() { create_std_data input.txt ed -s - <<'CMDS' H r input.txt 1,2m$ 1,2m$ 1,2m$ $m0 $m0 2,3m1 2,3m3 w output.txt CMDS cat > expected.txt <<'EOF' line5 line 1 line 2 line 3 line 4 EOF atf_check cmp output.txt expected.txt } # --------------------------------------------------------------------------- # Transfer / Copy (t) # --------------------------------------------------------------------------- atf_test_case transfer transfer_head() { atf_set "descr" "Test transfer/copy command (t)"; } transfer_body() { create_std_data input.txt ed -s - <<'CMDS' H r input.txt 1t0 2,3t2 ,t$ w output.txt CMDS cat > expected.txt <<'EOF' line 1 line 1 line 1 line 2 line 2 line 3 line 4 line5 line 1 line 1 line 1 line 2 line 2 line 3 line 4 line5 EOF atf_check cmp output.txt expected.txt } atf_test_case transfer_search transfer_search_head() { atf_set "descr" "Test transfer with address search (t)"; } transfer_search_body() { create_std_data input.txt ed -s - <<'CMDS' H r input.txt t0;/./ w output.txt CMDS cat > expected.txt <<'EOF' line 1 line5 line 2 line 3 line 4 line5 EOF atf_check cmp output.txt expected.txt } # --------------------------------------------------------------------------- # Undo (u) # --------------------------------------------------------------------------- atf_test_case undo undo_head() { atf_set "descr" "Test undo command (u)"; } undo_body() { create_std_data input.txt printf 'dummy\n' > readfile.txt ed -s - <<'CMDS' H r input.txt 1;r readfile.txt u a hello world . g/./s//x/\ a\ hello\ world u u u a hello world! . u 1,$d u 2,3d u c hello world!! . u u -1;.,+1j u u u .,+1t$ w output.txt CMDS cat > expected.txt <<'EOF' line 1 hello hello world!! line 2 line 3 line 4 line5 hello hello world!! EOF atf_check cmp output.txt expected.txt } # --------------------------------------------------------------------------- # Global (g) # --------------------------------------------------------------------------- atf_test_case global_move global_move_head() { atf_set "descr" "Test global command with move (g)"; } global_move_body() { create_std_data input.txt ed -s - <<'CMDS' H r input.txt g/./m0 g/./s/$/\ hello world g/hello /s/lo/p!/\ a\ order w output.txt CMDS cat > expected.txt <<'EOF' line5 help! world order line 4 help! world order line 3 help! world order line 2 help! world order line 1 help! world order EOF atf_check cmp output.txt expected.txt } atf_test_case global_change global_change_head() { atf_set "descr" "Test global command with change (g)"; } global_change_body() { create_std_data input.txt ed -s - <<'CMDS' H r input.txt g/[2-4]/-1,+1c\ hello world w output.txt CMDS printf 'hello world\n' > expected.txt atf_check cmp output.txt expected.txt } atf_test_case global_substitute global_substitute_head() { atf_set "descr" "Test global with substitute and move (g)"; } global_substitute_body() { create_std_data input.txt ed -s - <<'CMDS' H r input.txt g/./s//x/\ 3m0 g/./s/e/c/\ 2,3m1 w output.txt CMDS cat > expected.txt <<'EOF' linc 3 xine 1 xine 2 xinc 4 xinc5 EOF atf_check cmp output.txt expected.txt } atf_test_case global_undo global_undo_head() { atf_set "descr" "Test global with undo (g)"; } global_undo_body() { create_std_data input.txt ed -s - <<'CMDS' H r input.txt g/./s/./x/\ u\ s/./y/\ u\ s/./z/\ u u 0a hello . $a world . w output.txt CMDS cat > expected.txt <<'EOF' hello zine 1 line 2 line 3 line 4 line5 world EOF atf_check cmp output.txt expected.txt } atf_test_case global_copy global_copy_head() { atf_set "descr" "Test global with copy (g)"; } global_copy_body() { cat > input.txt <<'EOF' line 1 line 2 line 3 EOF ed -s - <<'CMDS' H r input.txt g/./1,3t$\ 1d w output.txt CMDS cat > expected.txt <<'EOF' line 1 line 2 line 3 line 2 line 3 line 1 line 3 line 1 line 2 EOF atf_check cmp output.txt expected.txt } # --------------------------------------------------------------------------- # Inverse global (v) # --------------------------------------------------------------------------- atf_test_case inverse_global inverse_global_head() { atf_set "descr" "Test inverse global command (v)"; } inverse_global_body() { create_std_data input.txt ed -s - <<'CMDS' H r input.txt v/[ ]/m0 v/[ ]/s/$/\ hello world v/hello /s/lo/p!/\ a\ order w output.txt CMDS cat > expected.txt <<'EOF' line5 order hello world line 1 order line 2 order line 3 order line 4 order EOF atf_check cmp output.txt expected.txt } # --------------------------------------------------------------------------- # Substitution (s) # --------------------------------------------------------------------------- atf_test_case subst_backreference subst_backreference_head() { atf_set "descr" "Test substitute with backreferences (s)"; } subst_backreference_body() { create_std_data input.txt ed -s - <<'CMDS' H r input.txt s/\([^ ][^ ]*\)/(\1)/g 2s /3/s /\(4\)/sr /\(.\)/srg %s/i/&e/ w output.txt CMDS cat > expected.txt <<'EOF' liene 1 (liene) (2) (liene) (3) liene (4) (()liene5) EOF atf_check cmp output.txt expected.txt } atf_test_case subst_range subst_range_head() { atf_set "descr" "Test substitute on range with count and repeat (s)"; } subst_range_body() { create_std_data input.txt ed -s - <<'CMDS' H r input.txt ,s/./(&)/3 s/$/00 2s//%/g s/^l w output.txt CMDS cat > expected.txt <<'EOF' li(n)e 1 i(n)e 200 li(n)e 3 li(n)e 4 li(n)e500 EOF atf_check cmp output.txt expected.txt } atf_test_case subst_charclass subst_charclass_head() { atf_set "descr" "Test substitute with character classes (s)"; } subst_charclass_body() { ed -s - <<'CMDS' H a hello/[]world . s/[/]/ / s/[[:digit:][]/ / s/[]]/ / w output.txt CMDS printf 'hello world\n' > expected.txt atf_check cmp output.txt expected.txt } # --------------------------------------------------------------------------- # Edit (e/E) # --------------------------------------------------------------------------- atf_test_case edit_file edit_file_head() { atf_set "descr" "Test edit file command (E)"; } edit_file_body() { printf 'hello world\n' > input.txt printf 'E e1_data.txt\n' > e1_data.txt ed -s - <<'CMDS' H r input.txt E e1_data.txt w output.txt CMDS printf 'E e1_data.txt\n' > expected.txt atf_check cmp output.txt expected.txt } atf_test_case edit_command edit_command_head() { atf_set "descr" "Test edit with shell command (E !)"; } edit_command_body() { printf 'E !echo hello world-\n' > input.txt ed -s - <<'CMDS' H r input.txt E !echo hello world- w output.txt CMDS printf 'hello world-\n' > expected.txt atf_check cmp output.txt expected.txt } atf_test_case edit_reread edit_reread_head() { atf_set "descr" "Test edit re-read default file (E)"; } edit_reread_body() { printf 'E !echo hello world-\n' > input.txt ed -s - <<'CMDS' H r input.txt E w output.txt CMDS printf 'E !echo hello world-\n' > expected.txt atf_check cmp output.txt expected.txt } atf_test_case edit_lowercase edit_lowercase_head() { atf_set "descr" "Test lowercase edit re-read (e)"; } edit_lowercase_body() { printf 'E !echo hello world-\n' > input.txt ed -s - <<'CMDS' H r input.txt e w output.txt CMDS printf 'E !echo hello world-\n' > expected.txt atf_check cmp output.txt expected.txt } # --------------------------------------------------------------------------- # Read (r) # --------------------------------------------------------------------------- atf_test_case read_command read_command_head() { atf_set "descr" "Test read with shell command (r !)"; } read_command_body() { create_std_data input.txt ed -s - <<'CMDS' H r input.txt 1;r !echo hello world 1 r !echo hello world w output.txt CMDS cat > expected.txt <<'EOF' line 1 hello world line 2 line 3 line 4 line5 hello world EOF atf_check cmp output.txt expected.txt } atf_test_case read_default read_default_head() { atf_set "descr" "Test read with default filename (r)"; } read_default_body() { create_std_data input.txt ed -s - <<'CMDS' H r input.txt r w output.txt CMDS cat > expected.txt <<'EOF' line 1 line 2 line 3 line 4 line5 line 1 line 2 line 3 line 4 line5 EOF atf_check cmp output.txt expected.txt } atf_test_case read_file read_file_head() { atf_set "descr" "Test read from file (r)"; } read_file_body() { printf 'r r3_data.txt\n' > r3_data.txt ed -s - <<'CMDS' H r r3_data.txt r r3_data.txt w output.txt CMDS cat > expected.txt <<'EOF' r r3_data.txt r r3_data.txt EOF atf_check cmp output.txt expected.txt } # --------------------------------------------------------------------------- # Write (w) # --------------------------------------------------------------------------- atf_test_case write_pipe write_pipe_head() { atf_set "descr" "Test write to shell command (w !)"; } write_pipe_body() { create_std_data input.txt ed -s - <<'CMDS' H r input.txt w !cat >\!.z r \!.z w output.txt CMDS cat > expected.txt <<'EOF' line 1 line 2 line 3 line 4 line5 line 1 line 2 line 3 line 4 line5 EOF atf_check cmp output.txt expected.txt } # --------------------------------------------------------------------------- # Quit (q) # --------------------------------------------------------------------------- atf_test_case quit quit_head() { atf_set "descr" "Test quit command (q)"; } quit_body() { ed -s - <<'CMDS' H w output.txt a hello . q CMDS atf_check -s exit:0 test ! -s output.txt } # --------------------------------------------------------------------------- # Shell command (!) # --------------------------------------------------------------------------- atf_test_case shell_command shell_command_head() { atf_set "descr" "Test shell command execution (!)"; } shell_command_body() { ed -s - <<'CMDS' H !read one hello, world a okay . w output.txt CMDS printf 'okay\n' > expected.txt atf_check cmp output.txt expected.txt } # --------------------------------------------------------------------------- # Newline handling (nl) # --------------------------------------------------------------------------- atf_test_case newline_insert newline_insert_head() { atf_set "descr" "Test inserting blank lines"; } newline_insert_body() { create_std_data input.txt ed -s - <<'CMDS' H r input.txt 1 0a hello world . w output.txt CMDS cat > expected.txt <<'EOF' hello world line 1 line 2 line 3 line 4 line5 EOF atf_check cmp output.txt expected.txt } atf_test_case newline_search newline_search_head() { atf_set "descr" "Test address search with semicolon"; } newline_search_body() { create_std_data input.txt ed -s - <<'CMDS' H r input.txt a hello world . 0;/./ w output.txt CMDS cat > expected.txt <<'EOF' line 1 line 2 line 3 line 4 line5 hello world EOF atf_check cmp output.txt expected.txt } # --------------------------------------------------------------------------- # Error tests # --------------------------------------------------------------------------- atf_test_case err_append_suffix err_append_suffix_head() { atf_set "descr" "Error: invalid append suffix (aa)"; } err_append_suffix_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H aa hello world . CMDS } atf_test_case err_addr_out_of_range err_addr_out_of_range_head() { atf_set "descr" "Error: address out of range"; } err_addr_out_of_range_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H 100 CMDS } atf_test_case err_addr_negative err_addr_negative_head() { atf_set "descr" "Error: negative address"; } err_addr_negative_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H -100 CMDS } atf_test_case err_bang_addr err_bang_addr_head() { atf_set "descr" "Error: shell command with address"; } err_bang_addr_body() { printf 'test\n' > input.txt atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H r input.txt .!date CMDS } atf_test_case err_bang_double err_bang_double_head() { atf_set "descr" "Error: double bang without previous command"; } err_bang_double_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H !! CMDS } atf_test_case err_change_suffix err_change_suffix_head() { atf_set "descr" "Error: invalid change suffix (cc)"; } err_change_suffix_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H cc hello world . CMDS } atf_test_case err_change_zero err_change_zero_head() { atf_set "descr" "Error: change at line 0"; } err_change_zero_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H 0c hello world . CMDS } atf_test_case err_delete_suffix err_delete_suffix_head() { atf_set "descr" "Error: invalid delete suffix (dd)"; } err_delete_suffix_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H dd CMDS } atf_test_case err_edit_suffix err_edit_suffix_head() { atf_set "descr" "Error: invalid edit suffix (ee)"; } err_edit_suffix_body() { printf 'test\n' > e1.err atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H ee e1.err CMDS } atf_test_case err_edit_addr err_edit_addr_head() { atf_set "descr" "Error: edit with address"; } err_edit_addr_body() { printf 'test\n' > e2.err atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H r e2.err .e e2.err CMDS } atf_test_case err_edit_nosuffix err_edit_nosuffix_head() { atf_set "descr" "Error: edit without space before filename"; } err_edit_nosuffix_body() { printf 'test\n' > ee.err atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H ee.err CMDS } atf_test_case err_file_addr err_file_addr_head() { atf_set "descr" "Error: file command with address"; } err_file_addr_body() { printf 'test\n' > input.txt atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H r input.txt .f f1.err CMDS } atf_test_case err_file_suffix err_file_suffix_head() { atf_set "descr" "Error: invalid file suffix"; } err_file_suffix_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H ff1.err CMDS } atf_test_case err_global_delim err_global_delim_head() { atf_set "descr" "Error: invalid pattern delimiter in global"; } err_global_delim_body() { printf 'test\n' > input.txt atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H r input.txt g/./s //x/ CMDS } atf_test_case err_global_empty err_global_empty_head() { atf_set "descr" "Error: empty pattern in global"; } err_global_empty_body() { printf 'test\n' > input.txt atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H r input.txt g//s/./x/ CMDS } atf_test_case err_global_incomplete err_global_incomplete_head() { atf_set "descr" "Error: incomplete global command"; } err_global_incomplete_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H g CMDS } atf_test_case err_help_addr err_help_addr_head() { atf_set "descr" "Error: help with address"; } err_help_addr_body() { printf 'test\n' > input.txt atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H r input.txt .h CMDS } atf_test_case err_insert_suffix err_insert_suffix_head() { atf_set "descr" "Error: invalid insert suffix (ii)"; } err_insert_suffix_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H ii hello world . CMDS } atf_test_case err_insert_zero err_insert_zero_head() { atf_set "descr" "Error: insert at line 0"; } err_insert_zero_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H 0i hello world . CMDS } atf_test_case err_mark_upper err_mark_upper_head() { atf_set "descr" "Error: mark with uppercase letter"; } err_mark_upper_body() { printf 'test\n' > input.txt atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H r input.txt kA CMDS } atf_test_case err_mark_zero err_mark_zero_head() { atf_set "descr" "Error: mark at line 0"; } err_mark_zero_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H 0ka CMDS } atf_test_case err_mark_ref err_mark_ref_head() { atf_set "descr" "Error: reference to deleted mark"; } err_mark_ref_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H a hello . .ka 'ad 'ap CMDS } atf_test_case err_move_dest err_move_dest_head() { atf_set "descr" "Error: move to own range"; } err_move_dest_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H a hello world . 1,$m1 CMDS } atf_test_case err_quit_addr err_quit_addr_head() { atf_set "descr" "Error: quit with address"; } err_quit_addr_body() { printf 'test\n' > input.txt atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H r input.txt .q CMDS } atf_test_case err_read_nofile err_read_nofile_head() { atf_set "descr" "Error: read nonexistent file"; } err_read_nofile_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H r a-good-book CMDS } atf_test_case err_subst_delim err_subst_delim_head() { atf_set "descr" "Error: invalid substitute delimiter"; } err_subst_delim_body() { printf 'test\n' > input.txt atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H r input.txt s . x CMDS } atf_test_case err_subst_infinite err_subst_infinite_head() { atf_set "descr" "Error: infinite substitution loop"; } err_subst_infinite_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H a a . s/x*/a/g CMDS } atf_test_case err_subst_bracket err_subst_bracket_head() { atf_set "descr" "Error: unbalanced brackets in substitute"; } err_subst_bracket_body() { printf 'test\n' > input.txt atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H r input.txt s/[xyx/a/ CMDS } atf_test_case err_subst_escape err_subst_escape_head() { atf_set "descr" "Error: invalid escape in substitute pattern"; } err_subst_escape_body() { printf 'test\n' > input.txt atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H r input.txt s/\a\b\c/xyz/ CMDS } atf_test_case err_subst_empty err_subst_empty_head() { atf_set "descr" "Error: empty substitute pattern"; } err_subst_empty_body() { printf 'test\n' > input.txt atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H r input.txt s//xyz/ CMDS } atf_test_case err_subst_bare err_subst_bare_head() { atf_set "descr" "Error: bare substitute without previous"; } err_subst_bare_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H s CMDS } atf_test_case err_subst_sr err_subst_sr_head() { atf_set "descr" "Error: invalid sr suffix"; } err_subst_sr_body() { atf_check -s exit:2 -o ignore -e not-empty ed -s - <<'CMDS' H a hello world . /./ sr CMDS } atf_test_case err_subst_equiv err_subst_equiv_head() { atf_set "descr" "Error: invalid equivalence class in substitute"; } err_subst_equiv_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H a hello . s/[h[=]/x/ CMDS } atf_test_case err_subst_class err_subst_class_head() { atf_set "descr" "Error: unterminated character class"; } err_subst_class_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H a hello . s/[h[:]/x/ CMDS } atf_test_case err_subst_collate err_subst_collate_head() { atf_set "descr" "Error: invalid collation class"; } err_subst_collate_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H a hello . s/[h[.]/x/ CMDS } atf_test_case err_transfer_suffix err_transfer_suffix_head() { atf_set "descr" "Error: invalid transfer suffix (tt)"; } err_transfer_suffix_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H tt CMDS } atf_test_case err_transfer_addr err_transfer_addr_head() { atf_set "descr" "Error: invalid transfer address"; } err_transfer_addr_body() { printf 'test\n' > input.txt atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H r input.txt t0;-1 CMDS } atf_test_case err_undo_addr err_undo_addr_head() { atf_set "descr" "Error: undo with address"; } err_undo_addr_body() { printf 'test\n' > input.txt atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H r input.txt .u CMDS } atf_test_case err_write_nopath err_write_nopath_head() { atf_set "descr" "Error: write to invalid path"; } err_write_nopath_body() { printf 'test\n' > input.txt atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H r input.txt w /to/some/far-away/place CMDS } atf_test_case err_write_suffix err_write_suffix_head() { atf_set "descr" "Error: invalid write suffix (ww)"; } err_write_suffix_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H ww.o CMDS } atf_test_case err_write_flags err_write_flags_head() { atf_set "descr" "Error: invalid write flags (wqp)"; } err_write_flags_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H wqp w.o CMDS } atf_test_case err_crypt_addr err_crypt_addr_head() { atf_set "descr" "Error: crypt with address"; } err_crypt_addr_body() { printf 'test\n' > input.txt atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H r input.txt .x CMDS } atf_test_case err_scroll err_scroll_head() { atf_set "descr" "Error: invalid scroll command"; } err_scroll_body() { atf_check -s exit:2 -e not-empty ed -s - <<'CMDS' H z z CMDS } # --------------------------------------------------------------------------- # Unicode support # --------------------------------------------------------------------------- atf_test_case unicode_list_multibyte unicode_list_multibyte_head() { atf_set "descr" "l command displays multibyte UTF-8 as-is"; } unicode_list_multibyte_body() { export LC_CTYPE=C.UTF-8 printf 'café\n' > input.txt atf_check -o inline:'café$\n' ed -s - <<'CMDS' H r input.txt l Q CMDS } atf_test_case unicode_list_cjk unicode_list_cjk_head() { atf_set "descr" "l command displays CJK characters as-is"; } unicode_list_cjk_body() { export LC_CTYPE=C.UTF-8 printf '日本語テスト\n' > input.txt atf_check -o inline:'日本語テスト$\n' ed -s - <<'CMDS' H r input.txt l Q CMDS } atf_test_case unicode_list_mixed unicode_list_mixed_head() { atf_set "descr" "l command displays mixed ASCII/UTF-8 correctly"; } unicode_list_mixed_body() { export LC_CTYPE=C.UTF-8 printf 'hello café 世界\n' > input.txt atf_check -o inline:'hello café 世界$\n' ed -s - <<'CMDS' H r input.txt l Q CMDS } atf_test_case unicode_list_invalid unicode_list_invalid_head() { atf_set "descr" "l command escapes invalid UTF-8 as octal"; } unicode_list_invalid_body() { export LC_CTYPE=C.UTF-8 printf '\200\201\376\377\n' > input.txt atf_check -o inline:'\\200\\201\\376\\377$\n' ed -s - <<'CMDS' H r input.txt l Q CMDS } atf_test_case unicode_list_wrap_cjk unicode_list_wrap_cjk_head() { atf_set "descr" "l command wraps correctly around double-width CJK"; } unicode_list_wrap_cjk_body() { export LC_CTYPE=C.UTF-8 # 69 A's + 日本 (2 CJK chars): 69 + 2 = 71 cols for 日 (fits), # 71 + 2 = 73 for 本 (exceeds 72), so 本 wraps to next line. printf 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA日本\n' > input.txt ed -s - <<'CMDS' > output.txt H r input.txt l Q CMDS printf 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA日\\\n本$\n' > expected.txt atf_check cmp output.txt expected.txt } atf_test_case unicode_print unicode_print_head() { atf_set "descr" "p command passes through UTF-8 correctly"; } unicode_print_body() { export LC_CTYPE=C.UTF-8 printf 'café 日本語\n' > input.txt atf_check -o inline:'café 日本語\n' ed -s - <<'CMDS' H r input.txt p Q CMDS } atf_test_case unicode_number unicode_number_head() { atf_set "descr" "n command displays line number with UTF-8"; } unicode_number_body() { export LC_CTYPE=C.UTF-8 printf 'café 日本語\n' > input.txt atf_check -o inline:'1\tcafé 日本語\n' ed -s - <<'CMDS' H r input.txt n Q CMDS } atf_test_case unicode_regex unicode_regex_head() { atf_set "descr" "Regex search matches UTF-8 characters"; } unicode_regex_body() { export LC_CTYPE=C.UTF-8 printf 'café\ntest\nüber\n' > input.txt atf_check -o inline:'café\n' ed -s - <<'CMDS' H r input.txt g/é/p Q CMDS } atf_test_case unicode_regex_charclass unicode_regex_charclass_head() { atf_set "descr" "Regex character classes work with UTF-8"; } unicode_regex_charclass_body() { export LC_CTYPE=C.UTF-8 printf 'café123\ntest456\n' > input.txt atf_check -o inline:'café123\n' ed -s - <<'CMDS' H r input.txt g/[[:alpha:]]*é/p Q CMDS } atf_test_case unicode_substitute unicode_substitute_head() { atf_set "descr" "Substitute replaces UTF-8 characters"; } unicode_substitute_body() { export LC_CTYPE=C.UTF-8 printf 'café\n' > input.txt ed -s - <<'CMDS' H r input.txt s/é/e/ w output.txt Q CMDS printf 'cafe\n' > expected.txt atf_check cmp output.txt expected.txt } atf_test_case unicode_substitute_cjk unicode_substitute_cjk_head() { atf_set "descr" "Substitute replaces CJK characters"; } unicode_substitute_cjk_body() { export LC_CTYPE=C.UTF-8 printf 'hello 世界\n' > input.txt ed -s - <<'CMDS' H r input.txt s/世界/world/ w output.txt Q CMDS printf 'hello world\n' > expected.txt atf_check cmp output.txt expected.txt } atf_test_case unicode_global_substitute unicode_global_substitute_head() { atf_set "descr" "Global substitute works with UTF-8"; } unicode_global_substitute_body() { export LC_CTYPE=C.UTF-8 printf 'à la carte\nà bientôt\nhello\n' > input.txt ed -s - <<'CMDS' H r input.txt g/à/s/à/a/ w output.txt Q CMDS cat > expected.txt <<'EOF' a la carte a bientôt hello EOF atf_check cmp output.txt expected.txt } atf_test_case unicode_join unicode_join_head() { atf_set "descr" "Join preserves UTF-8 content"; } unicode_join_body() { export LC_CTYPE=C.UTF-8 printf 'café\n世界\n' > input.txt ed -s - <<'CMDS' H r input.txt 1,2j w output.txt Q CMDS printf 'café世界\n' > expected.txt atf_check cmp output.txt expected.txt } atf_test_case unicode_append unicode_append_head() { atf_set "descr" "Append preserves UTF-8 text"; } unicode_append_body() { export LC_CTYPE=C.UTF-8 ed -s - <<'CMDS' H a première deuxième . w output.txt Q CMDS cat > expected.txt <<'EOF' première deuxième EOF atf_check cmp output.txt expected.txt } atf_test_case unicode_cyrillic unicode_cyrillic_head() { atf_set "descr" "Cyrillic: append, substitute, print, regex search"; } unicode_cyrillic_body() { export LC_CTYPE=C.UTF-8 ed -s - <<'CMDS' > output.txt H a Привет . s/ривет/ока/ 1p a Строка . 1 /а/p 1,$p Q CMDS cat > expected.txt <<'EOF' Пока Пока Строка Пока Строка EOF atf_check cmp output.txt expected.txt } # --------------------------------------------------------------------------- # Registration # --------------------------------------------------------------------------- atf_init_test_cases() { # Basic commands atf_add_test_case append atf_add_test_case address atf_add_test_case change atf_add_test_case delete atf_add_test_case insert atf_add_test_case join atf_add_test_case mark atf_add_test_case move atf_add_test_case transfer atf_add_test_case transfer_search atf_add_test_case undo # Global commands atf_add_test_case global_move atf_add_test_case global_change atf_add_test_case global_substitute atf_add_test_case global_undo atf_add_test_case global_copy atf_add_test_case inverse_global # Substitution atf_add_test_case subst_backreference atf_add_test_case subst_range atf_add_test_case subst_charclass # File operations atf_add_test_case edit_file atf_add_test_case edit_command atf_add_test_case edit_reread atf_add_test_case edit_lowercase atf_add_test_case read_command atf_add_test_case read_default atf_add_test_case read_file atf_add_test_case write_pipe atf_add_test_case quit atf_add_test_case shell_command # Newline handling atf_add_test_case newline_insert atf_add_test_case newline_search # Unicode support atf_add_test_case unicode_list_multibyte atf_add_test_case unicode_list_cjk atf_add_test_case unicode_list_mixed atf_add_test_case unicode_list_invalid atf_add_test_case unicode_list_wrap_cjk atf_add_test_case unicode_print atf_add_test_case unicode_number atf_add_test_case unicode_regex atf_add_test_case unicode_regex_charclass atf_add_test_case unicode_substitute atf_add_test_case unicode_substitute_cjk atf_add_test_case unicode_global_substitute atf_add_test_case unicode_join atf_add_test_case unicode_append atf_add_test_case unicode_cyrillic # Error tests atf_add_test_case err_append_suffix atf_add_test_case err_addr_out_of_range atf_add_test_case err_addr_negative atf_add_test_case err_bang_addr atf_add_test_case err_bang_double atf_add_test_case err_change_suffix atf_add_test_case err_change_zero atf_add_test_case err_delete_suffix atf_add_test_case err_edit_suffix atf_add_test_case err_edit_addr atf_add_test_case err_edit_nosuffix atf_add_test_case err_file_addr atf_add_test_case err_file_suffix atf_add_test_case err_global_delim atf_add_test_case err_global_empty atf_add_test_case err_global_incomplete atf_add_test_case err_help_addr atf_add_test_case err_insert_suffix atf_add_test_case err_insert_zero atf_add_test_case err_mark_upper atf_add_test_case err_mark_zero atf_add_test_case err_mark_ref atf_add_test_case err_move_dest atf_add_test_case err_quit_addr atf_add_test_case err_read_nofile atf_add_test_case err_subst_delim atf_add_test_case err_subst_infinite atf_add_test_case err_subst_bracket atf_add_test_case err_subst_escape atf_add_test_case err_subst_empty atf_add_test_case err_subst_bare atf_add_test_case err_subst_sr atf_add_test_case err_subst_equiv atf_add_test_case err_subst_class atf_add_test_case err_subst_collate atf_add_test_case err_transfer_suffix atf_add_test_case err_transfer_addr atf_add_test_case err_undo_addr atf_add_test_case err_write_nopath atf_add_test_case err_write_suffix atf_add_test_case err_write_flags atf_add_test_case err_crypt_addr atf_add_test_case err_scroll }