4646DUMMY_UNICODE_LENGTH = len (DUMMY_UNICODE )
4747DUMMY_FILE_WRITECHUNK = 'fileio/write_chunk'
4848DUMMY_FILE_WRITEFILE = 'fileio/write_file'
49+ # TODO: add similar tests for write_file_lines and enable next
50+ # DUMMY_FILE_WRITEFILELINES = 'fileio/write_file_lines'
51+ DUMMY_FILE_READFILE = 'fileio/read_file'
52+ DUMMY_FILE_READFILELINES = 'fileio/read_file_lines'
53+ DUMMY_FILE_READHEADLINES = 'fileio/read_head_lines'
54+ DUMMY_FILE_READTAILLINES = 'fileio/read_tail_lines'
55+ DUMMY_FILE_DELETEFILE = 'fileio/delete_file'
56+ DUMMY_FILE_GETFILESIZE = 'fileio/get_file_size'
57+ DUMMY_FILE_MAKESYMLINKSRC = 'fileio/make_symlink/link'
58+ DUMMY_FILE_MAKESYMLINKDST = 'fileio/make_symlink/target'
59+ # NOTE: getsize returns 4k for directories
60+ DUMMY_DIRECTORY_SIZE = 4096
4961
5062assert isinstance (DUMMY_BYTES , bytes )
5163
@@ -57,6 +69,8 @@ def setUp(self):
5769 """Initialize test environment for write_chunk tests"""
5870 super (MigSharedFileio__write_chunk , self ).setUp ()
5971 self .tmp_path = temppath (DUMMY_FILE_WRITECHUNK , self )
72+ # Output dir is created by default here
73+ os .makedirs (os .path .dirname (self .tmp_path ), exist_ok = True )
6074 cleanpath (os .path .dirname (DUMMY_FILE_WRITECHUNK ), self )
6175
6276 def test_return_false_on_invalid_data (self ):
@@ -155,6 +169,7 @@ def setUp(self):
155169 """Initialize test environment for write_file tests"""
156170 super (MigSharedFileio__write_file , self ).setUp ()
157171 self .tmp_path = temppath (DUMMY_FILE_WRITEFILE , self )
172+ # Output dir is created by default here
158173 cleanpath (os .path .dirname (DUMMY_FILE_WRITEFILE ), self )
159174
160175 def test_return_false_on_invalid_data (self ):
@@ -246,5 +261,273 @@ def test_store_unicode_in_binary_mode(self):
246261 self .assertEqual (content [:], DUMMY_UNICODE )
247262
248263
264+ class MigSharedFileio__read_file (MigTestCase ):
265+ """Test the read_file function from mig.shared.fileio module"""
266+
267+ def setUp (self ):
268+ """Initialize test environment for read_file tests"""
269+ super (MigSharedFileio__read_file , self ).setUp ()
270+ self .tmp_path = temppath (DUMMY_FILE_READFILE , self )
271+ # We generally need output dir to exist here
272+ os .makedirs (os .path .dirname (self .tmp_path ), exist_ok = True )
273+ cleanpath (os .path .dirname (self .tmp_path ), self )
274+
275+ def test_reads_bytes (self ):
276+ """Test read_file returns byte content with binary mode"""
277+ with open (self .tmp_path , 'wb' ) as fh :
278+ fh .write (DUMMY_BYTES )
279+ content = fileio .read_file (self .tmp_path , self .logger , mode = 'rb' )
280+ self .assertEqual (content , DUMMY_BYTES )
281+
282+ def test_reads_text (self ):
283+ """Test read_file returns text with text mode"""
284+ with open (self .tmp_path , 'w' ) as fh :
285+ fh .write (DUMMY_UNICODE )
286+ content = fileio .read_file (self .tmp_path , self .logger , mode = 'r' )
287+ self .assertEqual (content , DUMMY_UNICODE )
288+
289+ def test_allows_missing_file (self ):
290+ """Test read_file returns None with allow_missing=True"""
291+ content = fileio .read_file (
292+ 'missing.txt' , self .logger , allow_missing = True )
293+ self .assertIsNone (content )
294+
295+ def test_reports_missing_file (self ):
296+ """Test read_file returns None with allow_missing=False"""
297+ self .logger .forgive_errors ()
298+ content = fileio .read_file (
299+ 'missing.txt' , self .logger , allow_missing = False )
300+ self .assertIsNone (content )
301+
302+ def test_handles_directory_path (self ):
303+ """Test read_file returns None when path is directory"""
304+ self .logger .forgive_errors ()
305+ os .makedirs (self .tmp_path )
306+ content = fileio .read_file (self .tmp_path , self .logger )
307+ self .assertIsNone (content )
308+
309+
310+ class MigSharedFileio__read_file_lines (MigTestCase ):
311+ """Test the read_file_lines function from mig.shared.fileio module"""
312+
313+ def setUp (self ):
314+ """Initialize test environment for read_file_lines tests"""
315+ super (MigSharedFileio__read_file_lines , self ).setUp ()
316+ self .tmp_path = temppath (DUMMY_FILE_READFILELINES , self )
317+ # We generally need output dir to exist here
318+ os .makedirs (os .path .dirname (self .tmp_path ), exist_ok = True )
319+ cleanpath (os .path .dirname (self .tmp_path ), self )
320+
321+ def test_returns_empty_list_for_empty_file (self ):
322+ """Test read_file_lines returns empty list for empty file"""
323+ open (self .tmp_path , 'w' ).close ()
324+ lines = fileio .read_file_lines (self .tmp_path , self .logger )
325+ self .assertEqual (lines , [])
326+
327+ def test_reads_lines_from_file (self ):
328+ """Test read_file_lines returns lines from text file"""
329+ with open (self .tmp_path , 'w' ) as fh :
330+ fh .write ("line1\n line2\n line3" )
331+ lines = fileio .read_file_lines (self .tmp_path , self .logger )
332+ self .assertEqual (lines , ["line1\n " , "line2\n " , "line3" ])
333+
334+ def test_none_for_missing_file (self ):
335+ self .logger .forgive_errors ()
336+ lines = fileio .read_file_lines ('missing.txt' , self .logger )
337+ self .assertIsNone (lines )
338+
339+
340+ class MigSharedFileio__get_file_size (MigTestCase ):
341+ """Test the get_file_size function from mig.shared.fileio module"""
342+
343+ def setUp (self ):
344+ """Initialize test environment for get_file_size tests"""
345+ super (MigSharedFileio__get_file_size , self ).setUp ()
346+ self .tmp_path = temppath (DUMMY_FILE_GETFILESIZE , self )
347+ # We generally need output dir to exist here
348+ os .makedirs (os .path .dirname (self .tmp_path ), exist_ok = True )
349+ cleanpath (os .path .dirname (self .tmp_path ), self )
350+
351+ def test_returns_file_size (self ):
352+ """Test get_file_size returns correct file size"""
353+ with open (self .tmp_path , 'wb' ) as fh :
354+ fh .write (DUMMY_BYTES )
355+ size = fileio .get_file_size (self .tmp_path , self .logger )
356+ self .assertEqual (size , DUMMY_BYTES_LENGTH )
357+
358+ def test_handles_missing_file (self ):
359+ """Test get_file_size returns -1 for missing file"""
360+ self .logger .forgive_errors ()
361+ size = fileio .get_file_size ('missing.txt' , self .logger )
362+ # TODO: fix called function to return on exception and enable next line
363+ # self.assertEqual(size, -1)
364+ self .assertIsNone (size )
365+
366+ def test_handles_directory (self ):
367+ """Test get_file_size returns -1 when path is directory"""
368+ self .logger .forgive_errors ()
369+ os .makedirs (self .tmp_path )
370+ size = fileio .get_file_size (self .tmp_path , self .logger )
371+ self .assertEqual (size , DUMMY_DIRECTORY_SIZE )
372+
373+
374+ class MigSharedFileio__delete_file (MigTestCase ):
375+ """Test the delete_file function from mig.shared.fileio module"""
376+
377+ def setUp (self ):
378+ """Initialize test environment for delete_file tests"""
379+ super (MigSharedFileio__delete_file , self ).setUp ()
380+ self .tmp_path = temppath (DUMMY_FILE_DELETEFILE , self )
381+ # We generally need output dir to exist here
382+ os .makedirs (os .path .dirname (self .tmp_path ), exist_ok = True )
383+ cleanpath (os .path .dirname (DUMMY_FILE_DELETEFILE ), self )
384+
385+ def test_deletes_existing_file (self ):
386+ """Test delete_file removes existing file"""
387+ open (self .tmp_path , 'w' ).close ()
388+ result = fileio .delete_file (self .tmp_path , self .logger )
389+ self .assertTrue (result )
390+ self .assertFalse (os .path .exists (self .tmp_path ))
391+
392+ def test_handles_missing_file_with_allow_missing (self ):
393+ """Test delete_file succeeds with allow_missing=True"""
394+ result = fileio .delete_file (
395+ 'missing.txt' , self .logger , allow_missing = True )
396+ self .assertTrue (result )
397+
398+ def test_false_for_missing_file_without_allow_missing (self ):
399+ """Test delete_file returns False with allow_missing=False"""
400+ self .logger .forgive_errors ()
401+ result = fileio .delete_file ('missing.txt' ,
402+ self .logger ,
403+ allow_missing = False )
404+ self .assertFalse (result )
405+
406+
407+ class MigSharedFileio__read_head_lines (MigTestCase ):
408+ """Test the read_head_lines function from mig.shared.fileio module"""
409+
410+ def setUp (self ):
411+ """Initialize test environment for read_head_lines tests"""
412+ super (MigSharedFileio__read_head_lines , self ).setUp ()
413+ self .tmp_path = temppath (DUMMY_FILE_READHEADLINES , self )
414+ # We generally need output dir to exist here
415+ os .makedirs (os .path .dirname (self .tmp_path ), exist_ok = True )
416+ cleanpath (os .path .dirname (self .tmp_path ), self )
417+
418+ def test_reads_requested_lines (self ):
419+ """Test read_head_lines returns requested number of lines"""
420+ with open (self .tmp_path , 'w' ) as fh :
421+ fh .write ("line1\n line2\n line3\n line4" )
422+ lines = fileio .read_head_lines (self .tmp_path , 2 , self .logger )
423+ self .assertEqual (lines , ["line1\n " , "line2\n " ])
424+
425+ def test_returns_all_lines_when_requested_more (self ):
426+ """Test read_head_lines returns all lines when file has fewer"""
427+ with open (self .tmp_path , 'w' ) as fh :
428+ fh .write ("line1\n line2" )
429+ lines = fileio .read_head_lines (self .tmp_path , 5 , self .logger )
430+ self .assertEqual (lines , ["line1\n " , "line2" ])
431+
432+ def test_returns_empty_list_for_empty_file (self ):
433+ """Test read_head_lines returns empty for empty file"""
434+ open (self .tmp_path , 'w' ).close ()
435+ lines = fileio .read_head_lines (self .tmp_path , 3 , self .logger )
436+ self .assertEqual (lines , [])
437+
438+ def test_empty_for_missing_file (self ):
439+ """Test read_head_lines returns [] for missing file"""
440+ self .logger .forgive_errors ()
441+ lines = fileio .read_head_lines ('missing.txt' , 3 , self .logger )
442+ self .assertEqual (lines , [])
443+
444+
445+ class MigSharedFileio__read_tail_lines (MigTestCase ):
446+ """Test the read_tail_lines function from mig.shared.fileio module"""
447+
448+ def setUp (self ):
449+ """Initialize test environment for read_tail_lines tests"""
450+ super (MigSharedFileio__read_tail_lines , self ).setUp ()
451+ self .tmp_path = temppath (DUMMY_FILE_READTAILLINES , self )
452+ # We generally need output dir to exist here
453+ os .makedirs (os .path .dirname (self .tmp_path ), exist_ok = True )
454+ cleanpath (os .path .dirname (self .tmp_path ), self )
455+
456+ def test_reads_requested_lines (self ):
457+ """Test read_tail_lines returns requested number of lines"""
458+ with open (self .tmp_path , 'w' ) as fh :
459+ fh .write ("line1\n line2\n line3\n line4" )
460+ lines = fileio .read_tail_lines (self .tmp_path , 2 , self .logger )
461+ self .assertEqual (lines , ["line3\n " , "line4" ])
462+
463+ def test_returns_all_lines_when_requested_more (self ):
464+ """Test read_tail_lines returns all lines when file has fewer"""
465+ with open (self .tmp_path , 'w' ) as fh :
466+ fh .write ("line1\n line2" )
467+ lines = fileio .read_tail_lines (self .tmp_path , 5 , self .logger )
468+ self .assertEqual (lines , ["line1\n " , "line2" ])
469+
470+ def test_returns_empty_list_for_empty_file (self ):
471+ """Test read_tail_lines returns empty for empty file"""
472+ open (self .tmp_path , 'w' ).close ()
473+ lines = fileio .read_tail_lines (self .tmp_path , 3 , self .logger )
474+ self .assertEqual (lines , [])
475+
476+ def test_empty_for_missing_file (self ):
477+ """Test read_tail_lines returns [] for missing file"""
478+ self .logger .forgive_errors ()
479+ lines = fileio .read_tail_lines ('missing.txt' , 3 , self .logger )
480+ self .assertEqual (lines , [])
481+
482+
483+ class MigSharedFileio__make_symlink (MigTestCase ):
484+ """Test the make_symlink function from mig.shared.fileio module"""
485+
486+ def setUp (self ):
487+ """Initialize test environment for make_symlink tests"""
488+ super (MigSharedFileio__make_symlink , self ).setUp ()
489+ self .tmp_link = temppath (DUMMY_FILE_MAKESYMLINKSRC , self )
490+ self .tmp_target = temppath (DUMMY_FILE_MAKESYMLINKDST , self )
491+ # We generally need output dir to exist here
492+ os .makedirs (os .path .dirname (self .tmp_target ), exist_ok = True )
493+ cleanpath (os .path .dirname (self .tmp_link ), self )
494+ cleanpath (os .path .dirname (self .tmp_target ), self )
495+ with open (self .tmp_target , 'w' ) as fh :
496+ fh .write ("test" )
497+
498+ def test_creates_symlink (self ):
499+ """Test make_symlink creates working symlink"""
500+ result = fileio .make_symlink (
501+ self .tmp_target , self .tmp_link , self .logger )
502+ self .assertTrue (result )
503+ self .assertTrue (os .path .islink (self .tmp_link ))
504+ self .assertEqual (os .readlink (self .tmp_link ), self .tmp_target )
505+
506+ def test_force_overwrites_existing_link (self ):
507+ """Test make_symlink force replaces existing link"""
508+ os .symlink ('/dummy' , self .tmp_link )
509+ result = fileio .make_symlink (self .tmp_target , self .tmp_link , self .logger ,
510+ force = True )
511+ self .assertTrue (result )
512+ self .assertEqual (os .readlink (self .tmp_link ), self .tmp_target )
513+
514+ def test_fails_on_existing_link_without_force (self ):
515+ """Test make_symlink fails on existing link without force"""
516+ self .logger .forgive_errors ()
517+ os .symlink ('/dummy' , self .tmp_link )
518+ result = fileio .make_symlink (self .tmp_target , self .tmp_link , self .logger ,
519+ force = False )
520+ self .assertFalse (result )
521+
522+ def test_handles_nonexistent_target (self ):
523+ """Test make_symlink still creates broken symlink"""
524+ self .logger .forgive_errors ()
525+ broken_target = self .tmp_target + '-nonexistent'
526+ result = fileio .make_symlink (broken_target , self .tmp_link , self .logger )
527+ self .assertTrue (result )
528+ self .assertTrue (os .path .islink (self .tmp_link ))
529+ self .assertEqual (os .readlink (self .tmp_link ), broken_target )
530+
531+
249532if __name__ == '__main__' :
250533 testmain ()
0 commit comments