Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 <code>null</code>, 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 <code>null</code>, 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);
}

/**
Expand Down
28 changes: 5 additions & 23 deletions gui/src/main/java/com/devonfw/ide/gui/core/service/NlsService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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.
* <p>
* 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.
Expand Down Expand Up @@ -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");
}
Expand Down
16 changes: 14 additions & 2 deletions gui/src/test/java/com/devonfw/ide/gui/AppBaseTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Loading