1 /******************************************************************************* 2 * Copyright (c) 2009, 2017 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 ******************************************************************************/ 14 15 package org.eclipse.e4.ui.tests.application; 16 17 import static org.junit.Assert.assertFalse; 18 import static org.junit.Assert.assertTrue; 19 import static org.junit.Assert.fail; 20 21 import java.util.ArrayList; 22 import java.util.List; 23 import org.eclipse.e4.core.contexts.EclipseContextFactory; 24 import org.eclipse.e4.core.contexts.IEclipseContext; 25 import org.eclipse.e4.core.services.events.IEventBroker; 26 import org.eclipse.e4.ui.internal.workbench.UIEventPublisher; 27 import org.eclipse.e4.ui.model.application.MApplication; 28 import org.eclipse.e4.ui.model.application.MApplicationElement; 29 import org.eclipse.e4.ui.model.application.MApplicationFactory; 30 import org.eclipse.e4.ui.model.application.ui.basic.MBasicFactory; 31 import org.eclipse.e4.ui.model.application.ui.basic.MWindow; 32 import org.eclipse.e4.ui.model.application.ui.menu.MMenu; 33 import org.eclipse.e4.ui.model.application.ui.menu.MMenuFactory; 34 import org.eclipse.e4.ui.tests.model.test.MTestFactory; 35 import org.eclipse.e4.ui.tests.model.test.MTestHarness; 36 import org.eclipse.e4.ui.workbench.UIEvents.ApplicationElement; 37 import org.eclipse.e4.ui.workbench.UIEvents.Command; 38 import org.eclipse.e4.ui.workbench.UIEvents.Context; 39 import org.eclipse.e4.ui.workbench.UIEvents.Contribution; 40 import org.eclipse.e4.ui.workbench.UIEvents.Dirtyable; 41 import org.eclipse.e4.ui.workbench.UIEvents.ElementContainer; 42 import org.eclipse.e4.ui.workbench.UIEvents.EventTags; 43 import org.eclipse.e4.ui.workbench.UIEvents.Input; 44 import org.eclipse.e4.ui.workbench.UIEvents.Parameter; 45 import org.eclipse.e4.ui.workbench.UIEvents.UIElement; 46 import org.eclipse.e4.ui.workbench.UIEvents.UILabel; 47 import org.eclipse.e4.ui.workbench.UIEvents.Window; 48 import org.eclipse.emf.common.notify.Notifier; 49 import org.junit.Test; 50 import org.osgi.service.event.EventHandler; 51 52 public class UIEventsTest extends HeadlessApplicationElementTest { 53 54 class EventTester { 55 String testerName; 56 IEventBroker eventBroker; 57 String topic; 58 String[] attIds; 59 boolean[] hasFired; 60 61 EventHandler attListener = event -> { 62 assertTrue(event.getTopic().equals(topic), 63 "Incorrect Topic: " + event.getTopic()); //$NON-NLS-1$ 64 65 String attId = (String) event.getProperty(EventTags.ATTNAME); 66 int attIndex = getAttIndex(attId); 67 assertTrue(attIndex >= 0, "Unknown Attribite: " + attId); //$NON-NLS-1$ 68 hasFired[attIndex] = true; 69 }; 70 EventTester(String name, String topic, String[] attIds, IEventBroker eventBroker)71 public EventTester(String name, String topic, String[] attIds, 72 IEventBroker eventBroker) { 73 this.testerName = name; 74 this.topic = topic; 75 this.attIds = attIds; 76 this.eventBroker = eventBroker; 77 78 hasFired = new boolean[attIds.length]; 79 reset(); 80 81 eventBroker.subscribe(this.topic, attListener); 82 } 83 84 /** 85 * @param b 86 * @param string 87 */ assertTrue(boolean b, String string)88 protected void assertTrue(boolean b, String string) { 89 } 90 91 /** 92 * @param attId 93 * @return 94 */ getAttIndex(String attId)95 protected int getAttIndex(String attId) { 96 for (int i = 0; i < attIds.length; i++) { 97 if (attIds[i].equals(attId)) 98 return i; 99 } 100 return -1; 101 } 102 dispose()103 public void dispose() { 104 eventBroker.unsubscribe(attListener); 105 } 106 reset()107 public void reset() { 108 for (int i = 0; i < hasFired.length; i++) 109 hasFired[i] = false; 110 } 111 getAttIds(boolean fired)112 public String[] getAttIds(boolean fired) { 113 List<String> atts = new ArrayList<>(); 114 for (int i = 0; i < hasFired.length; i++) { 115 if (hasFired[i] == fired) 116 atts.add(attIds[i]); 117 } 118 119 return atts.toArray(new String[atts.size()]); 120 } 121 } 122 123 public class AppElementTester extends EventTester { AppElementTester(IEventBroker eventBroker)124 AppElementTester(IEventBroker eventBroker) { 125 super("AppElement", ApplicationElement.TOPIC_ALL, new String[] { 126 ApplicationElement.ELEMENTID, ApplicationElement.TAGS, 127 ApplicationElement.PERSISTEDSTATE }, eventBroker); 128 } 129 } 130 131 public class CommandTester extends EventTester { CommandTester(IEventBroker eventBroker)132 CommandTester(IEventBroker eventBroker) { 133 super("Command", Command.TOPIC_ALL, 134 new String[] { Command.COMMANDNAME }, eventBroker); 135 } 136 } 137 138 public class ContextTester extends EventTester { ContextTester(IEventBroker eventBroker)139 ContextTester(IEventBroker eventBroker) { 140 super("Context", Context.TOPIC_ALL, new String[] { Context.CONTEXT, 141 Context.VARIABLES }, eventBroker); 142 } 143 } 144 145 public class ContributionTester extends EventTester { ContributionTester(IEventBroker eventBroker)146 ContributionTester(IEventBroker eventBroker) { 147 super("Contribution", Contribution.TOPIC_ALL, new String[] { 148 Contribution.CONTRIBUTIONURI, Contribution.OBJECT }, 149 eventBroker); 150 } 151 } 152 153 public class ElementContainerTester extends EventTester { ElementContainerTester(IEventBroker eventBroker)154 ElementContainerTester(IEventBroker eventBroker) { 155 super("ElementContainer", ElementContainer.TOPIC_ALL, 156 new String[] { ElementContainer.CHILDREN, 157 ElementContainer.SELECTEDELEMENT }, eventBroker); 158 } 159 } 160 161 public class DirtyableTester extends EventTester { DirtyableTester(IEventBroker eventBroker)162 DirtyableTester(IEventBroker eventBroker) { 163 super("Dirtyable", Dirtyable.TOPIC_ALL, 164 new String[] { Dirtyable.DIRTY }, eventBroker); 165 } 166 } 167 168 public class InputTester extends EventTester { InputTester(IEventBroker eventBroker)169 InputTester(IEventBroker eventBroker) { 170 super("Input", Input.TOPIC_ALL, new String[] { Input.INPUTURI }, 171 eventBroker); 172 } 173 } 174 175 public class ParameterTester extends EventTester { ParameterTester(IEventBroker eventBroker)176 ParameterTester(IEventBroker eventBroker) { 177 super("Parameter", Parameter.TOPIC_ALL, new String[] { 178 Parameter.NAME, Parameter.VALUE }, eventBroker); 179 } 180 } 181 182 public class UIElementTester extends EventTester { UIElementTester(IEventBroker eventBroker)183 UIElementTester(IEventBroker eventBroker) { 184 super("UIElement", UIElement.TOPIC_ALL, new String[] { 185 UIElement.RENDERER, UIElement.TOBERENDERED, 186 UIElement.PARENT, UIElement.ONTOP, UIElement.VISIBLE, 187 UIElement.CONTAINERDATA, UIElement.WIDGET }, eventBroker); 188 } 189 } 190 191 public class UIItemTester extends EventTester { UIItemTester(IEventBroker eventBroker)192 UIItemTester(IEventBroker eventBroker) { 193 super("UIItem", UILabel.TOPIC_ALL, new String[] { UILabel.LABEL, 194 UILabel.ICONURI, UILabel.TOOLTIP }, eventBroker); 195 } 196 } 197 198 public class WindowTester extends EventTester { WindowTester(IEventBroker eventBroker)199 WindowTester(IEventBroker eventBroker) { 200 super("Window", Window.TOPIC_ALL, new String[] { Window.MAINMENU, 201 Window.X, Window.Y, Window.WIDTH, Window.HEIGHT }, 202 eventBroker); 203 } 204 } 205 206 @Override createApplicationElement( IEclipseContext appContext)207 protected MApplicationElement createApplicationElement( 208 IEclipseContext appContext) throws Exception { 209 MApplication application = MApplicationFactory.INSTANCE 210 .createApplication(); 211 application.getChildren().add(MBasicFactory.INSTANCE.createWindow()); 212 return application; 213 } 214 215 @Test testAllTopics()216 public void testAllTopics() { 217 IEventBroker eventBroker = rule.getApplicationContext().get(IEventBroker.class); 218 219 // Create a tester for each topic 220 AppElementTester appTester = new AppElementTester(eventBroker); 221 CommandTester commandTester = new CommandTester(eventBroker); 222 ContextTester contextTester = new ContextTester(eventBroker); 223 ContributionTester contributionTester = new ContributionTester( 224 eventBroker); 225 ElementContainerTester elementContainerTester = new ElementContainerTester( 226 eventBroker); 227 DirtyableTester dirtyableTester = new DirtyableTester(eventBroker); 228 InputTester inputTester = new InputTester(eventBroker); 229 ParameterTester parameterTester = new ParameterTester(eventBroker); 230 UIElementTester uiElementTester = new UIElementTester(eventBroker); 231 UIItemTester uiItemTester = new UIItemTester(eventBroker); 232 WindowTester windowTester = new WindowTester(eventBroker); 233 234 // Create an array to check for 'cross talk' (i.e. events being fired 235 // on incorrect topics 236 EventTester[] allTesters = { appTester, commandTester, contextTester, 237 contributionTester, elementContainerTester, inputTester, 238 parameterTester, uiElementTester, uiItemTester, windowTester }; 239 240 // Create the test harness and hook up the event publisher 241 MTestHarness allData = MTestFactory.eINSTANCE.createTestHarness(); 242 final UIEventPublisher ep = new UIEventPublisher(rule.getApplicationContext()); 243 ((Notifier) allData).eAdapters().add(ep); 244 rule.getApplicationContext().set(UIEventPublisher.class, ep); 245 246 // AppElement 247 reset(allTesters); 248 String newId = "Some New Id"; 249 allData.setElementId(newId); 250 allData.getTags().add("Testing"); 251 // allData.setTags("new Style"); 252 allData.getPersistedState().put("testing", "Some state"); 253 checkForFailures(allTesters, appTester); 254 255 // Test that no-ops don't throw events 256 appTester.reset(); 257 allData.setElementId(newId); 258 assertTrue("event thrown on No-Op", 259 appTester.getAttIds(true).length == 0); 260 261 // Command 262 reset(allTesters); 263 IEclipseContext newContext = EclipseContextFactory.create(); 264 allData.setContext(newContext); 265 allData.getVariables().add("foo"); 266 checkForFailures(allTesters, contextTester); 267 268 // Context 269 reset(allTesters); 270 allData.setContext(EclipseContextFactory.create()); 271 allData.getVariables().add("A var"); 272 checkForFailures(allTesters, contextTester); 273 274 // Contribution 275 reset(allTesters); 276 allData.setContributionURI("Some URI"); 277 allData.setObject("Some onbject"); 278 checkForFailures(allTesters, contributionTester); 279 280 // ElementContainer 281 reset(allTesters); 282 MMenu menu = MMenuFactory.INSTANCE.createMenu(); 283 allData.getChildren().add(menu); 284 allData.setSelectedElement(menu); 285 checkForFailures(allTesters, elementContainerTester); 286 287 // Input 288 reset(allTesters); 289 allData.setInputURI("New Input Uri"); 290 checkForFailures(allTesters, inputTester); 291 292 // Dirtyable 293 reset(allTesters); 294 allData.setDirty(!allData.isDirty()); 295 checkForFailures(allTesters, dirtyableTester); 296 297 // Parameter 298 reset(allTesters); 299 allData.setName("New Tag"); 300 allData.setValue("New Value"); 301 checkForFailures(allTesters, parameterTester); 302 303 // UIElement 304 reset(allTesters); 305 MTestHarness newParent = MTestFactory.eINSTANCE.createTestHarness(); 306 allData.setRenderer("New Renderer"); 307 allData.setParent(newParent); 308 allData.setToBeRendered(!allData.isToBeRendered()); 309 allData.setVisible(!allData.isVisible()); 310 allData.setOnTop(!allData.isOnTop()); 311 allData.setWidget("New Widget"); 312 allData.setContainerData("new Data"); 313 checkForFailures(allTesters, uiElementTester); 314 315 // UIItem 316 reset(allTesters); 317 allData.setLabel("New Name"); 318 allData.setIconURI("New Icon URI"); 319 allData.setTooltip("New Tooltip"); 320 checkForFailures(allTesters, uiItemTester); 321 322 // Window tests 323 reset(allTesters); 324 MWindow window = ((MApplication) applicationElement).getChildren().get( 325 0); 326 window.setX(1234); 327 window.setY(1234); 328 window.setWidth(1234); 329 window.setHeight(1234); 330 331 MMenu newMainMenu = MMenuFactory.INSTANCE.createMenu(); 332 window.setMainMenu(newMainMenu); 333 checkForFailures(allTesters, windowTester); 334 } 335 336 // Verify bug 374534 337 @Test testBrokerCleanup()338 public void testBrokerCleanup() { 339 final String testTopic = "test/374534"; 340 IEventBroker appEB = rule.getApplicationContext().get(IEventBroker.class); 341 342 IEclipseContext childContext = rule.getApplicationContext().createChild(); 343 IEventBroker childEB = childContext.get(IEventBroker.class); 344 assertFalse("child context has same IEventBroker", appEB == childEB); 345 346 final boolean seen[] = { false }; 347 childEB.subscribe(testTopic, event -> seen[0] = true); 348 349 // ensure the EBs are wired up 350 assertFalse(seen[0]); 351 appEB.send(testTopic, null); 352 assertTrue(seen[0]); 353 354 seen[0] = false; 355 childContext.dispose(); 356 appEB.send(testTopic, null); 357 assertFalse(seen[0]); 358 } 359 360 /** 361 * @param allTesters 362 * @param tester 363 */ checkForFailures(EventTester[] allTesters, EventTester tester)364 private void checkForFailures(EventTester[] allTesters, EventTester tester) { 365 ensureAllSet(tester); 366 ensureNoCrossTalk(allTesters, tester); 367 } 368 369 /** 370 * Ensures that no events were picked up from topics other than the one we 371 * expect to see changes in. 372 * 373 * @param tester 374 */ ensureNoCrossTalk(EventTester[] allTesters, EventTester skipMe)375 private void ensureNoCrossTalk(EventTester[] allTesters, EventTester skipMe) { 376 List<EventTester> badTesters = new ArrayList<>(); 377 for (EventTester t : allTesters) { 378 if (t.equals(skipMe)) 379 continue; 380 381 if (t.getAttIds(true).length > 0) 382 badTesters.add(t); 383 } 384 385 if (badTesters.size() > 0) { 386 String msg = "Events were fired in the wrong topic(s): " 387 + badTesters; 388 fail(msg); 389 } 390 } 391 392 /** 393 * @param tester 394 */ ensureAllSet(EventTester tester)395 private void ensureAllSet(EventTester tester) { 396 String[] unfiredIds = tester.getAttIds(false); 397 if (unfiredIds.length > 0) { 398 StringBuilder msg = new StringBuilder("No event fired:").append(unfiredIds); 399 for (String unfiredId : unfiredIds) { 400 msg.append(' ').append(unfiredId); 401 } 402 fail(msg.toString()); 403 } 404 } 405 406 /** 407 * @param allTesters 408 */ reset(EventTester[] allTesters)409 private void reset(EventTester[] allTesters) { 410 for (EventTester t : allTesters) { 411 t.reset(); 412 } 413 } 414 } 415