Skip to content

Commit 0a2fa46

Browse files
travkin79Copilot
andcommitted
Add UiUtilsThemeTest for reflection-based dark theme detection
Cover UiUtils.isDarkTheme() with mocked PlatformUI/Platform statics: WITH e4 CSS theming (dark id, light id, mixed-case id, and a null active theme falling back to the preference) and WITHOUT theming (theme bundle absent, theme service null, and a reflective loadClass failure) — all of which must degrade to the persisted themeid preference and finally to light without throwing. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent edabc79 commit 0a2fa46

1 file changed

Lines changed: 178 additions & 0 deletions

File tree

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
package com.microsoft.copilot.eclipse.ui.utils;
5+
6+
import static org.junit.jupiter.api.Assertions.assertFalse;
7+
import static org.junit.jupiter.api.Assertions.assertTrue;
8+
import static org.mockito.Mockito.doReturn;
9+
import static org.mockito.Mockito.doThrow;
10+
import static org.mockito.Mockito.mock;
11+
import static org.mockito.Mockito.mockStatic;
12+
import static org.mockito.Mockito.when;
13+
14+
import org.eclipse.core.runtime.Platform;
15+
import org.eclipse.core.runtime.preferences.IPreferencesService;
16+
import org.eclipse.e4.ui.css.swt.theme.ITheme;
17+
import org.eclipse.e4.ui.css.swt.theme.IThemeEngine;
18+
import org.eclipse.ui.IWorkbench;
19+
import org.eclipse.ui.PlatformUI;
20+
import org.junit.jupiter.api.Test;
21+
import org.mockito.MockedStatic;
22+
import org.osgi.framework.Bundle;
23+
24+
/**
25+
* Unit tests for {@link UiUtils#isDarkTheme()}.
26+
*
27+
* <p>The detection resolves the active e4 CSS theme reflectively through the theme bundle's own
28+
* class loader, so no compile-time reference to the friend-restricted
29+
* {@code org.eclipse.e4.ui.css.swt.theme} package is required in production. These tests drive that
30+
* logic entirely through mocked {@link PlatformUI} and {@link Platform} statics, covering both the
31+
* case where e4 CSS theming is available and the graceful fallbacks when it is not.
32+
*/
33+
class UiUtilsThemeTest {
34+
35+
private static final String THEME_BUNDLE = "org.eclipse.e4.ui.css.swt.theme";
36+
private static final String DARK_THEME_ID = "org.eclipse.e4.ui.css.theme.e4_dark";
37+
private static final String LIGHT_THEME_ID = "org.eclipse.e4.ui.css.theme.e4_default";
38+
39+
// ---------------------------------------------------------------------------
40+
// Theming available: active theme resolved via reflection.
41+
// ---------------------------------------------------------------------------
42+
43+
@Test
44+
void isDarkTheme_activeThemeIsDark_returnsTrue() throws Exception {
45+
try (MockedStatic<PlatformUI> platformUi = mockStatic(PlatformUI.class);
46+
MockedStatic<Platform> platform = mockStatic(Platform.class)) {
47+
IWorkbench workbench = givenThemingAvailable(platformUi, platform);
48+
givenActiveTheme(workbench, DARK_THEME_ID);
49+
50+
assertTrue(UiUtils.isDarkTheme());
51+
}
52+
}
53+
54+
@Test
55+
void isDarkTheme_activeThemeIsLight_returnsFalse() throws Exception {
56+
try (MockedStatic<PlatformUI> platformUi = mockStatic(PlatformUI.class);
57+
MockedStatic<Platform> platform = mockStatic(Platform.class)) {
58+
IWorkbench workbench = givenThemingAvailable(platformUi, platform);
59+
givenActiveTheme(workbench, LIGHT_THEME_ID);
60+
61+
assertFalse(UiUtils.isDarkTheme());
62+
}
63+
}
64+
65+
@Test
66+
void isDarkTheme_activeThemeIdMixedCase_returnsTrue() throws Exception {
67+
try (MockedStatic<PlatformUI> platformUi = mockStatic(PlatformUI.class);
68+
MockedStatic<Platform> platform = mockStatic(Platform.class)) {
69+
IWorkbench workbench = givenThemingAvailable(platformUi, platform);
70+
givenActiveTheme(workbench, "com.example.Solarized.DARK.Theme");
71+
72+
assertTrue(UiUtils.isDarkTheme());
73+
}
74+
}
75+
76+
@Test
77+
void isDarkTheme_activeThemeNull_fallsBackToPreference() throws Exception {
78+
try (MockedStatic<PlatformUI> platformUi = mockStatic(PlatformUI.class);
79+
MockedStatic<Platform> platform = mockStatic(Platform.class)) {
80+
IWorkbench workbench = givenThemingAvailable(platformUi, platform);
81+
givenActiveTheme(workbench, null);
82+
givenPersistedThemeId(platform, DARK_THEME_ID);
83+
84+
assertTrue(UiUtils.isDarkTheme());
85+
}
86+
}
87+
88+
// ---------------------------------------------------------------------------
89+
// Theming unavailable: graceful fallback to the persisted preference.
90+
// ---------------------------------------------------------------------------
91+
92+
@Test
93+
void isDarkTheme_themeBundleAbsent_fallsBackToPreferenceDark() {
94+
try (MockedStatic<PlatformUI> platformUi = mockStatic(PlatformUI.class);
95+
MockedStatic<Platform> platform = mockStatic(Platform.class)) {
96+
platform.when(() -> Platform.getBundle(THEME_BUNDLE)).thenReturn(null);
97+
givenPersistedThemeId(platform, DARK_THEME_ID);
98+
99+
assertTrue(UiUtils.isDarkTheme());
100+
}
101+
}
102+
103+
@Test
104+
void isDarkTheme_themeBundleAbsent_fallsBackToPreferenceLight() {
105+
try (MockedStatic<PlatformUI> platformUi = mockStatic(PlatformUI.class);
106+
MockedStatic<Platform> platform = mockStatic(Platform.class)) {
107+
platform.when(() -> Platform.getBundle(THEME_BUNDLE)).thenReturn(null);
108+
givenPersistedThemeId(platform, "");
109+
110+
assertFalse(UiUtils.isDarkTheme());
111+
}
112+
}
113+
114+
@Test
115+
void isDarkTheme_themeServiceNull_fallsBackToPreference() throws Exception {
116+
try (MockedStatic<PlatformUI> platformUi = mockStatic(PlatformUI.class);
117+
MockedStatic<Platform> platform = mockStatic(Platform.class)) {
118+
IWorkbench workbench = givenThemingAvailable(platformUi, platform);
119+
doReturn(null).when(workbench).getService(IThemeEngine.class);
120+
givenPersistedThemeId(platform, DARK_THEME_ID);
121+
122+
assertTrue(UiUtils.isDarkTheme());
123+
}
124+
}
125+
126+
@Test
127+
void isDarkTheme_reflectionFailure_fallsBackToPreferenceWithoutThrowing() throws Exception {
128+
try (MockedStatic<PlatformUI> platformUi = mockStatic(PlatformUI.class);
129+
MockedStatic<Platform> platform = mockStatic(Platform.class)) {
130+
IWorkbench workbench = mock(IWorkbench.class);
131+
platformUi.when(PlatformUI::getWorkbench).thenReturn(workbench);
132+
133+
Bundle themeBundle = mock(Bundle.class);
134+
doThrow(new ClassNotFoundException()).when(themeBundle)
135+
.loadClass("org.eclipse.e4.ui.css.swt.theme.IThemeEngine");
136+
platform.when(() -> Platform.getBundle(THEME_BUNDLE)).thenReturn(themeBundle);
137+
givenPersistedThemeId(platform, LIGHT_THEME_ID);
138+
139+
assertFalse(UiUtils.isDarkTheme());
140+
}
141+
}
142+
143+
// ---------------------------------------------------------------------------
144+
// Helpers.
145+
// ---------------------------------------------------------------------------
146+
147+
private static IWorkbench givenThemingAvailable(MockedStatic<PlatformUI> platformUi,
148+
MockedStatic<Platform> platform) throws ClassNotFoundException {
149+
IWorkbench workbench = mock(IWorkbench.class);
150+
platformUi.when(PlatformUI::getWorkbench).thenReturn(workbench);
151+
152+
Bundle themeBundle = mock(Bundle.class);
153+
doReturn(IThemeEngine.class).when(themeBundle)
154+
.loadClass("org.eclipse.e4.ui.css.swt.theme.IThemeEngine");
155+
doReturn(ITheme.class).when(themeBundle)
156+
.loadClass("org.eclipse.e4.ui.css.swt.theme.ITheme");
157+
platform.when(() -> Platform.getBundle(THEME_BUNDLE)).thenReturn(themeBundle);
158+
return workbench;
159+
}
160+
161+
private static void givenActiveTheme(IWorkbench workbench, String themeId) {
162+
IThemeEngine themeEngine = mock(IThemeEngine.class);
163+
doReturn(themeEngine).when(workbench).getService(IThemeEngine.class);
164+
165+
ITheme activeTheme = null;
166+
if (themeId != null) {
167+
activeTheme = mock(ITheme.class);
168+
when(activeTheme.getId()).thenReturn(themeId);
169+
}
170+
when(themeEngine.getActiveTheme()).thenReturn(activeTheme);
171+
}
172+
173+
private static void givenPersistedThemeId(MockedStatic<Platform> platform, String themeId) {
174+
IPreferencesService preferences = mock(IPreferencesService.class);
175+
platform.when(Platform::getPreferencesService).thenReturn(preferences);
176+
when(preferences.getString(THEME_BUNDLE, "themeid", "", null)).thenReturn(themeId);
177+
}
178+
}

0 commit comments

Comments
 (0)