/*
* � 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: 3 Jun 2009
* SinceVersionsSetTest.java
*/
package com.ibm.xsp.test.framework.version;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.ibm.commons.util.StringUtil;
import com.ibm.xsp.registry.FacesDefinition;
import com.ibm.xsp.registry.FacesLibrary;
import com.ibm.xsp.registry.FacesProperty;
import com.ibm.xsp.registry.FacesRegistry;
import com.ibm.xsp.registry.FacesSharableRegistry;
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)
*
*/
public class SinceVersionsSetTest extends AbstractXspTest {
@Override
public String getDescription() {
return "to prevent "
+ XspTestUtil.getShortClass(NoClassDefFoundError.class)
+ " and "
+ XspTestUtil.getShortClass(NoSuchMethodError.class)
+ " problems when running new apps on old servers, "
+ "new propertys and tags should have a <since> element indicating when they were added.";
}
public void testSinceVersionsSet() throws Exception {
FacesSharableRegistry reg = TestProject.createRegistry(this);
Map<String, FacesLibrary> prefixToLibrary = PrintTagNamesAndProps.getPrefixToNamespace(reg);
List<SinceVersionList> versionListArr = getSinceVersionLists();
if( null == versionListArr || versionListArr.isEmpty() ){
versionListArr = new ArrayList<SinceVersionList>();
versionListArr.add(new EmptySinceVersionList());
}
{
String[] skips = getExtraSkips();
skips = SkipFileContent.concatSkips(skips, this, "testSinceVersionsSet");
if( skips.length > 0 ){
int mostRecentIndex = versionListArr.size() - 1;
SinceVersionList sinceList = versionListArr.get(mostRecentIndex);
SinceVersionList wrapper = new SinceVersionListExtraSkipsWrapper(sinceList, getExtraSkips());
versionListArr.set(mostRecentIndex, wrapper);
}
}
String recentSinceVersion = getCurrentVersion(versionListArr);
String fails = "";
for (SinceVersionList versionInfo : versionListArr) {
fails += checkSinceVersion(reg, prefixToLibrary, versionInfo.tagsAndProps(), versionInfo.sinceVersion());
}
// when new props are added, in the 8.5.3 stream, without a <since> version,
// that aren't yet listed in SinceVersion853List, should complain:
// xp:something.newProp bad since version. Expected <since>8.5.3(probably)<, was <since>null<
// where the "(probably)" bit will be present until SinceVersion853List is re-generated.
List<Object[]> extraSinceList = findNewUnlistedTagsAndProps(reg, versionListArr);
String dependsLibraryIdFilter = getDependsLibraryIdFilter();
if( null != dependsLibraryIdFilter ){
extraSinceList = PrintTagNamesAndProps.filterToDependsLibrary(reg, dependsLibraryIdFilter, extraSinceList);
}
Object[][] extraSinceArr = extraSinceList.toArray(new Object[extraSinceList.size()][]);
fails += checkSinceVersion(reg, prefixToLibrary, extraSinceArr, recentSinceVersion+"(probably)");
String[] skips = concatSkips(versionListArr);
for (String skip : skips) {
int oldLen = fails.length();
fails = removeSkip(fails,skip);
if( fails.length() == oldLen ){
String proposedSkip = "Unused skip: "+skip;
fails = removeSkip(fails,skip);
if( fails.length() == oldLen ){
fails += proposedSkip+"\n";
}
}
}
if( fails.length() > 0 ){
fail(XspTestUtil.getMultilineFailMessage(fails, "<since> version mismatch"));
}
}
/**
* Available to override in subclasses. Defaults to the version of the last in the list.
* @param versionListArr
* @return
*/
protected String getCurrentVersion(List<SinceVersionList> versionListArr) {
SinceVersionList recentObj = getMostRecent(versionListArr);
String recentSinceVersion = recentObj.sinceVersion();
return recentSinceVersion;
}
/**
* Available to override in subclass, when null will test all tags in the
* registry, including the XPages runtime tags.
*
* @return
*/
protected String getDependsLibraryIdFilter(){
return null;
}
/**
* Should be overridden in subclass. The last version in the list
* is considered the current library tag version. The older versions
* will be treated as from older since versions,
* and will not require a since>currentVersion</since tag.
*
* @return
*/
protected List<SinceVersionList> getSinceVersionLists(){
List<SinceVersionList> list = new ArrayList<SinceVersionList>();
return list;
}
private String[] concatSkips(List<SinceVersionList> versionListArr) {
List<String[]> all = new ArrayList<String[]>(versionListArr.size());
for (SinceVersionList versionInfo : versionListArr) {
String[] skips = versionInfo.skips();
all.add(skips);
}
return XspTestUtil.concatStringArrays(all.toArray(new String[versionListArr.size()][]));
}
private String removeSkip(String fails, String skip) {
skip = skip+"\n";
int index = fails.indexOf(skip);
if( -1 == index ){
return fails;
}
if( 0 != index ){
// unused skips are nested, so need to check
// this is an actual match at the start of a line.
index = fails.indexOf("\n"+skip);
if( -1 == index ){
return fails;
}
index++;
}
int endIndex = index + skip.length();
return fails.substring(0, index)+ fails.substring(endIndex);
}
private String checkSinceVersion(FacesRegistry reg,
Map<String, FacesLibrary> libByPrefix, Object[][] tagsAndProps, String expectedSince) {
String fails = "";
for (Object[] tagAndProp : tagsAndProps) {
String prefixedTagName = getName(tagAndProp);
FacesDefinition def = PrintTagNamesAndProps.getDef(libByPrefix, prefixedTagName);
if( null == def ){
fails+= prefixedTagName+" not found. Expected <since>"+expectedSince+"<\n";
continue;
}
String actualDefSince = def.getSince();
boolean isNewTag = PrintTagNamesAndProps.isNewTag(tagAndProp);
if( isNewTag && !StringUtil.equals(expectedSince, actualDefSince) ){
fails += XspTestUtil.loc(def) + " bad since version. Expected <since>"
+ expectedSince + "<, was <since>" + actualDefSince
+ "<\n";
continue;
}
if( isNoProps(tagAndProp) ){
continue;
}
String[] propNames = getProps(tagAndProp);
for (String name : propNames) {
FacesProperty prop = def.getProperty(name);
if( null == prop ){
String msg = XspTestUtil.loc(def)+" " +name+" not found. Expected <since>"+expectedSince+"<";
fails+= msg + "\n";
continue;
}
String actualPropSince = prop.getSince();
if( null == actualPropSince ){
actualPropSince = actualDefSince;
}
if( ! StringUtil.equals(expectedSince, actualPropSince) ){
fails += XspTestUtil.loc(def) +" " +name+ " bad since version. Expected <since>"
+ expectedSince + "<, was <since>" + actualPropSince
+ "<\n";
continue;
}
}
}
return fails;
}
public void testCurrentSinceListCorrect() throws Exception {
List<SinceVersionList> versionListArr = getSinceVersionLists();
FacesSharableRegistry reg = TestProject.createRegistry(this);
List<Object[]> extraSinceList = findNewUnlistedTagsAndProps(reg, versionListArr);
String dependsLibraryIdFilter = getDependsLibraryIdFilter();
if( null != dependsLibraryIdFilter ){
extraSinceList = PrintTagNamesAndProps.filterToDependsLibrary(reg, dependsLibraryIdFilter, extraSinceList);
}
String fails ="";
for (Object[] extraTag : extraSinceList) {
if( isNoProps(extraTag) ){
fails += "new tag "+getName(extraTag)+"\n";
continue;
}
fails += "tag "+getName(extraTag)+" has new props: " +toString(getProps(extraTag))+"\n";
}
fails = XspTestUtil.removeMultilineFailSkips(fails,
SkipFileContent.concatSkips(getCurrentSkips(), this, "testCurrentSinceListCorrect"));
if( fails.length() > 0 ){
SinceVersionList mostRecent = getMostRecent(versionListArr);
String msg;
if( null != mostRecent ){
msg = XspTestUtil.getShortClass(mostRecent) + " is out of date. Please regenerate that class. ";
}else{
msg = "Known tags list not available. Please override getSinceVersionLists() and list these tags.";
}
fail(XspTestUtil.getMultilineFailMessage(fails, msg));
}
}
/**
* This should only be used in Green tests, used by testCurrentSinceListCorrect
* @return
*/
protected String[] getCurrentSkips(){
return StringUtil.EMPTY_STRING_ARRAY;
}
/**
* This should only be used in Green tests, used by testSinceVersionsSet.
* These skips should not be added from this method
* but should be supplied through {@link #getSinceVersionLists()},
* as values in {@link SinceVersionList#skips()}.
* @return
*/
protected String[] getExtraSkips() {
return StringUtil.EMPTY_STRING_ARRAY;
}
private List<Object[]> findNewUnlistedTagsAndProps(FacesSharableRegistry registry,
List<SinceVersionList> versionListArr) {
List<Object[]> expectedSinceList = new ArrayList<Object[]>();
for (SinceVersionList versionInfo : versionListArr) {
PrintTagNamesAndProps.addAll(expectedSinceList, versionInfo.tagsAndProps());
}
List<Object[]> actualSinceList = PrintTagNamesAndProps.getTagsAndProps(registry);
List<Object[]> extraSinceList = actualSinceList;
PrintTagNamesAndProps.removeAll(actualSinceList, expectedSinceList.toArray(new Object[expectedSinceList.size()][]), true);
return extraSinceList;
}
public SinceVersionList getMostRecent(List<SinceVersionList> versionListArr){
if( null == versionListArr || versionListArr.isEmpty() ){
return null;
}
return versionListArr.get(versionListArr.size()-1);
}
private String[] getProps(Object[] extraTag) {
return PrintTagNamesAndProps.getProps(extraTag);
}
private String getName(Object[] extraTag) {
return PrintTagNamesAndProps.getName(extraTag);
}
private boolean isNoProps(Object[] extraTag) {
return PrintTagNamesAndProps.isNoProps(extraTag);
}
private static String toString(String[] arr) {
if( null == arr ){
arr = StringUtil.EMPTY_STRING_ARRAY;
}
return StringUtil.toStringArray(arr, -1, Integer.MAX_VALUE);
}
private static final class EmptySinceVersionList implements SinceVersionList {
public Object[][] tagsAndProps() {
return new Object[0][];
}
public String[] skips() {
return StringUtil.EMPTY_STRING_ARRAY;
}
public String sinceVersion() {
// null is the first version.
return null;
}
}
/**
* Note, this is provided for building Green* subclasses,
* which skip all known fails (rather than skipping issues
* known to be not-a-problem). In that case, the skip
* method would be like so:
* @Override
* protected List<SinceVersionList> getSinceVersionLists() {
* List<SinceVersionList> list = super.getSinceVersionLists();
* int mostRecentIndex = list.size() - 1;
* SinceVersionList sinceList = list.get(mostRecentIndex);
* SinceVersionList wrapper = new SinceVersionListExtraSkipsWrapper(sinceList, skips);
* list.set(mostRecentIndex, wrapper);
* return list;
* }
* @author Maire Kehoe (mkehoe@ie.ibm.com)
*/
public static class SinceVersionListExtraSkipsWrapper implements SinceVersionList{
private SinceVersionList delegate;
private String[] extraSkips;
public SinceVersionListExtraSkipsWrapper(SinceVersionList delegate,
String[] extraSkips) {
super();
this.delegate = delegate;
this.extraSkips = extraSkips;
}
public Object[][] tagsAndProps() {
return delegate.tagsAndProps();
}
public String sinceVersion() {
return delegate.sinceVersion();
}
public String[] skips() {
return XspTestUtil.concat(delegate.skips(),extraSkips);
}
}
}