/*******************************************************************************
* Copyright (c) 2012 VMWare, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMWare, Inc. - initial API and implementation
*******************************************************************************/
package org.grails.ide.eclipse.runonserver.test;
import static org.springsource.ide.eclipse.commons.tests.util.StsTestUtil.assertNoErrors;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.util.Properties;
import junit.framework.AssertionFailedError;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.jobs.IJobManager;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;
import org.eclipse.wst.server.core.IModule;
import org.eclipse.wst.server.core.IModuleArtifact;
import org.eclipse.wst.server.core.IServer;
import org.eclipse.wst.server.core.IServerListener;
import org.eclipse.wst.server.core.IServerWorkingCopy;
import org.eclipse.wst.server.core.ServerEvent;
import org.eclipse.wst.server.core.ServerPort;
import org.eclipse.wst.server.core.ServerUtil;
import org.eclipse.wst.server.core.internal.ServerPlugin;
import org.eclipse.wst.server.core.util.WebResource;
import org.springsource.ide.eclipse.commons.core.ZipFileUtil;
import org.springsource.ide.eclipse.commons.frameworks.test.util.ACondition;
import org.springsource.ide.eclipse.commons.frameworks.test.util.SWTBotUtils;
import org.springsource.ide.eclipse.commons.tests.util.StsTestUtil;
import org.grails.ide.eclipse.commands.GrailsCommand;
import org.grails.ide.eclipse.commands.GrailsCommandFactory;
import org.grails.ide.eclipse.commands.GrailsCommandUtils;
import org.grails.ide.eclipse.commands.GrailsExecutor;
import org.grails.ide.eclipse.commands.GrailsExecutorListener;
import org.grails.ide.eclipse.core.GrailsCoreActivator;
import org.grails.ide.eclipse.core.internal.GrailsNature;
import org.grails.ide.eclipse.core.launch.SynchLaunch.ILaunchResult;
import org.grails.ide.eclipse.core.model.GrailsBuildSettingsHelper;
import org.grails.ide.eclipse.core.model.GrailsVersion;
import org.grails.ide.eclipse.runonserver.GrailsAppModuleDelegate;
import org.grails.ide.eclipse.runonserver.GrailsAppModuleFactoryDelegate;
import org.grails.ide.eclipse.runonserver.RunOnServerProperties;
import org.grails.ide.eclipse.test.util.GrailsTest;
import com.springsource.sts.server.insight.internal.ui.InsightTcServerCallback;
import com.springsource.sts.server.tc.tests.support.TcServerFixture;
import com.springsource.sts.server.tc.tests.support.TcServerHarness;
/**
* Note this test must *not* be run in the UI thread. This will cause deadlock
* and test failure.
*
* @author Kris De Volder
* @author Andrew Eisenberg
*/
public abstract class RunOnServerTestTemplate extends GrailsTest {
/**
* An executor listener that checks whether an expected command is executed.
*
* @author Kris De Volder
* @created 2011-01-19
*/
public class ExpectedCommand extends GrailsExecutorListener {
boolean executed = false;
Throwable caught = null;
private final IProject expectedProject;
private final String expectedCmd;
private final String expectedOutputSnippet;
public ExpectedCommand(IProject project, String cmd,
String outputSnippet) {
this.expectedProject = project;
this.expectedCmd = cmd;
this.expectedOutputSnippet = outputSnippet;
}
@Override
public void commandExecuted(GrailsCommand cmd, ILaunchResult result) {
try {
debug("Expected: " + expectedCmd);
debug("Command: " + cmd);
assertFalse("Expected command executed more than once: " + cmd,
executed);
assertEquals(expectedCmd, cmd.getCommand());
assertEquals(expectedProject, cmd.getProject());
assertContains(expectedOutputSnippet, result.getOutput());
assertTrue(result.isOK());
executed = true;
} catch (Throwable e) {
caught = e;
}
}
@Override
public void commandExecuted(GrailsCommand cmd, Throwable thrown) {
caught = thrown;
}
public void assertOk() throws Throwable {
if (caught != null) {
throw caught;
} else if (!executed) {
throw new AssertionFailedError("Exected command '"
+ expectedCmd + "' was not executed");
}
}
}
protected static TcServerFixture serverFixture = null;
private static URL serverZip = null;
public static void debug(String string) {
System.err.println(string);
}
/**
* If this is set to true, test won't create the test project if it already
* exists. (for faster test running).
* <p>
* Tests that make changes that may cause other tests to break should set
* this to false explicitly.
*/
private static boolean reusableTestProject = false;
private static final int SECOND = 1000;
public static void startServer(IServer server) throws CoreException,
OperationCanceledException, InterruptedException {
Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_BUILD, null);
// TODO: The above 'join' makes the test pass, but shouldn't be needed
// See https://issuetracker.springsource.com/browse/STS-1581
debug("Starting server '" + server + "'...");
final boolean[] started = new boolean[] { false };
IServerListener listener = new IServerListener() {
boolean starting = false;
public void serverChanged(ServerEvent event) {
IServer server = event.getServer();
debug("ServerEvent: " + event);
if (server.getServerState() == IServer.STATE_STARTING) {
starting = true;
debug("Server " + server + " is starting...");
}
if (server.getServerState() == IServer.STATE_STARTED
&& !started[0]) {
debug("Server " + server + " started");
// debug(SWTBotUtils.getConsoleText(new SWTWorkbenchBot()));
started[0] = true;
}
if (starting
&& server.getServerState() == IServer.STATE_STOPPED) {
debug("Server " + server + " stopped!");
try {
debug(SWTBotUtils.getConsoleText(new SWTWorkbenchBot()));
} catch (Throwable e) {
}
}
}
};
server.addServerListener(listener);
server.start(ILaunchManager.RUN_MODE, new NullProgressMonitor());
long endTime = System.currentTimeMillis() + 180 * SECOND;
while (System.currentTimeMillis() < endTime && !started[0]) {
System.out.println("Waiting for server to start ...");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
}
assertTrue("Server '" + server
+ "' didn't start within a reasonable time", started[0]);
server.removeServerListener(listener);
}
// protected static TcServerFixture serverFixture210 = new
// TcServerFixture(Activator.PLUGIN_ID, "com.springsource.tcserver.60",
// "tc-server-developer-2.1.0.RELEASE");
private IWorkspace ws = null;
public IProject project = null;
private IServer server;
private ExpectedCommand expectedCommand;
private TcServerHarness harness;
@Override
public void setUp() throws Exception {
super.setUp();
setJava16Compliance();
ensureDefaultGrailsVersion(GrailsVersion.MOST_RECENT);
// On the build server all output to System.out is eaten, which is
// annoying if you need to
// determine what went wrong. Send all output to system.err.
// savedOut = System.out;
// System.setOut(System.err);
if (!reusableTestProject) {
setupclass();
}
InsightTcServerCallback.disableDialog(false); // Dialog would freeze
// some tests, waiting
// for user to click.
ws = ResourcesPlugin.getWorkspace();
StsTestUtil.setAutoBuilding(false);
ensureProject("gTunes");
RunOnServerProperties.setEnv(project, "dev");
}
private void setupclass() throws CoreException, IOException {
reusableTestProject = true;
IProject gTunes = ResourcesPlugin.getWorkspace().getRoot()
.getProject("gTunes");
if (gTunes.exists()) {
gTunes.delete(true, true, null);
}
}
protected void stopServer() {
if (server!=null) {
if (server.getServerState() != IServer.STATE_STOPPED) {
server.stop(true);
}
long endTime = System.currentTimeMillis() + 120 * SECOND;
while (server.getServerState() != IServer.STATE_STOPPED) {
System.out.println("Waiting for server to stop");
try {
Thread.sleep(2 * SECOND);
} catch (Exception e) {
}
}
}
}
protected IProject ensureProject(String projectName) throws Exception {
GrailsVersion version = GrailsVersion.getDefault(); // Set in setUp
// method.
IJobManager jm = Job.getJobManager();
ISchedulingRule rule = ResourcesPlugin.getWorkspace().getRuleFactory()
.buildRule();
try {
jm.beginRule(rule, new NullProgressMonitor());
project = ws.getRoot().getProject(projectName);
if (!project.exists()) {
IPath workspaceLoc = ws.getRoot().getLocation();
URL projectZip = getProjectZip(projectName, version);
ZipFileUtil.unzip(projectZip, workspaceLoc.append(projectName)
.toFile(), projectName, null);
project.create(null);
GrailsCommandUtils.eclipsifyProject(null, true, project);
}
return project;
} finally {
jm.endRule(rule);
}
}
private IModule getModule() {
IModule[] modules = ServerUtil.getModules(project);
assertEquals(1, modules.length);
IModule module = modules[0];
return module;
}
/**
* @param module
* @throws CoreException
*/
private void serverAddModule(IModule module) throws CoreException {
IServerWorkingCopy wc = server.createWorkingCopy();
wc.modifyModules(
// Add:
new IModule[] { module },
// Remove:
new IModule[0], null);
wc.save(true, null);
}
private void serverRemoveModules() throws CoreException {
IServerWorkingCopy wc = server.createWorkingCopy();
IModule[] modules = wc.getModules();
if (modules == null || modules.length == 0) {
return;
}
wc.modifyModules(new IModule[0], modules, null);
wc.save(true, null);
}
private void assertNotContains(String expect, String actual) {
assertFalse(actual, actual.contains(expect));
}
private String getPageContent(URL url) throws Exception {
Object content = null;
long endTime = System.currentTimeMillis() + 30 * SECOND;
boolean ok = false;
Throwable e = null; // keeps last exception in the loop below.
// Loop that keeps trying to get the contents...
while (!ok && System.currentTimeMillis() < endTime) {
try {
Thread.sleep(2000);
content = url.getContent();
ok = content != null;
} catch (Throwable _e) {
e = _e;
System.out.println(_e);
ok = false;
}
}
if (!ok && e != null) {
// For more informative test failure, rethrow last exception from
// the loop (if we have one).
StsTestUtil.rethrow(e);
}
assertTrue("Couldn't get content for: " + url, ok && content != null);
InputStream in = (InputStream) content;
BufferedReader read = new BufferedReader(new InputStreamReader(in));
String s = null;
StringBuffer result = new StringBuffer();
while ((s = read.readLine()) != null) {
result.append(s);
result.append("\n");
}
return result.toString();
}
// //////////////////////////////////////////////////////////////////////////////////////////////////
public void testScaffolding() throws Exception {
assertTrue(GrailsNature.isGrailsAppProject(project));
assertNoErrors(project);
// The tests assume that created test server only has one port for http
// contents and that
// this port is set to the number passed into getTestServer. Do a simple
// check to see if
// this assumption holds.
int port = 12345;
server = getTestServer(port);
ServerPort[] ports = server.getServerPorts(new NullProgressMonitor());
assertEquals(
"The test server has more than one port, it should only have a single http port",
1, ports.length);
assertEquals("The server should have an http port", "HTTP",
ports[0].getProtocol());
assertEquals("Setting the server http port didn't work", port,
ports[0].getPort());
}
/**
* Test if the Module artifact adapter that makes run on server appear is
* active and returns the expected module artifact for a grails project.
*
* @throws Exception
*/
public void testModuleArtifactAdapter() throws Exception {
// As describe here:
// http://www.eclipse.org/webtools/wst/components/server/runOnServer.html
// To have Run On Server menu item appear on a grails project, a grails
// project needs to
// 1) Addapt to ILaunchable
// 2) Provide enablement by contributing to
// "org.eclipse.wst.server.ui.moduleArtifactAdapters" extension point
// 3) Addapt to IModuleArtifact
IModuleArtifact[] marts = ServerPlugin.getModuleArtifacts(project);
assertEquals(1, marts.length);
WebResource mart = (WebResource) marts[0];
IModule module = mart.getModule();
assertEquals(project.getName(), module.getName());
assertEquals(Path.EMPTY, mart.getPath());
}
/**
* Test whether a module is associated with a GrailsApp project.
*/
public void testGrailsAppHasModule() throws Exception {
IModule module = getModule(); //
assertEquals(GrailsAppModuleFactoryDelegate.TYPE, module
.getModuleType().getId());
assertEquals(project, module.getProject());
assertEquals(project.getName(), module.getName());
}
/**
* Test for https://issuetracker.springsource.com/browse/STS-1539 and
* https://issuetracker.springsource.com/browse/STS-1518
* <p>
* Both involve the audit logging plugin.
* <p>
* Note: this test will fail unless using a version of Greclipse that has
* the
*/
public void testAuditLoggingPlugin() throws Throwable {
try {
Class.forName("org.codehaus.jdt.groovy.internal.compiler.ast.GrailsGlobalPluginAwareEntityInjector");
} catch (ClassNotFoundException e) {
fail("This test requires a more recent version of Groovy Eclipse");
}
StsTestUtil.setAutoBuilding(true);
int port = StsTestUtil.findFreeSocketPort();
System.out.println("Creating server with port: " + port);
server = getTestServer(port);
IModule module = getModule();
serverAddModule(module);
System.out.println("Installing plugin audit logging...");
ILaunchResult result = GrailsCommandFactory.installPlugin(project,
"audit-logging").synchExec();
reusableTestProject = false; // Other tests don't expect audit-logging
// plugin
System.out.println("Refreshing dependencies...");
GrailsCommandUtils.refreshDependencies(JavaCore.create(project), false);
System.out.println("Installed plugin audit logging... DONE");
System.out.println("Starting server...");
startServer(server);
System.out.println("Starting server... DONE");
String pageContent = getPageContent(new URL("http://localhost:" + port
+ "/" + project.getName()));
assertContains(">gtunes.SongController</a></li>", pageContent);
assertContains(
">org.codehaus.groovy.grails.plugins.orm.auditable.AuditLogEventController</a></li>",
pageContent);
pageContent = getPageContent(new URL("http://localhost:" + port + "/"
+ project.getName() + "/auditLogEvent/list"));
assertContains("<h1>AuditLogEvent List</h1>", pageContent);
stopServer();
}
/**
* Test whether this module can be deployed to a TcServer instance.
*
* @throws Throwable
*/
public void testGrailsAppCanDeploy() throws Throwable {
int port = StsTestUtil.findFreeSocketPort();
server = getTestServer(port);
IModule module = getModule();
// Publishing and starting server makes expected URL return expected
// content?
serverAddModule(module);
// if (server.shouldPublish()) {
// server.publish(IServer.PUBLISH_AUTO, null);
// }
startServer(server);
String content = getPageContent(new URL("http://localhost:" + port
+ "/" + project.getName()));
System.out.println("Web content = " + content);
assertContains("Welcome to Grails", content);
assertContains(">gtunes.SongController</a></li>", content);
String songController = getPageContent(new URL("http://localhost:"
+ port + "/" + project.getName() + "/song"));
System.out.println("Song controller = " + songController);
}
/**
* Test whether this module can be deployed to a TcServer instance.
*
* @throws Throwable
*/
public void testGrailsAppContextRoot() throws Throwable {
// Change the "app.context" property
String contextRoot = "gTunes-blah";
GrailsBuildSettingsHelper.setApplicationProperty(project,
"app.context", contextRoot);
reusableTestProject = false;
int port = StsTestUtil.findFreeSocketPort();
server = getTestServer(port);
IModule module = getModule();
// Publishing and starting server makes expected URL return expected
// content?
serverAddModule(module);
// if (server.shouldPublish()) {
// server.publish(IServer.PUBLISH_AUTO, null);
// }
startServer(server);
String content = getPageContent(new URL("http://localhost:" + port
+ "/" + contextRoot));
System.out.println("Web content = " + content);
assertContains("Welcome to Grails", content);
assertRegexp("<li class=\"controller\"><a href=\"/" + contextRoot
+ "/song.*\">gtunes.SongController</a></li>", content);
if (GrailsVersion.getGrailsVersion(project).compareTo(
GrailsVersion.V_2_0_0) >= 0) {
String songController = getPageContent(new URL("http://localhost:"
+ port + "/" + contextRoot + "/song/index"));
System.out.println("Song controller = " + songController);
} else {
String songController = getPageContent(new URL("http://localhost:"
+ port + "/" + contextRoot + "/song"));
System.out.println("Song controller = " + songController);
}
}
/**
* Test related to STS-1361
*
* @throws Throwable
*/
public void testPublishDeleteRecreateRepublish() throws Throwable {
int port = StsTestUtil.findFreeSocketPort();
server = getTestServer(port);
IModule module = getModule();
// Publishing and starting server makes expected URL return expected
// content?
serverAddModule(module);
startServer(server);
String content = getPageContent(new URL("http://localhost:" + port
+ "/" + project.getName()));
System.out.println("Web content = " + content);
assertContains("Welcome to Grails", content);
assertContains(">gtunes.SongController</a></li>", content);
stopServer();
server.getModules();
project.delete(true, false, null);
reusableTestProject = false;
// Check: gTunes module should disappear from server "soon".
new ACondition() {
@Override
public boolean test() throws Exception {
return server.getModules().length == 0;
}
}.waitFor(3000);
GrailsCommandFactory.createApp("gTunes").synchExec();
project = ws.getRoot().getProject("gTunes");
project.refreshLocal(IResource.DEPTH_INFINITE, null);
GrailsCommandUtils.eclipsifyProject(null, true, project);
module = getModule();
serverAddModule(module);
startServer(server);
content = getPageContent(new URL("http://localhost:" + port + "/"
+ project.getName()));
System.out.println("Web content = " + content);
assertContains("Welcome to Grails", content);
assertNotContains(">gtunes.SongController</a></li>", content);
}
/**
* Test related to STS-1339: adding a new controller with server running,
* should make this new controller appear in the browser (eventually).
*/
public void testAddNewController() throws Throwable {
StsTestUtil.setAutoBuilding(true); // More likely the way user would be
// working is with this on.
final int port = StsTestUtil.findFreeSocketPort();
server = getTestServer(port);
IModule module = getModule();
// Publishing and starting server makes expected URL return expected
// content?
serverAddModule(module);
startServer(server);
String content = getPageContent(new URL("http://localhost:" + port
+ "/" + project.getName()));
System.out.println("Web content = " + content);
assertContains("Welcome to Grails", content);
assertContains(">gtunes.SongController</a></li>", content);
assertFalse(
"Books should only be added later, they are already there!",
content.contains(">gtunes.BookController</a></li>"));
System.out.println("Adding 'books' to the gTunes app");
reusableTestProject = false; // Other tests don't expect books
addBooksToGTunes();
// Check: book controller should appear on the main gTunes page
// eventually.
new ACondition("Loading new BookController") {
String content;
@Override
public boolean test() throws Exception {
content = getPageContent(new URL("http://localhost:" + port
+ "/" + project.getName()));
return content.contains(">gtunes.BookController</a></li>");
}
@Override
public String getMessage() {
if (content != null) {
return content;
} else {
return super.getMessage();
}
}
}.waitFor(180000);
}
/**
* Adds "Book" domain class and controller to the gTunes project.
*
* @throws CoreException
*/
protected void addBooksToGTunes() throws CoreException {
GrailsTest.createResource(project,
"grails-app/domain/gtunes/Book.groovy", "package gtunes\n"
+ "\n" + "class Book {\n" + "\n"
+ " static constraints = {\n" + " }\n" + " \n"
+ " String author\n" + " String title\n" + " \n"
+ "}\n");
GrailsTest.createResource(project,
"grails-app/controllers/gtunes/BookController.groovy",
"package gtunes\n" + "\n" + "class BookController {\n" + "\n"
+ " def scaffold = Book\n" + "}\n");
}
/**
* Test whether setting the 'env' property on a project results in a
* building the war file with the correct environment.
*
* @throws Throwable
*/
public void testEnv() throws Throwable {
File warFile = GrailsAppModuleDelegate.getWarFile(project);
StsTestUtil.setAutoBuilding(true); // More likely the way user would be
// working is with this on.
int port = StsTestUtil.findFreeSocketPort();
System.out.println("Using port: " + port);
server = getTestServer(port);
IModule module = getModule();
serverAddModule(module);
RunOnServerProperties.setEnv(project,
"force war build next time by bashing something into env");
// Check with 'dev' environment
expectedCommand(project, "dev war " + warFile,
"Environment set to development");
RunOnServerProperties.setEnv(project, "dev");
startServer(server);
getPageContent(new URL("http://localhost:" + port + "/"
+ project.getName()));
stopServer();
assertExpectedCommand();
// Check with 'prod' environment
port = StsTestUtil.findFreeSocketPort();
setHttpPort(server, port);
System.out.println("Using port: " + port);
expectedCommand(project, "prod war " + warFile,
"Environment set to production");
RunOnServerProperties.setEnv(project, "prod");
startServer(server);
getPageContent(new URL("http://localhost:" + port + "/"
+ project.getName()));
stopServer();
assertExpectedCommand();
// Check with 'custom' environment
port = StsTestUtil.findFreeSocketPort();
setHttpPort(server, port);
System.out.println("Using port: " + port);
expectedCommand(project, "war " + warFile,
"Environment set to blah blah");
RunOnServerProperties.setEnv(project, "blah blah");
startServer(server);
getPageContent(new URL("http://localhost:" + port + "/"
+ project.getName()));
stopServer();
assertExpectedCommand();
}
private void expectedCommand(IProject project, String cmd,
String outputSnippet) {
this.expectedCommand = new ExpectedCommand(project, cmd, outputSnippet);
GrailsExecutor.setListener(expectedCommand);
}
private void assertExpectedCommand() throws Throwable {
this.expectedCommand.assertOk();
}
/**
* What's the default grails version that we expect new projects to be
* created with.
*/
private String grailsVersion() {
return GrailsCoreActivator.getDefault().getInstallManager()
.getDefaultGrailsInstall().getVersionString();
}
/**
* Modifies properties file in a TcServer configuration folder to change the
* property that defines the httpPort number the server will be using.
* <p>
* Note that the implementation of this is hacky in that it makes various
* assumptions about the server and how it is being configured. It should
* work for the test fixtures in this suite (and testScaffolding checks
* whether it works).
*/
protected void setHttpPort(IServer server, int port) throws IOException,
CoreException {
IFile propFile = server.getServerConfiguration().getFile(
"catalina.properties");
IServerWorkingCopy wc = server.createWorkingCopy();
Properties props = new Properties();
InputStream in = null;
try {
in = propFile.getContents();
props.load(in);
} finally {
if (in != null) {
in.close();
}
}
props.put("bio.http.port", "" + port);
OutputStream out = null;
try {
out = new FileOutputStream(propFile.getLocation().toFile());
props.store(out, "# modified by RunOnServer tests");
} finally {
if (out != null) {
out.close();
}
}
propFile.refreshLocal(IResource.DEPTH_ONE, new NullProgressMonitor());
// Need to touch and save server working copy for STS to clue in that
// server port has changed
// See https://issuetracker.springsource.com/browse/STS-1535
wc.setServerConfiguration(server.getServerConfiguration());
wc.save(true, new NullProgressMonitor());
}
protected IServer getTestServer(int httpPort) throws Exception {
IServer created = getHarness().createServer(TcServerFixture.INST_INSIGHT);
setHttpPort(created, httpPort);
return created;
}
private TcServerHarness getHarness() {
if (harness==null) {
harness = new TcServerHarness(getTcServerFixture());
}
return harness;
}
@Override
protected void tearDown() throws Exception {
GrailsExecutor.setListener(null);
stopServer();
if (harness!=null) {
harness.dispose();
}
super.tearDown();
}
protected abstract TcServerFixture getTcServerFixture();
// protected URL getResourceURL(String resourcePath) {
// URL result = this.getClass().getClassLoader().getResource(resourcePath);
// Assert.isNotNull("Couldn't find resource: " + resourcePath, result);
// return result;
// }
// TODO: Add some test that put stuff into a form to see if basic
// functionality of the test app is working
// See here on help for submitting form requests:
// http://stackoverflow.com/questions/2793150/how-to-use-java-net-urlconnection-to-fire-and-handle-http-requests
}