Skip to content

Commit 1dcc052

Browse files
committed
Add a runner function to make invoking rules easier and handle the allowEmptyShould=true case
1 parent f746151 commit 1dcc052

File tree

5 files changed

+141
-2
lines changed

5 files changed

+141
-2
lines changed

nebula-archrules-core/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ repositories {
77
}
88
dependencies {
99
api("com.tngtech.archunit:archunit:1.4.1")
10+
testImplementation("org.assertj:assertj-core:3.27.6")
1011
}
1112
testing {
1213
suites {
@@ -20,3 +21,6 @@ java {
2021
languageVersion = JavaLanguageVersion.of(11)
2122
}
2223
}
24+
dependencyLocking {
25+
lockAllConfigurations()
26+
}
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# This is a Gradle generated file for dependency locking.
22
# Manual edits can break the build and are not advised.
33
# This file is expected to be part of source control.
4-
com.tngtech.archunit:archunit:1.4.1=compileClasspath,testCompileClasspath,testRuntimeClasspath
4+
com.tngtech.archunit:archunit:1.4.1=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
5+
net.bytebuddy:byte-buddy:1.17.7=testCompileClasspath,testRuntimeClasspath
56
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
7+
org.assertj:assertj-core:3.27.6=testCompileClasspath,testRuntimeClasspath
68
org.junit.jupiter:junit-jupiter-api:5.12.2=testCompileClasspath,testRuntimeClasspath
79
org.junit.jupiter:junit-jupiter-engine:5.12.2=testRuntimeClasspath
810
org.junit.jupiter:junit-jupiter-params:5.12.2=testCompileClasspath,testRuntimeClasspath
@@ -12,5 +14,5 @@ org.junit.platform:junit-platform-engine:1.12.2=testRuntimeClasspath
1214
org.junit.platform:junit-platform-launcher:1.12.2=testRuntimeClasspath
1315
org.junit:junit-bom:5.12.2=testCompileClasspath,testRuntimeClasspath
1416
org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
15-
org.slf4j:slf4j-api:2.0.17=compileClasspath,testCompileClasspath,testRuntimeClasspath
17+
org.slf4j:slf4j-api:2.0.17=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
1618
empty=annotationProcessor,testAnnotationProcessor
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.netflix.nebula.archrules.core;
2+
3+
import com.tngtech.archunit.lang.ConditionEvent;
4+
5+
import java.util.Collections;
6+
import java.util.List;
7+
8+
/**
9+
* Custom event for representing a failure of allowEmptyShould in a non-exceptional way
10+
*/
11+
public class NoClassesMatchedEvent implements ConditionEvent {
12+
public static final String NO_MATCH_MESSAGE = "no classes matched the required condition";
13+
14+
@Override
15+
public boolean isViolation() {
16+
return true;
17+
}
18+
19+
@Override
20+
public ConditionEvent invert() {
21+
return this;
22+
}
23+
24+
@Override
25+
public List<String> getDescriptionLines() {
26+
return List.of(NO_MATCH_MESSAGE);
27+
}
28+
29+
@Override
30+
public void handleWith(Handler handler) {
31+
handler.handle(Collections.emptyList(), NO_MATCH_MESSAGE);
32+
}
33+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.netflix.nebula.archrules.core;
2+
3+
import com.tngtech.archunit.core.domain.JavaClasses;
4+
import com.tngtech.archunit.core.importer.ClassFileImporter;
5+
import com.tngtech.archunit.lang.ArchRule;
6+
import com.tngtech.archunit.lang.ConditionEvents;
7+
import com.tngtech.archunit.lang.EvaluationResult;
8+
9+
public class Runner {
10+
public static EvaluationResult check(ArchRule rule, JavaClasses classesToCheck) {
11+
try {
12+
return rule.evaluate(classesToCheck);
13+
} catch (AssertionError e) {
14+
// evaluate the rule again with more leniency so we can get the priority
15+
final var result2 = rule.allowEmptyShould(true).evaluate(classesToCheck);
16+
if (result2.hasViolation()) {
17+
return result2;
18+
} else {
19+
ConditionEvents events = ConditionEvents.Factory.create();
20+
events.add(new NoClassesMatchedEvent());
21+
return new EvaluationResult(rule, events, result2.getPriority());
22+
}
23+
}
24+
}
25+
26+
/**
27+
* Check a rule against some classes.
28+
* This can be invoked from the real Gradle plugin or unit tests for rules to ensure the same logic is observed there.
29+
*/
30+
public static EvaluationResult check(ArchRule rule, Class<?>... classesToCheck) {
31+
final var classes = new ClassFileImporter()
32+
.importClasses(classesToCheck);
33+
return check(rule, classes);
34+
}
35+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.netflix.nebula.archrules.core;
2+
3+
import com.tngtech.archunit.lang.ArchRule;
4+
import com.tngtech.archunit.lang.Priority;
5+
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition;
6+
import org.junit.jupiter.api.Test;
7+
8+
import static com.tngtech.archunit.core.domain.JavaClass.Predicates.simpleName;
9+
import static org.assertj.core.api.Assertions.assertThat;
10+
11+
public class RunnerTest {
12+
@Test
13+
public void test_pass() {
14+
final var result = Runner.check(noDeprecatedRule, PassingClass.class);
15+
assertThat(result.hasViolation()).isFalse();
16+
}
17+
18+
@Test
19+
public void test_fail() {
20+
final var result = Runner.check(noDeprecatedRule, FailingClass.class);
21+
assertThat(result.hasViolation()).isTrue();
22+
}
23+
24+
@Test
25+
public void test_fail_and_pass() {
26+
final var result = Runner.check(noDeprecatedRule, PassingClass.class, FailingClass.class);
27+
assertThat(result.hasViolation()).isTrue();
28+
assertThat(result.getFailureReport().getDetails()).hasSize(1);
29+
}
30+
31+
static class PassingClass {
32+
}
33+
34+
@Deprecated
35+
static class FailingClass {
36+
}
37+
38+
private final ArchRule noDeprecatedRule = ArchRuleDefinition.classes().should()
39+
.notBeAnnotatedWith(Deprecated.class);
40+
private final ArchRule smokeTestRule = ArchRuleDefinition.priority(Priority.MEDIUM)
41+
.classes().that(simpleName("SmokeTest"))
42+
.should().beAnnotatedWith(Deprecated.class)
43+
.allowEmptyShould(false);
44+
45+
static class SmokeTestFail {
46+
}
47+
48+
@Deprecated
49+
static class SmokeTest {
50+
}
51+
52+
@Test
53+
public void test_smoke_pass() {
54+
final var result = Runner.check(smokeTestRule, SmokeTest.class);
55+
assertThat(result.hasViolation()).isFalse();
56+
}
57+
58+
@Test
59+
public void test_smoke_fail() {
60+
final var result = Runner.check(smokeTestRule, SmokeTestFail.class);
61+
assertThat(result.hasViolation()).isTrue();
62+
assertThat(result.getFailureReport().getDetails())
63+
.contains(NoClassesMatchedEvent.NO_MATCH_MESSAGE);
64+
}
65+
}

0 commit comments

Comments
 (0)