mirror of
https://github.com/GAM-team/GAM.git
synced 2025-07-10 22:53:34 +00:00
Refactor into Python package format (#1165)
* Refactor into a python module format -Updates import statements to be absolute vs implicitly relative -Uses import syntax that minimizes the need to update references in code and/or reformat affected lines (e.g. `import gapi.directory` becomes `from gam.gapi import directory as gapi_directory`) -Adds a `__main__.py` such that the module can be executed on its own using standard `python3 -m gam` syntax -Replaces __main__ import hack with module import -Updates the GAM path to be the module's parent dir * Add gam.py to /src for backwards compatibility A stub that calls gam.__main__.main() to be used by users who are not with the syntax of calling a module implementation. It should also provide immediate backwards-compatibility with existing scripts with references to this file. * Move build tools back to the main dir and out of the package * Fix pylint errors * Update build spec to use new package format Incorporates @jay0lee's patch from https://github.com/jay0lee/GAM/pull/1165#issuecomment-618430828
This commit is contained in:
234
src/gam/fileutils_test.py
Normal file
234
src/gam/fileutils_test.py
Normal file
@ -0,0 +1,234 @@
|
||||
"""Tests for fileutils."""
|
||||
|
||||
import io
|
||||
import os
|
||||
import unittest
|
||||
from unittest.mock import MagicMock
|
||||
from unittest.mock import patch
|
||||
|
||||
from gam import fileutils
|
||||
|
||||
|
||||
class FileutilsTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.fake_path = '/some/path/to/file'
|
||||
super(FileutilsTest, self).setUp()
|
||||
|
||||
@patch.object(fileutils.sys, 'stdin')
|
||||
def test_open_file_stdin(self, mock_stdin):
|
||||
mock_stdin.read.return_value = 'some stdin content'
|
||||
f = fileutils.open_file('-', mode='r')
|
||||
self.assertIsInstance(f, fileutils.io.StringIO)
|
||||
self.assertEqual(f.getvalue(), mock_stdin.read.return_value)
|
||||
|
||||
def test_open_file_stdout(self):
|
||||
f = fileutils.open_file('-', mode='w')
|
||||
self.assertEqual(fileutils.sys.stdout, f)
|
||||
|
||||
@patch.object(fileutils, 'open', new_callable=unittest.mock.mock_open)
|
||||
def test_open_file_opens_correct_path(self, mock_open):
|
||||
f = fileutils.open_file(self.fake_path)
|
||||
self.assertEqual(self.fake_path, mock_open.call_args[0][0])
|
||||
self.assertEqual(mock_open.return_value, f)
|
||||
|
||||
@patch.object(fileutils, 'open', new_callable=unittest.mock.mock_open)
|
||||
def test_open_file_expands_user_file_path(self, mock_open):
|
||||
file_path = '~/some/path/containing/tilde/shortcut/to/home'
|
||||
fileutils.open_file(file_path)
|
||||
opened_path = mock_open.call_args[0][0]
|
||||
home_path = os.environ.get('HOME')
|
||||
self.assertIsNotNone(home_path)
|
||||
self.assertIn(home_path, opened_path)
|
||||
|
||||
@patch.object(fileutils, 'open', new_callable=unittest.mock.mock_open)
|
||||
def test_open_file_opens_correct_mode(self, mock_open):
|
||||
fileutils.open_file(self.fake_path)
|
||||
self.assertEqual('r', mock_open.call_args[0][1])
|
||||
|
||||
@patch.object(fileutils, 'open', new_callable=unittest.mock.mock_open)
|
||||
def test_open_file_encoding_for_binary(self, mock_open):
|
||||
fileutils.open_file(self.fake_path, mode='b')
|
||||
self.assertIsNone(mock_open.call_args[1]['encoding'])
|
||||
|
||||
@patch.object(fileutils, 'open', new_callable=unittest.mock.mock_open)
|
||||
def test_open_file_default_system_encoding(self, mock_open):
|
||||
fileutils.open_file(self.fake_path)
|
||||
self.assertEqual(fileutils.GM_Globals[fileutils.GM_SYS_ENCODING],
|
||||
mock_open.call_args[1]['encoding'])
|
||||
|
||||
@patch.object(fileutils, 'open', new_callable=unittest.mock.mock_open)
|
||||
def test_open_file_utf8_encoding_specified(self, mock_open):
|
||||
fileutils.open_file(self.fake_path, encoding='UTF-8')
|
||||
self.assertEqual(fileutils.UTF8_SIG, mock_open.call_args[1]['encoding'])
|
||||
|
||||
def test_open_file_strips_utf_bom_in_utf(self):
|
||||
bom_prefixed_data = u'\ufefffoobar'
|
||||
fake_file = io.StringIO(bom_prefixed_data)
|
||||
mock_open = MagicMock(spec=open, return_value=fake_file)
|
||||
with patch.object(fileutils, 'open', mock_open):
|
||||
f = fileutils.open_file(self.fake_path, strip_utf_bom=True)
|
||||
self.assertEqual('foobar', f.read())
|
||||
|
||||
def test_open_file_strips_utf_bom_in_non_utf(self):
|
||||
bom_prefixed_data = b'\xef\xbb\xbffoobar'.decode('iso-8859-1')
|
||||
|
||||
# We need to trick the method under test into believing that a StringIO
|
||||
# instance is a file with an encoding. Since StringIO does not usually have,
|
||||
# an encoding, we'll mock it and add our own encoding, but send the other
|
||||
# methods in use (read and seek) back to the real StringIO object.
|
||||
real_stringio = io.StringIO(bom_prefixed_data)
|
||||
mock_file = MagicMock(spec=io.StringIO)
|
||||
mock_file.read.side_effect = real_stringio.read
|
||||
mock_file.seek.side_effect = real_stringio.seek
|
||||
mock_file.encoding = 'iso-8859-1'
|
||||
|
||||
mock_open = MagicMock(spec=open, return_value=mock_file)
|
||||
with patch.object(fileutils, 'open', mock_open):
|
||||
f = fileutils.open_file(self.fake_path, strip_utf_bom=True)
|
||||
self.assertEqual('foobar', f.read())
|
||||
|
||||
def test_open_file_strips_utf_bom_in_binary(self):
|
||||
bom_prefixed_data = u'\ufefffoobar'.encode('UTF-8')
|
||||
fake_file = io.BytesIO(bom_prefixed_data)
|
||||
mock_open = MagicMock(spec=open, return_value=fake_file)
|
||||
with patch.object(fileutils, 'open', mock_open):
|
||||
f = fileutils.open_file(self.fake_path, mode='rb', strip_utf_bom=True)
|
||||
self.assertEqual(b'foobar', f.read())
|
||||
|
||||
def test_open_file_strip_utf_bom_when_no_bom_in_data(self):
|
||||
no_bom_data = 'This data has no BOM'
|
||||
fake_file = io.StringIO(no_bom_data)
|
||||
mock_open = MagicMock(spec=open, return_value=fake_file)
|
||||
|
||||
with patch.object(fileutils, 'open', mock_open):
|
||||
f = fileutils.open_file(self.fake_path, strip_utf_bom=True)
|
||||
# Since there was no opening BOM, we should be back at the beginning of
|
||||
# the file.
|
||||
self.assertEqual(fake_file.tell(), 0)
|
||||
self.assertEqual(f.read(), no_bom_data)
|
||||
|
||||
@patch.object(fileutils, 'open', new_callable=unittest.mock.mock_open)
|
||||
def test_open_file_exits_on_io_error(self, mock_open):
|
||||
mock_open.side_effect = IOError('Fake IOError')
|
||||
with self.assertRaises(SystemExit) as context:
|
||||
fileutils.open_file(self.fake_path)
|
||||
self.assertEqual(context.exception.code, 6)
|
||||
|
||||
def test_close_file_closes_file_successfully(self):
|
||||
mock_file = MagicMock()
|
||||
self.assertTrue(fileutils.close_file(mock_file))
|
||||
self.assertEqual(mock_file.close.call_count, 1)
|
||||
|
||||
def test_close_file_with_error(self):
|
||||
mock_file = MagicMock()
|
||||
mock_file.close.side_effect = IOError()
|
||||
self.assertFalse(fileutils.close_file(mock_file))
|
||||
self.assertEqual(mock_file.close.call_count, 1)
|
||||
|
||||
@patch.object(fileutils.sys, 'stdin')
|
||||
def test_read_file_from_stdin(self, mock_stdin):
|
||||
mock_stdin.read.return_value = 'some stdin content'
|
||||
self.assertEqual(fileutils.read_file('-'), mock_stdin.read.return_value)
|
||||
|
||||
@patch.object(fileutils, '_open_file')
|
||||
def test_read_file_default_params(self, mock_open_file):
|
||||
fake_content = 'some fake content'
|
||||
mock_open_file.return_value.__enter__().read.return_value = fake_content
|
||||
self.assertEqual(fileutils.read_file(self.fake_path), fake_content)
|
||||
self.assertEqual(mock_open_file.call_args[0][0], self.fake_path)
|
||||
self.assertEqual(mock_open_file.call_args[0][1], 'r')
|
||||
self.assertIsNone(mock_open_file.call_args[1]['newline'])
|
||||
|
||||
@patch.object(fileutils.display, 'print_warning')
|
||||
@patch.object(fileutils, '_open_file')
|
||||
def test_read_file_continues_on_errors_without_displaying(
|
||||
self, mock_open_file, mock_print_warning):
|
||||
mock_open_file.side_effect = IOError()
|
||||
contents = fileutils.read_file(
|
||||
self.fake_path, continue_on_error=True, display_errors=False)
|
||||
self.assertIsNone(contents)
|
||||
self.assertFalse(mock_print_warning.called)
|
||||
|
||||
@patch.object(fileutils.display, 'print_warning')
|
||||
@patch.object(fileutils, '_open_file')
|
||||
def test_read_file_displays_errors(self, mock_open_file, mock_print_warning):
|
||||
mock_open_file.side_effect = IOError()
|
||||
fileutils.read_file(
|
||||
self.fake_path, continue_on_error=True, display_errors=True)
|
||||
self.assertTrue(mock_print_warning.called)
|
||||
|
||||
@patch.object(fileutils, '_open_file')
|
||||
def test_read_file_exits_code_6_when_continue_on_error_is_false(
|
||||
self, mock_open_file):
|
||||
mock_open_file.side_effect = IOError()
|
||||
with self.assertRaises(SystemExit) as context:
|
||||
fileutils.read_file(self.fake_path, continue_on_error=False)
|
||||
self.assertEqual(context.exception.code, 6)
|
||||
|
||||
@patch.object(fileutils, '_open_file')
|
||||
def test_read_file_exits_code_2_on_lookuperror(self, mock_open_file):
|
||||
mock_open_file.return_value.__enter__().read.side_effect = LookupError()
|
||||
with self.assertRaises(SystemExit) as context:
|
||||
fileutils.read_file(self.fake_path)
|
||||
self.assertEqual(context.exception.code, 2)
|
||||
|
||||
@patch.object(fileutils, '_open_file')
|
||||
def test_read_file_exits_code_2_on_unicodeerror(self, mock_open_file):
|
||||
mock_open_file.return_value.__enter__().read.side_effect = UnicodeError()
|
||||
with self.assertRaises(SystemExit) as context:
|
||||
fileutils.read_file(self.fake_path)
|
||||
self.assertEqual(context.exception.code, 2)
|
||||
|
||||
@patch.object(fileutils, '_open_file')
|
||||
def test_read_file_exits_code_2_on_unicodedecodeerror(self, mock_open_file):
|
||||
fake_decode_error = UnicodeDecodeError('fake-encoding', b'fakebytes', 0, 1,
|
||||
'testing only')
|
||||
mock_open_file.return_value.__enter__().read.side_effect = fake_decode_error
|
||||
with self.assertRaises(SystemExit) as context:
|
||||
fileutils.read_file(self.fake_path)
|
||||
self.assertEqual(context.exception.code, 2)
|
||||
|
||||
@patch.object(fileutils, '_open_file')
|
||||
def test_write_file_writes_data_to_file(self, mock_open_file):
|
||||
fake_data = 'some fake data'
|
||||
fileutils.write_file(self.fake_path, fake_data)
|
||||
self.assertEqual(mock_open_file.call_args[0][0], self.fake_path)
|
||||
self.assertEqual(mock_open_file.call_args[0][1], 'w')
|
||||
|
||||
opened_file = mock_open_file.return_value.__enter__()
|
||||
self.assertTrue(opened_file.write.called)
|
||||
self.assertEqual(opened_file.write.call_args[0][0], fake_data)
|
||||
|
||||
@patch.object(fileutils.display, 'print_error')
|
||||
@patch.object(fileutils, '_open_file')
|
||||
def test_write_file_continues_on_errors_without_displaying(
|
||||
self, mock_open_file, mock_print_error):
|
||||
mock_open_file.side_effect = IOError()
|
||||
status = fileutils.write_file(
|
||||
self.fake_path,
|
||||
'foo data',
|
||||
continue_on_error=True,
|
||||
display_errors=False)
|
||||
self.assertFalse(status)
|
||||
self.assertFalse(mock_print_error.called)
|
||||
|
||||
@patch.object(fileutils.display, 'print_error')
|
||||
@patch.object(fileutils, '_open_file')
|
||||
def test_write_file_displays_errors(self, mock_open_file, mock_print_error):
|
||||
mock_open_file.side_effect = IOError()
|
||||
fileutils.write_file(
|
||||
self.fake_path, 'foo data', continue_on_error=True, display_errors=True)
|
||||
self.assertTrue(mock_print_error.called)
|
||||
|
||||
@patch.object(fileutils, '_open_file')
|
||||
def test_write_file_exits_code_6_when_continue_on_error_is_false(
|
||||
self, mock_open_file):
|
||||
mock_open_file.side_effect = IOError()
|
||||
with self.assertRaises(SystemExit) as context:
|
||||
fileutils.write_file(self.fake_path, 'foo data', continue_on_error=False)
|
||||
self.assertEqual(context.exception.code, 6)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Reference in New Issue
Block a user