Skip to content

Commit

Permalink
Add Support for Configuration of JavaClasses globally
Browse files Browse the repository at this point in the history
Closes gh-58
  • Loading branch information
mnhock committed Jun 23, 2024
1 parent 6ab9483 commit 0507507
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 45 deletions.
21 changes: 17 additions & 4 deletions docs/USERGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,21 @@ Taikai.builder()
.check();
```

### 3.2 Enforcing Rules on Empty Sets
### 3.2 Setting the JavaClasses

You can configure `classes` as well. This allows you to specify specific Java classes to analyze. Note that setting both `namespace` and `classes` simultaneously is not supported and will result in an `IllegalArgumentException`.

```java
JavaClasses classes = new ClassFileImporter()
.importClasses(ClassToCheck.class)

Taikai.builder()
.classes(classes)
.build()
.check();
```

### 3.3 Enforcing Rules on Empty Sets

The `failOnEmpty` setting determines whether the build should fail if no classes match a given rule. This is useful to ensure that your rules are applied consistently and to avoid false positives. The default is `false`.

Expand All @@ -44,7 +58,7 @@ Taikai.builder()
.check();
```

### 3.3 Excluding Classes Globally
### 3.4 Excluding Classes Globally

You can globally exclude specific classes from all rule checks by using the `excludeClass` or `excludeClasses` methods in the builder. This ensures that the specified classes are not checked by any rule.

Expand All @@ -57,7 +71,7 @@ Taikai.builder()
.check();
```

### 3.4 Modifying an Existing Configuration
### 3.5 Modifying an Existing Configuration
The `toBuilder` method allows you to create a new Builder instance from an existing Taikai configuration. This is useful if you need to modify an existing configuration.

```java
Expand All @@ -83,7 +97,6 @@ Taikai modifiedTaikai = taikai.toBuilder()
modifiedTaikai.check();
```


## 4. Rules Overview

Taikai's architecture rules cover a wide range of categories to enforce best practices and maintain consistency.
Expand Down
21 changes: 15 additions & 6 deletions src/main/java/com/enofex/taikai/Taikai.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,24 @@ public final class Taikai {

private final boolean failOnEmpty;
private final String namespace;
private final JavaClasses classes;
private final Set<String> excludedClasses;
private final Collection<TaikaiRule> rules;

private Taikai(Builder builder) {
this.failOnEmpty = builder.failOnEmpty;
this.namespace = builder.namespace;
this.classes = builder.classes;;
this.excludedClasses = requireNonNullElse(builder.excludedClasses, Collections.emptySet());
this.rules = Stream.concat(
builder.configurers.all().stream().flatMap(configurer -> configurer.rules().stream()),
builder.rules.stream())
.toList();

if (this.namespace != null && this.classes != null) {
throw new IllegalArgumentException("Setting namespace and classes are not supported");
}

ArchConfiguration.get()
.setProperty("archRule.failOnEmptyShould", Boolean.toString(this.failOnEmpty));
}
Expand All @@ -50,11 +56,7 @@ public String namespace() {
}

public JavaClasses classes() {
return Namespace.withoutTests(this.namespace);
}

public JavaClasses classesWithTests() {
return Namespace.withTests(this.namespace);
return this.classes;
}

public Set<String> excludedClasses() {
Expand All @@ -66,7 +68,7 @@ public Collection<TaikaiRule> rules() {
}

public void check() {
this.rules.forEach(rule -> rule.check(this.namespace, this.excludedClasses));
this.rules.forEach(rule -> rule.check(this.namespace, this.classes, this.excludedClasses));
}

public static Builder builder() {
Expand All @@ -84,6 +86,7 @@ public static final class Builder {
private final Set<String> excludedClasses;
private boolean failOnEmpty;
private String namespace;
private JavaClasses classes;

public Builder() {
this.configurers = new Configurers();
Expand All @@ -97,6 +100,7 @@ public Builder(Taikai taikai) {
this.excludedClasses = taikai.excludedClasses();
this.failOnEmpty = taikai.failOnEmpty();
this.namespace = taikai.namespace();
this.classes = taikai.classes();
}

public Builder addRule(TaikaiRule rule) {
Expand All @@ -119,6 +123,11 @@ public Builder namespace(String namespace) {
return this;
}

public Builder classes(JavaClasses classes) {
this.classes = classes;
return this;
}

public Builder excludeClass(String className) {
this.excludedClasses.add(className);
return this;
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/com/enofex/taikai/TaikaiRule.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,14 @@ public static TaikaiRule of(ArchRule archRule, Configuration configuration) {
}

public void check(String globalNamespace) {
check(globalNamespace, Collections.emptySet());
check(globalNamespace, null, Collections.emptySet());
}

public void check(String globalNamespace, Set<String> excludedClasses) {
public void check(String globalNamespace, JavaClasses classes, Set<String> excludedClasses) {
if (this.configuration.javaClasses() != null) {
this.archRule.check(this.configuration.javaClasses());
} else if (classes != null) {
this.archRule.check(classes);
} else {
String namespace = this.configuration.namespace() != null
? this.configuration.namespace()
Expand Down
20 changes: 14 additions & 6 deletions src/test/java/com/enofex/taikai/TaikaiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.any;
Expand All @@ -16,6 +17,7 @@
import com.enofex.taikai.spring.SpringConfigurer;
import com.enofex.taikai.test.TestConfigurer;
import com.tngtech.archunit.ArchConfiguration;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
Expand All @@ -39,8 +41,7 @@ void shouldBuildTaikaiWithDefaultValues() {

assertFalse(taikai.failOnEmpty());
assertEquals(VALID_NAMESPACE, taikai.namespace());
assertNotNull(taikai.classes());
assertNotNull(taikai.classesWithTests());
assertNull(taikai.classes());
assertTrue(taikai.rules().isEmpty());
assertTrue(taikai.excludedClasses().isEmpty());
}
Expand All @@ -51,7 +52,7 @@ void shouldBuildTaikaiWithCustomValues() {
Collection<TaikaiRule> rules = Collections.singletonList(mockRule);

Taikai taikai = Taikai.builder()
.namespace(VALID_NAMESPACE)
.classes(new ClassFileImporter().importClasses(TaikaiTest.class))
.excludeClass("com.enofex.taikai.SomeClassToExclude")
.excludeClasses(
Set.of("com.enofex.taikai.foo.ClassToExclude", "com.enofex.taikai.bar.ClassToExclude"))
Expand All @@ -60,9 +61,8 @@ void shouldBuildTaikaiWithCustomValues() {
.build();

assertTrue(taikai.failOnEmpty());
assertEquals(VALID_NAMESPACE, taikai.namespace());
assertNull(taikai.namespace());
assertNotNull(taikai.classes());
assertNotNull(taikai.classesWithTests());
assertEquals(1, taikai.rules().size());
assertTrue(taikai.rules().contains(mockRule));
assertEquals(3, taikai.excludedClasses().size());
Expand Down Expand Up @@ -134,7 +134,7 @@ void shouldCheckRules() {
.build()
.check();

verify(mockRule, times(1)).check(VALID_NAMESPACE, Collections.emptySet());
verify(mockRule, times(1)).check(VALID_NAMESPACE, null, Collections.emptySet());
}

@Test
Expand Down Expand Up @@ -164,4 +164,12 @@ void shouldRebuildTaikaiWithNewValues() {
assertTrue(
modifiedTaikai.excludedClasses().contains("com.enofex.taikai.AnotherClassToExclude"));
}

@Test
void shouldThrowExceptionIfNamespaceAndClasses() {
assertThrows(IllegalArgumentException.class, () -> Taikai.builder()
.namespace(VALID_NAMESPACE)
.classes(new ClassFileImporter().importClasses(TaikaiTest.class))
.build());
}
}
38 changes: 11 additions & 27 deletions src/test/java/com/enofex/taikai/logging/LoggingConfigurerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import static org.junit.jupiter.api.Assertions.assertThrows;

import com.enofex.taikai.Taikai;
import com.enofex.taikai.TaikaiRule;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import java.util.EnumSet;
import java.util.logging.Logger;
Expand All @@ -16,59 +15,44 @@ class LoggingConfigurerTest {

@Test
void shouldApplyLoggerConventionsWithClass() {
TaikaiRule.Configuration configuration = TaikaiRule.Configuration.of(
new ClassFileImporter().importClasses(LoggerConventionsFollowed.class));

Taikai taikai = Taikai.builder()
.namespace("com.enofex.taikai")
.logging(logging -> logging
.loggersShouldFollowConventions(Logger.class, "logger", EnumSet.of(PRIVATE, FINAL),
configuration))
.classes(new ClassFileImporter().importClasses(LoggerConventionsFollowed.class))
.logging(logging -> logging.loggersShouldFollowConventions(Logger.class, "logger",
EnumSet.of(PRIVATE, FINAL)))
.build();

assertDoesNotThrow(taikai::check);
}

@Test
void shouldApplyLoggerConventionsWithTypeName() {
TaikaiRule.Configuration configuration = TaikaiRule.Configuration.of(
new ClassFileImporter().importClasses(LoggerConventionsFollowed.class));

Taikai taikai = Taikai.builder()
.namespace("com.enofex.taikai")
.classes(new ClassFileImporter().importClasses(LoggerConventionsFollowed.class))
.logging(logging -> logging
.loggersShouldFollowConventions("java.util.logging.Logger", "logger",
EnumSet.of(PRIVATE, FINAL), configuration))
EnumSet.of(PRIVATE, FINAL)))
.build();

assertDoesNotThrow(taikai::check);
}

@Test
void shouldThrowLoggerConventionsWithClassNaming() {
TaikaiRule.Configuration configuration = TaikaiRule.Configuration.of(
new ClassFileImporter().importClasses(LoggerConventionsNotFollowedNaming.class));

Taikai taikai = Taikai.builder()
.namespace("com.enofex.taikai")
.logging(logging -> logging
.loggersShouldFollowConventions(Logger.class, "logger", EnumSet.of(PRIVATE, FINAL),
configuration))
.classes(new ClassFileImporter().importClasses(LoggerConventionsNotFollowedNaming.class))
.logging(logging -> logging.loggersShouldFollowConventions(Logger.class, "logger",
EnumSet.of(PRIVATE, FINAL)))
.build();

assertThrows(AssertionError.class, () -> taikai.check());
}

@Test
void shouldThrowLoggerConventionsWithClassModifier() {
TaikaiRule.Configuration configuration = TaikaiRule.Configuration.of(
new ClassFileImporter().importClasses(LoggerConventionsPartiallyModifier.class));

Taikai taikai = Taikai.builder()
.namespace("com.enofex.taikai")
.logging(logging -> logging
.loggersShouldFollowConventions(Logger.class, "logger", EnumSet.of(PRIVATE, FINAL),
configuration))
.classes(new ClassFileImporter().importClasses(LoggerConventionsPartiallyModifier.class))
.logging(logging -> logging.loggersShouldFollowConventions(Logger.class, "logger",
EnumSet.of(PRIVATE, FINAL)))
.build();

assertThrows(AssertionError.class, () -> taikai.check());
Expand Down

0 comments on commit 0507507

Please sign in to comment.