/* * This software is distributed under the terms of the FSF * Gnu Lesser General Public License (see lgpl.txt). * * This program is distributed WITHOUT ANY WARRANTY. See the * GNU General Public License for more details. */ package com.scooterframework.test; import static org.junit.Assert.assertEquals; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.Connection; import java.util.Map; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import com.scooterframework.admin.ApplicationConfig; import com.scooterframework.admin.Constants; import com.scooterframework.admin.EnvConfig; import com.scooterframework.common.http.HTTPClient; import com.scooterframework.common.http.HTTPResponse; import com.scooterframework.orm.sqldataexpress.config.DatabaseConfig; import com.scooterframework.orm.sqldataexpress.util.DAOUtil; import com.scooterframework.orm.sqldataexpress.util.SqlExpressUtil; /** * <p> * FunctionalTestHelper class is super class of all functional test classes for * an application. Scooter uses a real, not simulated, web server to run * functional tests. * * <p> * By default, Scooter starts an embedded web server for functional tests on * port <tt>8080</tt>. You can change the default testing server port or use an * existing external server by specifying the system property appPort or * webStarted respectively. * * <p> * It is very easy to run functional tests with Scooter. Just run one of the * following Ant commands: * * <p> * Run blog application's functional tests on default port 8080: * * <pre> * >ant app_test_functional -DappPath=webapps/blog * </pre> * * <p> * Run blog application's functional tests on port 9999: * * <pre> * >ant app_test_functional -DappPath=webapps/blog -DappPort=9999 * </pre> * * <p> * Run blog application's functional tests with an existing web server: * * <pre> * >ant app_test_functional -DappPath=webapps/blog -DwebStarted=true * </pre> * * * @author (Fei) John Chen * */ public class FunctionalTestHelper { private static final String DEFAULT_TEST_HOST = "http://localhost"; private static final String DEFAULT_TEST_PORT = "8678"; private HTTPClient httpclient; /* * Not available if an external web server is used for testing. */ protected static ApplicationConfig ac; /* * Not available if an external web server is used for testing. */ protected static String contextName; /* * Not available if an external web server is used for testing. */ protected static EnvConfig wc; @BeforeClass public static void setUpBeforeClass() { initApp(); } @AfterClass public static void tearDownAfterClass() { if (ac != null) { DatabaseConfig.getInstance().restoreDefaultDatabaseConnectionName(); ApplicationConfig.getInstance().endApplication(); } } @Before public void setUp() { } @After public void tearDown() { if (httpclient != null) httpclient.shutDown(); } public void refreshHttpConnection() { if (httpclient != null) httpclient.shutDown(); httpclient = new HTTPClient(); } protected HTTPResponse fireHttpGetRequest(String uri) { uri = convert2URL(uri); refreshHttpConnection(); return httpclient.fireHttpGetRequest(uri); } protected HTTPResponse fireHttpPostRequest(String uri) { uri = convert2URL(uri); refreshHttpConnection(); return httpclient.fireHttpPostRequest(uri); } protected HTTPResponse fireHttpPostRequest(String uri, Map<String, String> params) { uri = convert2URL(uri); refreshHttpConnection(); return httpclient.fireHttpPostRequest(uri, params); } protected HTTPResponse fireHttpPutRequest(String uri) { uri = convert2URL(uri); refreshHttpConnection(); return httpclient.fireHttpPutRequest(uri); } protected HTTPResponse fireHttpPutRequest(String uri, Map<String, String> params) { uri = convert2URL(uri); refreshHttpConnection(); return httpclient.fireHttpPutRequest(uri, params); } protected HTTPResponse fireHttpDeleteRequest(String uri) { uri = convert2URL(uri); refreshHttpConnection(); return httpclient.fireHttpDeleteRequest(uri); } protected static void assertSuccess(HTTPResponse response) { assertEquals(200, response.getStatusCode()); } protected static void assertRedirectSuccess(HTTPResponse response) { assertEquals(302, response.getStatusCode()); } private static void initApp() { String appPath = System.getProperty("appPath"); String appPort = getAppPort(); String webStarted = System.getProperty("webStarted"); if (appPath == null) { throw new IllegalArgumentException("Please specify -DappPath=..."); } else { File dir = new File(appPath); if (!dir.exists()) { throw new IllegalArgumentException("App path \"" + appPath + "\" does not exist."); } } if (webStarted != null && (!webStarted.startsWith("$") || webStarted.equalsIgnoreCase("true"))) return; System.out.println("Starting functional test for " + appPath + " on port " + appPort); try { startUp(appPath, appPort); ac = ApplicationConfig.getInstance(); String runningEnvironment = ac.getRunningEnvironment(); if (!Constants.RUNNING_ENVIRONMENT_TEST.equals(runningEnvironment)) { ac.setRunningEnvironment(Constants.RUNNING_ENVIRONMENT_TEST); System.out.println("Scooter automatically switched to TEST environment."); } String testDB = DatabaseConfig.getInstance().tryToUseTestDatabaseConnection(); if (testDB != null) { System.out.println("Scooter automatically switched to test database \"" + testDB + "\" for testing."); boolean checkConn = validateConnection(testDB); if (!checkConn) { throw new IllegalArgumentException("Connection named '" + testDB + "' cannot be established."); } } else { System.out.println("Please make sure you are using a test database."); } contextName = ac.getContextName(); wc = EnvConfig.getInstance(); } catch (Throwable ex) { System.out.println("ERROR in starting up test server: " + ex.getMessage()); ex.printStackTrace(); } } private static void startUp(String appName, String appPort) throws Exception { ClassLoader cl = Thread.currentThread().getContextClassLoader(); String jettyMainClassName = "com.scooterframework.tools.webserver.StartServer"; if (appPort != null) { String[] args = { appName, appPort }; invokeMain(cl, jettyMainClassName, args); } else { String[] args = { appName }; invokeMain(cl, jettyMainClassName, args); } } private static void invokeMain(ClassLoader classloader, String classname, String[] args) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException { Class<?> invoked_class = classloader.loadClass(classname); Class<?>[] method_param_types = new Class[1]; method_param_types[0]= args.getClass(); Method main = invoked_class.getDeclaredMethod("main", method_param_types); Object[] method_params = new Object[1]; method_params[0] = args; main.invoke(null, method_params); } private static boolean validateConnection(String connectionName) { boolean check = false; Connection conn = null; try { conn = SqlExpressUtil.getConnection(connectionName); check = true; } catch (Exception ex) {} finally { DAOUtil.closeConnection(conn); } return check; } private void validateURI(String uri) { if (uri == null) throw new IllegalArgumentException("uri cannot be null."); if (!uri.startsWith("/")) throw new IllegalArgumentException("uri string must start with /."); } private String convert2URL(String uri) { if (uri.startsWith("http")) return uri; validateURI(uri); String appPath = System.getProperty("appPath"); String appPort = getAppPort(); String appName = ""; File dir = new File(appPath); try { String path = dir.getCanonicalPath(); appName = path.substring(path.lastIndexOf(File.separatorChar) + 1); } catch (IOException ex) { System.out.println("Error in convert2URL(: " + ex.getMessage()); } StringBuilder sb = new StringBuilder(); sb.append(DEFAULT_TEST_HOST).append(':').append(appPort).append('/'); sb.append(appName).append(uri); return sb.toString(); } private static String getAppPort() { String appPort = System.getProperty("appPort", DEFAULT_TEST_PORT); if (appPort == null || appPort.startsWith("$")) return DEFAULT_TEST_PORT; return appPort; } }