mirror of
https://github.com/jlengrand/github-api.git
synced 2026-04-04 08:21:23 +00:00
241 lines
11 KiB
Java
241 lines
11 KiB
Java
package org.kohsuke.github;
|
|
|
|
import com.tngtech.archunit.base.DescribedPredicate;
|
|
import com.tngtech.archunit.core.domain.*;
|
|
import com.tngtech.archunit.core.domain.properties.HasName;
|
|
import com.tngtech.archunit.core.domain.properties.HasOwner;
|
|
import com.tngtech.archunit.core.importer.ClassFileImporter;
|
|
import com.tngtech.archunit.core.importer.ImportOption;
|
|
import com.tngtech.archunit.lang.ArchCondition;
|
|
import com.tngtech.archunit.lang.ArchRule;
|
|
import org.apache.commons.io.IOUtils;
|
|
import org.apache.commons.lang3.StringUtils;
|
|
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
|
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
|
import org.apache.commons.lang3.builder.ToStringStyle;
|
|
import org.junit.BeforeClass;
|
|
import org.junit.Test;
|
|
|
|
import java.io.Closeable;
|
|
import java.io.InputStream;
|
|
import java.io.Reader;
|
|
import java.lang.reflect.Field;
|
|
import java.nio.charset.Charset;
|
|
|
|
import static com.google.common.base.Preconditions.checkNotNull;
|
|
import static com.tngtech.archunit.core.domain.JavaCall.Predicates.target;
|
|
import static com.tngtech.archunit.core.domain.JavaClass.Predicates.resideInAPackage;
|
|
import static com.tngtech.archunit.core.domain.JavaClass.Predicates.type;
|
|
import static com.tngtech.archunit.core.domain.JavaClass.namesOf;
|
|
import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.name;
|
|
import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.nameContaining;
|
|
import static com.tngtech.archunit.core.domain.properties.HasOwner.Predicates.With.owner;
|
|
import static com.tngtech.archunit.core.domain.properties.HasParameterTypes.Predicates.rawParameterTypes;
|
|
import static com.tngtech.archunit.lang.conditions.ArchConditions.*;
|
|
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
|
|
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.fields;
|
|
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.methods;
|
|
import static org.hamcrest.MatcherAssert.assertThat;
|
|
import static org.hamcrest.Matchers.greaterThan;
|
|
|
|
public class ArchTests {
|
|
|
|
private static final JavaClasses classFiles = new ClassFileImporter()
|
|
.withImportOption(new ImportOption.DoNotIncludeTests())
|
|
.withImportOption(new ImportOption.DoNotIncludeJars())
|
|
.importPackages("org.kohsuke.github");
|
|
|
|
private static final JavaClasses apacheCommons = new ClassFileImporter().importPackages("org.apache.commons.lang3");
|
|
|
|
private static final JavaClasses testClassFiles = new ClassFileImporter()
|
|
.withImportOption(new ImportOption.OnlyIncludeTests())
|
|
.withImportOption(new ImportOption.DoNotIncludeJars())
|
|
.importPackages("org.kohsuke.github");
|
|
|
|
private static final DescribedPredicate<JavaAnnotation<?>> previewAnnotationWithNoMediaType = new DescribedPredicate<JavaAnnotation<?>>(
|
|
"preview has no required media types defined") {
|
|
|
|
@Override
|
|
public boolean apply(JavaAnnotation<?> javaAnnotation) {
|
|
boolean isPreview = javaAnnotation.getRawType().isEquivalentTo(Preview.class);
|
|
Object[] values = (Object[]) javaAnnotation.getProperties().get("value");
|
|
return isPreview && values != null && values.length < 1;
|
|
}
|
|
};
|
|
|
|
@BeforeClass
|
|
public static void beforeClass() {
|
|
assertThat(classFiles.size(), greaterThan(0));
|
|
}
|
|
|
|
@Test
|
|
public void testPreviewsAreFlaggedAsDeprecated() {
|
|
|
|
String reason = "all preview APIs must be annotated as @Deprecated until they are promoted to stable";
|
|
|
|
ArchRule classRule = classes().that()
|
|
.areAnnotatedWith(Preview.class)
|
|
.should()
|
|
.beAnnotatedWith(Deprecated.class)
|
|
.andShould(not(beAnnotatedWith(previewAnnotationWithNoMediaType)))
|
|
.because(reason);
|
|
|
|
ArchRule methodRule = methods().that()
|
|
.areAnnotatedWith(Preview.class)
|
|
.should()
|
|
.beAnnotatedWith(Deprecated.class)
|
|
.andShould(not(beAnnotatedWith(previewAnnotationWithNoMediaType)))
|
|
.because(reason);
|
|
|
|
ArchRule enumFieldsRule = fields().that()
|
|
.areDeclaredInClassesThat()
|
|
.areEnums()
|
|
.and()
|
|
.areAnnotatedWith(Preview.class)
|
|
.should()
|
|
.beAnnotatedWith(Deprecated.class)
|
|
.andShould(not(beAnnotatedWith(previewAnnotationWithNoMediaType)))
|
|
.because(reason);
|
|
|
|
classRule.check(classFiles);
|
|
enumFieldsRule.check(classFiles);
|
|
methodRule.check(classFiles);
|
|
|
|
}
|
|
|
|
@Test
|
|
public void testBetaApisAreFlaggedAsDeprecated() {
|
|
|
|
String reason = "all beta APIs must be annotated as @Deprecated until they are promoted to stable";
|
|
|
|
ArchRule classRule = classes().that()
|
|
.areAnnotatedWith(BetaApi.class)
|
|
.should()
|
|
.beAnnotatedWith(Deprecated.class)
|
|
.because(reason);
|
|
|
|
ArchRule methodRule = methods().that()
|
|
.areAnnotatedWith(BetaApi.class)
|
|
.should()
|
|
.beAnnotatedWith(Deprecated.class)
|
|
.because(reason);
|
|
|
|
ArchRule enumFieldsRule = fields().that()
|
|
.areDeclaredInClassesThat()
|
|
.areEnums()
|
|
.and()
|
|
.areAnnotatedWith(BetaApi.class)
|
|
.should()
|
|
.beAnnotatedWith(Deprecated.class)
|
|
.because(reason);
|
|
|
|
classRule.check(classFiles);
|
|
enumFieldsRule.check(classFiles);
|
|
methodRule.check(classFiles);
|
|
|
|
}
|
|
|
|
@Test
|
|
public void testRequireUseOfAssertThat() {
|
|
|
|
final String reason = "This project uses `assertThat(...)` instead of other `assert*()` methods.";
|
|
|
|
final DescribedPredicate<HasName> assertMethodOtherThanAssertThat = nameContaining("assert")
|
|
.and(DescribedPredicate.not(name("assertThat")));
|
|
|
|
final ArchRule onlyAssertThatRule = classes()
|
|
.should(not(callMethodWhere(target(assertMethodOtherThanAssertThat))))
|
|
.because(reason);
|
|
|
|
onlyAssertThatRule.check(testClassFiles);
|
|
}
|
|
|
|
@Test
|
|
public void testRequireUseOfOnlySpecificApacheCommons() {
|
|
|
|
final ArchRule onlyApprovedApacheCommonsLang3Methods = classes()
|
|
.should(notCallMethodsInPackageUnless("org.apache.commons..",
|
|
// unless it is one of these methods
|
|
targetMethodIs(StringUtils.class, "capitalize", String.class),
|
|
targetMethodIs(StringUtils.class, "defaultString", String.class, String.class),
|
|
targetMethodIs(StringUtils.class, "equals", CharSequence.class, CharSequence.class),
|
|
targetMethodIs(StringUtils.class, "isBlank", CharSequence.class),
|
|
targetMethodIs(StringUtils.class, "isEmpty", CharSequence.class),
|
|
targetMethodIs(StringUtils.class, "join", Iterable.class, String.class),
|
|
targetMethodIs(StringUtils.class,
|
|
"prependIfMissing",
|
|
String.class,
|
|
CharSequence.class,
|
|
CharSequence[].class),
|
|
targetMethodIs(ToStringBuilder.class, "toString"),
|
|
targetMethodIs(ToStringBuilder.class, "append", String.class, Object.class),
|
|
targetMethodIs(ToStringBuilder.class, "append", String.class, long.class),
|
|
targetMethodIs(ToStringBuilder.class, "append", String.class, int.class),
|
|
targetMethodIs(ToStringBuilder.class, "isEmpty"),
|
|
targetMethodIs(ToStringBuilder.class, "equals"),
|
|
targetMethodIs(ToStringBuilder.class, "capitalize"),
|
|
targetMethodIs(ToStringStyle.class,
|
|
"append",
|
|
StringBuffer.class,
|
|
String.class,
|
|
Object.class,
|
|
Boolean.class),
|
|
targetMethodIs(ReflectionToStringBuilder.class, "accept", Field.class),
|
|
targetMethodIs(IOUtils.class, "closeQuietly", InputStream.class),
|
|
targetMethodIs(IOUtils.class, "closeQuietly", Closeable.class),
|
|
targetMethodIs(IOUtils.class, "toString", InputStream.class, Charset.class),
|
|
targetMethodIs(IOUtils.class, "toString", Reader.class),
|
|
targetMethodIs(IOUtils.class, "toByteArray", InputStream.class)))
|
|
.because(
|
|
"Commons methods must be manually verified to be compatible with commons-io:2.4 or earlier and commons-lang3:3.9 or earlier should be used.");
|
|
|
|
onlyApprovedApacheCommonsLang3Methods.check(classFiles);
|
|
}
|
|
|
|
public static ArchCondition<JavaClass> notCallMethodsInPackageUnless(final String packageIdentifier,
|
|
final DescribedPredicate<JavaCall<?>>... unlessPredicates) {
|
|
DescribedPredicate<JavaCall<?>> restrictedPackageCalls = target(
|
|
HasOwner.Predicates.With.<JavaClass>owner(resideInAPackage(packageIdentifier)));
|
|
|
|
if (unlessPredicates.length > 0) {
|
|
DescribedPredicate<JavaCall<?>> allowed = unlessPredicates[0];
|
|
for (int x = 1; x < unlessPredicates.length; x++) {
|
|
allowed = allowed.or(unlessPredicates[x]);
|
|
}
|
|
restrictedPackageCalls = unless(restrictedPackageCalls, allowed);
|
|
}
|
|
return not(callMethodWhere(restrictedPackageCalls));
|
|
}
|
|
|
|
public static DescribedPredicate<JavaCall<?>> targetMethodIs(Class<?> owner,
|
|
String methodName,
|
|
Class<?>... parameterTypes) {
|
|
return JavaCall.Predicates.target(owner(type(owner)))
|
|
.and(JavaCall.Predicates.target(name(methodName)))
|
|
.and(JavaCall.Predicates.target(rawParameterTypes(parameterTypes)))
|
|
.as("method is %s",
|
|
Formatters.formatMethodSimple(owner.getSimpleName(), methodName, namesOf(parameterTypes)));
|
|
}
|
|
|
|
public static <T> DescribedPredicate<T> unless(DescribedPredicate<? super T> first,
|
|
DescribedPredicate<? super T> second) {
|
|
return new UnlessPredicate(first, second);
|
|
}
|
|
|
|
private static class UnlessPredicate<T> extends DescribedPredicate<T> {
|
|
private final DescribedPredicate<T> current;
|
|
private final DescribedPredicate<? super T> other;
|
|
|
|
UnlessPredicate(DescribedPredicate<T> current, DescribedPredicate<? super T> other) {
|
|
super(current.getDescription() + " unless " + other.getDescription());
|
|
this.current = checkNotNull(current);
|
|
this.other = checkNotNull(other);
|
|
}
|
|
|
|
@Override
|
|
public boolean apply(T input) {
|
|
return current.apply(input) && !other.apply(input);
|
|
}
|
|
}
|
|
}
|