This is an automated email from the ASF dual-hosted git repository.

rmaucher pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat-jakartaee-migration.git


The following commit(s) were added to refs/heads/main by this push:
     new 024cc0e  Code review for tests
024cc0e is described below

commit 024cc0e2da33d6fc00f766074264a5ac75cbbb46
Author: remm <remm@meteor>
AuthorDate: Thu Sep 3 21:10:22 2026 +0200

    Code review for tests
    
    Minor fixes and cleanups, tightening of the tests, no real "functional"
    changes.
    Co authored with OpenCode.
---
 .../tomcat/jakartaee/ClassConverterTest.java       |   2 +
 .../tomcat/jakartaee/ManifestConverterTest.java    |  19 +-
 .../tomcat/jakartaee/MigrationCacheTest.java       |  48 ++-
 .../apache/tomcat/jakartaee/MigrationTaskTest.java |  49 ++-
 .../org/apache/tomcat/jakartaee/MigrationTest.java | 417 ++++++++++++---------
 .../tomcat/jakartaee/NoExitSecurityManager.java    |  10 +
 .../apache/tomcat/jakartaee/StringManagerTest.java |  11 +-
 .../apache/tomcat/jakartaee/TesterConstants.java   |  28 ++
 .../apache/tomcat/jakartaee/TextConverterTest.java |  62 ++-
 src/test/resources/testbuild.xml                   |   2 +-
 10 files changed, 416 insertions(+), 232 deletions(-)

diff --git a/src/test/java/org/apache/tomcat/jakartaee/ClassConverterTest.java 
b/src/test/java/org/apache/tomcat/jakartaee/ClassConverterTest.java
index 0aa1d55..d57a01b 100644
--- a/src/test/java/org/apache/tomcat/jakartaee/ClassConverterTest.java
+++ b/src/test/java/org/apache/tomcat/jakartaee/ClassConverterTest.java
@@ -24,6 +24,7 @@ import org.apache.bcel.classfile.JavaClass;
 import org.junit.Test;
 
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -53,6 +54,7 @@ public class ClassConverterTest {
         // Get the original bytes
         try (InputStream is = 
this.getClass().getResourceAsStream("/org/apache/tomcat/jakartaee/TesterConstants.class");
                 ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+            assertNotNull(is);
             byte[] buf = new byte[1024];
             int len;
             while ((len = is.read(buf)) > 0) {
diff --git 
a/src/test/java/org/apache/tomcat/jakartaee/ManifestConverterTest.java 
b/src/test/java/org/apache/tomcat/jakartaee/ManifestConverterTest.java
index 1ea90e7..79a9d82 100644
--- a/src/test/java/org/apache/tomcat/jakartaee/ManifestConverterTest.java
+++ b/src/test/java/org/apache/tomcat/jakartaee/ManifestConverterTest.java
@@ -22,6 +22,7 @@ import static org.junit.Assert.assertTrue;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.jar.Attributes;
 import java.util.jar.Manifest;
 
@@ -45,8 +46,10 @@ public class ManifestConverterTest {
     public void testConvert() throws IOException {
         ManifestConverter converter = new ManifestConverter();
         ByteArrayOutputStream os = new ByteArrayOutputStream();
-        boolean converted = converter.convert("/MANIFEST.test.MF", 
getClass().getResourceAsStream("/MANIFEST.test.MF"),
-                os, EESpecProfiles.TOMCAT);
+        boolean converted;
+        try (InputStream src = 
getClass().getResourceAsStream("/MANIFEST.test.MF")) {
+            converted = converter.convert("/MANIFEST.test.MF", src, os, 
EESpecProfiles.TOMCAT);
+        }
         assertTrue(converted);
 
         String result = os.toString("UTF-8");
@@ -128,12 +131,14 @@ public class ManifestConverterTest {
         manifest.write(manifestBytes);
 
         ByteArrayOutputStream dest = new ByteArrayOutputStream();
-        converter.convert("META-INF/MANIFEST.MF",
+        boolean converted = converter.convert("META-INF/MANIFEST.MF",
                 new ByteArrayInputStream(manifestBytes.toByteArray()), dest, 
EESpecProfiles.TOMCAT);
 
+        // The suffix is added without counting as a conversion
+        assertFalse("Version suffix should not count as a conversion", 
converted);
         String result = dest.toString("UTF-8");
         assertTrue("Implementation-Version should have migration suffix",
-                result.contains("-migrated-"));
+                result.contains("Implementation-Version: 1.0.0-" + 
Info.getVersion()));
     }
 
     @Test
@@ -203,9 +208,10 @@ public class ManifestConverterTest {
     }
 
     @Test
-    public void testConvertPreservesNonStringValues() throws IOException {
+    public void testConvertNoConversionNeededWithTomcatProfile() throws 
IOException {
         ManifestConverter converter = new ManifestConverter();
 
+        // Create a manifest with no javax packages
         Manifest manifest = new Manifest();
         manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, 
"1.0");
 
@@ -216,7 +222,6 @@ public class ManifestConverterTest {
         boolean converted = converter.convert("META-INF/MANIFEST.MF",
                 new ByteArrayInputStream(manifestBytes.toByteArray()), dest, 
EESpecProfiles.TOMCAT);
 
-        // Should not throw and should handle gracefully
-        assertTrue("Conversion should complete", !converted);
+        assertFalse("Should not convert manifest with no javax packages", 
converted);
     }
 }
diff --git a/src/test/java/org/apache/tomcat/jakartaee/MigrationCacheTest.java 
b/src/test/java/org/apache/tomcat/jakartaee/MigrationCacheTest.java
index 7ac1864..3e1f772 100644
--- a/src/test/java/org/apache/tomcat/jakartaee/MigrationCacheTest.java
+++ b/src/test/java/org/apache/tomcat/jakartaee/MigrationCacheTest.java
@@ -20,6 +20,7 @@ package org.apache.tomcat.jakartaee;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileWriter;
+import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
@@ -181,7 +182,7 @@ public class MigrationCacheTest {
 
         String stats = cache.getStats();
         assertNotNull("Stats should not be null", stats);
-        assertTrue("Stats should contain entry count", stats.contains("0"));
+        assertTrue("Stats should contain entry count", stats.contains("0 
entries"));
     }
 
     @Test
@@ -249,7 +250,7 @@ public class MigrationCacheTest {
     public void testCacheNullDirectory() throws Exception {
         try {
             new MigrationCache(null, 30);
-            fail("Should throw IllegalStateException for null directory");
+            fail("Should throw IllegalArgumentException for null directory");
         } catch (IllegalArgumentException e) {
             assertTrue("Error message should mention null", 
e.getMessage().contains("null") || e.getMessage().contains("Null"));
         }
@@ -263,9 +264,8 @@ public class MigrationCacheTest {
         try {
             new MigrationCache(regularFile, 30);
             fail("Should throw IOException when path is not a directory");
-        } catch (Exception e) {
-            assertTrue("Should be IOException or similar",
-                    e instanceof Exception);
+        } catch (IOException e) {
+            // Expected
         }
     }
 
@@ -359,7 +359,7 @@ public class MigrationCacheTest {
 
         String stats = cache.getStats();
         assertNotNull("Stats should not be null", stats);
-        assertTrue("Stats should contain entry count", stats.contains("3"));
+        assertTrue("Stats should contain entry count", stats.contains("3 
entries"));
     }
 
     @Test
@@ -400,12 +400,12 @@ public class MigrationCacheTest {
 
     @Test
     public void testCacheMetadataWithInvalidDate() throws Exception {
-        // Create a metadata file with invalid date format
+        // Create a metadata file with a valid hash but an invalid date format
         File metadataFile = new File(tempCacheDir, "cache-metadata.txt");
         try (FileWriter writer = new FileWriter(metadataFile)) {
             writer.write("# Migration cache metadata - 
hash|last_access_date\n");
-            writer.write("abc123|not-a-date\n");
-            writer.write("def456|2024-01-01\n");
+            
writer.write("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef|not-a-date\n");
+            
writer.write("fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210|2024-01-01\n");
         }
 
         // Should handle invalid dates gracefully
@@ -499,7 +499,7 @@ public class MigrationCacheTest {
         MigrationCache cache = new MigrationCache(tempCacheDir, 30);
         String stats = cache.getStats();
         assertNotNull("Stats should not be null", stats);
-        assertTrue("Stats should contain entry count", stats.contains("1"));
+        assertTrue("Stats should contain entry count", stats.contains("1 
entries"));
     }
 
     @Test
@@ -531,13 +531,31 @@ public class MigrationCacheTest {
         byte[] sourceData = "test source 
content".getBytes(StandardCharsets.UTF_8);
         CacheEntry entry = cache.getCacheEntry(sourceData, 
EESpecProfiles.TOMCAT);
 
-        // Begin store creates temp file
+        // Begin store creates the temp file
         OutputStream os = entry.beginStore();
+        os.write("partial data".getBytes(StandardCharsets.UTF_8));
         os.close();
 
-        // Delete temp file manually to simulate failure
-        // The temp file path is internal, so we can't easily delete it
-        // Instead, test that commit works normally after writing
-        entry.commitStore();
+        // Locate and delete the temp file to simulate a failure between
+        // beginStore() and commitStore()
+        File tempFile = null;
+        File[] files = tempCacheDir.listFiles();
+        if (files != null) {
+            for (File file : files) {
+                if (file.isFile() && file.getName().startsWith("temp-") && 
file.getName().endsWith(".tmp")) {
+                    tempFile = file;
+                }
+            }
+        }
+        assertNotNull("Temp file should have been created", tempFile);
+        assertTrue("Temp file should be deleted", tempFile.delete());
+
+        try {
+            entry.commitStore();
+            fail("Should throw IOException when the temp file is missing");
+        } catch (IOException e) {
+            assertTrue("Unexpected error message: " + e.getMessage(),
+                    e.getMessage() != null && e.getMessage().contains("does 
not exist"));
+        }
     }
 }
diff --git a/src/test/java/org/apache/tomcat/jakartaee/MigrationTaskTest.java 
b/src/test/java/org/apache/tomcat/jakartaee/MigrationTaskTest.java
index 78d9bc9..8762002 100644
--- a/src/test/java/org/apache/tomcat/jakartaee/MigrationTaskTest.java
+++ b/src/test/java/org/apache/tomcat/jakartaee/MigrationTaskTest.java
@@ -21,8 +21,11 @@ import java.io.File;
 import java.io.OutputStream;
 import java.io.PrintStream;
 import java.nio.charset.StandardCharsets;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
 
 import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
 import org.apache.tools.ant.BuildException;
 import org.apache.tools.ant.DefaultLogger;
 import org.apache.tools.ant.Project;
@@ -66,9 +69,15 @@ public class MigrationTaskTest {
         project.addBuildListener(logger);
     }
 
-    @Test(expected = BuildException.class)
+    @Test
     public void testInvalidProfile() {
-        project.executeTarget("invalid-profile");
+        try {
+            project.executeTarget("invalid-profile");
+            fail("Should throw BuildException when profile is invalid");
+        } catch (BuildException e) {
+            assertTrue("Error should mention the invalid profile",
+                    e.getMessage().contains("jserv"));
+        }
     }
 
     @Test
@@ -94,8 +103,7 @@ public class MigrationTaskTest {
             task.execute();
             fail("Should throw BuildException when source is null");
         } catch (BuildException e) {
-            assertTrue("Error should mention source",
-                    e.getMessage().contains("source") || 
e.getMessage().toLowerCase().contains("source"));
+            assertTrue("Error should mention source", 
e.getMessage().contains("source"));
         }
     }
 
@@ -103,15 +111,13 @@ public class MigrationTaskTest {
     public void testMigrationTaskNoDest() {
         MigrationTask task = new MigrationTask();
         task.setProject(project);
-        task.setLocation(null);
         task.setSrc(new File("target/test-classes/HelloServlet.java"));
 
         try {
             task.execute();
             fail("Should throw BuildException when dest is null");
         } catch (BuildException e) {
-            assertTrue("Error should mention destination",
-                    e.getMessage().contains("dest") || 
e.getMessage().toLowerCase().contains("dest"));
+            assertTrue("Error should mention destination", 
e.getMessage().contains("dest"));
         }
     }
 
@@ -119,7 +125,6 @@ public class MigrationTaskTest {
     public void testMigrationTaskSourceNotExists() {
         MigrationTask task = new MigrationTask();
         task.setProject(project);
-        task.setLocation(null);
         task.setSrc(new File("target/test-classes/nonexistent.java"));
         task.setDest(new File("target/test-classes/output.java"));
 
@@ -127,7 +132,7 @@ public class MigrationTaskTest {
             task.execute();
             fail("Should throw BuildException when source does not exist");
         } catch (BuildException e) {
-            // Expected
+            assertTrue("Error should mention source", 
e.getMessage().contains("source"));
         }
     }
 
@@ -135,23 +140,27 @@ public class MigrationTaskTest {
     public void testMigrationTaskWithZipInMemory() throws Exception {
         MigrationTask task = new MigrationTask();
         task.setProject(project);
-        task.setLocation(null);
-        task.setSrc(new File("target/test-classes/HelloServlet.java"));
-        File destFile = tempFolder.newFile("ant-zip-memory.java");
+        task.setSrc(new File("target/test-classes/hellocgi.jar"));
+        File destFile = tempFolder.newFile("ant-zip-memory.jar");
         task.setDest(destFile);
         task.setZipInMemory(true);
         task.execute();
 
         assertTrue("Migrated file should exist", destFile.exists());
-        String migratedSource = FileUtils.readFileToString(destFile, 
StandardCharsets.UTF_8);
-        assertTrue("Imports should be migrated", 
migratedSource.contains("import jakarta.servlet"));
+        try (JarFile jar = new JarFile(destFile)) {
+            JarEntry entry = 
jar.getJarEntry("org/apache/tomcat/jakartaee/HelloCGI.class");
+            assertNotNull("HelloCGI class not found in migrated JAR", entry);
+            byte[] classBytes = IOUtils.toByteArray(jar.getInputStream(entry));
+            assertTrue("Class should be migrated",
+                    new String(classBytes, StandardCharsets.ISO_8859_1)
+                            
.contains("jakarta/servlet/CommonGatewayInterface"));
+        }
     }
 
     @Test
     public void testMigrationTaskWithExcludes() throws Exception {
         MigrationTask task = new MigrationTask();
         task.setProject(project);
-        task.setLocation(null);
         task.setSrc(new File("target/test-classes/HelloServlet.java"));
         File destFile = tempFolder.newFile("ant-excludes.java");
         task.setDest(destFile);
@@ -159,27 +168,32 @@ public class MigrationTaskTest {
         task.execute();
 
         assertTrue("Migrated file should exist", destFile.exists());
+        String content = FileUtils.readFileToString(destFile, 
StandardCharsets.UTF_8);
+        assertTrue("Excluded file should not be converted", 
content.contains("import javax.servlet"));
     }
 
     @Test
     public void testMigrationTaskWithMatchExcludesAgainstPathName() throws 
Exception {
         MigrationTask task = new MigrationTask();
         task.setProject(project);
-        task.setLocation(null);
         task.setSrc(new File("target/test-classes/HelloServlet.java"));
         File destFile = tempFolder.newFile("ant-path-excludes.java");
         task.setDest(destFile);
+        // Matches the full path but not the bare file name, so the exclude
+        // only takes effect when excludes are matched against the path name
+        task.setExcludes("**" + File.separator + "HelloServlet.java");
         task.setMatchExcludesAgainstPathName(true);
         task.execute();
 
         assertTrue("Migrated file should exist", destFile.exists());
+        String content = FileUtils.readFileToString(destFile, 
StandardCharsets.UTF_8);
+        assertTrue("Excluded file should not be converted", 
content.contains("import javax.servlet"));
     }
 
     @Test
     public void testMigrationTaskWithEeProfile() throws Exception {
         MigrationTask task = new MigrationTask();
         task.setProject(project);
-        task.setLocation(null);
         task.setSrc(new File("target/test-classes/HelloServlet.java"));
         File destFile = tempFolder.newFile("ant-ee-profile.java");
         task.setDest(destFile);
@@ -206,7 +220,6 @@ public class MigrationTaskTest {
     public void testMigrationTaskDefaultProfile() throws Exception {
         MigrationTask task = new MigrationTask();
         task.setProject(project);
-        task.setLocation(null);
         task.setSrc(new File("target/test-classes/HelloServlet.java"));
         File destFile = tempFolder.newFile("ant-default-profile.java");
         task.setDest(destFile);
diff --git a/src/test/java/org/apache/tomcat/jakartaee/MigrationTest.java 
b/src/test/java/org/apache/tomcat/jakartaee/MigrationTest.java
index ef0bac0..17e7af0 100644
--- a/src/test/java/org/apache/tomcat/jakartaee/MigrationTest.java
+++ b/src/test/java/org/apache/tomcat/jakartaee/MigrationTest.java
@@ -25,6 +25,7 @@ import java.net.URL;
 import java.net.URLClassLoader;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
 import java.util.zip.CRC32;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
@@ -69,7 +70,7 @@ public class MigrationTest {
 
     @Test
     public void testMigrateSingleSourceFile() throws Exception {
-        File migratedFile = new 
File("target/test-classes/HelloServlet.migrated.java");
+        File migratedFile = tempFolder.newFile("HelloServlet.migrated.java");
         MigrationCLI.main(new String[] 
{"target/test-classes/HelloServlet.java", migratedFile.getAbsolutePath()});
 
         assertTrue("Migrated file not found", migratedFile.exists());
@@ -81,7 +82,7 @@ public class MigrationTest {
 
     @Test
     public void testMigrateSingleSourceFileWithProfile() throws Exception {
-        File migratedFile = new 
File("target/test-classes/HelloServlet.migrated.java");
+        File migratedFile = tempFolder.newFile("HelloServlet.migrated.java");
         MigrationCLI.main(new String[] {"-logLevel=FINE", "-profile=EE", 
"target/test-classes/HelloServlet.java", migratedFile.getAbsolutePath()});
 
         assertTrue("Migrated file not found", migratedFile.exists());
@@ -94,7 +95,7 @@ public class MigrationTest {
     @Test
     public void testMigrateSingleSourceFileInPlace() throws Exception {
         File sourceFile = new File("target/test-classes/HelloServlet.java");
-        File migratedFile = new 
File("target/test-classes/HelloServlet.inplace.java");
+        File migratedFile = tempFolder.newFile("HelloServlet.inplace.java");
         FileUtils.copyFile(sourceFile, migratedFile);
 
         MigrationCLI.main(new String[] {"-profile=EE", 
migratedFile.getAbsolutePath(), migratedFile.getAbsolutePath()});
@@ -123,7 +124,7 @@ public class MigrationTest {
     @Test
     public void testMigrateDirectory() throws Exception {
         File sourceDirectory = new File("src/test/resources");
-        File destinationDirectory = new File("target/test-classes/migration");
+        File destinationDirectory = new File(tempFolder.getRoot(), 
"migration");
 
         Migration migration = new Migration();
         migration.setSource(sourceDirectory);
@@ -132,14 +133,14 @@ public class MigrationTest {
 
         assertTrue("Destination directory not found", 
destinationDirectory.exists());
 
-        File migratedFile = new 
File("target/test-classes/migration/HelloServlet.java");
+        File migratedFile = new File(destinationDirectory, 
"HelloServlet.java");
         assertTrue("Migrated file not found", migratedFile.exists());
 
         String migratedSource = FileUtils.readFileToString(migratedFile, 
StandardCharsets.UTF_8);
         assertFalse("Imports not migrated", migratedSource.contains("import 
javax.servlet"));
         assertTrue("Migrated imports not found", 
migratedSource.contains("import jakarta.servlet"));
 
-        File migratedSpiFile = new 
File("target/test-classes/migration/javax.enterprise.inject.spi.Extension");
+        File migratedSpiFile = new File(destinationDirectory, 
"javax.enterprise.inject.spi.Extension");
         assertTrue("SPI file has not been migrated by renaming", 
migratedSpiFile.exists());
 
         String migratedSpiSource = FileUtils.readFileToString(migratedSpiFile, 
StandardCharsets.UTF_8);
@@ -149,7 +150,7 @@ public class MigrationTest {
     @Test
     public void testMigrateDirectoryWithEeProfile() throws Exception {
         File sourceDirectory = new File("src/test/resources");
-        File destinationDirectory = new 
File("target/test-classes/migration-ee");
+        File destinationDirectory = new File(tempFolder.getRoot(), 
"migration-ee");
 
         Migration migration = new Migration();
         migration.setEESpecProfile(EESpecProfiles.EE);
@@ -176,19 +177,24 @@ public class MigrationTest {
     @Test
     public void testMigrateClassFile() throws Exception {
         File classFile = new 
File("target/test-classes/org/apache/tomcat/jakartaee/HelloCGI.class");
-        File classFileOriginal = new 
File("target/test-classes/org/apache/tomcat/jakartaee/HelloCGI-original.class");
+        File classFileOriginal = new File(tempFolder.getRoot(), 
"HelloCGI-original.class");
         FileUtils.copyFile(classFile, classFileOriginal);
 
-        Migration migration = new Migration();
-        migration.setSource(classFile);
-        migration.setDestination(classFile);
-        migration.execute();
-
-        Class<?> cls = Class.forName("org.apache.tomcat.jakartaee.HelloCGI");
-        assertEquals("jakarta.servlet.CommonGatewayInterface", 
cls.getSuperclass().getName());
+        try {
+            Migration migration = new Migration();
+            migration.setSource(classFile);
+            migration.setDestination(classFile);
+            migration.execute();
 
-        Assert.assertTrue("Failed to delete migrated class file", 
classFile.delete());
-        FileUtils.copyFile(classFileOriginal, classFile);
+            Class<?> cls = 
Class.forName("org.apache.tomcat.jakartaee.HelloCGI");
+            assertEquals("jakarta.servlet.CommonGatewayInterface", 
cls.getSuperclass().getName());
+        } finally {
+            // Always restore the original class file, otherwise a failure in
+            // this test would leave the (shared) class file migrated and
+            // break other tests on the next non-clean build.
+            Assert.assertTrue("Failed to delete migrated class file", 
classFile.delete());
+            FileUtils.copyFile(classFileOriginal, classFile);
+        }
     }
 
     @Test
@@ -198,7 +204,7 @@ public class MigrationTest {
 
     private void testMigrateJarFileInternal(boolean zipInMemory) throws 
Exception {
         File jarFile = new File("target/test-classes/hellocgi.jar");
-        File jarFileTarget = new 
File("target/test-classes/hellocgi-target.jar");
+        File jarFileTarget = tempFolder.newFile("hellocgi-target.jar");
 
         Migration migration = new Migration();
         migration.setSource(jarFile);
@@ -263,8 +269,8 @@ public class MigrationTest {
 
     private void testMigrateSignedJarFile(String algorithm, EESpecProfile 
profile) throws Exception {
         File jarFileSrc = new File("target/test-classes/hellocgi-signed-" + 
algorithm + ".jar");
-        File jarFileTmp = new File("target/test-classes/hellocgi-signed-" + 
algorithm + "-tmp.jar");
-        Files.copy(jarFileSrc.toPath(), jarFileTmp.toPath());
+        File jarFileTmp = tempFolder.newFile("hellocgi-signed-" + algorithm + 
"-tmp.jar");
+        Files.copy(jarFileSrc.toPath(), jarFileTmp.toPath(), 
StandardCopyOption.REPLACE_EXISTING);
 
         Migration migration = new Migration();
         migration.setEESpecProfile(profile);
@@ -292,57 +298,44 @@ public class MigrationTest {
     @Test
     public void testMigrateJarWithCache() throws Exception {
         File jarFile = new File("target/test-classes/hellocgi.jar");
-        File jarFileTarget = new 
File("target/test-classes/hellocgi-cached.jar");
-        File cacheDir = new File("target/test-classes/cache-test");
+        File jarFileTarget = tempFolder.newFile("hellocgi-cached.jar");
+        // Not pre-created: the MigrationCache constructor must create it
+        File cacheDir = new File(tempFolder.getRoot(), "cache-test");
 
-        try {
-            // Clean up cache directory
-            if (cacheDir.exists()) {
-                FileUtils.deleteDirectory(cacheDir);
-            }
-
-            // First migration - cache miss
-            Migration migration1 = new Migration();
-            migration1.setSource(jarFile);
-            migration1.setDestination(jarFileTarget);
-            migration1.setCache(new MigrationCache(cacheDir, 30));
-            migration1.execute();
-
-            assertTrue("Target JAR should exist after first migration", 
jarFileTarget.exists());
-            assertTrue("Cache directory should be created", cacheDir.exists());
+        // Note: top-level archives are not cached (only nested archives are),
+        // so the cache is only set up, not exercised, by this test.
+        Migration migration1 = new Migration();
+        migration1.setSource(jarFile);
+        migration1.setDestination(jarFileTarget);
+        migration1.setCache(new MigrationCache(cacheDir, 30));
+        migration1.execute();
 
-            // Verify the migrated JAR works
-            verifyHelloCGIMigrated(jarFileTarget);
+        assertTrue("Target JAR should exist after first migration", 
jarFileTarget.exists());
+        assertTrue("Cache directory should be created", cacheDir.exists());
 
-            // Delete target and migrate again - cache hit
-            jarFileTarget.delete();
-            assertFalse("Target should be deleted", jarFileTarget.exists());
+        // Verify the migrated JAR works
+        verifyHelloCGIMigrated(jarFileTarget);
 
-            Migration migration2 = new Migration();
-            migration2.setSource(jarFile);
-            migration2.setDestination(jarFileTarget);
-            migration2.setCache(new MigrationCache(cacheDir, 30));
-            migration2.execute();
+        // Delete target and migrate again
+        jarFileTarget.delete();
+        assertFalse("Target should be deleted", jarFileTarget.exists());
 
-            assertTrue("Target JAR should exist after second migration", 
jarFileTarget.exists());
+        Migration migration2 = new Migration();
+        migration2.setSource(jarFile);
+        migration2.setDestination(jarFileTarget);
+        migration2.setCache(new MigrationCache(cacheDir, 30));
+        migration2.execute();
 
-            // Verify the cached JAR works
-            verifyHelloCGIMigrated(jarFileTarget);
+        assertTrue("Target JAR should exist after second migration", 
jarFileTarget.exists());
 
-            // Note: We don't assert that duration2 < duration1 because the 
times are too short
-            // and can vary. The important thing is both migrations work 
correctly.
-        } finally {
-            // Clean up
-            if (cacheDir.exists()) {
-                FileUtils.deleteDirectory(cacheDir);
-            }
-        }
+        // Verify the migrated JAR works
+        verifyHelloCGIMigrated(jarFileTarget);
     }
 
     @Test
     public void testMigrateJarWithCacheDisabled() throws Exception {
         File jarFile = new File("target/test-classes/hellocgi.jar");
-        File jarFileTarget = new 
File("target/test-classes/hellocgi-nocache.jar");
+        File jarFileTarget = tempFolder.newFile("hellocgi-nocache.jar");
 
         Migration migration = new Migration();
         migration.setSource(jarFile);
@@ -352,58 +345,35 @@ public class MigrationTest {
 
         assertTrue("Target JAR should exist", jarFileTarget.exists());
 
-        File cgiapiFile = new File("target/test-classes/cgi-api.jar");
-        URLClassLoader classloader = new URLClassLoader(
-                new URL[]{jarFileTarget.toURI().toURL(), 
cgiapiFile.toURI().toURL()},
-                ClassLoader.getSystemClassLoader().getParent());
-        Class<?> cls = Class.forName("org.apache.tomcat.jakartaee.HelloCGI", 
true, classloader);
-        assertEquals("jakarta.servlet.CommonGatewayInterface", 
cls.getSuperclass().getName());
+        verifyHelloCGIMigrated(jarFileTarget);
     }
 
     @Test
     public void testMigrateCLIWithCacheOption() throws Exception {
         File sourceFile = new File("target/test-classes/hellocgi.jar");
-        File targetFile = new 
File("target/test-classes/hellocgi-cli-cached.jar");
-        File cacheDir = new File("target/test-classes/cache-cli-test");
+        File targetFile = tempFolder.newFile("hellocgi-cli-cached.jar");
+        // Not pre-created: the MigrationCache constructor must create it
+        File cacheDir = new File(tempFolder.getRoot(), "cache-cli-test");
 
-        try {
-            // Clean up
-            if (cacheDir.exists()) {
-                FileUtils.deleteDirectory(cacheDir);
-            }
-            if (targetFile.exists()) {
-                targetFile.delete();
-            }
-
-            // Run with custom cache
-            MigrationCLI.main(new String[] {
-                    "-cache",
-                    "-cacheLocation=" + cacheDir.getAbsolutePath(),
-                    sourceFile.getAbsolutePath(),
-                    targetFile.getAbsolutePath()
-            });
+        // Run with custom cache
+        MigrationCLI.main(new String[] {
+                "-cache",
+                "-cacheLocation=" + cacheDir.getAbsolutePath(),
+                sourceFile.getAbsolutePath(),
+                targetFile.getAbsolutePath()
+        });
 
-            assertTrue("Target file should exist", targetFile.exists());
-            assertTrue("Cache directory should be created", cacheDir.exists());
+        assertTrue("Target file should exist", targetFile.exists());
+        assertTrue("Cache directory should be created", cacheDir.exists());
 
-            // Verify the migrated JAR works
-            verifyHelloCGIMigrated(targetFile);
-        } finally {
-            // Clean up
-            if (cacheDir.exists()) {
-                FileUtils.deleteDirectory(cacheDir);
-            }
-        }
+        // Verify the migrated JAR works
+        verifyHelloCGIMigrated(targetFile);
     }
 
     @Test
     public void testMigrateCLIWithNoCacheOption() throws Exception {
         File sourceFile = new File("target/test-classes/hellocgi.jar");
-        File targetFile = new 
File("target/test-classes/hellocgi-cli-nocache.jar");
-
-        if (targetFile.exists()) {
-            targetFile.delete();
-        }
+        File targetFile = tempFolder.newFile("hellocgi-cli-nocache.jar");
 
         // Run without cache (no -cache option)
         MigrationCLI.main(new String[] {
@@ -418,7 +388,7 @@ public class MigrationTest {
     }
 
     @Test
-    public void testExecuteThrowsWhenAlreadyRunning() throws Exception {
+    public void testReExecuteAfterCompletion() throws Exception {
         // Note: After execute() completes, state is COMPLETE, not RUNNING.
         // So calling execute() again will work (it will run again).
         // The IllegalStateException is only thrown if state is RUNNING.
@@ -451,8 +421,9 @@ public class MigrationTest {
     public void testMigrateDirectoryCannotCreateDest() throws Exception {
         Migration migration = new Migration();
         File sourceDirectory = new File("src/test/resources");
-        // Use a path that definitely can't be created
-        File destDirectory = new File("/proc/nonexistent/immutable/path/dest");
+        // Use an existing regular file as the destination so that the
+        // destination directory cannot be created (portable across platforms)
+        File destDirectory = tempFolder.newFile("immutable-dest");
 
         try {
             migration.setSource(sourceDirectory);
@@ -504,16 +475,43 @@ public class MigrationTest {
 
     @Test
     public void testMigrateJarWithZip64ExtraField() throws Exception {
-        File jarFile = new File("target/test-classes/hellocgi.jar");
-        File jarFileTarget = tempFolder.newFile("zip64-test.jar");
+        // Create a JAR whose entry carries a ZIP64 extra field (ID 0x0001)
+        File jarFile = tempFolder.newFile("zip64-test.jar");
+        try (FileOutputStream fos = new FileOutputStream(jarFile);
+                
org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream zos =
+                        new 
org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream(fos)) {
+            org.apache.commons.compress.archivers.zip.ZipArchiveEntry entry =
+                    new 
org.apache.commons.compress.archivers.zip.ZipArchiveEntry("test.txt");
+            // ZIP64 extended information extra field: header 0x0001, 16 bytes 
of data
+            entry.setExtra(new byte[] { 0x01, 0x00, 0x10, 0x00,
+                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 });
+            zos.putArchiveEntry(entry);
+            
zos.write("javax.servlet.http.HttpServlet".getBytes(StandardCharsets.ISO_8859_1));
+            zos.closeArchiveEntry();
+        }
+
+        File jarFileTarget = tempFolder.newFile("zip64-migrated.jar");
 
         Migration migration = new Migration();
         migration.setSource(jarFile);
         migration.setDestination(jarFileTarget);
+        migration.setZipInMemory(false); // Streaming mode removes the ZIP64 
extra field
         migration.execute();
 
         assertTrue("Target JAR should exist", jarFileTarget.exists());
-        assertTrue("Target JAR should have content", jarFileTarget.length() > 
0);
+
+        try (org.apache.commons.compress.archivers.zip.ZipFile jar =
+                
org.apache.commons.compress.archivers.zip.ZipFile.builder().setFile(jarFileTarget).get())
 {
+            org.apache.commons.compress.archivers.zip.ZipArchiveEntry entry =
+                    
(org.apache.commons.compress.archivers.zip.ZipArchiveEntry) 
jar.getEntry("test.txt");
+            assertNotNull("Entry should exist in migrated JAR", entry);
+            assertNull("ZIP64 extra field should have been removed",
+                    entry.getExtraField(new 
org.apache.commons.compress.archivers.zip.ZipShort(1)));
+
+            byte[] content = readAllBytes(jar.getInputStream(entry), (int) 
entry.getSize());
+            assertTrue("Entry content should be migrated",
+                    new String(content, 
StandardCharsets.ISO_8859_1).contains("jakarta.servlet"));
+        }
     }
 
     @Test
@@ -538,22 +536,27 @@ public class MigrationTest {
 
     @Test
     public void testMigrateWithDisabledDefaultExcludes() throws Exception {
-        File sourceFile = new File("target/test-classes/HelloServlet.java");
-        File destFile = tempFolder.newFile("no-default-excludes.java");
+        // A valid archive whose file name matches one of the default exclude
+        // patterns (commons-lang-*.jar). With the default excludes disabled
+        // it must be processed (and converted) as a normal archive.
+        File sourceDirectory = 
tempFolder.newFolder("no-default-excludes-test");
+        createNestedJarWithContent(sourceDirectory, "commons-lang-3.12.0.jar", 
"nested.txt",
+                "javax.servlet.http.HttpServlet");
+        File destinationDirectory = 
tempFolder.newFolder("no-default-excludes-dest");
 
         Migration migration = new Migration();
-        migration.setSource(sourceFile);
-        migration.setDestination(destFile);
+        migration.setSource(sourceDirectory);
+        migration.setDestination(destinationDirectory);
         migration.setEnableDefaultExcludes(false);
         migration.execute();
 
-        assertTrue("Migrated file should exist", destFile.exists());
-        String migratedSource = FileUtils.readFileToString(destFile, 
StandardCharsets.UTF_8);
-        assertTrue("Imports should be migrated", 
migratedSource.contains("import jakarta.servlet"));
+        assertTrue("Archive should have been converted", 
migration.hasConverted());
+        verifyArchiveEntryContent(new File(destinationDirectory, 
"commons-lang-3.12.0.jar"),
+                "nested.txt", "jakarta.servlet");
     }
 
     @Test
-    public void testMigrateNestedJarInWar() throws Exception {
+    public void testMigrateJarFileInMemoryBasic() throws Exception {
         File jarFile = new File("target/test-classes/hellocgi.jar");
         File jarFileTarget = tempFolder.newFile("nested-test.jar");
 
@@ -639,7 +642,7 @@ public class MigrationTest {
     @Test
     public void testMigrateLargeStoredEntryInMemory() throws Exception {
         // Create a large file (>10MB) to test in-memory migration with large 
STORED entries
-        byte[] largeContent = new byte[11 * 1024 * 024]; // 11MB
+        byte[] largeContent = new byte[11 * 1024 * 1024]; // 11MB
         for (int i = 0; i < largeContent.length; i++) {
             largeContent[i] = (byte) (i % 256);
         }
@@ -671,7 +674,8 @@ public class MigrationTest {
     @Test
     public void testMigrateNestedArchiveWithCache() throws Exception {
         // Create a nested JAR with javax.servlet references
-        File nestedJar = createNestedJarWithContent("nested.jar", 
"nested.txt", "javax.servlet.http.HttpServlet");
+        File nestedJar = createNestedJarWithContent(tempFolder.getRoot(), 
"nested.jar", "nested.txt",
+                "javax.servlet.http.HttpServlet");
 
         // Create a WAR containing the nested JAR
         File warFile = tempFolder.newFile("app.war");
@@ -715,7 +719,8 @@ public class MigrationTest {
     @Test
     public void testMigrateNestedArchiveWithCacheHit() throws Exception {
         // Create a nested JAR with javax.servlet references
-        File nestedJar = createNestedJarWithContent("nested-hit.jar", 
"nested.txt", "javax.servlet.http.HttpServlet");
+        File nestedJar = createNestedJarWithContent(tempFolder.getRoot(), 
"nested-hit.jar", "nested.txt",
+                "javax.servlet.http.HttpServlet");
 
         // Create two WARs with the same nested JAR
         File warFile1 = createWarWithNestedJar(nestedJar, "app1.war");
@@ -733,10 +738,35 @@ public class MigrationTest {
 
         assertTrue("First target WAR should exist", warTarget1.exists());
 
+        // The nested JAR should now be stored in the cache
+        File cachedJar = null;
+        File[] subdirs = cacheDir.listFiles();
+        if (subdirs != null) {
+            for (File subdir : subdirs) {
+                if (subdir.isDirectory()) {
+                    File[] files = subdir.listFiles();
+                    if (files != null) {
+                        for (File file : files) {
+                            if (file.isFile() && 
file.getName().endsWith(".jar")) {
+                                cachedJar = file;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        assertNotNull("Nested JAR should be cached after first migration", 
cachedJar);
+
         // Create second WAR with same nested JAR
         File warFile2 = createWarWithNestedJar(nestedJar, "app2.war");
         File warTarget2 = tempFolder.newFile("app2-migrated.war");
 
+        // Replace the cached content with a canary: if the second migration
+        // hits the cache, the nested JAR in the second WAR must be a byte
+        // for byte copy of the canary.
+        byte[] canary = "cached nested 
jar".getBytes(StandardCharsets.ISO_8859_1);
+        Files.write(cachedJar.toPath(), canary);
+
         // Second migration - should hit cache for nested JAR
         Migration migration2 = new Migration();
         migration2.setSource(warFile2);
@@ -747,9 +777,15 @@ public class MigrationTest {
 
         assertTrue("Second target WAR should exist", warTarget2.exists());
 
-        // Verify both WARs have migrated nested content
-        for (File warTarget : new File[]{warTarget1, warTarget2}) {
-            verifyNestedJarContentMigrated(warTarget, 
"WEB-INF/lib/nested.jar", "jakarta.servlet");
+        // First WAR must have the migrated nested content
+        verifyNestedJarContentMigrated(warTarget1, "WEB-INF/lib/nested.jar", 
"jakarta.servlet");
+
+        // Second WAR's nested JAR must be served from the cache (the canary)
+        try (JarFile war = new JarFile(warTarget2)) {
+            JarEntry nestedEntry = war.getJarEntry("WEB-INF/lib/nested.jar");
+            assertNotNull("Nested JAR should exist", nestedEntry);
+            byte[] nestedJarBytes = 
readAllBytes(war.getInputStream(nestedEntry), (int) nestedEntry.getSize());
+            assertArrayEquals("Nested JAR should be served from the cache", 
canary, nestedJarBytes);
         }
     }
 
@@ -768,8 +804,8 @@ public class MigrationTest {
         return warFile;
     }
 
-    private File createNestedJarWithContent(String jarName, String entryName, 
String content) throws Exception {
-        File nestedJar = tempFolder.newFile(jarName);
+    private File createNestedJarWithContent(File parentDir, String jarName, 
String entryName, String content) throws Exception {
+        File nestedJar = new File(parentDir, jarName);
         try (FileOutputStream fos = new FileOutputStream(nestedJar);
                 
org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream zos =
                         new 
org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream(fos)) {
@@ -784,11 +820,12 @@ public class MigrationTest {
 
     private void verifyHelloCGIMigrated(File jarFileTarget) throws Exception {
         File cgiapiFile = new File("target/test-classes/cgi-api.jar");
-        URLClassLoader classloader = new URLClassLoader(
+        try (URLClassLoader classloader = new URLClassLoader(
                 new URL[]{jarFileTarget.toURI().toURL(), 
cgiapiFile.toURI().toURL()},
-                ClassLoader.getSystemClassLoader().getParent());
-        Class<?> cls = Class.forName("org.apache.tomcat.jakartaee.HelloCGI", 
true, classloader);
-        assertEquals("jakarta.servlet.CommonGatewayInterface", 
cls.getSuperclass().getName());
+                ClassLoader.getSystemClassLoader().getParent())) {
+            Class<?> cls = 
Class.forName("org.apache.tomcat.jakartaee.HelloCGI", true, classloader);
+            assertEquals("jakarta.servlet.CommonGatewayInterface", 
cls.getSuperclass().getName());
+        }
     }
 
     private void verifyNestedJarContentMigrated(File warFile, String 
nestedEntryName, String expectedContent) throws Exception {
@@ -883,7 +920,7 @@ public class MigrationTest {
         assertTrue("Large JAR should still exist", largeJar.exists());
         assertTrue("hasConverted should be true", migration.hasConverted());
 
-        // Verify the text file was migrated
+        // Verify the text file was migrated and the large entry was preserved
         try (JarFile jar = new JarFile(largeJar)) {
             JarEntry textEntry = jar.getJarEntry("test.txt");
             assertNotNull("test.txt should exist", textEntry);
@@ -891,6 +928,11 @@ public class MigrationTest {
             byte[] textBytes = readAllBytes(jar.getInputStream(textEntry), 
(int) textEntry.getSize());
             String migratedText = new String(textBytes, 
StandardCharsets.ISO_8859_1);
             assertTrue("Text should be migrated", 
migratedText.contains("jakarta.servlet"));
+
+            JarEntry largeEntry = jar.getJarEntry("large-data.bin");
+            assertNotNull("large-data.bin should exist", largeEntry);
+            byte[] largeBytes = readAllBytes(jar.getInputStream(largeEntry), 
(int) largeEntry.getSize());
+            assertArrayEquals("Large entry content should be preserved", 
largeContent, largeBytes);
         }
     }
 
@@ -905,9 +947,11 @@ public class MigrationTest {
         File sourceFile = new File(subDir2, "test.txt");
         Files.write(sourceFile.toPath(), 
"javax.servlet".getBytes(StandardCharsets.ISO_8859_1));
 
-        // Create a destination where nested subdir can't be created
-        // Use /proc as it's typically a read-only mount on Linux
-        File destDir = new File("/proc/nested-test-dest");
+        // Create a regular file where a nested subdirectory is expected so
+        // that the subdirectory cannot be created (portable across platforms)
+        File destDir = tempFolder.newFolder("nested-test-dest");
+        File blockedSubDir = new File(destDir, "level1");
+        Files.createFile(blockedSubDir.toPath());
 
         Migration migration = new Migration();
         migration.setSource(sourceDir);
@@ -950,7 +994,8 @@ public class MigrationTest {
     @Test
     public void testMigrateNestedJarInWarStreaming() throws Exception {
         // Create a WAR with a nested JAR that has javax references
-        File nestedJar = createNestedJarWithContent("nested-streaming.jar", 
"nested.txt", "javax.servlet.http.HttpServlet");
+        File nestedJar = createNestedJarWithContent(tempFolder.getRoot(), 
"nested-streaming.jar", "nested.txt",
+                "javax.servlet.http.HttpServlet");
 
         File warFile = createWarWithNestedJar(nestedJar, "streaming-test.war");
         File warTarget = tempFolder.newFile("streaming-test-migrated.war");
@@ -971,7 +1016,8 @@ public class MigrationTest {
     @Test
     public void testMigrateNestedJarInWarInMemory() throws Exception {
         // Create a WAR with a nested JAR that has javax references
-        File nestedJar = createNestedJarWithContent("nested-memory.jar", 
"nested.txt", "javax.servlet.http.HttpServlet");
+        File nestedJar = createNestedJarWithContent(tempFolder.getRoot(), 
"nested-memory.jar", "nested.txt",
+                "javax.servlet.http.HttpServlet");
 
         File warFile = createWarWithNestedJar(nestedJar, "memory-test.war");
         File warTarget = tempFolder.newFile("memory-test-migrated.war");
@@ -991,24 +1037,48 @@ public class MigrationTest {
 
     @Test
     public void testMigrateWithStoreMethodInZip() throws Exception {
-        File jarFile = new File("target/test-classes/hellocgi.jar");
-        File jarFileTarget = tempFolder.newFile("stored-method-test.jar");
+        // Create a JAR with a STORED (uncompressed) entry
+        File jarFile = createStoredEntryJar("stored-method-test.jar", 
"test.txt",
+                "javax.servlet.http.HttpServlet");
+        File jarFileTarget = tempFolder.newFile("stored-method-migrated.jar");
 
         Migration migration = new Migration();
         migration.setSource(jarFile);
         migration.setDestination(jarFileTarget);
+        migration.setZipInMemory(false); // Streaming mode handles STORED 
entries
         migration.execute();
 
+        assertTrue("Target JAR should exist", jarFileTarget.exists());
+
         try (JarFile jar = new JarFile(jarFileTarget)) {
-            java.util.Enumeration<java.util.jar.JarEntry> entries = 
jar.entries();
-            while (entries.hasMoreElements()) {
-                java.util.jar.JarEntry entry = entries.nextElement();
-                if (!entry.isDirectory()) {
-                    break;
-                }
-            }
+            JarEntry entry = jar.getJarEntry("test.txt");
+            assertNotNull("Stored entry should exist in migrated JAR", entry);
+            assertEquals("Stored entry should remain stored", 
java.util.zip.ZipEntry.STORED, entry.getMethod());
+
+            byte[] content = readAllBytes(jar.getInputStream(entry), (int) 
entry.getSize());
+            assertTrue("Stored entry content should be migrated",
+                    new String(content, 
StandardCharsets.ISO_8859_1).contains("jakarta.servlet"));
         }
-        assertTrue("Target JAR should exist", jarFileTarget.exists());
+    }
+
+    private File createStoredEntryJar(String jarName, String entryName, String 
content) throws Exception {
+        File jarFile = tempFolder.newFile(jarName);
+        byte[] data = content.getBytes(StandardCharsets.ISO_8859_1);
+        try (FileOutputStream fos = new FileOutputStream(jarFile);
+                
org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream zos =
+                        new 
org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream(fos)) {
+            org.apache.commons.compress.archivers.zip.ZipArchiveEntry entry =
+                    new 
org.apache.commons.compress.archivers.zip.ZipArchiveEntry(entryName);
+            
entry.setMethod(org.apache.commons.compress.archivers.zip.ZipArchiveEntry.STORED);
+            entry.setSize(data.length);
+            CRC32 crc = new CRC32();
+            crc.update(data);
+            entry.setCrc(crc.getValue());
+            zos.putArchiveEntry(entry);
+            zos.write(data);
+            zos.closeArchiveEntry();
+        }
+        return jarFile;
     }
 
     @Test
@@ -1028,7 +1098,8 @@ public class MigrationTest {
     @Test
     public void testMigrateFileToNewParentDirectory() throws Exception {
         File sourceFile = new File("target/test-classes/HelloServlet.java");
-        File destFile = new File(tempFolder.newFolder("new", "parent"), 
"migrated.java");
+        // Parent directory is not pre-created: execute() must create it
+        File destFile = new File(tempFolder.getRoot(), 
"new/parent/migrated.java");
 
         Migration migration = new Migration();
         migration.setSource(sourceFile);
@@ -1091,11 +1162,14 @@ public class MigrationTest {
 
         MigrationCLI.main(new String[] {
                 "-matchExcludesAgainstPathName",
+                "-exclude=*/HelloServlet.java",
                 sourceFile.getAbsolutePath(),
                 targetFile.getAbsolutePath()
         });
 
         assertTrue("Target file should exist", targetFile.exists());
+        String content = FileUtils.readFileToString(targetFile, 
StandardCharsets.UTF_8);
+        assertTrue("Excluded file should not be converted", 
content.contains("import javax.servlet"));
     }
 
     @Test
@@ -1104,23 +1178,16 @@ public class MigrationTest {
         File targetFile = tempFolder.newFile("cli-cache-retention.java");
         File cacheDir = tempFolder.newFolder("cache-retention-test");
 
-        try {
-            MigrationCLI.main(new String[] {
-                    "-cache",
-                    "-cacheLocation=" + cacheDir.getAbsolutePath(),
-                    "-cacheRetention=7",
-                    sourceFile.getAbsolutePath(),
-                    targetFile.getAbsolutePath()
-            });
-
-            assertTrue("Target file should exist", targetFile.exists());
-            assertTrue("Cache directory should be created", cacheDir.exists());
-        } finally {
-            // Clean up
-            if (cacheDir.exists()) {
-                FileUtils.deleteDirectory(cacheDir);
-            }
-        }
+        MigrationCLI.main(new String[] {
+                "-cache",
+                "-cacheLocation=" + cacheDir.getAbsolutePath(),
+                "-cacheRetention=7",
+                sourceFile.getAbsolutePath(),
+                targetFile.getAbsolutePath()
+        });
+
+        assertTrue("Target file should exist", targetFile.exists());
+        assertTrue("Cache directory should be created", cacheDir.exists());
     }
 
     @Test
@@ -1191,20 +1258,34 @@ public class MigrationTest {
     }
 
     @Test
-    public void testMigrateWithDefaultExcludesDisabled() throws Exception {
-        File sourceFile = new File("target/test-classes/HelloServlet.java");
-        File destFile = tempFolder.newFile("no-default-excludes.java");
+    public void testMigrateWithDefaultExcludes() throws Exception {
+        // A valid archive whose file name matches one of the default exclude
+        // patterns (commons-lang-*.jar). With the default excludes enabled
+        // (the default) it is copied unchanged and not converted.
+        File sourceDirectory = tempFolder.newFolder("default-excludes-test");
+        createNestedJarWithContent(sourceDirectory, "commons-lang-3.12.0.jar", 
"nested.txt",
+                "javax.servlet.http.HttpServlet");
+        File destinationDirectory = 
tempFolder.newFolder("default-excludes-dest");
 
         Migration migration = new Migration();
-        migration.setSource(sourceFile);
-        migration.setDestination(destFile);
-        migration.setEnableDefaultExcludes(false);
-        migration.setEESpecProfile(EESpecProfiles.EE);
+        migration.setSource(sourceDirectory);
+        migration.setDestination(destinationDirectory);
         migration.execute();
 
-        assertTrue("Migrated file should exist", destFile.exists());
-        String migratedSource = FileUtils.readFileToString(destFile, 
StandardCharsets.UTF_8);
-        assertTrue("Imports should be migrated", 
migratedSource.contains("import jakarta.servlet"));
+        File destArchive = new File(destinationDirectory, 
"commons-lang-3.12.0.jar");
+        assertTrue("Excluded archive should still be copied", 
destArchive.exists());
+        assertFalse("Excluded archive should not be converted", 
migration.hasConverted());
+        verifyArchiveEntryContent(destArchive, "nested.txt", "javax.servlet");
+    }
+
+    private void verifyArchiveEntryContent(File archiveFile, String entryName, 
String expectedContent) throws Exception {
+        try (JarFile archive = new JarFile(archiveFile)) {
+            JarEntry entry = archive.getJarEntry(entryName);
+            assertNotNull("Entry should exist in " + archiveFile.getName(), 
entry);
+            byte[] content = readAllBytes(archive.getInputStream(entry), (int) 
entry.getSize());
+            assertTrue("Entry content in " + archiveFile.getName() + " should 
contain " + expectedContent,
+                    new String(content, 
StandardCharsets.ISO_8859_1).contains(expectedContent));
+        }
     }
 
     @Test
diff --git 
a/src/test/java/org/apache/tomcat/jakartaee/NoExitSecurityManager.java 
b/src/test/java/org/apache/tomcat/jakartaee/NoExitSecurityManager.java
index 446e96c..8e7405e 100644
--- a/src/test/java/org/apache/tomcat/jakartaee/NoExitSecurityManager.java
+++ b/src/test/java/org/apache/tomcat/jakartaee/NoExitSecurityManager.java
@@ -19,6 +19,16 @@ package org.apache.tomcat.jakartaee;
 
 import java.security.Permission;
 
+/**
+ * A {@link SecurityManager} used by the tests to turn
+ * {@link System#exit(int)} into a {@link SecurityException} (whose message is
+ * the exit status) instead of terminating the test JVM. All other permission
+ * checks are permitted.
+ * <p>
+ * It can only be installed on JDK versions that still support a security
+ * manager. On JDK 25 and later (JEP 486) {@code System.setSecurityManager()}
+ * itself fails and the tests that rely on this class are skipped.
+ */
 public class NoExitSecurityManager extends SecurityManager {
 
     @Override
diff --git a/src/test/java/org/apache/tomcat/jakartaee/StringManagerTest.java 
b/src/test/java/org/apache/tomcat/jakartaee/StringManagerTest.java
index 095baa6..1b7b51c 100644
--- a/src/test/java/org/apache/tomcat/jakartaee/StringManagerTest.java
+++ b/src/test/java/org/apache/tomcat/jakartaee/StringManagerTest.java
@@ -69,13 +69,6 @@ public class StringManagerTest {
                 "nonexistent.key.12345", result);
     }
 
-    @Test
-    public void testGetStringWithArgsNoArgs() {
-        StringManager sm = StringManager.getManager(Migration.class);
-        String result = sm.getString("migration.notCompleted");
-        assertEquals("Migration has not completed", result);
-    }
-
     @Test
     public void testGetManagerByClass() {
         StringManager sm1 = StringManager.getManager(Migration.class);
@@ -101,9 +94,11 @@ public class StringManagerTest {
     public void testGetManagerDifferentLocale() {
         StringManager sm1 = 
StringManager.getManager("org.apache.tomcat.jakartaee", Locale.ENGLISH);
         StringManager sm2 = 
StringManager.getManager("org.apache.tomcat.jakartaee", Locale.FRANCE);
-        // May or may not be the same depending on available bundles
+        // Managers are cached per (package, requested locale) so different
+        // locales always produce different instances
         assertNotNull("Manager should not be null", sm1);
         assertNotNull("Manager should not be null", sm2);
+        assertNotSame("Different locales should return different managers", 
sm1, sm2);
     }
 
     @Test
diff --git a/src/test/java/org/apache/tomcat/jakartaee/TesterConstants.java 
b/src/test/java/org/apache/tomcat/jakartaee/TesterConstants.java
index afbf569..27acfb7 100644
--- a/src/test/java/org/apache/tomcat/jakartaee/TesterConstants.java
+++ b/src/test/java/org/apache/tomcat/jakartaee/TesterConstants.java
@@ -1,5 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package org.apache.tomcat.jakartaee;
 
+/**
+ * Test fixture whose constants exist to embed {@code javax} class names in
+ * the constant pool of this class file.
+ * <p>
+ * {@link ClassConverterTest} converts that class file and checks which of the
+ * embedded strings were changed. The {@code JAVA_PRESENT_} constants reference
+ * a class that exists in the {@code jakarta} namespace, so the converter must
+ * rewrite them. The {@code JAVA_NOT_PRESENT_} constants reference a class that
+ * does not exist in either namespace, so the converter must leave them
+ * unchanged.
+ */
 public class TesterConstants {
 
     public static final String JAVA_PRESENT_DOT = 
"javax.servlet.CommonGatewayInterface";
diff --git a/src/test/java/org/apache/tomcat/jakartaee/TextConverterTest.java 
b/src/test/java/org/apache/tomcat/jakartaee/TextConverterTest.java
index bde3479..0a10fad 100644
--- a/src/test/java/org/apache/tomcat/jakartaee/TextConverterTest.java
+++ b/src/test/java/org/apache/tomcat/jakartaee/TextConverterTest.java
@@ -1,3 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package org.apache.tomcat.jakartaee;
 
 import static org.junit.Assert.assertEquals;
@@ -14,26 +31,27 @@ public class TextConverterTest {
 
     private static final String TEST_FILENAME = "text.txt";
 
-       private static final String INPUT = 
"javax.servlet.http.HttpServletRequest";
-       private static final String OUTPUT = 
"jakarta.servlet.http.HttpServletRequest";
+    private static final String INPUT = 
"javax.servlet.http.HttpServletRequest";
+    private static final String OUTPUT = 
"jakarta.servlet.http.HttpServletRequest";
 
-       @Test
-       public void testConvert() throws IOException {
+    @Test
+    public void testConvert() throws IOException {
 
-               // prepare
-               TextConverter converter = new TextConverter();
-               ByteArrayInputStream in = new 
ByteArrayInputStream(INPUT.getBytes(StandardCharsets.ISO_8859_1));
-               ByteArrayOutputStream out = new ByteArrayOutputStream();
-               EESpecProfile profile = EESpecProfiles.EE;
+        // prepare
+        TextConverter converter = new TextConverter();
+        ByteArrayInputStream in = new 
ByteArrayInputStream(INPUT.getBytes(StandardCharsets.ISO_8859_1));
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        EESpecProfile profile = EESpecProfiles.EE;
 
-               // test
-               converter.convert(TEST_FILENAME, in, out, profile);
+        // test
+        boolean converted = converter.convert(TEST_FILENAME, in, out, profile);
 
-               // assert
-               String result = new String(out.toByteArray(), 
StandardCharsets.ISO_8859_1);
-               assertEquals(OUTPUT, result);
+        // assert
+        assertTrue("Should convert when javax packages present", converted);
+        String result = new String(out.toByteArray(), 
StandardCharsets.ISO_8859_1);
+        assertEquals(OUTPUT, result);
 
-       }
+    }
 
     @Test
     public void testAcceptsJava() {
@@ -168,6 +186,20 @@ public class TextConverterTest {
         assertEquals(content, result);
     }
 
+    @Test
+    public void testConvertJakartaWithJee8Profile() throws IOException {
+        TextConverter converter = new TextConverter();
+        String content = "import jakarta.servlet.http.HttpServletRequest;";
+        ByteArrayInputStream in = new 
ByteArrayInputStream(content.getBytes(StandardCharsets.ISO_8859_1));
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        boolean converted = converter.convert("Test.java", in, out, 
EESpecProfiles.JEE8);
+
+        assertTrue("JEE8 profile should convert jakarta packages", converted);
+        String result = new String(out.toByteArray(), 
StandardCharsets.ISO_8859_1);
+        assertEquals("import javax.servlet.http.HttpServletRequest;", result);
+    }
+
     @Test
     public void testConvertEmptyContent() throws IOException {
         TextConverter converter = new TextConverter();
diff --git a/src/test/resources/testbuild.xml b/src/test/resources/testbuild.xml
index 19b4dec..9301abf 100644
--- a/src/test/resources/testbuild.xml
+++ b/src/test/resources/testbuild.xml
@@ -1,4 +1,4 @@
-<project name="Jsign Ant tests">
+<project name="Jakarta EE migration Ant tests">
 
   <taskdef name="javax2jakarta" 
classname="org.apache.tomcat.jakartaee.MigrationTask"/>
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to