Skip to content

Commit

Permalink
add members().that().{have,doNotHave}FullName[Not][Matching] syntax
Browse files Browse the repository at this point in the history
Signed-off-by: Manfred Hanke <Manfred.Hanke@tngtech.com>
  • Loading branch information
hankem authored and codecholeric committed Jul 29, 2019
1 parent c45ffe1 commit 291d1d5
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,52 @@ public interface HasName {
interface AndFullName extends HasName {
@PublicAPI(usage = ACCESS)
String getFullName();

final class Predicates {
private Predicates() {
}

@PublicAPI(usage = ACCESS)
public static DescribedPredicate<HasName.AndFullName> fullName(String fullName) {
return new FullNameEqualsPredicate(fullName);
}

/**
* Matches full names against a regular expression.
*/
@PublicAPI(usage = ACCESS)
public static DescribedPredicate<HasName.AndFullName> fullNameMatching(String regex) {
return new FullNameMatchingPredicate(regex);
}

private static class FullNameEqualsPredicate extends DescribedPredicate<HasName.AndFullName> {
private final String fullName;

FullNameEqualsPredicate(String fullName) {
super(String.format("full name '%s'", fullName));
this.fullName = fullName;
}

@Override
public boolean apply(HasName.AndFullName input) {
return input.getFullName().equals(fullName);
}
}

private static class FullNameMatchingPredicate extends DescribedPredicate<HasName.AndFullName> {
private final Pattern pattern;

FullNameMatchingPredicate(String regex) {
super(String.format("full name matching '%s'", regex));
this.pattern = Pattern.compile(regex);
}

@Override
public boolean apply(HasName.AndFullName input) {
return pattern.matcher(input.getFullName()).matches();
}
}
}
}

final class Predicates {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
import static com.tngtech.archunit.core.domain.JavaMember.Predicates.declaredIn;
import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith;
import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.metaAnnotatedWith;
import static com.tngtech.archunit.core.domain.properties.HasName.AndFullName.Predicates.fullName;
import static com.tngtech.archunit.core.domain.properties.HasName.AndFullName.Predicates.fullNameMatching;
import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.name;
import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.nameMatching;
import static com.tngtech.archunit.lang.conditions.ArchPredicates.are;
Expand Down Expand Up @@ -71,6 +73,26 @@ public CONJUNCTION haveNameNotMatching(String regex) {
return givenWith(SyntaxPredicates.haveNameNotMatching(regex));
}

@Override
public CONJUNCTION haveFullName(String fullName) {
return givenWith(have(fullName(fullName)));
}

@Override
public CONJUNCTION doNotHaveFullName(String fullName) {
return givenWith(doNot(have(fullName(fullName))));
}

@Override
public CONJUNCTION haveFullNameMatching(String regex) {
return givenWith(have(fullNameMatching(regex)));
}

@Override
public CONJUNCTION haveFullNameNotMatching(String regex) {
return givenWith(have(not(fullNameMatching(regex)).as("full name not matching '%s'", regex)));
}

@Override
public CONJUNCTION arePublic() {
return givenWith(SyntaxPredicates.arePublic());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,42 @@ public interface MembersThat<CONJUNCTION extends GivenMembersConjunction<?>> {
@PublicAPI(usage = ACCESS)
CONJUNCTION haveNameNotMatching(String regex);

/**
* Matches members by their full name.
*
* @param fullName The member's full name
* @return A syntax conjunction element, which can be completed to form a full rule
*/
@PublicAPI(usage = ACCESS)
CONJUNCTION haveFullName(String fullName);

/**
* Matches members that do not have a certain full name.
*
* @param fullName The member's full name
* @return A syntax conjunction element, which can be completed to form a full rule
*/
@PublicAPI(usage = ACCESS)
CONJUNCTION doNotHaveFullName(String fullName);

/**
* Matches members with a full name matching a given regular expression.
*
* @param regex A regular expression
* @return A syntax conjunction element, which can be completed to form a full rule
*/
@PublicAPI(usage = ACCESS)
CONJUNCTION haveFullNameMatching(String regex);

/**
* Matches members with a full name not matching a given regular expression.
*
* @param regex A regular expression
* @return A syntax conjunction element, which can be completed to form a full rule
*/
@PublicAPI(usage = ACCESS)
CONJUNCTION haveFullNameNotMatching(String regex);

/**
* Matches public members.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,76 @@
import org.assertj.core.api.AbstractBooleanAssert;
import org.junit.Test;

import static com.tngtech.archunit.core.domain.properties.HasName.AndFullName.Predicates.fullName;
import static com.tngtech.archunit.core.domain.properties.HasName.AndFullName.Predicates.fullNameMatching;
import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.name;
import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.nameMatching;
import static com.tngtech.archunit.testutil.Assertions.assertThat;

public class HasNameTest {
@Test
public void match_against_regex() {
assertMatches("Some.foobar", ".*foo.*").isTrue();
assertMatches("Some.fobar", ".*foo.*").isFalse();
assertMatches("Some.foobar", ".*fob?o*.*").isTrue();
assertMatches("com.tngtech.SomeClass", ".*W.*").isFalse();
assertMatches("com.tngtech.SomeClass", "com.*").isTrue();
assertMatches("com.tngtech.SomeClass", "co\\..*").isFalse();
assertMatches("com.tngtech.SomeClass", ".*Class").isTrue();
assertMatches("com.tngtech.SomeClass", ".*Clas").isFalse();
assertMatches("com.tngtech.SomeClass", ".*\\.S.*s").isTrue();
public void nameMatching_predicate() {
assertNameMatches("Some.foobar", ".*foo.*").isTrue();
assertNameMatches("Some.fobar", ".*foo.*").isFalse();
assertNameMatches("Some.foobar", ".*fob?o*.*").isTrue();
assertNameMatches("com.tngtech.SomeClass", ".*W.*").isFalse();
assertNameMatches("com.tngtech.SomeClass", "com.*").isTrue();
assertNameMatches("com.tngtech.SomeClass", "co\\..*").isFalse();
assertNameMatches("com.tngtech.SomeClass", ".*Class").isTrue();
assertNameMatches("com.tngtech.SomeClass", ".*Clas").isFalse();
assertNameMatches("com.tngtech.SomeClass", ".*\\.S.*s").isTrue();

assertThat(nameMatching(".*foo")).hasDescription("name matching '.*foo'");
}

@Test
public void match_against_name() {
public void name_predicate() {
assertThat(name("some.Foo"))
.accepts(newHasName("some.Foo"))
.rejects(newHasName("some.Fo"))
.hasDescription("name 'some.Foo'");
assertThat(name("Foo")).rejects(newHasName("some.Foo"));
}

private AbstractBooleanAssert assertMatches(String input, String regex) {
@Test
public void fullName_predicate() {
assertThat(fullName("some.Foo.field1"))
.accepts(newHasNameAndFullName("field1", "some.Foo.field1"))
.rejects(newHasNameAndFullName("field", "some.Foo.field"))
.rejects(newHasNameAndFullName("field12", "some.Foo.field12"))
.rejects(newHasNameAndFullName("some.Foo.field1", "some.Foo.field1.property"))
.hasDescription("full name 'some.Foo.field1'");
}

@Test
public void fullNameMatching_predicate() {
assertThat(fullNameMatching(".*method\\(.*\\)"))
.accepts(newHasNameAndFullName("method", "some.Foo.method(int)"))
.accepts(newHasNameAndFullName("method", "some.Foo.method()"))
.rejects(newHasNameAndFullName("method", "some.Foo.method"))
.hasDescription("full name matching '.*method\\(.*\\)'");
}

private AbstractBooleanAssert assertNameMatches(String input, String regex) {
return assertThat(nameMatching(regex).apply(newHasName(input)))
.as(input + " =~ " + regex);
}

private HasName newHasName(final String name) {
return new HasName() {
return newHasNameAndFullName(name, "full " + name);
}

private HasName.AndFullName newHasNameAndFullName(final String name, final String fullName) {
return new HasName.AndFullName() {
@Override
public String getName() {
return name;
}

@Override
public String getFullName() {
return fullName;
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
Expand Down Expand Up @@ -52,6 +53,7 @@
import static com.tngtech.java.junit.dataprovider.DataProviders.$$;
import static java.util.Collections.emptySet;
import static java.util.Collections.singleton;
import static java.util.regex.Pattern.quote;
import static org.assertj.core.api.Assertions.assertThat;

@RunWith(DataProviderRunner.class)
Expand Down Expand Up @@ -147,6 +149,7 @@ public void complex_members_syntax() {

@DataProvider
public static Object[][] restricted_property_rule_starts() {
String classNameDot = ClassWithVariousMembers.class.getName() + ".";
ImmutableList.Builder<Object[]> data = ImmutableList.<Object[]>builder().add(
$(described(members().that().haveName(FIELD_A)), ImmutableSet.of(FIELD_A)),
$(described(codeUnits().that().haveName(FIELD_A)), ImmutableSet.of()),
Expand Down Expand Up @@ -186,6 +189,30 @@ public static Object[][] restricted_property_rule_starts() {
$(described(codeUnits().that().haveNameNotMatching(".*init.*")), ALL_METHOD_DESCRIPTIONS),
$(described(constructors().that().haveNameNotMatching(".*init.*")), emptySet()),

$(described(members().that().haveFullName(classNameDot + FIELD_A)), ImmutableSet.of(FIELD_A)),
$(described(codeUnits().that().haveFullName(classNameDot + METHOD_A)), ImmutableSet.of(METHOD_A)),
$(described(methods().that().haveFullName(classNameDot + METHOD_A)), ImmutableSet.of(METHOD_A)),
$(described(constructors().that().haveFullName(classNameDot + CONSTRUCTOR_ONE_ARG)), ImmutableSet.of(CONSTRUCTOR_ONE_ARG)),
$(described(fields().that().haveFullName(classNameDot + FIELD_A)), ImmutableSet.of(FIELD_A)),
$(described(members().that().doNotHaveFullName(classNameDot + FIELD_A)), union(allFieldsExcept(FIELD_A), ALL_CODE_UNIT_DESCRIPTIONS)),
$(described(codeUnits().that().doNotHaveFullName(classNameDot + FIELD_A)), ALL_CODE_UNIT_DESCRIPTIONS),
$(described(methods().that().doNotHaveFullName(classNameDot + METHOD_A)), allMethodsExcept(METHOD_A)),
$(described(constructors().that().doNotHaveFullName(classNameDot + CONSTRUCTOR_ONE_ARG)), allConstructorsExcept(CONSTRUCTOR_ONE_ARG)),
$(described(fields().that().doNotHaveFullName(classNameDot + FIELD_A)), allFieldsExcept(FIELD_A)),

$(described(members().that().haveFullNameMatching(quote(classNameDot) + ".*A.*")), ImmutableSet.of(FIELD_A, METHOD_A)),
$(described(codeUnits().that().haveFullNameMatching(quote(classNameDot) + ".*A.*")), ImmutableSet.of(METHOD_A)),
$(described(methods().that().haveFullNameMatching(quote(classNameDot) + ".*A.*")), ImmutableSet.of(METHOD_A)),
$(described(constructors().that().haveFullNameMatching(".*init.*")), ALL_CONSTRUCTOR_DESCRIPTIONS),
$(described(fields().that().haveFullNameMatching(quote(classNameDot) + ".*A.*")), ImmutableSet.of(FIELD_A)),
$(described(members().that().haveFullNameNotMatching(quote(classNameDot) + "f.*A.*")), union(
allFieldsExcept(FIELD_A),
ALL_CODE_UNIT_DESCRIPTIONS)),
$(described(codeUnits().that().haveFullNameNotMatching(quote(classNameDot) + "f.*A.*")), ALL_CODE_UNIT_DESCRIPTIONS),
$(described(methods().that().haveFullNameNotMatching(quote(classNameDot) + ".*A.*")), allMethodsExcept(METHOD_A)),
$(described(constructors().that().haveFullNameNotMatching(".*init.*")), emptySet()),
$(described(fields().that().haveFullNameNotMatching(quote(classNameDot) + ".*A.*")), allFieldsExcept(FIELD_A)),

$(described(members().that().arePublic()), ImmutableSet.of(
FIELD_PUBLIC, METHOD_PUBLIC, CONSTRUCTOR_PUBLIC)),
$(described(fields().that().arePublic()), ImmutableSet.of(FIELD_C)),
Expand Down

0 comments on commit 291d1d5

Please sign in to comment.