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);
+ }
+}