@@ -186,3 +186,145 @@ fn asa_cjk_control_char() {
186186 // 3-byte CJK char as control: treated as unknown, like space
187187 asa_test ( "日test\n " , "test\n " ) ;
188188}
189+
190+ // ===== FILE ARGUMENT TESTS =====
191+
192+ fn asa_test_with_args ( args : & [ & str ] , expected_output : & str , expected_exit_code : i32 ) {
193+ let str_args: Vec < String > = args. iter ( ) . map ( |s| String :: from ( * s) ) . collect ( ) ;
194+ run_test ( TestPlan {
195+ cmd : String :: from ( "asa" ) ,
196+ args : str_args,
197+ stdin_data : String :: new ( ) ,
198+ expected_out : String :: from ( expected_output) ,
199+ expected_err : String :: from ( "" ) ,
200+ expected_exit_code,
201+ } ) ;
202+ }
203+
204+ // Test single file argument
205+ #[ test]
206+ fn asa_single_file ( ) {
207+ let project_root = env ! ( "CARGO_MANIFEST_DIR" ) ;
208+ let file1 = format ! ( "{}/tests/asa/file1.txt" , project_root) ;
209+ let expected = "Line 1 from file1\n Line 2 from file1\n \n Line 3 with double-space\n " ;
210+ asa_test_with_args ( & [ file1. as_str ( ) ] , expected, 0 ) ;
211+ }
212+
213+ // Test multiple file arguments (files processed sequentially)
214+ #[ test]
215+ fn asa_multiple_files ( ) {
216+ let project_root = env ! ( "CARGO_MANIFEST_DIR" ) ;
217+ let file1 = format ! ( "{}/tests/asa/file1.txt" , project_root) ;
218+ let file2 = format ! ( "{}/tests/asa/file2.txt" , project_root) ;
219+ let expected = concat ! (
220+ "Line 1 from file1\n " ,
221+ "Line 2 from file1\n " ,
222+ "\n " ,
223+ "Line 3 with double-space\n " ,
224+ "\x0c " ,
225+ "Page 1 from file2\n " ,
226+ "Normal line\r " ,
227+ "Overprint this\n "
228+ ) ;
229+ asa_test_with_args ( & [ file1. as_str ( ) , file2. as_str ( ) ] , expected, 0 ) ;
230+ }
231+
232+ // Test stdin with "-" argument
233+ #[ test]
234+ fn asa_stdin_dash ( ) {
235+ run_test ( TestPlan {
236+ cmd : String :: from ( "asa" ) ,
237+ args : vec ! [ String :: from( "-" ) ] ,
238+ stdin_data : String :: from ( " test from stdin\n " ) ,
239+ expected_out : String :: from ( "test from stdin\n " ) ,
240+ expected_err : String :: from ( "" ) ,
241+ expected_exit_code : 0 ,
242+ } ) ;
243+ }
244+
245+ // ===== EDGE CASE TESTS (POSIX COMPLIANCE) =====
246+
247+ // Test lines with only newline (control char missing)
248+ #[ test]
249+ fn asa_line_with_only_newline ( ) {
250+ // Empty line (just newline): first char is newline itself
251+ // The implementation should handle this gracefully
252+ asa_test ( "\n " , "\n " ) ;
253+ }
254+
255+ // Test very long lines (POSIX doesn't specify line length limits for asa)
256+ #[ test]
257+ fn asa_long_line ( ) {
258+ let long_content = "x" . repeat ( 5000 ) ;
259+ let input = format ! ( " {}\n " , long_content) ;
260+ let expected = format ! ( "{}\n " , long_content) ;
261+ asa_test ( & input, & expected) ;
262+ }
263+
264+ // Test tab character as control (treated as unknown, like space)
265+ #[ test]
266+ fn asa_tab_control ( ) {
267+ asa_test ( "\t text\n " , "text\n " ) ;
268+ }
269+
270+ // Test newline as control character (edge case)
271+ // First line is empty (just newline), second line has space control with "text"
272+ // Empty first line should output just a newline, then second line outputs normally
273+ #[ test]
274+ fn asa_newline_control ( ) {
275+ asa_test ( "\n text\n " , "\n text\n " ) ;
276+ }
277+
278+ // Test carriage return within content (not as control)
279+ #[ test]
280+ fn asa_embedded_carriage_return ( ) {
281+ asa_test ( " text\r with\r cr\n " , "text\r with\r cr\n " ) ;
282+ }
283+
284+ // Test form feed within content (not as control)
285+ #[ test]
286+ fn asa_embedded_form_feed ( ) {
287+ asa_test ( " text\x0c with\x0c ff\n " , "text\x0c with\x0c ff\n " ) ;
288+ }
289+
290+ // ===== SEQUENTIAL FILE PROCESSING =====
291+
292+ // Test that each file starts as if it's independent
293+ // (first line of second file should be treated as first line)
294+ #[ test]
295+ fn asa_file_independence ( ) {
296+ let project_root = env ! ( "CARGO_MANIFEST_DIR" ) ;
297+ let file2 = format ! ( "{}/tests/asa/file2.txt" , project_root) ;
298+ // file2.txt starts with '1' (form-feed) as first line
299+ // Should output form-feed at start since it's the first line of the file
300+ let expected = "\x0c Page 1 from file2\n Normal line\r Overprint this\n " ;
301+ asa_test_with_args ( & [ file2. as_str ( ) ] , expected, 0 ) ;
302+ }
303+
304+ // ===== SPECIAL CHARACTER COMBINATIONS =====
305+
306+ // Test all standard control characters in sequence
307+ #[ test]
308+ fn asa_all_controls_sequence ( ) {
309+ let input = " space\n 0zero\n 1one\n +plus\n " ;
310+ let expected = "space\n \n zero\n \x0c one\r plus\n " ;
311+ asa_test ( input, expected) ;
312+ }
313+
314+ // Test repeated form-feeds
315+ #[ test]
316+ fn asa_multiple_form_feeds ( ) {
317+ asa_test (
318+ "1page1\n 1page2\n 1page3\n " ,
319+ "\x0c page1\n \x0c page2\n \x0c page3\n " ,
320+ ) ;
321+ }
322+
323+ // Test alternating double-space and normal
324+ #[ test]
325+ fn asa_alternating_spacing ( ) {
326+ asa_test (
327+ " normal\n 0double\n normal\n 0double\n " ,
328+ "normal\n \n double\n normal\n \n double\n " ,
329+ ) ;
330+ }
0 commit comments