Skip to content

Commit

Permalink
fix: utils tests on macOS
Browse files Browse the repository at this point in the history
test_utils tests were failing on macOS when the settings file was properly
defined and present.

Close #560.
  • Loading branch information
regisb committed Jan 8, 2022
1 parent 37be2bf commit 43d5da8
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 25 deletions.
60 changes: 55 additions & 5 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import tempfile
import unittest
from io import StringIO
from unittest.mock import MagicMock, patch
from unittest.mock import MagicMock, mock_open, patch

from tutor import exceptions, utils

Expand Down Expand Up @@ -152,9 +152,59 @@ def test_execute_keyboard_interrupt(
process.kill.assert_called_once()

@patch("sys.platform", "win32")
def test_check_macos_memory_win32_should_skip(self) -> None:
utils.check_macos_memory()
def test_check_macos_docker_memory_win32_should_skip(self) -> None:
utils.check_macos_docker_memory()

@patch("sys.platform", "darwin")
def test_check_macos_memory_darwin_filenotfound(self) -> None:
self.assertRaises(exceptions.TutorError, utils.check_macos_memory)
def test_check_macos_docker_memory_darwin(self) -> None:
with patch("tutor.utils.open", mock_open(read_data='{"memoryMiB": 4096}')):
utils.check_macos_docker_memory()

@patch("sys.platform", "darwin")
def test_check_macos_docker_memory_darwin_filenotfound(self) -> None:
with patch("tutor.utils.open", mock_open()) as mock_open_settings:
mock_open_settings.return_value.__enter__.side_effect = FileNotFoundError
with self.assertRaises(exceptions.TutorError) as e:
utils.check_macos_docker_memory()
self.assertIn("Error accessing Docker settings file", e.exception.args[0])

@patch("sys.platform", "darwin")
def test_check_macos_docker_memory_darwin_json_decode_error(self) -> None:
with patch("tutor.utils.open", mock_open(read_data="invalid")):
with self.assertRaises(exceptions.TutorError) as e:
utils.check_macos_docker_memory()
self.assertIn("invalid JSON", e.exception.args[0])

@patch("sys.platform", "darwin")
def test_check_macos_docker_memory_darwin_key_error(self) -> None:
with patch("tutor.utils.open", mock_open(read_data="{}")):
with self.assertRaises(exceptions.TutorError) as e:
utils.check_macos_docker_memory()
self.assertIn("key 'memoryMiB' not found", e.exception.args[0])

@patch("sys.platform", "darwin")
def test_check_macos_docker_memory_darwin_type_error(self) -> None:
with patch(
"tutor.utils.open", mock_open(read_data='{"memoryMiB": "invalidstring"}')
):
with self.assertRaises(exceptions.TutorError) as e:
utils.check_macos_docker_memory()
self.assertIn("Unexpected JSON data", e.exception.args[0])

@patch("sys.platform", "darwin")
def test_check_macos_docker_memory_darwin_insufficient_memory(self) -> None:
with patch("tutor.utils.open", mock_open(read_data='{"memoryMiB": 4095}')):
with self.assertRaises(exceptions.TutorError) as e:
utils.check_macos_docker_memory()
self.assertEqual(
"Docker is configured to allocate 4095 MiB RAM, less than the recommended 4096 MiB",
e.exception.args[0],
)

@patch("sys.platform", "darwin")
def test_check_macos_docker_memory_darwin_encoding_error(self) -> None:
with patch("tutor.utils.open", mock_open()) as mock_open_settings:
mock_open_settings.return_value.__enter__.side_effect = TypeError
with self.assertRaises(exceptions.TutorError) as e:
utils.check_macos_docker_memory()
self.assertIn("Text encoding error", e.exception.args[0])
12 changes: 5 additions & 7 deletions tutor/commands/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,14 @@ def local(context: click.Context) -> None:
@click.pass_context
def quickstart(context: click.Context, non_interactive: bool, pullimages: bool) -> None:
try:
utils.check_macos_memory()
utils.check_macos_docker_memory()
except exceptions.TutorError as e:
fmt.echo_alert(
"""Could not verify sufficient RAM allocation in Docker:
{}
Tutor may not work if Docker is configured with < 4 GB RAM. Please follow instructions from:
f"""Could not verify sufficient RAM allocation in Docker:
{e}
Tutor may not work if Docker is configured with < 4 GB RAM. Please follow the instructions from:
https://docs.tutor.overhang.io/install.html
""".format(
str(e)
)
"""
)

if tutor_env.needs_major_upgrade(context.obj.root):
Expand Down
27 changes: 14 additions & 13 deletions tutor/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,12 @@ def check_output(*command: str) -> bytes:
) from e


def check_macos_memory() -> None:
def check_macos_docker_memory() -> None:
"""
Try to check that the RAM allocated to the Docker VM on macOS is at least 4 GB.
Parse macOS Docker settings file from user directory and return the max
allocated memory. Will raise TutorError in case of parsing/loading error.
"""
if sys.platform != "darwin":
return
Expand All @@ -238,27 +241,25 @@ def check_macos_memory() -> None:
data = json.load(fp)
memory_mib = int(data["memoryMiB"])
except OSError as e:
raise exceptions.TutorError(
"Error accessing {}: [{}] {}".format(settings_path, e.errno, e.strerror)
) from e
raise exceptions.TutorError(f"Error accessing Docker settings file: {e}") from e
except json.JSONDecodeError as e:
raise exceptions.TutorError(
"Error reading {}: invalid JSON: {} [{}:{}]".format(
settings_path, e.msg, e.lineno, e.colno
)
f"Error reading {settings_path}, invalid JSON: {e}"
) from e
except (ValueError, TypeError, OverflowError) as e:
# ValueError from open() indicates an encoding error
except ValueError as e:
raise exceptions.TutorError(
"Text encoding error or unexpected JSON data: in {}: {}".format(
settings_path, str(e)
)
f"Unexpected JSON data in {settings_path}: {e}"
) from e
except KeyError as e:
# Value is absent (Docker creates the file with the default setting of 2048 explicitly
# written in, so we shouldn't need to assume a default value here.)
raise exceptions.TutorError(
"key 'memoryMiB' not found in {}".format(settings_path)
f"key 'memoryMiB' not found in {settings_path}"
) from e
except (TypeError, OverflowError) as e:
# TypeError from open() indicates an encoding error
raise exceptions.TutorError(
f"Text encoding error in {settings_path}: {e}"
) from e

if memory_mib < 4096:
Expand Down

0 comments on commit 43d5da8

Please sign in to comment.