|
17 | 17 | package org.eclipse.e4.ui.tests.workbench; |
18 | 18 |
|
19 | 19 | import static org.junit.jupiter.api.Assertions.assertEquals; |
| 20 | +import static org.junit.jupiter.api.Assertions.assertNotEquals; |
20 | 21 | import static org.junit.jupiter.api.Assertions.assertTrue; |
21 | 22 |
|
22 | 23 | import jakarta.annotation.PostConstruct; |
|
44 | 45 | import org.eclipse.e4.ui.internal.workbench.swt.E4Application; |
45 | 46 | import org.eclipse.e4.ui.model.application.MApplication; |
46 | 47 | import org.eclipse.e4.ui.model.application.MApplicationElement; |
| 48 | +import org.eclipse.e4.ui.model.application.commands.MCommand; |
47 | 49 | import org.eclipse.e4.ui.model.application.impl.ApplicationFactoryImpl; |
48 | 50 | import org.eclipse.e4.ui.model.application.ui.MUIElement; |
49 | 51 | import org.eclipse.e4.ui.model.application.ui.advanced.MArea; |
@@ -340,6 +342,54 @@ public void testImports_noImportElementId() throws Exception { |
340 | 342 | assertEquals("Could not resolve import for null", logMessages.poll()); |
341 | 343 | } |
342 | 344 |
|
| 345 | + /** |
| 346 | + * Tests that an import resolving to an element of an incompatible type (two |
| 347 | + * elements sharing an elementId) is skipped with a warning instead of aborting |
| 348 | + * model assembly with a {@link ClassCastException}. See |
| 349 | + * <a href="https://github.com/eclipse-platform/eclipse.platform.ui/issues/4148">issue |
| 350 | + * 4148</a>. |
| 351 | + */ |
| 352 | + @Test |
| 353 | + public void testImports_typeIncompatibleElement() throws Exception { |
| 354 | + List<MApplicationElement> imports = new ArrayList<>(); |
| 355 | + List<MApplicationElement> addedElements = new ArrayList<>(); |
| 356 | + |
| 357 | + final String sharedElementId = "testImports_typeIncompatible_id"; |
| 358 | + |
| 359 | + // The fragment imports a window with the shared id ... |
| 360 | + MTrimmedWindow importWindow = modelService.createModelElement(MTrimmedWindow.class); |
| 361 | + importWindow.setElementId(sharedElementId); |
| 362 | + MModelFragments fragment = MFragmentFactory.INSTANCE.createModelFragments(); |
| 363 | + fragment.getImports().add(importWindow); |
| 364 | + imports.add(importWindow); |
| 365 | + |
| 366 | + // ... but the application only contains a command (a different type) that |
| 367 | + // shares that id, so the id-based lookup resolves to a type-incompatible |
| 368 | + // element for the MUIElement-typed placeholder reference. |
| 369 | + MCommand collidingCommand = modelService.createModelElement(MCommand.class); |
| 370 | + collidingCommand.setElementId(sharedElementId); |
| 371 | + application.getCommands().add(collidingCommand); |
| 372 | + |
| 373 | + MPlaceholder placeholder = modelService.createModelElement(MPlaceholder.class); |
| 374 | + placeholder.setRef(importWindow); |
| 375 | + addedElements.add(placeholder); |
| 376 | + |
| 377 | + CountDownLatch countDownLatch = new CountDownLatch(1); |
| 378 | + this.logListener.countDownLatch = countDownLatch; |
| 379 | + |
| 380 | + // Must not throw: the incompatible resolution is skipped, the reference is |
| 381 | + // left untouched, and every other fragment still applies. |
| 382 | + assembler.resolveImports(imports, addedElements); |
| 383 | + |
| 384 | + assertNotEquals(collidingCommand, placeholder.getRef()); |
| 385 | + assertEquals(importWindow, placeholder.getRef()); |
| 386 | + |
| 387 | + boolean completed = countDownLatch.await(COUNTDOWN_TIMEOUT, TimeUnit.MILLISECONDS); |
| 388 | + assertTrue(completed, "Timeout - no event received"); |
| 389 | + assertEquals(1, logMessages.size()); |
| 390 | + assertTrue(logMessages.poll().startsWith("Skipping import for feature")); |
| 391 | + } |
| 392 | + |
343 | 393 | /** |
344 | 394 | * Make sure that all fragments and imports are resolved before the |
345 | 395 | * post-processors are run. For reference, see |
|
0 commit comments