Merge lp:~alexlauni/unity/unity.ap-switcher-emu into lp:unity
- unity.ap-switcher-emu
- Merge into trunk
Proposed by
Alex Launi
on 2012-04-24
| Status: | Merged |
|---|---|
| Approved by: | Thomi Richards on 2012-04-26 |
| Approved revision: | 2322 |
| Merged at revision: | 2349 |
| Proposed branch: | lp:~alexlauni/unity/unity.ap-switcher-emu |
| Merge into: | lp:unity |
| Diff against target: |
846 lines (+292/-298) 2 files modified
tests/autopilot/autopilot/emulators/unity/switcher.py (+124/-121) tests/autopilot/autopilot/tests/test_switcher.py (+168/-177) |
| To merge this branch: | bzr merge lp:~alexlauni/unity/unity.ap-switcher-emu |
| Related bugs: |
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Thomi Richards (community) | 2012-04-24 | Approve on 2012-04-26 | |
|
Review via email:
|
|||
Commit Message
Port autopilot switcher emulator to new-style API.
Description of the Change
Refactors autopilot switcher emulator, and updates tests to match.
To post a comment you must log in.
lp:~alexlauni/unity/unity.ap-switcher-emu
updated
on 2012-04-26
- 2321. By Alex Launi on 2012-04-26
-
update from merge comments
- 2322. By Alex Launi on 2012-04-26
-
merge thomi's whitespace fixes
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
| 1 | === modified file 'tests/autopilot/autopilot/emulators/unity/switcher.py' |
| 2 | --- tests/autopilot/autopilot/emulators/unity/switcher.py 2012-04-16 01:03:10 +0000 |
| 3 | +++ tests/autopilot/autopilot/emulators/unity/switcher.py 2012-04-26 20:14:23 +0000 |
| 4 | @@ -8,10 +8,9 @@ |
| 5 | # |
| 6 | |
| 7 | import logging |
| 8 | -from time import sleep |
| 9 | |
| 10 | -from autopilot.emulators.unity import get_state_by_path, make_introspection_object |
| 11 | -from autopilot.emulators.X11 import Keyboard, Mouse |
| 12 | +from autopilot.emulators.unity import UnityIntrospectionObject |
| 13 | +from autopilot.emulators.X11 import Mouse |
| 14 | from autopilot.keybindings import KeybindingsHelper |
| 15 | |
| 16 | # even though we don't use these directly, we need to make sure they've been |
| 17 | @@ -21,63 +20,80 @@ |
| 18 | |
| 19 | logger = logging.getLogger(__name__) |
| 20 | |
| 21 | - |
| 22 | -# TODO: THis class needs to be ported to the new-style emulator classes. |
| 23 | -# See launcher.py or dash.py for reference. |
| 24 | +class SwitcherMode(): |
| 25 | + """Define the possible modes the switcher can be in""" |
| 26 | + NORMAL = 0 |
| 27 | + ALL = 1 |
| 28 | + DETAIL = 2 |
| 29 | + |
| 30 | + |
| 31 | class Switcher(KeybindingsHelper): |
| 32 | - """Interact with the Unity switcher.""" |
| 33 | + """A class for interacting with the switcher. |
| 34 | + |
| 35 | + Abstracts out switcher implementation, and makes the necessary functionality available |
| 36 | + to consumer code. |
| 37 | + |
| 38 | + """ |
| 39 | |
| 40 | def __init__(self): |
| 41 | super(Switcher, self).__init__() |
| 42 | - self._keyboard = Keyboard() |
| 43 | self._mouse = Mouse() |
| 44 | - |
| 45 | - def initiate(self): |
| 46 | - """Start the switcher with alt+tab.""" |
| 47 | - logger.debug("Initiating switcher with Alt+Tab") |
| 48 | - self.keybinding_hold("switcher/reveal_normal") |
| 49 | - self.keybinding_tap("switcher/reveal_normal") |
| 50 | - sleep(1) |
| 51 | - |
| 52 | - def initiate_detail_mode(self): |
| 53 | - """Start detail mode with alt+`""" |
| 54 | - logger.debug("Initiating switcher detail mode with Alt+`") |
| 55 | - self.keybinding_hold("switcher/reveal_details") |
| 56 | - self.keybinding_tap("switcher/reveal_details") |
| 57 | - sleep(1) |
| 58 | - |
| 59 | - def initiate_all_mode(self): |
| 60 | - """Start switcher in 'all workspaces' mode. |
| 61 | - |
| 62 | - Shows apps from all workspaces, instead of just the current workspace. |
| 63 | - """ |
| 64 | - logger.debug("Initiating switcher in 'all workspaces' mode.") |
| 65 | - self.keybinding_hold("switcher/reveal_all") |
| 66 | - self.keybinding_tap("switcher/reveal_all") |
| 67 | - sleep(1) |
| 68 | - |
| 69 | - def initiate_right_arrow(self): |
| 70 | - """Impropperly attempt to start switcher.""" |
| 71 | - logger.debug("Initiating switcher with Alt+Right (should not work)") |
| 72 | - self.keybinding_hold("switcher/reveal_impropper") |
| 73 | - self.keybinding_tap("switcher/right") |
| 74 | - sleep(1) |
| 75 | - |
| 76 | - def terminate(self): |
| 77 | - """Stop switcher without activating the selected icon.""" |
| 78 | - logger.debug("Terminating switcher.") |
| 79 | - self.keybinding("switcher/cancel") |
| 80 | - self.keybinding_release("switcher/reveal_normal") |
| 81 | - |
| 82 | - def cancel(self): |
| 83 | - """Stop switcher without activating the selected icon and releasing the keys.""" |
| 84 | - logger.debug("Cancelling switcher.") |
| 85 | - self.keybinding("switcher/cancel") |
| 86 | - |
| 87 | - def stop(self): |
| 88 | - """Stop switcher and activate the selected icon.""" |
| 89 | - logger.debug("Stopping switcher") |
| 90 | - self.keybinding_release("switcher/reveal_normal") |
| 91 | + controllers = SwitcherController.get_all_instances() |
| 92 | + assert(len(controllers) == 1) |
| 93 | + self.controller = controllers[0] |
| 94 | + |
| 95 | + @property |
| 96 | + def visible(self): |
| 97 | + """Is the switcher currently visible |
| 98 | + |
| 99 | + """ |
| 100 | + return self.controller.visible |
| 101 | + |
| 102 | + @property |
| 103 | + def icons(self): |
| 104 | + """The set of icons in the switcher model |
| 105 | + |
| 106 | + """ |
| 107 | + return self.controller.model.icons |
| 108 | + |
| 109 | + @property |
| 110 | + def current_icon(self): |
| 111 | + """The currently selected switcher icon""" |
| 112 | + return self.icons[self.selection_index] |
| 113 | + |
| 114 | + @property |
| 115 | + def selection_index(self): |
| 116 | + """The index of the currently selected icon""" |
| 117 | + return self.controller.model.selection_index |
| 118 | + |
| 119 | + @property |
| 120 | + def mode(self): |
| 121 | + """Returns the SwitcherMode that the switcher is currently in.""" |
| 122 | + if not self.visible: |
| 123 | + return None |
| 124 | + if self.controller.model.detail_selection and not self.controller.model.only_detail_on_viewport: |
| 125 | + return SwitcherMode.DETAIL, SwitcherMode.ALL |
| 126 | + elif self.controller.model.detail_selection: |
| 127 | + return SwitcherMode.DETAIL |
| 128 | + elif not self.controller.model.only_detail_on_viewport: |
| 129 | + return SwitcherMode.ALL |
| 130 | + else: |
| 131 | + return SwitcherMode.NORMAL |
| 132 | + |
| 133 | + def initiate(self, mode=SwitcherMode.NORMAL): |
| 134 | + """Initiates the switcher in designated mode. Defaults to NORMAL""" |
| 135 | + if mode == SwitcherMode.NORMAL: |
| 136 | + logger.debug("Initiating switcher with Alt+Tab") |
| 137 | + self.keybinding_hold_part_then_tap("switcher/reveal_normal") |
| 138 | + self.visible.wait_for(True) |
| 139 | + elif mode == SwitcherMode.DETAIL: |
| 140 | + logger.debug("Initiating switcher detail mode with Alt+`") |
| 141 | + self.keybinding_hold_part_then_tap("switcher/reveal_details") |
| 142 | + self.controller.model.detail_selection.wait_for(True) |
| 143 | + elif mode == SwitcherMode.ALL: |
| 144 | + logger.debug("Initiating switcher in 'all workspaces' mode. Ctrl+Alt+Tab") |
| 145 | + self.keybinding_hold_part_then_tap("switcher/reveal_all") |
| 146 | + self.controller.model.only_detail_on_viewport.wait_for(False) |
| 147 | |
| 148 | def next_icon(self): |
| 149 | """Move to the next icon.""" |
| 150 | @@ -89,14 +105,37 @@ |
| 151 | logger.debug("Selecting previous item in switcher.") |
| 152 | self.keybinding("switcher/prev") |
| 153 | |
| 154 | - def next_icon_mouse(self): |
| 155 | - """Move to the next icon using the mouse scroll wheel""" |
| 156 | + def cancel(self): |
| 157 | + """Stop switcher without activating the selected icon and releasing the keys. |
| 158 | + |
| 159 | + NOTE: Does not release Alt. |
| 160 | + |
| 161 | + """ |
| 162 | + logger.debug("Cancelling switcher.") |
| 163 | + self.keybinding("switcher/cancel") |
| 164 | + self.controller.visible.wait_for(False) |
| 165 | + |
| 166 | + def terminate(self): |
| 167 | + """Stop switcher without activating the selected icon.""" |
| 168 | + logger.debug("Terminating switcher.") |
| 169 | + self.keybinding("switcher/cancel") |
| 170 | + self.keybinding_release("switcher/reveal_normal") |
| 171 | + self.controller.visible.wait_for(False) |
| 172 | + |
| 173 | + def select(self): |
| 174 | + """Stop switcher and activate the selected icon.""" |
| 175 | + logger.debug("Stopping switcher") |
| 176 | + self.keybinding_release("switcher/reveal_normal") |
| 177 | + self.controller.visible.wait_for(False) |
| 178 | + |
| 179 | + def next_via_mouse(self): |
| 180 | + """Move to the next icon using the mouse scroll wheel.""" |
| 181 | logger.debug("Selecting next item in switcher with mouse scroll wheel.") |
| 182 | self._mouse.press(6) |
| 183 | self._mouse.release(6) |
| 184 | |
| 185 | - def previous_icon_mouse(self): |
| 186 | - """Move to the previous icon using the mouse scroll wheel""" |
| 187 | + def previous_via_mouse(self): |
| 188 | + """Move to the previous icon using the mouse scroll wheel.""" |
| 189 | logger.debug("Selecting previous item in switcher with mouse scroll wheel.") |
| 190 | self._mouse.press(7) |
| 191 | self._mouse.release(7) |
| 192 | @@ -105,11 +144,13 @@ |
| 193 | """Show detail mode.""" |
| 194 | logger.debug("Showing details view.") |
| 195 | self.keybinding("switcher/detail_start") |
| 196 | + self.controller.model.detail_selection.wait_for(True) |
| 197 | |
| 198 | def hide_details(self): |
| 199 | """Hide detail mode.""" |
| 200 | logger.debug("Hiding details view.") |
| 201 | self.keybinding("switcher/detail_stop") |
| 202 | + self.controller.model.detail_selection.wait_for(False) |
| 203 | |
| 204 | def next_detail(self): |
| 205 | """Move to next detail in the switcher.""" |
| 206 | @@ -121,65 +162,27 @@ |
| 207 | logger.debug("Selecting previous item in details mode.") |
| 208 | self.keybinding("switcher/detail_prev") |
| 209 | |
| 210 | - def __get_icon(self, index): |
| 211 | - return self.__get_model()['Children'][index][1][0] |
| 212 | - |
| 213 | - @property |
| 214 | - def current_icon(self): |
| 215 | - """Get the currently-selected icon.""" |
| 216 | - if not self.get_is_visible: |
| 217 | - return None |
| 218 | - model = self.__get_model() |
| 219 | - sel_idx = self.get_selection_index() |
| 220 | - try: |
| 221 | - return make_introspection_object(model['Children'][sel_idx]) |
| 222 | - except KeyError: |
| 223 | - return None |
| 224 | - |
| 225 | - def get_icon_name(self, index): |
| 226 | - return self.__get_icon(index)['tooltip_text'] |
| 227 | - |
| 228 | - def get_icon_desktop_file(self, index): |
| 229 | - try: |
| 230 | - return self.__get_icon(index)['desktop_file'] |
| 231 | - except: |
| 232 | - return None |
| 233 | - |
| 234 | - def get_model_size(self): |
| 235 | - return len(self.__get_model()['Children']) |
| 236 | - |
| 237 | - def get_selection_index(self): |
| 238 | - return int(self.__get_model()['selection-index']) |
| 239 | - |
| 240 | - def get_last_selection_index(self): |
| 241 | - return bool(self.__get_model()['last-selection-index']) |
| 242 | - |
| 243 | - def get_is_visible(self): |
| 244 | - return bool(self.__get_controller()['visible']) |
| 245 | - |
| 246 | - def get_monitor(self): |
| 247 | - return int(self.__get_controller()['monitor']) |
| 248 | - |
| 249 | - def get_is_in_details_mode(self): |
| 250 | - """Return True if the SwitcherView is in details mode.""" |
| 251 | - return bool(self.__get_model()['detail-selection']) |
| 252 | - |
| 253 | - def get_switcher_icons(self): |
| 254 | - """Get all icons in the switcher model. |
| 255 | - |
| 256 | - The switcher needs to be initiated in order to get the model. |
| 257 | - |
| 258 | - """ |
| 259 | - icons = [] |
| 260 | - model = get_state_by_path('//SwitcherModel')[0] |
| 261 | - for child in model['Children']: |
| 262 | - icon = make_introspection_object(child) |
| 263 | - if icon: |
| 264 | - icons.append(icon) |
| 265 | - return icons |
| 266 | - |
| 267 | - def __get_model(self): |
| 268 | - return get_state_by_path('/Unity/SwitcherController/SwitcherModel')[0] |
| 269 | - |
| 270 | - def __get_controller(self): |
| 271 | - return get_state_by_path('/Unity/SwitcherController')[0] |
| 272 | + |
| 273 | +class SwitcherController(UnityIntrospectionObject): |
| 274 | + """An emulator class for interacting with the switcher controller.""" |
| 275 | + |
| 276 | + @property |
| 277 | + def view(self): |
| 278 | + return self.get_children_by_type(SwitcherView)[0] |
| 279 | + |
| 280 | + @property |
| 281 | + def model(self): |
| 282 | + return self.get_children_by_type(SwitcherModel)[0] |
| 283 | + |
| 284 | + |
| 285 | +class SwitcherView(UnityIntrospectionObject): |
| 286 | + """An emulator class for interacting with with SwitcherView.""" |
| 287 | + |
| 288 | + |
| 289 | +class SwitcherModel(UnityIntrospectionObject): |
| 290 | + """An emulator class for interacting with the SwitcherModel.""" |
| 291 | + |
| 292 | + @property |
| 293 | + def icons(self): |
| 294 | + return self.get_children_by_type(SimpleLauncherIcon) |
| 295 | + |
| 296 | |
| 297 | === modified file 'tests/autopilot/autopilot/tests/test_switcher.py' |
| 298 | --- tests/autopilot/autopilot/tests/test_switcher.py 2012-04-16 01:03:10 +0000 |
| 299 | +++ tests/autopilot/autopilot/tests/test_switcher.py 2012-04-26 20:14:23 +0000 |
| 300 | @@ -6,198 +6,172 @@ |
| 301 | # under the terms of the GNU General Public License version 3, as published |
| 302 | # by the Free Software Foundation. |
| 303 | |
| 304 | -from testtools.matchers import Equals, NotEquals, Contains, Not |
| 305 | +import logging |
| 306 | +from testtools.matchers import Equals, Contains, Not |
| 307 | from time import sleep |
| 308 | |
| 309 | +from autopilot.matchers import Eventually |
| 310 | +#from autopilot.emulators.window import Window |
| 311 | +from autopilot.emulators.unity.switcher import SwitcherMode |
| 312 | from autopilot.tests import AutopilotTestCase |
| 313 | |
| 314 | - |
| 315 | -class SwitcherTests(AutopilotTestCase): |
| 316 | +logger = logging.getLogger(__name__) |
| 317 | + |
| 318 | +class SwitcherTestCase(AutopilotTestCase): |
| 319 | + def set_timeout_setting(self, value): |
| 320 | + self.set_unity_option("alt_tab_timeout", value) |
| 321 | + sleep(1) |
| 322 | + |
| 323 | + |
| 324 | +class SwitcherTests(SwitcherTestCase): |
| 325 | """Test the switcher.""" |
| 326 | - |
| 327 | def set_timeout_setting(self, value): |
| 328 | self.set_unity_option("alt_tab_timeout", value) |
| 329 | + sleep(1) |
| 330 | |
| 331 | def setUp(self): |
| 332 | super(SwitcherTests, self).setUp() |
| 333 | - |
| 334 | + self.set_timeout_setting(False) |
| 335 | self.char_map = self.start_app('Character Map') |
| 336 | self.calc = self.start_app('Calculator') |
| 337 | self.mahjongg = self.start_app('Mahjongg') |
| 338 | |
| 339 | def tearDown(self): |
| 340 | super(SwitcherTests, self).tearDown() |
| 341 | + |
| 342 | + def test_nothing(self): |
| 343 | + pass |
| 344 | + #Window() |
| 345 | + |
| 346 | + def test_witcher_starts_in_normal_mode(self): |
| 347 | + """Switcher must start in normal (i.e.- not details) mode.""" |
| 348 | + self.start_app("Character Map") |
| 349 | sleep(1) |
| 350 | |
| 351 | + self.switcher.initiate() |
| 352 | + self.addCleanup(self.switcher.terminate) |
| 353 | + self.assertThat(self.switcher.mode, Equals(SwitcherMode.NORMAL)) |
| 354 | + |
| 355 | def test_switcher_move_next(self): |
| 356 | - self.set_timeout_setting(False) |
| 357 | - sleep(1) |
| 358 | - |
| 359 | + """Test that pressing the next icon binding moves to the next icon""" |
| 360 | self.switcher.initiate() |
| 361 | - sleep(.2) |
| 362 | + self.addCleanup(self.switcher.terminate) |
| 363 | |
| 364 | - start = self.switcher.get_selection_index() |
| 365 | + start = self.switcher.selection_index |
| 366 | self.switcher.next_icon() |
| 367 | - sleep(.2) |
| 368 | - |
| 369 | - end = self.switcher.get_selection_index() |
| 370 | - self.switcher.terminate() |
| 371 | - |
| 372 | - self.assertThat(start, NotEquals(0)) |
| 373 | - self.assertThat(end, Equals(start + 1)) |
| 374 | + self.assertThat(self.switcher.selection_index, Equals(start + 1)) |
| 375 | |
| 376 | def test_switcher_move_prev(self): |
| 377 | - self.set_timeout_setting(False) |
| 378 | - sleep(1) |
| 379 | - |
| 380 | + """Test that pressing the previous icon binding moves to the previous icon""" |
| 381 | self.switcher.initiate() |
| 382 | - sleep(.2) |
| 383 | + self.addCleanup(self.switcher.terminate) |
| 384 | |
| 385 | - start = self.switcher.get_selection_index() |
| 386 | + start = self.switcher.selection_index |
| 387 | self.switcher.previous_icon() |
| 388 | - sleep(.2) |
| 389 | - |
| 390 | - end = self.switcher.get_selection_index() |
| 391 | - self.switcher.terminate() |
| 392 | - |
| 393 | - self.assertThat(start, NotEquals(0)) |
| 394 | - self.assertThat(end, Equals(start - 1)) |
| 395 | + self.assertThat(self.switcher.selection_index, Equals(start - 1)) |
| 396 | |
| 397 | def test_switcher_scroll_next(self): |
| 398 | - self.set_timeout_setting(False) |
| 399 | - sleep(1) |
| 400 | - |
| 401 | + """Test that scrolling the mouse wheel down moves to the next icon""" |
| 402 | self.switcher.initiate() |
| 403 | - sleep(.2) |
| 404 | - |
| 405 | - start = self.switcher.get_selection_index() |
| 406 | - self.switcher.next_icon_mouse() |
| 407 | - sleep(.2) |
| 408 | - |
| 409 | - end = self.switcher.get_selection_index() |
| 410 | - self.assertThat(start, NotEquals(0)) |
| 411 | - self.assertThat(end, Equals(start + 1)) |
| 412 | - |
| 413 | - self.switcher.terminate() |
| 414 | + self.addCleanup(self.switcher.terminate) |
| 415 | + |
| 416 | + start = self.switcher.selection_index |
| 417 | + self.switcher.next_via_mouse() |
| 418 | + |
| 419 | + self.assertThat(self.switcher.selection_index, Equals(start + 1)) |
| 420 | |
| 421 | def test_switcher_scroll_prev(self): |
| 422 | - self.set_timeout_setting(False) |
| 423 | - sleep(1) |
| 424 | - |
| 425 | + """Test that scrolling the mouse wheel up moves to the previous icon""" |
| 426 | self.switcher.initiate() |
| 427 | - sleep(.2) |
| 428 | - |
| 429 | - start = self.switcher.get_selection_index() |
| 430 | - self.switcher.previous_icon_mouse() |
| 431 | - sleep(.2) |
| 432 | - |
| 433 | - end = self.switcher.get_selection_index() |
| 434 | - self.assertThat(start, NotEquals(0)) |
| 435 | + self.addCleanup(self.switcher.terminate) |
| 436 | + |
| 437 | + start = self.switcher.selection_index |
| 438 | + self.switcher.previous_via_mouse() |
| 439 | + |
| 440 | + end = self.switcher.selection_index |
| 441 | self.assertThat(end, Equals(start - 1)) |
| 442 | |
| 443 | self.switcher.terminate() |
| 444 | |
| 445 | def test_switcher_scroll_next_ignores_fast_events(self): |
| 446 | - self.set_timeout_setting(False) |
| 447 | - sleep(1) |
| 448 | - |
| 449 | + """Ensures that smoothing is working correctly for next icon scrolling. |
| 450 | + |
| 451 | + Only the first event in a rapid fire string of events should be acted upon. |
| 452 | + The rest ignored. |
| 453 | + |
| 454 | + """ |
| 455 | self.switcher.initiate() |
| 456 | - sleep(.2) |
| 457 | - |
| 458 | - # Quickly repeatead events should be ignored (except the first) |
| 459 | - start = self.switcher.get_selection_index() |
| 460 | - self.switcher.next_icon_mouse() |
| 461 | - self.switcher.next_icon_mouse() |
| 462 | - self.switcher.next_icon_mouse() |
| 463 | - sleep(.2) |
| 464 | - |
| 465 | - end = self.switcher.get_selection_index() |
| 466 | - self.assertThat(start, NotEquals(0)) |
| 467 | - self.assertThat(end, Equals(start + 1)) |
| 468 | - |
| 469 | - self.switcher.terminate() |
| 470 | + self.addCleanup(self.switcher.terminate) |
| 471 | + |
| 472 | + # Quickly repeated events should be ignored (except the first) |
| 473 | + start = self.switcher.selection_index |
| 474 | + self.switcher.next_via_mouse() |
| 475 | + self.switcher.next_via_mouse() |
| 476 | + self.switcher.next_via_mouse() |
| 477 | + |
| 478 | + self.assertThat(self.switcher.selection_index, Equals(start + 1)) |
| 479 | |
| 480 | def test_switcher_scroll_prev_ignores_fast_events(self): |
| 481 | - self.set_timeout_setting(False) |
| 482 | - sleep(1) |
| 483 | - |
| 484 | + """Ensures that smoothing is working correctly for previous icon scrolling. |
| 485 | + |
| 486 | + Only the first event in a rapid fire string of events should be acted upon. |
| 487 | + The rest ignored. |
| 488 | + |
| 489 | + """ |
| 490 | self.switcher.initiate() |
| 491 | - sleep(.2) |
| 492 | + self.addCleanup(self.switcher.terminate) |
| 493 | |
| 494 | # Quickly repeatead events should be ignored (except the first) |
| 495 | - start = self.switcher.get_selection_index() |
| 496 | - self.switcher.previous_icon_mouse() |
| 497 | - self.switcher.previous_icon_mouse() |
| 498 | - self.switcher.previous_icon_mouse() |
| 499 | - sleep(.2) |
| 500 | - |
| 501 | - end = self.switcher.get_selection_index() |
| 502 | - self.assertThat(end, Equals(start - 1)) |
| 503 | - |
| 504 | - self.switcher.terminate() |
| 505 | + start = self.switcher.selection_index |
| 506 | + self.switcher.previous_via_mouse() |
| 507 | + self.switcher.previous_via_mouse() |
| 508 | + self.switcher.previous_via_mouse() |
| 509 | + |
| 510 | + self.assertThat(self.switcher.selection_index, Equals(start - 1)) |
| 511 | |
| 512 | def test_switcher_arrow_key_does_not_init(self): |
| 513 | - self.set_timeout_setting(False) |
| 514 | - sleep(1) |
| 515 | - |
| 516 | - self.switcher.initiate_right_arrow() |
| 517 | - sleep(.2) |
| 518 | - |
| 519 | - self.assertThat(self.switcher.get_is_visible(), Equals(False)) |
| 520 | - self.switcher.terminate() |
| 521 | - self.set_timeout_setting(True) |
| 522 | + """Ensure that Alt+Right does not initiate switcher. |
| 523 | + |
| 524 | + Regression test for LP:?????? |
| 525 | + |
| 526 | + """ |
| 527 | + self.keyboard.press('Alt') |
| 528 | + self.addCleanup(self.keyboard.release, 'Alt') |
| 529 | + self.keyboard.press_and_release('Right') |
| 530 | + self.assertThat(self.switcher.visible, Equals(False)) |
| 531 | |
| 532 | def test_lazy_switcher_initiate(self): |
| 533 | - self.set_timeout_setting(False) |
| 534 | - sleep(1) |
| 535 | - |
| 536 | self.keybinding_hold("switcher/reveal_normal") |
| 537 | self.addCleanup(self.keybinding_release, "switcher/reveal_normal") |
| 538 | - sleep(1) |
| 539 | - self.assertThat(self.switcher.get_is_visible(), Equals(False)) |
| 540 | + self.assertThat(self.switcher.visible, Eventually(Equals(False))) |
| 541 | |
| 542 | - sleep(1) |
| 543 | self.keybinding_tap("switcher/reveal_normal") |
| 544 | self.addCleanup(self.keybinding, "switcher/cancel") |
| 545 | - sleep(.5) |
| 546 | - self.assertThat(self.switcher.get_is_visible(), Equals(True)) |
| 547 | + self.assertThat(self.switcher.visible, Eventually(Equals(True))) |
| 548 | |
| 549 | def test_switcher_cancel(self): |
| 550 | - self.set_timeout_setting(False) |
| 551 | - sleep(1) |
| 552 | - |
| 553 | self.switcher.initiate() |
| 554 | self.addCleanup(self.switcher.terminate) |
| 555 | - sleep(.2) |
| 556 | - |
| 557 | - self.assertThat(self.switcher.get_is_visible(), Equals(True)) |
| 558 | - |
| 559 | + |
| 560 | + self.assertThat(self.switcher.visible, Eventually(Equals(True))) |
| 561 | self.switcher.cancel() |
| 562 | - sleep(.2) |
| 563 | - |
| 564 | - self.assertThat(self.switcher.get_is_visible(), Equals(False)) |
| 565 | + self.assertThat(self.switcher.visible, Eventually(Equals(False))) |
| 566 | |
| 567 | def test_lazy_switcher_cancel(self): |
| 568 | - self.set_timeout_setting(False) |
| 569 | - sleep(1) |
| 570 | - |
| 571 | self.keybinding_hold("switcher/reveal_normal") |
| 572 | self.addCleanup(self.keybinding_release, "switcher/reveal_normal") |
| 573 | - sleep(1) |
| 574 | - self.assertThat(self.switcher.get_is_visible(), Equals(False)) |
| 575 | - |
| 576 | - sleep(1) |
| 577 | + self.assertThat(self.switcher.visible, Eventually(Equals(False))) |
| 578 | self.keybinding_tap("switcher/reveal_normal") |
| 579 | - self.addCleanup(self.keybinding, "switcher/cancel") |
| 580 | - sleep(.5) |
| 581 | - self.assertThat(self.switcher.get_is_visible(), Equals(True)) |
| 582 | - |
| 583 | + self.assertThat(self.switcher.visible, Eventually(Equals(True))) |
| 584 | self.switcher.cancel() |
| 585 | - sleep(.2) |
| 586 | - |
| 587 | - self.assertThat(self.switcher.get_is_visible(), Equals(False)) |
| 588 | + self.assertThat(self.switcher.visible, Eventually(Equals(False))) |
| 589 | |
| 590 | def test_switcher_appears_on_monitor_with_focused_window(self): |
| 591 | + """Tests that the switches appears on the correct monitor. |
| 592 | + |
| 593 | + This is defined as the monitor with a focused window. |
| 594 | + |
| 595 | + """ |
| 596 | num_monitors = self.screen_geo.get_num_monitors() |
| 597 | if num_monitors == 1: |
| 598 | self.skip("No point testing this on one monitor") |
| 599 | @@ -206,21 +180,24 @@ |
| 600 | for monitor in range(num_monitors): |
| 601 | self.screen_geo.drag_window_to_monitor(calc_win, monitor) |
| 602 | self.switcher.initiate() |
| 603 | - sleep(1) |
| 604 | - self.assertThat(self.switcher.get_monitor(), Equals(monitor)) |
| 605 | - self.switcher.terminate() |
| 606 | - |
| 607 | - |
| 608 | -class SwitcherWindowsManagementTests(AutopilotTestCase): |
| 609 | + self.addCleanup(self.switcher.terminate) |
| 610 | + self.assertThat(self.switcher.controller.monitor, Eventually(Equals(monitor))) |
| 611 | + |
| 612 | + |
| 613 | +class SwitcherWindowsManagementTests(SwitcherTestCase): |
| 614 | """Test the switcher window management.""" |
| 615 | |
| 616 | def test_switcher_raises_only_last_focused_window(self): |
| 617 | - """Tests that when we do an alt+tab only the previously focused window |
| 618 | - is raised. |
| 619 | + """Tests that when we do an alt+tab only the previously focused window is raised. |
| 620 | + |
| 621 | This is tests by opening 2 Calculators and a Mahjongg. |
| 622 | Then we do a quick alt+tab twice. |
| 623 | Then we close the currently focused window. |
| 624 | + |
| 625 | """ |
| 626 | + #FIXME: Setup |
| 627 | + # There are a lot of asserts in this test that are just making sure the env. is properly |
| 628 | + # initialized. |
| 629 | self.close_all_app("Mahjongg") |
| 630 | self.close_all_app("Calculator") |
| 631 | |
| 632 | @@ -243,37 +220,33 @@ |
| 633 | self.assertTrue(mah_win2.is_focused) |
| 634 | |
| 635 | self.assertVisibleWindowStack([mah_win2, calc_win, mah_win1]) |
| 636 | + #end setup? |
| 637 | |
| 638 | self.keybinding("switcher/reveal_normal") |
| 639 | sleep(1) |
| 640 | - self.assertTrue(calc_win.is_focused) |
| 641 | + self.assertThat(calc_win.is_focused, Equals(True)) |
| 642 | self.assertVisibleWindowStack([calc_win, mah_win2, mah_win1]) |
| 643 | |
| 644 | self.keybinding("switcher/reveal_normal") |
| 645 | sleep(1) |
| 646 | - self.assertTrue(mah_win2.is_focused) |
| 647 | + self.assertThat(mah_win2.is_focused, Equals(True)) |
| 648 | self.assertVisibleWindowStack([mah_win2, calc_win, mah_win1]) |
| 649 | |
| 650 | self.keybinding("window/close") |
| 651 | sleep(1) |
| 652 | - |
| 653 | - self.assertTrue(calc_win.is_focused) |
| 654 | + self.assertThat(calc_win.is_focused, Equals(True)) |
| 655 | self.assertVisibleWindowStack([calc_win, mah_win1]) |
| 656 | |
| 657 | |
| 658 | -class SwitcherDetailsTests(AutopilotTestCase): |
| 659 | +class SwitcherDetailsTests(SwitcherTestCase): |
| 660 | """Test the details mode for the switcher.""" |
| 661 | - |
| 662 | - def test_switcher_starts_in_normal_mode(self): |
| 663 | - """Switcher must start in normal (i.e.- not details) mode.""" |
| 664 | - self.start_app("Character Map") |
| 665 | - sleep(1) |
| 666 | - |
| 667 | - self.switcher.initiate() |
| 668 | - self.addCleanup(self.switcher.terminate) |
| 669 | - self.assertThat(self.switcher.get_is_in_details_mode(), Equals(False)) |
| 670 | + def setUp(self): |
| 671 | + super(SwitcherDetailsTests, self).setUp() |
| 672 | + self.set_timeout_setting(True) |
| 673 | |
| 674 | def test_details_mode_on_delay(self): |
| 675 | + """Test that details mode activates on a timeout.""" |
| 676 | + #FIXME: Setup |
| 677 | self.close_all_app('Character Map') |
| 678 | self.workspace.switch_to(1) |
| 679 | self.start_app("Character Map") |
| 680 | @@ -285,17 +258,23 @@ |
| 681 | # the character map. |
| 682 | self.start_app('Mahjongg') |
| 683 | sleep(1) |
| 684 | + # end setup |
| 685 | |
| 686 | self.switcher.initiate() |
| 687 | self.addCleanup(self.switcher.terminate) |
| 688 | # Wait longer than details mode. |
| 689 | sleep(3) |
| 690 | - self.assertTrue(self.switcher.get_is_in_details_mode()) |
| 691 | + self.assertThat(self.switcher.mode, Equals(SwitcherMode.DETAIL)) |
| 692 | |
| 693 | def test_no_details_for_apps_on_different_workspace(self): |
| 694 | - # Re bug: 933406 |
| 695 | + """Tests that details mode does not initiates when there are multiple windows |
| 696 | + |
| 697 | + of an application spread across different workspaces. |
| 698 | + Regression test for LP:933406. |
| 699 | + |
| 700 | + """ |
| 701 | + #Fixme: setup |
| 702 | self.close_all_app('Character Map') |
| 703 | - |
| 704 | self.workspace.switch_to(1) |
| 705 | self.start_app("Character Map") |
| 706 | sleep(1) |
| 707 | @@ -306,15 +285,16 @@ |
| 708 | # the character map. |
| 709 | self.start_app('Mahjongg') |
| 710 | sleep(1) |
| 711 | + # end setup |
| 712 | |
| 713 | self.switcher.initiate() |
| 714 | self.addCleanup(self.switcher.terminate) |
| 715 | # Wait longer than details mode. |
| 716 | sleep(3) |
| 717 | - self.assertFalse(self.switcher.get_is_in_details_mode()) |
| 718 | - |
| 719 | - |
| 720 | -class SwitcherDetailsModeTests(AutopilotTestCase): |
| 721 | + self.assertThat(self.switcher.mode, Equals(SwitcherMode.NORMAL)) |
| 722 | + |
| 723 | + |
| 724 | +class SwitcherDetailsModeTests(SwitcherTestCase): |
| 725 | """Tests for the details mode of the switcher. |
| 726 | |
| 727 | Tests for initiation with both grave (`) and Down arrow. |
| 728 | @@ -327,34 +307,38 @@ |
| 729 | ] |
| 730 | |
| 731 | def test_can_start_details_mode(self): |
| 732 | - """Must be able to initiate details mode using selected scenario keycode.""" |
| 733 | + """Must be able to initiate details mode using selected scenario keycode. |
| 734 | + |
| 735 | + """ |
| 736 | self.start_app("Character Map") |
| 737 | self.switcher.initiate() |
| 738 | self.addCleanup(self.switcher.terminate) |
| 739 | self.keyboard.press_and_release(self.initiate_keycode) |
| 740 | - self.assertThat(self.switcher.get_is_in_details_mode(), Equals(True)) |
| 741 | - |
| 742 | - def test_tab_from_last_detail_works(self): |
| 743 | - """Pressing tab while showing last switcher item in details mode |
| 744 | + self.assertThat(self.switcher.mode, Equals(SwitcherMode.DETAIL)) |
| 745 | + |
| 746 | + def test_next_icon_from_last_detail_works(self): |
| 747 | + """Pressing next while showing last switcher item in details mode |
| 748 | + |
| 749 | must select first item in the model in non-details mode. |
| 750 | |
| 751 | """ |
| 752 | self.start_app("Character Map") |
| 753 | self.switcher.initiate() |
| 754 | self.addCleanup(self.switcher.terminate) |
| 755 | - while self.switcher.get_selection_index() < self.switcher.get_model_size() -1: |
| 756 | + while self.switcher.selection_index < len(self.switcher.icons) -1: |
| 757 | self.switcher.next_icon() |
| 758 | self.keyboard.press_and_release(self.initiate_keycode) |
| 759 | sleep(0.5) |
| 760 | self.switcher.next_icon() |
| 761 | - self.assertThat(self.switcher.get_selection_index(), Equals(0)) |
| 762 | - |
| 763 | - |
| 764 | -class SwitcherWorkspaceTests(AutopilotTestCase): |
| 765 | + self.assertThat(self.switcher.selection_index, Equals(0)) |
| 766 | + |
| 767 | + |
| 768 | +class SwitcherWorkspaceTests(SwitcherTestCase): |
| 769 | """Test Switcher behavior with respect to multiple workspaces.""" |
| 770 | |
| 771 | def test_switcher_shows_current_workspace_only(self): |
| 772 | """Switcher must show apps from the current workspace only.""" |
| 773 | + #FIXME: SETUP |
| 774 | self.close_all_app('Calculator') |
| 775 | self.close_all_app('Character Map') |
| 776 | |
| 777 | @@ -364,11 +348,12 @@ |
| 778 | self.workspace.switch_to(2) |
| 779 | char_map = self.start_app("Character Map") |
| 780 | sleep(1) |
| 781 | + # END SETUP |
| 782 | |
| 783 | self.switcher.initiate() |
| 784 | - sleep(1) |
| 785 | - icon_names = [i.tooltip_text for i in self.switcher.get_switcher_icons()] |
| 786 | - self.switcher.terminate() |
| 787 | + self.addCleanup(self.switcher.terminate) |
| 788 | + |
| 789 | + icon_names = [i.tooltip_text for i in self.switcher.icons] |
| 790 | self.assertThat(icon_names, Contains(char_map.name)) |
| 791 | self.assertThat(icon_names, Not(Contains(calc.name))) |
| 792 | |
| 793 | @@ -377,23 +362,29 @@ |
| 794 | self.close_all_app('Calculator') |
| 795 | self.close_all_app('Character Map') |
| 796 | |
| 797 | + #FIXME: this is setup |
| 798 | self.workspace.switch_to(1) |
| 799 | calc = self.start_app("Calculator") |
| 800 | sleep(1) |
| 801 | self.workspace.switch_to(2) |
| 802 | char_map = self.start_app("Character Map") |
| 803 | sleep(1) |
| 804 | - |
| 805 | - self.switcher.initiate_all_mode() |
| 806 | - sleep(1) |
| 807 | - icon_names = [i.tooltip_text for i in self.switcher.get_switcher_icons()] |
| 808 | - self.switcher.terminate() |
| 809 | + # END SETUP |
| 810 | + |
| 811 | + self.switcher.initiate(SwitcherMode.ALL) |
| 812 | + self.addCleanup(self.switcher.terminate) |
| 813 | + |
| 814 | + icon_names = [i.tooltip_text for i in self.switcher.icons] |
| 815 | self.assertThat(icon_names, Contains(calc.name)) |
| 816 | self.assertThat(icon_names, Contains(char_map.name)) |
| 817 | |
| 818 | def test_switcher_can_switch_to_minimised_window(self): |
| 819 | """Switcher must be able to switch to a minimised window when there's |
| 820 | - another instance of the same application on a different workspace.""" |
| 821 | + |
| 822 | + another instance of the same application on a different workspace. |
| 823 | + |
| 824 | + """ |
| 825 | + #FIXME this is setup. |
| 826 | # disable automatic gridding of the switcher after a timeout, since it makes |
| 827 | # it harder to write the tests. |
| 828 | self.set_unity_option("alt_tab_timeout", False) |
| 829 | @@ -411,14 +402,14 @@ |
| 830 | |
| 831 | self.start_app("Calculator") |
| 832 | sleep(1) |
| 833 | + # END SETUP |
| 834 | |
| 835 | self.switcher.initiate() |
| 836 | - sleep(1) |
| 837 | while self.switcher.current_icon.tooltip_text != mahjongg.name: |
| 838 | + logger.debug("%s -> %s" % (self.switcher.current_icon.tooltip_text, mahjongg.name)) |
| 839 | self.switcher.next_icon() |
| 840 | sleep(1) |
| 841 | - self.switcher.stop() |
| 842 | - sleep(1) |
| 843 | + self.switcher.select() |
| 844 | |
| 845 | #get mahjongg windows - there should be two: |
| 846 | wins = mahjongg.get_windows() |


Hi,
A few things:
== switcher.py ==
1) Diff has several bits of trailing whitespace:
116 + @property
170 +
310 +
330 +
2) Your docstrings need to be PEP257 compliant. Specifically:
"""One line docstrings look like this."""
"""Multi-line docstrings start with a single sentence.
Then a newline, followed by one or more sentences. Multi-line
docstrings end with a blank line before the closing quotes as
well, like this.
"""
3) The docstring for 'Switcher.property' should be changes, as should the implementation. Currently it will sometimes return a typle of items, and sometimes return a single item. I suggest the following:
@property
def mode(self):
"""Returns a tuple of SwitcherMode items that describe the mode the switcher is in.
For example, to check whether the switcher is in details mode:
>>> SwitcherMode.DETAIL in self.switcher.mode
... True
""" .model. detail_ selection and not self.controller .model. only_detail_ on_viewport: DETAIL, SwitcherMode.ALL) .model. detail_ selection: DETAIL, ) .model. only_detail_ on_viewport: NORMAL, )
if not self.visible:
return tuple()
if self.controller
return (SwitcherMode.
elif self.controller
return (SwitcherMode.
elif not self.controller
return (SwitcherMode.ALL,)
else:
return (SwitcherMode.
== test_switcher.py ==
1) Unused import: NotEquals
2) PEP8 compliance: need 2 blank lines between module-level blocks. So 2 blank lines between the end of one class and the start of the next, and between the end of the import block and the start of the first class.
3) Docstring issues here similar to the switcher.py file.
4) Lots of trailing whitespace in this file!
5) Should probably use Eventually in the first few tests of SwitcherTests class, for example:
def test_witcher_ starts_ in_normal_ mode(self) :
"""Switcher must start in normal (i.e.- not details) mode.
"""
self.start_ app("Character Map")
sleep(1)
6) Is the alt+Tab timeout value an introspectable property? If not, can we make it one? THen we can replace this:
370 + def set_timeout_ setting( self, value): unity_option( "alt_tab_ timeout" , value)
371 + self.set_
372 + sleep(1)
With this:
370 + def set_timeout_ setting( self, value): unity_option( "alt_tab_ timeout" , value) alt_tab_ timeout. wait_for( value)
371 + self.set_
372 + self.switcher.
Otherwise, looks good - all switcher tests pass for me locally.