File size: 11,468 Bytes
ad474c9 | 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 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 | """The webOS: window manager, apps, filesystem wiring, and the memory map.
Each test either reads a desktop that nothing mutates (built once per class) or
builds its own, because the window manager is stateful and a shared desktop would
make the tests order-dependent. ``d=2000`` throughout: it is the codec floor, and
the desktop renders a full 80x30 shader frame per call.
"""
import unittest
from hdc.gfx import SHADERS
from hdc.os import APPS, COLS, ROWS, Desktop, MemoryMap, boot, tour
class ReadOnlyDesktop(unittest.TestCase):
"""A started desktop shared by the tests that never mutate it."""
@classmethod
def setUpClass(cls):
cls.desktop = Desktop(d=2000).start()
def test_start_opens_the_tiled_session(self):
desktop = Desktop(d=2000).start()
self.assertEqual([w.app for w in desktop.windows],
["terminal", "monitor", "files"])
self.assertEqual(desktop.windows[desktop.focus].app, "terminal")
def test_start_is_idempotent(self):
desktop = Desktop(d=2000)
desktop.start()
before = len(desktop.windows)
desktop.start()
self.assertEqual(len(desktop.windows), before)
def test_launching_an_open_app_raises_it_instead_of_duplicating(self):
desktop = Desktop(d=2000).start()
desktop.launch("files")
desktop.launch("files")
self.assertEqual(sum(1 for w in desktop.windows if w.app == "files"), 1)
self.assertEqual(desktop.windows[desktop.focus].app, "files")
def test_close_removes_the_focused_window(self):
# a fresh desktop: closing mutates the session, and the class-level one
# is shared with the frame test below
desktop = Desktop(d=2000).start()
before = len(desktop.windows)
focused = desktop.focused()
self.assertTrue(desktop.close())
self.assertEqual(len(desktop.windows), before - 1)
self.assertNotIn(focused, desktop.windows)
def test_frame_has_the_grid_shape_and_a_menu_bar(self):
frame = self.desktop.frame()
self.assertEqual(len(frame), ROWS)
for row in frame:
self.assertLessEqual(len(row), COLS)
text = "\n".join(frame)
self.assertIn("webOS", frame[0])
for key in ("terminal", "monitor", "shaders"):
self.assertIn(key, frame[0])
# the taskbar names the open windows
self.assertIn("[Terminal]", frame[-1])
self.assertGreater(self.desktop.last_frame_ms, 0.0)
def test_memory_counts_the_filesystem_cells(self):
"""The filesystem writes into the machine's storage, not its own.
This is the bug the coordinator fixed: two stores made every free-cell
figure a lie, so the counts here are pinned.
"""
desktop = self.desktop
buckets = desktop.memory.buckets()
self.assertIn("fs", buckets)
self.assertEqual(buckets["fs"], len(desktop.fs.nodes))
self.assertEqual(desktop.memory.stat()["cells"], len(desktop.kernel.storage))
def test_memory_report_and_state_shape(self):
report = self.desktop.memory.report()
self.assertIn("cells", report)
state = self.desktop.state()
for key in ("frame", "windows", "memory", "fs", "status", "apps"):
self.assertIn(key, state)
self.assertEqual(len(state["frame"]), ROWS)
self.assertIn("webOS", self.desktop.report())
class TestMemoryAllocator(unittest.TestCase):
def setUp(self):
self.desktop = Desktop(d=2000)
self.map = self.desktop.memory
def test_allocate_and_free(self):
before = self.map.stat()["cells"]
names = self.map.allocate("scratch", 4)
self.assertEqual(len(names), 4)
self.assertEqual(self.map.stat()["cells"], before + 4)
self.assertEqual(self.map.buckets().get("mem"), 4)
self.assertEqual(self.map.free("scratch"), 4)
self.assertEqual(self.map.stat()["cells"], before)
def test_free_unknown_tag_is_zero(self):
self.assertEqual(self.map.free("never-allocated"), 0)
def test_allocate_zero_is_empty(self):
self.assertEqual(self.map.allocate("nothing", 0), [])
class TestTerminalApp(unittest.TestCase):
def setUp(self):
self.desktop = Desktop(d=2000).start()
self.terminal = self.desktop.windows[0]
self.desktop.focus = 0
def test_typing_then_enter_runs_the_machine(self):
self.desktop.text("stat")
self.assertEqual(self.terminal.state["typing"], "stat")
result = self.desktop.key("Enter")
self.assertEqual(result["status"], "kernel: stat")
buffer = "\n".join(self.terminal.state["buffer"])
self.assertIn("cells=", buffer)
self.assertEqual(self.terminal.state["typing"], "")
def test_backspace_edits_the_typing_buffer(self):
self.desktop.text("stad")
self.desktop.key("Backspace")
self.assertEqual(self.terminal.state["typing"], "sta")
def test_empty_enter_does_not_run_anything(self):
result = self.desktop.key("Enter")
self.assertEqual(result["action"], "key")
self.assertEqual(self.terminal.state["typing"], "")
class TestFilesApp(unittest.TestCase):
def setUp(self):
self.desktop = Desktop(d=2000).start()
self.desktop.key("F2")
self.window = self.desktop.focused()
# entries are populated by render(), which the frame does
self.desktop.frame()
self.assertEqual(self.window.app, "files")
def test_enter_opens_a_directory_and_u_goes_back_up(self):
self.assertEqual(self.window.state["cwd"], "/home/user")
self.desktop.key("Enter") # cursor starts on img/
self.assertEqual(self.window.state["cwd"], "/home/user/img")
self.desktop.key("u")
self.assertEqual(self.window.state["cwd"], "/home/user")
def test_enter_on_a_file_opens_the_editor(self):
self.desktop.key("ArrowDown") # img/ -> src/
self.desktop.key("ArrowDown") # src/ -> README.txt
self.desktop.key("Enter")
editor = self.desktop.focused()
self.assertEqual(editor.app, "editor")
self.assertTrue(editor.state["path"].endswith("README.txt"))
def test_cursor_stays_inside_the_listing(self):
for _ in range(20):
self.desktop.key("ArrowDown")
entries = self.window.state["entries"]
self.assertLessEqual(self.window.state["cursor"], len(entries) - 1)
class TestEditorApp(unittest.TestCase):
def test_appending_reaches_the_filesystem(self):
desktop = Desktop(d=2000).start()
path = "/home/user/README.txt"
window = desktop.open_file(path)
self.assertEqual(window.app, "editor")
desktop.text("hello line")
result = desktop.key("Enter")
self.assertEqual(result["status"], "appended to " + path)
self.assertIn("hello line", desktop.fs.read(path))
self.assertEqual(window.state["typing"], "")
def test_editor_without_a_file_says_so(self):
desktop = Desktop(d=2000).start()
window = desktop.launch("editor")
desktop.frame()
app = window.state["app"]
self.assertIn("no file open", "\n".join(app.render(desktop, window)))
class TestShaderApp(unittest.TestCase):
def setUp(self):
self.desktop = Desktop(d=2000).start()
self.desktop.key("F5")
self.window = self.desktop.focused()
self.desktop.frame() # render() picks the default
def test_space_cycles_the_shader(self):
first = self.window.state["shader"]
self.assertIn(first, SHADERS)
self.desktop.key(" ")
second = self.window.state["shader"]
self.assertIn(second, SHADERS)
self.assertNotEqual(first, second)
def test_e_toggles_the_edge_pass(self):
self.assertFalse(self.window.state.get("edges"))
self.desktop.key("e")
self.assertEqual(self.window.state["edges"], "proxy")
self.desktop.key("e")
self.assertFalse(self.window.state.get("edges"))
def test_the_window_holds_a_rendered_surface(self):
surface = self.window.state["frame"]
self.assertEqual(len(surface.rows()), self.window.inner_h - 2)
self.assertEqual(len(surface.rows()[0]) <= self.window.inner_w, True)
class TestKeyboardRouting(unittest.TestCase):
def test_function_keys_launch_every_app(self):
desktop = Desktop(d=2000)
for key, app in Desktop.FUNCTION_KEYS.items():
result = desktop.key(key)
self.assertEqual(result["action"], "launch")
self.assertIsNotNone(desktop.find(app), msg=app)
self.assertEqual(len(desktop.windows), len(APPS))
def test_tab_cycles_focus(self):
desktop = Desktop(d=2000).start()
desktop.focus = 0
desktop.key("Tab")
self.assertEqual(desktop.focus, 1)
desktop.key("Ctrl+Tab")
self.assertEqual(desktop.focus, 0)
def test_ctrl_w_closes_and_ctrl_right_moves(self):
desktop = Desktop(d=2000).start()
before = len(desktop.windows)
desktop.key("Ctrl+W")
self.assertEqual(len(desktop.windows), before - 1)
desktop = Desktop(d=2000).start()
window = desktop.focused()
start_x = window.x
result = desktop.key("Ctrl+Right")
self.assertEqual(result["action"], "move")
self.assertEqual(window.x, start_x + 3)
def test_key_with_no_window_is_not_an_exception(self):
desktop = Desktop(d=2000)
self.assertEqual(desktop.key("q")["action"], "none")
self.assertEqual(desktop.text("x")["action"], "none")
def test_unknown_app_raises(self):
with self.assertRaises(KeyError):
Desktop(d=2000).launch("spreadsheet")
class TestClickFocus(unittest.TestCase):
def test_click_focuses_the_window_under_the_cell(self):
desktop = Desktop(d=2000)
terminal = desktop.launch("terminal", 1, 1)
monitor = desktop.launch("monitor", 50, 1)
self.assertEqual(desktop.focused(), monitor)
desktop.click(2, 2)
self.assertEqual(desktop.focused(), terminal)
desktop.click(60, 2)
self.assertEqual(desktop.focused(), monitor)
def test_click_on_empty_desktop_keeps_focus(self):
desktop = Desktop(d=2000).start()
focused = desktop.focused()
self.assertEqual(desktop.click(0, ROWS - 1), "")
self.assertEqual(desktop.focused(), focused)
class TestSession(unittest.TestCase):
def test_boot_returns_a_started_desktop(self):
desktop = boot(2000)
self.assertEqual(len(desktop.windows), 3)
self.assertEqual(desktop.windows[0].app, "terminal")
def test_tour_prints_a_frame_and_the_report(self):
text = tour(2000)
self.assertIn("=== frame after tour ===", text)
self.assertIn("webOS:", text)
self.assertIn("memory:", text)
class TestMemoryMapStandalone(unittest.TestCase):
def test_buckets_group_by_prefix(self):
desktop = Desktop(d=2000)
map_ = MemoryMap(desktop.kernel.storage)
desktop.kernel.storage.write("code/0", 0)
buckets = map_.buckets()
self.assertIn("code", buckets)
self.assertIn("fs", buckets)
self.assertEqual(buckets["code"], 1)
if __name__ == "__main__":
unittest.main()
|