changeset 1657:331e21088194

Make itests independent of configured log levels. Reviewed-by: omajid Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2015-March/012952.html PR2265
author Severin Gehwolf <sgehwolf@redhat.com>
date Mon, 02 Mar 2015 17:10:50 +0100
parents 4414429cfe31
children 3e61adfe186a
files agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/AgentApplication.java integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/DevWebStorageTest.java integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/EnvironmentExecutor.java integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/EnvironmentExecutorTest.java integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/IntegrationTest.java integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/LocaleExecutor.java integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/LogConfigurator.java integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/LogConfiguratorTest.java integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/PropertiesExecutor.java integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/PropertiesExecutorTest.java integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/SimpleExecutor.java integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/StorageTest.java
diffstat 12 files changed, 635 insertions(+), 83 deletions(-) [+]
line wrap: on
line diff
--- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/AgentApplication.java	Mon Mar 09 19:53:53 2015 +0100
+++ b/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/AgentApplication.java	Mon Mar 02 17:10:50 2015 +0100
@@ -62,6 +62,7 @@
 import com.redhat.thermostat.common.MultipleServiceTracker.Action;
 import com.redhat.thermostat.common.cli.AbstractStateNotifyingCommand;
 import com.redhat.thermostat.common.cli.Arguments;
+import com.redhat.thermostat.common.cli.Command;
 import com.redhat.thermostat.common.cli.CommandContext;
 import com.redhat.thermostat.common.cli.CommandException;
 import com.redhat.thermostat.common.utils.LoggingUtils;
@@ -79,6 +80,16 @@
 @SuppressWarnings("restriction")
 public final class AgentApplication extends AbstractStateNotifyingCommand {
 
+    /**
+     * Property for turning on verbose mode. This is there so as to be able to
+     * run integration tests independent of log levels.
+     */
+    private static final String VERBOSE_MODE_PROPERTY = "thermostat.agent.verbose";
+    // Messages printed in verbose mode. Integration tests use this. Be careful
+    // when you change those!
+    private static final String VERBOSE_MODE_AGENT_STOPPED_MSG = "Agent stopped.";
+    private static final String VERBOSE_MODE_AGENT_STARTED_MSG = "Agent started.";
+    
     private static final Logger logger = LoggingUtils.getLogger(AgentApplication.class);
     
     private final BundleContext bundleContext;
@@ -237,7 +248,12 @@
                 // there will be no way to actually stop Thermostat.
                 ex.printStackTrace();
             }
-            logger.fine("Agent stopped.");       
+            logger.fine("Agent stopped.");
+            // Hook for integration tests. Print a well known message to stdout
+            // if verbose mode is turned on via the system property.
+            if (Boolean.getBoolean(VERBOSE_MODE_PROPERTY)) {
+                System.out.println(VERBOSE_MODE_AGENT_STOPPED_MSG);
+            }
             shutdown();
         }
         
@@ -272,9 +288,13 @@
             shutdown();
         }
         logger.fine("Agent started.");
+        // Hook for integration tests. Print a well known message to stdout
+        // if verbose mode is turned on via the system property.
+        if (Boolean.getBoolean(VERBOSE_MODE_PROPERTY)) {
+            System.out.println(VERBOSE_MODE_AGENT_STARTED_MSG);
+        }
 
         logger.info("Agent id: " + agent.getId());
-        logger.info("agent started.");
         return agent;
     }
     
--- a/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/DevWebStorageTest.java	Mon Mar 09 19:53:53 2015 +0100
+++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/DevWebStorageTest.java	Mon Mar 02 17:10:50 2015 +0100
@@ -42,6 +42,7 @@
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.NoSuchFileException;
+import java.util.Map;
 
 import org.junit.After;
 import org.junit.Test;
@@ -92,12 +93,12 @@
     }
 
     private void runWebStorageTest() throws Exception {
-        SpawnResult spawnResult = spawnThermostatAndGetProcess("web-storage-service");
+        Map<String, String> testProperties = getVerboseModeProperties();
+        SpawnResult spawnResult = spawnThermostatWithPropertiesSetAndGetProcess(testProperties, "web-storage-service");
         Spawn service = spawnResult.spawn;
 
         try {
-            // This depends on the log level
-            service.expectErr("agent started");
+            service.expect("Agent started.");
         } finally {
             // service.stop only stops the agent/webservice.
             killRecursively(spawnResult.process);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/EnvironmentExecutor.java	Mon Mar 02 17:10:50 2015 +0100
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2012-2015 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 expectj.Executor;
+
+/**
+ * Runs any script in $THERMOSTAT_HOME/bin with the given name, args
+ * and enviroment.
+ *
+ */
+class EnvironmentExecutor implements Executor {
+
+    private final String[] env;
+    private final String args;
+    private final String script;
+
+    /**
+     * 
+     * @param script The script name (e.g. "thermostat")
+     * @param args The space separated list of arguments
+     * @param env List of environment variables in key=value pair format.
+     */
+    public EnvironmentExecutor(String script, String args, String[] env) {
+        this.args = args;
+        this.env = env;
+        this.script = script;
+    }
+
+    @Override
+    public Process execute() throws IOException {
+        String command = buildCommand();
+        Process p = Runtime.getRuntime().exec(command, env);
+        return p;
+    }
+
+    @Override
+    public String toString() {
+        return script + " " + args;
+    }
+    
+    private String buildCommand() {
+        return IntegrationTest.getSystemBinRoot() + "/" + script + " " + args;
+    }
+    
+    // for testing
+    String[] getEnv() {
+        return env;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/EnvironmentExecutorTest.java	Mon Mar 02 17:10:50 2015 +0100
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2012-2015 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 org.junit.Test;
+
+public class EnvironmentExecutorTest {
+    
+    @Test
+    public void testExecutor() {
+        String[] env = new String[] { "env1=bar1" };
+        EnvironmentExecutor executor = new EnvironmentExecutor("thermostat", "bar baz", env);
+        assertEquals("thermostat bar baz", executor.toString());
+        assertEquals(1, executor.getEnv().length);
+        assertEquals("env1=bar1", executor.getEnv()[0]);
+    }
+}
--- a/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/IntegrationTest.java	Mon Mar 09 19:53:53 2015 +0100
+++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/IntegrationTest.java	Mon Mar 02 17:10:50 2015 +0100
@@ -45,7 +45,10 @@
 import java.nio.file.Files;
 import java.nio.file.NoSuchFileException;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
 
 import com.redhat.thermostat.client.cli.internal.ShellPrompt;
 import com.redhat.thermostat.common.utils.StreamUtils;
@@ -64,8 +67,38 @@
  */
 public class IntegrationTest {
     
+    /**
+     * Configure the log level to FINEST, and configure a file handler so as for
+     * log messages to go to USER_THERMOSTAT_HOME/integration-tests.log rather
+     * than stdout. This is to ensure integration tests pass without dependency
+     * on log levels. See:
+     *   http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=1594
+     */
+    static {
+        createUserThermostatHomeAndEtc();
+        File loggingProperties = new File(getUserThermostatHome() + File.separator + "etc" + File.separator + "logging.properties");
+        File logFile = new File(getUserThermostatHome() + File.separator + "integration-tests.log");
+        LogConfigurator configurator = new LogConfigurator(Level.FINEST, loggingProperties, logFile);
+        configurator.writeConfiguration();
+    }
+    
     public static final String ITEST_USER_HOME_PROP = "com.redhat.thermostat.itest.thermostatUserHome";
     public static final String ITEST_THERMOSTAT_HOME_PROP = "com.redhat.thermostat.itest.thermostatHome";
+
+    private static final String AGENT_VERBOSE_MODE_PROP = "thermostat.agent.verbose";
+    private static final String THERMOSTAT_HOME = "THERMOSTAT_HOME";
+    private static final String USER_THERMOSTAT_HOME = "USER_THERMOSTAT_HOME";
+
+    public static final String[] DEFAULT_ENVIRONMENT = new String[] {
+        THERMOSTAT_HOME + "=" + IntegrationTest.getThermostatHome(),
+        USER_THERMOSTAT_HOME + "=" + IntegrationTest.getUserThermostatHome(),
+    };
+    
+    public static final String[] DEFAULT_ENV_WITH_LANG_C = new String [] {
+        THERMOSTAT_HOME + "=" + IntegrationTest.getThermostatHome(),
+        USER_THERMOSTAT_HOME + "=" + IntegrationTest.getUserThermostatHome(),
+        "LANG=C"
+    };
     
     public static class SpawnResult {
         final Process process;
@@ -82,10 +115,15 @@
     public static final String SHELL_DISCONNECT_PROMPT = "Thermostat - >";
     public static final String SHELL_CONNECT_PROMPT = "Thermostat + >";
 
-    private static final String THERMOSTAT_HOME = "THERMOSTAT_HOME";
-    private static final String USER_THERMOSTAT_HOME = "USER_THERMOSTAT_HOME";
     private static final String THERMOSTAT_SCRIPT = "thermostat";
     
+    private static void createUserThermostatHomeAndEtc() {
+        File userThHome = new File(getUserThermostatHome());
+        userThHome.mkdir();
+        File etcThHome = new File(userThHome, "etc");
+        etcThHome.mkdir();
+    }
+    
     /**
      * Utility method for creating the setup file - and its parent directories
      * which makes basic thermostat commands to be able to run (instead of
@@ -110,6 +148,7 @@
         }
     }
     
+
     /**
      * Utility method for removing stamp files which may get created by certain
      * integration test runs. For example a test which runs the "service"
@@ -138,6 +177,13 @@
             // wanted to delete that file, so that should be fine.
         }
     }
+    
+    protected static Map<String, String> getVerboseModeProperties() {
+        Map<String, String> testProperties = new HashMap<>();
+        // See AgentApplication.VERBOSE_MODE_PROPERTY
+        testProperties.put(AGENT_VERBOSE_MODE_PROP, Boolean.TRUE.toString());
+        return testProperties;
+    }
 
     /* This is a mirror of paths from c.r.t.shared.Configuration */
 
@@ -263,18 +309,31 @@
         return result.toString();
     }
 
-	public static SpawnResult spawnThermostatAndGetProcess(String... args) throws IOException {
-	    return runComandAndGetProcess(THERMOSTAT_SCRIPT, args);
-	}
-	
-	private static SpawnResult runComandAndGetProcess(String script, String[] args) throws IOException {
-	    String toExecute = convertArgsToString(args);
+    public static SpawnResult spawnThermostatAndGetProcess(String... args)
+            throws IOException {
+        return runComandAndGetProcess(THERMOSTAT_SCRIPT, args);
+    }
+
+    public static SpawnResult spawnThermostatWithPropertiesSetAndGetProcess(
+            Map<String, String> props, String... args) throws IOException {
+        return runCommandAndGetProcess(THERMOSTAT_SCRIPT, args, props);
+    }
+
+    private static SpawnResult runComandAndGetProcess(String script,
+            String[] args) throws IOException {
+        return runCommandAndGetProcess(THERMOSTAT_SCRIPT, args,
+                new HashMap<String, String>());
+    }
+
+    private static SpawnResult runCommandAndGetProcess(String script, String[] args, Map<String, String> props) throws IOException {
+        String toExecute = convertArgsToString(args);
 
         final Process[] process = new Process[1];
 
         ExpectJ expect = new ExpectJ(TIMEOUT_IN_SECONDS);
 
-        Spawn spawn = expect.spawn(new SimpleExecutor(script, toExecute) {
+        Spawn spawn = expect.spawn(new PropertiesExecutor(script, toExecute,
+                props) {
             @Override
             public Process execute() throws IOException {
                 Process p = super.execute();
@@ -284,6 +343,7 @@
         });
 
         return new SpawnResult(process[0], spawn);
+
     }
 
     protected static boolean isDevelopmentBuild() {
@@ -385,73 +445,6 @@
     public static void handleAuthPrompt(Spawn spawn, String url, String user, String password) throws IOException {
         spawn.send(user + "\r");
         spawn.send(password + "\r");
-        
-    }
-
-    private static class LocaleExecutor extends EnvironmentExecutor {
-
-        public static final String[] ENV_WITH_LANG_C = {
-                THERMOSTAT_HOME + "=" + getThermostatHome(),
-                USER_THERMOSTAT_HOME + "=" + getUserThermostatHome(),
-                "LANG=C"
-        };
-
-        public LocaleExecutor(String script, String args) {
-            super(script, args, ENV_WITH_LANG_C);
-        }
-
-    }
-
-    private static class SimpleExecutor extends EnvironmentExecutor {
-
-        public static final String[] ENV_WITH = {
-                THERMOSTAT_HOME + "=" + getThermostatHome(),
-                USER_THERMOSTAT_HOME + "=" + getUserThermostatHome(),
-        };
-
-        public SimpleExecutor(String script, String args) {
-            super(script, args, ENV_WITH);
-        }
-    }
-
-    /**
-     * Runs any script in $THERMOSTAT_HOME/bin with the given name, args
-     * and enviroment.
-     *
-     */
-    private static class EnvironmentExecutor implements Executor {
-
-        private final String[] env;
-        private final String args;
-        private final String script;
-
-        /**
-         * 
-         * @param script The script name (e.g. "thermostat")
-         * @param args The space separated list of arguments
-         * @param env List of environment variables in key=value pair format.
-         */
-        public EnvironmentExecutor(String script, String args, String[] env) {
-            this.args = args;
-            this.env = env;
-            this.script = script;
-        }
-
-        @Override
-        public Process execute() throws IOException {
-            String command = buildCommand();
-            Process p = Runtime.getRuntime().exec(command, env);
-            return p;
-        }
-
-        @Override
-        public String toString() {
-            return args;
-        }
-        
-        private String buildCommand() {
-            return getSystemBinRoot() + "/" + script + " " + args;
-        }
     }
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/LocaleExecutor.java	Mon Mar 02 17:10:50 2015 +0100
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2012-2015 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;
+
+class LocaleExecutor extends EnvironmentExecutor {
+
+    public LocaleExecutor(String script, String args) {
+        super(script, args, IntegrationTest.DEFAULT_ENV_WITH_LANG_C);
+    }
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/LogConfigurator.java	Mon Mar 02 17:10:50 2015 +0100
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2012-2015 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.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Properties;
+import java.util.logging.FileHandler;
+import java.util.logging.Level;
+import java.util.logging.SimpleFormatter;
+
+class LogConfigurator {
+
+    private static final String TH_HANDLERS_PROP = "com.redhat.thermostat.handlers";
+    private static final String TH_LOG_LEVEL_PROP = "com.redhat.thermostat.level";
+    private static final String FILE_HANDLER_PATTERN_PROP = "java.util.logging.FileHandler.pattern";
+    private static final String FILE_HANDLER_FORMATTER_PROP = "java.util.logging.FileHandler.formatter"; 
+    
+    private final Level desiredLevel;
+    private final File loggingPropertiesFile;
+    private final File destinationLogFile;
+    
+    LogConfigurator(Level desiredLevel, File loggingPropertiesFile, File destinationLogFile) {
+        this.desiredLevel = desiredLevel;
+        this.loggingPropertiesFile = loggingPropertiesFile;
+        this.destinationLogFile = destinationLogFile;
+    }
+    
+    /**
+     * Configure integration tests to use a FileHandler and only a FileHandler.
+     * @return valid thermostat logging.properties.
+     */
+    Properties getLoggingProperties() {
+        Properties props = new Properties();
+        // configure java.util.logging.FileHandler
+        props.put(FILE_HANDLER_PATTERN_PROP, destinationLogFile.getAbsolutePath());
+        props.put(FILE_HANDLER_FORMATTER_PROP, SimpleFormatter.class.getName());
+        // configure thermostat to use a FileHandler only
+        props.put(TH_HANDLERS_PROP, FileHandler.class.getName());
+        props.put(TH_LOG_LEVEL_PROP, desiredLevel.toString());
+        return props;
+    }
+    
+    /**
+     * Writes the log configuration to disk.
+     */
+    void writeConfiguration() {
+        // Clean up potentially existing log files from previous runs. We cannot
+        // delete on exit since jenkins (might) archive this file for debug purposes.
+        if (this.destinationLogFile.exists()) {
+            this.destinationLogFile.delete();
+        }
+        Properties props = getLoggingProperties();
+        try (FileOutputStream fout = new FileOutputStream(loggingPropertiesFile)) {
+            props.store(fout, "Thermostat integration test logging properties");
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to write itest logging configuration");
+        }
+        System.out.println("Configured logging.properties for integration tests:");
+        System.out.println("  Log level:            " + desiredLevel.toString());
+        System.out.println("  Destination log file: " + destinationLogFile.getAbsolutePath());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/LogConfiguratorTest.java	Mon Mar 02 17:10:50 2015 +0100
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2012-2015 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.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.util.Properties;
+import java.util.logging.FileHandler;
+import java.util.logging.Level;
+import java.util.logging.SimpleFormatter;
+
+import org.junit.Test;
+
+public class LogConfiguratorTest {
+
+    @Test
+    public void canGetLoggingProperties() {
+        File loggingProps = mock(File.class);
+        File dest = mock(File.class);
+        String logPath = "/path/to/log.file";
+        when(dest.getAbsolutePath()).thenReturn(logPath);
+        LogConfigurator configurator = new LogConfigurator(Level.OFF, loggingProps, dest);
+        Properties expected = configurator.getLoggingProperties();
+        assertEquals(FileHandler.class.getName(), expected.get("com.redhat.thermostat.handlers"));
+        assertEquals(logPath, expected.get("java.util.logging.FileHandler.pattern"));
+        assertEquals(SimpleFormatter.class.getName(), expected.get("java.util.logging.FileHandler.formatter"));
+        assertEquals(Level.OFF.toString(), expected.get("com.redhat.thermostat.level"));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/PropertiesExecutor.java	Mon Mar 02 17:10:50 2015 +0100
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2012-2015 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.util.Map;
+
+class PropertiesExecutor extends EnvironmentExecutor {
+    
+    private static final String ARG_TEMPLATE = "-J-D%s=%s";
+    
+    // main constructor
+    public PropertiesExecutor(String script, String args, Map<String, String> props) {
+        super(script, prependProperties(args, props), IntegrationTest.DEFAULT_ENVIRONMENT);
+    }
+    
+    // Test-only
+    PropertiesExecutor(String script, String args, Map<String, String> props, String[] env) {
+        super(script, prependProperties(args, props), env);
+    }
+    
+    static private String prependProperties(String args, Map<String, String> props) {
+        String retval = "";
+        for (String key: props.keySet()) {
+            retval = retval + String.format(ARG_TEMPLATE, key, props.get(key)) + " ";
+        }
+        retval = retval.trim();
+        if (retval.length() == 0) {
+            return args;
+        } else {
+            return retval + " " + args;
+        }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/PropertiesExecutorTest.java	Mon Mar 02 17:10:50 2015 +0100
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2012-2015 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 java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+
+public class PropertiesExecutorTest {
+    
+    @Test
+    public void testExecutorEmptyProps() {
+        Map<String, String> props = new HashMap<>();
+        PropertiesExecutor executor = getPropertiesExecutor(props);
+        assertEquals("thermostat bar baz", executor.toString());
+    }
+    
+    @Test
+    public void testExecutorWithSomePropsSet() {
+        Map<String, String> props = new HashMap<>();
+        props.put("prop1", "propval1");
+        props.put("prop2", "propval2");
+        PropertiesExecutor executor = getPropertiesExecutor(props);
+        // props are converted to a string in random order. Make assertions
+        // not depend on any specific order.
+        assertTrue(executor.toString().startsWith("thermostat"));
+        assertTrue(executor.toString().endsWith("bar baz"));
+        assertTrue(executor.toString().contains("-J-Dprop2=propval2"));
+        assertTrue(executor.toString().contains("-J-Dprop1=propval1"));
+    }
+    
+    private PropertiesExecutor getPropertiesExecutor(Map<String, String> props) {
+        String[] emptyEnv = new String[] {};
+        PropertiesExecutor executor = new PropertiesExecutor("thermostat", "bar baz", props, emptyEnv);
+        assertEquals(0, executor.getEnv().length);
+        return executor;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/SimpleExecutor.java	Mon Mar 02 17:10:50 2015 +0100
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2012-2015 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;
+
+class SimpleExecutor extends EnvironmentExecutor {
+
+    public SimpleExecutor(String script, String args) {
+        super(script, args, IntegrationTest.DEFAULT_ENVIRONMENT);
+    }
+    
+}
\ No newline at end of file
--- a/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/StorageTest.java	Mon Mar 09 19:53:53 2015 +0100
+++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/StorageTest.java	Mon Mar 02 17:10:50 2015 +0100
@@ -37,6 +37,7 @@
 package com.redhat.thermostat.itest;
 
 import java.io.IOException;
+import java.util.Map;
 
 import org.junit.After;
 import org.junit.Before;
@@ -79,11 +80,12 @@
 
     @Test
     public void testServiceStartAndKilling() throws Exception {
-        SpawnResult spawnResult = spawnThermostatAndGetProcess("service");
+        Map<String, String> testProperties = getVerboseModeProperties();
+        SpawnResult spawnResult = spawnThermostatWithPropertiesSetAndGetProcess(testProperties, "service");
         Spawn service = spawnResult.spawn;
 
         try {
-            service.expectErr("agent started");
+            service.expect("Agent started.");
         } finally {
             killRecursively(spawnResult.process);
             try {