/* * � Copyright IBM Corp. 2006, 2014 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. */ /* * Author: Maire Kehoe (mkehoe@ie.ibm.com) * Date: 1 Nov 2006 * RegisteredSerializationTest.java */ package com.ibm.xsp.test.framework.serialize; import java.io.NotSerializableException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Stack; import java.util.TimeZone; import javax.faces.application.StateManager; import javax.faces.component.UIComponent; import javax.faces.component.UIViewRoot; import javax.faces.context.FacesContext; import javax.faces.el.MethodBinding; import javax.faces.el.ValueBinding; import javax.faces.model.SelectItem; import com.ibm.commons.util.StringUtil; import com.ibm.xsp.application.ApplicationEx; import com.ibm.xsp.application.UniqueViewIdManager; import com.ibm.xsp.binding.ComponentBindingObject; import com.ibm.xsp.binding.PropertyMap; import com.ibm.xsp.page.translator.ReflectUtil; import com.ibm.xsp.registry.FacesComplexDefinition; import com.ibm.xsp.registry.FacesComponentDefinition; import com.ibm.xsp.registry.FacesContainerProperty; import com.ibm.xsp.registry.FacesDefinition; import com.ibm.xsp.registry.FacesProperty; import com.ibm.xsp.registry.FacesPropertyType; import com.ibm.xsp.registry.FacesSharableRegistry; import com.ibm.xsp.registry.FacesSimpleProperty; import com.ibm.xsp.registry.RegistryUtil; import com.ibm.xsp.registry.types.FacesSimpleTypes; import com.ibm.xsp.test.framework.AbstractXspTest; import com.ibm.xsp.test.framework.ConfigUtil; import com.ibm.xsp.test.framework.TestProject; import com.ibm.xsp.test.framework.XspTestUtil; import com.ibm.xsp.test.framework.registry.XspRegistryTestUtil; import com.ibm.xsp.test.framework.setup.SkipFileContent; import com.ibm.xsp.util.TypedUtil; import com.sun.faces.context.FacesContextImpl; import com.sun.faces.el.ValueBindingImpl; /** * @author Maire Kehoe (mkehoe@ie.ibm.com) * 1 Nov 2006 * * Unit: RegisteredSerializationTest.java */ public class RegisteredSerializationTest extends AbstractXspTest { /** * */ private static final ComplexInfo PROPERTY_TYPE_INFO = new ComplexInfo(PropertyMap.class, new PropertyMap(), false); @Override public String getDescription() { return "creates and serializes tags in the registry."; } private Object[][] nonTagsToTest; // non-tags within the current library to test. private Object[][] allSkips; protected Object getInstanceFromOtherTests(Class<?> defClass) { // skip testing these here because checked in other test // [Just need the instance, to test setting them on stuff.] return null; } private String targetLibrary; public void testRegisteredObjectsSerialization() throws Exception { TestProject.createRegistry(this); FacesSharableRegistry registry = TestProject.getRegistry(this); nonTagsToTest = getNonTagsToTest(); allSkips = getSkipProperty(registry); targetLibrary = ConfigUtil.getTargetLibrary(this); FacesContext restoreContext = TestProject.createFacesContext(this); FacesContext context = TestProject.createFacesContext(this); UIViewRoot root = TestProject.loadEmptyPage(this, context); // make a list of all complex-types List<ComplexInfo> complexes = findAllComplexesInRegistry(registry); List<ComplexInfo> defaultComplexes = addDefaultComplexes(new ArrayList<ComplexInfo>()); complexes.addAll(defaultComplexes); // found all complex-types List<UIComponent> children = TypedUtil.getChildren(root); removeChildren(children); Serializer serializer = createSerializer(registry); serializer.init(TestProject.getApplication(this), root, context, restoreContext); initSerializer(serializer); setCurrentContext(context); String fails = ""; List<ComponentInfo> allComponentInfos = createComponentInfos(registry.findComponentDefs()); // find all locations where defs in the entire registry // can be added to the control tree to test serializations assignTestStacks(allComponentInfos, complexes); // for the defs in the current library List<FacesDefinition> defsToCheck = TestProject.getLibCompComplexDefs(registry, this); List<FacesDefinition> extraDefs = new ArrayList<FacesDefinition>(); for (Class<?> extraDefJavaClass : getExtraDefsToTest()) { FacesDefinition def; if( UIComponent.class.isAssignableFrom(extraDefJavaClass) ){ // control def = XspRegistryTestUtil.getFirstComponentDefinition(registry, extraDefJavaClass); }else{ // complex-type def = RegistryUtil.getFirstComplexDefinition(registry, extraDefJavaClass); } if( null == def ){ fails += extraDefJavaClass.getName() + " Definition not found for class in getExtraDefsToTest()\n"; continue; } if( defsToCheck.contains(def) ){ fails += extraDefJavaClass.getName() + " Definition in getExtraDefsToTest() already present in library\n"; continue; } extraDefs.add(def); } defsToCheck.addAll(extraDefs); for (FacesDefinition def : defsToCheck) { if( ! def.isTag() && !isNonTagToTest(def.getJavaClass()) && !extraDefs.contains(def) ){ // ignore abstract defs, except for those explicitly listed to test continue; } InitializationStack stackToTest; FacesComponentDefinition controlDefToCreate; ComplexInfo targetComplexInfo = null; if( def instanceof FacesComponentDefinition ){ FacesComponentDefinition comp = (FacesComponentDefinition) def; ComponentInfo info = findMatch(allComponentInfos, comp); stackToTest = new InitializationStack(info); controlDefToCreate = comp; System.out.println("RegisteredSerializationTest Checking " +XspTestUtil.loc(def)+" in control tree."); }else{ // complex FacesComplexDefinition complexDef = (FacesComplexDefinition) def; ComplexInfo complexInfo = findMatch(complexes, complexDef); targetComplexInfo = complexInfo; stackToTest = complexInfo.locationToTestStack; if( null == stackToTest ){ fails += XspTestUtil.loc(def)+ " Untested, cannot find setter in any control or complex-type.\n"; continue; } System.out.println("RegisteredSerializationTest Checking " +XspTestUtil.loc(def)+" in control tree at "+stackToTest.toStackString()); controlDefToCreate = stackToTest.findRoot().definition; } UIComponent controlInstance; try{ controlInstance = (UIComponent) controlDefToCreate.getJavaClass().newInstance(); }catch(Exception ex){ fails += XspTestUtil.loc(def)+ " Untested, problem creating instance of control " +controlDefToCreate.getJavaClass()+"\n"; ex.printStackTrace(); continue; } if( def == controlDefToCreate ){ // testing this control, rather than a complex-type // invoke every setter on this control. fails += callControlSetters(controlDefToCreate, controlInstance, complexes, defaultComplexes); }else{ // testing some complex-type, use the stack to create the controls up to that element Object containerObj = controlInstance; FacesDefinition containerDef = controlDefToCreate; FacesProperty propertyObj; InitializationStackElement controlStackElement = stackToTest.stack.get(0); propertyObj = controlStackElement.propertyOfObject; boolean problemConstructingStack = false; int count = stackToTest.stack.size(); for (int i = 1/*skip 0*/; i < count; i++) { InitializationStackElement item = stackToTest.stack.get(i); ComplexInfo nestedComplexInfo = item.objectComplex; boolean isTargetComplexInfo = nestedComplexInfo == targetComplexInfo; Object nestedComplexInstance; try{ nestedComplexInstance = nestedComplexInfo.definition.getJavaClass().newInstance(); }catch(Exception ex){ ex.printStackTrace(); if( isTargetComplexInfo ){ fails += XspTestUtil.loc(def) + " Problem creating an instance of the complex-type " +controlDefToCreate.getJavaClass()+"\n"; }else{ fails += XspTestUtil.loc(def) + " Untested, problem creating instance of complex " +controlDefToCreate.getJavaClass()+"\n"; } problemConstructingStack = true; break; } String setterFails = callSingleSetter(def, containerDef, containerObj, propertyObj, nestedComplexInstance); if( setterFails.length() > 0){ fails += XspTestUtil.loc(def)+ " "+setterFails; problemConstructingStack = true; break; } containerObj = nestedComplexInstance; containerDef = nestedComplexInfo.definition; propertyObj = item.propertyOfObject; } if( problemConstructingStack ){ // then can't test this complex-type continue; } if( ! containerDef.equals(targetComplexInfo.definition) ){ throw new RuntimeException("trace create didn't create target complex-type"); } // targetting this complex-type,invoke every setter fails += callComplexSetters( (FacesComplexDefinition) containerDef, containerObj, controlInstance, complexes, defaultComplexes); } // next serialize the control. if( null != controlInstance ){ // serialize and deserialize each component children.add(controlInstance); try{ UIViewRoot restored = serializer.saveAndRestore(); String equalFails = checkEquals("",root, restored); if( equalFails.length() > 0 ){ fails += XspTestUtil.loc(def)+ " "+ equalFails; // already ends in \n } }catch(Throwable e){ fails += XspTestUtil.loc(def)+ " "+ logSerializeProblem(controlInstance, e)+"\n"; } try{ children.remove(controlInstance); }catch(Exception e){ // some control has overridden setParent and it has a bug. if( children.size() != 0 ){ e.printStackTrace(); // it failed before the calling the superclass setParent, so // can't recover this JUnit test run. throw e; } fails += XspTestUtil.loc(def)+ " "+ logRemoveControlProblem(controlInstance, e)+"\n"; } } // end if( null != component ){ } fails += serializer.getUnusedSkipsFails(); Object[][] skippedAllowNoComplex = allSkips; for (Object[] skip : skippedAllowNoComplex) { if( skip.length < 5 || ! Boolean.TRUE.equals(skip[4]) ){ // skip not used fails+="unused allowNoComplex skip: " + XspTestUtil.getShortClass((Class<?>) skip[0]) + "." + skip[1] + "(" + XspTestUtil.getShortClass((Class<?>) skip[2])+")\n"; } } for (Object[] nonTag : nonTagsToTest) { if( nonTag.length == 1 ){ fails+="non-tag not found in library: "+nonTag[0]+"\n"; } } fails = XspTestUtil.removeMultilineFailSkips(fails, SkipFileContent.concatSkips(getSkips(), this, "testRegisteredObjectsSerialization")); if( fails.length() > 0 ){ fail( XspTestUtil.getMultilineFailMessage(fails.toString()) ); } } protected String[] getSkips(){ return StringUtil.EMPTY_STRING_ARRAY; } private String callSingleSetter(FacesDefinition defUnderTest, FacesDefinition containerDef, Object containerObj, FacesProperty prop, Object nestedComplexInstance) { String fails = ""; int skipIndex; if( -1 != (skipIndex = findSkipIndex(containerDef, prop)) ){ Object substitute = getSkipSubstitute(skipIndex); if( null != substitute ){ TypedUtil.getAttributes((UIComponent)containerObj).put(prop.getName(), substitute); } return fails; } String addMethod = null; if( prop instanceof FacesContainerProperty ){ FacesContainerProperty container = (FacesContainerProperty) prop; addMethod = container.getCollectionAddMethod(); prop = container.getItemProperty(); if( null == addMethod ){ fails += XspTestUtil.loc(defUnderTest) +" " + "Cannot set container property " + XspTestUtil.getShortClass(containerDef.getJavaClass()) + "\""+prop.getName()+"\"(" + XspTestUtil.getShortClass(prop.getJavaClass())+")\n"; return fails; } } if( null == addMethod ){ try{ if( prop.isAttribute() ){ // themeId, disableTheme are attributes, i.e. no set method. TypedUtil.getAttributes((UIComponent)containerObj).put(prop.getName(), nestedComplexInstance); }else{ fails += callSetter(containerObj, containerDef, prop, nestedComplexInstance); } }catch(Exception ex){ ex.printStackTrace(); String message = XspTestUtil.loc(defUnderTest) + " Cannot set property " + XspTestUtil.getShortClass(containerDef.getJavaClass()) + "\"" + prop.getName() + "\"("+ XspTestUtil.getShortClass(prop.getJavaClass()) + ") " + XspTestUtil.getShortClass(ex) + "\n"; fails += message; } }else{ fails+=callAddMethod(addMethod, containerObj, prop.getJavaClass(), nestedComplexInstance); } return fails; } private ComplexInfo findMatch(List<ComplexInfo> allComplexInfos, FacesComplexDefinition def) { for (ComplexInfo complexInfo : allComplexInfos) { if( complexInfo.definition == def ){ return complexInfo; } } return null; } private ComponentInfo findMatch(List<ComponentInfo> allComponentInfos, FacesComponentDefinition def) { for (ComponentInfo componentInfo : allComponentInfos) { if( componentInfo.definition == def ){ return componentInfo; } } return null; } private void assignTestStacks(List<ComponentInfo> allComponentInfos, List<ComplexInfo> complexes) { InitializationStack cursorStack = new InitializationStack(); for (ComponentInfo controlInfo : allComponentInfos) { if( ! ReflectUtil.isClassInstantiable(controlInfo.definition.getJavaClass()) ){ // cannot set a complex-type onto a non-instantiable definition. continue; } cursorStack.pushRoot(controlInfo); try{ FacesDefinition def = controlInfo.definition; assignStacksForDefProps(complexes, def, cursorStack); }finally{ cursorStack.popRoot(controlInfo); } } } private void assignStacksForDefProps(List<ComplexInfo> complexes, FacesDefinition def, InitializationStack cursorStack) { for (String propertyName : def.getPropertyNames()) { FacesProperty prop = def.getProperty(propertyName); FacesProperty possibleContainerProp = prop; if( prop instanceof FacesContainerProperty ){ prop = ((FacesContainerProperty)prop).getItemProperty(); } if( prop instanceof FacesSimpleProperty && FacesSimpleTypes.isPrimitive( ((FacesSimpleProperty)prop).getType()) ){ // String, int, etc. continue; } ComplexInfo firstComplex = getComplexForClass(complexes, prop.getJavaClass()); if( null == firstComplex ){ // no matching complex-type continue; } if( null != firstComplex.locationToTestStack ){ // already found stacks under that complex-type continue; } cursorStack.pushProperty(possibleContainerProp); try{ List<ComplexInfo> complexesForProp = getComplexesForClass(complexes, prop.getJavaClass()); for (ComplexInfo inner : complexesForProp) { InitializationStack innerStack = cursorStack.copyState(); innerStack.pushObject(inner); inner.locationToTestStack = innerStack; } for (ComplexInfo inner : complexesForProp) { FacesDefinition innerDef = inner.definition; if( null == innerDef ){ // one of the default instances, like java.util.Date continue; } cursorStack.pushObject(inner); try{ assignStacksForDefProps(complexes, innerDef, cursorStack); }finally{ cursorStack.popObject(inner); } } }finally{ cursorStack.popProperty(possibleContainerProp); } } } private List<ComplexInfo> findAllComplexesInRegistry(FacesSharableRegistry registry) throws Exception { List<ComplexInfo> complexes = new ArrayList<ComplexInfo>(); List<FacesComplexDefinition> complexDefs = registry.findComplexDefs(); //Collections.reverse(complexDefs); // test ..xsp.core complexes first for (FacesComplexDefinition def : complexDefs) { if( registry.isLocalDef(def) ){ continue; } if( ! def.isTag() ){ continue; } ComplexInfo complex = createComplexInstance(def); if( null != complex ){ complexes.add(complex); } } return complexes; } protected void initSerializer(Serializer serializer) { // Available to override in subclasses } private static void setCurrentContext(final FacesContext context){ new FacesContextImpl(){ { setCurrentInstance(context); } }.getClass(); // getClass() to prevent compile warning: The allocated object is never used } protected int getDebugIndex(){ return -1; } private List<ComponentInfo> createComponentInfos( List<FacesComponentDefinition> defs) { List<ComponentInfo> infos = new ArrayList<ComponentInfo>(); for (FacesComponentDefinition comp : defs) { infos.add(new ComponentInfo(comp, isInTargetLibrary(comp))); } return infos; } /** * Format is * {definitionClass, used(generated)} * @return */ protected Object[][] getNonTagsToTest() { return XspTestUtil.EMPTY_OBJECT_ARRAY_ARRAY; } /** * Definitions from other libraries (that are in the registry), to be tested. * @return */ protected Class<?>[] getExtraDefsToTest(){ return new Class<?>[0]; } /** * Available to be overridden in the subclass. * @param reg * @return */ protected Serializer createSerializer(FacesSharableRegistry reg) { return new NonCompareSerializer(); } public static interface Serializer{ public void init(ApplicationEx application, UIViewRoot root, FacesContext createContext, FacesContext restoreContext); public UIViewRoot saveAndRestore(); public String getUnusedSkipsFails(); } public static class NonCompareSerializer implements Serializer { protected StateManager stateManager; protected FacesContext createContext; protected FacesContext restoreContext; protected ApplicationEx application; protected UIViewRoot root; public void init(ApplicationEx application, UIViewRoot beforeRoot, FacesContext createContext, FacesContext restoreContext) { this.stateManager = new StateManagerTestImpl(); this.createContext = createContext; this.restoreContext = restoreContext; this.application = application; this.root = beforeRoot; } public String getUnusedSkipsFails(){ return ""; } public UIViewRoot saveAndRestore() { String renderKitId = application.getViewHandler().calculateRenderKitId(createContext); createContext.setViewRoot(root); UIViewRoot root = createContext.getViewRoot(); UniqueViewIdManager.setUniqueViewId(root,null); String viewId = root.getViewId(); stateManager.saveSerializedView(createContext); createContext.setViewRoot(null); //will be null on restore setCurrentContext(restoreContext); UIViewRoot restoredView = stateManager.restoreView(restoreContext, viewId, renderKitId); createContext.setViewRoot(root); restoreContext.setViewRoot(restoredView); return restoredView; } } private String logRemoveControlProblem( UIComponent component, Exception e) { String message = XspTestUtil.getShortClass(e) + " removing a " + XspTestUtil.getShortClass(component) +" from the control tree"; // if( e instanceof NotSerializableException){ message += ": " + e.getMessage(); // } System.err.println(RegisteredSerializationTest.class.getName() + ".testRegisteredObjectsSerialization() " + "fail : " + message); e.printStackTrace(); return message; } private String logSerializeProblem( UIComponent component, Throwable e) { if( e.getCause() instanceof NotSerializableException || e instanceof InvocationTargetException){ e = e.getCause(); } String message = XspTestUtil.getShortClass(e) + " serializing a " + XspTestUtil.getShortClass(component); // if( e instanceof NotSerializableException){ message += ": " + e.getMessage(); // } System.err.println(RegisteredSerializationTest.class.getName() + ".testRegisteredObjectsSerialization() " + "fail : " + message); e.printStackTrace(); return message; } public String checkEquals(String message, Object expected, Object actual) throws Exception { if( null == expected || null == actual ){ return checkEqualMsg(message, expected, actual); } if( ! StringUtil.equals(expected.getClass(), actual.getClass()) ){ return checkEqualMsg(message, expected.getClass(), actual.getClass()); } // if is defined in the registry boolean isComponentExpected = expected instanceof UIComponent; FacesDefinition def; FacesSharableRegistry reg = TestProject.getRegistry(this); if( isComponentExpected ){ def = XspRegistryTestUtil.getFirstComponentDefinition( reg, expected.getClass()); }else{ def = RegistryUtil.getFirstComplexDefinition(reg, expected.getClass()); } if( isComponentExpected && null == def ){ return message + " Control class not known to registry: "+XspTestUtil.getShortClass(expected)+"\n"; } if( null != def ){ String defDescr = XspTestUtil.getShortClass(expected); // if( StringUtil.isNotEmpty(message) && !isComponentExpected ){ // defDescr = message + "(" + defDescr+ ")"; // } String fails = ""; for (FacesProperty prop : RegistryUtil.getProperties(def)) { String propDescr = defDescr+"."+prop.getName(); if( prop.getName().equals("loaded")){ // loaded attribute gets ignored. continue; } if( isComponentExpected && prop.isAttribute() ){ Object expectedProp = ((UIComponent)expected).getAttributes().get(prop.getName()); Object actualProp = ((UIComponent)actual).getAttributes().get(prop.getName()); fails += checkEquals(propDescr, expectedProp, actualProp); continue; } if( isSkipped(getSkippedNoGetter(), def, prop) ){ continue; } Method mtd = getGetMethod(expected, prop); Object expectedProp = mtd.invoke(expected, (Object[])null); Object actualProp = mtd.invoke(actual, (Object[])null); fails += checkEquals(propDescr, expectedProp, actualProp); } if( isComponentExpected){ UIComponent eComp = (UIComponent) expected; UIComponent aComp = (UIComponent) actual; fails += checkEquals("", eComp.getChildren(), aComp.getChildren()); fails += checkEquals(defDescr+".facets", eComp.getFacets(), aComp.getFacets()); } return fails; } if (expected instanceof Collection) { Collection<?> expectedCol = (Collection<?>) expected; Collection<?> actualCol = (Collection<?>) actual; assertEquals(expectedCol.size(), actualCol.size()); String fails = ""; Iterator<?> eIter = expectedCol.iterator(); Iterator<?> aIter = actualCol.iterator(); int i = 0; while (eIter.hasNext()) { Object eItem = eIter.next(); Object aItem = aIter.next(); String colMsg = StringUtil.isEmpty(message)? "" : message + "[" + i + "]"; fails += checkEquals(colMsg, eItem, aItem); i++; } return fails; } if (expected instanceof Map) { Map<?,?> eMap = (Map<?,?>) expected; Map<?,?> aMap = (Map<?,?>) actual; assertEquals(message+".size",eMap.size(), aMap.size()); String fails = ""; for (Map.Entry<?,?> pair : eMap.entrySet()) { Object eKey = pair.getKey(); Object eItem = pair.getValue(); Object aItem = aMap.get(eKey); fails += checkEquals(message + "[" + eKey+ "]", eItem, aItem); } return fails; } if( expected instanceof Object[]){ Object[] eArr = (Object[]) expected; Object[] aArr = (Object[]) actual; assertEquals(message+".length", eArr.length, aArr.length); String fails = ""; for (int i = 0; i < eArr.length; i++) { Object eItem = eArr[i]; Object aItem = aArr[i]; fails += checkEquals(message + "["+i+"]", eItem, aItem); } return fails; } if( expected instanceof ValueBindingImpl){ ValueBindingImpl eVB = (ValueBindingImpl) expected; ValueBindingImpl aVB = (ValueBindingImpl) expected; return checkEquals(message + ".expressionString", eVB .getExpressionString(), aVB.getExpressionString()); } if( expected instanceof SelectItem ){ SelectItem e = (SelectItem) expected; SelectItem a = (SelectItem) actual; String fails = ""; fails += checkEquals(message+".label", e.getLabel(), a.getLabel()); fails += checkEquals(message+".value", e.getValue(), a.getValue()); fails += checkEquals(message+".description", e.getDescription(), a.getDescription()); return fails; } if( expected instanceof MethodBinding){ MethodBinding eBinding = (MethodBinding) expected; MethodBinding aBinding = (MethodBinding) expected; return checkEquals(message + ".expressionString", eBinding .getExpressionString(), aBinding.getExpressionString()); } return checkEqualMsg(message, expected, actual); } private String checkEqualMsg(String message, Object expected, Object actual) { if( ! StringUtil.equals(expected, actual) ){ return message + " expected: <" + expected + "> but was: <"+ actual + ">\n"; } return ""; } protected Object[][] getSkippedNoGetter() { return XspTestUtil.EMPTY_OBJECT_ARRAY_ARRAY; } private boolean isSkipped(Object[][] skipped, FacesDefinition def, FacesProperty prop) { for (int i = 0; i < skipped.length; i++) { Object[] skip = skipped[i]; Class<?> clazz = (Class<?>) skip[0]; if( ! clazz.equals(def.getJavaClass())){ continue; } String propName = (String) skip[1]; if( propName.equals(prop.getName()) ){ return true; } } return false; } private Method getGetMethod(Object expected, FacesProperty prop) throws NoSuchMethodException { String methodName = boolean.class.equals(prop.getJavaClass())? "is":"get"; methodName += Character.toUpperCase(prop.getName().charAt(0)); methodName += prop.getName().substring(1); Class<? extends Object> expectedClass = expected.getClass(); try{ Method mtd = expectedClass.getMethod(methodName, (Class[])null); return mtd; }catch(NoSuchMethodException e){ throw new RuntimeException("No such method " + XspTestUtil.getShortClass(expectedClass) + "." + methodName + "() in " + expectedClass.getName(), e); } } private String callControlSetters(FacesComponentDefinition def, UIComponent component, List<ComplexInfo> complexes, List<ComplexInfo> defaultComplexes) throws Exception{ String fails = ""; for (FacesProperty prop : RegistryUtil.getProperties(def)) { String propName = prop.getName(); int skipIndex; if( -1 != (skipIndex = findSkipIndex(def, prop)) ){ Object substitute = getSkipSubstitute(skipIndex); if( null != substitute ){ TypedUtil.getAttributes(component).put(propName, substitute); } continue; // skip this property } String addMethod = null; if( prop instanceof FacesContainerProperty ){ FacesContainerProperty container = (FacesContainerProperty) prop; addMethod = container.getCollectionAddMethod(); prop = container.getItemProperty(); if( null == addMethod ){ fails += XspTestUtil.loc(def) +" \"" +propName+"\" " +"Cannot test the container property \""+propName+"\" : " +XspTestUtil.getShortClass(prop.getJavaClass())+"\n"; continue; } } Class<?> propClass = prop.getJavaClass(); ComplexInfo defaultComplexForClass = getComplexForClass(defaultComplexes, propClass); if( null != defaultComplexForClass ){ if( null == addMethod ){ fails += callSetInAttributes(component, propName, defaultComplexForClass, def, prop); }else{ fails+=callAddMethod(addMethod, component, prop.getJavaClass(), defaultComplexForClass.getInstance()); } }else if( prop instanceof FacesPropertyType ){ ComplexInfo complex = PROPERTY_TYPE_INFO; if( null == addMethod ){ fails += callSetInAttributes(component, propName, complex, def, prop); }else{ fails+=callAddMethod(addMethod, component, prop.getJavaClass(), complex.getInstance()); } } else{ // complex-type ComplexInfo complexForProp = getTagComplexForClass(complexes, propClass); if( null == complexForProp ){ fails += XspTestUtil.loc(def) + " \"" + propName + "\" " +"No complex tag found to test setter with class " + prop.getJavaClass() + "\n"; }else{ // found Object complexInstance; try{ complexInstance = complexForProp.javaClass.newInstance(); }catch(Exception ex){ ex.printStackTrace(); String message = XspTestUtil.loc(def) + " \"" + propName + "\" " + XspTestUtil.getShortClass(ex) + " creating a " + complexForProp.javaClass+" to test setter\n"; fails += message; continue; } if( complexInstance instanceof ComponentBindingObject ){ ((ComponentBindingObject)complexInstance).setComponent(component); } if( null == addMethod ){ try{ if( prop.isAttribute() ){ // themeId, disableTheme are attributes, i.e. no set method. TypedUtil.getAttributes(component).put(propName, complexInstance); }else{ fails += callSetter(component, def, prop, complexInstance); } }catch(Exception ex){ ex.printStackTrace(); String message = XspTestUtil.loc(def) + " \"" + propName + "\" " + XspTestUtil.getShortClass(ex) + " calling setter with a " + complexInstance.getClass()+"\n"; fails += message; } }else{ StringBuilder failsBuf = new StringBuilder(); fails+=callAddMethod(addMethod, component, prop.getJavaClass(), complexInstance); fails += failsBuf; } } // end else complex-type found } // end else need complex-type } return fails; } private String callComplexSetters(FacesComplexDefinition def, Object complexInstance, UIComponent component, List<ComplexInfo> complexes, List<ComplexInfo> defaultComplexes) throws Exception{ String fails = ""; for (FacesProperty prop : RegistryUtil.getProperties(def)) { String propName = prop.getName(); if( "loaded".equals(prop.getName()) ){ continue; } String addMethod = null; if( prop instanceof FacesContainerProperty ){ FacesContainerProperty container = (FacesContainerProperty) prop; addMethod = container.getCollectionAddMethod(); prop = container.getItemProperty(); if( null == addMethod ){ fails += XspTestUtil.loc(def) +" \"" +propName+"\" " +"Cannot test the container property \""+propName+"\" : " +XspTestUtil.getShortClass(prop.getJavaClass())+"\n"; continue; } } int skipIndex; if( -1 != (skipIndex = findSkipIndex(def, prop)) ){ Object substitute = getSkipSubstitute(skipIndex); if( null != substitute ){ fails += callSetter(complexInstance, def, prop, substitute); } continue; } Class<?> propClass = prop.getJavaClass(); ComplexInfo complexForProp = getTagComplexForClass(complexes, propClass); if( null == complexForProp ){ fails += XspTestUtil.loc(def) + " \"" + propName + "\" " +"No complex tag found to test setter with class " + prop.getJavaClass() + "\n"; }else{ // found Object innerComplexInstance; try{ if( null == complexForProp.definition ){ // e.g. java.lang.String def. innerComplexInstance = complexForProp.getInstance(); }else{ innerComplexInstance = complexForProp.javaClass.newInstance(); } }catch(Exception ex){ ex.printStackTrace(); String message = XspTestUtil.loc(def) + " \"" + propName + "\" " + XspTestUtil.getShortClass(ex) + " creating a " + complexForProp.javaClass+" to test setter\n"; fails += message; continue; } if( innerComplexInstance instanceof ComponentBindingObject ){ ((ComponentBindingObject)innerComplexInstance).setComponent(component); } if( null == addMethod ){ fails += callSetter(complexInstance, def, prop, innerComplexInstance); }else{ StringBuilder failsBuf = new StringBuilder(); fails+=callAddMethod(addMethod, complexInstance, prop.getJavaClass(), innerComplexInstance); fails += failsBuf; } } } return fails; } private class InitializationStack{ Stack<InitializationStackElement> stack = new Stack<InitializationStackElement>(); public InitializationStack() { super(); } public InitializationStack(ComponentInfo controlInfo) { super(); pushRoot(controlInfo); } public ComponentInfo findRoot(){ if( ! stack.get(0).isObjectControl ){ throw new IllegalStateException(); } return stack.get(0).objectControl; } public void pushRoot(ComponentInfo control){ InitializationStackElement item = new InitializationStackElement(control); stack.push(item); } public void pushObject(ComplexInfo complex){ InitializationStackElement item = new InitializationStackElement(complex); stack.push(item); } public void pushProperty(FacesProperty prop){ InitializationStackElement item = stack.peek(); if( null != item.propertyOfObject ){ throw new RuntimeException(); } item.propertyOfObject = prop; } public void popProperty(FacesProperty prop){ InitializationStackElement item = stack.peek(); if( prop != item.propertyOfObject ){ throw new RuntimeException(); } item.propertyOfObject = null; } public void popObject(ComplexInfo complex){ InitializationStackElement item = stack.peek(); if( null != item.propertyOfObject ){ throw new RuntimeException(); } if( item.isObjectControl || complex != item.objectComplex ){ throw new RuntimeException(); } stack.pop(); } public void popRoot(ComponentInfo control){ InitializationStackElement item = stack.peek(); if( null != item.propertyOfObject ){ throw new RuntimeException(); } if( !item.isObjectControl || control != item.objectControl ){ throw new RuntimeException(); } stack.pop(); } public InitializationStack copyState(){ InitializationStack copy = new InitializationStack(); copy.stack = new Stack<InitializationStackElement>(); for (InitializationStackElement item : this.stack) { InitializationStackElement itemCopy = new InitializationStackElement(); itemCopy.isObjectControl = item.isObjectControl; itemCopy.objectControl = item.objectControl; itemCopy.objectComplex = item.objectComplex; itemCopy.propertyOfObject = item.propertyOfObject; copy.stack.push(itemCopy); } return copy; } public String toStackString() { StringBuilder b = new StringBuilder(); for (InitializationStackElement item : this.stack) { FacesDefinition def = item.getObjectDef(); b.append("(").append(XspTestUtil.getShortClass(def.getJavaClass())).append(")"); if( null != item.propertyOfObject ){ b.append('\"').append(item.propertyOfObject.getName()).append('\"'); } } return b.toString(); } @Override public String toString() { return XspTestUtil.getShortClass(this)+"@"+hashCode()+"_"+toStackString(); } } private class InitializationStackElement{ boolean isObjectControl; ComplexInfo objectComplex; ComponentInfo objectControl; // optional, will not be present for the first item in the stack. FacesProperty propertyOfObject; public InitializationStackElement() { super(); } public InitializationStackElement(ComplexInfo object) { super(); this.isObjectControl = false; this.objectComplex = object; } public InitializationStackElement(ComponentInfo object) { super(); this.isObjectControl = true; this.objectControl = object; } public FacesDefinition getObjectDef(){ if( isObjectControl ){ return objectControl.definition; } return objectComplex.definition; } } private String callSetter(Object instance, FacesDefinition definition, FacesProperty prop, Object value) throws Exception{ String fails = ""; String methodName = prop.getName(); methodName = Character.toUpperCase(methodName.charAt(0))+methodName.substring(1); methodName = "set"+methodName; try{ Method method = definition.getJavaClass().getMethod(methodName, new Class[]{prop.getJavaClass()}); try{ method.invoke(instance, new Object[]{value}); }catch(InvocationTargetException ex){ throw (Exception) ex.getTargetException(); } }catch(Exception ex){ String message = XspTestUtil.getShortClass(ex) + " invoking " + XspTestUtil.getShortClass(instance) + "." + methodName + "(" + XspTestUtil.getAfterLastDot(prop.getJavaClass().getName()) + ") setting the complex to a " + value.getClass().getName(); fails += message+"\n"; ex.printStackTrace(); } return fails; } private String callSetInAttributes(UIComponent component, String propName, ComplexInfo complex, FacesComponentDefinition def, FacesProperty prop) throws Exception { String fails = ""; if( "loaded".equals(propName) ){ // do not set loaded on runtime UIComponent object, // it is only used by the AbstractCompiledPage. return fails; } try{ if( prop.isAttribute() ){ // themeId, disableTheme are attributes, i.e. no set method. TypedUtil.getAttributes(component).put(propName, complex.getInstance()); }else{ fails += callSetter(component, def, prop, complex.getInstance()); } }catch(Exception ex){ String message = XspTestUtil.getShortClass(ex) + " setting a complex for " + XspTestUtil.getShortClass(component) + "." + propName + " to " + complex.javaClass; fails += message+"\n"; ex.printStackTrace(); } return fails; } private String callAddMethod(String addMethod, Object component, Class<?> propClass, Object instance){ String fails = ""; try{ Method method = component.getClass().getMethod(addMethod, new Class[]{propClass}); try{ method.invoke(component, new Object[]{instance}); }catch(InvocationTargetException ex){ throw (Exception) ex.getTargetException(); } }catch(Exception ex){ String message = XspTestUtil.getShortClass(ex) + " invoking "+ XspTestUtil.getShortClass(component) + "." +addMethod+ "(" +XspTestUtil.getAfterLastDot(propClass.getName())+ ") setting the complex " + instance.getClass().getName(); fails += message+"\n"; ex.printStackTrace(); } return fails; } /** * @param complexes */ private List<ComplexInfo> addDefaultComplexes(List<ComplexInfo> complexes) { complexes.add(new ComplexInfo(String.class, "testString", false)); complexes.add(new ComplexInfo(Object.class, "testObject", false)); complexes.add(new ComplexInfo(int.class, 3, false)); complexes.add(new ComplexInfo(boolean.class, Boolean.TRUE, false)); complexes.add(new ComplexInfo(long.class, 3L, false)); complexes.add(new ComplexInfo(double.class, 3.5, false)); complexes.add(new ComplexInfo(Locale.class, new Locale("de"), false)); complexes.add(new ComplexInfo(TimeZone.class, TimeZone.getTimeZone("GMT"), false)); complexes.add(new ComplexInfo(Date.class, new Date(), false)); ValueBinding binding = //new MultiPartValueBinding(null, "constantVB"); // TestProject.getApplication(this).createValueBinding("#{'testVB'}"); complexes.add(new ComplexInfo(ValueBinding.class, binding, false)); return complexes; } private ComplexInfo createComplexInstance(FacesComplexDefinition def) throws Exception{ Class<?> defClass = def.getJavaClass(); Object instance = getInstanceFromOtherTests(defClass); if( null != instance ){ return new ComplexInfo(defClass, instance, false); } ComplexInfo info = new ComplexInfo(def, isInTargetLibrary(def)); return info; } private boolean isInTargetLibrary(FacesDefinition def) { if( null == targetLibrary ){ // if no library specified, test all. return true; } return targetLibrary.equals(RegistryUtil.getProject(def).getId()); } protected static class ComponentInfo{ public final FacesComponentDefinition definition; // the original ComponentInfo, that this is a duplicate of. public final ComponentInfo originalComponent; public final String toTest; public final boolean inTargetLibrary; public boolean isInited; private UIComponent instance; public ComponentInfo(FacesComponentDefinition definition, boolean inTargetLibrary) { super(); this.definition = definition; this.originalComponent = null; this.toTest = null; this.inTargetLibrary = inTargetLibrary; } private ComponentInfo(ComponentInfo originalComponent, String toTest) { super(); this.definition = originalComponent.definition; this.originalComponent = originalComponent; this.toTest = toTest; this.inTargetLibrary = originalComponent.inTargetLibrary; } public ComponentInfo duplicate(String reason){ return new ComponentInfo(this, reason); } public boolean isInited(){ return isInited; } public Object getInstance() throws Exception{ if( null == instance ){ throw new NullPointerException(); } return instance; } public void initialize(UIComponent instance) throws Exception{ this.instance = instance; isInited = true; } } protected static class ComplexInfo{ // These few fields represents the meta-data info of the complex-type public boolean inTargetLibrary; public Class<?> javaClass; public FacesComplexDefinition definition; // These fields correspond to an actual created complex-type instance public boolean isInited; public String initedBy; private Object instance; // This field is when the complex-type instance has not been created // but a component property has been found where the complex-type // can be tested, so if necessary, another instance of the component // can be created to test this complex-type. public ComponentInfo locationToTestDef; public InitializationStack locationToTestStack; public ComplexInfo(FacesComplexDefinition definition, boolean inTestLibrary) { this.definition = definition; this.javaClass = definition.getJavaClass(); this.inTargetLibrary = inTestLibrary; } public ComplexInfo(Class<?> javaClass, Object instance, boolean inTestLibrary) { this.javaClass = javaClass; isInited = true; initedBy = "default complex"; this.instance = instance; this.inTargetLibrary = inTestLibrary; } public boolean isInited(){ return isInited; } public Object getInstance() throws Exception{ if( null == instance ){ throw new NullPointerException(); } return instance; } public void initialize(UIComponent closest, Object object, FacesProperty prop) throws Exception{ instance = javaClass.newInstance(); if( instance instanceof ComponentBindingObject ){ ((ComponentBindingObject)instance).setComponent(closest); } this.initedBy = object.getClass().getName()+"."+prop.getName()+"(" +prop.getJavaClass()+")"; isInited = true; } } private Object getSkipSubstitute(int skipIndex){ if( -1 == skipIndex ){ throw new IllegalArgumentException(); } Object[] skipMatch = allSkips[skipIndex]; Object substitute = skipMatch[3]; return substitute; } private int findSkipIndex(FacesDefinition def, FacesProperty prop) { Class<?> defClass = def.getJavaClass(); Object[][] skipNameToClass = allSkips; for (int j = 0; j < skipNameToClass.length; j++) { Object[] skip = skipNameToClass[j]; Class<?> skipDefClass = (Class<?>) skip[0]; if( ! skipDefClass.isAssignableFrom(defClass)){ continue; } String skipName = (String) skip[1]; if( ! skipName.equals(prop.getName()) ){ continue; } Class<?> skipClass = (Class<?>) skip[2]; if (! skipClass.equals(prop.getJavaClass())) { continue; } if( skip.length < 5 ){ // expand skip to length 5 Object[] copy = new Object[5]; System.arraycopy(skip, 0, copy, 0, skip.length); skipNameToClass[j] = copy; skip = copy; } // mark the skip as used skip[4] = Boolean.TRUE; return j; } return -1; } /** * Format of the allowNoComplexForProperty skip arrays: * {defClass, propName, propClass, defaultValue(optional), skipUsed(generated)} * e.g. * private Object[][] skipAllowNoComplexForProperty = new Object[][]{ * new Object[]{UIDataPanelBase.class, "data", Collection.class}, * new Object[]{UIDataEx.class, "data", DataSource.class}, * // better, provides a value to use instead: * new Object[]{UISelectMany.class, "value", Object.class, new Object[]{"testObj[]"}}, * }; * The last value is added while the unit test is running. * @param reg * @return */ protected Object[][] getSkipProperty(FacesSharableRegistry reg) { return XspTestUtil.EMPTY_OBJECT_ARRAY_ARRAY; } /** * @param root */ private void removeChildren(List<UIComponent> children) { while (children.size() > 0) { children.remove(0); } } private List<ComplexInfo> getComplexesForClass(List<ComplexInfo> complexes, Class<?> propClass){ List<ComplexInfo> foundList = new ArrayList<ComplexInfo>(); for (ComplexInfo complex : complexes) { if( propClass.equals(complex.javaClass) || ! Object.class.equals(propClass) && propClass.isAssignableFrom(complex.javaClass) ){ foundList.add(complex); } } return foundList; } private ComplexInfo getComplexForClass(List<ComplexInfo> defaultComplexes, Class<?> propClass){ for (ComplexInfo complex : defaultComplexes) { if( propClass.equals(complex.javaClass) || ! Object.class.equals(propClass) && propClass.isAssignableFrom(complex.javaClass) ){ return complex; } } return null; } private ComplexInfo getTagComplexForClass(List<ComplexInfo> defaultComplexes, Class<?> propClass){ for (ComplexInfo complex : defaultComplexes) { if( propClass.equals(complex.javaClass) || ! Object.class.equals(propClass) && propClass.isAssignableFrom(complex.javaClass) ){ if( null == complex.definition || complex.definition.isTag() ){ return complex; } } } return null; } private boolean isNonTagToTest(Class<?> javaClass) { int i = 0; for (Object[] item : nonTagsToTest) { if( javaClass.equals(item[0]) ){ if( item.length == 1 ){ // set item[1] to true, indicating that the tag is in the registry nonTagsToTest[i] = new Object[]{ javaClass, true }; } return true; } i++; } return false; } }