8377897: jpackage: make jdk.jpackage.internal.MockUtils available from other packages

Reviewed-by: almatvee
This commit is contained in:
Alexey Semenyuk 2026-02-23 17:34:55 +00:00
parent 1ae2fee007
commit acde30e0ab
19 changed files with 331 additions and 130 deletions

View File

@ -30,6 +30,7 @@ import static jdk.jpackage.internal.LinuxPackagingPipeline.APPLICATION_LAYOUT;
import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_LINUX_APP_IMAGE;
import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_LINUX_DEB;
import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_LINUX_RPM;
import static jdk.jpackage.internal.util.MemoizingSupplier.runOnce;
import java.util.Map;
import java.util.Optional;

View File

@ -25,6 +25,7 @@
package jdk.jpackage.internal;
import static java.util.stream.Collectors.toMap;
import static jdk.jpackage.internal.util.MemoizingSupplier.runOnce;
import java.io.IOException;
import java.io.UncheckedIOException;
@ -36,7 +37,6 @@ import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
@ -50,6 +50,7 @@ import jdk.jpackage.internal.model.Application;
import jdk.jpackage.internal.model.BundlingOperationDescriptor;
import jdk.jpackage.internal.model.JPackageException;
import jdk.jpackage.internal.model.Package;
import jdk.jpackage.internal.util.MemoizingSupplier;
import jdk.jpackage.internal.util.PathUtils;
import jdk.jpackage.internal.util.Result;
@ -66,7 +67,7 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment {
return runOnce(e.getValue());
}));
this.defaultOperationSupplier = Objects.requireNonNull(defaultOperationSupplier).map(DefaultBundlingEnvironment::runOnce);
this.defaultOperationSupplier = Objects.requireNonNull(defaultOperationSupplier).map(MemoizingSupplier::runOnce);
}
@ -110,10 +111,6 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment {
return new Builder();
}
static <T> Supplier<T> runOnce(Supplier<T> supplier) {
return new CachingSupplier<>(supplier);
}
static <T extends SystemEnvironment> Supplier<Result<Consumer<Options>>> createBundlerSupplier(
Supplier<Result<T>> sysEnvResultSupplier, BiConsumer<Options, T> bundler) {
Objects.requireNonNull(sysEnvResultSupplier);
@ -223,25 +220,6 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment {
});
}
private static final class CachingSupplier<T> implements Supplier<T> {
CachingSupplier(Supplier<T> getter) {
this.getter = Objects.requireNonNull(getter);
}
@Override
public T get() {
return cachedValue.updateAndGet(v -> {
return Optional.ofNullable(v).orElseGet(getter);
});
}
private final Supplier<T> getter;
private final AtomicReference<T> cachedValue = new AtomicReference<>();
}
private final Map<BundlingOperationDescriptor, Supplier<Result<Consumer<Options>>>> bundlers;
private final Optional<Supplier<Optional<BundlingOperationDescriptor>>> defaultOperationSupplier;
}

View File

@ -49,7 +49,7 @@ import jdk.jpackage.internal.util.CommandOutputControl.Result;
import jdk.jpackage.internal.util.RetryExecutor;
import jdk.jpackage.internal.util.function.ExceptionBox;
final class Executor {
public final class Executor {
static Executor of(String... cmdline) {
return of(List.of(cmdline));
@ -78,118 +78,118 @@ final class Executor {
mapper = other.mapper;
}
Executor saveOutput(boolean v) {
public Executor saveOutput(boolean v) {
commandOutputControl.saveOutput(v);
return this;
}
Executor saveOutput() {
public Executor saveOutput() {
return saveOutput(true);
}
Executor saveFirstLineOfOutput() {
public Executor saveFirstLineOfOutput() {
commandOutputControl.saveFirstLineOfOutput();
return this;
}
Executor charset(Charset v) {
public Executor charset(Charset v) {
commandOutputControl.charset(v);
return this;
}
Executor storeOutputInFiles(boolean v) {
public Executor storeOutputInFiles(boolean v) {
commandOutputControl.storeOutputInFiles(v);
return this;
}
Executor storeOutputInFiles() {
public Executor storeOutputInFiles() {
return storeOutputInFiles(true);
}
Executor binaryOutput(boolean v) {
public Executor binaryOutput(boolean v) {
commandOutputControl.binaryOutput(v);
return this;
}
Executor binaryOutput() {
public Executor binaryOutput() {
return binaryOutput(true);
}
Executor discardStdout(boolean v) {
public Executor discardStdout(boolean v) {
commandOutputControl.discardStdout(v);
return this;
}
Executor discardStdout() {
public Executor discardStdout() {
return discardStdout(true);
}
Executor discardStderr(boolean v) {
public Executor discardStderr(boolean v) {
commandOutputControl.discardStderr(v);
return this;
}
Executor discardStderr() {
public Executor discardStderr() {
return discardStderr(true);
}
Executor timeout(long v, TimeUnit unit) {
public Executor timeout(long v, TimeUnit unit) {
return timeout(Duration.of(v, unit.toChronoUnit()));
}
Executor timeout(Duration v) {
public Executor timeout(Duration v) {
timeout = v;
return this;
}
Executor toolProvider(ToolProvider v) {
public Executor toolProvider(ToolProvider v) {
toolProvider = Objects.requireNonNull(v);
processBuilder = null;
return this;
}
Optional<ToolProvider> toolProvider() {
public Optional<ToolProvider> toolProvider() {
return Optional.ofNullable(toolProvider);
}
Executor processBuilder(ProcessBuilder v) {
public Executor processBuilder(ProcessBuilder v) {
processBuilder = Objects.requireNonNull(v);
toolProvider = null;
return this;
}
Optional<ProcessBuilder> processBuilder() {
public Optional<ProcessBuilder> processBuilder() {
return Optional.ofNullable(processBuilder);
}
Executor args(List<String> v) {
public Executor args(List<String> v) {
args.addAll(v);
return this;
}
Executor args(String... args) {
public Executor args(String... args) {
return args(List.of(args));
}
List<String> args() {
public List<String> args() {
return args;
}
Executor setQuiet(boolean v) {
public Executor setQuiet(boolean v) {
quietCommand = v;
return this;
}
Executor mapper(UnaryOperator<Executor> v) {
public Executor mapper(UnaryOperator<Executor> v) {
mapper = v;
return this;
}
Optional<UnaryOperator<Executor>> mapper() {
public Optional<UnaryOperator<Executor>> mapper() {
return Optional.ofNullable(mapper);
}
Executor copy() {
public Executor copy() {
return new Executor(this);
}
@ -261,7 +261,7 @@ final class Executor {
});
}
List<String> commandLine() {
public List<String> commandLine() {
if (processBuilder != null) {
return Stream.of(processBuilder.command(), args).flatMap(Collection::stream).toList();
} else if (toolProvider != null) {

View File

@ -25,9 +25,9 @@
package jdk.jpackage.internal;
@FunctionalInterface
interface ExecutorFactory {
public interface ExecutorFactory {
Executor executor();
static final ExecutorFactory DEFAULT = Executor::new;
public static final ExecutorFactory DEFAULT = Executor::new;
}

View File

@ -33,17 +33,17 @@ public final class Globals {
private Globals() {
}
Globals objectFactory(ObjectFactory v) {
public Globals objectFactory(ObjectFactory v) {
checkMutable();
objectFactory = Optional.ofNullable(v).orElse(ObjectFactory.DEFAULT);
return this;
}
ObjectFactory objectFactory() {
public ObjectFactory objectFactory() {
return objectFactory;
}
Globals executorFactory(ExecutorFactory v) {
public Globals executorFactory(ExecutorFactory v) {
return objectFactory(ObjectFactory.build(objectFactory).executorFactory(v).create());
}

View File

@ -28,38 +28,38 @@ import java.util.Objects;
import java.util.Optional;
import jdk.jpackage.internal.util.CompositeProxy;
interface ObjectFactory extends ExecutorFactory, RetryExecutorFactory {
public interface ObjectFactory extends ExecutorFactory, RetryExecutorFactory {
static ObjectFactory.Builder build() {
public static ObjectFactory.Builder build() {
return new Builder();
}
static ObjectFactory.Builder build(ObjectFactory from) {
public static ObjectFactory.Builder build(ObjectFactory from) {
return build().initFrom(from);
}
static final class Builder {
public static final class Builder {
private Builder() {
}
ObjectFactory create() {
public ObjectFactory create() {
return CompositeProxy.build().invokeTunnel(CompositeProxyTunnel.INSTANCE).create(
ObjectFactory.class,
Optional.ofNullable(executorFactory).orElse(ExecutorFactory.DEFAULT),
Optional.ofNullable(retryExecutorFactory).orElse(RetryExecutorFactory.DEFAULT));
}
Builder initFrom(ObjectFactory of) {
public Builder initFrom(ObjectFactory of) {
Objects.requireNonNull(of);
return executorFactory(of).retryExecutorFactory(of);
}
Builder executorFactory(ExecutorFactory v) {
public Builder executorFactory(ExecutorFactory v) {
executorFactory = v;
return this;
}
Builder retryExecutorFactory(RetryExecutorFactory v) {
public Builder retryExecutorFactory(RetryExecutorFactory v) {
retryExecutorFactory = v;
return this;
}
@ -68,5 +68,5 @@ interface ObjectFactory extends ExecutorFactory, RetryExecutorFactory {
private RetryExecutorFactory retryExecutorFactory;
}
static final ObjectFactory DEFAULT = build().create();
public static final ObjectFactory DEFAULT = build().create();
}

View File

@ -27,9 +27,9 @@ package jdk.jpackage.internal;
import jdk.jpackage.internal.util.RetryExecutor;
@FunctionalInterface
interface RetryExecutorFactory {
public interface RetryExecutorFactory {
<T, E extends Exception> RetryExecutor<T, E> retryExecutor(Class<? extends E> exceptionType);
static final RetryExecutorFactory DEFAULT = RetryExecutor::new;
public static final RetryExecutorFactory DEFAULT = RetryExecutor::new;
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jpackage.internal.util;
import jdk.jpackage.internal.util.function.ExceptionBox;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.function.Supplier;
public final class MemoizingSupplier<T> implements Supplier<T> {
public MemoizingSupplier(Supplier<T> supplier) {
this.future = new FutureTask<>(Objects.requireNonNull(supplier)::get);
}
@Override
public T get() {
try {
future.run();
return future.get();
} catch (InterruptedException ex) {
throw ExceptionBox.toUnchecked(ex);
} catch (ExecutionException ex) {
throw ExceptionBox.toUnchecked(ExceptionBox.unbox(ex.getCause()));
}
}
public static <T> Supplier<T> runOnce(Supplier<T> supplier) {
return new MemoizingSupplier<>(supplier);
}
private final FutureTask<T> future;
}

View File

@ -29,6 +29,7 @@ import static jdk.jpackage.internal.WinPackagingPipeline.APPLICATION_LAYOUT;
import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_WIN_APP_IMAGE;
import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_WIN_EXE;
import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_WIN_MSI;
import static jdk.jpackage.internal.util.MemoizingSupplier.runOnce;
import jdk.jpackage.internal.cli.Options;

View File

@ -21,18 +21,27 @@
* questions.
*/
package jdk.jpackage.internal;
package jdk.jpackage.test.stdmock;
import static jdk.jpackage.internal.util.MemoizingSupplier.runOnce;
import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.spi.ToolProvider;
import java.util.stream.Collectors;
import jdk.internal.util.OperatingSystem;
import jdk.jpackage.internal.Executor;
import jdk.jpackage.internal.ExecutorFactory;
import jdk.jpackage.internal.Globals;
import jdk.jpackage.internal.ObjectFactory;
import jdk.jpackage.internal.cli.CliBundlingEnvironment;
import jdk.jpackage.internal.cli.Main;
import jdk.jpackage.internal.util.function.ExceptionBox;
@ -41,11 +50,11 @@ import jdk.jpackage.test.mock.ToolProviderCommandMock;
import jdk.jpackage.test.mock.VerbatimCommandMock;
/**
* Bridges "jdk.jpackage.internal" and "jdk.jpackage.test.mock" packages.
* Utilities to create jpackage mock.
*/
public final class MockUtils {
public final class JPackageMockUtils {
private MockUtils() {
private JPackageMockUtils() {
}
public static JPackageToolProviderBuilder buildJPackage() {
@ -58,30 +67,23 @@ public final class MockUtils {
return createJPackageToolProvider(os(), createObjectFactory());
}
public Consumer<Globals> createGlobalsMutator() {
var objectFactory = createObjectFactory();
return globals -> {
globals.objectFactory(objectFactory);
};
}
public void applyToGlobals() {
createGlobalsMutator().accept(Globals.instance());
Globals.instance().objectFactory(createObjectFactory());
}
ExecutorFactory createExecutorFactory() {
var commandMocksExecutorFactory = Optional.ofNullable(script).map(MockUtils::withCommandMocks).map(mapper -> {
public ExecutorFactory createExecutorFactory() {
var commandMocksExecutorFactory = Optional.ofNullable(script).map(JPackageMockUtils::withCommandMocks).map(mapper -> {
return mapper.apply(ExecutorFactory.DEFAULT);
}).orElse(ExecutorFactory.DEFAULT);
var recordingExecutorFactory = Optional.ofNullable(listener).map(MockUtils::withCommandListener).map(mapper -> {
var recordingExecutorFactory = Optional.ofNullable(listener).map(JPackageMockUtils::withCommandListener).map(mapper -> {
return mapper.apply(commandMocksExecutorFactory);
}).orElse(commandMocksExecutorFactory);
return recordingExecutorFactory;
}
ObjectFactory createObjectFactory() {
public ObjectFactory createObjectFactory() {
var executorFactory = createExecutorFactory();
if (executorFactory == ExecutorFactory.DEFAULT) {
return ObjectFactory.DEFAULT;
@ -125,6 +127,31 @@ public final class MockUtils {
return createJPackageToolProvider(OperatingSystem.current(), script);
}
public static Map<OperatingSystem, Supplier<CliBundlingEnvironment>> availableBundlingEnvironments() {
return Map.ofEntries(
Map.entry(OperatingSystem.WINDOWS, "WinBundlingEnvironment"),
Map.entry(OperatingSystem.LINUX, "LinuxBundlingEnvironment"),
Map.entry(OperatingSystem.MACOS, "MacBundlingEnvironment")
).entrySet().stream().map(e -> {
Constructor<?> ctor;
try {
ctor = Class.forName("jdk.jpackage.internal." + e.getValue()).getConstructor();
} catch (NoSuchMethodException | SecurityException ex) {
throw ExceptionBox.toUnchecked(ex);
} catch (ClassNotFoundException ex) {
return Optional.<Map.Entry<OperatingSystem, Supplier<CliBundlingEnvironment>>>empty();
}
return Optional.of(Map.entry(e.getKey(), toSupplier(() -> {
return (CliBundlingEnvironment)ctor.newInstance();
})));
}).flatMap(Optional::stream).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
public static CliBundlingEnvironment createBundlingEnvironment(OperatingSystem os) {
Objects.requireNonNull(os);
return Objects.requireNonNull(availableBundlingEnvironments().get(os)).get();
}
private static UnaryOperator<ExecutorFactory> withCommandListener(Consumer<List<String>> listener) {
Objects.requireNonNull(listener);
return executorFactory -> {
@ -180,39 +207,11 @@ public final class MockUtils {
};
}
public static CliBundlingEnvironment createBundlingEnvironment(OperatingSystem os) {
Objects.requireNonNull(os);
String bundlingEnvironmentClassName;
switch (os) {
case WINDOWS -> {
bundlingEnvironmentClassName = "WinBundlingEnvironment";
}
case LINUX -> {
bundlingEnvironmentClassName = "LinuxBundlingEnvironment";
}
case MACOS -> {
bundlingEnvironmentClassName = "MacBundlingEnvironment";
}
default -> {
throw new IllegalArgumentException();
}
}
return toSupplier(() -> {
var ctor = Class.forName(String.join(".",
DefaultBundlingEnvironment.class.getPackageName(),
bundlingEnvironmentClassName
)).getConstructor();
return (CliBundlingEnvironment)ctor.newInstance();
}).get();
}
static ToolProvider createJPackageToolProvider(OperatingSystem os, ObjectFactory of) {
private static ToolProvider createJPackageToolProvider(OperatingSystem os, ObjectFactory of) {
Objects.requireNonNull(os);
Objects.requireNonNull(of);
var impl = new Main.Provider(DefaultBundlingEnvironment.runOnce(() -> {
var impl = new Main.Provider(runOnce(() -> {
return createBundlingEnvironment(os);
}));

View File

@ -37,6 +37,7 @@ import jdk.jpackage.test.mock.CommandActionSpecs;
import jdk.jpackage.test.mock.CommandMockExit;
import jdk.jpackage.test.mock.CommandMockSpec;
import jdk.jpackage.test.mock.Script;
import jdk.jpackage.test.stdmock.JPackageMockUtils;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
@ -137,7 +138,7 @@ public class LinuxPackageArchTest {
Globals.main(() -> {
MockUtils.buildJPackage().script(script).applyToGlobals();
JPackageMockUtils.buildJPackage().script(script).applyToGlobals();
Result<LinuxPackageArch> arch = LinuxPackageArch.create(pkgType);

View File

@ -34,6 +34,7 @@ import jdk.jpackage.test.mock.CommandActionSpecs;
import jdk.jpackage.test.mock.CommandMockExit;
import jdk.jpackage.test.mock.CommandMockSpec;
import jdk.jpackage.test.mock.Script;
import jdk.jpackage.test.stdmock.JPackageMockUtils;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
@ -86,7 +87,7 @@ public class LinuxSystemEnvironmentTest {
Globals.main(() -> {
MockUtils.buildJPackage().script(script).applyToGlobals();
JPackageMockUtils.buildJPackage().script(script).applyToGlobals();
var actual = LinuxSystemEnvironment.detectNativePackageType();

View File

@ -36,9 +36,9 @@
* @requires (os.family == "linux")
* @library /test/jdk/tools/jpackage/helpers
* @build jdk.jpackage.test.mock.*
* @build jdk.jpackage.test.stdmock.*
* @compile/module=jdk.jpackage -Xlint:all -Werror
* jdk/jpackage/internal/LinuxSystemEnvironmentTest.java
* ../../share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java
* @run junit jdk.jpackage/jdk.jpackage.internal.LinuxSystemEnvironmentTest
*/
@ -57,8 +57,8 @@
* @requires (os.family == "linux")
* @library /test/jdk/tools/jpackage/helpers
* @build jdk.jpackage.test.mock.*
* @build jdk.jpackage.test.stdmock.*
* @compile/module=jdk.jpackage -Xlint:all -Werror
* jdk/jpackage/internal/LinuxPackageArchTest.java
* ../../share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java
* @run junit jdk.jpackage/jdk.jpackage.internal.LinuxPackageArchTest
*/

View File

@ -56,6 +56,7 @@ import jdk.jpackage.test.mock.CommandMockSpec;
import jdk.jpackage.test.mock.MockIllegalStateException;
import jdk.jpackage.test.mock.ScriptSpec;
import jdk.jpackage.test.mock.ScriptSpecInDir;
import jdk.jpackage.test.stdmock.JPackageMockUtils;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
@ -240,7 +241,7 @@ public class MacDmgPackagerTest {
var script = dir(Objects.requireNonNull(workDir)).create();
ExecutorFactory executorFactory = MockUtils.buildJPackage()
ExecutorFactory executorFactory = JPackageMockUtils.buildJPackage()
.script(script).listener(System.out::println).createExecutorFactory();
var objectFactory = ObjectFactory.build()
@ -406,14 +407,14 @@ public class MacDmgPackagerTest {
}
}
private final static BiConsumer<Path, Consumer<Path>> EXPAND_PATH = (path, sink) -> {
private static final BiConsumer<Path, Consumer<Path>> EXPAND_PATH = (path, sink) -> {
do {
sink.accept(path);
path = path.getParent();
} while (path != null);
};
private final static List<Path> DMG_ICON_FILES = Stream.of(
private static final List<Path> DMG_ICON_FILES = Stream.of(
".VolumeIcon.icns",
".background/background.tiff"
).map(Path::of).collect(Collectors.toUnmodifiableList());

View File

@ -32,11 +32,11 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import jdk.jpackage.internal.util.RetryExecutor;
import jdk.jpackage.test.mock.CommandActionSpecs;
import jdk.jpackage.test.mock.CommandMockExit;
import jdk.jpackage.test.mock.CommandMockSpec;
import jdk.jpackage.test.mock.Script;
import jdk.jpackage.test.stdmock.JPackageMockUtils;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
@ -143,7 +143,7 @@ public class MacDmgSystemEnvironmentTest {
}).createSequence();
Globals.main(() -> {
MockUtils.buildJPackage().script(script).applyToGlobals();
JPackageMockUtils.buildJPackage().script(script).applyToGlobals();
var actual = MacDmgSystemEnvironment.findSetFileUtility();

View File

@ -36,9 +36,9 @@
* @requires (os.family == "mac")
* @library /test/jdk/tools/jpackage/helpers
* @build jdk.jpackage.test.mock.*
* @build jdk.jpackage.test.stdmock.*
* @compile/module=jdk.jpackage -Xlint:all -Werror
* jdk/jpackage/internal/MacDmgSystemEnvironmentTest.java
* ../../share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java
* @run junit jdk.jpackage/jdk.jpackage.internal.MacDmgSystemEnvironmentTest
*/
@ -47,8 +47,8 @@
* @requires (os.family == "mac")
* @library /test/jdk/tools/jpackage/helpers
* @build jdk.jpackage.test.mock.*
* @build jdk.jpackage.test.stdmock.*
* @compile/module=jdk.jpackage -Xlint:all -Werror
* jdk/jpackage/internal/MacDmgPackagerTest.java
* ../../share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java
* @run junit jdk.jpackage/jdk.jpackage.internal.MacDmgPackagerTest
*/

View File

@ -54,6 +54,7 @@ import jdk.jpackage.test.mock.CommandActionSpecs;
import jdk.jpackage.test.mock.CommandMock;
import jdk.jpackage.test.mock.CommandMockExit;
import jdk.jpackage.test.mock.Script;
import jdk.jpackage.test.stdmock.JPackageMockUtils;
import org.junit.jupiter.api.Test;
@ -96,7 +97,7 @@ public class DefaultBundlingEnvironmentTest extends JUnitAdapter {
var script = createMockScript(op);
ToolProvider jpackage = MockUtils.buildJPackage()
ToolProvider jpackage = JPackageMockUtils.buildJPackage()
.os(op.os())
.script(script)
.listener(executedCommands::add).create();

View File

@ -50,7 +50,6 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.internal.util.OperatingSystem;
import jdk.jpackage.internal.Globals;
import jdk.jpackage.internal.MockUtils;
import jdk.jpackage.internal.model.ConfigException;
import jdk.jpackage.internal.model.ExecutableAttributesWithCapturedOutput;
import jdk.jpackage.internal.model.JPackageException;
@ -67,6 +66,7 @@ import jdk.jpackage.test.TKit;
import jdk.jpackage.test.mock.CommandActionSpecs;
import jdk.jpackage.test.mock.Script;
import jdk.jpackage.test.mock.VerbatimCommandMock;
import jdk.jpackage.test.stdmock.JPackageMockUtils;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
@ -124,9 +124,8 @@ public class MainTest extends JUnitAdapter {
var jpackageToolProviderMock = new ToolProvider() {
@Override
public int run(PrintWriter out, PrintWriter err, String... args) {
var globalsMutator = MockUtils.buildJPackage().script(script).createGlobalsMutator();
return Globals.main(() -> {
globalsMutator.accept(Globals.instance());
return Globals.main(() -> {
JPackageMockUtils.buildJPackage().script(script).applyToGlobals();
var result = ExecutionResult.create(args);

View File

@ -0,0 +1,163 @@
/*
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jpackage.internal.util;
import static jdk.jpackage.internal.util.function.ThrowingRunnable.toRunnable;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.function.Supplier;
import java.util.stream.IntStream;
import org.junit.jupiter.api.Test;
class MemoizingSupplierTest {
@Test
void test() {
var supplier = count(() -> "foo");
var runOnceSupplier = MemoizingSupplier.runOnce(supplier);
assertEquals(0, supplier.counter());
assertEquals("foo", runOnceSupplier.get());
assertEquals("foo", runOnceSupplier.get());
assertEquals(1, supplier.counter());
}
@Test
void test_null() {
CountingSupplier<String> supplier = count(() -> null);
var runOnceSupplier = MemoizingSupplier.runOnce(supplier);
assertEquals(0, supplier.counter());
assertEquals(null, runOnceSupplier.get());
assertEquals(null, runOnceSupplier.get());
assertEquals(1, supplier.counter());
}
@Test
void test_throws_Exception() {
CountingSupplier<String> supplier = count(() -> {
throw new IllegalStateException("Kaput!");
});
var runOnceSupplier = MemoizingSupplier.runOnce(supplier);
assertEquals(0, supplier.counter());
assertThrowsExactly(IllegalStateException.class, () -> {
runOnceSupplier.get();
});
assertThrowsExactly(IllegalStateException.class, () -> {
runOnceSupplier.get();
});
assertEquals(1, supplier.counter());
}
@Test
void test_throws_Error() {
CountingSupplier<String> supplier = count(() -> {
throw new Error("Grand kaput!");
});
var runOnceSupplier = MemoizingSupplier.runOnce(supplier);
assertEquals(0, supplier.counter());
assertThrowsExactly(Error.class, () -> {
runOnceSupplier.get();
});
assertThrowsExactly(Error.class, () -> {
runOnceSupplier.get();
});
assertEquals(1, supplier.counter());
}
@Test
void testAsync() throws InterruptedException {
var supplier = count(() -> "foo");
var runOnceSupplier = MemoizingSupplier.runOnce(supplier);
final var supplierCount = 100;
final var supplierExecutor = Executors.newVirtualThreadPerTaskExecutor();
// Schedule invoking "runOnceSupplier.get()" in a separate virtual threads.
// Start and suspend threads, waiting until all scheduled threads have started.
// After all scheduled threads start, resume them.
// This should result in multiple simultaneous "runOnceSupplier.get()" calls.
var readyLatch = new CountDownLatch(supplierCount);
var startLatch = new CountDownLatch(1);
var futures = IntStream.range(0, supplierCount).mapToObj(_ -> {
return CompletableFuture.runAsync(toRunnable(() -> {
readyLatch.countDown();
startLatch.await();
runOnceSupplier.get();
}), supplierExecutor);
}).toList();
readyLatch.await();
startLatch.countDown();
CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join();
assertEquals(1, supplier.counter());
}
private static <T> CountingSupplier<T> count(Supplier<T> supplier) {
return new CountingSupplier<>(supplier);
}
private static final class CountingSupplier<T> implements Supplier<T> {
CountingSupplier(Supplier<T> impl) {
this.impl = Objects.requireNonNull(impl);
}
@Override
public T get() {
counter++;
return impl.get();
}
int counter() {
return counter;
}
private int counter;
private final Supplier<T> impl;
}
}