changeset 852:c78625c43086

Fix broken commands and add tests Fix some commands that are currently broken and add some tests to ensure that the commands are available and can be invoked. The tests are very basic. Most of the tests for VM don't even test actual data, just that the output does not contain errors. Reviewed-by: vanaltj Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-December/004585.html
author Omair Majid <omajid@redhat.com>
date Wed, 12 Dec 2012 19:39:21 -0500
parents ff0f033570c2
children 7b05bcaae364
files distribution/config/commands/dump-heap.properties distribution/config/commands/find-objects.properties distribution/config/commands/find-root.properties distribution/config/commands/list-heap-dumps.properties distribution/config/commands/list-vms.properties distribution/config/commands/object-info.properties distribution/config/commands/save-heap-dump-to-file.properties distribution/config/commands/show-heap-histogram.properties distribution/config/commands/vm-info.properties distribution/config/commands/vm-stat.properties 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/StorageTest.java distribution/src/test/java/com/redhat/thermostat/distribution/VmCommandsTest.java
diffstat 14 files changed, 388 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/distribution/config/commands/dump-heap.properties	Wed Dec 12 16:11:10 2012 -0500
+++ b/distribution/config/commands/dump-heap.properties	Wed Dec 12 19:39:21 2012 -0500
@@ -7,6 +7,7 @@
           thermostat-storage-mongodb-${project.version}.jar, \
           mongo.jar, \
           commons-beanutils.jar, \
+          commons-codec.jar, \
           commons-collections.jar, \
           commons-logging.jar, \
           thermostat-web-common-${project.version}.jar, \
--- a/distribution/config/commands/find-objects.properties	Wed Dec 12 16:11:10 2012 -0500
+++ b/distribution/config/commands/find-objects.properties	Wed Dec 12 19:39:21 2012 -0500
@@ -6,6 +6,7 @@
           thermostat-storage-mongodb-${project.version}.jar, \
           mongo.jar, \
           commons-beanutils.jar, \
+          commons-codec.jar, \
           commons-collections.jar, \
           commons-logging.jar, \
           thermostat-web-common-${project.version}.jar, \
--- a/distribution/config/commands/find-root.properties	Wed Dec 12 16:11:10 2012 -0500
+++ b/distribution/config/commands/find-root.properties	Wed Dec 12 19:39:21 2012 -0500
@@ -6,6 +6,7 @@
           thermostat-storage-mongodb-${project.version}.jar, \
           mongo.jar, \
           commons-beanutils.jar, \
+          commons-codec.jar, \
           commons-collections.jar, \
           commons-logging.jar, \
           thermostat-web-common-${project.version}.jar, \
--- a/distribution/config/commands/list-heap-dumps.properties	Wed Dec 12 16:11:10 2012 -0500
+++ b/distribution/config/commands/list-heap-dumps.properties	Wed Dec 12 19:39:21 2012 -0500
@@ -1,6 +1,7 @@
 bundles = thermostat-storage-mongodb-${project.version}.jar, \
           mongo.jar, \
           commons-beanutils.jar, \
+          commons-codec.jar, \
           commons-collections.jar, \
           commons-logging.jar, \
           thermostat-client-core-@project.version@.jar, \
--- a/distribution/config/commands/list-vms.properties	Wed Dec 12 16:11:10 2012 -0500
+++ b/distribution/config/commands/list-vms.properties	Wed Dec 12 19:39:21 2012 -0500
@@ -7,6 +7,7 @@
           gson.jar, \
           mongo.jar, \
           commons-beanutils.jar, \
+          commons-codec.jar, \
           commons-collections.jar, \
           commons-logging.jar, \
 
--- a/distribution/config/commands/object-info.properties	Wed Dec 12 16:11:10 2012 -0500
+++ b/distribution/config/commands/object-info.properties	Wed Dec 12 19:39:21 2012 -0500
@@ -1,6 +1,7 @@
 bundles = thermostat-storage-mongodb-${project.version}.jar, \
           mongo.jar, \
           commons-beanutils.jar, \
+          commons-codec.jar, \
           commons-collections.jar, \
           commons-logging.jar, \
           thermostat-client-core-@project.version@.jar, \
--- a/distribution/config/commands/save-heap-dump-to-file.properties	Wed Dec 12 16:11:10 2012 -0500
+++ b/distribution/config/commands/save-heap-dump-to-file.properties	Wed Dec 12 19:39:21 2012 -0500
@@ -1,6 +1,7 @@
 bundles = thermostat-storage-mongodb-${project.version}.jar, \
           mongo.jar, \
           commons-beanutils.jar, \
+          commons-codec.jar, \
           commons-collections.jar, \
           commons-logging.jar, \
           thermostat-client-core-@project.version@.jar, \
--- a/distribution/config/commands/show-heap-histogram.properties	Wed Dec 12 16:11:10 2012 -0500
+++ b/distribution/config/commands/show-heap-histogram.properties	Wed Dec 12 19:39:21 2012 -0500
@@ -1,6 +1,7 @@
 bundles = thermostat-storage-mongodb-${project.version}.jar, \
           mongo.jar, \
           commons-beanutils.jar, \
+          commons-codec.jar, \
           commons-collections.jar, \
           commons-logging.jar, \
           thermostat-client-core-@project.version@.jar, \
--- a/distribution/config/commands/vm-info.properties	Wed Dec 12 16:11:10 2012 -0500
+++ b/distribution/config/commands/vm-info.properties	Wed Dec 12 19:39:21 2012 -0500
@@ -8,6 +8,7 @@
           gson.jar, \
           mongo.jar, \
           commons-beanutils.jar, \
+          commons-codec.jar, \
           commons-collections.jar, \
           commons-logging.jar
 
--- a/distribution/config/commands/vm-stat.properties	Wed Dec 12 16:11:10 2012 -0500
+++ b/distribution/config/commands/vm-stat.properties	Wed Dec 12 19:39:21 2012 -0500
@@ -8,6 +8,7 @@
           gson.jar, \
           mongo.jar, \
           commons-beanutils.jar, \
+          commons-codec.jar, \
           commons-collections.jar, \
           commons-logging.jar
 
--- a/distribution/src/test/java/com/redhat/thermostat/distribution/CliTest.java	Wed Dec 12 16:11:10 2012 -0500
+++ b/distribution/src/test/java/com/redhat/thermostat/distribution/CliTest.java	Wed Dec 12 19:39:21 2012 -0500
@@ -42,27 +42,19 @@
 
 import java.io.IOException;
 
-import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
 
-import expectj.ExpectJ;
 import expectj.Spawn;
 
-public class CliTest {
-
-    private static final long TIMEOUT_IN_SECONDS = 2;
-
-    private ExpectJ expect;
-
-    @Before
-    public void setUp() throws IOException {
-        expect = new ExpectJ(TIMEOUT_IN_SECONDS);
-    }
+/**
+ * Integration tests to exercise the basics of the thermostat command line.
+ */
+public class CliTest extends IntegrationTest {
 
     @Test
     public void testExpectIsSane() throws Exception {
-        Spawn shell = expect.spawn(getThermostatExecutable());
+        Spawn shell = spawnThermostat();
 
         try {
             shell.expect("some-random-text-that-is-not-really-possible");
@@ -75,7 +67,7 @@
 
     @Test
     public void testSimpleInvocationPrintsHelp() throws Exception {
-        Spawn shell = expect.spawn(getThermostatExecutable());
+        Spawn shell = spawnThermostat();
         shell.expectClose();
 
         String stdOut = shell.getCurrentStandardOutContents();
@@ -88,7 +80,7 @@
 
     @Test
     public void testHelpCommandInvocation() throws Exception {
-        Spawn shell = expect.spawn(getThermostatExecutable() + " help");
+        Spawn shell = spawnThermostat("help");
         shell.expectClose();
 
         String stdOut = shell.getCurrentStandardOutContents();
@@ -101,7 +93,7 @@
     @Ignore("this is currently broken; help's usage includes stuff about usernames and passwords")
     @Test
     public void testHelpOnHelp() throws Exception {
-        Spawn shell = expect.spawn(getThermostatExecutable() + " help help");
+        Spawn shell = spawnThermostat("help", "help");
         shell.expectClose();
 
         String stdOut = shell.getCurrentStandardOutContents();
@@ -116,7 +108,7 @@
 
     @Test
     public void testVersionArgument() throws Exception {
-        Spawn shell = expect.spawn(getThermostatExecutable() + " --version");
+        Spawn shell = spawnThermostat("--version");
         shell.expectClose();
 
         String stdOut = shell.getCurrentStandardOutContents();
@@ -128,7 +120,7 @@
 
     @Test
     public void testShell() throws Exception {
-        Spawn shell = expect.spawn(getThermostatExecutable() + " shell");
+        Spawn shell = spawnThermostat("shell");
 
         shell.expect("Thermostat >");
         shell.send("help\n");
@@ -144,7 +136,7 @@
 
     @Test
     public void testShellHelp() throws Exception {
-        Spawn shell = expect.spawn(getThermostatExecutable() + " help shell");
+        Spawn shell = spawnThermostat("help", "shell");
         shell.expectClose();
 
         String stdOut = shell.getCurrentStandardOutContents();
@@ -162,14 +154,14 @@
 
     @Test
     public void testShellUnrecognizedArgument() throws Exception {
-        Spawn shell = expect.spawn(getThermostatExecutable() + " shell --foo");
+        Spawn shell = spawnThermostat("shell", "--foo");
         shell.expectErr("Unrecognized option: --foo");
         shell.expectClose();
     }
 
     @Test
     public void testInvalidCommand() throws Exception {
-        Spawn shell = expect.spawn(getThermostatExecutable() + " foobar baz");
+        Spawn shell = spawnThermostat("foobar", "baz");
 
         // TODO should this be stderr?
         shell.expect("unknown command 'foobar'");
@@ -180,10 +172,6 @@
         assertMatchesHelpCommandList(stdOut);
     }
 
-    public String getThermostatExecutable() {
-        return "target/bin/thermostat";
-    }
-
     private static void assertMatchesHelpCommandList(String actual) {
         assertTrue(actual.contains("list of commands"));
         assertTrue(actual.contains("help"));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/distribution/src/test/java/com/redhat/thermostat/distribution/IntegrationTest.java	Wed Dec 12 19:39:21 2012 -0500
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2012 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 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);
+            }
+        }
+        return expect.spawn(result.toString());
+    }
+
+    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/distribution/src/test/java/com/redhat/thermostat/distribution/StorageTest.java	Wed Dec 12 19:39:21 2012 -0500
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2012 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;
+            }
+        });
+
+        storage.expect("agent started");
+
+        killRecursively(process[0]);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/distribution/src/test/java/com/redhat/thermostat/distribution/VmCommandsTest.java	Wed Dec 12 19:39:21 2012 -0500
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2012 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 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 = spawnThermostat("list-vms");
+        vmList.expectClose();
+
+        assertEquals("HOST_ID HOST VM_ID STATUS VM_NAME\n", vmList.getCurrentStandardOutContents());
+    }
+
+    @Test
+    public void testVmStat() throws Exception {
+        Spawn vmStat = spawnThermostat("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 = spawnThermostat("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 = spawnThermostat(command);
+            heapCommand.expectClose();
+
+            assertCommandIsFound(
+                    heapCommand.getCurrentStandardOutContents(),
+                    heapCommand.getCurrentStandardErrContents());
+            assertNoExceptions(
+                    heapCommand.getCurrentStandardOutContents(),
+                    heapCommand.getCurrentStandardErrContents());
+        }
+    }
+}