Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,11 @@
if (!Files.exists(restorationPath)) {
Files.createDirectories(restorationPath);
}
CacheUtils.unzip(cachedZip.toPath(), restorationPath, cacheConfig.isPreservePermissions());
CacheUtils.unzip(
cachedZip.toPath(),
restorationPath,
cacheConfig.isPreservePermissions(),
cacheConfig.isPreserveTimestamps());
LOGGER.debug("Restored directory artifact by unzipping: {} -> {}", artifact.getFileName(), restorationPath);
}

Expand Down Expand Up @@ -482,7 +486,7 @@
project.addLifecyclePhase("package");
}
}
restoredAttachedArtifacts.forEach(project::addAttachedArtifact);

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-17-zulu 3.10.0-rc-1

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-17-zulu 3.10.0-rc-1

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-21-zulu 3.10.0-rc-1

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-25-zulu 3.10.0-rc-1

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-25-zulu 4.0.0-rc-5

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-21-zulu 4.0.0-rc-5

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-21-zulu 3.10.0-rc-1

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-21-zulu 4.0.0-rc-5

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-25-zulu 3.10.0-rc-1

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-25-zulu 4.0.0-rc-5

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-25-zulu 3.10.0-rc-1

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-21-zulu 3.10.0-rc-1

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-21-zulu 3.10.0-rc-1

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-25-zulu 3.10.0-rc-1

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated
restorationReport.setSuccess(true);
} catch (Exception e) {
LOGGER.debug("Cannot restore cache, continuing with normal build.", e);
Expand Down Expand Up @@ -708,7 +712,12 @@
File originalFile)
throws IOException {
Path tempZip = Files.createTempFile("maven-cache-", "-" + project.getArtifactId() + ".zip");
boolean hasFiles = CacheUtils.zip(originalFile.toPath(), tempZip, "*", cacheConfig.isPreservePermissions());
boolean hasFiles = CacheUtils.zip(
originalFile.toPath(),
tempZip,
"*",
cacheConfig.isPreservePermissions(),
cacheConfig.isPreserveTimestamps());
if (hasFiles) {
// Temporarily replace artifact file with zip for saving
projectArtifact.setFile(tempZip.toFile());
Expand Down Expand Up @@ -1059,7 +1068,8 @@
throws IOException {
Path temp = Files.createTempFile("maven-incremental-", project.getArtifactId());
temp.toFile().deleteOnExit();
boolean hasFile = CacheUtils.zip(dir, temp, glob, cacheConfig.isPreservePermissions(), cacheConfig.isPreserveTimestamps());
boolean hasFile = CacheUtils.zip(
dir, temp, glob, cacheConfig.isPreservePermissions(), cacheConfig.isPreserveTimestamps());
if (hasFile) {
projectHelper.attachArtifact(project, "zip", classifier, temp.toFile());
}
Expand All @@ -1074,7 +1084,8 @@
if (!Files.exists(outputDir)) {
Files.createDirectories(outputDir);
}
CacheUtils.unzip(artifactFilePath, outputDir, cacheConfig.isPreservePermissions(), cacheConfig.isPreserveTimestamps());
CacheUtils.unzip(
artifactFilePath, outputDir, cacheConfig.isPreservePermissions(), cacheConfig.isPreserveTimestamps());
}

// TODO: move to config
Expand Down
68 changes: 68 additions & 0 deletions src/main/java/org/apache/maven/buildcache/CacheUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,23 @@
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Strings;
import org.apache.commons.lang3.mutable.MutableBoolean;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.handler.ArtifactHandler;
import org.apache.maven.buildcache.xml.build.CompletedExecution;
import org.apache.maven.buildcache.xml.build.PropertyValue;
import org.apache.maven.buildcache.xml.build.Scm;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.plugin.PluginParameterExpressionEvaluator;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.apache.maven.artifact.Artifact.LATEST_VERSION;
import static org.apache.maven.artifact.Artifact.SNAPSHOT_VERSION;
Expand All @@ -65,6 +71,7 @@
* Cache Utils
*/
public class CacheUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(CacheUtils.class);

public static boolean isPom(MavenProject project) {
return project.getPackaging().equals("pom");
Expand Down Expand Up @@ -108,7 +115,7 @@
}

public static Path getMultimoduleRoot(MavenSession session) {
return session.getRequest().getMultiModuleProjectDirectory().toPath();

Check warning on line 118 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-17-zulu 3.10.0-rc-1

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 118 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-17-zulu 3.10.0-rc-1

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 118 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-21-zulu 3.10.0-rc-1

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 118 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-25-zulu 3.10.0-rc-1

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 118 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-25-zulu 4.0.0-rc-5

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 118 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-21-zulu 4.0.0-rc-5

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 118 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-21-zulu 3.10.0-rc-1

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 118 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-21-zulu 4.0.0-rc-5

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 118 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-25-zulu 3.10.0-rc-1

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 118 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-25-zulu 4.0.0-rc-5

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 118 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-25-zulu 3.10.0-rc-1

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 118 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-21-zulu 3.10.0-rc-1

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 118 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-21-zulu 3.10.0-rc-1

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 118 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-25-zulu 3.10.0-rc-1

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated
}

public static Scm readGitInfo(MavenSession session) throws IOException {
Expand Down Expand Up @@ -405,4 +412,65 @@
}
return permissions;
}

static Object interpolateExpression(String expression, MavenSession session, MojoExecution execution) {
try {
PluginParameterExpressionEvaluator evaluator = new PluginParameterExpressionEvaluator(session, execution);
return evaluator.evaluate(expression);
} catch (ExpressionEvaluationException e) {
LOGGER.warn("Cannot interpolate expression '{}': {}", expression, e.getMessage(), e);
return expression; // return the expression as is when interpolation fails
}
}

static String normalizeValue(Object value, Path baseDirPath) {
if (value instanceof File) {
Path path = ((File) value).toPath();
return normalizedPath(path, baseDirPath);
} else if (value instanceof Path) {
return normalizedPath(((Path) value), baseDirPath);
} else if (value != null && value.getClass().isArray()) {
return ArrayUtils.toString(value);
} else {
return String.valueOf(value);
}
}

/*
Best effort to normalize paths from Mojo fields.
- all absolute paths under project root are relativized for portability
- redundant '..' and '.' are removed to have consistent views on all paths
- all relative paths are considered portable and are not be touched
- absolute paths outside of project directory cannot be deterministically
relativized and are not touched
*/
private static String normalizedPath(Path path, Path baseDirPath) {
boolean isProjectSubdir = path.isAbsolute() && path.startsWith(baseDirPath);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(
"normalizedPath isProjectSubdir {} path '{}' - baseDirPath '{}', path.isAbsolute() {},"
+ " path.startsWith(baseDirPath) {}",
isProjectSubdir,
path,
baseDirPath,
path.isAbsolute(),
path.startsWith(baseDirPath));
}
Path preparedPath = isProjectSubdir ? baseDirPath.relativize(path) : path;
String normalizedPath = preparedPath.normalize().toString();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("normalizedPath '{}' - {} return {}", path, baseDirPath, normalizedPath);
}
return normalizedPath;
}

static void addProperty(
CompletedExecution execution, String propertyName, Object value, Path baseDirPath, boolean tracked) {
final PropertyValue valueType = new PropertyValue();
valueType.setName(propertyName);
final String valueText = normalizeValue(value, baseDirPath);
valueType.setValue(valueText);
valueType.setTracked(tracked);
execution.addProperty(valueType);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ void testPermissionsAffectFileHashWhenEnabled() throws IOException {
// When: Create ZIP files with preservePermissions=true
Path zip1 = tempDir.resolve("cache1.zip");
Path zip2 = tempDir.resolve("cache2.zip");
CacheUtils.zip(sourceDir1, zip1, "*", true);
CacheUtils.zip(sourceDir2, zip2, "*", true);
CacheUtils.zip(sourceDir1, zip1, "*", true, false);
CacheUtils.zip(sourceDir2, zip2, "*", true, false);

// Then: ZIP files should have different hashes despite identical content
byte[] hash1 = Files.readAllBytes(zip1);
Expand Down Expand Up @@ -124,16 +124,16 @@ void testPermissionsDoNotAffectHashWhenDisabled() throws IOException {
// When: Create ZIP files with preservePermissions=false
Path zip1 = tempDir.resolve("cache1.zip");
Path zip2 = tempDir.resolve("cache2.zip");
CacheUtils.zip(sourceDir1, zip1, "*", false);
CacheUtils.zip(sourceDir2, zip2, "*", false);
CacheUtils.zip(sourceDir1, zip1, "*", false, false);
CacheUtils.zip(sourceDir2, zip2, "*", false, false);

// Unzip and verify permissions are NOT preserved
Path extractDir1 = tempDir.resolve("extracted1");
Path extractDir2 = tempDir.resolve("extracted2");
Files.createDirectories(extractDir1);
Files.createDirectories(extractDir2);
CacheUtils.unzip(zip1, extractDir1, false);
CacheUtils.unzip(zip2, extractDir2, false);
CacheUtils.unzip(zip1, extractDir1, false, false);
CacheUtils.unzip(zip2, extractDir2, false, false);

Path extractedFile1 = extractDir1.resolve("script.sh");
Path extractedFile2 = extractDir2.resolve("script.sh");
Expand Down Expand Up @@ -163,12 +163,12 @@ void testPermissionsAreRestoredOnUnzip() throws Exception {

// When: ZIP file is created from that directory with "preservePermissions=true"
Path zippedFile = tempDir.resolve("target.zip");
CacheUtils.zip(source, zippedFile, "*", true);
CacheUtils.zip(source, zippedFile, "*", true, false);

// And: ZIP file is extracted with "preservePermissions=true"
Path unzippedDir = tempDir.resolve("target-unzipped");
Files.createDirectories(unzippedDir);
CacheUtils.unzip(zippedFile, unzippedDir, true);
CacheUtils.unzip(zippedFile, unzippedDir, true, false);

// Then: extracted file should have executable permissions
Path extractedScript = unzippedDir.resolve("script.sh");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@
*/
package org.apache.maven.buildcache;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
Expand All @@ -38,7 +35,9 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

Expand Down Expand Up @@ -78,11 +77,11 @@ void testFileTimestampPreservation() throws IOException {

// When: Zip and unzip using CacheUtils
Path zipFile = tempDir.resolve("cache.zip");
CacheUtils.zip(sourceDir, zipFile, "*", true);
CacheUtils.zip(sourceDir, zipFile, "*", false, true);

Path extractDir = tempDir.resolve("extracted");
Files.createDirectories(extractDir);
CacheUtils.unzip(zipFile, extractDir, true);
CacheUtils.unzip(zipFile, extractDir, false, true);

// Then: File timestamps should be preserved
Path extractedFile1 = extractDir.resolve("com").resolve("example").resolve("Service.class");
Expand Down Expand Up @@ -124,11 +123,11 @@ void testDirectoryTimestampPreservation() throws IOException {

// When: Zip and unzip using CacheUtils
Path zipFile = tempDir.resolve("cache.zip");
CacheUtils.zip(sourceDir, zipFile, "*", true);
CacheUtils.zip(sourceDir, zipFile, "*", false, true);

Path extractDir = tempDir.resolve("extracted");
Files.createDirectories(extractDir);
CacheUtils.unzip(zipFile, extractDir, true);
CacheUtils.unzip(zipFile, extractDir, false, true);

// Then: Directory timestamps should be preserved
Path extractedDir = extractDir.resolve("com").resolve("example");
Expand All @@ -154,7 +153,7 @@ void testDirectoryEntriesStoredInZip() throws IOException {

// When: Create zip
Path zipFile = tempDir.resolve("cache.zip");
CacheUtils.zip(sourceDir, zipFile, "*", true);
CacheUtils.zip(sourceDir, zipFile, "*", false, true);

// Then: Zip should contain directory entries
List<String> entries = new ArrayList<>();
Expand Down Expand Up @@ -192,7 +191,7 @@ void testTimestampsInZipEntries() throws IOException {

// When: Create zip
Path zipFile = tempDir.resolve("cache.zip");
CacheUtils.zip(sourceDir, zipFile, "*", true);
CacheUtils.zip(sourceDir, zipFile, "*", false, true);

// Then: Zip entries should have correct timestamps
try (ZipInputStream zis = new ZipInputStream(Files.newInputStream(zipFile))) {
Expand Down Expand Up @@ -250,7 +249,7 @@ void testMavenWarningScenario() throws IOException {
Instant packageTime = compileTime.plus(5, ChronoUnit.SECONDS);
Path jarFile = tempDir.resolve("target").resolve("my-module-1.0.jar");
Files.createDirectories(jarFile.getParent());
CacheUtils.zip(classesDir, jarFile, "*", true);
CacheUtils.zip(classesDir, jarFile, "*", false, true);
Files.setLastModifiedTime(jarFile, FileTime.from(packageTime));

long jarTimestamp = Files.getLastModifiedTime(jarFile).toMillis();
Expand All @@ -259,7 +258,7 @@ void testMavenWarningScenario() throws IOException {
deleteRecursively(classesDir);

// When: Simulate cache restoration - restore JAR contents back to target/classes
CacheUtils.unzip(jarFile, classesDir, true);
CacheUtils.unzip(jarFile, classesDir, false, true);

// Then: Restored file should NOT be newer than JAR
Path restoredClass = classesDir.resolve("com").resolve("example").resolve("Service.class");
Expand All @@ -270,11 +269,11 @@ void testMavenWarningScenario() throws IOException {
if (restoredTimestamp > jarTimestamp) {
long diffSeconds = (restoredTimestamp - jarTimestamp) / 1000;
fail(String.format(
"[WARNING] File 'target/classes/com/example/Service.class' is more recent%n" +
" than the packaged artifact 'my-module-1.0.jar'%n" +
" (difference: %d seconds)%n" +
" Please run a full 'mvn clean package' build%n%n" +
"This indicates timestamps are not being preserved correctly during cache restoration.",
"[WARNING] File 'target/classes/com/example/Service.class' is more recent%n"
+ " than the packaged artifact 'my-module-1.0.jar'%n"
+ " (difference: %d seconds)%n"
+ " Please run a full 'mvn clean package' build%n%n"
+ "This indicates timestamps are not being preserved correctly during cache restoration.",
diffSeconds));
}

Expand All @@ -299,8 +298,7 @@ void testMultipleFilesTimestampConsistency() throws IOException {
packageDir.resolve("Service.class"),
packageDir.resolve("Repository.class"),
packageDir.resolve("Controller.class"),
packageDir.resolve("Model.class")
);
packageDir.resolve("Model.class"));

for (Path file : files) {
writeString(file, "// " + file.getFileName() + " content");
Expand All @@ -311,11 +309,11 @@ void testMultipleFilesTimestampConsistency() throws IOException {

// When: Zip and unzip
Path zipFile = tempDir.resolve("cache.zip");
CacheUtils.zip(sourceDir, zipFile, "*", true);
CacheUtils.zip(sourceDir, zipFile, "*", false, true);

Path extractDir = tempDir.resolve("extracted");
Files.createDirectories(extractDir);
CacheUtils.unzip(zipFile, extractDir, true);
CacheUtils.unzip(zipFile, extractDir, false, true);

// Then: All files should have consistent timestamps
for (Path originalFile : files) {
Expand Down Expand Up @@ -348,11 +346,11 @@ void testPreserveTimestampsFalse() throws IOException {

// When: Zip and unzip with preserveTimestamps=false
Path zipFile = tempDir.resolve("cache.zip");
CacheUtils.zip(sourceDir, zipFile, "*", false);
CacheUtils.zip(sourceDir, zipFile, "*", false, false);

Path extractDir = tempDir.resolve("extracted");
Files.createDirectories(extractDir);
CacheUtils.unzip(zipFile, extractDir, false);
CacheUtils.unzip(zipFile, extractDir, false, false);

// Then: Extracted file should NOT have the original timestamp
// (it should have a timestamp close to now, not 1 hour ago)
Expand All @@ -365,17 +363,22 @@ void testPreserveTimestampsFalse() throws IOException {
long diffFromCurrent = Math.abs(extractedTimestamp - currentTime);

// The extracted file should be much closer to current time than to the old timestamp
assertTrue(diffFromCurrent < diffFromOriginal,
String.format("When preserveTimestamps=false, extracted file timestamp should be close to current time.%n" +
"Original timestamp (1 hour ago): %s (%d)%n" +
"Extracted timestamp: %s (%d)%n" +
"Current time: %s (%d)%n" +
"Diff from original: %d seconds%n" +
"Diff from current: %d seconds%n" +
"Expected: diff from current < diff from original",
Instant.ofEpochMilli(originalTimestamp), originalTimestamp,
Instant.ofEpochMilli(extractedTimestamp), extractedTimestamp,
Instant.ofEpochMilli(currentTime), currentTime,
assertTrue(
diffFromCurrent < diffFromOriginal,
String.format(
"When preserveTimestamps=false, extracted file timestamp should be close to current time.%n"
+ "Original timestamp (1 hour ago): %s (%d)%n"
+ "Extracted timestamp: %s (%d)%n"
+ "Current time: %s (%d)%n"
+ "Diff from original: %d seconds%n"
+ "Diff from current: %d seconds%n"
+ "Expected: diff from current < diff from original",
Instant.ofEpochMilli(originalTimestamp),
originalTimestamp,
Instant.ofEpochMilli(extractedTimestamp),
extractedTimestamp,
Instant.ofEpochMilli(currentTime),
currentTime,
diffFromOriginal / 1000,
diffFromCurrent / 1000));
}
Expand All @@ -390,15 +393,14 @@ private void assertTimestampPreserved(String fileName, long expectedMs, long act

if (diffMs > TIMESTAMP_TOLERANCE_MS) {
String errorMessage = String.format(
"%s%n" +
"File: %s%n" +
"Expected timestamp: %s (%d)%n" +
"Actual timestamp: %s (%d)%n" +
"Difference: %d seconds (%.2f hours)%n" +
"%n" +
"Timestamps must be preserved within %d ms tolerance.%n" +
"This failure indicates CacheUtils.zip() or CacheUtils.unzip() is not%n" +
"correctly preserving file/directory timestamps.",
"%s%n" + "File: %s%n"
+ "Expected timestamp: %s (%d)%n"
+ "Actual timestamp: %s (%d)%n"
+ "Difference: %d seconds (%.2f hours)%n"
+ "%n"
+ "Timestamps must be preserved within %d ms tolerance.%n"
+ "This failure indicates CacheUtils.zip() or CacheUtils.unzip() is not%n"
+ "correctly preserving file/directory timestamps.",
message,
fileName,
Instant.ofEpochMilli(expectedMs),
Expand Down
Loading
Loading