/*
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions copyright [year] [name of copyright owner]".
*
* Copyright 2026 3A Systems, LLC.
*/
package org.opends.quicksetup;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertSame;
import static org.testng.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import org.forgerock.i18n.LocalizableMessage;
import org.opends.server.DirectoryServerTestCase;
import org.opends.server.TestCaseUtils;
import org.testng.SkipException;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.forgerock.opendj.cli.ArgumentParser;
/**
* Tests when a launcher creates its temporary log file.
*
* Nothing removes that file unless the operation it belongs to succeeds, so a road which
* attempts nothing - {@code setup --help}, {@code --version}, a usage error - must not create
* one, nor the directory it would live in (issue #1030).
*/
@SuppressWarnings("javadoc")
@Test(groups = { "precommit", "quicksetup" }, sequential = true)
public class LauncherTest extends DirectoryServerTestCase
{
private static final String PREFIX = "opendj-setup-";
private File tempDir;
private final List created = new ArrayList<>();
@BeforeClass
public void setUp() throws IOException
{
tempDir = TestCaseUtils.createTemporaryDirectory("launcherTest");
}
@AfterClass
public void tearDown() throws IOException
{
for (TempLogFile logFile : created)
{
logFile.deleteLogFileAfterSuccess();
}
TestCaseUtils.deleteDirectory(tempDir);
}
@Test
public void testBuildingALauncherLeavesNothingOnDisk() throws Exception
{
final File instance = new File(tempDir, "not-yet-laid-down");
final File logs = new File(instance, "logs");
final TestLauncher launcher = new TestLauncher(logs);
assertFalse(launcher.hasTempLogFile(), "the log must wait for a road that can fail");
assertFalse(logs.exists(), logs.getPath());
assertFalse(instance.exists(), instance.getPath());
}
@Test
public void testAskingForTheLogCreatesItOnce() throws Exception
{
final File logs = new File(tempDir, "asked-for/logs");
final TestLauncher launcher = new TestLauncher(logs);
final TempLogFile logFile = launcher.getTempLogFile();
created.add(logFile);
assertTrue(logFile.isReadable(), logFile.getPath());
assertEquals(logFile.getLogFile().getCanonicalFile().getParentFile(), logs.getCanonicalFile());
assertTrue(launcher.hasTempLogFile());
assertSame(launcher.getTempLogFile(), logFile, "a second ask must not create a second log");
}
/**
* The wizard road: the splash screen comes up before the user has said anything, so the log
* cannot be created on the way to it - it is the application that asks for one, when it
* starts the install. A wizard quit at any step leaves nothing behind.
*/
@Test
public void testLaunchingTheGuiCreatesNoLog() throws Exception
{
final File instance = new File(tempDir, "quit-at-the-first-step");
final File logs = new File(instance, "logs");
final TestLauncher launcher = new TestLauncher(logs);
// The wizard behind the splash screen quits without installing anything.
launcher.launchGui(new String[0]);
assertFalse(launcher.hasTempLogFile(), "the splash screen must not cost a log");
assertFalse(logs.exists(), logs.getPath());
assertFalse(instance.exists(), instance.getPath());
// What the wizard was handed is the launcher's own log, made on the first ask.
final TempLogFile logFile = launcher.splashLogFile.get();
created.add(logFile);
assertTrue(logFile.isReadable(), logFile.getPath());
assertSame(logFile, launcher.getTempLogFile());
}
/**
* A GUI which does not come up costs no log either, and its reason is not lost for that: it
* goes into the log as soon as something asks for one - on the headless road, the operation
* which runs on the command line instead.
*/
@Test
public void testAGuiWhichDoesNotComeUpIsLoggedOnceThereIsALog() throws Exception
{
if ("true".equalsIgnoreCase(System.getenv("OPENDJ_LOG_TO_STDOUT")))
{
// The reason is written to the log, which then goes to stdout and leaves the file empty.
throw new SkipException("OPENDJ_LOG_TO_STDOUT writes the log to stdout, not to the file");
}
final File logs = new File(tempDir, "headless/logs");
final TestLauncher launcher = new TestLauncher(logs);
launcher.splashFailure = new IllegalStateException("no display at all");
assertTrue(launcher.launchGui(new String[0]) != 0, "a GUI which does not come up is reported");
assertFalse(launcher.hasTempLogFile(), "a GUI which does not come up must not cost a log");
assertFalse(logs.exists(), logs.getPath());
final TempLogFile logFile = launcher.getTempLogFile();
created.add(logFile);
logFile.writer.shutdown();
final String contents = logFile.readContents();
assertTrue(contents.contains("no display at all"), contents);
}
/** A launcher with nothing in it but the log file behaviour under test. */
private static final class TestLauncher extends Launcher
{
/** What {@link Launcher#launchGui(String[])} handed the splash screen. */
private Supplier splashLogFile;
/** What the splash screen throws, if it is not to come up. */
private RuntimeException splashFailure;
TestLauncher(final File tempLogFileDirectory)
{
super(new String[0], PREFIX, tempLogFileDirectory);
}
@Override
void startSplashScreen(final Supplier tempLogFile, final String[] args)
{
// No display here, and no install either: the wizard is quit at its first step - or it
// does not come up at all.
splashLogFile = tempLogFile;
if (splashFailure != null)
{
throw splashFailure;
}
}
@Override
public ArgumentParser getArgumentParser()
{
return null;
}
@Override
protected LocalizableMessage getFrameTitle()
{
return LocalizableMessage.raw("test");
}
@Override
protected CliApplication createCliApplication()
{
return null;
}
@Override
protected void willLaunchGui()
{
// nothing is launched here
}
@Override
protected void guiLaunchFailed()
{
// nothing is launched here
}
}
}