changeset 979:930ef1c74e99

Integeration test for WAR. Reviewed-by: neugens, jerboaa Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-February/005732.html
author Roman Kennke <rkennke@redhat.com>
date Tue, 19 Feb 2013 14:24:46 +0100
parents 92588dada473
children c464fa479682
files common/core/src/main/java/com/redhat/thermostat/common/ApplicationInfo.java common/core/src/main/resources/com/redhat/thermostat/app-info.properties distribution/pom.xml distribution/src/test/java/com/redhat/thermostat/distribution/CliTest.java distribution/src/test/java/com/redhat/thermostat/distribution/IntegrationTest.java distribution/src/test/java/com/redhat/thermostat/distribution/StorageConnectionTest.java distribution/src/test/java/com/redhat/thermostat/distribution/StorageTest.java distribution/src/test/java/com/redhat/thermostat/distribution/VmCommandsTest.java integration-tests/pom.xml integration-tests/src/test/java/com/redhat/thermostat/itest/CliTest.java integration-tests/src/test/java/com/redhat/thermostat/itest/IntegrationTest.java integration-tests/src/test/java/com/redhat/thermostat/itest/StorageConnectionTest.java integration-tests/src/test/java/com/redhat/thermostat/itest/StorageTest.java integration-tests/src/test/java/com/redhat/thermostat/itest/VmCommandsTest.java integration-tests/src/test/java/com/redhat/thermostat/itest/WebAppTest.java pom.xml web/client/src/main/java/com/redhat/thermostat/web/client/internal/WebStorage.java web/server/pom.xml
diffstat 18 files changed, 1024 insertions(+), 706 deletions(-) [+]
line wrap: on
line diff
--- a/common/core/src/main/java/com/redhat/thermostat/common/ApplicationInfo.java	Fri Feb 15 17:24:29 2013 -0500
+++ b/common/core/src/main/java/com/redhat/thermostat/common/ApplicationInfo.java	Tue Feb 19 14:24:46 2013 +0100
@@ -77,6 +77,10 @@
         return new Version();
     }
 
+    public String getMavenVersion() {
+        return appInfo.getProperty("APP_VERSION");
+    }
+
     public String getDescription() {
         return t.localize(LocaleResources.APPLICATION_INFO_DESCRIPTION);
     }
--- a/common/core/src/main/resources/com/redhat/thermostat/app-info.properties	Fri Feb 15 17:24:29 2013 -0500
+++ b/common/core/src/main/resources/com/redhat/thermostat/app-info.properties	Tue Feb 19 14:24:46 2013 +0100
@@ -3,4 +3,4 @@
 APP_EMAIL = ${thermostat.email}
 APP_WEBSITE = ${thermostat.url}
 APP_COPYRIGHT = Copyright 2012, 2013 Red Hat, Inc.
-
+APP_VERSION = ${project.version}
--- a/distribution/pom.xml	Fri Feb 15 17:24:29 2013 -0500
+++ b/distribution/pom.xml	Tue Feb 19 14:24:46 2013 +0100
@@ -309,11 +309,6 @@
       <artifactId>junit</artifactId>
       <scope>test</scope>
     </dependency>
-    <dependency>
-      <groupId>net.sourceforge.expectj</groupId>
-      <artifactId>expectj</artifactId>
-      <scope>test</scope>
-    </dependency>
 
     <!-- thermostat parts -->
     <dependency>
--- a/distribution/src/test/java/com/redhat/thermostat/distribution/CliTest.java	Fri Feb 15 17:24:29 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,196 +0,0 @@
-/*
- * Copyright 2012, 2013 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat 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, or (at your
- * option) any later version.
- *
- * Thermostat 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 Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.distribution;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.io.IOException;
-
-import org.junit.Ignore;
-import org.junit.Test;
-
-import expectj.Spawn;
-import expectj.TimeoutException;
-
-/**
- * Integration tests to exercise the basics of the thermostat command line.
- */
-public class CliTest extends IntegrationTest {
-
-    @Test
-    public void testExpectIsSane() throws Exception {
-        Spawn shell = spawnThermostat();
-
-        try {
-            shell.expect("some-random-text-that-is-not-really-possible");
-            fail("should never match");
-        } catch (IOException endOfStream) {
-            assertTrue(endOfStream.getMessage().contains("End of stream reached, no match found"));
-        }
-        shell.expectClose();
-    }
-
-    @Test
-    public void testSimpleInvocationPrintsHelp() throws Exception {
-        Spawn shell = spawnThermostat();
-        shell.expectClose();
-
-        String stdOut = shell.getCurrentStandardOutContents();
-
-        assertMatchesHelpCommandList(stdOut);
-
-        String stdErr = shell.getCurrentStandardErrContents();
-        assertEquals(stdErr, "");
-    }
-
-    @Test
-    public void testHelpCommandInvocation() throws Exception {
-        Spawn shell = spawnThermostat("help");
-        shell.expectClose();
-
-        String stdOut = shell.getCurrentStandardOutContents();
-        String stdErr = shell.getCurrentStandardErrContents();
-
-        assertMatchesHelpCommandList(stdOut);
-        assertEquals(stdErr, "");
-    }
-
-    @Test
-    public void testHelpOnHelp() throws Exception {
-        Spawn shell = spawnThermostat("help", "help");
-        shell.expectClose();
-
-        String stdOut = shell.getCurrentStandardOutContents();
-        String stdErr = shell.getCurrentStandardErrContents();
-
-        String[] lines = stdOut.split("\n");
-        String usage = lines[0];
-        assertEquals("usage: thermostat help [command-name]", usage);
-
-        assertEquals(stdErr, "");
-    }
-
-    @Test
-    public void testVersionArgument() throws Exception {
-        Spawn shell = spawnThermostat("--version");
-        shell.expectClose();
-
-        String stdOut = shell.getCurrentStandardOutContents();
-        String stdErr = shell.getCurrentStandardErrContents();
-
-        assertTrue(stdOut.matches("Thermostat version \\d+\\.\\d+\\.\\d+\n"));
-        assertEquals(stdErr, "");
-    }
-
-    @Test
-    public void testShell() throws Exception {
-        Spawn shell = spawnThermostat("shell");
-
-        shell.expect(SHELL_PROMPT);
-        shell.send("help\n");
-
-        shell.expect(SHELL_PROMPT);
-
-        assertMatchesHelpCommandList(shell.getCurrentStandardOutContents());
-
-        shell.send("exit\n");
-
-        shell.expectClose();
-    }
-
-    @Test
-    public void testShellHelp() throws Exception {
-        Spawn shell = spawnThermostat("help", "shell");
-        shell.expectClose();
-
-        String stdOut = shell.getCurrentStandardOutContents();
-
-        String[] lines = stdOut.split("\n");
-        String usage = lines[0];
-        assertTrue(usage.matches("^usage: thermostat shell$"));
-        String description = lines[1];
-        assertTrue(description.matches("^\\s+launches the Thermostat interactive shell$"));
-        assertTrue(lines[2].matches("thermostat shell"));
-    }
-
-    @Test
-    public void testShellUnrecognizedArgument() throws Exception {
-        Spawn shell = spawnThermostat("shell", "--foo");
-        shell.expectErr("Unrecognized option: --foo");
-        shell.expectClose();
-    }
-
-    @Test
-    public void testUnrecognizedEventsInShell() throws IOException, TimeoutException {
-        // test '!' events
-        Spawn shell = spawnThermostat("shell");
-
-        shell.expect(SHELL_PROMPT);
-        shell.send("what!?!\n");
-        shell.expect(SHELL_PROMPT);
-        shell.send("exit\n");
-
-        assertTrue(shell.getCurrentStandardErrContents().contains("!?!: event not found"));
-        assertNoExceptions(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents());
-    }
-
-    @Test
-    public void testInvalidCommand() throws Exception {
-        Spawn shell = spawnThermostat("foobar", "baz");
-
-        // TODO should this be stderr?
-        shell.expect("unknown command 'foobar'");
-        shell.expectClose();
-
-        String stdOut = shell.getCurrentStandardOutContents();
-
-        assertMatchesHelpCommandList(stdOut);
-    }
-
-    private static void assertMatchesHelpCommandList(String actual) {
-        assertTrue(actual.contains("list of commands"));
-        assertTrue(actual.contains("help"));
-        assertTrue(actual.contains("agent"));
-        assertTrue(actual.contains("gui"));
-        assertTrue(actual.contains("ping"));
-        assertTrue(actual.contains("shell"));
-    }
-
-}
-
--- a/distribution/src/test/java/com/redhat/thermostat/distribution/IntegrationTest.java	Fri Feb 15 17:24:29 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,166 +0,0 @@
-/*
- * Copyright 2012, 2013 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat 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, or (at your
- * option) any later version.
- *
- * Thermostat 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 Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.distribution;
-
-import static org.junit.Assert.assertFalse;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.List;
-
-import com.redhat.thermostat.common.utils.StreamUtils;
-
-import expectj.Executor;
-import expectj.ExpectJ;
-import expectj.Spawn;
-
-/**
- * Helper methods to support writing an integration test.
- */
-public class IntegrationTest {
-
-    public static final long TIMEOUT_IN_SECONDS = 30;
-
-    public static final String SHELL_PROMPT = "Thermostat >";
-
-    public static String getThermostatExecutable() {
-        return "target/bin/thermostat";
-    }
-
-    public static String getStorageDataDirectory() {
-        return "target/storage/db";
-    }
-
-    public static Spawn spawnThermostat(String... args) throws IOException {
-        ExpectJ expect = new ExpectJ(TIMEOUT_IN_SECONDS);
-        StringBuilder result = new StringBuilder(getThermostatExecutable());
-        if (args != null) {
-            for (String arg : args) {
-                result.append(" ").append(arg);
-            }
-        }
-        String toExecute = result.toString();
-        //System.out.println("executing: '" + toExecute + "'");
-        return expect.spawn(toExecute);
-    }
-
-    public static Spawn spawn(List<String> args) throws IOException {
-        ExpectJ expect = new ExpectJ(TIMEOUT_IN_SECONDS);
-        StringBuilder result = new StringBuilder();
-        for (String arg : args) {
-            result.append(arg).append(" ");
-        }
-        return expect.spawn(result.substring(0, result.length() - 1));
-    }
-
-    public static Spawn spawn(Executor executor) throws IOException {
-        ExpectJ expect = new ExpectJ(TIMEOUT_IN_SECONDS);
-        return expect.spawn(executor);
-    }
-
-    /**
-     * Kill the process and all its children, recursively. Sends SIGTERM.
-     */
-    public static void killRecursively(Process process) throws Exception {
-        killRecursively(getPid(process));
-    }
-
-    private static void killRecursively(int pid) throws Exception {
-        List<Integer> childPids = findChildPids(pid);
-        for (Integer childPid : childPids) {
-            killRecursively(childPid);
-        }
-        killProcess(pid);
-    }
-
-    private static void killProcess(int processId) throws Exception {
-        Runtime.getRuntime().exec("kill " + processId).waitFor();
-    }
-
-    private static List<Integer> findChildPids(int processId) throws IOException {
-        String children = new String(StreamUtils.readAll(Runtime.getRuntime().exec("ps --ppid " + processId + " -o pid=").getInputStream()));
-        String[] childPids = children.split("\n");
-        List<Integer> result = new ArrayList<>();
-        for (String childPid : childPids) {
-            String pidString = childPid.trim();
-            if (pidString.length() == 0) {
-                continue;
-            }
-            try {
-                result.add(Integer.parseInt(pidString));
-            } catch (NumberFormatException nfe) {
-                System.err.println(nfe);
-            }
-        }
-        return result;
-    }
-
-    private static int getPid(Process process) throws Exception {
-        final String UNIX_PROCESS_CLASS = "java.lang.UNIXProcess";
-        if (!process.getClass().getName().equals(UNIX_PROCESS_CLASS)) {
-            throw new IllegalArgumentException("can only kill " + UNIX_PROCESS_CLASS + "; input is a " + process.getClass());
-        }
-
-        Class<?> processClass = process.getClass();
-        Field pidField = processClass.getDeclaredField("pid");
-        pidField.setAccessible(true);
-        return (int) pidField.get(process);
-    }
-
-    public static void deleteFilesUnder(String path) throws IOException {
-        String[] filesToDelete = new File(path).list();
-        for (String toDelete : filesToDelete) {
-            if (!new File(path, toDelete).delete()) {
-                throw new IOException("cant delete: '" + new File(path, toDelete).toString() + "'.");
-            }
-        }
-    }
-
-    public static void assertCommandIsFound(String stdOutContents, String stdErrContents) {
-        assertFalse(stdOutContents.contains("unknown command"));
-        assertFalse(stdErrContents.contains("unknown command"));
-    }
-
-    public static void assertNoExceptions(String stdOutContents, String stdErrContents) {
-        assertFalse(stdOutContents.contains("Exception"));
-        assertFalse(stdErrContents.contains("Exception"));
-    }
-
-}
-
--- a/distribution/src/test/java/com/redhat/thermostat/distribution/StorageConnectionTest.java	Fri Feb 15 17:24:29 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,114 +0,0 @@
-/*
- * Copyright 2012, 2013 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat 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, or (at your
- * option) any later version.
- *
- * Thermostat 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 Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.distribution;
-
-import java.io.IOException;
-
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Ignore;
-import org.junit.Test;
-
-import expectj.ExpectJException;
-import expectj.Spawn;
-import expectj.TimeoutException;
-
-public class StorageConnectionTest extends IntegrationTest {
-
-    @BeforeClass
-    public static void setUpOnce() throws IOException, TimeoutException, ExpectJException {
-        Spawn storage = spawnThermostat("storage", "--start");
-        storage.expect("pid:");
-        storage.expectClose();
-
-        assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents());
-    }
-
-    @AfterClass
-    public static void tearDownOnce() throws IOException, TimeoutException, ExpectJException {
-        Spawn storage = spawnThermostat("storage", "--stop");
-        storage.expect("server shutdown complete");
-        storage.expectClose();
-
-        assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents());
-    }
-
-    @Test
-    public void testConnect() throws ExpectJException, TimeoutException, IOException {
-        Spawn shell = spawnThermostat("shell");
-
-        shell.expect(SHELL_PROMPT);
-        shell.send("connect -d mongodb://127.0.0.1:27518\n");
-        shell.expect(SHELL_PROMPT);
-        shell.send("exit\n");
-        shell.expectClose();
-
-        assertCommandIsFound(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents());
-        assertNoExceptions(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents());
-    }
-
-    @Test
-    public void testDisconnectWithoutConnecting() throws ExpectJException, TimeoutException, IOException {
-        Spawn shell = spawnThermostat("shell");
-
-        shell.expect(SHELL_PROMPT);
-        shell.send("disconnect\n");
-        shell.expect(SHELL_PROMPT);
-        shell.send("exit\n");
-        shell.expectClose();
-
-        assertCommandIsFound(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents());
-        assertNoExceptions(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents());
-    }
-
-    @Test
-    public void testConnectAndDisconnectInShell() throws IOException, TimeoutException, ExpectJException {
-        Spawn shell = spawnThermostat("shell");
-
-        shell.expect(SHELL_PROMPT);
-        shell.send("connect -d mongodb://127.0.0.1:27518\n");
-        shell.expect(SHELL_PROMPT);
-        shell.send("disconnect\n");
-        shell.send("exit\n");
-        shell.expectClose();
-
-        assertNoExceptions(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents());
-    }
-
-    // TODO add a test to make sure connect/disconnect is not visible outside the shell
-}
-
--- a/distribution/src/test/java/com/redhat/thermostat/distribution/StorageTest.java	Fri Feb 15 17:24:29 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,89 +0,0 @@
-/*
- * Copyright 2012, 2013 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat 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, or (at your
- * option) any later version.
- *
- * Thermostat 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 Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.distribution;
-
-import java.io.IOException;
-
-import org.junit.Test;
-
-import expectj.Executor;
-import expectj.Spawn;
-
-public class StorageTest extends IntegrationTest {
-
-    @Test
-    public void startAndStopStorage() throws Exception {
-        Spawn storage;
-
-        storage = spawnThermostat("storage", "--start");
-        storage.expect("pid:");
-        storage.expectClose();
-
-        assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents());
-
-        storage = spawnThermostat("storage", "--stop");
-        storage.expect("server shutdown complete");
-        storage.expectClose();
-
-        assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents());
-    }
-
-    @Test
-    public void testServiceStartAndKilling() throws Exception {
-        Spawn storage;
-        final Process[] process = new Process[1];
-
-        storage = spawn(new Executor() {
-            @Override
-            public Process execute() throws IOException {
-                ProcessBuilder builder = new ProcessBuilder(getThermostatExecutable(), "service");
-                Process service = builder.start();
-                process[0] = service;
-                return service;
-            }
-        });
-
-        try {
-            storage.expectErr("agent started");
-        }
-        finally {
-            killRecursively(process[0]);
-        }
-    }
-
-}
-
--- a/distribution/src/test/java/com/redhat/thermostat/distribution/VmCommandsTest.java	Fri Feb 15 17:24:29 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,135 +0,0 @@
-/*
- * Copyright 2012, 2013 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat 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, or (at your
- * option) any later version.
- *
- * Thermostat 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 Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.distribution;
-
-import static org.junit.Assert.assertEquals;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import expectj.Spawn;
-
-/** Integration tests for the various vm commands */
-public class VmCommandsTest extends IntegrationTest {
-
-    @BeforeClass
-    public static void setUpOnce() throws IOException, InterruptedException {
-        String staleDataDir = getStorageDataDirectory();
-        deleteFilesUnder(staleDataDir);
-
-        Process startStorage = new ProcessBuilder(getThermostatExecutable(), "storage", "--start").start();
-        startStorage.waitFor();
-
-        // TODO insert actual data into the database and test that
-    }
-
-    @AfterClass
-    public static void tearDownOnce() throws InterruptedException, IOException {
-        Process stopStorage = new ProcessBuilder(getThermostatExecutable(), "storage", "--stop").start();
-        stopStorage.waitFor();
-    }
-
-    @Test
-    public void testListVms() throws Exception {
-        Spawn vmList = commandAgainstMongo("list-vms");
-        vmList.expectClose();
-
-        assertEquals("HOST_ID HOST VM_ID STATUS VM_NAME\n", vmList.getCurrentStandardOutContents());
-    }
-
-    @Test
-    public void testVmStat() throws Exception {
-        Spawn vmStat = commandAgainstMongo("vm-stat");
-        vmStat.expectClose();
-
-        System.out.println(vmStat.getCurrentStandardOutContents());
-        assertCommandIsFound(vmStat.getCurrentStandardOutContents(), vmStat.getCurrentStandardErrContents());
-        assertNoExceptions(vmStat.getCurrentStandardOutContents(), vmStat.getCurrentStandardErrContents());
-    }
-
-    @Test
-    public void testVmInfo() throws Exception {
-        Spawn vmInfo = commandAgainstMongo("vm-info");
-        vmInfo.expectClose();
-
-        assertNoExceptions(vmInfo.getCurrentStandardOutContents(), vmInfo.getCurrentStandardErrContents());
-    }
-
-    @Test
-    public void testHeapCommands() throws Exception {
-        String[] commands = new String[] {
-                "dump-heap",
-                "list-heap-dumps",
-                "save-heap-dump-to-file",
-                "show-heap-histogram",
-                "find-objects",
-                "object-info",
-                "find-root"
-        };
-
-        for (String command : commands) {
-            Spawn heapCommand = commandAgainstMongo(command);
-            heapCommand.expectClose();
-
-            assertCommandIsFound(
-                    heapCommand.getCurrentStandardOutContents(),
-                    heapCommand.getCurrentStandardErrContents());
-            assertNoExceptions(
-                    heapCommand.getCurrentStandardOutContents(),
-                    heapCommand.getCurrentStandardErrContents());
-        }
-    }
-
-    private static Spawn commandAgainstMongo(String... args) throws IOException {
-        if (args == null || args.length == 0) {
-            throw new IllegalArgumentException("args must be an array with something");
-        }
-        List<String> completeArgs = new ArrayList<>();
-        completeArgs.addAll(Arrays.asList(args));
-        completeArgs.add("-d");
-        completeArgs.add("mongodb://127.0.0.1:27518");
-        return spawnThermostat(completeArgs.toArray(new String[0]));
-    }
-
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/integration-tests/pom.xml	Tue Feb 19 14:24:46 2013 +0100
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+
+ Copyright 2013 Red Hat, Inc.
+
+ This file is part of Thermostat.
+
+ Thermostat 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, or (at your
+ option) any later version.
+
+ Thermostat 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 Thermostat; see the file COPYING.  If not see
+ <http://www.gnu.org/licenses />.
+
+ Linking this code with other modules is making a combined work
+ based on this code.  Thus, the terms and conditions of the GNU
+ General Public License cover the whole combination.
+
+ As a special exception, the copyright holders of this code give
+ you permission to link this code with independent modules to
+ produce an executable, regardless of the license terms of these
+ independent modules, and to copy and distribute the resulting
+ executable under terms of your choice, provided that you also
+ meet, for each linked independent module, the terms and conditions
+ of the license of that module.  An independent module is a module
+ which is not derived from or based on this code.  If you modify
+ this code, you may extend this exception to your version of the
+ library, but you are not obligated to do so.  If you do not wish
+ to do so, delete this exception statement from your version.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.redhat.thermostat</groupId>
+    <artifactId>thermostat</artifactId>
+    <version>0.6.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>thermostat-integration-tests</artifactId>
+  <packaging>jar</packaging>
+
+  <name>Thermostat Integration Tests</name>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <main.basedir>${project.basedir}/..</main.basedir>
+  </properties>
+
+  <build>
+    <plugins>
+      <!-- skip unit test run, tests to be executed during integration-test -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+        <executions>
+          <execution>
+            <id>run-integration-tests</id>
+            <phase>integration-test</phase>
+            <goals>
+              <goal>test</goal>
+            </goals>
+            <configuration>
+              <skip>false</skip>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <version>2.4</version>
+        <executions>
+          <execution>
+            <id>copy-deps</id>
+            <phase>package</phase>
+            <goals>
+              <goal>copy-dependencies</goal>
+            </goals>
+            <configuration>
+              <excludeTransitive>true</excludeTransitive>
+              <excludeGroupIds>org.osgi,junit,org.hamcrest</excludeGroupIds>
+              <outputDirectory>${project.build.directory}/libs</outputDirectory>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+
+    <!-- integration tests -->
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>net.sourceforge.expectj</groupId>
+      <artifactId>expectj</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <!-- thermostat parts -->
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-distribution</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-web-war</artifactId>
+      <version>${project.version}</version>
+      <type>war</type>
+      <scope>test</scope>
+    </dependency>
+
+    <!--  Embedded jetty for testing the WAR (no peace from here on..). -->
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-server</artifactId>
+      <version>${jetty.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-webapp</artifactId>
+      <version>${jetty.version}</version>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+</project>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/integration-tests/src/test/java/com/redhat/thermostat/itest/CliTest.java	Tue Feb 19 14:24:46 2013 +0100
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat 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, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.itest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import expectj.Spawn;
+import expectj.TimeoutException;
+
+/**
+ * Integration tests to exercise the basics of the thermostat command line.
+ */
+public class CliTest extends IntegrationTest {
+
+    @Test
+    public void testExpectIsSane() throws Exception {
+        Spawn shell = spawnThermostat();
+
+        try {
+            shell.expect("some-random-text-that-is-not-really-possible");
+            fail("should never match");
+        } catch (IOException endOfStream) {
+            assertTrue(endOfStream.getMessage().contains("End of stream reached, no match found"));
+        }
+        shell.expectClose();
+    }
+
+    @Test
+    public void testSimpleInvocationPrintsHelp() throws Exception {
+        Spawn shell = spawnThermostat();
+        shell.expectClose();
+
+        String stdOut = shell.getCurrentStandardOutContents();
+
+        assertMatchesHelpCommandList(stdOut);
+
+        String stdErr = shell.getCurrentStandardErrContents();
+        assertEquals(stdErr, "");
+    }
+
+    @Test
+    public void testHelpCommandInvocation() throws Exception {
+        Spawn shell = spawnThermostat("help");
+        shell.expectClose();
+
+        String stdOut = shell.getCurrentStandardOutContents();
+        String stdErr = shell.getCurrentStandardErrContents();
+
+        assertMatchesHelpCommandList(stdOut);
+        assertEquals(stdErr, "");
+    }
+
+    @Test
+    public void testHelpOnHelp() throws Exception {
+        Spawn shell = spawnThermostat("help", "help");
+        shell.expectClose();
+
+        String stdOut = shell.getCurrentStandardOutContents();
+        String stdErr = shell.getCurrentStandardErrContents();
+
+        String[] lines = stdOut.split("\n");
+        String usage = lines[0];
+        assertEquals("usage: thermostat help [command-name]", usage);
+
+        assertEquals(stdErr, "");
+    }
+
+    @Test
+    public void testVersionArgument() throws Exception {
+        Spawn shell = spawnThermostat("--version");
+        shell.expectClose();
+
+        String stdOut = shell.getCurrentStandardOutContents();
+        String stdErr = shell.getCurrentStandardErrContents();
+
+        assertTrue(stdOut.matches("Thermostat version \\d+\\.\\d+\\.\\d+\n"));
+        assertEquals(stdErr, "");
+    }
+
+    @Test
+    public void testShell() throws Exception {
+        Spawn shell = spawnThermostat("shell");
+
+        shell.expect(SHELL_PROMPT);
+        shell.send("help\n");
+
+        shell.expect(SHELL_PROMPT);
+
+        assertMatchesHelpCommandList(shell.getCurrentStandardOutContents());
+
+        shell.send("exit\n");
+
+        shell.expectClose();
+    }
+
+    @Test
+    public void testShellHelp() throws Exception {
+        Spawn shell = spawnThermostat("help", "shell");
+        shell.expectClose();
+
+        String stdOut = shell.getCurrentStandardOutContents();
+
+        String[] lines = stdOut.split("\n");
+        String usage = lines[0];
+        assertTrue(usage.matches("^usage: thermostat shell$"));
+        String description = lines[1];
+        assertTrue(description.matches("^\\s+launches the Thermostat interactive shell$"));
+        assertTrue(lines[2].matches("thermostat shell"));
+    }
+
+    @Test
+    public void testShellUnrecognizedArgument() throws Exception {
+        Spawn shell = spawnThermostat("shell", "--foo");
+        shell.expectErr("Unrecognized option: --foo");
+        shell.expectClose();
+    }
+
+    @Test
+    public void testUnrecognizedEventsInShell() throws IOException, TimeoutException {
+        // test '!' events
+        Spawn shell = spawnThermostat("shell");
+
+        shell.expect(SHELL_PROMPT);
+        shell.send("what!?!\n");
+        shell.expect(SHELL_PROMPT);
+        shell.send("exit\n");
+
+        assertTrue(shell.getCurrentStandardErrContents().contains("!?!: event not found"));
+        assertNoExceptions(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents());
+    }
+
+    @Test
+    public void testInvalidCommand() throws Exception {
+        Spawn shell = spawnThermostat("foobar", "baz");
+
+        // TODO should this be stderr?
+        shell.expect("unknown command 'foobar'");
+        shell.expectClose();
+
+        String stdOut = shell.getCurrentStandardOutContents();
+
+        assertMatchesHelpCommandList(stdOut);
+    }
+
+    private static void assertMatchesHelpCommandList(String actual) {
+        assertTrue(actual.contains("list of commands"));
+        assertTrue(actual.contains("help"));
+        assertTrue(actual.contains("agent"));
+        assertTrue(actual.contains("gui"));
+        assertTrue(actual.contains("ping"));
+        assertTrue(actual.contains("shell"));
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/integration-tests/src/test/java/com/redhat/thermostat/itest/IntegrationTest.java	Tue Feb 19 14:24:46 2013 +0100
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat 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, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.itest;
+
+import static org.junit.Assert.assertFalse;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.redhat.thermostat.common.utils.StreamUtils;
+
+import expectj.Executor;
+import expectj.ExpectJ;
+import expectj.Spawn;
+
+/**
+ * Helper methods to support writing an integration test.
+ */
+public class IntegrationTest {
+
+    public static final long TIMEOUT_IN_SECONDS = 30;
+
+    public static final String SHELL_PROMPT = "Thermostat >";
+
+    public static String getThermostatExecutable() {
+        return "../distribution/target/bin/thermostat";
+    }
+
+    public static String getStorageDataDirectory() {
+        return "../distribution/target/storage/db";
+    }
+
+    public static Spawn spawnThermostat(String... args) throws IOException {
+        ExpectJ expect = new ExpectJ(TIMEOUT_IN_SECONDS);
+        StringBuilder result = new StringBuilder(getThermostatExecutable());
+        if (args != null) {
+            for (String arg : args) {
+                result.append(" ").append(arg);
+            }
+        }
+        String toExecute = result.toString();
+        //System.out.println("executing: '" + toExecute + "'");
+        return expect.spawn(toExecute);
+    }
+
+    public static Spawn spawn(List<String> args) throws IOException {
+        ExpectJ expect = new ExpectJ(TIMEOUT_IN_SECONDS);
+        StringBuilder result = new StringBuilder();
+        for (String arg : args) {
+            result.append(arg).append(" ");
+        }
+        return expect.spawn(result.substring(0, result.length() - 1));
+    }
+
+    public static Spawn spawn(Executor executor) throws IOException {
+        ExpectJ expect = new ExpectJ(TIMEOUT_IN_SECONDS);
+        return expect.spawn(executor);
+    }
+
+    /**
+     * Kill the process and all its children, recursively. Sends SIGTERM.
+     */
+    public static void killRecursively(Process process) throws Exception {
+        killRecursively(getPid(process));
+    }
+
+    private static void killRecursively(int pid) throws Exception {
+        List<Integer> childPids = findChildPids(pid);
+        for (Integer childPid : childPids) {
+            killRecursively(childPid);
+        }
+        killProcess(pid);
+    }
+
+    private static void killProcess(int processId) throws Exception {
+        Runtime.getRuntime().exec("kill " + processId).waitFor();
+    }
+
+    private static List<Integer> findChildPids(int processId) throws IOException {
+        String children = new String(StreamUtils.readAll(Runtime.getRuntime().exec("ps --ppid " + processId + " -o pid=").getInputStream()));
+        String[] childPids = children.split("\n");
+        List<Integer> result = new ArrayList<>();
+        for (String childPid : childPids) {
+            String pidString = childPid.trim();
+            if (pidString.length() == 0) {
+                continue;
+            }
+            try {
+                result.add(Integer.parseInt(pidString));
+            } catch (NumberFormatException nfe) {
+                System.err.println(nfe);
+            }
+        }
+        return result;
+    }
+
+    private static int getPid(Process process) throws Exception {
+        final String UNIX_PROCESS_CLASS = "java.lang.UNIXProcess";
+        if (!process.getClass().getName().equals(UNIX_PROCESS_CLASS)) {
+            throw new IllegalArgumentException("can only kill " + UNIX_PROCESS_CLASS + "; input is a " + process.getClass());
+        }
+
+        Class<?> processClass = process.getClass();
+        Field pidField = processClass.getDeclaredField("pid");
+        pidField.setAccessible(true);
+        return (int) pidField.get(process);
+    }
+
+    public static void deleteFilesUnder(String path) throws IOException {
+        String[] filesToDelete = new File(path).list();
+        for (String toDelete : filesToDelete) {
+            if (!new File(path, toDelete).delete()) {
+                throw new IOException("cant delete: '" + new File(path, toDelete).toString() + "'.");
+            }
+        }
+    }
+
+    public static void assertCommandIsFound(String stdOutContents, String stdErrContents) {
+        assertFalse(stdOutContents.contains("unknown command"));
+        assertFalse(stdErrContents.contains("unknown command"));
+    }
+
+    public static void assertNoExceptions(String stdOutContents, String stdErrContents) {
+        assertFalse(stdOutContents.contains("Exception"));
+        assertFalse(stdErrContents.contains("Exception"));
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/integration-tests/src/test/java/com/redhat/thermostat/itest/StorageConnectionTest.java	Tue Feb 19 14:24:46 2013 +0100
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat 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, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.itest;
+
+import java.io.IOException;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import expectj.ExpectJException;
+import expectj.Spawn;
+import expectj.TimeoutException;
+
+public class StorageConnectionTest extends IntegrationTest {
+
+    @BeforeClass
+    public static void setUpOnce() throws IOException, TimeoutException, ExpectJException {
+        Spawn storage = spawnThermostat("storage", "--start");
+        storage.expect("pid:");
+        storage.expectClose();
+
+        assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents());
+    }
+
+    @AfterClass
+    public static void tearDownOnce() throws IOException, TimeoutException, ExpectJException {
+        Spawn storage = spawnThermostat("storage", "--stop");
+        storage.expect("server shutdown complete");
+        storage.expectClose();
+
+        assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents());
+    }
+
+    @Test
+    public void testConnect() throws ExpectJException, TimeoutException, IOException {
+        Spawn shell = spawnThermostat("shell");
+
+        shell.expect(SHELL_PROMPT);
+        shell.send("connect -d mongodb://127.0.0.1:27518\n");
+        shell.expect(SHELL_PROMPT);
+        shell.send("exit\n");
+        shell.expectClose();
+
+        assertCommandIsFound(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents());
+        assertNoExceptions(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents());
+    }
+
+    @Test
+    public void testDisconnectWithoutConnecting() throws ExpectJException, TimeoutException, IOException {
+        Spawn shell = spawnThermostat("shell");
+
+        shell.expect(SHELL_PROMPT);
+        shell.send("disconnect\n");
+        shell.expect(SHELL_PROMPT);
+        shell.send("exit\n");
+        shell.expectClose();
+
+        assertCommandIsFound(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents());
+        assertNoExceptions(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents());
+    }
+
+    @Test
+    public void testConnectAndDisconnectInShell() throws IOException, TimeoutException, ExpectJException {
+        Spawn shell = spawnThermostat("shell");
+
+        shell.expect(SHELL_PROMPT);
+        shell.send("connect -d mongodb://127.0.0.1:27518\n");
+        shell.expect(SHELL_PROMPT);
+        shell.send("disconnect\n");
+        shell.send("exit\n");
+        shell.expectClose();
+
+        assertNoExceptions(shell.getCurrentStandardOutContents(), shell.getCurrentStandardErrContents());
+    }
+
+    // TODO add a test to make sure connect/disconnect is not visible outside the shell
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/integration-tests/src/test/java/com/redhat/thermostat/itest/StorageTest.java	Tue Feb 19 14:24:46 2013 +0100
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat 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, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.itest;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import expectj.Executor;
+import expectj.Spawn;
+
+public class StorageTest extends IntegrationTest {
+
+    @Test
+    public void startAndStopStorage() throws Exception {
+        Spawn storage;
+
+        storage = spawnThermostat("storage", "--start");
+        storage.expect("pid:");
+        storage.expectClose();
+
+        assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents());
+
+        storage = spawnThermostat("storage", "--stop");
+        storage.expect("server shutdown complete");
+        storage.expectClose();
+
+        assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents());
+    }
+
+    @Test
+    public void testServiceStartAndKilling() throws Exception {
+        Spawn storage;
+        final Process[] process = new Process[1];
+
+        storage = spawn(new Executor() {
+            @Override
+            public Process execute() throws IOException {
+                ProcessBuilder builder = new ProcessBuilder(getThermostatExecutable(), "service");
+                Process service = builder.start();
+                process[0] = service;
+                return service;
+            }
+        });
+
+        try {
+            storage.expectErr("agent started");
+        }
+        finally {
+            killRecursively(process[0]);
+        }
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/integration-tests/src/test/java/com/redhat/thermostat/itest/VmCommandsTest.java	Tue Feb 19 14:24:46 2013 +0100
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat 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, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.itest;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import expectj.Spawn;
+
+/** Integration tests for the various vm commands */
+public class VmCommandsTest extends IntegrationTest {
+
+    @BeforeClass
+    public static void setUpOnce() throws IOException, InterruptedException {
+        String staleDataDir = getStorageDataDirectory();
+        deleteFilesUnder(staleDataDir);
+
+        Process startStorage = new ProcessBuilder(getThermostatExecutable(), "storage", "--start").start();
+        startStorage.waitFor();
+
+        // TODO insert actual data into the database and test that
+    }
+
+    @AfterClass
+    public static void tearDownOnce() throws InterruptedException, IOException {
+        Process stopStorage = new ProcessBuilder(getThermostatExecutable(), "storage", "--stop").start();
+        stopStorage.waitFor();
+    }
+
+    @Test
+    public void testListVms() throws Exception {
+        Spawn vmList = commandAgainstMongo("list-vms");
+        vmList.expectClose();
+
+        assertEquals("HOST_ID HOST VM_ID STATUS VM_NAME\n", vmList.getCurrentStandardOutContents());
+    }
+
+    @Test
+    public void testVmStat() throws Exception {
+        Spawn vmStat = commandAgainstMongo("vm-stat");
+        vmStat.expectClose();
+
+        System.out.println(vmStat.getCurrentStandardOutContents());
+        assertCommandIsFound(vmStat.getCurrentStandardOutContents(), vmStat.getCurrentStandardErrContents());
+        assertNoExceptions(vmStat.getCurrentStandardOutContents(), vmStat.getCurrentStandardErrContents());
+    }
+
+    @Test
+    public void testVmInfo() throws Exception {
+        Spawn vmInfo = commandAgainstMongo("vm-info");
+        vmInfo.expectClose();
+
+        assertNoExceptions(vmInfo.getCurrentStandardOutContents(), vmInfo.getCurrentStandardErrContents());
+    }
+
+    @Test
+    public void testHeapCommands() throws Exception {
+        String[] commands = new String[] {
+                "dump-heap",
+                "list-heap-dumps",
+                "save-heap-dump-to-file",
+                "show-heap-histogram",
+                "find-objects",
+                "object-info",
+                "find-root"
+        };
+
+        for (String command : commands) {
+            Spawn heapCommand = commandAgainstMongo(command);
+            heapCommand.expectClose();
+
+            assertCommandIsFound(
+                    heapCommand.getCurrentStandardOutContents(),
+                    heapCommand.getCurrentStandardErrContents());
+            assertNoExceptions(
+                    heapCommand.getCurrentStandardOutContents(),
+                    heapCommand.getCurrentStandardErrContents());
+        }
+    }
+
+    private static Spawn commandAgainstMongo(String... args) throws IOException {
+        if (args == null || args.length == 0) {
+            throw new IllegalArgumentException("args must be an array with something");
+        }
+        List<String> completeArgs = new ArrayList<>();
+        completeArgs.addAll(Arrays.asList(args));
+        completeArgs.add("-d");
+        completeArgs.add("mongodb://127.0.0.1:27518");
+        return spawnThermostat(completeArgs.toArray(new String[0]));
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/integration-tests/src/test/java/com/redhat/thermostat/itest/WebAppTest.java	Tue Feb 19 14:24:46 2013 +0100
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat 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, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.itest;
+
+import static com.redhat.thermostat.itest.IntegrationTest.assertNoExceptions;
+import static com.redhat.thermostat.itest.IntegrationTest.spawnThermostat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.UUID;
+
+import org.eclipse.jetty.security.DefaultUserIdentity;
+import org.eclipse.jetty.security.MappedLoginService;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.UserIdentity;
+import org.eclipse.jetty.util.security.Password;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.redhat.thermostat.common.ApplicationInfo;
+import com.redhat.thermostat.storage.config.ConnectionConfiguration;
+import com.redhat.thermostat.storage.config.StartupConfiguration;
+import com.redhat.thermostat.storage.core.Add;
+import com.redhat.thermostat.storage.core.Cursor;
+import com.redhat.thermostat.storage.core.Query;
+import com.redhat.thermostat.test.FreePortFinder;
+import com.redhat.thermostat.test.FreePortFinder.TryPort;
+import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO;
+import com.redhat.thermostat.vm.classstat.common.model.VmClassStat;
+import com.redhat.thermostat.web.client.internal.WebStorage;
+
+import expectj.Spawn;
+
+public class WebAppTest {
+
+    private static WebStorage webStorage;
+    private static Server server;
+    private static int port;
+
+    @BeforeClass
+    public static void setUpOnce() throws Exception {
+        Spawn storage = spawnThermostat("storage", "--start");
+        storage.expect("pid:");
+        storage.expectClose();
+
+        assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents());
+
+        port = FreePortFinder.findFreePort(new TryPort() {
+            
+            @Override
+            public void tryPort(int port) throws Exception {
+                startServer(port);
+            }
+        });
+        registerCategory();
+    }
+
+    @AfterClass
+    public static void tearDownOnce() throws Exception {
+        server.stop();
+        server.join();
+
+        Spawn storage = spawnThermostat("storage", "--stop");
+        storage.expect("server shutdown complete");
+        storage.expectClose();
+
+        assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents());
+    }
+
+    private static void startServer(int port) throws Exception {
+        server = new Server(port);
+        ApplicationInfo appInfo = new ApplicationInfo();
+        String version = appInfo.getMavenVersion();
+        String warfile = "target/libs/thermostat-web-war-" + version + ".war";
+        WebAppContext ctx = new WebAppContext(warfile, "/thermostat");
+        ctx.getSecurityHandler().setAuthMethod("BASIC");
+        ctx.getSecurityHandler().setLoginService(new MappedLoginService() {
+            
+            @Override
+            protected void loadUsers() throws IOException {
+                putUser("testname", new Password("testpasswd"), new String[] { "thermostat-agent" });
+                putUser("test-no-role", new Password("testpasswd"), new String[] { "fluff" });
+                putUser("test-cmd-channel", new Password("testpasswd"), new String[] { "thermostat-cmd-channel" });
+            }
+            
+            @Override
+            protected UserIdentity loadUser(String username) {
+                if (username.equals("test-cmd-channel")) {
+                    return new DefaultUserIdentity(null, null, new String[] { "thermostat-cmd-channel" });
+                }
+                return new DefaultUserIdentity(null, null, new String[] { "thermostat-agent" });
+            }
+        });
+        server.setHandler(ctx);
+        server.start();
+    }
+
+    private static void registerCategory() {
+        String url = "http://localhost:" + port + "/thermostat/storage";
+        StartupConfiguration config = new ConnectionConfiguration(url, "testname", "testpasswd");
+        webStorage = new WebStorage(config);
+        webStorage.setAgentId(new UUID(42, 24));
+        webStorage.getConnection().connect();
+        webStorage.registerCategory(VmClassStatDAO.vmClassStatsCategory);
+    }
+
+    @Test
+    public void testPutFind() {
+        Add add = webStorage.createAdd(VmClassStatDAO.vmClassStatsCategory);
+        VmClassStat pojo = new VmClassStat();
+        pojo.setAgentId("fluff");
+        pojo.setLoadedClasses(12345);
+        pojo.setTimeStamp(42);
+        pojo.setVmId(987);
+        add.setPojo(pojo);
+        add.apply();
+
+        Query<VmClassStat> query = webStorage.createQuery(VmClassStatDAO.vmClassStatsCategory);
+        Cursor<VmClassStat> cursor = query.execute();
+        assertTrue(cursor.hasNext());
+        VmClassStat foundPojo = cursor.next();
+        assertEquals("fluff", foundPojo.getAgentId());
+        assertEquals(42, foundPojo.getTimeStamp());
+        assertEquals(987, foundPojo.getVmId());
+        assertEquals(12345, foundPojo.getLoadedClasses());
+        assertFalse(cursor.hasNext());
+    }
+}
--- a/pom.xml	Fri Feb 15 17:24:29 2013 -0500
+++ b/pom.xml	Tue Feb 19 14:24:46 2013 +0100
@@ -142,6 +142,7 @@
     <module>vm-heap-analysis</module>
     <module>numa</module>
     <!-- development related modules -->
+    <module>integration-tests</module>
     <module>dev</module>
   </modules>
 
--- a/web/client/src/main/java/com/redhat/thermostat/web/client/internal/WebStorage.java	Fri Feb 15 17:24:29 2013 -0500
+++ b/web/client/src/main/java/com/redhat/thermostat/web/client/internal/WebStorage.java	Tue Feb 19 14:24:46 2013 +0100
@@ -85,6 +85,7 @@
 import com.redhat.thermostat.common.ssl.SSLContextFactory;
 import com.redhat.thermostat.common.ssl.SslInitException;
 import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.storage.config.AuthenticationConfiguration;
 import com.redhat.thermostat.storage.config.StartupConfiguration;
 import com.redhat.thermostat.storage.core.Add;
 import com.redhat.thermostat.storage.core.AuthToken;
@@ -341,6 +342,11 @@
         random = new SecureRandom();
         conn = new WebConnection();
 
+        setEndpoint(config.getDBConnectionString());
+        if (config instanceof AuthenticationConfiguration) {
+            AuthenticationConfiguration authConfig = (AuthenticationConfiguration) config;
+            setAuthConfig(authConfig.getUsername(), authConfig.getPassword());
+        }
         // setup SSL if necessary
         if (config.getDBConnectionString().startsWith(HTTPS_PREFIX)) {
             registerSSLScheme(connManager);
--- a/web/server/pom.xml	Fri Feb 15 17:24:29 2013 -0500
+++ b/web/server/pom.xml	Tue Feb 19 14:24:46 2013 +0100
@@ -87,6 +87,7 @@
       <groupId>javax.servlet</groupId>
       <artifactId>servlet-api</artifactId>
       <version>${javax.servlet.version}</version>
+      <scope>provided</scope>
     </dependency>
 
     <dependency>