/*
* � Copyright IBM Corp. 2012, 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: 17 Feb 2012
* RenderThemeControlTest.java
*/
package com.ibm.xsp.test.framework.render;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.render.Renderer;
import com.ibm.commons.Platform;
import com.ibm.commons.util.StringUtil;
import com.ibm.xsp.context.FacesContextEx;
import com.ibm.xsp.registry.FacesComponentDefinition;
import com.ibm.xsp.registry.FacesSharableRegistry;
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.XspRenderUtil;
import com.ibm.xsp.test.framework.XspTestUtil;
import com.ibm.xsp.test.framework.registry.annotate.DefinitionTagsAnnotater;
import com.ibm.xsp.test.framework.setup.SkipFileContent;
import com.sun.faces.context.FacesContextImpl;
/**
*
* @author Maire Kehoe (mkehoe@ie.ibm.com)
*/
public class RenderThemeControlTest extends AbstractXspTest {
@Override
public String getDescription() {
return "that the controls render in different themes without exceptions";
}
public void testRenderControlsInThemes() throws Exception {
FacesSharableRegistry reg = TestProject.createRegistryWithAnnotater(this,
new DefinitionTagsAnnotater());
if( TestProject.getLibComponents(reg, this).isEmpty() ){
// no controls in this library, skip control rendering test
String fails = "";
fails = XspTestUtil.removeMultilineFailSkips(fails,
SkipFileContent.concatSkips(getSkipFails(), this, "testRenderControlsInThemes"));
if( fails.length() > 0 ){
fail(XspTestUtil.getMultilineFailMessage(fails));
}
return;
}
File dominoFolder = TestProject.detectDominoInstallLocation(this);
// the platform is needed to load the .theme files from the Domino install location
if(!TestProject.isNotesDLL()){
TestProject.initPlatform(this, dominoFolder);
}
File themesFolder = new File(dominoFolder, "xsp/nsf/themes");
List<String> themeFileIds = detectThemeFileIds(themesFolder);
List<String> themeIds = new ArrayList<String>();
themeIds.addAll(themeFileIds);
themeIds.addAll(computeContributedThemes(themeIds));
themeIds.add(0, null); // first use the default, not-configured theme (usually "webstandard")
// String paths = StringUtil.concatStrings(StringUtil.toStringArray(themeIds), /*separator*/' ', /*trim*/false);
// System.out.println("RenderThemeControlTest.testRenderControlsInThemes() testing themeIds: "+paths);
String fails = "";
ContextAndPage[] contexts = new ContextAndPage[themeIds.size()];
int i = 0;
for (String themeId : themeIds) {
// System.out.println("RenderThemeControlTest.testRenderControlsInThemes()"
// + " ===================== themeId= "+themeId);
boolean problemWithTheme = false;
// set up the page to be rendered
FacesContextEx context = (FacesContextEx) TestProject.createFacesContext(this);
ResponseBuffer.initContext(context);
if( null != themeId ){
context.setSessionProperty("xsp.theme", themeId);
}
UIViewRoot root = TestProject.loadEmptyPage(this, context);
String expectedThemeName = (null != themeId)? themeId :
Platform.getInstance().isPlatform("Notes")? "notes" : "webstandard";
if( !expectedThemeName.equals(context.getStyleKit().getName()) ){
String actualCurrentTheme = context.getStyleKit().getName();
fails += "Problem setting theme to " +themeId+
", current theme is: " +actualCurrentTheme+"\n";
problemWithTheme = true;
}
UIComponent p = problemWithTheme? null : XspRenderUtil.createContainerParagraph(root);
if( problemWithTheme ){
contexts[i++] = null;
}else{
contexts[i++] = new ContextAndPage(themeId, context, root, p);
}
}
// for each control
for (FacesComponentDefinition def : TestProject.getLibComponents(reg, this)) {
if( !def.isTag() ){
continue; // skip non-tags
}
if( DefinitionTagsAnnotater.isTaggedNoRenderedOutput(def) ){
// verified in RenderControlTest
continue;
}
// verify can create a control instance
try{
def.getJavaClass().newInstance();
}catch(Exception e){
// no need to fail here as RenderControlTest
// will already be failing for the issue.
continue;
}
String defFails = "";
boolean testedFirstTheme = false;
String firstDefFail = null;
boolean allFailsSameAsFirst = true;
for (ContextAndPage objs : contexts){
if( null == objs ){
// reported above as Problem setting theme
continue;
}
String themeId = objs.themeId;
FacesContextEx context = objs.context;
UIViewRoot root = objs.root;
UIComponent p = objs.p;
setCurrentContext(context);
String currentDefFail = null;
// create a control instance
UIComponent instance = null;
try{
instance = (UIComponent) def.getJavaClass().newInstance();
}catch(Exception e){
currentDefFail = "Exception re-creating instance "+e;
defFails += XspTestUtil.loc(def)
+" [theme:" +themeId+"]"
+" " +currentDefFail +"\n";
}
String page = null;
if( null != instance ){
XspRenderUtil.resetContainerChild(root, p, instance);
XspRenderUtil.initControl(this, instance, context);
// only apply the theme after adding the control to the page tree
// so that instance.getParent() is non-null (prevents NullPointerEx)
context.getStyleKit().applyStyles(context, instance);
// verify there is a renderer - the theme may modify the rendererType
// so the renderer would be different to that tested in ComponentRendererTest
String actualComponentFamily = instance.getFamily();
String actualRendererType = instance.getRendererType();
Renderer renderer = context.getRenderKit().getRenderer(actualComponentFamily, actualRendererType);
if( null == renderer ){
currentDefFail = "No renderer found for "
+"component-family " +actualComponentFamily
+", renderer-type "+actualRendererType;
defFails += XspTestUtil.loc(def)
+" [theme:" +themeId+"]"
+ " " +currentDefFail+"\n";
}
try{
page = ResponseBuffer.encode(p, context);
}catch(Exception e){
currentDefFail = "Problem rendering page: "+e;
String msg = XspTestUtil.loc(def)
+" [theme:" +themeId+"]"
+ " " +currentDefFail;
System.err.println("RenderThemeControlTest.testRenderControlsInThemes() "+msg);
e.printStackTrace();
defFails += msg+"\n";
ResponseBuffer.clear(context);
}
}
if( null != page ){
// System.out.println("RenderThemeControlTest.testRenderControlsInThemes() "
// +XspTestUtil.loc(def)
// +" [theme:" +themeId+"]"
// +"\n"+page);
if( ! page.startsWith("<p>") ){
currentDefFail = "Wrote attributes to the parent <p> tag: "+ page;
defFails += XspTestUtil.loc(def)
+" [theme:" +themeId+"]"
+ " " + currentDefFail + "\n";
}
if( page.equals("<p></p>") ){
currentDefFail = "No output rendered.";
defFails += XspTestUtil.loc(def)
+" [theme:" +themeId+"]"
+ " " +currentDefFail +"\n";
}
}
if( ! testedFirstTheme ){
firstDefFail = currentDefFail;
testedFirstTheme = true;
}else{
if( !StringUtil.equals(firstDefFail, currentDefFail) ){
allFailsSameAsFirst = false;
}
}
}
if( allFailsSameAsFirst ){
if( null != firstDefFail ){
// no need to fail here as RenderControlTest
// will already be failing for the issue.
// fails += XspTestUtil.loc(def)
// +" [all-themes]"
// + " " +firstDefFail +"\n";
}
}else{
fails += defFails;
}
}
fails = XspTestUtil.removeMultilineFailSkips(fails,
SkipFileContent.concatSkips(getSkipFails(), this, "testRenderControlsInThemes"));
if( fails.length() > 0 ){
fail(XspTestUtil.getMultilineFailMessage(fails));
}
}
protected List<String> computeContributedThemes(
List<String> themeFileIds) {
// TODO in Extlib, compute "oneui_mobile", etc.
return Collections.emptyList();
}
/**
* Available to override in subclasses.
* @return
*/
protected String[] getSkipFails() {
return StringUtil.EMPTY_STRING_ARRAY;
}
/**
* Utility method available to call from subclass.
* Uses {@link ConfigUtil#isIgnoreThemeFilesWithUnderscore(AbstractXspTest)}
* @param themesFolder
* @return
*/
protected List<String> detectThemeFileIds(File themesFolder) {
boolean excludeUnderscoredThemes = ConfigUtil.isIgnoreThemeFilesWithUnderscore(this);
List<String> themeFileShortNames = new ArrayList<String>();
for (File themeFile : themesFolder.listFiles()) {
String nameWithSuffix = themeFile.getName();
if( !nameWithSuffix.endsWith(".theme") ){
continue;
}
String baseName = nameWithSuffix.substring(0, nameWithSuffix.length()-".theme".length());
if( excludeUnderscoredThemes && -1 != baseName.indexOf('_')){
continue;
}
themeFileShortNames.add(baseName);
}
return themeFileShortNames;
}
private void setCurrentContext(final FacesContextEx context ){
new FacesContextImpl(){
{
setCurrentInstance(context);
}
}.getClass(); // getClass() to prevent compile warning: The allocated object is never used
}
private static class ContextAndPage{
public String themeId;
public FacesContextEx context;
public UIViewRoot root;
public UIComponent p;
public ContextAndPage(String themeId, FacesContextEx context, UIViewRoot root, UIComponent p) {
super();
this.context = context;
this.root = root;
this.p = p;
this.themeId = themeId;
}
}
}