You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by ol...@apache.org on 2024/04/30 21:01:31 UTC
(maven-build-cache-extension) branch master updated: [MBUILDCACHE-90] Mandatory clean option to store in cache (#103)
This is an automated email from the ASF dual-hosted git repository.
olamy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-build-cache-extension.git
The following commit(s) were added to refs/heads/master by this push:
new 8e3dad7 [MBUILDCACHE-90] Mandatory clean option to store in cache (#103)
8e3dad7 is described below
commit 8e3dad72f0a0a8b38778ca7faba1bb769adb3175
Author: Kévin Buntrock <ke...@kleegroup.com>
AuthorDate: Tue Apr 30 23:01:25 2024 +0200
[MBUILDCACHE-90] Mandatory clean option to store in cache (#103)
---
.../BuildCacheMojosExecutionStrategy.java | 5 +
.../apache/maven/buildcache/xml/CacheConfig.java | 5 +
.../maven/buildcache/xml/CacheConfigImpl.java | 6 +
src/main/mdo/build-cache-config.mdo | 6 +
src/site/markdown/parameters.md | 1 +
.../maven/buildcache/its/MandatoryCleanTest.java | 156 +++++++++++++++++++++
.../apache/maven/buildcache/util/LogFileUtils.java | 17 +++
.../projects/mandatory-clean/.mvn/extensions.xml | 25 ++++
.../.mvn/maven-build-cache-config.xml | 28 ++++
.../projects/mandatory-clean/forked-module/pom.xml | 56 ++++++++
.../maven/buildcache/ForkedExecutionClass.java | 24 ++++
.../mandatory-clean/non-forked-module/pom.xml | 32 +++++
.../java/org/apache/maven/buildcache/Test.java | 24 ++++
src/test/projects/mandatory-clean/pom.xml | 37 +++++
14 files changed, 422 insertions(+)
diff --git a/src/main/java/org/apache/maven/buildcache/BuildCacheMojosExecutionStrategy.java b/src/main/java/org/apache/maven/buildcache/BuildCacheMojosExecutionStrategy.java
index e35981d..7726312 100644
--- a/src/main/java/org/apache/maven/buildcache/BuildCacheMojosExecutionStrategy.java
+++ b/src/main/java/org/apache/maven/buildcache/BuildCacheMojosExecutionStrategy.java
@@ -150,6 +150,11 @@ public class BuildCacheMojosExecutionStrategy implements MojosExecutionStrategy
if (cacheState == INITIALIZED && (!result.isSuccess() || !restored)) {
if (cacheConfig.isSkipSave()) {
LOGGER.info("Cache saving is disabled.");
+ } else if (cacheConfig.isMandatoryClean()
+ && lifecyclePhasesHelper
+ .getCleanSegment(project, mojoExecutions)
+ .isEmpty()) {
+ LOGGER.info("Cache storing is skipped since there was no \"clean\" phase.");
} else {
final Map<String, MojoExecutionEvent> executionEvents = mojoListener.getProjectExecutions(project);
cacheController.save(result, mojoExecutions, executionEvents);
diff --git a/src/main/java/org/apache/maven/buildcache/xml/CacheConfig.java b/src/main/java/org/apache/maven/buildcache/xml/CacheConfig.java
index 797e1f4..3bcfa23 100644
--- a/src/main/java/org/apache/maven/buildcache/xml/CacheConfig.java
+++ b/src/main/java/org/apache/maven/buildcache/xml/CacheConfig.java
@@ -139,4 +139,9 @@ public interface CacheConfig {
* Flag to disable cache saving
*/
boolean isSkipSave();
+
+ /**
+ * Flag to save in cache only if a build went through the clean lifecycle
+ */
+ boolean isMandatoryClean();
}
diff --git a/src/main/java/org/apache/maven/buildcache/xml/CacheConfigImpl.java b/src/main/java/org/apache/maven/buildcache/xml/CacheConfigImpl.java
index 8c98afb..208c92d 100644
--- a/src/main/java/org/apache/maven/buildcache/xml/CacheConfigImpl.java
+++ b/src/main/java/org/apache/maven/buildcache/xml/CacheConfigImpl.java
@@ -92,6 +92,7 @@ public class CacheConfigImpl implements org.apache.maven.buildcache.xml.CacheCon
public static final String LAZY_RESTORE_PROPERTY_NAME = "maven.build.cache.lazyRestore";
public static final String RESTORE_GENERATED_SOURCES_PROPERTY_NAME = "maven.build.cache.restoreGeneratedSources";
public static final String ALWAYS_RUN_PLUGINS = "maven.build.cache.alwaysRunPlugins";
+ public static final String MANDATORY_CLEAN = "maven.build.cache.mandatoryClean";
/**
* Flag to control if we should skip lookup for cached artifacts globally or for a particular project even if
@@ -513,6 +514,11 @@ public class CacheConfigImpl implements org.apache.maven.buildcache.xml.CacheCon
return getProperty(SKIP_SAVE, false);
}
+ @Override
+ public boolean isMandatoryClean() {
+ return getProperty(MANDATORY_CLEAN, getConfiguration().isMandatoryClean());
+ }
+
@Override
public String getId() {
checkInitializedState();
diff --git a/src/main/mdo/build-cache-config.mdo b/src/main/mdo/build-cache-config.mdo
index 65f2ec7..9ff96c8 100644
--- a/src/main/mdo/build-cache-config.mdo
+++ b/src/main/mdo/build-cache-config.mdo
@@ -194,6 +194,12 @@ under the License.
TODO: not implemented
</description>
</field>
+ <field>
+ <name>mandatoryClean</name>
+ <type>boolean</type>
+ <defaultValue>false</defaultValue>
+ <description>Enable the cache storing ability only if a build went through the clean lifecycle.</description>
+ </field>
<field>
<name>multiModule</name>
<association>
diff --git a/src/site/markdown/parameters.md b/src/site/markdown/parameters.md
index abb2fe5..76aed1a 100644
--- a/src/site/markdown/parameters.md
+++ b/src/site/markdown/parameters.md
@@ -37,6 +37,7 @@ This document contains various configuration parameters supported by the cache e
| `-Dmaven.build.cache.alwaysRunPlugins=<list of plugins>` | Comma separated list of plugins to always run regardless of cache state. An example: `maven-deploy-plugin:*,maven-install-plugin:install` | Remote cache setup/tuning/troubleshooting |
| `-Dmaven.build.cache.skipCache=(true/false)` | Skip looking up artifacts in caches. Does not affect writing artifacts to caches, disables only reading when set to `true` | May be used to trigger a forced rebuild when matching artifacts do exist in caches |
| `-Dmaven.build.cache.skipSave=(true/false)` | Skip writing build result in caches. Does not affect reading from the cache. | Configuring MR builds to benefits from the cache, but restricting writes to the `master` branch |
+| `-Dmaven.build.cache.mandatoryClean=(true/false)` | Enable or disable the necessity to execute the `clean` phase in order to store the build result in cache | Reducing the risk to save "wrong" files in cache in a local dev environnement |
### Project-level properties
diff --git a/src/test/java/org/apache/maven/buildcache/its/MandatoryCleanTest.java b/src/test/java/org/apache/maven/buildcache/its/MandatoryCleanTest.java
new file mode 100644
index 0000000..72ded2c
--- /dev/null
+++ b/src/test/java/org/apache/maven/buildcache/its/MandatoryCleanTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.maven.buildcache.its;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.maven.buildcache.its.junit.IntegrationTest;
+import org.apache.maven.buildcache.util.LogFileUtils;
+import org.apache.maven.buildcache.xml.CacheConfigImpl;
+import org.apache.maven.it.VerificationException;
+import org.apache.maven.it.Verifier;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.maven.buildcache.xml.CacheConfigImpl.CACHE_LOCATION_PROPERTY_NAME;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Test the "mandatoryClean" parameter : saving in cache should be done only if a clean phase has been executed.
+ */
+@IntegrationTest("src/test/projects/mandatory-clean")
+public class MandatoryCleanTest {
+
+ private static final String MODULE_NAME_1 = "org.apache.maven.caching.test.simple:non-forked-module";
+ private static final String MODULE_NAME_2 = "org.apache.maven.caching.test.simple:forked-module";
+ private static final String CACHE_BUILD_LOG = "Found cached build, restoring %s from cache";
+
+ @Test
+ void simple(Verifier verifier) throws VerificationException, IOException {
+
+ verifier.setAutoclean(false);
+ Path tempDirectory = Files.createTempDirectory("simple-mandatory-clean");
+ verifier.getCliOptions().clear();
+ verifier.addCliOption("-D" + CACHE_LOCATION_PROPERTY_NAME + "=" + tempDirectory.toAbsolutePath());
+
+ verifier.setLogFileName("../log-1.txt");
+ verifier.executeGoal("verify");
+ verifier.verifyErrorFreeLog();
+ List<String> cacheSkippedBuild1 = LogFileUtils.findLinesContainingTextsInLogs(
+ verifier, "Cache storing is skipped since there was no \"clean\" phase.");
+ Assertions.assertEquals(2, cacheSkippedBuild1.size(), "Expected 2 skipped module caching");
+
+ assertThrows(
+ VerificationException.class,
+ () -> verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_1)),
+ "not expected to be loaded from the cache");
+ assertThrows(
+ VerificationException.class,
+ () -> verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_2)),
+ "not expected to be loaded from the cache");
+
+ verifier.setLogFileName("../log-2.txt");
+ verifier.executeGoals(Arrays.asList("clean", "verify"));
+ verifier.verifyErrorFreeLog();
+ List<String> cacheSkippedBuild2 = LogFileUtils.findLinesContainingTextsInLogs(
+ verifier, "Cache storing is skipped since there was no \"clean\" phase.");
+ Assertions.assertEquals(0, cacheSkippedBuild2.size(), "Expected 2 skipped module caching");
+ assertThrows(
+ VerificationException.class,
+ () -> verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_1)),
+ "not expected to be loaded from the cache");
+ assertThrows(
+ VerificationException.class,
+ () -> verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_2)),
+ "not expected to be loaded from the cache");
+
+ verifier.setLogFileName("../log-3.txt");
+ verifier.executeGoal("verify");
+ verifier.verifyErrorFreeLog();
+ List<String> cacheSkippedBuild3 = LogFileUtils.findLinesContainingTextsInLogs(
+ verifier, "Cache storing is skipped since there was no \"clean\" phase.");
+ Assertions.assertEquals(0, cacheSkippedBuild3.size(), "loading from cache, no more caching required");
+ // Expect to find and restore cached project
+ verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_1));
+ verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_2));
+ }
+
+ @Test
+ void disabledViaProperty(Verifier verifier) throws VerificationException {
+
+ verifier.setAutoclean(false);
+
+ verifier.setLogFileName("../log-1.txt");
+ verifier.executeGoal("verify");
+ verifier.verifyErrorFreeLog();
+ List<String> cacheSkippedBuild1 = LogFileUtils.findLinesContainingTextsInLogs(
+ verifier, "Cache storing is skipped since there was no \"clean\" phase.");
+ Assertions.assertEquals(2, cacheSkippedBuild1.size(), "Expected 2 skipped module caching");
+
+ assertThrows(
+ VerificationException.class,
+ () -> verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_1)),
+ "not expected to be loaded from the cache");
+ assertThrows(
+ VerificationException.class,
+ () -> verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_2)),
+ "not expected to be loaded from the cache");
+
+ verifier.setLogFileName("../log-2.txt");
+ verifier.getCliOptions().clear();
+ // With "true", we do not change the initially expected behaviour
+ verifier.addCliOption("-D" + CacheConfigImpl.MANDATORY_CLEAN + "=true");
+ verifier.executeGoal("verify");
+ verifier.verifyErrorFreeLog();
+ List<String> cacheSkippedBuild2 = LogFileUtils.findLinesContainingTextsInLogs(
+ verifier, "Cache storing is skipped since there was no \"clean\" phase.");
+ Assertions.assertEquals(2, cacheSkippedBuild2.size(), "Expected 2 skipped module caching");
+
+ assertThrows(
+ VerificationException.class,
+ () -> verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_1)),
+ "not expected to be loaded from the cache");
+ assertThrows(
+ VerificationException.class,
+ () -> verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_2)),
+ "not expected to be loaded from the cache");
+
+ // With "false", we remove the need for the clean phase
+ verifier.getCliOptions().clear();
+ verifier.addCliOption("-D" + CacheConfigImpl.MANDATORY_CLEAN + "=false");
+ verifier.setLogFileName("../log-3.txt");
+ verifier.executeGoal("verify");
+ verifier.verifyErrorFreeLog();
+ List<String> cacheSkippedBuild3 = LogFileUtils.findLinesContainingTextsInLogs(
+ verifier, "Cache storing is skipped since there was no \"clean\" phase.");
+ Assertions.assertEquals(0, cacheSkippedBuild3.size(), "Expected 2 skipped module caching");
+ assertThrows(
+ VerificationException.class,
+ () -> verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_1)),
+ "not expected to be loaded from the cache");
+ assertThrows(
+ VerificationException.class,
+ () -> verifier.verifyTextInLog(String.format(CACHE_BUILD_LOG, MODULE_NAME_2)),
+ "not expected to be loaded from the cache");
+ }
+}
diff --git a/src/test/java/org/apache/maven/buildcache/util/LogFileUtils.java b/src/test/java/org/apache/maven/buildcache/util/LogFileUtils.java
index 58f65c9..c7ac884 100644
--- a/src/test/java/org/apache/maven/buildcache/util/LogFileUtils.java
+++ b/src/test/java/org/apache/maven/buildcache/util/LogFileUtils.java
@@ -21,6 +21,7 @@ package org.apache.maven.buildcache.util;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
+import java.util.stream.Collectors;
import org.apache.maven.it.VerificationException;
import org.apache.maven.it.Verifier;
@@ -60,4 +61,20 @@ public final class LogFileUtils {
return null;
}
+
+ /**
+ * Find lines matching all the strings given as parameter in the log file attached to a verifier
+ * @param verifier the maven verifier instance
+ * @param texts all the matching strings to find
+ * @return a list of matching strings
+ * @throws VerificationException
+ */
+ public static List<String> findLinesContainingTextsInLogs(final Verifier verifier, final String... texts)
+ throws VerificationException {
+ List<String> lines = verifier.loadFile(verifier.getBasedir(), verifier.getLogFileName(), false);
+ return lines.stream()
+ .map(s -> verifier.stripAnsi(s))
+ .filter(s -> Arrays.stream(texts).allMatch(text -> s.contains(text)))
+ .collect(Collectors.toList());
+ }
}
diff --git a/src/test/projects/mandatory-clean/.mvn/extensions.xml b/src/test/projects/mandatory-clean/.mvn/extensions.xml
new file mode 100644
index 0000000..8df78f8
--- /dev/null
+++ b/src/test/projects/mandatory-clean/.mvn/extensions.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2021 the original author or authors.
+
+ Licensed 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.
+
+-->
+<extensions>
+ <extension>
+ <groupId>org.apache.maven.extensions</groupId>
+ <artifactId>maven-build-cache-extension</artifactId>
+ <version>${projectVersion}</version>
+ </extension>
+</extensions>
diff --git a/src/test/projects/mandatory-clean/.mvn/maven-build-cache-config.xml b/src/test/projects/mandatory-clean/.mvn/maven-build-cache-config.xml
new file mode 100644
index 0000000..9f7c588
--- /dev/null
+++ b/src/test/projects/mandatory-clean/.mvn/maven-build-cache-config.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+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.
+-->
+
+<cache xmlns="http://maven.apache.org/CACHE-CONFIG/1.0.0">
+
+ <configuration>
+ <mandatoryClean>true</mandatoryClean>
+ </configuration>
+
+</cache>
diff --git a/src/test/projects/mandatory-clean/forked-module/pom.xml b/src/test/projects/mandatory-clean/forked-module/pom.xml
new file mode 100644
index 0000000..a20c2ed
--- /dev/null
+++ b/src/test/projects/mandatory-clean/forked-module/pom.xml
@@ -0,0 +1,56 @@
+<!--
+
+ Copyright 2021 the original author or authors.
+
+ Licensed 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.
+
+-->
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <parent>
+ <groupId>org.apache.maven.caching.test.simple</groupId>
+ <artifactId>parent-multi-module</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.apache.maven.caching.test.simple</groupId>
+ <artifactId>forked-module</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <packaging>jar</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-pmd-plugin</artifactId>
+ <version>3.19.0</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>check</goal>
+ <goal>pmd</goal>
+ </goals>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.maven.wagon</groupId>
+ <artifactId>wagon-http</artifactId>
+ <version>3.5.2</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/src/test/projects/mandatory-clean/forked-module/src/main/java/org/apache/maven/buildcache/ForkedExecutionClass.java b/src/test/projects/mandatory-clean/forked-module/src/main/java/org/apache/maven/buildcache/ForkedExecutionClass.java
new file mode 100644
index 0000000..920b9f9
--- /dev/null
+++ b/src/test/projects/mandatory-clean/forked-module/src/main/java/org/apache/maven/buildcache/ForkedExecutionClass.java
@@ -0,0 +1,24 @@
+/*
+ * 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.maven.buildcache;
+
+class ForkedExecutionClass
+{
+
+}
\ No newline at end of file
diff --git a/src/test/projects/mandatory-clean/non-forked-module/pom.xml b/src/test/projects/mandatory-clean/non-forked-module/pom.xml
new file mode 100644
index 0000000..a9d9917
--- /dev/null
+++ b/src/test/projects/mandatory-clean/non-forked-module/pom.xml
@@ -0,0 +1,32 @@
+<!--
+
+ Copyright 2021 the original author or authors.
+
+ Licensed 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.
+
+-->
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <parent>
+ <groupId>org.apache.maven.caching.test.simple</groupId>
+ <artifactId>parent-multi-module</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.apache.maven.caching.test.simple</groupId>
+ <artifactId>non-forked-module</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <packaging>jar</packaging>
+
+</project>
\ No newline at end of file
diff --git a/src/test/projects/mandatory-clean/non-forked-module/src/main/java/org/apache/maven/buildcache/Test.java b/src/test/projects/mandatory-clean/non-forked-module/src/main/java/org/apache/maven/buildcache/Test.java
new file mode 100644
index 0000000..03f66a8
--- /dev/null
+++ b/src/test/projects/mandatory-clean/non-forked-module/src/main/java/org/apache/maven/buildcache/Test.java
@@ -0,0 +1,24 @@
+/*
+ * 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.maven.buildcache;
+
+class Test
+{
+
+}
\ No newline at end of file
diff --git a/src/test/projects/mandatory-clean/pom.xml b/src/test/projects/mandatory-clean/pom.xml
new file mode 100644
index 0000000..c2390cd
--- /dev/null
+++ b/src/test/projects/mandatory-clean/pom.xml
@@ -0,0 +1,37 @@
+<!--
+
+ Copyright 2021 the original author or authors.
+
+ Licensed 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.
+
+-->
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.apache.maven.caching.test.simple</groupId>
+ <artifactId>parent-multi-module</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+ <properties>
+ <maven.compiler.source>1.8</maven.compiler.source>
+ <maven.compiler.target>1.8</maven.compiler.target>
+ </properties>
+
+ <modules>
+ <module>non-forked-module</module>
+ <module>forked-module</module>
+ </modules>
+
+</project>
\ No newline at end of file