/* * � Copyright IBM Corp. 2013 * * 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: 24 Feb 2009 * NoRunTimeBindingsTest.java */ package com.ibm.xsp.test.framework.registry; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import com.ibm.commons.util.StringUtil; import com.ibm.xsp.complex.ValueBindingObject; import com.ibm.xsp.registry.*; import com.ibm.xsp.registry.parse.ParseUtil; import com.ibm.xsp.test.framework.AbstractXspTest; import com.ibm.xsp.test.framework.TestProject; import com.ibm.xsp.test.framework.XspTestUtil; import com.ibm.xsp.test.framework.setup.SkipFileContent; /** * @author Maire Kehoe (mkehoe@ie.ibm.com) * 24 Feb 2009 * * Unit: NoRunTimeBindingsTest.java */ public class NoRunTimeBindingsTest extends AbstractXspTest { @Override public String getDescription() { return "that only the expected properties disallow runtime bindings (and are hardcoded in this test)"; } public void testNoRunTimeBindings() throws Exception { String fails = ""; List<DefinitionDisallowInfo> disallowInfos = parseDisallowInfo(getDisallowedBindingPropList()); String[] propNamesAlwaysDisallowArr = getPropNamesAlwaysDisallow(); String[] propNameSuffixesAlwaysDisallowArr = getPropNameSuffixesAlwaysDisallow(); int[] disallowIndexHint = new int[]{-1}; FacesSharableRegistry reg = TestProject.createRegistry(this); for (FacesDefinition def : TestProject.getLibCompComplexDefs(reg, this)) { clearIndexHint(disallowIndexHint); // only testing inline and group-type-ref props, not inherited for (String propName : def.getDefinedPropertyNames()) { FacesProperty prop = def.getProperty(propName); boolean isContainerProp = prop instanceof FacesContainerProperty; if( isContainerProp ){ prop = ((FacesContainerProperty)prop).getItemProperty(); } if( prop instanceof FacesMethodBindingProperty ){ continue; } boolean actualDisallow = prop instanceof FacesSimpleProperty && !((FacesSimpleProperty)prop).isAllowRunTimeBinding(); boolean isSuspectShouldDisallow = false; String reason = null; int disallowPropNameIndex; int disallowPropNameSuffixIndex; if( isContainerProp ){ isSuspectShouldDisallow = true; reason = "property allows multiple values"; }else if( (def instanceof FacesComplexDefinition) && !(ValueBindingObject.class.isAssignableFrom(def.getJavaClass())) ){ isSuspectShouldDisallow = true; reason = "complex-type not implementing ValueBindingObject"; } else if( -1 != (disallowPropNameIndex = XspTestUtil.indexOf(propNamesAlwaysDisallowArr, propName)) ){ isSuspectShouldDisallow = true; reason = "property-name " + propNamesAlwaysDisallowArr[disallowPropNameIndex] + " usually disallows"; }else if( -1 != (disallowPropNameSuffixIndex = XspTestUtil.endsWithIndex(propNameSuffixesAlwaysDisallowArr, propName)) ){ isSuspectShouldDisallow = true; reason = "property-name suffix " +propNameSuffixesAlwaysDisallowArr[disallowPropNameSuffixIndex] +" usually disallows"; }else if( prop instanceof FacesSimpleProperty && null != ((FacesSimpleProperty)prop).getTypeDefinition() ){ isSuspectShouldDisallow = true; reason = "property-class corresponds to complex-type class"; } if( actualDisallow){ if( !isMarkSkipped(disallowInfos, disallowIndexHint, def, propName)){ if( isSuspectShouldDisallow ){ fails += def.getFile().getFilePath()+ " " + ParseUtil.getTagRef(def) + " "+ propName + " Property has <allow-run-time-binding>false< " + "but not in junit hard-coded list of disallow props. " + "Disallow is expected because: " +reason +". Skip would be: " + toSkipString(def.getJavaClass(), propName) +"\n"; }else{ fails += def.getFile().getFilePath()+ " " + ParseUtil.getTagRef(def) + " "+ propName + " Property has <allow-run-time-binding>false< " + "but not in junit hard-coded list of disallow props. " + "Intentional disallow?" +" Skip would be: " + toSkipString(def.getJavaClass(), propName) +"\n"; } }// else is disallow & in hardcoded list of disallows }else{ // !actualDisallow if( isSuspectShouldDisallow ){ fails += def.getFile().getFilePath()+ " " + ParseUtil.getTagRef(def) + " "+ propName + " Property does not have <allow-run-time-binding>false< " + "but disallow is expected because: "+reason +"\n"; } } } clearIndexHint(disallowIndexHint); } for (DefinitionDisallowInfo info : disallowInfos) { if( info.isDisallowAllProps ){ if( ! info.disallowAllSkipUsed ){ String skipStr = toSkipString(info.isById, info.definitionId, info.definitionClass, null); fails += "Unused disallowed skip: "+skipStr+"\n"; } }else{ // !info.isDisallowAllProps int len = info.disallowedPropNames.length; for (int i = 0; i < len; i++) { boolean used = info.disallowedPropSkipUsed[i]; if( ! used ){ String propName = info.disallowedPropNames[i]; String skipStr = toSkipString(info.isById, info.definitionId, info.definitionClass, propName); fails += "Unused disallowed skip: "+skipStr+"\n"; } } } } fails = XspTestUtil.removeMultilineFailSkips(fails, SkipFileContent.concatSkips(getSkips(), this, "testNoRunTimeBindings")); if( fails.length() > 0 ){ fail(XspTestUtil.getMultilineFailMessage(fails)); } } protected String[] getPropNamesAlwaysDisallow() { return StringUtil.EMPTY_STRING_ARRAY; } protected String[] getPropNameSuffixesAlwaysDisallow() { return StringUtil.EMPTY_STRING_ARRAY; } protected Object[][] getDisallowedBindingPropList() { return new Object[0][]; } protected String[] getSkips(){ return StringUtil.EMPTY_STRING_ARRAY; } private String toSkipString(Class<?> definitionClass, String propName) { return toSkipString(false, null, definitionClass, propName); } private String toSkipString(boolean isById, String definitionId, Class<?> definitionClass, String propName) { StringBuilder skipAsStr = new StringBuilder(); skipAsStr.append("new Object[]{"); if( isById ){ skipAsStr.append('"'); skipAsStr.append(definitionId); skipAsStr.append('"'); }else{ skipAsStr.append(XspTestUtil.getShortClass(definitionClass)); skipAsStr.append(".class"); } if( null != propName ){ skipAsStr.append(", new String[]{\""); skipAsStr.append(propName); skipAsStr.append("\"}},"); }else{ skipAsStr.append("},"); } String skipStr = skipAsStr.toString(); return skipStr; } private List<DefinitionDisallowInfo> parseDisallowInfo(Object[][] disallowedArray){ List<DefinitionDisallowInfo> infos = new ArrayList<DefinitionDisallowInfo>(disallowedArray.length); Set<Object> definitionIdSet = new HashSet<Object>(); for (Object[] classToPropNames : disallowedArray) { Object definitionIdObj = classToPropNames[0]; boolean isById = definitionIdObj instanceof String; Class<?> definitionClass; String definitionId; if( isById ){ definitionClass = null; definitionId = (String) definitionIdObj; }else{ definitionClass = (Class<?>) definitionIdObj; definitionId = null; } boolean isDisallowAllProps = (classToPropNames.length == 1); String[] disallowedPropNames; if( ! isDisallowAllProps ){ disallowedPropNames = (String[]) classToPropNames[1]; }else{ disallowedPropNames = null; } if( definitionIdSet.contains(definitionIdObj) ){ DefinitionDisallowInfo existingInfo = null; for (DefinitionDisallowInfo info : infos) { if( match(info, isById, definitionId, definitionClass) ){ existingInfo = info; break; } } if( null == existingInfo ){ throw new RuntimeException("Bad junit test state"); } if( isDisallowAllProps || existingInfo.isDisallowAllProps ){ throw new RuntimeException("Cannot merge incompatible skips for: "+definitionIdObj); } existingInfo.addPropNames(disallowedPropNames); }else{ // ! existingInfo infos.add(new DefinitionDisallowInfo(isById, definitionClass, definitionId, isDisallowAllProps, disallowedPropNames)); definitionIdSet.add(definitionIdObj); } } return infos; } /** * @param info * @param isById * @param definitionId * @param definitionClass * @return */ private boolean match(DefinitionDisallowInfo info, boolean isById, String definitionId, Class<?> definitionClass) { if( info.isById != isById ){ return false; } if( !StringUtil.equals(info.definitionId, definitionId) ){ return false; } if( !StringUtil.equals(info.definitionClass, definitionClass) ){ return false; } return true; } private boolean isMarkSkipped(List<DefinitionDisallowInfo> disallowedInfos, int[] indexHint, FacesDefinition def, String propName){ boolean recentlyFound = false; if( -1 == indexHint[0] ){ int len = disallowedInfos.size(); for (int i = 0; i < len; i++) { DefinitionDisallowInfo info = disallowedInfos.get(i); if( match(info, def) ){ indexHint[0] = i; recentlyFound = true; break; } } } if( -1 == indexHint[0] ){ return false; } DefinitionDisallowInfo indexedInfo = disallowedInfos.get(indexHint[0]); if( !match(indexedInfo, def) ){ throw new RuntimeException("JUnit test in bad state, didn't call clearIndexHint before move to next definition"); } if( indexedInfo.isDisallowAllProps ){ if( recentlyFound ){ indexedInfo.disallowAllSkipUsed = true; } return true; } int propIndex = XspTestUtil.indexOf(indexedInfo.disallowedPropNames, propName); if( -1 == propIndex ){ return false; } indexedInfo.disallowedPropSkipUsed[propIndex] = true; return true; } private boolean match(DefinitionDisallowInfo info, FacesDefinition def){ if( info.isById ){ return info.definitionId.equals(def.getId()); } return info.definitionClass.equals(def.getJavaClass()); } private void clearIndexHint(int[] indexHint){ indexHint[0] = -1; } private static class DefinitionDisallowInfo{ boolean isById; Class<?> definitionClass; String definitionId; boolean isDisallowAllProps; boolean disallowAllSkipUsed = false; String[] disallowedPropNames; boolean[] disallowedPropSkipUsed; public DefinitionDisallowInfo(boolean isById, Class<?> definitionClass, String definitionId, boolean isDisallowAllProps, String[] disallowedPropNames) { super(); this.isById = isById; this.definitionClass = definitionClass; this.definitionId = definitionId; this.isDisallowAllProps = isDisallowAllProps; this.disallowedPropNames = disallowedPropNames; this.disallowedPropSkipUsed = isDisallowAllProps? null : new boolean[disallowedPropNames.length]; } public void addPropNames(String[] moreDisallowedPropNames) { disallowedPropNames = XspTestUtil.concat(disallowedPropNames, moreDisallowedPropNames); disallowedPropSkipUsed = new boolean[disallowedPropNames.length]; } } }