diff --git a/gui/src/main/java/com/devonfw/ide/gui/core/context/GuiStateManager.java b/gui/src/main/java/com/devonfw/ide/gui/core/context/GuiStateManager.java
index e166ffb5c8..5e3e0ca941 100644
--- a/gui/src/main/java/com/devonfw/ide/gui/core/context/GuiStateManager.java
+++ b/gui/src/main/java/com/devonfw/ide/gui/core/context/GuiStateManager.java
@@ -17,6 +17,8 @@
import com.devonfw.ide.gui.core.factory.TabFactory;
import com.devonfw.ide.gui.core.service.CommandletService;
import com.devonfw.ide.gui.core.service.NlsService;
+import com.devonfw.tools.ide.context.IdeContext;
+import com.devonfw.tools.ide.context.IdeContextConsole;
import com.devonfw.tools.ide.context.IdeStartContextImpl;
import com.devonfw.tools.ide.log.IdeLogLevel;
import com.devonfw.tools.ide.log.IdeLogListenerBuffer;
@@ -62,23 +64,41 @@ public class GuiStateManager {
/**
+ * Creates the state manager for the given root directory, deriving the {@link NlsService} context from the GUI's own start context.
+ *
* @param ideRoot the root directory of IDEasy. If null, the IDE_ROOT environment variable is used.
*/
public GuiStateManager(String ideRoot) {
+ // production derives the NlsService context from the GUI start context (which resolves the real user configuration); tests inject an isolated context
+ this(ideRoot, null);
+ }
+
+ /**
+ * Creates the state manager for the given root directory using the supplied {@link NlsService} context.
+ *
+ * @param ideRoot the root directory of IDEasy. If null, the IDE_ROOT environment variable is used.
+ * @param nlsContext the {@link IdeContext} handed to the {@link NlsService}, or {@code null} to derive one from the GUI start context (production default).
+ * Injecting an isolated context (for example an {@link com.devonfw.tools.ide.context.IdeTestContext}) keeps the GUI test independent from the real
+ * user home and the {@code IDE_OPTIONS} environment variable.
+ */
+ public GuiStateManager(String ideRoot, IdeContext nlsContext) {
+
this.ideRootDir = Path.of(ideRoot != null ? ideRoot : System.getenv(IdeVariables.IDE_ROOT.getName()));
//This specific ordering needs to be kept
+ final IdeLogListenerBuffer buffer = new IdeLogListenerBuffer();
+ IdeLogLevel logLevel = IdeLogLevel.DEBUG;
+ startContext = new IdeStartContextImpl(logLevel, buffer);
+
this.projectManager = new ProjectManager(ideRootDir);
this.taskManager = new TaskManager();
this.eventBus = new GuiEventBus();
- this.nlsService = new NlsService(null);
+ // inject an application-level context so NlsService never has to create one itself; binding the default to startContext keeps the GUI logging context
+ IdeContext resolvedNlsContext = (nlsContext != null) ? nlsContext : new IdeContextConsole(startContext);
+ this.nlsService = new NlsService(resolvedNlsContext, null);
this.commandletService = new CommandletService(this);
this.tabFactory = new TabFactory(this);
-
- final IdeLogListenerBuffer buffer = new IdeLogListenerBuffer();
- IdeLogLevel logLevel = IdeLogLevel.DEBUG;
- startContext = new IdeStartContextImpl(logLevel, buffer);
}
/**
diff --git a/gui/src/main/java/com/devonfw/ide/gui/core/service/NlsService.java b/gui/src/main/java/com/devonfw/ide/gui/core/service/NlsService.java
index 570347eed9..a11b7d22b1 100644
--- a/gui/src/main/java/com/devonfw/ide/gui/core/service/NlsService.java
+++ b/gui/src/main/java/com/devonfw/ide/gui/core/service/NlsService.java
@@ -23,7 +23,6 @@
import org.slf4j.LoggerFactory;
import com.devonfw.tools.ide.context.IdeContext;
-import com.devonfw.tools.ide.context.IdeContextConsole;
import com.devonfw.tools.ide.environment.EnvironmentVariables;
import com.devonfw.tools.ide.environment.EnvironmentVariablesType;
@@ -59,32 +58,17 @@ public class NlsService {
private volatile EnvironmentVariables userEnvironmentVariables;
- /**
- * The {@link IdeContext} used to resolve the user environment variables. If {@code null}, an {@link IdeContextConsole} is created lazily on first
- * persistence, so that merely constructing the service has no side effects.
- */
+ /** The {@link IdeContext} used to resolve the user environment variables. */
private final IdeContext context;
private static final Pattern USER_LANG_PATTERN = Pattern.compile("-Duser\\.language=(\\S*)");
- /**
- * Creates the service and initializes it with the given locale. The user environment variables are resolved from an {@link IdeContextConsole} that is created
- * lazily on first persistence, so that merely constructing the service has no side effects.
- *
- * @param locale the preferred locale, or {@code null} to use persisted or default locale.
- */
- public NlsService(Locale locale) {
-
- this.context = null;
- initialize(locale);
- }
-
/**
* Creates the service using the given {@link IdeContext} to resolve the user configuration and initializes it with the given locale.
*
- * This constructor enables "design for testability" (see {@code documentation/contributing/junit-testing.adoc}): by injecting an {@link IdeContext} (for - * example an {@link com.devonfw.tools.ide.context.IdeTestContext}) the service can be isolated from the real user home and the environment of the machine - * running the test. + * The {@link IdeContext} is always injected by the caller (see the {@code documentation/contributing/junit-testing.adoc} guidance): production code + * passes an application-level context that resolves the real user configuration, while tests pass an isolated {@code IdeTestContext} to stay independent + * from the machine's user home and the {@code IDE_OPTIONS} environment variable. * * @param context the {@link IdeContext} to use for resolving the user environment variables. * @param locale the preferred locale, or {@code null} to use persisted or default locale. @@ -388,9 +372,7 @@ private EnvironmentVariables getUserEnvironmentVariables() { private EnvironmentVariables createUserEnvironmentVariables() { - // if no context was injected (see NlsService(Locale)) we create a console context lazily here, so constructing the service has no side effects - IdeContext context = (this.context != null) ? this.context : new IdeContextConsole(); - EnvironmentVariables userVariables = context.getVariables().getByType(EnvironmentVariablesType.USER); + EnvironmentVariables userVariables = this.context.getVariables().getByType(EnvironmentVariablesType.USER); if (userVariables == null) { throw new IllegalStateException("Failed to resolve USER environment variables from IDE context"); } diff --git a/gui/src/test/java/com/devonfw/ide/gui/AppBaseTest.java b/gui/src/test/java/com/devonfw/ide/gui/AppBaseTest.java index 139cb3e4c1..551bdde653 100644 --- a/gui/src/test/java/com/devonfw/ide/gui/AppBaseTest.java +++ b/gui/src/test/java/com/devonfw/ide/gui/AppBaseTest.java @@ -20,6 +20,7 @@ import org.assertj.core.data.Offset; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import com.devonfw.ide.gui.core.context.GuiStateManager; import com.devonfw.ide.gui.core.context.TaskManager; @@ -28,6 +29,8 @@ import com.devonfw.ide.gui.core.mainwindow.MainWindowViewModel; import com.devonfw.ide.gui.core.progress.ProgressBarTask; import com.devonfw.ide.gui.core.progress.taskwindow.TaskOverviewWindow; +import com.devonfw.tools.ide.context.IdeTestContext; +import com.devonfw.tools.ide.environment.IdeSystemTestImpl; /** * Basic UI Test for the main screen @@ -58,13 +61,22 @@ public class AppBaseTest extends UIBasedApplicationTest { private MainWindowViewModel viewModel; + /** Throw-away user home for the isolated NlsService context so the persisted locale never rewrites the real {@code ~/.ide/ide.properties}. */ + @TempDir + private static Path nlsUserHome; + @Override public void start(Stage stage) { - //We simply use project-0 here, as we only use the ide root for this test + // We simply use project-0 here, as we only use the ide root for this test setTestContext("testProject", "project-0"); - this.guiStateManager = new GuiStateManager(getTestContext().getIdeRoot().toString()); + // isolate the NlsService from the developer's real user home and the IDE_OPTIONS environment variable: persisting the locale must not rewrite the + // real ~/.ide/ide.properties, so point an isolated IdeTestContext at a throw-away home and hand it to the NlsService via the state manager + final IdeTestContext nlsContext = new IdeTestContext(); + nlsContext.setSystem(new IdeSystemTestImpl()); + nlsContext.setUserHome(nlsUserHome); + this.guiStateManager = new GuiStateManager(getTestContext().getIdeRoot().toString(), nlsContext); this.guiStateManager.getNlsService().setLocale(Locale.ENGLISH); this.taskManager = guiStateManager.getTaskManager(); this.viewModel = new MainWindowViewModel(guiStateManager); diff --git a/gui/src/test/java/com/devonfw/ide/gui/core/mainwindow/console/ConsolePanelTest.java b/gui/src/test/java/com/devonfw/ide/gui/core/mainwindow/console/ConsolePanelTest.java index 50d101487f..dc0c24021a 100644 --- a/gui/src/test/java/com/devonfw/ide/gui/core/mainwindow/console/ConsolePanelTest.java +++ b/gui/src/test/java/com/devonfw/ide/gui/core/mainwindow/console/ConsolePanelTest.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.net.URL; +import java.nio.file.Path; import java.util.List; import java.util.Locale; @@ -15,15 +16,22 @@ import javafx.stage.Stage; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import com.devonfw.ide.gui.UIBasedApplicationTest; import com.devonfw.ide.gui.core.context.IdeGuiLogListener; import com.devonfw.ide.gui.core.event.GuiEventBus; import com.devonfw.ide.gui.core.service.NlsService; +import com.devonfw.tools.ide.context.IdeTestContext; +import com.devonfw.tools.ide.environment.IdeSystemTestImpl; import com.devonfw.tools.ide.log.IdeLogLevel; class ConsolePanelTest extends UIBasedApplicationTest { + /** Throw-away user home for the isolated NlsService context so a persisted locale never rewrites the real {@code ~/.ide/ide.properties}. */ + @TempDir + private static Path nlsUserHome; + private ConsoleViewModel consoleViewModel; private GuiEventBus eventBus; @@ -34,7 +42,11 @@ public void start(Stage stage) throws IOException { URL consoleViewUrl = getClass().getResource("console.fxml"); assertThat(consoleViewUrl).as("Cannot resolve console UI FXML resource!").isNotNull(); - NlsService nlsService = new NlsService(Locale.ENGLISH); + // isolate the NlsService from the developer's real user home and the IDE_OPTIONS environment variable so constructing it has no side effects + final IdeTestContext nlsContext = new IdeTestContext(); + nlsContext.setSystem(new IdeSystemTestImpl()); + nlsContext.setUserHome(nlsUserHome); + NlsService nlsService = new NlsService(nlsContext, Locale.ENGLISH); this.eventBus = new GuiEventBus(); FXMLLoader fxmlLoader = new FXMLLoader(consoleViewUrl); diff --git a/gui/src/test/java/com/devonfw/ide/gui/core/service/NlsServiceTest.java b/gui/src/test/java/com/devonfw/ide/gui/core/service/NlsServiceTest.java index 3385dcdf78..83fa1ec6dc 100644 --- a/gui/src/test/java/com/devonfw/ide/gui/core/service/NlsServiceTest.java +++ b/gui/src/test/java/com/devonfw/ide/gui/core/service/NlsServiceTest.java @@ -87,7 +87,7 @@ private NlsService newService(Locale locale) { @MethodSource("testGetInstanceWithLocaleProvider") public void testGetInstanceWithLocale(Locale systemLocale, Locale providedLocale, Locale expectedServiceLocale) { Locale.setDefault(systemLocale); - NlsService service = new NlsService(providedLocale); + NlsService service = newService(providedLocale); assertThat(service.getLocale()).isEqualTo(expectedServiceLocale); assertThat(service.getResourceBundle()).isNotNull();