Skip to content

Commit

Permalink
introduce ConditionEvents.addEventWithFormattedMessage
Browse files Browse the repository at this point in the history
The private helper method `ArchConditions#createMessage(T, String)`
(which prepends the corresponding object's description, and appends it source code location)
is actually useful public API.

Exposing it on `ConditionEvents` allows `ArchCondition` implementations
to get rid of even more boilerplate code repeating the corresponding object.

Signed-off-by: Manfred Hanke <Manfred.Hanke@tngtech.com>
  • Loading branch information
hankem committed Mar 4, 2022
1 parent f83822c commit 3ae3f61
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
import com.google.common.collect.Ordering;
import com.google.common.reflect.TypeToken;
import com.tngtech.archunit.PublicAPI;
import com.tngtech.archunit.base.HasDescription;
import com.tngtech.archunit.base.Optional;
import com.tngtech.archunit.core.domain.properties.HasSourceCodeLocation;

import static com.tngtech.archunit.PublicAPI.State.EXPERIMENTAL;
import static com.tngtech.archunit.PublicAPI.Usage.ACCESS;
Expand All @@ -46,6 +48,27 @@ public void add(ConditionEvent event) {
eventsByViolation.get(Type.from(event.isViolation())).add(event);
}

/**
* adds a {@link SimpleConditionEvent} with a message built from
* the {@link HasDescription#getDescription() corresponding object's description},
* the actual message content formatted with {@link String#format(String, Object...)}, and
* the {@link HasSourceCodeLocation#getSourceCodeLocation() corresponding object's source code location}.
*/
@PublicAPI(usage = ACCESS)
public <T extends HasDescription & HasSourceCodeLocation> void addEventWithFormattedMessage(
T correspondingObject,
boolean satisfied,
String messageContentFormat,
Object... messageContentArgs
) {
String message = String.format("%s %s in %s",
correspondingObject.getDescription(),
String.format(messageContentFormat, messageContentArgs),
correspondingObject.getSourceCodeLocation()
);
add(new SimpleConditionEvent(correspondingObject, satisfied, message));
}

/**
* Can be used to override the information about the number of violations. If absent the violated rule
* will simply report the number of violation lines as the number of violations (which is typically
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1205,10 +1205,6 @@ public static ArchCondition<JavaCodeUnit> declareThrowableOfType(DescribedPredic
return new DoesConditionByPredicate<>(declareThrowableOfType);
}

private static <T extends HasDescription & HasSourceCodeLocation> String createMessage(T object, String message) {
return object.getDescription() + " " + message + " in " + object.getSourceCodeLocation();
}

private static final IsConditionByPredicate<JavaClass> BE_TOP_LEVEL_CLASSES =
new IsConditionByPredicate<>("a top level class", JavaClass.Predicates.TOP_LEVEL_CLASSES);
private static final IsConditionByPredicate<JavaClass> BE_NESTED_CLASSES =
Expand Down Expand Up @@ -1249,8 +1245,8 @@ private static class ModifierCondition<T extends HasModifiers & HasDescription &
@Override
public void check(T hasModifiers, ConditionEvents events) {
boolean satisfied = hasModifiers.getModifiers().contains(modifier);
String infix = (satisfied ? "is " : "is not ") + modifier.toString().toLowerCase();
events.add(new SimpleConditionEvent(hasModifiers, satisfied, createMessage(hasModifiers, infix)));
events.addEventWithFormattedMessage(hasModifiers, satisfied,
"is %s%s", satisfied ? "" : "not ", modifier.toString().toLowerCase());
}
}

Expand All @@ -1265,11 +1261,9 @@ private static class ImplementsCondition extends ArchCondition<JavaClass> {
@Override
public void check(JavaClass javaClass, ConditionEvents events) {
boolean satisfied = implement.apply(javaClass);
String description = satisfied
? implement.getDescription().replace("implement", "implements")
: implement.getDescription().replace("implement", "does not implement");
String message = createMessage(javaClass, description);
events.add(new SimpleConditionEvent(javaClass, satisfied, message));
String replacementForImplement = satisfied ? "implements" : "does not implement";
events.addEventWithFormattedMessage(javaClass, satisfied,
implement.getDescription().replace("implement", replacementForImplement));
}
}

Expand All @@ -1283,9 +1277,8 @@ private static class InterfacesCondition extends ArchCondition<JavaClass> {
@Override
public void check(JavaClass javaClass, ConditionEvents events) {
boolean isInterface = javaClass.isInterface();
String message = createMessage(javaClass,
(isInterface ? "is an" : "is not an") + " interface");
events.add(new SimpleConditionEvent(javaClass, isInterface, message));
events.addEventWithFormattedMessage(javaClass, isInterface,
"is %san interface", isInterface ? "" : "not ");
}
}

Expand All @@ -1299,9 +1292,8 @@ private static class EnumsCondition extends ArchCondition<JavaClass> {
@Override
public void check(JavaClass javaClass, ConditionEvents events) {
boolean isEnum = javaClass.isEnum();
String message = createMessage(javaClass,
(isEnum ? "is an" : "is not an") + " enum");
events.add(new SimpleConditionEvent(javaClass, isEnum, message));
events.addEventWithFormattedMessage(javaClass, isEnum,
"is %san enum", isEnum ? "" : "not ");
}
}

Expand All @@ -1315,9 +1307,8 @@ private static class RecordsCondition extends ArchCondition<JavaClass> {
@Override
public void check(JavaClass javaClass, ConditionEvents events) {
boolean isRecord = javaClass.isRecord();
String message = createMessage(javaClass,
(isRecord ? "is a" : "is not a") + " record");
events.add(new SimpleConditionEvent(javaClass, isRecord, message));
events.addEventWithFormattedMessage(javaClass, isRecord,
"is %sa record", isRecord ? "" : "not ");
}
}

Expand Down Expand Up @@ -1359,9 +1350,8 @@ private static class BeClassCondition extends ArchCondition<JavaClass> {
@Override
public void check(JavaClass javaClass, ConditionEvents events) {
boolean itemEquivalentToClazz = javaClass.getName().equals(className);
String message = createMessage(javaClass,
(itemEquivalentToClazz ? "is " : "is not ") + className);
events.add(new SimpleConditionEvent(javaClass, itemEquivalentToClazz, message));
events.addEventWithFormattedMessage(javaClass, itemEquivalentToClazz,
"is %s%s", itemEquivalentToClazz ? "" : "not ", className);
}
}

Expand All @@ -1378,9 +1368,8 @@ private static class SimpleNameCondition extends ArchCondition<JavaClass> {
@Override
public void check(JavaClass javaClass, ConditionEvents events) {
boolean satisfied = haveSimpleName.apply(javaClass);
String message = createMessage(javaClass,
String.format("%s simple name '%s'", satisfied ? "has" : "does not have", name));
events.add(new SimpleConditionEvent(javaClass, satisfied, message));
events.addEventWithFormattedMessage(javaClass, satisfied,
"%s simple name '%s'", satisfied ? "has" : "does not have", name);
}
}

Expand Down Expand Up @@ -1463,9 +1452,8 @@ private static class MatchingCondition<T extends HasDescription & HasSourceCodeL
@Override
public void check(T item, ConditionEvents events) {
boolean satisfied = matcher.apply(item);
String message = createMessage(item,
String.format("%s '%s'", satisfied ? "matches" : "does not match", regex));
events.add(new SimpleConditionEvent(item, satisfied, message));
events.addEventWithFormattedMessage(item, satisfied,
"%s '%s'", satisfied ? "matches" : "does not match", regex);
}
}

Expand All @@ -1482,9 +1470,8 @@ private static class StartingCondition<T extends HasDescription & HasSourceCodeL
@Override
public void check(T item, ConditionEvents events) {
boolean satisfied = startingWith.apply(item);
String message = createMessage(item,
String.format("name %s '%s'", satisfied ? "starts with" : "does not start with", prefix));
events.add(new SimpleConditionEvent(item, satisfied, message));
events.addEventWithFormattedMessage(item, satisfied,
"name %s '%s'", satisfied ? "starts with" : "does not start with", prefix);
}
}

Expand All @@ -1501,9 +1488,8 @@ private static class ContainingCondition<T extends HasDescription & HasSourceCod
@Override
public void check(T item, ConditionEvents events) {
boolean satisfied = containing.apply(item);
String message = createMessage(item,
String.format("name %s '%s'", satisfied ? "contains" : "does not contain", infix));
events.add(new SimpleConditionEvent(item, satisfied, message));
events.addEventWithFormattedMessage(item, satisfied,
"name %s '%s'", satisfied ? "contains" : "does not contain", infix);
}
}

Expand All @@ -1520,9 +1506,8 @@ private static class EndingCondition<T extends HasDescription & HasSourceCodeLoc
@Override
public void check(T item, ConditionEvents events) {
boolean satisfied = endingWith.apply(item);
String message = createMessage(item,
String.format("name %s '%s'", satisfied ? "ends with" : "does not end with", suffix));
events.add(new SimpleConditionEvent(item, satisfied, message));
events.addEventWithFormattedMessage(item, satisfied,
"name %s with '%s'", satisfied ? "ends" : "does not end", suffix);
}
}

Expand All @@ -1538,9 +1523,7 @@ private static class DoesConditionByPredicate<T extends HasDescription & HasSour
@Override
public void check(T item, ConditionEvents events) {
boolean satisfied = predicate.apply(item);
String message = createMessage(item,
(satisfied ? "does " : "does not ") + predicate.getDescription());
events.add(new SimpleConditionEvent(item, satisfied, message));
events.addEventWithFormattedMessage(item, satisfied, "does %s%s", satisfied ? "" : "not ", predicate.getDescription());
}
}

Expand All @@ -1561,9 +1544,8 @@ private static class IsConditionByPredicate<T extends HasDescription & HasSource
@Override
public void check(T member, ConditionEvents events) {
boolean satisfied = predicate.apply(member);
String message = createMessage(member,
(satisfied ? "is " : "is not ") + eventDescription);
events.add(new SimpleConditionEvent(member, satisfied, message));
events.addEventWithFormattedMessage(member, satisfied,
"is %s%s", satisfied ? "" : "not ", eventDescription);
}
}

Expand All @@ -1578,8 +1560,8 @@ private static class HaveConditionByPredicate<T extends HasDescription & HasSour
@Override
public void check(T object, ConditionEvents events) {
boolean satisfied = rawType.apply(object);
String message = createMessage(object, (satisfied ? "has " : "does not have ") + rawType.getDescription());
events.add(new SimpleConditionEvent(object, satisfied, message));
events.addEventWithFormattedMessage(object, satisfied,
"%s %s", satisfied ? "has" : "does not have", rawType.getDescription());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,34 @@
import java.util.Set;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
import org.junit.Test;
import org.junit.runner.RunWith;

import static com.google.common.collect.Iterables.getOnlyElement;
import static com.tngtech.archunit.core.domain.TestUtils.importClasses;
import static com.tngtech.archunit.testutil.Assertions.assertThat;
import static com.tngtech.java.junit.dataprovider.DataProviders.$;
import static com.tngtech.java.junit.dataprovider.DataProviders.$$;
import static org.assertj.core.api.Assertions.assertThat;

@RunWith(DataProviderRunner.class)
public class ConditionEventsTest {
@Test
public void addEventWithFormattedMessage_prepends_description_and_appends_sourceCodeLocation() {
JavaClass correspondingObject = getOnlyElement(importClasses(getClass()));
ConditionEvents events = new ConditionEvents();

events.addEventWithFormattedMessage(correspondingObject, true, "is satisfied");
events.addEventWithFormattedMessage(correspondingObject, false, "is violated");

assertThat(events)
.containAllowed(correspondingObject.getDescription() + " is satisfied in " + correspondingObject.getSourceCodeLocation())
.containViolations(correspondingObject.getDescription() + " is violated in " + correspondingObject.getSourceCodeLocation());
}

@DataProvider
public static Object[][] eventsWithEmpty() {
return $$(
Expand Down Expand Up @@ -125,7 +140,7 @@ private static class BaseHandler<T> implements ViolationHandler<T> {

@Override
public void handle(Collection<T> violatingObjects, String message) {
recorded.add(Iterables.getOnlyElement(violatingObjects));
recorded.add(getOnlyElement(violatingObjects));
}

List<T> getRecorded() {
Expand Down Expand Up @@ -168,5 +183,4 @@ private static class WrongType {

private static class WrongSupertype {
}

}

0 comments on commit 3ae3f61

Please sign in to comment.