package groovy.inspect;
import groovy.lang.GroovyShell;
import groovy.lang.MetaProperty;
import groovy.lang.PropertyValue;
import org.jmock.Mock;
import org.jmock.cglib.MockObjectTestCase;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class InspectorTest extends MockObjectTestCase implements Serializable {
public String someField = "only for testing";
public static final String SOME_CONST = "only for testing";
public InspectorTest(String name) {
super(name);
}
// additional constructor not used directly but exercises inspection code
public InspectorTest(String name, Object other) throws RuntimeException, Throwable {
super(name);
}
public void testCtor() {
Object object = new Object();
Inspector inspector = new Inspector(object);
assertEquals(object, inspector.getObject());
try {
new Inspector(null);
fail("should have thown IllegalArgumentException");
} catch (Exception expected) {
}
}
public void testClassPropsJava() {
Inspector insp = new Inspector(this);
String[] classProps = insp.getClassProps();
assertEquals("package groovy.inspect", classProps[Inspector.CLASS_PACKAGE_IDX]);
assertEquals("public class InspectorTest", classProps[Inspector.CLASS_CLASS_IDX]);
assertEquals("implements Serializable ", classProps[Inspector.CLASS_INTERFACE_IDX]);
assertEquals("extends MockObjectTestCase", classProps[Inspector.CLASS_SUPERCLASS_IDX]);
assertEquals("is Primitive: false, is Array: false, is Groovy: false", classProps[Inspector.CLASS_OTHER_IDX]);
}
public void testClassPropsGroovy() {
Object testObject = new GroovyShell().evaluate("class Test {def meth1(a,b){}}\nreturn new Test()");
Inspector insp = new Inspector(testObject);
String[] classProps = insp.getClassProps();
assertEquals("package n/a", classProps[Inspector.CLASS_PACKAGE_IDX]);
assertEquals("public class Test", classProps[Inspector.CLASS_CLASS_IDX]);
assertEquals("implements GroovyObject ", classProps[Inspector.CLASS_INTERFACE_IDX]);
assertEquals("extends Object", classProps[Inspector.CLASS_SUPERCLASS_IDX]);
assertEquals("is Primitive: false, is Array: false, is Groovy: true", classProps[Inspector.CLASS_OTHER_IDX]);
}
public void testMethods() {
Inspector insp = new Inspector(new Object());
Object[] methods = insp.getMethods();
assertEquals(10, methods.length);
String[] names = {"hashCode", "getClass", "wait", "wait", "wait", "equals", "notify", "notifyAll", "toString", "java.lang.Object"};
assertNameEquals(names, methods);
String[] details = {"JAVA", "public final", "Object", "void", "wait", "long, int", "InterruptedException"};
assertContains(methods, details);
// ctors are not considered static !
String[] ctorDetails = {"JAVA", "public", "Object", "Object", "java.lang.Object", "", ""};
assertContains(methods, ctorDetails);
}
public void testStaticMethods() {
Inspector insp = new Inspector(this);
Object[] methods = insp.getMethods();
for (int i = 0; i < methods.length; i++) {
String[] strings = (String[]) methods[i];
if (strings[1].indexOf("static") > -1) return; // ok, found one static method
}
fail("there should have been at least one static method in this TestCase, e.g. 'fail'.");
}
public void testMetaMethods() {
Inspector insp = new Inspector(new Object());
Object[] metaMethods = insp.getMetaMethods();
String[] names = {"sleep", "sleep", "println", "println", "println", "find", "find", "findResult", "findResult",
"print", "print", "each", "invokeMethod", "asType", "inspect", "is", "isCase", "identity", "getAt",
"putAt", "dump", "getMetaPropertyValues", "getProperties", "use", "use", "use", "printf", "printf",
"eachWithIndex", "every", "every", "any", "any", "grep", "grep", "collect", "collect", "findAll","findAll",
"split", "findIndexOf", "findIndexOf", "findLastIndexOf", "findLastIndexOf", "findIndexValues", "findIndexValues",
"iterator", "addShutdownHook", "sprintf", "sprintf", "with", "inject", "getMetaClass", "setMetaClass",
"metaClass", "respondsTo", "respondsTo", "hasProperty", "toString", "asBoolean"
};
assertEquals("Incorrect number of methods found examining: " + getNamesFor(metaMethods), names.length, metaMethods.length);
assertNameEquals(names, metaMethods);
String[] details = {"GROOVY", "public", "Object", "void", "println", "Object", "n/a"};
assertContains(metaMethods, details);
}
static class ClassWithPrivate {
private String hidden = "you can't see me";
}
// TODO: if our code can never access inspect in this way, it would be better
// to move this to a boundary class and then we wouldn't need this test
public void testInspectPrivateField() throws NoSuchFieldException {
ClassWithPrivate underInspection = new ClassWithPrivate();
Field field = underInspection.getClass().getDeclaredField("hidden");
Inspector inspector = getTestableInspector(underInspection);
String[] result = inspector.fieldInfo(field);
assertEquals(Inspector.NOT_APPLICABLE, result[Inspector.MEMBER_VALUE_IDX]);
}
// TODO: if our code can never access inspect in this way, it would be better
// to move this to a boundary class and then we wouldn't need this test
public void testInspectUninspectableProperty() {
Object dummyInstance = new Object();
Inspector inspector = getTestableInspector(dummyInstance);
Class[] paramTypes = {Object.class, MetaProperty.class};
Object[] params = {null, null};
Mock mock = mock(PropertyValue.class, paramTypes, params);
mock.expects(once()).method("getType");
mock.expects(once()).method("getName");
mock.expects(once()).method("getValue").will(throwException(new RuntimeException()));
PropertyValue propertyValue = (PropertyValue) mock.proxy();
String[] result = inspector.fieldInfo(propertyValue);
assertEquals(Inspector.NOT_APPLICABLE, result[Inspector.MEMBER_VALUE_IDX]);
}
private Inspector getTestableInspector(Object objectUnderInspection) {
return new Inspector(objectUnderInspection) {
public String[] fieldInfo(Field field) {
return super.fieldInfo(field);
}
public String[] fieldInfo(PropertyValue pv) {
return super.fieldInfo(pv);
}
};
}
public void testSortWithDifferentOrigin() {
String[] details2 = {"JAVA", "public", "Object", "void", "println", "Object", "n/a"};
String[] details1 = {"GROOVY", "public", "Object", "void", "println", "Object", "n/a"};
String[] first = sortWithMemberComparator(details1, details2);
assertEquals("GROOVY", first[0]);
}
public void testSortWithDifferentModifier() {
String[] details2 = {null, "public", "Object", "void", "println", "Object", "n/a"};
String[] details1 = {null, "private", "Object", "void", "println", "Object", "n/a"};
String[] first = sortWithMemberComparator(details1, details2);
assertEquals("private", first[1]);
}
private String[] sortWithMemberComparator(String[] details1, String[] details2) {
List details = new ArrayList();
details.add(details1);
details.add(details2);
Inspector.sort(details);
return (String[]) details.get(0);
}
public void testStaticMetaMethods() {
Matcher matcher = Pattern.compile("").matcher("");
Inspector insp = new Inspector(matcher);
Object[] metaMethods = insp.getMetaMethods();
assertUnique(Inspector.sort(Arrays.asList(metaMethods)));
String[] details = {"GROOVY", "public static", "Matcher", "Matcher", "getLastMatcher", "", "n/a"};
assertContains(metaMethods, details);
}
public void testFields() {
Inspector insp = new Inspector(this);
Object[] fields = insp.getPublicFields();
assertEquals(5, fields.length); // 3 from JMock
String[] names = {"someField", "SOME_CONST", "ANYTHING", "NULL", "NOT_NULL"};
assertNameEquals(names, fields);
String[] details = {"JAVA", "public", "InspectorTest", "String", "someField", "\"only for testing\""};
assertContains(fields, details);
}
public void testProperties() {
Inspector insp = new Inspector(this);
Object[] properties = insp.getPropertyInfo();
assertEquals(2, properties.length);
String[] names = {"class", "name"};
assertNameEquals(names, properties);
String[] details = {"GROOVY", "public", "n/a", "Class", "class", "class groovy.inspect.InspectorTest"};
assertContains(properties, details);
}
public void testPrint() {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(bytes);
String ls = System.getProperty("line.separator");
String[] first = {"a", "b"};
String[] second = {"x", "y"};
Object[] memberInfo = {first, second};
Inspector.print(printStream, memberInfo);
assertEquals("0:\ta b " + ls + "1:\tx y " + ls, bytes.toString());
// just for coverage, print to System.out (yuck)
Inspector.print(memberInfo);
}
private List getNamesFor(Object[] metaMethods) {
List result = new ArrayList();
for (int i = 0; i < metaMethods.length; i++) {
String[] strings = (String[]) metaMethods[i];
result.add(strings[Inspector.MEMBER_NAME_IDX]);
}
return result;
}
private void assertNameEquals(String[] names, Object[] metaMethods) {
Set metaSet = new HashSet();
for (int i = 0; i < metaMethods.length; i++) {
String[] strings = (String[]) metaMethods[i];
metaSet.add(strings[Inspector.MEMBER_NAME_IDX]);
}
Set nameSet = new HashSet(Arrays.asList(names));
assertEquals(nameSet, metaSet);
}
private void assertContains(Object[] candidates, String[] sample) {
String sampleBuffer = concat(sample);
for (int i = 0; i < candidates.length; i++) {
String[] entry = (String[]) candidates[i];
if (sampleBuffer.equals(concat(entry))) return;
}
fail("should have found sample: " + sampleBuffer);
}
private void assertUnique(Collection sortedMembers) {
if (sortedMembers.size() < 2) return;
Comparator comp = new Inspector.MemberComparator();
Iterator iter = sortedMembers.iterator();
Object last = iter.next();
while (iter.hasNext()) {
Object element = iter.next();
if (0 == comp.compare(last, element)) {
fail("found duplication for element " + element);
}
last = element;
}
}
private String concat(String[] details) {
StringBuffer detailBuffer = new StringBuffer();
for (int i = 0; i < details.length; i++) {
detailBuffer.append(details[i]);
detailBuffer.append(" ");
}
return detailBuffer.toString();
}
}