/**
* This file is part of Jahia, next-generation open source CMS:
* Jahia's next-generation, open source CMS stems from a widely acknowledged vision
* of enterprise application convergence - web, search, document, social and portal -
* unified by the simplicity of web content management.
*
* For more information, please visit http://www.jahia.com.
*
* Copyright (C) 2002-2011 Jahia Solutions Group SA. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* As a special exception to the terms and conditions of version 2.0 of
* the GPL (or any later version), you may redistribute this Program in connection
* with Free/Libre and Open Source Software ("FLOSS") applications as described
* in Jahia's FLOSS exception. You should have received a copy of the text
* describing the FLOSS exception, and it is also available here:
* http://www.jahia.com/license
*
* Commercial and Supported Versions of the program (dual licensing):
* alternatively, commercial and supported versions of the program may be used
* in accordance with the terms and conditions contained in a separate
* written agreement between you and Jahia Solutions Group SA.
*
* If you are unsure which license is appropriate for your use,
* please contact the sales department at sales@jahia.com.
*/
package org.jahia.bin;
import org.jahia.params.ProcessingContextFactory;
import org.jahia.params.ProcessingContext;
import org.jahia.params.BasicSessionState;
import org.jahia.params.ParamBean;
import org.jahia.services.SpringContextSingleton;
import org.jahia.services.content.JCRSessionFactory;
import org.jahia.services.usermanager.JahiaUser;
import org.jahia.test.JahiaAdminUser;
import org.jahia.test.SurefireJUnitXMLResultFormatter;
import org.jahia.exceptions.JahiaException;
import org.junit.internal.requests.FilterRequest;
import org.junit.runner.Description;
import org.junit.runner.JUnitCore;
import org.junit.runner.Request;
import org.junit.runner.Result;
import org.junit.runner.manipulation.Filter;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* JUnit test runner servlet.
* User: toto
* Date: Feb 11, 2009
* Time: 4:07:40 PM
*/
@SuppressWarnings("serial")
public class TestServlet extends HttpServlet implements Controller, ServletContextAware {
private transient static Logger logger = org.slf4j.LoggerFactory.getLogger(TestServlet.class);
private ServletContext servletContext;
protected void handleGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {
// should be protected in production
// if (System.getProperty("org.jahia.selftest") == null) {
// httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
// return;
// }
final ProcessingContextFactory pcf = (ProcessingContextFactory) SpringContextSingleton.
getInstance().getContext().getBean(ProcessingContextFactory.class.getName());
ProcessingContext ctx = null;
try {
// should send response wrapper !
ctx = pcf.getContext(httpServletRequest, httpServletResponse, servletContext);
} catch (JahiaException e) {
logger.error("Error while trying to build ProcessingContext", e);
return;
}
try {
ctx.setOperationMode(ParamBean.EDIT);
// ctx.setEntryLoadRequest(new EntryLoadRequest(EntryLoadRequest.STAGING_WORKFLOW_STATE, 0, ctx.getLocales()));
JahiaUser admin = JahiaAdminUser.getAdminUser(0);
JCRSessionFactory.getInstance().setCurrentUser(admin);
ctx.setTheUser(admin);
} catch (JahiaException e) {
logger.error("Error getting user", e);
}
try {
String pathInfo = StringUtils.substringAfter(httpServletRequest.getPathInfo(), "/test");
if (StringUtils.isNotEmpty(pathInfo)) {
final Set<String> ignoreTests = getIgnoreTests();
// Execute one test
String className = pathInfo.substring(pathInfo.lastIndexOf('/')+1);
try {
JUnitCore junitcore = new JUnitCore();
SurefireJUnitXMLResultFormatter xmlResultFormatter = new SurefireJUnitXMLResultFormatter(httpServletResponse.getOutputStream());
junitcore.addListener(xmlResultFormatter);
Class testClass = Class.forName(className);
List<Class> classes = getTestClasses(testClass, new ArrayList<Class>());
if (classes.isEmpty()) {
Description description = Description.createSuiteDescription(testClass);
xmlResultFormatter.testRunStarted(description);
xmlResultFormatter.testRunFinished(new Result());
} else {
junitcore.run(new FilterRequest(Request.classes(classes
.toArray(new Class[classes.size()])), new Filter() {
@Override
public boolean shouldRun(Description description) {
return !ignoreTests.contains(description.getDisplayName());
}
@Override
public String describe() {
return "Filter out Jahia configured methods";
}
}));
}
} catch (Exception e) {
logger.error("Error executing test", e);
}
} else {
WebApplicationContext webApplicationContext = (WebApplicationContext) servletContext.getAttribute(WebApplicationContext.class.getName() + ".jahiaModules");
Map<String,TestBean> testBeans = webApplicationContext.getBeansOfType(TestBean.class);
PrintWriter pw = httpServletResponse.getWriter();
// Return the lists of available tests
List<String> tests = new LinkedList<String>();
SortedSet<TestBean> s = new TreeSet<TestBean>(testBeans.values());
for (TestBean testBean : s) {
for (String o : testBean.getTestCases()) {
tests.add(o);
}
}
for (String c : tests) {
pw.println(c);
}
}
} finally {
try {
ctx.setUserGuest();
} catch (JahiaException e) {
logger.error(e.getMessage(), e);
}
}
}
private List<Class> getTestClasses(Class testClass, List<Class> classes) {
Method suiteMethod = null;
try {
// check if there is a suite method
suiteMethod = testClass.getMethod("suite", new Class[0]);
} catch (NoSuchMethodException e) {
// no appropriate suite method found. We don't report any
// error here since it might be perfectly normal.
}
if (suiteMethod != null) {
// if there is a suite method available, then try
// to extract the suite from it. If there is an error
// here it will be caught below and reported.
try {
classes = getTestClasses((Test)suiteMethod.invoke(null, new Class[0]), classes);
} catch (Exception e) {
logger.error("Error getting classes of suite", e);
}
} else {
classes.add(testClass);
}
return classes;
}
private List<Class> getTestClasses(Test test, List<Class> classes) {
if (test instanceof TestSuite) {
// if there is a suite method available, then try
// to extract the suite from it. If there is an error
// here it will be caught below and reported.
Set<Class> tempClasses = new HashSet<Class>();
for (Enumeration<Test> tests = ((TestSuite)test).tests(); tests.hasMoreElements(); ) {
Test currentTest = tests.nextElement();
if (currentTest instanceof TestSuite || !tempClasses.contains(currentTest.getClass())) {
classes = getTestClasses(currentTest, classes);
tempClasses.add(currentTest.getClass());
}
}
} else {
classes.add(test.getClass());
}
return classes;
}
private Set<String> getIgnoreTests() {
WebApplicationContext webApplicationContext = (WebApplicationContext) servletContext.getAttribute(WebApplicationContext.class.getName() + ".jahiaModules");
Map<String,TestBean> testBeans = webApplicationContext.getBeansOfType(TestBean.class);
// Return the lists of available tests
Set<String> ignoreTests = new HashSet<String>();
SortedSet<TestBean> s = new TreeSet<TestBean>(testBeans.values());
for (TestBean testBean : s) {
if (testBean.getIgnoredTests() != null) {
ignoreTests.addAll(testBean.getIgnoredTests());
}
}
return ignoreTests;
}
private boolean isMethodAnnotationPresent(Class<?> checkedClass, Class<? extends Annotation> annotationClass) {
boolean isPresent = false;
for (Method method : checkedClass.getMethods()) {
if (method.isAnnotationPresent(annotationClass)) {
isPresent = true;
break;
}
}
return isPresent;
}
private Class<?> getTestClass(String line) {
Class<?> clazz = null;
if (StringUtils.isNotBlank(line) && !line.trim().startsWith("#") && !line.trim().startsWith("//")) {
String clazzName = null;
if (line.contains("/")) {
// assume that it is a path for .class file
clazzName = line.trim();
clazzName = clazzName.replace('/','.').substring(0, clazzName.lastIndexOf('.'));
} else {
// assume it is fully qualified class name
clazzName = line.trim();
}
try {
Class<?> c = Class.forName(clazzName);
if (TestCase.class.isAssignableFrom(c) || isMethodAnnotationPresent(c, org.junit.Test.class)) {
clazz = c;
}
} catch (ClassNotFoundException e) {
logger.error("Error finding class for name " + line, e);
}
}
return clazz;
}
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (request.getMethod().equalsIgnoreCase("get")) {
handleGet(request, response);
} else {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
return null;
}
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
private List<String> readTests(String resource, boolean verify) {
InputStream is = getClass().getClassLoader().getResourceAsStream(resource);
List<String> tests = Collections.emptyList();
try {
if (is != null) {
tests = new LinkedList<String>();
@SuppressWarnings("unchecked")
List<String> lines = IOUtils.readLines(is);
for (String line : lines) {
if (verify) {
// check the class
Class<?> c = getTestClass(line);
if (c != null) {
tests.add(c.getName());
}
} else if (StringUtils.isNotBlank(line) && !line.trim().startsWith("#") && !line.trim().startsWith("//")){
// just add an non-empty line
tests.add(line);
}
}
}
}
catch (Exception e) {
logger.warn("Unable to read class names from the resource " + resource);
} finally {
IOUtils.closeQuietly(is);
}
return tests;
}
}