Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions archunit-junit/junit6/aggregator/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
plugins {
id 'archunit.java-release-conventions'
}

ext.moduleName = 'com.tngtech.archunit.junit6'

ext.minimumJavaVersion = JavaVersion.VERSION_17

dependencies {
api(project(":archunit-junit6-api"))
implementation(project(":archunit-junit6-engine"))
}

shadowJar {
exclude '**'
}
39 changes: 39 additions & 0 deletions archunit-junit/junit6/api/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
plugins {
id 'archunit.java-release-conventions'
}

ext.moduleName = 'com.tngtech.archunit.junit6.api'

ext.minimumJavaVersion = JavaVersion.VERSION_17

dependencies {
api project(path: ':archunit')
api project(path: ':archunit-junit', configuration: 'archJunitApi')
api libs.junit6PlatformCommons
}

javadoc {
source(project(':archunit-junit').sourceSets.archJunitApi.allJava)
}
sourcesJar {
from project(':archunit-junit').sourceSets.archJunitApi.allSource
}

shadowJar {
exclude 'META-INF/maven/**'

dependencies {
exclude(dependency { !it.name.contains('archunit-junit') })
}
}

def configureDependencies = { deps ->
deps.children().removeIf { dep ->
dep.scope.text() != 'compile' || !(dep.artifactId.text() in ['archunit'])
}
}
this.with project(':archunit-junit').configureJUnitArchive(configureDependencies)

singlePackageExport {
exportedPackage = 'com.tngtech.archunit.junit'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright 2014-2026 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.junit;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import com.tngtech.archunit.PublicAPI;
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.core.importer.ImportOption;
import com.tngtech.archunit.core.importer.ImportOption.DoNotIncludeJars;
import com.tngtech.archunit.core.importer.ImportOption.DoNotIncludeTests;
import org.junit.platform.commons.annotation.Testable;

import static com.tngtech.archunit.PublicAPI.Usage.ACCESS;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
* Specifies which packages/locations should be scanned and tested when running a JUnit 5 test.
* <br><br>
* To ignore certain classes (e.g. classes in test scope) see {@link #importOptions()}, in particular {@link DoNotIncludeTests} and
* {@link DoNotIncludeJars}.
* <br><br>
* When checking rules, it is important to remember that all relevant information/classes need to be imported for the rules
* to work. For example, if class A accesses class B and class B extends class C, but class B is not imported, then
* a rule checking for no accesses to classes assignable to C will not fail, since ArchUnit does not know about the details
* of class B, but only simple information like the fully qualified name. For information how to configure the import and
* resolution behavior of missing classes, compare {@link ClassFileImporter}.
*
* @see ClassFileImporter
*/
@Testable
@Target(TYPE)
@Retention(RUNTIME)
@PublicAPI(usage = ACCESS)
public @interface AnalyzeClasses {
/**
* @return Packages to look for within the classpath / modulepath
*/
String[] packages() default {};

/**
* @return Classes that specify packages to look for within the classpath / modulepath
*/
Class<?>[] packagesOf() default {};

/**
* @return Implementations of {@link LocationProvider}. Allows to completely customize the sources,
* where classes are imported from.
*/
Class<? extends LocationProvider>[] locations() default {};

/**
* @return Whether to look for classes on the whole classpath.
* Warning: Without any further {@link #importOptions() filtering} this can require a lot of resources.
*/
boolean wholeClasspath() default false;

/**
* Allows to filter the class import. The supplied types will be instantiated and used to create the
* {@link ImportOption ImportOptions} passed to the {@link ClassFileImporter}. Considering caching, compare the notes on
* {@link ImportOption}.
*
* @return The types of {@link ImportOption} to use for the import
*/
Class<? extends ImportOption>[] importOptions() default {};

/**
* Controls, if {@link JavaClasses} should be cached by location,
* to be reused between several test classes, or just within the same class.
*
* @return The {@link CacheMode} to use for this test class.
*/
CacheMode cacheMode() default CacheMode.FOREVER;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright 2014-2026 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.junit;

import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import com.tngtech.archunit.PublicAPI;

import static com.tngtech.archunit.PublicAPI.Usage.ACCESS;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
* {@link ArchTag @ArchTag} is a {@linkplain Repeatable repeatable} annotation that allows
* tagging any {@link ArchTest @ArchTest} field/method/class. Sets of rules can be classified to run together this way.
*
* Rules could be tagged like
* <br><br>
* <pre><code>
*{@literal @}ArchTag("dependencies")
* static ArchRule no_accesses_from_server_to_client = classes()...
* </code></pre>
*
* Users of {@link ArchTag} must follow the syntax conventions that the JUnit Platform Engine defines:
*
* <ul>
* <li>A tag must not be blank.</li>
* <li>A <em>trimmed</em> tag must not contain whitespace.</li>
* <li>A <em>trimmed</em> tag must not contain ISO control characters.</li>
* <li>A <em>trimmed</em> tag must not contain any of the following
* <em>reserved characters</em>.
* <ul>
* <li>{@code ,}: <em>comma</em></li>
* <li>{@code (}: <em>left parenthesis</em></li>
* <li>{@code )}: <em>right parenthesis</em></li>
* <li>{@code &}: <em>ampersand</em></li>
* <li>{@code |}: <em>vertical bar</em></li>
* <li>{@code !}: <em>exclamation point</em></li>
* </ul>
* </li>
* </ul>
*/
@Inherited
@Documented
@Retention(RUNTIME)
@PublicAPI(usage = ACCESS)
@Repeatable(ArchTags.class)
@Target({TYPE, METHOD, FIELD})
public @interface ArchTag {
/**
* The actual <em>tag</em>. It will first be {@linkplain String#trim() trimmed} and must then adhere to the
* {@linkplain ArchTag Syntax Rules for Tags}. Otherwise the tag will be ignored.
*/
String value();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2014-2026 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.junit;

import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import com.tngtech.archunit.Internal;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
* Simply a container for {@link ArchTag}. Should never be used directly, but instead
* {@link ArchTag} should be used in a {@linkplain Repeatable repeatable} manner, e.g.
* <br><br>
* <pre><code>
*{@literal @}ArchTag("foo")
*{@literal @}ArchTag("bar")
* static ArchRule example = classes()...
* </code></pre>
*/
@Internal
@Inherited
@Documented
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD})
public @interface ArchTags {
ArchTag[] value();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2014-2026 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.junit;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.lang.ArchRule;
import org.junit.platform.commons.annotation.Testable;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
* Marks ArchUnit tests to be executed by the test infrastructure. These tests can have the following form:
* <ul>
* <li>
* A static field of type {@link ArchRule} -&gt; this rule will automatically be checked against the imported classes
* </li>
* <li>
* A static method with one parameter {@link JavaClasses} -&gt; this method will be called with the imported classes
* </li>
* </ul>
* <br>Example:
* <pre><code>
*{@literal @}ArchTest
* public static final ArchRule someRule = classes()... ;
*
*{@literal @}ArchTest
* public static void someMethod(JavaClasses classes) {
* // do something with classes
* }
* </code></pre>
*/
@Testable
@Target({FIELD, METHOD})
@Retention(RUNTIME)
public @interface ArchTest {
}
43 changes: 43 additions & 0 deletions archunit-junit/junit6/engine-api/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
plugins {
id 'archunit.java-release-conventions'
}

ext.moduleName = 'com.tngtech.archunit.junit6.engineapi'

ext.minimumJavaVersion = JavaVersion.VERSION_17

dependencies {
api libs.junit6PlatformEngine
implementation project(path: ':archunit')

testImplementation project(path: ':archunit-junit6-api')
testImplementation libs.assertj
}

compileJava.dependsOn project(':archunit-junit6-api').finishArchive

test {
useJUnitPlatform() {
excludeEngines 'archunit'
}
}

shadowJar {
exclude 'META-INF/maven/**'

dependencies {
exclude(dependency { true })
}
}

// dependencies to archunit only cover annotations; we can skip those without breaking consumers to keep the dependency slim
def configureDependencies = { deps ->
deps.children().removeIf{ dep ->
dep.artifactId.text() != 'junit-platform-engine'
}
}
this.with project(':archunit-junit').configureJUnitArchive(configureDependencies)

singlePackageExport {
exportedPackage = 'com.tngtech.archunit.junit.engine_api'
}
Loading