2021-05-18 20:39:25 +10:00
|
|
|
#!/usr/bin/env python3.9
|
2015-12-14 18:03:11 +00:00
|
|
|
|
|
|
|
|
|
2016-03-09 10:47:09 +00:00
|
|
|
import asyncio
|
2016-01-21 23:22:42 +00:00
|
|
|
import contextlib
|
|
|
|
|
import io
|
2015-12-14 18:03:11 +00:00
|
|
|
import os
|
|
|
|
|
import shutil
|
|
|
|
|
import tempfile
|
|
|
|
|
import unittest
|
|
|
|
|
|
2016-10-26 14:09:05 +02:00
|
|
|
os.environ["TERM"] = "xterm-256color"
|
2016-10-26 13:43:02 +02:00
|
|
|
|
2017-06-28 11:12:13 +01:00
|
|
|
import golden
|
2018-09-17 23:59:38 +10:00
|
|
|
import eris.fill3 as fill3
|
|
|
|
|
import eris.__main__ as __main__
|
2015-12-14 18:03:11 +00:00
|
|
|
|
|
|
|
|
|
2016-01-29 21:29:44 +00:00
|
|
|
_DIMENSIONS = (100, 60)
|
2015-12-14 18:03:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def _widget_to_string(widget, dimensions=_DIMENSIONS):
|
|
|
|
|
appearance = (widget.appearance_min() if dimensions is None
|
|
|
|
|
else widget.appearance(dimensions))
|
|
|
|
|
return str(fill3.join("\n", appearance))
|
|
|
|
|
|
|
|
|
|
|
2016-02-15 14:11:08 +00:00
|
|
|
def _touch(path):
|
2015-12-14 18:03:11 +00:00
|
|
|
open(path, "w").close()
|
|
|
|
|
|
|
|
|
|
|
2016-02-15 14:11:08 +00:00
|
|
|
def _assert_widget_appearance(widget, golden_path, dimensions=_DIMENSIONS):
|
2016-01-21 23:22:42 +00:00
|
|
|
golden_path_absolute = os.path.join(os.path.dirname(__file__), golden_path)
|
|
|
|
|
golden.assertGolden(_widget_to_string(widget, dimensions),
|
|
|
|
|
golden_path_absolute)
|
2015-12-14 18:03:11 +00:00
|
|
|
|
|
|
|
|
|
2016-02-15 14:11:08 +00:00
|
|
|
class _MockMainLoop:
|
2015-12-14 18:03:11 +00:00
|
|
|
|
|
|
|
|
def add_reader(self, foo, bar):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
2016-01-21 23:22:42 +00:00
|
|
|
class ScreenWidgetTestCase(unittest.TestCase):
|
2015-12-14 18:03:11 +00:00
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
self.temp_dir = tempfile.mkdtemp()
|
2020-03-28 23:48:21 +10:00
|
|
|
project_dir = os.path.join(self.temp_dir, "project")
|
|
|
|
|
os.mkdir(project_dir)
|
|
|
|
|
foo_path = os.path.join(project_dir, "foo.py")
|
2016-02-15 14:11:08 +00:00
|
|
|
_touch(foo_path)
|
2016-10-22 19:44:17 +02:00
|
|
|
jobs_added_event = asyncio.Event()
|
|
|
|
|
appearance_changed_event = asyncio.Event()
|
2020-03-28 23:48:21 +10:00
|
|
|
summary = __main__.Summary(project_dir, jobs_added_event)
|
2017-06-27 14:03:32 +01:00
|
|
|
log = __main__.Log(appearance_changed_event)
|
2019-12-04 18:39:22 +10:00
|
|
|
self.main_widget = __main__.Screen(
|
|
|
|
|
summary, log, appearance_changed_event, _MockMainLoop())
|
2015-12-14 18:03:11 +00:00
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
|
shutil.rmtree(self.temp_dir)
|
|
|
|
|
|
2020-03-28 23:48:21 +10:00
|
|
|
def test_initial_appearance(self):
|
|
|
|
|
_assert_widget_appearance(self.main_widget, "golden-files/initial")
|
2015-12-14 18:03:11 +00:00
|
|
|
|
|
|
|
|
def test_help_appearance(self):
|
|
|
|
|
self.main_widget.toggle_help()
|
2016-02-15 14:11:08 +00:00
|
|
|
_assert_widget_appearance(self.main_widget, "golden-files/help")
|
2015-12-14 18:03:11 +00:00
|
|
|
|
2020-03-28 23:48:21 +10:00
|
|
|
def test_log_appearance(self):
|
|
|
|
|
log_shown = _widget_to_string(self.main_widget)
|
|
|
|
|
self.main_widget.toggle_log()
|
|
|
|
|
log_hidden = _widget_to_string(self.main_widget)
|
|
|
|
|
actual = "shown:\n%s\nhidden:\n%s" % (log_shown, log_hidden)
|
|
|
|
|
_assert_widget_appearance(self.main_widget, "golden-files/log")
|
2015-12-14 18:03:11 +00:00
|
|
|
|
2020-03-28 23:48:21 +10:00
|
|
|
def test_window_orientation(self):
|
|
|
|
|
window_left_right = _widget_to_string(self.main_widget)
|
|
|
|
|
self.main_widget.toggle_window_orientation()
|
|
|
|
|
window_top_bottom = _widget_to_string(self.main_widget)
|
|
|
|
|
actual = ("left-right:\n%s\ntop-bottom:\n%s" %
|
|
|
|
|
(window_left_right, window_top_bottom))
|
|
|
|
|
_assert_widget_appearance(self.main_widget,
|
|
|
|
|
"golden-files/window-orientation")
|
2015-12-14 18:03:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class SummaryCursorTest(unittest.TestCase):
|
|
|
|
|
|
|
|
|
|
def setUp(self):
|
2017-06-27 14:03:32 +01:00
|
|
|
self.original_method = __main__.Summary.sync_with_filesystem
|
|
|
|
|
__main__.Summary.sync_with_filesystem = lambda foo: None
|
|
|
|
|
self.summary = __main__.Summary(None, None)
|
2020-03-28 13:37:07 +10:00
|
|
|
self.summary._entries = [[1, 1, 1], [1, 1], [1, 1, 1]]
|
2015-12-14 18:03:11 +00:00
|
|
|
|
|
|
|
|
def tearDown(self):
|
2017-06-27 14:03:32 +01:00
|
|
|
__main__.Summary.sync_with_filesystem = self.original_method
|
2015-12-14 18:03:11 +00:00
|
|
|
|
|
|
|
|
def _assert_movements(self, movements):
|
|
|
|
|
for movement, expected_position in movements:
|
|
|
|
|
movement()
|
|
|
|
|
self.assertEqual(self.summary.cursor_position(), expected_position)
|
|
|
|
|
|
|
|
|
|
def test_cursor_movement(self):
|
|
|
|
|
self.assertEqual(self.summary.cursor_position(), (0, 0))
|
|
|
|
|
self._assert_movements([(self.summary.cursor_right, (1, 0)),
|
|
|
|
|
(self.summary.cursor_down, (1, 1)),
|
|
|
|
|
(self.summary.cursor_left, (0, 1)),
|
|
|
|
|
(self.summary.cursor_up, (0, 0))])
|
|
|
|
|
|
|
|
|
|
def test_cursor_wrapping(self):
|
|
|
|
|
self._assert_movements([(self.summary.cursor_up, (0, 2)),
|
|
|
|
|
(self.summary.cursor_down, (0, 0)),
|
|
|
|
|
(self.summary.cursor_left, (2, 0)),
|
|
|
|
|
(self.summary.cursor_right, (0, 0))])
|
|
|
|
|
|
|
|
|
|
def test_cursor_moving_between_different_sized_rows(self):
|
|
|
|
|
self.summary._cursor_position = (2, 0)
|
|
|
|
|
self._assert_movements([(self.summary.cursor_down, (1, 1)),
|
|
|
|
|
(self.summary.cursor_down, (2, 2))])
|
|
|
|
|
|
|
|
|
|
|
2020-03-09 14:31:01 +10:00
|
|
|
class SummarySyncWithFilesystemTestCase(unittest.TestCase):
|
2015-12-14 18:03:11 +00:00
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
self.temp_dir = tempfile.mkdtemp()
|
|
|
|
|
self.foo_path = os.path.join(self.temp_dir, "foo")
|
2020-03-09 14:31:01 +10:00
|
|
|
self.bar_path = os.path.join(self.temp_dir, "bar.md")
|
|
|
|
|
self.zoo_path = os.path.join(self.temp_dir, "zoo.html")
|
2016-10-22 19:44:17 +02:00
|
|
|
self.jobs_added_event = asyncio.Event()
|
|
|
|
|
self.appearance_changed_event = asyncio.Event()
|
2017-06-27 14:03:32 +01:00
|
|
|
self.summary = __main__.Summary(self.temp_dir, self.jobs_added_event)
|
2020-03-09 14:31:01 +10:00
|
|
|
self.loop = asyncio.new_event_loop()
|
|
|
|
|
callback = lambda event: __main__.on_filesystem_event(
|
|
|
|
|
event, self.summary, self.temp_dir, self.appearance_changed_event)
|
|
|
|
|
__main__.setup_inotify(self.temp_dir, self.loop, callback,
|
|
|
|
|
__main__.is_path_excluded)
|
2020-03-28 13:37:07 +10:00
|
|
|
_touch(self.foo_path)
|
|
|
|
|
_touch(self.bar_path)
|
|
|
|
|
self.log = __main__.Log(self.appearance_changed_event)
|
2020-10-12 22:45:34 +10:00
|
|
|
self.loop.run_until_complete(self.summary.sync_with_filesystem(
|
|
|
|
|
self.appearance_changed_event, self.log))
|
2020-03-28 13:37:07 +10:00
|
|
|
self.jobs_added_event.clear()
|
2015-12-14 18:03:11 +00:00
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
|
shutil.rmtree(self.temp_dir)
|
|
|
|
|
|
|
|
|
|
def _assert_paths(self, expected_paths):
|
2020-03-28 13:37:07 +10:00
|
|
|
actual_paths = [entry[0].path for entry in self.summary._entries]
|
2020-03-09 14:31:01 +10:00
|
|
|
self.assertEqual(set(actual_paths), set(expected_paths))
|
2015-12-14 18:03:11 +00:00
|
|
|
|
2020-03-28 13:37:07 +10:00
|
|
|
def _assert_summary_invariants(self):
|
|
|
|
|
completed_total = 0
|
|
|
|
|
result_total = 0
|
|
|
|
|
for row in self.summary._entries:
|
|
|
|
|
for result in row:
|
|
|
|
|
if result.is_completed:
|
|
|
|
|
completed_total += 1
|
|
|
|
|
result_total += 1
|
|
|
|
|
self.assertEqual(self.summary.completed_total, completed_total)
|
|
|
|
|
self.assertEqual(self.summary.result_total, result_total)
|
|
|
|
|
max_width = max((len(row) for row in self.summary._entries), default=0)
|
2020-04-09 11:08:39 +10:00
|
|
|
self.assertEqual(__main__.Entry.MAX_WIDTH, max_width)
|
2020-03-28 13:37:07 +10:00
|
|
|
max_path_length = max(
|
|
|
|
|
(len(row.path) - 2 for row in self.summary._entries), default=0)
|
|
|
|
|
self.assertEqual(self.summary._max_path_length, max_path_length)
|
|
|
|
|
|
2015-12-14 18:03:11 +00:00
|
|
|
def test_summary_initial_state(self):
|
2020-03-28 13:37:07 +10:00
|
|
|
self._assert_summary_invariants()
|
2020-03-09 14:31:01 +10:00
|
|
|
self._assert_paths(["./bar.md", "./foo"])
|
2016-10-22 19:44:17 +02:00
|
|
|
self.assertFalse(self.jobs_added_event.is_set())
|
2015-12-14 18:03:11 +00:00
|
|
|
|
|
|
|
|
def test_sync_removed_file(self):
|
2020-03-09 14:31:01 +10:00
|
|
|
async def foo():
|
|
|
|
|
os.remove(self.bar_path)
|
|
|
|
|
self.loop.run_until_complete(foo())
|
|
|
|
|
self._assert_paths(["./foo"])
|
2020-03-28 13:37:07 +10:00
|
|
|
self._assert_summary_invariants()
|
2016-10-22 19:44:17 +02:00
|
|
|
self.assertFalse(self.jobs_added_event.is_set())
|
2015-12-14 18:03:11 +00:00
|
|
|
|
|
|
|
|
def test_sync_added_file(self):
|
2020-03-09 14:31:01 +10:00
|
|
|
async def foo():
|
|
|
|
|
_touch(self.zoo_path)
|
|
|
|
|
self.loop.run_until_complete(foo())
|
|
|
|
|
self._assert_paths(["./bar.md", "./foo", "./zoo.html"])
|
2020-03-28 13:37:07 +10:00
|
|
|
self._assert_summary_invariants()
|
2016-10-22 19:44:17 +02:00
|
|
|
self.assertTrue(self.jobs_added_event.is_set())
|
2015-12-14 18:03:11 +00:00
|
|
|
|
|
|
|
|
def test_sync_linked_files(self):
|
2019-12-04 18:39:22 +10:00
|
|
|
"""Symbolic and hard-linked files are given distinct entry objects."""
|
2015-12-14 18:03:11 +00:00
|
|
|
baz_path = os.path.join(self.temp_dir, "baz")
|
|
|
|
|
os.symlink(self.foo_path, baz_path)
|
|
|
|
|
os.link(self.foo_path, self.zoo_path)
|
2020-03-28 13:37:07 +10:00
|
|
|
log = __main__.Log(self.appearance_changed_event)
|
2020-10-12 22:45:34 +10:00
|
|
|
self.loop.run_until_complete(self.summary.sync_with_filesystem(
|
|
|
|
|
self.appearance_changed_event, log))
|
2020-03-09 14:31:01 +10:00
|
|
|
self._assert_paths(["./bar.md", "./baz", "./foo", "./zoo.html"])
|
2020-03-28 13:37:07 +10:00
|
|
|
self.assertTrue(id(self.summary._entries[1]) != # baz
|
|
|
|
|
id(self.summary._entries[2])) # foo
|
|
|
|
|
self.assertTrue(id(self.summary._entries[2]) != # foo
|
|
|
|
|
id(self.summary._entries[3])) # zoo
|
2016-10-22 19:44:17 +02:00
|
|
|
self.assertTrue(self.jobs_added_event.is_set())
|
2015-12-14 18:03:11 +00:00
|
|
|
|
|
|
|
|
|
2016-01-21 23:22:42 +00:00
|
|
|
def _mount_total():
|
|
|
|
|
with open("/proc/mounts") as proc_mounts:
|
|
|
|
|
return len(proc_mounts.readlines())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _tmp_total():
|
|
|
|
|
return len(os.listdir("/tmp"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MainTestCase(unittest.TestCase):
|
|
|
|
|
|
2016-01-26 14:44:42 +00:00
|
|
|
def test_main_and_restart_and_no_leaks_and_is_relocatable(self):
|
2016-03-09 10:47:09 +00:00
|
|
|
def test_run(root_path, loop):
|
2016-01-21 23:22:42 +00:00
|
|
|
mount_total = _mount_total()
|
2019-08-21 17:44:50 +10:00
|
|
|
tmp_total = _tmp_total()
|
2016-01-26 14:44:42 +00:00
|
|
|
foo_path = os.path.join(root_path, "foo")
|
2016-01-21 23:22:42 +00:00
|
|
|
open(foo_path, "w").close()
|
2017-08-28 16:23:05 +01:00
|
|
|
__main__.manage_cache(root_path)
|
2017-06-27 14:03:32 +01:00
|
|
|
with __main__.chdir(root_path):
|
2016-01-21 23:22:42 +00:00
|
|
|
with contextlib.redirect_stdout(io.StringIO()):
|
2017-06-27 14:03:32 +01:00
|
|
|
__main__.main(root_path, loop, worker_count=2,
|
|
|
|
|
is_being_tested=True)
|
2020-04-25 15:18:43 +10:00
|
|
|
for file_name in ["summary.pickle", "creation_time",
|
2017-08-28 16:23:05 +01:00
|
|
|
"foo-metadata", "foo-contents"]:
|
2018-09-17 23:59:38 +10:00
|
|
|
self.assertTrue(os.path.exists(".eris/" + file_name))
|
2016-01-21 23:22:42 +00:00
|
|
|
self.assertEqual(_mount_total(), mount_total)
|
2019-08-21 17:44:50 +10:00
|
|
|
self.assertEqual(_tmp_total(), tmp_total)
|
2016-01-26 14:44:42 +00:00
|
|
|
temp_dir = tempfile.mkdtemp()
|
|
|
|
|
try:
|
2016-03-09 10:47:09 +00:00
|
|
|
loop = asyncio.get_event_loop()
|
2016-01-26 14:44:42 +00:00
|
|
|
first_dir = os.path.join(temp_dir, "first")
|
|
|
|
|
os.mkdir(first_dir)
|
2016-03-09 10:47:09 +00:00
|
|
|
test_run(first_dir, loop)
|
2016-01-26 14:44:42 +00:00
|
|
|
second_dir = os.path.join(temp_dir, "second")
|
|
|
|
|
os.rename(first_dir, second_dir)
|
2016-03-09 10:47:09 +00:00
|
|
|
test_run(second_dir, loop)
|
2020-03-09 14:31:01 +10:00
|
|
|
loop.close()
|
2019-09-02 14:11:35 +10:00
|
|
|
loop.stop()
|
2016-01-21 23:22:42 +00:00
|
|
|
finally:
|
|
|
|
|
shutil.rmtree(temp_dir)
|
|
|
|
|
|
|
|
|
|
|
2015-12-14 18:03:11 +00:00
|
|
|
if __name__ == "__main__":
|
|
|
|
|
golden.main()
|