diff --git a/archunit/src/main/java/com/tngtech/archunit/library/freeze/StoreEmptyException.java b/archunit/src/main/java/com/tngtech/archunit/library/freeze/StoreEmptyException.java new file mode 100644 index 000000000..bdb4af70a --- /dev/null +++ b/archunit/src/main/java/com/tngtech/archunit/library/freeze/StoreEmptyException.java @@ -0,0 +1,23 @@ +/* + * Copyright 2014-2025 TNG Technology Consulting GmbH + * + * 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. + */ +package com.tngtech.archunit.library.freeze; + +class StoreEmptyException extends RuntimeException { + StoreEmptyException(String message) { + super(message); + } + +} diff --git a/archunit/src/main/java/com/tngtech/archunit/library/freeze/TextFileBasedViolationStore.java b/archunit/src/main/java/com/tngtech/archunit/library/freeze/TextFileBasedViolationStore.java index 05eba8224..54f9084bb 100644 --- a/archunit/src/main/java/com/tngtech/archunit/library/freeze/TextFileBasedViolationStore.java +++ b/archunit/src/main/java/com/tngtech/archunit/library/freeze/TextFileBasedViolationStore.java @@ -73,11 +73,14 @@ public final class TextFileBasedViolationStore implements ViolationStore { private static final String ALLOW_STORE_CREATION_DEFAULT = "false"; private static final String ALLOW_STORE_UPDATE_PROPERTY_NAME = "default.allowStoreUpdate"; private static final String ALLOW_STORE_UPDATE_DEFAULT = "true"; + private static final String ALLOW_STORE_EMPTY_RULE_VIOLATION_PROPERTY_NAME = "default.allowStoreEmptyRuleViolation"; + private static final String ALLOW_STORE_EMPTY_RULE_VIOLATION_DEFAULT = "true"; private final RuleViolationFileNameStrategy ruleViolationFileNameStrategy; private boolean storeCreationAllowed; private boolean storeUpdateAllowed; + private boolean storeEmptyRuleViolationAllowed; private File storeFolder; private FileSyncedProperties storedRules; @@ -103,6 +106,7 @@ public TextFileBasedViolationStore(RuleViolationFileNameStrategy ruleViolationFi public void initialize(Properties properties) { storeCreationAllowed = Boolean.parseBoolean(properties.getProperty(ALLOW_STORE_CREATION_PROPERTY_NAME, ALLOW_STORE_CREATION_DEFAULT)); storeUpdateAllowed = Boolean.parseBoolean(properties.getProperty(ALLOW_STORE_UPDATE_PROPERTY_NAME, ALLOW_STORE_UPDATE_DEFAULT)); + storeEmptyRuleViolationAllowed = Boolean.parseBoolean(properties.getProperty(ALLOW_STORE_EMPTY_RULE_VIOLATION_PROPERTY_NAME, ALLOW_STORE_EMPTY_RULE_VIOLATION_DEFAULT)); String path = properties.getProperty(STORE_PATH_PROPERTY_NAME, STORE_PATH_DEFAULT); storeFolder = new File(path); ensureExistence(storeFolder); @@ -140,6 +144,10 @@ public boolean contains(ArchRule rule) { @Override public void save(ArchRule rule, List violations) { log.trace("Storing evaluated rule '{}' with {} violations: {}", rule.getDescription(), violations.size(), violations); + if (!storeEmptyRuleViolationAllowed) { + throw new StoreEmptyException(String.format("Saving empty violations for freezing rule is disabled (enable by configuration %s.%s=true)", + ViolationStoreFactory.FREEZE_STORE_PROPERTY_NAME, ALLOW_STORE_EMPTY_RULE_VIOLATION_PROPERTY_NAME)); + } if (!storeUpdateAllowed) { throw new StoreUpdateFailedException(String.format( "Updating frozen violations is disabled (enable by configuration %s.%s=true)", diff --git a/archunit/src/test/java/com/tngtech/archunit/library/freeze/FreezingArchRuleTest.java b/archunit/src/test/java/com/tngtech/archunit/library/freeze/FreezingArchRuleTest.java index 8c5e4f87d..0003a4dec 100644 --- a/archunit/src/test/java/com/tngtech/archunit/library/freeze/FreezingArchRuleTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/library/freeze/FreezingArchRuleTest.java @@ -3,14 +3,7 @@ import java.io.File; import java.io.IOException; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; +import java.util.*; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; @@ -55,6 +48,7 @@ public class FreezingArchRuleTest { private static final String STORE_DEFAULT_PATH_PROPERTY_NAME = "freeze.store.default.path"; private static final String ALLOW_STORE_CREATION_PROPERTY_NAME = "freeze.store.default.allowStoreCreation"; private static final String ALLOW_STORE_UPDATE_PROPERTY_NAME = "freeze.store.default.allowStoreUpdate"; + private static final String ALLOW_STORE_EMPTY_RULE_VIOLATION_PROPERTY_NAME = "freeze.store.default.allowStoreEmptyRuleViolation"; private static final String LINE_MATCHER_PROPERTY_NAME = "freeze.lineMatcher"; @Rule @@ -482,6 +476,21 @@ public void can_prevent_default_ViolationStore_from_updating_existing_rules() th expectStoreUpdateDisabledException(() -> frozenRule.check(importClasses(getClass()))); } + @Test + public void warns_when_default_ViolationStore_gets_empty() throws IOException { + useTemporaryDefaultStorePath(); + ArchConfiguration.get().setProperty(ALLOW_STORE_CREATION_PROPERTY_NAME, "true"); + + RuleCreator someRule = rule("some description"); + freeze(someRule.withViolations("remaining", "will be solved").create()).check(importClasses(getClass())); + + ArchConfiguration.get().setProperty(ALLOW_STORE_EMPTY_RULE_VIOLATION_PROPERTY_NAME, "false"); + FreezingArchRule frozenRule = freeze(someRule.withoutViolations().create()); + assertThatThrownBy(() -> frozenRule.check(importClasses(getClass()))) + .isInstanceOf(StoreEmptyException.class) + .hasMessageContaining("Saving empty violations for freezing rule is disabled (enable by configuration " + ALLOW_STORE_EMPTY_RULE_VIOLATION_PROPERTY_NAME + "=true)"); + } + @Test public void allows_to_adjust_default_store_file_names_via_delegation() throws IOException { // GIVEN