From acde30e0ab9cd8fbb6d2e1193771a40fd370659a Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Mon, 23 Feb 2026 17:34:55 +0000 Subject: [PATCH] 8377897: jpackage: make jdk.jpackage.internal.MockUtils available from other packages Reviewed-by: almatvee --- .../internal/LinuxBundlingEnvironment.java | 1 + .../internal/DefaultBundlingEnvironment.java | 28 +-- .../jdk/jpackage/internal/Executor.java | 54 +++--- .../jpackage/internal/ExecutorFactory.java | 4 +- .../jdk/jpackage/internal/Globals.java | 6 +- .../jdk/jpackage/internal/ObjectFactory.java | 18 +- .../internal/RetryExecutorFactory.java | 4 +- .../internal/util/MemoizingSupplier.java | 56 ++++++ .../internal/WinBundlingEnvironment.java | 1 + .../test/stdmock/JPackageMockUtils.java} | 91 +++++----- .../internal/LinuxPackageArchTest.java | 3 +- .../internal/LinuxSystemEnvironmentTest.java | 3 +- .../jdk/tools/jpackage/junit/linux/junit.java | 4 +- .../jpackage/internal/MacDmgPackagerTest.java | 7 +- .../internal/MacDmgSystemEnvironmentTest.java | 4 +- .../tools/jpackage/junit/macosx/junit.java | 4 +- .../DefaultBundlingEnvironmentTest.java | 3 +- .../jdk/jpackage/internal/cli/MainTest.java | 7 +- .../internal/util/MemoizingSupplierTest.java | 163 ++++++++++++++++++ 19 files changed, 331 insertions(+), 130 deletions(-) create mode 100644 src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/MemoizingSupplier.java rename test/jdk/tools/jpackage/{junit/share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java => helpers/jdk/jpackage/test/stdmock/JPackageMockUtils.java} (76%) create mode 100644 test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/MemoizingSupplierTest.java diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxBundlingEnvironment.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxBundlingEnvironment.java index d2169ede461..d6d9def9407 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxBundlingEnvironment.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxBundlingEnvironment.java @@ -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; diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java index 331bde29d27..3a58180aa35 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java @@ -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 Supplier runOnce(Supplier supplier) { - return new CachingSupplier<>(supplier); - } - static Supplier>> createBundlerSupplier( Supplier> sysEnvResultSupplier, BiConsumer bundler) { Objects.requireNonNull(sysEnvResultSupplier); @@ -223,25 +220,6 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { }); } - - private static final class CachingSupplier implements Supplier { - - CachingSupplier(Supplier getter) { - this.getter = Objects.requireNonNull(getter); - } - - @Override - public T get() { - return cachedValue.updateAndGet(v -> { - return Optional.ofNullable(v).orElseGet(getter); - }); - } - - private final Supplier getter; - private final AtomicReference cachedValue = new AtomicReference<>(); - } - - private final Map>>> bundlers; private final Optional>> defaultOperationSupplier; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Executor.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Executor.java index 53c587ab37c..f9ccec7e3bb 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Executor.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Executor.java @@ -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() { + public Optional toolProvider() { return Optional.ofNullable(toolProvider); } - Executor processBuilder(ProcessBuilder v) { + public Executor processBuilder(ProcessBuilder v) { processBuilder = Objects.requireNonNull(v); toolProvider = null; return this; } - Optional processBuilder() { + public Optional processBuilder() { return Optional.ofNullable(processBuilder); } - Executor args(List v) { + public Executor args(List v) { args.addAll(v); return this; } - Executor args(String... args) { + public Executor args(String... args) { return args(List.of(args)); } - List args() { + public List args() { return args; } - Executor setQuiet(boolean v) { + public Executor setQuiet(boolean v) { quietCommand = v; return this; } - Executor mapper(UnaryOperator v) { + public Executor mapper(UnaryOperator v) { mapper = v; return this; } - Optional> mapper() { + public Optional> mapper() { return Optional.ofNullable(mapper); } - Executor copy() { + public Executor copy() { return new Executor(this); } @@ -261,7 +261,7 @@ final class Executor { }); } - List commandLine() { + public List commandLine() { if (processBuilder != null) { return Stream.of(processBuilder.command(), args).flatMap(Collection::stream).toList(); } else if (toolProvider != null) { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ExecutorFactory.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ExecutorFactory.java index ce703358b82..82d81d1052e 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ExecutorFactory.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ExecutorFactory.java @@ -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; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Globals.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Globals.java index 91ae37870a5..d5c714d1a47 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Globals.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Globals.java @@ -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()); } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ObjectFactory.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ObjectFactory.java index f1a83eb9eab..b6a95ae539b 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ObjectFactory.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ObjectFactory.java @@ -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(); } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RetryExecutorFactory.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RetryExecutorFactory.java index 3efb522abd4..f18ce61fc04 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RetryExecutorFactory.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RetryExecutorFactory.java @@ -27,9 +27,9 @@ package jdk.jpackage.internal; import jdk.jpackage.internal.util.RetryExecutor; @FunctionalInterface -interface RetryExecutorFactory { +public interface RetryExecutorFactory { RetryExecutor retryExecutor(Class exceptionType); - static final RetryExecutorFactory DEFAULT = RetryExecutor::new; + public static final RetryExecutorFactory DEFAULT = RetryExecutor::new; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/MemoizingSupplier.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/MemoizingSupplier.java new file mode 100644 index 00000000000..2974c498867 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/MemoizingSupplier.java @@ -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 implements Supplier { + + public MemoizingSupplier(Supplier 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 Supplier runOnce(Supplier supplier) { + return new MemoizingSupplier<>(supplier); + } + + private final FutureTask future; +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinBundlingEnvironment.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinBundlingEnvironment.java index de52a222d7d..e10bfb95abf 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinBundlingEnvironment.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinBundlingEnvironment.java @@ -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; diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/stdmock/JPackageMockUtils.java similarity index 76% rename from test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java rename to test/jdk/tools/jpackage/helpers/jdk/jpackage/test/stdmock/JPackageMockUtils.java index e88077a6c9d..604bcd0711f 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/stdmock/JPackageMockUtils.java @@ -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 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> 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.>>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 withCommandListener(Consumer> 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); })); diff --git a/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxPackageArchTest.java b/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxPackageArchTest.java index baf03a32142..d1332fc59e5 100644 --- a/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxPackageArchTest.java +++ b/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxPackageArchTest.java @@ -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 arch = LinuxPackageArch.create(pkgType); diff --git a/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxSystemEnvironmentTest.java b/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxSystemEnvironmentTest.java index 8ff958491b1..5104dd1b252 100644 --- a/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxSystemEnvironmentTest.java +++ b/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxSystemEnvironmentTest.java @@ -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(); diff --git a/test/jdk/tools/jpackage/junit/linux/junit.java b/test/jdk/tools/jpackage/junit/linux/junit.java index 0fd337c812c..ba06cb30db7 100644 --- a/test/jdk/tools/jpackage/junit/linux/junit.java +++ b/test/jdk/tools/jpackage/junit/linux/junit.java @@ -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 */ diff --git a/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgPackagerTest.java b/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgPackagerTest.java index e5da383142a..0e4893c8a06 100644 --- a/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgPackagerTest.java +++ b/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgPackagerTest.java @@ -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> EXPAND_PATH = (path, sink) -> { + private static final BiConsumer> EXPAND_PATH = (path, sink) -> { do { sink.accept(path); path = path.getParent(); } while (path != null); }; - private final static List DMG_ICON_FILES = Stream.of( + private static final List DMG_ICON_FILES = Stream.of( ".VolumeIcon.icns", ".background/background.tiff" ).map(Path::of).collect(Collectors.toUnmodifiableList()); diff --git a/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgSystemEnvironmentTest.java b/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgSystemEnvironmentTest.java index de2b07e86a6..8e892106cb4 100644 --- a/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgSystemEnvironmentTest.java +++ b/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgSystemEnvironmentTest.java @@ -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(); diff --git a/test/jdk/tools/jpackage/junit/macosx/junit.java b/test/jdk/tools/jpackage/junit/macosx/junit.java index c7fd2bc5f8d..2253211add0 100644 --- a/test/jdk/tools/jpackage/junit/macosx/junit.java +++ b/test/jdk/tools/jpackage/junit/macosx/junit.java @@ -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 */ diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/DefaultBundlingEnvironmentTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/DefaultBundlingEnvironmentTest.java index 709f0f8413b..ac05f5e2b69 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/DefaultBundlingEnvironmentTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/DefaultBundlingEnvironmentTest.java @@ -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(); diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.java index 239ce0d0ce9..1c06d592006 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.java @@ -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); diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/MemoizingSupplierTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/MemoizingSupplierTest.java new file mode 100644 index 00000000000..654071359cb --- /dev/null +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/MemoizingSupplierTest.java @@ -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 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 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 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 CountingSupplier count(Supplier supplier) { + return new CountingSupplier<>(supplier); + } + + private static final class CountingSupplier implements Supplier { + + CountingSupplier(Supplier impl) { + this.impl = Objects.requireNonNull(impl); + } + + @Override + public T get() { + counter++; + return impl.get(); + } + + int counter() { + return counter; + } + + private int counter; + private final Supplier impl; + } +}