| 1 | /******************************************************************************* |
| 2 | * Copyright (c) 2016 Ericsson and others |
| 3 | * |
| 4 | * All rights reserved. This program and the accompanying materials are |
| 5 | * made available under the terms of the Eclipse Public License v1.0 which |
| 6 | * accompanies this distribution, and is available at |
| 7 | * http://www.eclipse.org/legal/epl-v10.html |
| 8 | *******************************************************************************/ |
| 9 | package org.eclipse.tracecompass.lttng2.kernel.ui.swtbot.tests; |
| 10 | |
| 11 | import static org.junit.Assert.assertTrue; |
| 12 | |
| 13 | import org.eclipse.jdt.annotation.NonNull; |
| 14 | import org.eclipse.jface.bindings.keys.KeyStroke; |
| 15 | import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotView; |
| 16 | import org.eclipse.swtbot.swt.finder.SWTBot; |
| 17 | import org.eclipse.swtbot.swt.finder.finders.UIThreadRunnable; |
| 18 | import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner; |
| 19 | import org.eclipse.swtbot.swt.finder.keyboard.Keyboard; |
| 20 | import org.eclipse.swtbot.swt.finder.keyboard.KeyboardFactory; |
| 21 | import org.eclipse.swtbot.swt.finder.keyboard.Keystrokes; |
| 22 | import org.eclipse.swtbot.swt.finder.matchers.WidgetOfType; |
| 23 | import org.eclipse.swtbot.swt.finder.utils.SWTUtils; |
| 24 | import org.eclipse.swtbot.swt.finder.waits.Conditions; |
| 25 | import org.eclipse.swtbot.swt.finder.widgets.SWTBotButton; |
| 26 | import org.eclipse.swtbot.swt.finder.widgets.SWTBotCheckBox; |
| 27 | import org.eclipse.swtbot.swt.finder.widgets.SWTBotCombo; |
| 28 | import org.eclipse.swtbot.swt.finder.widgets.SWTBotLabel; |
| 29 | import org.eclipse.swtbot.swt.finder.widgets.SWTBotRadio; |
| 30 | import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell; |
| 31 | import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal; |
| 32 | import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager; |
| 33 | import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; |
| 34 | import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; |
| 35 | import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; |
| 36 | import org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared.ConditionHelpers; |
| 37 | import org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared.SWTBotUtils; |
| 38 | import org.eclipse.tracecompass.tmf.ui.views.timegraph.AbstractTimeGraphView; |
| 39 | import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry; |
| 40 | import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphControl; |
| 41 | import org.junit.After; |
| 42 | import org.junit.Before; |
| 43 | import org.junit.Test; |
| 44 | import org.junit.runner.RunWith; |
| 45 | |
| 46 | /** |
| 47 | * Abstract class to build SWTBot test for time graph find dialog. Test the time |
| 48 | * graph view find dialog and its options. |
| 49 | * |
| 50 | * @author Jean-Christian Kouame |
| 51 | */ |
| 52 | @RunWith(SWTBotJunit4ClassRunner.class) |
| 53 | public abstract class FindDialogTestBase extends KernelTestBase { |
| 54 | |
| 55 | private static final Keyboard KEYBOARD = KeyboardFactory.getSWTKeyboard(); |
| 56 | private static final @NonNull ITmfTimestamp START_TIME = TmfTimestamp.create(1368000272650993664L, ITmfTimestamp.NANOSECOND_SCALE); |
| 57 | private static final @NonNull String SPACE = " "; |
| 58 | private static final String REGEX_PREFIX = "\\A"; |
| 59 | private static final String DIALOG_TITLE = "Find"; |
| 60 | private String fFindText; |
| 61 | private SWTBotView fViewBot; |
| 62 | |
| 63 | private static int fSelectionIndex; |
| 64 | |
| 65 | /** |
| 66 | * Get the title of the time graph view the find dialog will use |
| 67 | * |
| 68 | * @return The view title |
| 69 | */ |
| 70 | protected abstract String getViewTitle(); |
| 71 | |
| 72 | /** |
| 73 | * Get the string that will be used for the search |
| 74 | * |
| 75 | * @return The string |
| 76 | */ |
| 77 | protected abstract String getFindText(); |
| 78 | |
| 79 | /** |
| 80 | * Initialize the test and open the timegraph view and the find dialog |
| 81 | */ |
| 82 | @Before |
| 83 | public void init() { |
| 84 | String title = getViewTitle(); |
| 85 | fViewBot = fBot.viewByTitle(title); |
| 86 | fViewBot.show(); |
| 87 | fViewBot.setFocus(); |
| 88 | |
| 89 | TmfSignalManager.dispatchSignal(new TmfSelectionRangeUpdatedSignal(this, START_TIME)); |
| 90 | fBot.waitUntil(ConditionHelpers.timeGraphIsReadyCondition((AbstractTimeGraphView) fViewBot.getViewReference().getPart(false), new TmfTimeRange(START_TIME, START_TIME), START_TIME)); |
| 91 | openDialog(fViewBot); |
| 92 | |
| 93 | fFindText = getFindText(); |
| 94 | } |
| 95 | |
| 96 | /** |
| 97 | * After method to close the dialog |
| 98 | */ |
| 99 | @After |
| 100 | public void afterTest() { |
| 101 | closeDialog(getDialogBot()); |
| 102 | } |
| 103 | |
| 104 | private static void openDialog(SWTBotView view) { |
| 105 | view.setFocus(); |
| 106 | SWTBotUtils.pressShortcutGoToTreeTop(KEYBOARD); |
| 107 | if (SWTUtils.isMac()) { |
| 108 | KEYBOARD.pressShortcut(Keystrokes.COMMAND, KeyStroke.getInstance('F')); |
| 109 | } else { |
| 110 | KEYBOARD.pressShortcut(Keystrokes.CTRL, KeyStroke.getInstance('F')); |
| 111 | } |
| 112 | fBot.waitUntil(Conditions.shellIsActive(DIALOG_TITLE)); |
| 113 | } |
| 114 | |
| 115 | /** |
| 116 | * Test wrap search option |
| 117 | */ |
| 118 | @Test |
| 119 | public void testWrapSearch() { |
| 120 | SWTBot bot = getDialogBot(); |
| 121 | SWTBotButton findButton = bot.button("Find"); |
| 122 | |
| 123 | SearchOptions options = getOptions(false, false, true, false, false); |
| 124 | search(fFindText, options, findButton, bot); |
| 125 | assertTrue(isWrapped(bot)); |
| 126 | verifySelection(fFindText, options, fViewBot, isWrapped(bot)); |
| 127 | options = getOptions(true, false, true, false, false); |
| 128 | search(fFindText, options, findButton, bot); |
| 129 | assertTrue(isWrapped(bot)); |
| 130 | } |
| 131 | |
| 132 | /** |
| 133 | * Test the direction search option |
| 134 | */ |
| 135 | @Test |
| 136 | public void testDirection() { |
| 137 | SWTBot bot = getDialogBot(); |
| 138 | SWTBotButton findButton = bot.button("Find"); |
| 139 | |
| 140 | // forward |
| 141 | testDirectionSearch(true, fFindText, bot, findButton, fViewBot); |
| 142 | testDirectionSearch(false, fFindText, bot, findButton, fViewBot); |
| 143 | } |
| 144 | |
| 145 | private void testDirectionSearch(boolean forward, String findText, SWTBot bot, SWTBotButton findButton, SWTBotView view) { |
| 146 | SearchOptions options = getOptions(forward, false, true, false, false); |
| 147 | fSelectionIndex = getSelectionIndex(view); |
| 148 | search(findText, options, findButton, bot); |
| 149 | verifySelection(findText, options, view, isWrapped(bot)); |
| 150 | } |
| 151 | |
| 152 | /** |
| 153 | * Test the case sensitive search option |
| 154 | */ |
| 155 | @Test |
| 156 | public void testCaseSensitive() { |
| 157 | SWTBot bot = getDialogBot(); |
| 158 | SWTBotButton findButton = bot.button("Find"); |
| 159 | |
| 160 | SearchOptions options = getOptions(true, true, false, false, false); |
| 161 | search(fFindText, options, findButton, bot); |
| 162 | verifyStatusLabel(bot, true); |
| 163 | search(fFindText.toLowerCase(), options, findButton, bot); |
| 164 | verifyStatusLabel(bot, false); |
| 165 | } |
| 166 | |
| 167 | /** |
| 168 | * Test the whole word search option |
| 169 | */ |
| 170 | @Test |
| 171 | public void testWholeWord() { |
| 172 | SWTBot bot = getDialogBot(); |
| 173 | SWTBotButton findButton = bot.button("Find"); |
| 174 | |
| 175 | @NonNull |
| 176 | String text = fFindText.split(SPACE)[0]; |
| 177 | System.out.println("Reg ex : " + text); |
| 178 | SearchOptions options = getOptions(true, false, false, true, false); |
| 179 | search(text, options, findButton, bot); |
| 180 | verifyStatusLabel(bot, true); |
| 181 | search(text.substring(0, text.length() - 1), options, findButton, bot); |
| 182 | verifyStatusLabel(bot, false); |
| 183 | } |
| 184 | |
| 185 | /** |
| 186 | * Test the regular expression search option |
| 187 | */ |
| 188 | @Test |
| 189 | public void testRegEx() { |
| 190 | SWTBot bot = getDialogBot(); |
| 191 | SWTBotButton findButton = bot.button("Find"); |
| 192 | |
| 193 | final String text = REGEX_PREFIX + fFindText.split(SPACE)[0]; |
| 194 | System.out.println("Reg ex : " + text); |
| 195 | SearchOptions options = getOptions(true, false, false, false, true); |
| 196 | search(text, options, findButton, bot); |
| 197 | verifyStatusLabel(bot, true); |
| 198 | options = getOptions(true, false, false, false, false); |
| 199 | search(text, options, findButton, bot); |
| 200 | verifyStatusLabel(bot, false); |
| 201 | } |
| 202 | |
| 203 | /** |
| 204 | * Test open/close the find dialog |
| 205 | */ |
| 206 | @Test |
| 207 | public void testOpenCloseDialog() { |
| 208 | SWTBotShell shell = getDialogShell(); |
| 209 | closeDialog(getDialogBot()); |
| 210 | fBot.waitUntil(Conditions.shellCloses(shell)); |
| 211 | openDialog(fViewBot); |
| 212 | } |
| 213 | |
| 214 | private static void verifyStatusLabel(SWTBot bot, boolean shouldBeFound) { |
| 215 | // Get the second label in the dialog |
| 216 | SWTBotLabel statusLabel = bot.label(1); |
| 217 | assertTrue("status label", shouldBeFound == !statusLabel.getText().equals("Entry not found")); |
| 218 | } |
| 219 | |
| 220 | /** |
| 221 | * Get the find dialog bot |
| 222 | * |
| 223 | * @return The bot |
| 224 | */ |
| 225 | private static SWTBot getDialogBot() { |
| 226 | return getDialogShell().bot(); |
| 227 | } |
| 228 | |
| 229 | /** |
| 230 | * Get the find dialog shell bot |
| 231 | * |
| 232 | * @return The shell bot |
| 233 | */ |
| 234 | private static SWTBotShell getDialogShell() { |
| 235 | return fBot.shell(DIALOG_TITLE); |
| 236 | } |
| 237 | |
| 238 | private static void closeDialog(SWTBot bot) { |
| 239 | bot.button("Close").click(); |
| 240 | } |
| 241 | |
| 242 | private static void search(String findText, SearchOptions options, SWTBotButton findButton, SWTBot bot) { |
| 243 | // set the text to search |
| 244 | SWTBotCombo findFieldCombo = bot.comboBox(); |
| 245 | findFieldCombo.setText(findText); |
| 246 | assertTrue("Find combo", findFieldCombo.getText().equals(findText)); |
| 247 | |
| 248 | // set the options |
| 249 | SWTBotRadio directions = options.forwardSearch ? bot.radio("Forward").click() : bot.radio("Backward").click(); |
| 250 | assertTrue("direction", directions.isSelected()); |
| 251 | |
| 252 | setCheckButton("Case sensitive", options.caseSensitive, bot); |
| 253 | setCheckButton("Wrap search", options.wrapSearch, bot); |
| 254 | setCheckButton("Whole word", options.wholeWord, bot); |
| 255 | setCheckButton("Regular expression", options.regExSearch, bot); |
| 256 | |
| 257 | findButton.click(); |
| 258 | } |
| 259 | |
| 260 | private SearchOptions getOptions(boolean forward, boolean caseSensitive, boolean wrapSearch, boolean wholeWord, boolean regEx) { |
| 261 | SearchOptions options = new SearchOptions(); |
| 262 | options.forwardSearch = forward; |
| 263 | options.caseSensitive = caseSensitive; |
| 264 | options.wrapSearch = wrapSearch; |
| 265 | options.wholeWord = wholeWord; |
| 266 | options.regExSearch = regEx; |
| 267 | return options; |
| 268 | } |
| 269 | |
| 270 | private static void setCheckButton(String mnemonic, boolean option, SWTBot bot) { |
| 271 | final SWTBotCheckBox checkBox = bot.checkBox(mnemonic); |
| 272 | if (checkBox.isEnabled()) { |
| 273 | if (option) { |
| 274 | checkBox.select(); |
| 275 | } else { |
| 276 | checkBox.deselect(); |
| 277 | } |
| 278 | } |
| 279 | } |
| 280 | |
| 281 | private static void verifySelection(String name, SearchOptions options, SWTBotView view, boolean isWrapped) { |
| 282 | final String entryName = getTimegraphSelectionName(view); |
| 283 | assertTrue("entry name", entryName != null && entryName.contains(name)); |
| 284 | |
| 285 | final int selectionIndex = getSelectionIndex(view); |
| 286 | if (!isWrapped) { |
| 287 | assertTrue("selection index", options.forwardSearch ? selectionIndex > fSelectionIndex : selectionIndex < fSelectionIndex); |
| 288 | } else { |
| 289 | assertTrue("selection index", options.forwardSearch ? selectionIndex <= fSelectionIndex : selectionIndex >= fSelectionIndex); |
| 290 | } |
| 291 | fSelectionIndex = selectionIndex; |
| 292 | } |
| 293 | |
| 294 | private static boolean isWrapped(final SWTBot bot) { |
| 295 | return bot.label(1).getText().equals("Wrapped search"); |
| 296 | } |
| 297 | |
| 298 | /** |
| 299 | * Get the timegraph view selected entry |
| 300 | * |
| 301 | * @param viewBot |
| 302 | * The timegraph view bot |
| 303 | * @return The selected entry |
| 304 | */ |
| 305 | private static String getTimegraphSelectionName(final SWTBotView view) { |
| 306 | final TimeGraphControl timegraph = view.bot().widget(WidgetOfType.widgetOfType(TimeGraphControl.class)); |
| 307 | return UIThreadRunnable.syncExec(() -> { |
| 308 | ITimeGraphEntry entry = timegraph.getSelectedTrace(); |
| 309 | if (entry != null) { |
| 310 | return entry.getName(); |
| 311 | } |
| 312 | return null; |
| 313 | }); |
| 314 | } |
| 315 | |
| 316 | /** |
| 317 | * Get the index of the entry selected in the timegraph view |
| 318 | * |
| 319 | * @param viewBot |
| 320 | * The timegraph view bot |
| 321 | * @return |
| 322 | */ |
| 323 | private static Integer getSelectionIndex(SWTBotView viewBot) { |
| 324 | final TimeGraphControl timegraph = viewBot.bot().widget(WidgetOfType.widgetOfType(TimeGraphControl.class)); |
| 325 | return UIThreadRunnable.syncExec(() -> { |
| 326 | return timegraph.getSelectedIndex(); |
| 327 | }); |
| 328 | } |
| 329 | |
| 330 | private class SearchOptions { |
| 331 | boolean forwardSearch; |
| 332 | boolean caseSensitive; |
| 333 | boolean wrapSearch; |
| 334 | boolean wholeWord; |
| 335 | boolean regExSearch; |
| 336 | } |
| 337 | } |