/* Copyright (c) 2009-2011 Olivier Chafik, All Rights Reserved This file is part of JNAerator (http://jnaerator.googlecode.com/). JNAerator is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. JNAerator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with JNAerator. If not, see <http://www.gnu.org/licenses/>. */ package com.ochafik.lang.jnaerator.parser; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import static org.junit.Assert.*; import static com.ochafik.lang.SyntaxUtils.*; import com.ochafik.beans.BeansUtils; import com.ochafik.junit.ParameterizedWithDescription; //import com.ochafik.lang.jnaerator.parser.Expression.FunctionCall; import com.ochafik.lang.reflect.GettersAndSettersHelper; import com.ochafik.lang.reflect.GettersAndSettersHelper.GetterAndSetterInfo; import com.nativelibs4java.jalico.Pair; @SuppressWarnings("unused") //@RunWith(Parameterized.class) @RunWith(Parameterized.class) public class ObjCppElementsTest { String description; Class<? extends Element> type; /// Gives the set of subclasses that implement a given abstract class or interface Map<Class<?>, Set<Class<?>>> implementations; public ObjCppElementsTest(String description, Class<? extends Element> type, Map<Class<?>, Set<Class<?>>> implementations) { super(); this.description = description; this.type = type; this.implementations = implementations; } final static Set<String> fieldsExcludedFromGetterSetterChecks = new HashSet<String>(); static { fieldsExcludedFromGetterSetterChecks.add("previousSibling"); fieldsExcludedFromGetterSetterChecks.add("nextSibling"); fieldsExcludedFromGetterSetterChecks.add("plain"); fieldsExcludedFromGetterSetterChecks.add("class"); fieldsExcludedFromGetterSetterChecks.add("const"); fieldsExcludedFromGetterSetterChecks.add("unsigned"); fieldsExcludedFromGetterSetterChecks.add("gettersAndSetters"); fieldsExcludedFromGetterSetterChecks.add("modifiersStringPrefix"); fieldsExcludedFromGetterSetterChecks.add("id"); fieldsExcludedFromGetterSetterChecks.add("varArgs"); fieldsExcludedFromGetterSetterChecks.add("parents"); fieldsExcludedFromGetterSetterChecks.add("pathInFramework"); fieldsExcludedFromGetterSetterChecks.add("plainStorage"); } // @Test // public void checkDerivedClone() { // try { // Method m = type.getMethod("clone"); // assertEquals(m + " must have a return type of " + type.getSimpleName(), m.getReturnType(), type); // } catch (Exception e) { // assertTrue("Failed to get clone method (bad visibility ?)", false); // } // } @Test public void checkGettersAndSetters() { Element element = newElement(); GettersAndSettersHelper helper = element.getGettersAndSetters();//new GettersAndSettersHelper(type); for (Map.Entry<String, GetterAndSetterInfo> e : helper.gettersAndSetters.entrySet()) { GetterAndSetterInfo p = e.getValue(); String fieldName = e.getKey(); Type fieldType = helper.getFieldType(fieldName ); String className = element.getClass().getSimpleName(); assertNotNull("Field " + className + "." + fieldName + " does not have a getter !", p.getter); if (!fieldsExcludedFromGetterSetterChecks.contains(fieldName)) assertNotNull("Field " + className + "." + fieldName + " does not have a setter !", p.setter); if (p.setter == null) continue; helper.assertConsistentPair(p); // System.err.println("Testing field " + type.getSimpleName() + "." + fieldName);// + ": "); if (fieldType instanceof Class<?> && Element.class.isAssignableFrom((Class<?>) fieldType)) { testSetNewInstancesOf(fieldType, implementations, element, fieldName, p.setter, p.getter, null); //System.err.println(); } else if (fieldType instanceof ParameterizedType) { ParameterizedType paramType = (ParameterizedType) fieldType; Type rawType = paramType.getRawType(); if (rawType instanceof Class<?>) { Class<?> rawClass = (Class<?>) rawType; boolean isList = List.class.isAssignableFrom(rawClass); if (isList || Set.class.isAssignableFrom(rawClass)) { Collection<Element> container = isList ? new ArrayList<Element>() : new TreeSet<Element>(new Comparator<Element>() { public int compare(Element o1, Element o2) { return o1.toString().compareTo(o2.toString()); } }); Type[] typeArguments = paramType.getActualTypeArguments(); if (typeArguments.length == 1 && typeArguments[0] instanceof Class<?> && Element.class.isAssignableFrom((Class<?>) typeArguments[0])) { Class<?> elementType = (Class<?>) typeArguments[0]; testSetNewInstancesOf(elementType, implementations, element, fieldName, p.setter, p.getter, container); } } else { //assertTrue("Field '" + fieldName + "' of type " + fieldType + " cannot be tested !" , false); } } } else if (String.class.isAssignableFrom(as(fieldType, Class.class))) { Element instance = newElement(); BeansUtils.set(instance, fieldName, String.class, ""); Object v = BeansUtils.get(instance, fieldName); assertEquals("Testing " + fieldName + " :get(set(\"\"))", v, ""); BeansUtils.set(instance, fieldName, String.class, null); v = BeansUtils.get(instance, fieldName); assertEquals(v, null); } } } private void testSetNewInstancesOf(Type fieldType, Map<Class<?>, Set<Class<?>>> implementations2, Element element, String fieldName, Method setter, Method getter, Collection<Element> container) { Set<Class<?>> fieldTypeImplementations = implementations.get(fieldType); assertNotNull("Found no implementation of " + fieldType, fieldTypeImplementations); for (Class<?> fieldTypeImplClass : fieldTypeImplementations) { testSetNewInstanceOf(fieldTypeImplClass, element, fieldName, setter, getter, container); } } @SuppressWarnings("unchecked") private void testSetNewInstanceOf(Class<?> fieldTypeImplClass, Element element, String fieldName, Method setter, Method getter, Collection<Element> container) { Element arg = null, replace = null; try { arg = (Element) fieldTypeImplClass.newInstance(); replace = (Element) fieldTypeImplClass.newInstance(); } catch (Exception ex) { ex.printStackTrace(); assertFalse("Failed to create new " + fieldTypeImplClass.getName() + " : " + ex + "; cause : " + ex.getCause(), true); } // System.err.print("setter "); /// Setter Object valueToBeSet; if (container == null) { valueToBeSet = arg; } else { container.clear(); container.add(arg); valueToBeSet = container; } try { setter.invoke(element, valueToBeSet); if (!fieldName.equals("parentElement")) { assertNotNull("Parent element not set in " + setter, arg.getParentElement()); assertEquals("Parent element not set properly in " + setter, element, arg.getParentElement()); } } catch (Exception ex) { ex.printStackTrace(); assertFalse("Failed to call " + setter + " with a " + fieldTypeImplClass.getName() + " : " + ex + "; cause : " + ex.getCause(), true); } if (!fieldName.equals("parentElement")) { final Set<Integer> visitedIds = new TreeSet<Integer>(); element.accept(new Scanner() { @Override protected void visitElement(Element d) { if (d != null) visitedIds.add(d.getId()); super.visitElement(d); } }); assertTrue("Scanner did not visit child element " + fieldName + " with a " + fieldTypeImplClass.getName() , visitedIds.contains(arg.getId())); } if (getter != null) { // System.err.print("getter "); /// Getter try { Object got = getter.invoke(element); Element elementGot = container == null ? (Element)got : ((Collection<Element>)got).iterator().next(); assertEquals("Getter of " + fieldName + " does not return same object as setter in " + type.getName(), arg, elementGot); if (!fieldName.equals("parentElement")) { if (container != null) { container.clear(); container.add(replace); } arg.replaceBy(replace); Object got2 = getter.invoke(element); Element elementGot2 = container == null ? (Element)got2 : ((Collection<Element>)got2).iterator().next(); // if (arg != elementGot2) // arg =arg; // debug: break here assertFalse("Replacement of field " + fieldName + " in " + type.getName() + " failed", arg == elementGot2); assertEquals("Replacement of field " + fieldName + " in " + type.getName() + " failed", replace, elementGot2); assertNull("Parent element not removed in " + setter, elementGot.getParentElement()); assertNotNull("Parent element not set in " + setter, elementGot2.getParentElement()); assertEquals("Parent element not set properly in " + setter, element, elementGot2.getParentElement()); } } catch (Exception ex) { ex.printStackTrace(); assertFalse("Failed to call " + setter + " with a " + fieldTypeImplClass.getName() + " : " + ex + "; cause : " + ex.getCause(), true); } } } private Element newElement() { try { return type.newInstance(); } catch (Exception ex) { ex.printStackTrace(); assertFalse("Failed to create new " + type.getName() + " : " + ex + "; cause : " + ex.getCause(), true); return null; } } @Parameters public static List<Object[]> getClassesToTest() throws IOException { Set<Class<?>> elementClasses = new HashSet<Class<?>>(); for (Method method : Scanner.class.getMethods()) { for (Class<?> argType : method.getParameterTypes()) if (Element.class.isAssignableFrom(argType)) elementClasses.add(argType); Class<?> returnType = method.getReturnType(); if (Element.class.isAssignableFrom(returnType)) elementClasses.add(returnType); } Map<Class<?>, Set<Class<?>>> implementations = new LinkedHashMap<Class<?>, Set<Class<?>>>(); for (Class<?> c : elementClasses) { if ((c.getModifiers() & Modifier.ABSTRACT) != 0) continue; Class<?> s = c; do { Set<Class<?>> cs = implementations.get(s); if (cs == null) implementations.put(s, cs = new HashSet<Class<?>>()); cs.add(c); s = s.getSuperclass(); } while (s != null); } List<Object[]> ret = new ArrayList<Object[]>(); for (Class<?> c : elementClasses) { if ((c.getModifiers() & Modifier.ABSTRACT) != 0) continue; ret.add(new Object[] {c.getName(), c, implementations}); } return ret; } public static class TestSiblings { @Test public void addSibling() { VariablesDeclaration d = new VariablesDeclaration(); Declarator s1 = new Declarator.DirectDeclarator(), s2 = new Declarator.DirectDeclarator(); d.setDeclarators(Arrays.asList(s1)); s1.insertSibling(s2, false); List<Declarator> list = d.getDeclarators(); assertEquals("Failed to add after", 2, list.size()); assertSame("Added, but not after", s1, list.get(0)); assertSame(s2, list.get(1)); s2.replaceBy(null); list = d.getDeclarators(); assertEquals("Failed to remove added element", 1, list.size()); assertSame("Removed bad element", s1, list.get(0)); d.setDeclarators(Arrays.asList(s1)); s1.insertSibling(s2, true); list = d.getDeclarators(); assertEquals("Failed to add before", 2, list.size()); assertSame("Added, but not before", s2, list.get(0)); assertSame(s1, list.get(1)); s2.replaceBy(null); list = d.getDeclarators(); assertEquals("Failed to remove added element", 1, list.size()); assertSame("Removed bad element", s1, list.get(0)); } } }