diff a/test/jdk/tools/jlink/runtimeImage/PackagedModulesVsRuntimeImageLinkTest.java b/test/jdk/tools/jlink/runtimeImage/PackagedModulesVsRuntimeImageLinkTest.java
--- a/test/jdk/tools/jlink/runtimeImage/PackagedModulesVsRuntimeImageLinkTest.java
+++ b/test/jdk/tools/jlink/runtimeImage/PackagedModulesVsRuntimeImageLinkTest.java
@@ -1,7 +1,8 @@
 /*
  * Copyright (c) 2024, Red Hat, Inc.
+ * Copyright (c) 2025, 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.
@@ -29,10 +30,14 @@
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
 import tests.Helper;
 import tests.JImageGenerator;
 
 
@@ -45,13 +50,14 @@
  * @enablePreview
  * @modules java.base/jdk.internal.jimage
  *          jdk.jlink/jdk.tools.jlink.internal
  *          jdk.jlink/jdk.tools.jlink.plugin
  *          jdk.jlink/jdk.tools.jimage
+ *          jdk.jlink
  * @build tests.* jdk.test.lib.process.OutputAnalyzer
  *        jdk.test.lib.process.ProcessTools
- * @run main/othervm/timeout=1200 -Xmx1g PackagedModulesVsRuntimeImageLinkTest
+ * @run main/othervm/timeout=1200 -ea -esa -DDISABLE_PREVIEW_PATCHING=false -Xmx1g PackagedModulesVsRuntimeImageLinkTest
  */
 public class PackagedModulesVsRuntimeImageLinkTest extends AbstractLinkableRuntimeTest {
 
     public static void main(String[] args) throws Exception {
         PackagedModulesVsRuntimeImageLinkTest test = new PackagedModulesVsRuntimeImageLinkTest();
@@ -68,11 +74,10 @@
                 .validatingModule("java.se");
         if (isLinkableRuntime) {
             builder.setLinkableRuntime();
         }
         Path javaSEruntimeLink = createJavaImageRuntimeLink(builder.build());
-
         // create a java.se using packaged modules (jmod-full)
         Path javaSEJmodFull = JImageGenerator.getJLinkTask()
                 .output(helper.createNewImageDir("java-se-jmodfull"))
                 .addMods("java.se").call().assertSuccess();
 
@@ -118,13 +123,12 @@
         // and offset differences in container bytes)
         Path jimageJmodLess = javaSEJmodLess.resolve(Path.of("lib")).resolve(Path.of("modules"));
         Path jimageJmodFull = javaSEJmodFull.resolve(Path.of("lib")).resolve(Path.of("modules"));
         List<String> jimageContentJmodLess = JImageHelper.listContents(jimageJmodLess);
         List<String> jimageContentJmodFull = JImageHelper.listContents(jimageJmodFull);
-        if (jimageContentJmodLess.size() != jimageContentJmodFull.size()) {
-            throw new AssertionError(String.format("Size of jimage content differs for jmod-less (%d) v. jmod-full (%d)", jimageContentJmodLess.size(), jimageContentJmodFull.size()));
-        }
+        assertSameContent("jmod-less", jimageContentJmodLess, "jmod-full", jimageContentJmodFull);
+        // Both lists are same size, with same names, so enumerate either.
         for (int i = 0; i < jimageContentJmodFull.size(); i++) {
             if (!jimageContentJmodFull.get(i).equals(jimageContentJmodLess.get(i))) {
                 throw new AssertionError(String.format("Jimage content differs at index %d: jmod-full was: '%s' jmod-less was: '%s'",
                                                        i,
                                                        jimageContentJmodFull.get(i),
@@ -143,12 +147,41 @@
                 throw new AssertionError("Content bytes mismatch for " + loc);
             }
         }
     }
 
+    // Helper to assert the content of two jimage files are the same and provide
+    // useful debug information otherwise.
+    private static void assertSameContent(
+            String lhsLabel, List<String> lhsNames, String rhsLabel, List<String> rhsNames) {
+
+        List<String> lhsOnly =
+                lhsNames.stream().filter(Predicate.not(Set.copyOf(rhsNames)::contains)).toList();
+        List<String> rhsOnly =
+                rhsNames.stream().filter(Predicate.not(Set.copyOf(lhsNames)::contains)).toList();
+        if (!lhsOnly.isEmpty() || !rhsOnly.isEmpty()) {
+            String message = String.format(
+                    "jimage content differs for %s (%d) v. %s (%d)",
+                    lhsLabel, lhsNames.size(), rhsLabel, rhsNames.size());
+            if (!lhsOnly.isEmpty()) {
+                message += "\nOnly in " + lhsLabel + ":\n\t" + String.join("\n\t", lhsOnly);
+            }
+            if (!rhsOnly.isEmpty()) {
+                message += "\nOnly in " + rhsLabel + ":\n\t" + String.join("\n\t", rhsOnly);
+            }
+            throw new AssertionError(message);
+        }
+    }
+
     private static boolean isTreeInfoResource(String path) {
-        return path.startsWith("/packages") || path.startsWith("/modules");
+        return pathStartsWith(path, "/packages") || pathStartsWith(path, "/modules");
+    }
+
+    // Handle both "<prefix>" and "<prefix>/...".
+    private static boolean pathStartsWith(String path, String prefix) {
+        int plen = prefix.length();
+        return path.startsWith(prefix) && (path.length() == plen || path.charAt(plen) == '/');
     }
 
     private static void handleFileMismatch(Path a, Path b) {
         throw new AssertionError("Files mismatch: " + a + " vs. " + b);
     }
