|
23 | 23 | import time |
24 | 24 | from datetime import datetime |
25 | 25 |
|
26 | | -from PyQt6.QtCore import Qt, QSettings, QItemSelectionModel |
| 26 | +from PyQt6.QtCore import Qt, QSettings, QItemSelectionModel, QModelIndex |
27 | 27 | from PyQt6.QtGui import QPixmap, QIcon |
28 | 28 | from PyQt6.QtWidgets import QMessageBox |
29 | 29 |
|
@@ -156,6 +156,37 @@ def _is_icon(icon, icon_name: str = 'checked'): |
156 | 156 | raise ValueError |
157 | 157 |
|
158 | 158 |
|
| 159 | +# A RemoteVM is a BaseVM: it has none of the AppVM properties below, so reading |
| 160 | +# them must raise (as on a real system) rather than return a mock value. |
| 161 | +REMOTE_VM_MISSING_PROPS = [ |
| 162 | + "template", |
| 163 | + "netvm", |
| 164 | + "provides_network", |
| 165 | + "default_dispvm", |
| 166 | + "template_for_dispvms", |
| 167 | + "virt_mode", |
| 168 | +] |
| 169 | + |
| 170 | +# The qubesd response a property.Get returns for a property the VM doesn't have. |
| 171 | +_NO_SUCH_PROPERTY = b"2\x00QubesNoSuchPropertyError\x00\x00No such property\x00" |
| 172 | + |
| 173 | + |
| 174 | +def _add_remote_vm(qapp, name="test-remote"): |
| 175 | + """Register a RemoteVM in the mock app""" |
| 176 | + qube = MockQube(name=name, qapp=qapp, klass="RemoteVM") |
| 177 | + qapp._qubes[name] = qube |
| 178 | + for prop in REMOTE_VM_MISSING_PROPS: |
| 179 | + qube.properties.pop(prop, None) |
| 180 | + qapp.update_vm_calls() |
| 181 | + for prop in REMOTE_VM_MISSING_PROPS: |
| 182 | + qapp.expected_calls[ |
| 183 | + (name, "admin.vm.property.Get", prop, None)] = _NO_SUCH_PROPERTY |
| 184 | + qapp.expected_calls[ |
| 185 | + (name, "admin.vm.property.GetDefault", prop, None)] = \ |
| 186 | + _NO_SUCH_PROPERTY |
| 187 | + return qube |
| 188 | + |
| 189 | + |
159 | 190 | def test_000_window_loads(qapp, test_qubes_app): |
160 | 191 | dispatcher = MockDispatcher(test_qubes_app) |
161 | 192 |
|
@@ -313,6 +344,70 @@ def test_004_hide_column(mock_settings, qubes_manager): |
313 | 344 | mock_settings.assert_called_with('columns/Is DVM Template', False) |
314 | 345 |
|
315 | 346 |
|
| 347 | +def test_006_remote_vm_listed(qapp, test_qubes_app): |
| 348 | + """ |
| 349 | + A RemoteVM is listed in the manager |
| 350 | + """ |
| 351 | + _add_remote_vm(test_qubes_app) |
| 352 | + dispatcher = MockAsyncDispatcher(test_qubes_app) |
| 353 | + qube_manager_window = qube_manager.VmManagerWindow( |
| 354 | + qapp, test_qubes_app, dispatcher) |
| 355 | + assert "test-remote" in _get_current_vms(qube_manager_window) |
| 356 | + |
| 357 | + |
| 358 | +def test_007_remote_vm_added(qubes_manager): |
| 359 | + """ |
| 360 | + A RemoteVM appearing at runtime is added to the table, not skipped. |
| 361 | + """ |
| 362 | + assert "test-remote" not in _get_current_vms(qubes_manager) |
| 363 | + _add_remote_vm(qubes_manager.qubes_app) |
| 364 | + qubes_manager.qubes_app.domains.clear_cache() |
| 365 | + qubes_manager.on_domain_added(None, "domain-add", "test-remote") |
| 366 | + assert "test-remote" in _get_current_vms(qubes_manager) |
| 367 | + |
| 368 | + |
| 369 | +def test_008_remote_vm_actions_disabled(qubes_manager): |
| 370 | + """ |
| 371 | + Selecting a RemoteVM disable the management actions it cannot support. |
| 372 | + """ |
| 373 | + _add_remote_vm(qubes_manager.qubes_app) |
| 374 | + qubes_manager.qubes_app.domains.clear_cache() |
| 375 | + qubes_manager.on_domain_added(None, "domain-add", "test-remote") |
| 376 | + _select_vm(qubes_manager, "test-remote") |
| 377 | + qubes_manager.table_selection_changed() |
| 378 | + for action in ("action_settings", "action_appmenus", "action_clonevm", |
| 379 | + "action_pausevm", "action_shutdownvm", "action_restartvm", |
| 380 | + "action_killvm", "action_open_console", |
| 381 | + "action_run_command_in_vm", "action_editfwrules"): |
| 382 | + assert not getattr(qubes_manager, action).isEnabled(), action |
| 383 | + assert not qubes_manager.network_menu.isEnabled() |
| 384 | + assert not qubes_manager.template_menu.isEnabled() |
| 385 | + |
| 386 | + |
| 387 | +def test_009_remote_vm_backup_not_checkable(qubes_manager): |
| 388 | + """The Backup checkbox is not user-toggleable for a RemoteVM, whose |
| 389 | + include_in_backups property does not exist.""" |
| 390 | + _add_remote_vm(qubes_manager.qubes_app) |
| 391 | + qubes_manager.qubes_app.domains.clear_cache() |
| 392 | + qubes_manager.on_domain_added(None, "domain-add", "test-remote") |
| 393 | + model = qubes_manager.qubes_model |
| 394 | + backup_col = model.columns_indices.index("Backup") |
| 395 | + name_col = model.columns_indices.index("Name") |
| 396 | + checkable = Qt.ItemFlag.ItemIsUserCheckable |
| 397 | + seen_remote = seen_normal = False |
| 398 | + for row in range(model.rowCount(QModelIndex())): |
| 399 | + name = model.data(model.index(row, name_col), |
| 400 | + Qt.ItemDataRole.DisplayRole) |
| 401 | + flags = model.flags(model.index(row, backup_col)) |
| 402 | + if name == "test-remote": |
| 403 | + seen_remote = True |
| 404 | + assert not (flags & checkable) |
| 405 | + elif name != "dom0": |
| 406 | + seen_normal = True |
| 407 | + assert flags & checkable |
| 408 | + assert seen_remote and seen_normal |
| 409 | + |
| 410 | + |
316 | 411 | @mock.patch('qubesmanager.settings.VMSettingsWindow') |
317 | 412 | def test_200_vm_open_settings(mock_window, qubes_manager): |
318 | 413 | _select_vm(qubes_manager, 'test-blue') |
|
0 commit comments