diff --git a/hamcrest/src/main/java/org/hamcrest/reflection/Visibility.java b/hamcrest/src/main/java/org/hamcrest/reflection/Visibility.java new file mode 100644 index 000000000..d05f1ec37 --- /dev/null +++ b/hamcrest/src/main/java/org/hamcrest/reflection/Visibility.java @@ -0,0 +1,57 @@ +package org.hamcrest.reflection; + +import java.lang.reflect.Member; +import java.lang.reflect.Modifier; +import java.util.Objects; + +/** + * Represents the 4 states of visibility. + * + * @author JJ Brown + */ +enum Visibility { + PUBLIC("public"), + PROTECTED("protected"), + PACKAGE_PROTECTED("package-protected (no modifiers)"), + PRIVATE("private"); + + public String getDescription() { + return description; + } + + private final String description; + + Visibility(String description) { + this.description = description; + } + + static Visibility of(Class clazz) { + Objects.requireNonNull(clazz, "Cannot determine the visibility of a null-valued reflective Class object"); + + if (Modifier.isPublic(clazz.getModifiers())) { + return Visibility.PUBLIC; + } + if (Modifier.isProtected(clazz.getModifiers())) { + return Visibility.PROTECTED; + } + if (Modifier.isPrivate(clazz.getModifiers())) { + return Visibility.PRIVATE; + } + return Visibility.PACKAGE_PROTECTED; + } + + static Visibility of(Member member) { + Objects.requireNonNull(member, "Cannot determine the visibility of a null-valued reflective member object"); + + if (Modifier.isPublic(member.getModifiers())) { + return Visibility.PUBLIC; + } + if (Modifier.isProtected(member.getModifiers())) { + return Visibility.PROTECTED; + } + if (Modifier.isPrivate(member.getModifiers())) { + return Visibility.PRIVATE; + } + return Visibility.PACKAGE_PROTECTED; + } +} diff --git a/hamcrest/src/main/java/org/hamcrest/reflection/VisibilityMatcher.java b/hamcrest/src/main/java/org/hamcrest/reflection/VisibilityMatcher.java new file mode 100644 index 000000000..6acc50ddd --- /dev/null +++ b/hamcrest/src/main/java/org/hamcrest/reflection/VisibilityMatcher.java @@ -0,0 +1,62 @@ +package org.hamcrest.reflection; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; + +import java.lang.reflect.Member; + +/** + * Matches the visibility of a reflective element, like a {@link Class} or a {@link java.lang.reflect.Method}, + * to make assertions about the scope of a module's API. + *

+ * This class is intentionally not exposed to the public API, to help keep implementation details hidden (and easy to change). + * Please use {@link VisibilityMatchers} to instantiate instances of this class. + * + * @param the type of the element being matched; could be anything + * @author JJ Brown + * @see VisibilityMatchers + */ +class VisibilityMatcher extends BaseMatcher { + private final Visibility expectedVisibility; + + VisibilityMatcher(Visibility expectedVisibility) { + this.expectedVisibility = expectedVisibility; + } + + @Override + public boolean matches(Object actual) { + if (actual == null) { + return false; + } + if (actual instanceof Class) { + return expectedVisibility == Visibility.of((Class) actual); + } + if (actual instanceof Member) { + return expectedVisibility == Visibility.of((Member) actual); + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendText("is ").appendText(expectedVisibility.getDescription()); + } + + @Override + public void describeMismatch(Object item, Description description) { + if (item == null) { + description.appendText("was null"); + } else if (item instanceof Class) { + description.appendText("was a ") + .appendText(Visibility.of((Class) item).getDescription()) + .appendText(" class"); + } else if (item instanceof Member) { + description.appendText("was a ") + .appendText(Visibility.of((Member) item).getDescription()) + .appendText(" ") + .appendText(item.getClass().getName()); + } else { + description.appendText("was " + item.getClass().getName() + " instead of a reflective element like a Class, Constructor, or Method"); + } + } +} diff --git a/hamcrest/src/main/java/org/hamcrest/reflection/VisibilityMatchers.java b/hamcrest/src/main/java/org/hamcrest/reflection/VisibilityMatchers.java new file mode 100644 index 000000000..b3a69ca40 --- /dev/null +++ b/hamcrest/src/main/java/org/hamcrest/reflection/VisibilityMatchers.java @@ -0,0 +1,89 @@ +package org.hamcrest.reflection; + +import org.hamcrest.Matcher; + +/** + * Defines matchers that check the visibility of reflective objects like {@link java.lang.Class} or {@link java.lang.reflect.Method}. + * {@code null} values never match, nor do normal objects; these simply do not match, without raising an Exception. + * + * @author JJ Brown + */ +public class VisibilityMatchers { + // Each matcher is stateless and can match any type, so the individual instances are only made once and stored here for re-use. + private static final VisibilityMatcher PUBLIC = new VisibilityMatcher<>(Visibility.PUBLIC); + private static final VisibilityMatcher PROTECTED = new VisibilityMatcher<>(Visibility.PROTECTED); + private static final VisibilityMatcher PACKAGE_PROTECTED = new VisibilityMatcher<>(Visibility.PACKAGE_PROTECTED); + private static final VisibilityMatcher PRIVATE = new VisibilityMatcher<>(Visibility.PRIVATE); + + /** + * Matchers reflective elements that have public visibility. + * Specifically, this matcher only matches elements marked with the keyword {@code public}. + *
+ * This method matches {@link Class} objects or other {@link java.lang.reflect.Member reflective objects} + * like {@link java.lang.reflect.Field} or {@link java.lang.reflect.Method} used in reflection. + * Any other kind of object, or {@code null} values, do not match (but will not cause an Exception). + * + * @param the type of the object being matched + * @return a matcher that matches reflective elements with exactly the given level of visibility + */ + @SuppressWarnings("unchecked") + public static Matcher isPublic() { + // Each matcher is stateless and can match any type (the generic is for type safety at the use site), + // so it's fine to cast the non-reifiable generic type here at runtime and re-use the same instance. + return (Matcher) PUBLIC; + } + + /** + * Matchers reflective elements that have protected visibility. + * Specifically, this matcher only matches elements marked with the keyword {@code protected}; it does NOT match public or private elements. + *
+ * This method matches {@link Class} objects or other {@link java.lang.reflect.Member reflective objects} + * like {@link java.lang.reflect.Field} or {@link java.lang.reflect.Method} used in reflection. + * Any other kind of object, or {@code null} values, do not match (but will not cause an Exception). + * + * @param the type of the object being matched + * @return a matcher that matches reflective elements with exactly the given level of visibility + */ + @SuppressWarnings("unchecked") + public static Matcher isProtected() { + // Each matcher is stateless and can match any type (the generic is for type safety at the use site), + // so it's fine to cast the non-reifiable generic type here at runtime and re-use the same instance. + return (Matcher) PROTECTED; + } + + /** + * Matchers reflective elements that have package-protected visibility. + * Specifically, this matcher only matches elements not marked with any of the visibility keywords {@code public}, {@code protected}, or {@code private}. + *
+ * This method matches {@link Class} objects or other {@link java.lang.reflect.Member reflective objects} + * like {@link java.lang.reflect.Field} or {@link java.lang.reflect.Method} used in reflection. + * Any other kind of object, or {@code null} values, do not match (but will not cause an Exception). + * + * @param the type of the object being matched + * @return a matcher that matches reflective elements with exactly the given level of visibility + */ + @SuppressWarnings("unchecked") + public static Matcher isPackageProtected() { + // Each matcher is stateless and can match any type (the generic is for type safety at the use site), + // so it's fine to cast the non-reifiable generic type here at runtime and re-use the same instance. + return (Matcher) PACKAGE_PROTECTED; + } + + /** + * Matchers reflective elements that have private visibility. + * Specifically, this matcher only matches elements marked with the keyword {@code private}. + *
+ * This method matches {@link Class} objects or other {@link java.lang.reflect.Member reflective objects} + * like {@link java.lang.reflect.Field} or {@link java.lang.reflect.Method} used in reflection. + * Any other kind of object, or {@code null} values, do not match (but will not cause an Exception). + * + * @param the type of the object being matched + * @return a matcher that matches reflective elements with exactly the given level of visibility + */ + @SuppressWarnings("unchecked") + public static Matcher isPrivate() { + // Each matcher is stateless and can match any type (the generic is for type safety at the use site), + // so it's fine to cast the non-reifiable generic type here at runtime and re-use the same instance. + return (Matcher) PRIVATE; + } +} \ No newline at end of file diff --git a/hamcrest/src/main/java/org/hamcrest/reflection/package.html b/hamcrest/src/main/java/org/hamcrest/reflection/package.html new file mode 100644 index 000000000..83330870f --- /dev/null +++ b/hamcrest/src/main/java/org/hamcrest/reflection/package.html @@ -0,0 +1,11 @@ + + + + + + +

Matchers that perform checks on reflective elements, such as Class<?> and Method<?> objects.

+

This provides tools to enforce boundaries about the scope of visible items in a module, + and to explicitly ensure that items are available to reflection when they may only be loaded at runtime.

+ + \ No newline at end of file diff --git a/hamcrest/src/test/java/org/hamcrest/reflection/IsPackageProtectedTest.java b/hamcrest/src/test/java/org/hamcrest/reflection/IsPackageProtectedTest.java new file mode 100644 index 000000000..8d6a05939 --- /dev/null +++ b/hamcrest/src/test/java/org/hamcrest/reflection/IsPackageProtectedTest.java @@ -0,0 +1,126 @@ +package org.hamcrest.reflection; + + +import org.hamcrest.AbstractMatcherTest; +import org.hamcrest.Matcher; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import static org.hamcrest.reflection.VisibilityMatchers.isPackageProtected; +import static org.hamcrest.reflection.VisibilityMatchers.isPublic; + +@SuppressWarnings("unused") +public class IsPackageProtectedTest extends AbstractMatcherTest { + @Override + protected Matcher createMatcher() { + return isPackageProtected(); + } + + @Test + public void test_packageExposesPublicFactoryMethod() throws NoSuchMethodException { + assertMatches(isPublic(), VisibilityMatchers.class.getMethod("isPackageProtected")); + } + + @Test + public void test_isPackageProtected_matchesOnlyPackageProtectedClasses() { + assertDoesNotMatch(isPackageProtected(), PublicClass.class); + assertDoesNotMatch(isPackageProtected(), ProtectedClass.class); + assertMatches(isPackageProtected(), PackageProtectedClass.class); + assertDoesNotMatch(isPackageProtected(), PrivateClass.class); + + assertDescription("is package-protected (no modifiers)", isPackageProtected()); + + assertMismatchDescription("was a public class", isPackageProtected(), PublicClass.class); + assertMismatchDescription("was a protected class", isPackageProtected(), ProtectedClass.class); + assertMismatchDescription("was a private class", isPackageProtected(), PrivateClass.class); + } + + + @Test + public void test_isPackageProtected_matchesOnlyPackageProtectedFields() throws NoSuchFieldException { + Field publicField = ExampleFields.class.getDeclaredField("publicField"); + Field protectedField = ExampleFields.class.getDeclaredField("protectedField"); + Field packageProtectedField = ExampleFields.class.getDeclaredField("packageProtectedField"); + Field privateField = ExampleFields.class.getDeclaredField("privateField"); + + assertDoesNotMatch(isPackageProtected(), publicField); + assertDoesNotMatch(isPackageProtected(), protectedField); + assertMatches(isPackageProtected(), packageProtectedField); + assertDoesNotMatch(isPackageProtected(), privateField); + + assertDescription("is package-protected (no modifiers)", isPackageProtected()); + + assertMismatchDescription("was a public java.lang.reflect.Field", isPackageProtected(), publicField); + assertMismatchDescription("was a protected java.lang.reflect.Field", isPackageProtected(), protectedField); + assertMismatchDescription("was a private java.lang.reflect.Field", isPackageProtected(), privateField); + } + + + @Test + public void test_isPackageProtected_matchesOnlyPackageProtectedMethods() throws NoSuchMethodException { + Method publicMethod = ExampleMethods.class.getDeclaredMethod("publicMethod"); + Method protectedMethod = ExampleMethods.class.getDeclaredMethod("protectedMethod"); + Method packageProtectedMethod = ExampleMethods.class.getDeclaredMethod("packageProtectedMethod"); + Method privateMethod = ExampleMethods.class.getDeclaredMethod("privateMethod"); + + assertDoesNotMatch(isPackageProtected(), publicMethod); + assertDoesNotMatch(isPackageProtected(), protectedMethod); + assertMatches(isPackageProtected(), packageProtectedMethod); + assertDoesNotMatch(isPackageProtected(), privateMethod); + + assertDescription("is package-protected (no modifiers)", isPackageProtected()); + + assertMismatchDescription("was a public java.lang.reflect.Method", isPackageProtected(), publicMethod); + assertMismatchDescription("was a protected java.lang.reflect.Method", isPackageProtected(), protectedMethod); + assertMismatchDescription("was a private java.lang.reflect.Method", isPackageProtected(), privateMethod); + } + + @Test + public void test_isPackageProtected_doesNotMatchNull() { + assertDoesNotMatch(isPackageProtected(), null); + + assertMismatchDescription("was null", isPackageProtected(), null); + } + + @Test + public void test_isPackageProtected_doesNotMatchNonReflectiveElement() { + assertDoesNotMatch(isPackageProtected(), new Object()); + + assertMismatchDescription("was java.lang.Object instead of a reflective element like a Class, Constructor, or Method", isPackageProtected(), new Object()); + } + + public static class PublicClass { + } + + protected static class ProtectedClass { + } + + static class PackageProtectedClass { + } + + private static class PrivateClass { + } + + private static class ExampleFields { + public Void publicField; + protected Void protectedField; + Void packageProtectedField; + private Void privateField; + } + + private static class ExampleMethods { + public void publicMethod() { + } + + protected void protectedMethod() { + } + + void packageProtectedMethod() { + } + + private void privateMethod() { + } + } +} \ No newline at end of file diff --git a/hamcrest/src/test/java/org/hamcrest/reflection/IsPrivateTest.java b/hamcrest/src/test/java/org/hamcrest/reflection/IsPrivateTest.java new file mode 100644 index 000000000..9878a81b1 --- /dev/null +++ b/hamcrest/src/test/java/org/hamcrest/reflection/IsPrivateTest.java @@ -0,0 +1,126 @@ +package org.hamcrest.reflection; + + +import org.hamcrest.AbstractMatcherTest; +import org.hamcrest.Matcher; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import static org.hamcrest.reflection.VisibilityMatchers.isPrivate; +import static org.hamcrest.reflection.VisibilityMatchers.isPublic; + +@SuppressWarnings("unused") +public class IsPrivateTest extends AbstractMatcherTest { + @Override + protected Matcher createMatcher() { + return isPrivate(); + } + + @Test + public void test_packageExposesPublicFactoryMethod() throws NoSuchMethodException { + assertMatches(isPublic(), VisibilityMatchers.class.getMethod("isPrivate")); + } + + @Test + public void test_isPrivate_matchesOnlyProtectedClasses() { + assertDoesNotMatch(isPrivate(), PublicClass.class); + assertDoesNotMatch(isPrivate(), ProtectedClass.class); + assertDoesNotMatch(isPrivate(), PackageProtectedClass.class); + assertMatches(isPrivate(), PrivateClass.class); + + assertDescription("is private", isPrivate()); + + assertMismatchDescription("was a public class", isPrivate(), PublicClass.class); + assertMismatchDescription("was a protected class", isPrivate(), ProtectedClass.class); + assertMismatchDescription("was a package-protected (no modifiers) class", isPrivate(), PackageProtectedClass.class); + } + + + @Test + public void test_isPrivate_matchesOnlyProtectedFields() throws NoSuchFieldException { + Field publicField = ExampleFields.class.getDeclaredField("publicField"); + Field protectedField = ExampleFields.class.getDeclaredField("protectedField"); + Field packageProtectedField = ExampleFields.class.getDeclaredField("packageProtectedField"); + Field privateField = ExampleFields.class.getDeclaredField("privateField"); + + assertDoesNotMatch(isPrivate(), publicField); + assertDoesNotMatch(isPrivate(), protectedField); + assertDoesNotMatch(isPrivate(), packageProtectedField); + assertMatches(isPrivate(), privateField); + + assertDescription("is private", isPrivate()); + + assertMismatchDescription("was a public java.lang.reflect.Field", isPrivate(), publicField); + assertMismatchDescription("was a protected java.lang.reflect.Field", isPrivate(), protectedField); + assertMismatchDescription("was a package-protected (no modifiers) java.lang.reflect.Field", isPrivate(), packageProtectedField); + } + + + @Test + public void test_isPrivate_matchesOnlyProtectedMethods() throws NoSuchMethodException { + Method publicMethod = ExampleMethods.class.getDeclaredMethod("publicMethod"); + Method protectedMethod = ExampleMethods.class.getDeclaredMethod("protectedMethod"); + Method packageProtectedMethod = ExampleMethods.class.getDeclaredMethod("packageProtectedMethod"); + Method privateMethod = ExampleMethods.class.getDeclaredMethod("privateMethod"); + + assertDoesNotMatch(isPrivate(), publicMethod); + assertDoesNotMatch(isPrivate(), protectedMethod); + assertDoesNotMatch(isPrivate(), packageProtectedMethod); + assertMatches(isPrivate(), privateMethod); + + assertDescription("is private", isPrivate()); + + assertMismatchDescription("was a public java.lang.reflect.Method", isPrivate(), publicMethod); + assertMismatchDescription("was a protected java.lang.reflect.Method", isPrivate(), protectedMethod); + assertMismatchDescription("was a package-protected (no modifiers) java.lang.reflect.Method", isPrivate(), packageProtectedMethod); + } + + @Test + public void test_isPrivate_doesNotMatchNull() { + assertDoesNotMatch(isPrivate(), null); + + assertMismatchDescription("was null", isPrivate(), null); + } + + @Test + public void test_isPrivate_doesNotMatchNonReflectiveElement() { + assertDoesNotMatch(isPrivate(), new Object()); + + assertMismatchDescription("was java.lang.Object instead of a reflective element like a Class, Constructor, or Method", isPrivate(), new Object()); + } + + public static class PublicClass { + } + + protected static class ProtectedClass { + } + + static class PackageProtectedClass { + } + + private static class PrivateClass { + } + + private static class ExampleFields { + public Void publicField; + protected Void protectedField; + Void packageProtectedField; + private Void privateField; + } + + private static class ExampleMethods { + public void publicMethod() { + } + + protected void protectedMethod() { + } + + void packageProtectedMethod() { + } + + private void privateMethod() { + } + } +} \ No newline at end of file diff --git a/hamcrest/src/test/java/org/hamcrest/reflection/IsProtectedTest.java b/hamcrest/src/test/java/org/hamcrest/reflection/IsProtectedTest.java new file mode 100644 index 000000000..864a5b51c --- /dev/null +++ b/hamcrest/src/test/java/org/hamcrest/reflection/IsProtectedTest.java @@ -0,0 +1,126 @@ +package org.hamcrest.reflection; + + +import org.hamcrest.AbstractMatcherTest; +import org.hamcrest.Matcher; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import static org.hamcrest.reflection.VisibilityMatchers.isProtected; +import static org.hamcrest.reflection.VisibilityMatchers.isPublic; + +@SuppressWarnings("unused") +public class IsProtectedTest extends AbstractMatcherTest { + @Override + protected Matcher createMatcher() { + return isProtected(); + } + + @Test + public void test_packageExposesPublicFactoryMethod() throws NoSuchMethodException { + assertMatches(isPublic(), VisibilityMatchers.class.getMethod("isProtected")); + } + + @Test + public void test_isProtected_matchesOnlyProtectedClasses() { + assertDoesNotMatch(isProtected(), PublicClass.class); + assertMatches(isProtected(), ProtectedClass.class); + assertDoesNotMatch(isProtected(), PackageProtectedClass.class); + assertDoesNotMatch(isProtected(), PrivateClass.class); + + assertDescription("is protected", isProtected()); + + assertMismatchDescription("was a public class", isProtected(), PublicClass.class); + assertMismatchDescription("was a package-protected (no modifiers) class", isProtected(), PackageProtectedClass.class); + assertMismatchDescription("was a private class", isProtected(), PrivateClass.class); + } + + + @Test + public void test_isProtected_matchesOnlyProtectedFields() throws NoSuchFieldException { + Field publicField = ExampleFields.class.getDeclaredField("publicField"); + Field protectedField = ExampleFields.class.getDeclaredField("protectedField"); + Field packageProtectedField = ExampleFields.class.getDeclaredField("packageProtectedField"); + Field privateField = ExampleFields.class.getDeclaredField("privateField"); + + assertDoesNotMatch(isProtected(), publicField); + assertMatches(isProtected(), protectedField); + assertDoesNotMatch(isProtected(), packageProtectedField); + assertDoesNotMatch(isProtected(), privateField); + + assertDescription("is protected", isProtected()); + + assertMismatchDescription("was a public java.lang.reflect.Field", isProtected(), publicField); + assertMismatchDescription("was a package-protected (no modifiers) java.lang.reflect.Field", isProtected(), packageProtectedField); + assertMismatchDescription("was a private java.lang.reflect.Field", isProtected(), privateField); + } + + + @Test + public void test_isProtected_matchesOnlyProtectedMethods() throws NoSuchMethodException { + Method publicMethod = ExampleMethods.class.getDeclaredMethod("publicMethod"); + Method protectedMethod = ExampleMethods.class.getDeclaredMethod("protectedMethod"); + Method packageProtectedMethod = ExampleMethods.class.getDeclaredMethod("packageProtectedMethod"); + Method privateMethod = ExampleMethods.class.getDeclaredMethod("privateMethod"); + + assertDoesNotMatch(isProtected(), publicMethod); + assertMatches(isProtected(), protectedMethod); + assertDoesNotMatch(isProtected(), packageProtectedMethod); + assertDoesNotMatch(isProtected(), privateMethod); + + assertDescription("is protected", isProtected()); + + assertMismatchDescription("was a public java.lang.reflect.Method", isProtected(), publicMethod); + assertMismatchDescription("was a package-protected (no modifiers) java.lang.reflect.Method", isProtected(), packageProtectedMethod); + assertMismatchDescription("was a private java.lang.reflect.Method", isProtected(), privateMethod); + } + + @Test + public void test_isProtected_doesNotMatchNull() { + assertDoesNotMatch(isProtected(), null); + + assertMismatchDescription("was null", isProtected(), null); + } + + @Test + public void test_isProtected_doesNotMatchNonReflectiveElement() { + assertDoesNotMatch(isProtected(), new Object()); + + assertMismatchDescription("was java.lang.Object instead of a reflective element like a Class, Constructor, or Method", isProtected(), new Object()); + } + + public static class PublicClass { + } + + protected static class ProtectedClass { + } + + static class PackageProtectedClass { + } + + private static class PrivateClass { + } + + private static class ExampleFields { + public Void publicField; + protected Void protectedField; + Void packageProtectedField; + private Void privateField; + } + + private static class ExampleMethods { + public void publicMethod() { + } + + protected void protectedMethod() { + } + + void packageProtectedMethod() { + } + + private void privateMethod() { + } + } +} \ No newline at end of file diff --git a/hamcrest/src/test/java/org/hamcrest/reflection/IsPublicTest.java b/hamcrest/src/test/java/org/hamcrest/reflection/IsPublicTest.java new file mode 100644 index 000000000..b4eb8267e --- /dev/null +++ b/hamcrest/src/test/java/org/hamcrest/reflection/IsPublicTest.java @@ -0,0 +1,126 @@ +package org.hamcrest.reflection; + + +import org.hamcrest.AbstractMatcherTest; +import org.hamcrest.Matcher; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import static org.hamcrest.reflection.VisibilityMatchers.isPublic; + +@SuppressWarnings("unused") +public class IsPublicTest extends AbstractMatcherTest { + @Override + protected Matcher createMatcher() { + return isPublic(); + } + + @Test + public void test_packageExposesPublicFactoryMethod() throws NoSuchMethodException { + assertMatches(isPublic(), VisibilityMatchers.class); + assertMatches(isPublic(), VisibilityMatchers.class.getMethod("isPublic")); + } + + @Test + public void test_isPublic_matchesOnlyPublicClasses() { + assertMatches(isPublic(), PublicClass.class); + assertDoesNotMatch(isPublic(), ProtectedClass.class); + assertDoesNotMatch(isPublic(), PackageProtectedClass.class); + assertDoesNotMatch(isPublic(), PrivateClass.class); + + assertDescription("is public", isPublic()); + + assertMismatchDescription("was a protected class", isPublic(), ProtectedClass.class); + assertMismatchDescription("was a package-protected (no modifiers) class", isPublic(), PackageProtectedClass.class); + assertMismatchDescription("was a private class", isPublic(), PrivateClass.class); + } + + + @Test + public void test_isPublic_matchesOnlyPublicFields() throws NoSuchFieldException { + Field publicField = ExampleFields.class.getDeclaredField("publicField"); + Field protectedField = ExampleFields.class.getDeclaredField("protectedField"); + Field packageProtectedField = ExampleFields.class.getDeclaredField("packageProtectedField"); + Field privateField = ExampleFields.class.getDeclaredField("privateField"); + + assertMatches(isPublic(), publicField); + assertDoesNotMatch(isPublic(), protectedField); + assertDoesNotMatch(isPublic(), packageProtectedField); + assertDoesNotMatch(isPublic(), privateField); + + assertDescription("is public", isPublic()); + + assertMismatchDescription("was a protected java.lang.reflect.Field", isPublic(), protectedField); + assertMismatchDescription("was a package-protected (no modifiers) java.lang.reflect.Field", isPublic(), packageProtectedField); + assertMismatchDescription("was a private java.lang.reflect.Field", isPublic(), privateField); + } + + + @Test + public void test_isPublic_matchesOnlyPublicMethods() throws NoSuchMethodException { + Method publicMethod = ExampleMethods.class.getDeclaredMethod("publicMethod"); + Method protectedMethod = ExampleMethods.class.getDeclaredMethod("protectedMethod"); + Method packageProtectedMethod = ExampleMethods.class.getDeclaredMethod("packageProtectedMethod"); + Method privateMethod = ExampleMethods.class.getDeclaredMethod("privateMethod"); + + assertMatches(isPublic(), publicMethod); + assertDoesNotMatch(isPublic(), protectedMethod); + assertDoesNotMatch(isPublic(), packageProtectedMethod); + assertDoesNotMatch(isPublic(), privateMethod); + + assertDescription("is public", isPublic()); + + assertMismatchDescription("was a protected java.lang.reflect.Method", isPublic(), protectedMethod); + assertMismatchDescription("was a package-protected (no modifiers) java.lang.reflect.Method", isPublic(), packageProtectedMethod); + assertMismatchDescription("was a private java.lang.reflect.Method", isPublic(), privateMethod); + } + + @Test + public void test_isPublic_doesNotMatchNull() { + assertDoesNotMatch(isPublic(), null); + + assertMismatchDescription("was null", isPublic(), null); + } + + @Test + public void test_isPublic_doesNotMatchNonReflectiveElement() { + assertDoesNotMatch(isPublic(), new Object()); + + assertMismatchDescription("was java.lang.Object instead of a reflective element like a Class, Constructor, or Method", isPublic(), new Object()); + } + + public static class PublicClass { + } + + protected static class ProtectedClass { + } + + static class PackageProtectedClass { + } + + private static class PrivateClass { + } + + private static class ExampleFields { + public Void publicField; + protected Void protectedField; + Void packageProtectedField; + private Void privateField; + } + + private static class ExampleMethods { + public void publicMethod() { + } + + protected void protectedMethod() { + } + + void packageProtectedMethod() { + } + + private void privateMethod() { + } + } +} \ No newline at end of file diff --git a/hamcrest/src/test/java/org/hamcrest/reflection/VisibilityMatcherTest.java b/hamcrest/src/test/java/org/hamcrest/reflection/VisibilityMatcherTest.java new file mode 100644 index 000000000..d8eca11ea --- /dev/null +++ b/hamcrest/src/test/java/org/hamcrest/reflection/VisibilityMatcherTest.java @@ -0,0 +1,31 @@ +package org.hamcrest.reflection; + +import org.junit.Test; + +import java.lang.reflect.Constructor; + +import static org.hamcrest.AbstractMatcherTest.assertMatches; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.reflection.VisibilityMatchers.isPublic; + +/** + * This test is half meant to enforce the scope of the API, + * and half meant to demonstrate one way these matchers can be used. + */ +public class VisibilityMatcherTest { + @Test + public void test_allConstructorsAreNotPublic() { + for (Constructor c : VisibilityMatcher.class.getConstructors()) { + assertMatches( + "The constructors shouldn't be public, so that their implementation details are easy to change if need be; all we get are Matchers of reflective elements since that's all the behavior we need. Prefer the more-fluent API in VisibilityMatchers.", + not(isPublic()), c); + } + } + + @Test + public void test_classIsNotPublic() { + assertMatches( + "The class shouldn't be public, so that its implementation details are easy to change if need be; all we get are Matchers of reflective elements since that's all the behavior we need. Prefer the more-fluent API in VisibilityMatchers.", + not(isPublic()), VisibilityMatcher.class); + } +}