1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
|
"""Tests for editing files and contacts in an external editor"""
import datetime
import unittest
from contextlib import contextmanager
from unittest import mock
from khard.helpers.interactive import Editor, EditState
from .helpers import mock_stream
class EditFiles(unittest.TestCase):
t1 = datetime.datetime(2021, 1, 1, 12, 21, 42)
t2 = datetime.datetime(2021, 1, 1, 12, 21, 43)
editor = Editor("edit", "merge")
@staticmethod
@contextmanager
def _mock_popen(returncode=0):
"""Mock the subprocess.Popen class, set the returncode attribute of the
child process object."""
child_process = mock.Mock()
child_process.returncode = returncode
Popen = mock.Mock(return_value=child_process)
with mock.patch("subprocess.Popen", Popen) as popen:
yield popen
@staticmethod
def _edit_files(write="changed"):
"""Mock function for khar.helpers.interactive.Editor.edit_files
Create a function that will write the specified text to all files
passed as arguments.
"""
def edit_files(self, *files):
for f in files:
with open(f, "w") as fp:
fp.write(write)
return edit_files
def test_calls_subprocess_popen_with_editor_for_one_args(self):
with self._mock_popen() as popen:
with mock.patch(
"khard.helpers.interactive.Editor._mtime",
mock.Mock(return_value=self.t1),
):
self.editor.edit_files("file")
popen.assert_called_with(["edit", "file"])
def test_calls_subprocess_popen_with_merge_editor_for_two_args(self):
with self._mock_popen() as popen:
with mock.patch(
"khard.helpers.interactive.Editor._mtime",
mock.Mock(return_value=self.t1),
):
self.editor.edit_files("file1", "file2")
popen.assert_called_with(["merge", "file1", "file2"])
def test_failing_external_command_returns_aborted_state(self):
with self._mock_popen(1):
with mock.patch(
"khard.helpers.interactive.Editor._mtime",
mock.Mock(return_value=self.t1),
):
actual = self.editor.edit_files("file")
self.assertEqual(actual, EditState.aborted)
def test_returns_state_modiefied_if_timestamp_does_change(self):
with self._mock_popen():
with mock.patch(
"khard.helpers.interactive.Editor._mtime",
mock.Mock(side_effect=[self.t1, self.t2]),
):
actual = self.editor.edit_files("file")
self.assertEqual(actual, EditState.modified)
def test_returns_state_unmodiefied_if_timestamp_does_not_change(self):
with self._mock_popen():
with mock.patch(
"khard.helpers.interactive.Editor._mtime",
mock.Mock(side_effect=[self.t1, self.t1]),
):
actual = self.editor.edit_files("file")
self.assertEqual(actual, EditState.unmodified)
def test_editing_templates(self):
t1 = "some: yaml\ndocument: true\n"
with mock.patch(
"khard.helpers.interactive.Editor.edit_files", self._edit_files()
):
actual = self.editor.edit_templates(lambda x: x, t1)
self.assertEqual(actual, "changed")
def test_exception_from_yaml_conversion_is_caught(self):
t1 = "key: value\n"
with mock.patch(
"khard.helpers.interactive.Editor.edit_files", self._edit_files()
):
with mock.patch(
"khard.helpers.interactive.confirm", mock.Mock(return_value=False)
) as confirm:
with mock_stream(): # hide stdout in test
actual = self.editor.edit_templates(
mock.Mock(side_effect=ValueError), t1
)
self.assertIsNone(actual)
confirm.assert_called_once()
|