diff --git a/tests/test_not_configured.py b/tests/test_not_configured.py index fdae8d5b..9ae13ba5 100644 --- a/tests/test_not_configured.py +++ b/tests/test_not_configured.py @@ -1,4 +1,6 @@ import pytest +import json +from six import StringIO from mock import MagicMock from thefuck.shells.generic import ShellConfiguration from thefuck.not_configured import main @@ -11,19 +13,33 @@ def usage_tracker(mocker): new_callable=MagicMock) -def _assert_tracker_updated(usage_tracker, pid): +@pytest.fixture(autouse=True) +def usage_tracker_io(usage_tracker): + io = StringIO() usage_tracker.return_value \ - .open.return_value \ - .__enter__.return_value \ - .write.assert_called_once_with(str(pid)) + .open.return_value \ + .__enter__.return_value = io + return io -def _change_tracker(usage_tracker, pid): - usage_tracker.return_value.exists.return_value = True +@pytest.fixture(autouse=True) +def usage_tracker_exists(usage_tracker): usage_tracker.return_value \ - .open.return_value \ - .__enter__.return_value \ - .read.return_value = str(pid) + .exists.return_value = True + return usage_tracker.return_value.exists + + +def _assert_tracker_updated(usage_tracker_io, pid): + usage_tracker_io.seek(0) + info = json.load(usage_tracker_io) + assert info['pid'] == pid + + +def _change_tracker(usage_tracker_io, pid): + usage_tracker_io.truncate(0) + info = {'pid': pid, 'time': 0} + json.dump(info, usage_tracker_io) + usage_tracker_io.seek(0) @pytest.fixture(autouse=True) @@ -67,29 +83,28 @@ def test_for_generic_shell(shell, logs): logs.how_to_configure_alias.assert_called_once() -def test_on_first_run(usage_tracker, shell_pid, logs): +def test_on_first_run(usage_tracker_io, usage_tracker_exists, shell_pid, logs): shell_pid.return_value = 12 - usage_tracker.return_value.exists.return_value = False main() - _assert_tracker_updated(usage_tracker, 12) + usage_tracker_exists.return_value = False + _assert_tracker_updated(usage_tracker_io, 12) logs.how_to_configure_alias.assert_called_once() -def test_on_run_after_other_commands(usage_tracker, shell_pid, shell, logs): +def test_on_run_after_other_commands(usage_tracker_io, shell_pid, shell, logs): shell_pid.return_value = 12 shell.get_history.return_value = ['fuck', 'ls'] - _change_tracker(usage_tracker, 12) + _change_tracker(usage_tracker_io, 12) main() logs.how_to_configure_alias.assert_called_once() -def test_on_first_run_from_current_shell(usage_tracker, shell_pid, +def test_on_first_run_from_current_shell(usage_tracker_io, shell_pid, shell, logs): shell.get_history.return_value = ['fuck'] shell_pid.return_value = 12 - _change_tracker(usage_tracker, 55) main() - _assert_tracker_updated(usage_tracker, 12) + _assert_tracker_updated(usage_tracker_io, 12) logs.how_to_configure_alias.assert_called_once() @@ -104,21 +119,21 @@ def test_when_cant_configure_automatically(shell_pid, shell, logs): logs.how_to_configure_alias.assert_called_once() -def test_when_already_configured(usage_tracker, shell_pid, +def test_when_already_configured(usage_tracker_io, shell_pid, shell, shell_config, logs): shell.get_history.return_value = ['fuck'] shell_pid.return_value = 12 - _change_tracker(usage_tracker, 12) + _change_tracker(usage_tracker_io, 12) shell_config.read.return_value = 'eval $(thefuck --alias)' main() logs.already_configured.assert_called_once() -def test_when_successfuly_configured(usage_tracker, shell_pid, - shell, shell_config, logs): +def test_when_successfully_configured(usage_tracker_io, shell_pid, + shell, shell_config, logs): shell.get_history.return_value = ['fuck'] shell_pid.return_value = 12 - _change_tracker(usage_tracker, 12) + _change_tracker(usage_tracker_io, 12) shell_config.read.return_value = '' main() shell_config.write.assert_any_call('eval $(thefuck --alias)') diff --git a/thefuck/const.py b/thefuck/const.py index 7c800057..9530a428 100644 --- a/thefuck/const.py +++ b/thefuck/const.py @@ -63,3 +63,5 @@ SETTINGS_HEADER = u"""# The Fuck settings file """ ARGUMENT_PLACEHOLDER = 'THEFUCK_ARGUMENT_PLACEHOLDER' + +CONFIGURATION_TIMEOUT = 60 diff --git a/thefuck/not_configured.py b/thefuck/not_configured.py index af6f418d..8ff5fe26 100644 --- a/thefuck/not_configured.py +++ b/thefuck/not_configured.py @@ -4,9 +4,11 @@ from .system import init_output init_output() import os # noqa: E402 -from psutil import Process # noqa: E402 +import json # noqa: E402 +import time # noqa: E402 import six # noqa: E402 -from . import logs # noqa: E402 +from psutil import Process # noqa: E402 +from . import logs, const # noqa: E402 from .shells import shell # noqa: E402 from .conf import settings # noqa: E402 from .system import Path # noqa: E402 @@ -30,19 +32,32 @@ def _get_not_configured_usage_tracker_path(): def _record_first_run(): """Records shell pid to tracker file.""" - with _get_not_configured_usage_tracker_path().open('w') as tracker: - tracker.write(six.text_type(_get_shell_pid())) + info = {'pid': _get_shell_pid(), + 'time': time.time()} + + mode = 'wb' if six.PY2 else 'w' + with _get_not_configured_usage_tracker_path().open(mode) as tracker: + json.dump(info, tracker) def _is_second_run(): """Returns `True` when we know that `fuck` called second time.""" tracker_path = _get_not_configured_usage_tracker_path() - if not tracker_path.exists() or not shell.get_history()[-1] == 'fuck': + if not tracker_path.exists(): return False current_pid = _get_shell_pid() with tracker_path.open('r') as tracker: - return tracker.read() == six.text_type(current_pid) + try: + info = json.load(tracker) + except ValueError: + return False + + if not (isinstance(info, dict) and info.get('pid') == current_pid): + return False + + return (shell.get_history()[-1] == 'fuck' or + time.time() - info.get('time', 0) < const.CONFIGURATION_TIMEOUT) def _is_already_configured(configuration_details):