Skip to content

Commit 676f3a6

Browse files
committed
Adds basic unit tests for:
1. `read_file` 2. `read_file_lines` 3. `get_file_size` 4. `delete_file` 5. `read_head_lines` 6. `read_tail_lines` 7. `make_symlink` reusing the existing structure. Includes a TODO to fix get_file_size to actually return result value on exception as it currently doesn't and therefore effectively returns None.
1 parent 065b656 commit 676f3a6

File tree

1 file changed

+283
-0
lines changed

1 file changed

+283
-0
lines changed

tests/test_mig_shared_fileio.py

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,18 @@
4646
DUMMY_UNICODE_LENGTH = len(DUMMY_UNICODE)
4747
DUMMY_FILE_WRITECHUNK = 'fileio/write_chunk'
4848
DUMMY_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

5062
assert 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\nline2\nline3")
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\nline2\nline3\nline4")
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\nline2")
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\nline2\nline3\nline4")
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\nline2")
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+
249532
if __name__ == '__main__':
250533
testmain()

0 commit comments

Comments
 (0)