changeset 170:0d34a379de82

- Added new framework (package org.thermosta.qa2.framework) - Removed hardcoded display in org.thermostat.qa.framework.GuiRobot class
author Zdenek Zambersky <zzambers@redhat.com>
date Tue, 10 Mar 2015 13:38:36 +0100
parents 19c8294a826f
children 1d7ab9111fd2
files src/org/thermostat/qa/framework/GuiRobot.java src/org/thermostat/qa2/framework/Assert.java src/org/thermostat/qa2/framework/NativeProcess.java src/org/thermostat/qa2/framework/Robot.java src/org/thermostat/qa2/framework/Shell.java src/org/thermostat/qa2/framework/SimpleLogger.java src/org/thermostat/qa2/framework/TestResult.java src/org/thermostat/qa2/framework/TestRunner.java src/org/thermostat/qa2/framework/ThermostatQAConfig.java src/org/thermostat/qa2/framework/annotations/Before.java src/org/thermostat/qa2/framework/annotations/InheritAnnotations.java src/org/thermostat/qa2/framework/annotations/RunAgent.java src/org/thermostat/qa2/framework/annotations/RunGnomeKeyring.java src/org/thermostat/qa2/framework/annotations/RunGui.java src/org/thermostat/qa2/framework/annotations/RunStorage.java src/org/thermostat/qa2/framework/annotations/SetupStorage.java src/org/thermostat/qa2/framework/annotations/SetupThermostat.java src/org/thermostat/qa2/framework/annotations/Test.java src/org/thermostat/qa2/framework/annotations/ThermostatSetupTarget.java src/org/thermostat/qa2/framework/services/AbstractProcessService.java src/org/thermostat/qa2/framework/services/AbstractService.java src/org/thermostat/qa2/framework/services/BackupService.java src/org/thermostat/qa2/framework/services/GnomeKeyring.java src/org/thermostat/qa2/framework/services/Service.java src/org/thermostat/qa2/framework/services/ShellService.java src/org/thermostat/qa2/framework/services/ThermostatAgent.java src/org/thermostat/qa2/framework/services/ThermostatGui.java src/org/thermostat/qa2/framework/services/ThermostatService.java src/org/thermostat/qa2/framework/services/ThermostatStorage.java src/org/thermostat/qa2/framework/services/Tomcat.java src/org/thermostat/qa2/framework/streamprocessors/BufferingStreamProcessor.java src/org/thermostat/qa2/framework/streamprocessors/LoggingStreamProcessor.java src/org/thermostat/qa2/framework/streamprocessors/StreamProcessor.java src/org/thermostat/qa2/framework/utils/AnnotationUtilities.java src/org/thermostat/qa2/framework/utils/CommonUtilities.java src/org/thermostat/qa2/framework/utils/FileUtilities.java src/org/thermostat/qa2/framework/utils/ProcessUtilities.java src/org/thermostat/qa2/framework/utils/ThermostatUtilities.java
diffstat 38 files changed, 3551 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/src/org/thermostat/qa/framework/GuiRobot.java	Wed Dec 10 18:54:35 2014 +0100
+++ b/src/org/thermostat/qa/framework/GuiRobot.java	Tue Mar 10 13:38:36 2015 +0100
@@ -175,7 +175,7 @@
     {
         //import -display :1 -window root screenshots/fileName.png
         List<String> content = new LinkedList<String>();
-        content.add("import -display :1 -window root screenshots/"+fileName+".png");
+        content.add("import -window root screenshots/"+fileName+".png");
         ThermostatUtilities.runBashScriptWithContent("xvfbScreenCapture.sh", content);
     }
     
@@ -258,7 +258,7 @@
                 //xdotool mousemove x y
                 //xdotool click --clearmodifiers 1 
                 List<String> content = new LinkedList<String>();
-                content.add("export DISPLAY=:1");
+                //content.add("export DISPLAY=:1");
                 content.add("xdotool mousemove "+x+" "+y);
                 content.add("xdotool click --clearmodifiers 1");
                 try
@@ -325,7 +325,7 @@
                 //TODO - does this work with the given keycodes?
                 //use xdotool to type for xvfb
                 List<String> content = new LinkedList<String>();
-                content.add("export DISPLAY=:1");
+                //content.add("export DISPLAY=:1");
                 content.add("xdotool key Ctrl+"+(char)c);
                 try
                 {
@@ -382,7 +382,7 @@
                 //TODO - insert code with xdotool for (activating and)
                 //resizing the thermostat window
                 List<String> content = new LinkedList<String>();
-                content.add("export DISPLAY=:1");
+                //content.add("export DISPLAY=:1");
                 //content.add("xdotool click "+(from.x + from.width<<1)+" "+(from.y + from.height<<1));
                 content.add("win=`xdotool getactivewindow`");
                 content.add("xdotool windowsize $win "+(from.x + xd)+" "+(from.y + yd));
@@ -453,7 +453,7 @@
             case XVFB:
                 //xdotool command for entering FN
                 List<String> content = new LinkedList<String>();
-                content.add("export DISPLAY=:1");
+                //content.add("export DISPLAY=:1");
                 content.add("xdotool key -clearmodifiers F"+N);
                 try
                 {
@@ -485,7 +485,7 @@
                 //export DISPLAY=:1
                 //xdotool key right
                 List<String> content = new LinkedList<String>();
-                content.add("export DISPLAY=:1");
+                //content.add("export DISPLAY=:1");
                 content.add("xdotool key -clearmodifiers Right");
                 try
                 {
@@ -518,7 +518,7 @@
                 //export DISPLAY=:1
                 //xdotool key right
                 List<String> content = new LinkedList<String>();
-                content.add("export DISPLAY=:1");
+                //content.add("export DISPLAY=:1");
                 content.add("xdotool key -clearmodifiers Left");
                 try
                 {
@@ -548,7 +548,7 @@
                 //TODO - does this work with the given keycodes?
                 //use xdotool to type for xvfb
                 List<String> content = new LinkedList<String>();
-                content.add("export DISPLAY=:1");
+                //content.add("export DISPLAY=:1");
                 content.add("xdotool key -clearmodifiers Down");
                 try
                 {
@@ -578,7 +578,7 @@
                 //TODO - does this work with the given keycodes?
                 //use xdotool to type for xvfb
                 List<String> content = new LinkedList<String>();
-                content.add("export DISPLAY=:1");
+                //content.add("export DISPLAY=:1");
                 content.add("xdotool key -clearmodifiers Return");
                 try
                 {
@@ -611,7 +611,7 @@
             case XVFB:
                 //type whole word using xdotool
                 List<String> content = new LinkedList<String>();
-                content.add("export DISPLAY=:1");
+                //content.add("export DISPLAY=:1");
                 content.add("xdotool type -clearmodifiers "+string);
                 try
                 {
@@ -643,7 +643,7 @@
             case XVFB:
                 //type the character using xdotool
                 List<String> content = new LinkedList<String>();
-                content.add("export DISPLAY=:1");
+                //content.add("export DISPLAY=:1");
                 content.add("xdotool type -clearmodifiers "+(char)c);
                 try
                 {
@@ -676,7 +676,7 @@
             case XVFB:
                 //type whole word using xdotool
                 List<String> content = new LinkedList<String>();
-                content.add("export DISPLAY=:1");
+                //content.add("export DISPLAY=:1");
                 content.add("xdotool type -clearmodifiers "+string);
                 try
                 {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/Assert.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,138 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework;
+
+import java.util.List;
+import org.thermostat.qa2.framework.services.Service;
+import org.thermostat.qa2.framework.utils.CommonUtilities;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public class Assert {
+
+    static final int runTimeout = 5;
+    static final int failTimeout = 10;
+
+    public static void assertFailsToStartAndRun(Service service, String message) {
+        Assert.assertFailsToStartAndRun(service, failTimeout, message);
+    }
+
+    public static void assertFailsToStartAndRun(Service service, long timeoutInSec, String message) {
+        try {
+            service.start();
+        } catch (Exception e) {
+            return;
+        }
+        boolean error = false;
+        for (int i = 0; i < timeoutInSec; ++i) {
+            if (!service.isRunning()) {
+                return;
+            }
+            CommonUtilities.sleep(1000);
+        }
+        throw new AssertionError(message);
+    }
+
+    public static void assertRuns(Service service, String message) throws Exception {
+        assertRuns(service, runTimeout, message);
+    }
+
+    public static void assertRuns(Service service, long timeoutInSec, String message) throws Exception {
+        for (int i = 0; i < timeoutInSec; ++i) {
+            if (!service.isRunning()) {
+                throw new AssertionError(message);
+            }
+            CommonUtilities.sleep(1000);
+        }
+    }
+
+    public static void assertContainsPattern(List<String> lines, String patterns) {
+        assertContainsPatterns(lines, new String[]{patterns});
+    }
+
+    public static void assertContainsPatterns(List<String> lines, String[] patterns) {
+        for (String pattern : patterns) {
+            boolean b = CommonUtilities.findInLineList(lines, pattern, false);
+            if (!b) {
+                throw new AssertionError("Pattern not found: " + pattern);
+            }
+        }
+    }
+
+    public static void assertNotContainsPatterns(List<String> lines, String[] patterns) {
+        for (String pattern : patterns) {
+            boolean b = CommonUtilities.findInLineList(lines, pattern, false);
+            if (b) {
+                throw new AssertionError("Unwanted pattern found: " + pattern);
+            }
+        }
+    }
+
+    public static void assertEquals(List<String> lines1, List<String> lines2) {
+        if (lines1.size() != lines2.size()) {
+            throw new AssertionError("Line lists are not equal in length");
+        }
+        for (int i = 0; i < lines1.size(); ++i) {
+            String line1 = lines1.get(i);
+            String line2 = lines2.get(i);
+            if (!line1.equals(line2)) {
+                throw new AssertionError("Lines are not equal: " + line1 + " != " + line2);
+            }
+        }
+    }
+
+    public static void assertEquals(Object o1, Object o2, String message) {
+        assertTrue(o1.equals(o2), message);
+    }
+
+    public static void assertTrue(boolean value, String message) {
+        if (!value) {
+            throw new AssertionError(message);
+        }
+    }
+
+    public static void assertFalse(boolean value, String message) {
+        if (value) {
+            throw new AssertionError(message);
+        }
+    }
+
+    public static void assertNull(Object object, String message) {
+        assertTrue(object == null, message);
+    }
+
+    public static void assertNotNull(Object object, String message) {
+        assertTrue(object != null, message);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/NativeProcess.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,233 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework;
+
+import org.thermostat.qa2.framework.streamprocessors.StreamProcessor;
+import org.thermostat.qa2.framework.streamprocessors.BufferingStreamProcessor;
+import org.thermostat.qa2.framework.streamprocessors.LoggingStreamProcessor;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public class NativeProcess {
+
+    public static final int MODE_NONE = 0;
+    public static final int MODE_CLOSE = 1;
+    public static final int MODE_DISCARD = 2;
+    public static final int MODE_LOG = 4;
+    public static final int MODE_BUFFER = 8;
+    public static final int MODE_LOG_AND_BUFFER = 4 | 8;
+
+    ProcessBuilder processBuilder;
+    public java.lang.Process process;
+    String[] commands;
+
+    public Thread stdoutProcessingThread = null;
+    public int stdoutMode = MODE_LOG;
+    public StreamProcessor stdoutStreamProcessor;
+
+    public Thread stderrProcessingThread = null;
+    public int stderrMode = MODE_LOG;
+    public StreamProcessor stderrStreamProcessor;
+
+    public PrintStream output = System.out;
+    SimpleLogger logger = new SimpleLogger(output);
+
+    String label = null;
+
+    public NativeProcess(String command) {
+        commands = new String[]{command};
+        processBuilder = new ProcessBuilder(command);
+    }
+
+    public NativeProcess(String... command) {
+        commands = command;
+        processBuilder = new ProcessBuilder(command);
+    }
+
+    public void setWorkingDirectory(String s) {
+        processBuilder.directory(new File(s));
+    }
+
+    public void setWorkingDirectory(File f) {
+        processBuilder.directory(f);
+        if (label != null) {
+            logger.setPrefix(label + " (workdir setup)");
+            logger.log(f.getPath());
+        }
+    }
+
+    public void setEnvironmentVariable(String name, String value) {
+        processBuilder.environment().put(name, value);
+        if (label != null) {
+            logger.setPrefix(label + " (environment add)");
+            logger.log(name + "=" + value);
+        }
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+
+    }
+
+    public void start() throws IOException {
+        if (label == null) {
+            setLabel(getName());
+        } else {
+            logger.setPrefix(label);
+        }
+        logger.log(commands);
+        process = processBuilder.start();
+        startLogging();
+    }
+
+    public void destroy() {
+        try {
+            process.getOutputStream().close();
+//            process.getInputStream().close();
+//            process.getErrorStream().close();
+        } catch (IOException ex) {
+            Logger.getLogger(NativeProcess.class.getName()).log(Level.SEVERE, null, ex);
+        }
+        process.destroy();
+    }
+
+    public String getName() {
+        return commands[0];
+    }
+
+    private StreamProcessor initStreamProcessor(InputStream is, PrintStream out, String postfix, int mode, InputStream comparator) throws IOException {
+        StreamProcessor sp;
+        switch (mode) {
+            case MODE_NONE:
+                sp = null;
+                break;
+            case MODE_CLOSE:
+                sp = null;
+                is.close();
+                break;
+            case MODE_DISCARD:
+                sp = new StreamProcessor(is);
+                break;
+            case MODE_LOG:
+                sp = new LoggingStreamProcessor(is, new SimpleLogger(out, label + postfix));
+                break;
+            case MODE_BUFFER:
+                sp = new BufferingStreamProcessor(is, null, false);
+                break;
+            case MODE_LOG_AND_BUFFER:
+                sp = new BufferingStreamProcessor(is, new SimpleLogger(out, label + postfix), true);
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown stream processing mode");
+        }
+        return sp;
+    }
+
+    private void startLogging() throws IOException {
+
+        stdoutStreamProcessor = initStreamProcessor(process.getInputStream(), System.out, " (stdout)", stdoutMode, null);
+        if (stdoutStreamProcessor != null) {
+            stdoutProcessingThread = new Thread(stdoutStreamProcessor);
+            stdoutProcessingThread.setDaemon(true);
+            stdoutProcessingThread.start();
+        }
+
+        stderrStreamProcessor = initStreamProcessor(process.getErrorStream(), System.out, " (stderr)", stderrMode, null);
+        if (stderrStreamProcessor != null) {
+            stderrProcessingThread = new Thread(stderrStreamProcessor);
+            stderrProcessingThread.setDaemon(true);
+            stderrProcessingThread.start();
+        }
+
+    }
+
+    public void setStdOutMode(int mode) {
+        stdoutMode = mode;
+    }
+
+    public void setStdErrMode(int mode) {
+        stderrMode = mode;
+    }
+
+    public StreamProcessor getStdoutProcessor() {
+        return stdoutStreamProcessor;
+    }
+
+    public List<String> getStdoutLines() throws IOException {
+        return ((BufferingStreamProcessor) getStdoutProcessor()).getLineList();
+    }
+
+    public StreamProcessor getStderrProcessor() {
+        return stderrStreamProcessor;
+    }
+
+    public List<String> getStderrLines() throws IOException {
+        return ((BufferingStreamProcessor) getStderrProcessor()).getLineList();
+    }
+
+    public int waitForRaw() throws InterruptedException {
+        int exitCode;
+        for (;;) {
+            try {
+                exitCode = process.waitFor();
+            } catch (InterruptedException e) {
+                continue;
+            }
+            break;
+        }
+        if (stdoutProcessingThread != null) {
+            stdoutProcessingThread.join();
+        }
+        if (stderrProcessingThread != null) {
+            stderrProcessingThread.join();
+        }
+        logger.setPrefix(label + " (exit code)");
+        logger.log(exitCode);
+        return exitCode;
+    }
+
+    public void waitFor() throws Exception {
+        int exitCode = waitForRaw();
+        if (exitCode != 0) {
+            throw new Exception("non zero exit code: " + exitCode);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/Robot.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,193 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework;
+
+import java.awt.AWTException;
+import java.awt.Rectangle;
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import org.thermostat.qa.common.Configuration;
+import org.thermostat.qa.framework.GuiRobot;
+import static org.thermostat.qa2.framework.ThermostatQAConfig.getPatternsDir;
+import org.thermostat.qa2.framework.utils.CommonUtilities;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+// wrapper of original robot, to be used by gui tests
+public class Robot {
+
+    public Robot() throws AWTException {
+        Configuration c = new Configuration(null);
+        guiRobot = new GuiRobot(c);
+    }
+
+    GuiRobot guiRobot;
+    long delay = 500;
+    long screenshotDelay = 5000;
+
+    public void pressDownKey() {
+        guiRobot.pressDownKey();
+        CommonUtilities.sleep(delay);
+    }
+
+    public void pressLeftKey() {
+        guiRobot.pressLeftKey();
+        CommonUtilities.sleep(delay);
+    }
+
+    public void pressRightKey() {
+        guiRobot.pressRightKey();
+        CommonUtilities.sleep(delay);
+    }
+
+    public void pressEnterKey() {
+        guiRobot.pressEnterKey();
+        CommonUtilities.sleep(delay);
+    }
+
+    public void clickOnRectangle(Rectangle rectangle) {
+        guiRobot.clickToRectangle(rectangle);
+        CommonUtilities.sleep(delay);
+    }
+
+    public void doubleClickOnRectangle(Rectangle rectangle) {
+        guiRobot.clickToRectangle(rectangle);
+        guiRobot.clickToRectangle(rectangle);
+        CommonUtilities.sleep(delay);
+    }
+
+    public void resize(Rectangle from, int xd, int yd) {
+        guiRobot.resize(from, xd, yd);
+        CommonUtilities.sleep(delay);
+    }
+
+    public Rectangle findPattern(String name) throws IOException, Exception {
+        System.out.println("INFO: searching for pattern: " + name);
+        String patternName = getPatternsDir() + File.separator + name.replace("/", File.separator);
+        int count = 0;
+        int notExistingCount = 0;
+        String patternFile = patternName + ".png";
+        do {
+            if (new File(patternFile).exists()) {
+                Rectangle rect = guiRobot.findPattern(patternFile);
+                if (rect != null) {
+                    System.out.println("INFO:     pattern: " + patternFile + ": FOUND");
+                    return rect;
+                }
+                System.out.println("INFO:     pattern: " + patternFile + ": NOT FOUND");
+                notExistingCount = 0;
+            } else {
+                ++notExistingCount;
+            }
+            patternFile = patternName + count++ + ".png";
+        } while (notExistingCount < 5);
+        return null;
+        //throw new Exception("Pattern not found: " + name);
+    }
+
+    public Rectangle checkForPattern(String name) throws IOException, Exception {
+        Rectangle rect = findPattern(name);
+        if (rect == null) {
+            throw new AssertionError("Pattern not found: " + name);
+        }
+        guiRobot.highlightRectangle(rect);
+        return rect;
+
+    }
+
+    public Rectangle checkForAnyPattern(String... names) throws IOException, Exception {
+        Rectangle rect = null;
+        for (String pattern : names) {
+            rect = findPattern(pattern);
+            if (rect != null) {
+                return rect;
+            }
+        }
+        throw new AssertionError("patterns: " + Arrays.toString(names) + "not found");
+    }
+
+    public void makeScreenshot(String name) throws IOException {
+        CommonUtilities.sleep(screenshotDelay);
+        guiRobot.prepareScreenshot(name);
+    }
+
+    public void saveScreenshot(String name) throws IOException {
+        guiRobot.saveScreenshot(name);
+    }
+
+    public void clickOnPattern(String name) throws Exception {
+        Rectangle rect = checkForPattern(name);
+        clickOnRectangle(rect);
+    }
+
+    public void clickOnAnyPattern(String... names) throws Exception {
+        Rectangle rect = checkForAnyPattern(names);
+        clickOnRectangle(rect);
+    }
+
+    public void doubleClickOnPattern(String name) throws Exception {
+        Rectangle rect = checkForPattern(name);
+        doubleClickOnRectangle(rect);
+    }
+
+    public void doubleClickOnAnyPattern(String... names) throws Exception {
+        Rectangle rect = checkForAnyPattern(names);
+        doubleClickOnRectangle(rect);
+    }
+
+    public void enterMainMenu() {
+        guiRobot.pressFNKey(10);
+        CommonUtilities.sleep(delay);
+    }
+
+    public void clickOnVMView() throws Exception {
+        makeScreenshot("GuiStarted1");
+        Rectangle r = findPattern("MainWindow/host_icon_with_arrow");
+        if (r != null) {
+            r.width = 6;
+            clickOnRectangle(r);
+        }
+
+        makeScreenshot("GuiStarted2");
+        clickOnAnyPattern("MainWindow/vm_icon_grey", "MainWindow/vm_icon_whiteblue");
+
+        makeScreenshot("VMViewActive1");
+        checkForPattern("MainWindow/vm_icon_blue");
+    }
+
+    public void writeText(String text) {
+        guiRobot.writeSmallLettersText(text);
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/Shell.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,97 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public class Shell extends org.thermostat.qa2.framework.NativeProcess {
+
+    OutputStreamWriter osw;
+    boolean startOfLine = true;
+
+    public Shell(String... commands) {
+        super(commands);
+    }
+
+    @Override
+    public void start() throws IOException {
+        super.start();
+        osw = new OutputStreamWriter(process.getOutputStream());
+        logger.setPrefix(label + " (stdin)");
+    }
+
+    private void writeImpl(String s) throws IOException {
+        if (startOfLine) {
+            logger.printLogPrefix();
+            startOfLine = false;
+        }
+        logger.print(s);
+        osw.write(s);
+        osw.flush();
+    }
+
+    private void writelnImpl(String s) throws IOException {
+        logger.log(s);
+        osw.write(s + "\n");
+        osw.flush();
+        startOfLine = true;
+    }
+
+    public void write(String s) throws IOException {
+        int oldIndex = 0;
+        int newIndex;
+        for (;;) {
+            newIndex = s.indexOf("\n", oldIndex);
+            if (newIndex < 0) {
+                break;
+            }
+            writelnImpl(s.substring(oldIndex, newIndex));
+            oldIndex = newIndex + 1;
+        }
+        if (oldIndex < s.length()) {
+            writeImpl(s.substring(oldIndex));
+        }
+    }
+
+    public void writeln(String s) throws IOException {
+        write(s + '\n');
+    }
+
+    public void exit() throws IOException {
+        writeln("exit");
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/SimpleLogger.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,106 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework;
+
+import java.io.PrintStream;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public class SimpleLogger {
+
+    public PrintStream ps;
+    public String prefix;
+
+    public SimpleLogger(PrintStream ps) {
+        this.ps = ps;
+        this.prefix = "";
+    }
+
+    public SimpleLogger(PrintStream ps, String prefix) {
+        this.ps = ps;
+        setPrefix(prefix);
+    }
+
+    public SimpleLogger(PrintStream ps, String... prefixes) {
+        this.ps = ps;
+        setPrefixes(prefixes);
+    }
+
+    public void setPrefix(String prefix) {
+        this.prefix = prefix + ": ";
+    }
+
+    public void setPrefixes(String... prefixes) {
+        prefix = "";
+        for (String pfx : prefixes) {
+            this.prefix += pfx + ": ";
+        }
+    }
+
+    public String getLogPrefix() {
+        return "INFO: " + prefix;
+    }
+
+    public void printLogPrefix() {
+        ps.print(getLogPrefix());
+    }
+
+    public void print(String s) {
+        ps.print(s);
+    }
+
+    public void println(String s) {
+        ps.println(s);
+    }
+
+    public void log(String text) {
+        ps.println(getLogPrefix() + text);
+    }
+
+    public void log(String... strings) {
+        String string = "";
+        for (String s : strings) {
+            string += s + " ";
+        }
+        log(string);
+    }
+
+    public void log(int i) {
+        log(Integer.toString(i));
+    }
+
+    public void err(String text) {
+        ps.println("ERROR: " + prefix + text);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/TestResult.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,41 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public enum TestResult {
+
+    PASSED, FAILED, ERROR, IGNORED
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/TestRunner.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,315 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.thermostat.qa2.framework.annotations.*;
+import org.thermostat.qa2.framework.services.*;
+import org.thermostat.qa2.framework.utils.AnnotationUtilities;
+import org.thermostat.qa2.framework.utils.CommonUtilities;
+import org.thermostat.qa2.framework.utils.ProcessUtilities;
+import org.thermostat.qa2.framework.utils.ThermostatUtilities;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public class TestRunner {
+
+    static ArrayList<Service> services = new ArrayList();
+
+    boolean interrupted = false;
+    String testClass;
+
+    public void setTestClass(String name) {
+        testClass = name;
+    }
+
+    public synchronized void interrupt() {
+        interrupted = true;
+    }
+
+    public synchronized boolean isInterrupted() {
+        return interrupted;
+    }
+
+    public void run() {
+        try {
+            Class c = Class.forName(testClass);
+            runTestClass(c);
+        } catch (Exception ex) {
+        }
+
+    }
+
+    public void runTestClass(Class c) throws Exception {
+        List<Method> methods = Arrays.asList(c.getDeclaredMethods());
+        List<Method> testMethods = AnnotationUtilities.getAnnotatedObjects(methods, Test.class);
+        int testCount = testMethods.size();
+        if (testCount > 0) {
+            long startTime = System.currentTimeMillis();
+            int passedCount = 0;
+            int failedCount = 0;
+            int errorCount = 0;
+            int ignoredCount = 0;
+
+            Object o = c.newInstance();
+            String className = c.getCanonicalName();
+
+            List<Method> beforeMethods = AnnotationUtilities.getAnnotatedObjects(methods, Before.class);
+            boolean beforeMethodFailed = false;
+            try {
+                for (Method m : beforeMethods) {
+                    runThermostatSetup(m);
+                    CommonUtilities.printHeading("Running \"before\" method: " + m.getName() + " ...");
+                    m.invoke(o);
+                }
+            } catch (Throwable e) {
+                e.printStackTrace();
+                beforeMethodFailed = true;
+            } finally {
+                runServiceCleanups();
+            }
+            for (Method m : testMethods) {
+                TestResult result;
+                boolean afterSetup = false;
+                Throwable t = null;
+                testrun:
+                if (beforeMethodFailed || isInterrupted()) {
+                    result = TestResult.ERROR;
+                } else {
+                    try {
+                        runThermostatSetup(m);
+                        afterSetup = true;
+                        if (isInterrupted()) {
+                            result = TestResult.ERROR;
+                            break testrun;
+                        }
+                        CommonUtilities.printHeading("Running test method: " + m.getName() + " ...");
+                        m.invoke(o);
+                        result = TestResult.PASSED;
+                    } catch (Throwable e) {
+                        t = e;
+                        //e.printStackTrace();
+                        System.out.println("INFO: thrown: " + e);
+                        result = afterSetup ? TestResult.FAILED : TestResult.ERROR;
+                    } finally {
+                        CommonUtilities.printHeading("Running automatic cleanup: ...");
+                        runServiceCleanups();
+                    }
+                }
+                String testName = className + "." + m.getName();
+                switch (result) {
+                    case PASSED:
+                        System.out.println("PASSED: " + testName);
+                        ++passedCount;
+                        break;
+                    case FAILED:
+                        System.out.println("FAILED: " + testName + ": FAILED null");
+                        if (t != null) {
+                            t.printStackTrace();
+                        }
+                        ++failedCount;
+                        break;
+                    case ERROR:
+                        System.out.println("ERROR: " + testName);
+                        ++errorCount;
+                        break;
+                    case IGNORED:
+                        System.out.println("IGNORED: " + testName);
+                        ++ignoredCount;
+                        break;
+                }
+            }
+            long duration = System.currentTimeMillis() - startTime;
+            System.out.println("SUMMARY: " + className + "    total: " + testCount
+                    + "    passed: " + passedCount + "    failed: " + failedCount
+                    + "    error: " + errorCount + "    ignored: " + ignoredCount
+                    + "    duration: " + duration);
+        }
+    }
+
+    public static void runThermostatSetup(Method testMethod) throws Exception {
+        ThermostatSetupTarget setupTargetA = AnnotationUtilities.findAnnotationWithClassInheritance(testMethod, ThermostatSetupTarget.class);
+        if (setupTargetA == null) {
+            return;
+        }
+        SetupThermostat setupThermostatA = AnnotationUtilities.findAnnotationWithClassInheritance(testMethod, SetupThermostat.class);
+        SetupStorage setupStorageA = AnnotationUtilities.findAnnotationWithClassInheritance(testMethod, SetupStorage.class);
+        RunStorage startStorageA = AnnotationUtilities.findAnnotationWithClassInheritance(testMethod, RunStorage.class);
+        RunAgent startAgentA = AnnotationUtilities.findAnnotationWithClassInheritance(testMethod, RunAgent.class);
+        RunGnomeKeyring startGnomeKeyringA = AnnotationUtilities.findAnnotationWithClassInheritance(testMethod, RunGnomeKeyring.class);
+        RunGui startGuiA = AnnotationUtilities.findAnnotationWithClassInheritance(testMethod, RunGui.class);
+
+        boolean setupThermostat = setupThermostatA != null;
+        boolean setupStorage = setupStorageA != null;
+        boolean startStorage = startStorageA != null;
+        boolean startAgent = startAgentA != null;
+        boolean startGnomeKeyring = startGnomeKeyringA != null;
+        boolean startGui = startGuiA != null;
+
+        String setupTarget = setupTargetA.value();
+        String thermostatHome = ThermostatQAConfig.getThermostatHome(setupTarget);
+        String thermostatUserHome = ThermostatQAConfig.getThermostatUserHome(setupTarget);
+
+        // setup thermostat
+        if (setupThermostat) {
+            if (new File(thermostatUserHome).exists()) {
+                BackupService backupService = new BackupService(thermostatUserHome);
+                backupService.start();
+                ProcessUtilities.run("rm", "-rf", "--", thermostatUserHome);
+            }
+            ThermostatUtilities.runThermostatSetup(thermostatHome, thermostatUserHome);
+        }
+        // setup storage
+        if (setupStorage) {
+            String storageType = setupStorageA.type();
+            boolean web = ThermostatQAConfig.getStorageTypeAsBoolean(storageType);
+            boolean badAgentLogin = setupStorageA.badAgentLogin();
+            boolean badClientLogin = setupStorageA.badClientLogin();
+            if (!web) {
+                ThermostatUtilities.setupDefaultMongoUsers(setupTarget);
+            }
+            boolean needBackup = ThermostatQAConfig.getThermostatConfigFiles(setupTarget, web).length > 0;
+            if (needBackup) {
+                BackupService backupService = new BackupService(thermostatHome + File.separator + "etc");
+                backupService.start();
+            }
+            ThermostatUtilities.copyThermostatConfigFiles(setupTarget, web, badAgentLogin, badClientLogin);
+            String tomcatHome = ThermostatQAConfig.getTomcatHome();
+            //BackupService backupService = new BackupService(tomcatHome + File.separator + "logs");
+            //backupService.start();
+            CommonUtilities.printHeading("Cleaning up tomcat logs");
+            ProcessUtilities.shellRun("rm -rf -- \"" + tomcatHome + File.separator + "logs" + File.separator + "\"" + "*");
+        }
+        // start storage
+        if (startStorage) {
+            String storageType = setupStorageA.type();
+            boolean web = ThermostatQAConfig.getStorageTypeAsBoolean(storageType);
+            ThermostatStorage storage = new ThermostatStorage(setupTarget);
+            storage.start();
+            if (web) {
+                Tomcat tomcat = new Tomcat();
+                tomcat.start();
+            }
+        }
+        // start agent
+        if (startAgent) {
+            ThermostatAgent agent = new ThermostatAgent(setupTarget);
+            agent.start();
+        }
+        // start gnome keyring
+        if (startGnomeKeyring) {
+            GnomeKeyring keyring = new GnomeKeyring();
+            keyring.start();
+        }
+        // start gui
+        if (startGui) {
+            ThermostatGui gui = new ThermostatGui(setupTarget);
+            gui.start();
+        }
+    }
+
+    public static void registerServiceCleanup(Service service) {
+        services.add(service);
+    }
+
+    public static void unregisterServiceCleanup(Service service) {
+        services.remove(service);
+    }
+
+    public static void runServiceCleanups() {
+        for (int i = services.size() - 1; i >= 0; --i) {
+            try {
+                services.get(i).stop();
+                services.remove(i);
+            } catch (Exception ex) {
+            }
+        }
+    }
+
+    public static void runTests(final TestRunner r) {
+        final Thread testingThread = new Thread() {
+            @Override
+            public void run() {
+                r.run();
+            }
+        };
+        final Thread hookThread = new Thread() {
+            @Override
+            public void run() {
+                try {
+                    r.interrupt();
+                    if (testingThread.isAlive()) {
+                        testingThread.join();
+                    }
+                } catch (InterruptedException ex) {
+                }
+            }
+        };
+        testingThread.setDaemon(true);
+        Runtime runtime = Runtime.getRuntime();
+        runtime.addShutdownHook(hookThread);
+        testingThread.start();
+        try {
+            testingThread.join();
+        } catch (InterruptedException ex) {
+        }
+        runtime.removeShutdownHook(hookThread);
+        System.exit(0);
+    }
+
+    public static void main(String[] args) {
+        if (args.length > 0) {
+            String className = args[0];
+            try {
+                Class c = Class.forName(className);
+                try {
+                    TestRunner runner = new TestRunner();
+                    runner.testClass = className;
+                    runTests(runner);
+                } catch (Exception ex) {
+                    Logger.getLogger(TestRunner.class.getName()).log(Level.SEVERE, null, ex);
+                }
+            } catch (ClassNotFoundException ex) {
+                Logger.getLogger(TestRunner.class.getName()).log(Level.SEVERE, null, ex);
+            }
+
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/ThermostatQAConfig.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,219 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework;
+
+import java.io.File;
+import java.io.PrintStream;
+import java.util.ArrayList;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public class ThermostatQAConfig {
+
+    public static PrintStream logStream = System.out;
+    public static String tomcatHome;
+    public static int mongoPort = 27518;
+    public static int agentPort = 12000;
+    public static int webStoragePort = 8080;
+    public static String gnomeKeyringConfigDir;
+    public static String backupDir;
+
+    public static Login mongoLogin = new Login("mongodevuser", "mongodevpassword");
+    public static Login agentLogin = new Login("agent-tester", "heslo1");
+    public static Login clientLogin = new Login("client-tester", "heslo2");
+    public static Login commandChannelLogin = new Login("cmdc-tester", "heslo3");
+
+    ///////////////////////
+    // tested thermostat //
+    ///////////////////////
+    public static String thermostatHome;
+    public static String thermostatUserHome;
+    public static String thermostatVersion;
+
+    //////////////////////
+    // other thermostat //
+    //////////////////////
+    public static String thermostatOtherHome;
+    //static String ThermostatOtherUserHome;
+    public static String thermostatOtherVersion;
+
+    static {
+        tomcatHome = System.getProperty("tomcat.home");
+
+        thermostatHome = System.getProperty("thermostat.home");
+        thermostatUserHome = System.getProperty("thermostat.user.home");
+        thermostatVersion = System.getProperty("thermostat.version");
+
+        thermostatOtherHome = System.getProperty("thermostat.other.home");
+        thermostatOtherVersion = System.getProperty("thermostat.other.version");
+
+        gnomeKeyringConfigDir = System.getProperty("gnome-keyring.config.path");
+        backupDir = System.getProperty("backup.path");
+    }
+
+    static String getPatternsDir() {
+        return "." + File.separator + "patterns" + File.separator + "1.1.0" + File.separator + "noAA";
+    }
+
+    // true -> tested, false -> other
+    static boolean getTargetAsBoolean(String target) {
+        if (target.equals("tested")) {
+            return true;
+        } else if (target.equals("other")) {
+            return false;
+        } else {
+            throw new IllegalArgumentException("Invalid target thermostat: " + target);
+        }
+    }
+
+    // true -> web, false -> mongo
+    static boolean getStorageTypeAsBoolean(String storageType) {
+        if (storageType.equals("web")) {
+            return true;
+        } else if (storageType.equals("mongo")) {
+            return false;
+        } else {
+            throw new IllegalArgumentException("Invalid storage type: " + storageType);
+        }
+    }
+
+    public static String getTomcatHome() {
+        return tomcatHome;
+    }
+
+    public static String getThermostatHome(String target) {
+        return getTargetAsBoolean(target) ? thermostatHome : thermostatOtherHome;
+    }
+
+    public static String getThermostatUserHome() {
+        return getThermostatUserHome("tested");
+    }
+    
+    public static String getThermostatUserHome(String target) {
+        return thermostatUserHome;
+    }
+
+    public static String getThermostatVersion(String target) {
+        return getTargetAsBoolean(target) ? thermostatVersion : thermostatOtherVersion;
+    }
+
+    public static String getThermostatExecutablePath() {
+        return getThermostatExecutablePath("tested");
+    }
+
+    public static String getThermostatExecutablePath(String target) {
+        return getThermostatHome(target) + File.separator + "bin" + File.separator + "thermostat";
+    }
+
+    public static String[] getThermostatUserConfigFiles(String target, boolean web, boolean agentBadlogin, boolean clientBadLogin) {
+        return getThermostatUserConfigFilesForVersion(getThermostatVersion(target), web, agentBadlogin, clientBadLogin);
+    }
+
+    public static String[] getThermostatUserConfigFilesNames() {
+        return new String[]{"agent.auth", "agent.properties", "client.properties"};
+    }
+
+    public static String[] getThermostatConfigFiles(String target, boolean web) {
+        return getThermostatConfigFilesForVersion(getThermostatVersion(target), web);
+    }
+
+    static String[] getThermostatUserConfigFilesForVersion(String thermostatVersion, boolean web, boolean agentBadlogin, boolean clientBadLogin) {
+        String prefix = "storageconfig" + File.separator + thermostatVersion;
+        if (web) {
+            prefix += File.separator + "web-tomcat";
+        } else {
+            prefix += File.separator + "db-mongodb";
+        }
+        ArrayList<String> configFiles = new ArrayList();
+        configFiles.add(prefix + File.separator + (agentBadlogin ? "agent.badauth" : "agent.auth"));
+        configFiles.add(prefix + File.separator + "agent.properties");
+        configFiles.add(prefix + File.separator + (clientBadLogin ? "client.badauth" : "client.properties"));
+        return configFiles.toArray(new String[configFiles.size()]);
+    }
+
+    static String[] getThermostatConfigFilesForVersion(String thermostatVersion, boolean web) {
+        String prefix = "storageconfig" + File.separator + thermostatVersion;
+        if (web) {
+            prefix += File.separator + "web-tomcat";
+        } else {
+            prefix += File.separator + "db-mongodb";
+        }
+        ArrayList<String> configFiles = new ArrayList();
+        if (web) {
+            configFiles.add(prefix + File.separator + "thermostat-users.properties");
+            configFiles.add(prefix + File.separator + "thermostat-roles.properties");
+        }
+        return configFiles.toArray(new String[configFiles.size()]);
+    }
+
+    public static String getThermostatOutputTextsDir() {
+        return "." + File.separator + "outputtexts" + File.separator + "1.1.0";
+    }
+
+    public static Login getMongoLogin() {
+        return mongoLogin;
+    }
+
+    public static Login getAgentLogin() {
+        return agentLogin;
+    }
+
+    public static Login getClientLogin() {
+        return clientLogin;
+    }
+
+    public static Login getCommandChannelLogin() {
+        return commandChannelLogin;
+    }
+
+    public static class Login {
+
+        String username;
+        String password;
+
+        public Login(String username, String password) {
+            this.username = username;
+            this.password = password;
+        }
+
+        public String getUsername() {
+            return username;
+        }
+
+        public String getPassword() {
+            return password;
+        }
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/annotations/Before.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,43 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Before {
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/annotations/InheritAnnotations.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,43 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface InheritAnnotations {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/annotations/RunAgent.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,45 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+@InheritAnnotations
+@Retention(RetentionPolicy.RUNTIME)
+@RunStorage
+public @interface RunAgent {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/annotations/RunGnomeKeyring.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,44 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+@InheritAnnotations
+@Retention(RetentionPolicy.RUNTIME)
+public @interface RunGnomeKeyring {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/annotations/RunGui.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,46 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+@InheritAnnotations
+@Retention(RetentionPolicy.RUNTIME)
+@RunStorage
+@RunGnomeKeyring
+public @interface RunGui {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/annotations/RunStorage.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,46 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+// also starts tomcat for web storage
+@InheritAnnotations
+@Retention(RetentionPolicy.RUNTIME)
+@SetupStorage(type = "web")
+public @interface RunStorage {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/annotations/SetupStorage.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,50 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+@SetupThermostat
+@InheritAnnotations
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SetupStorage {
+
+    String type();
+
+    boolean badAgentLogin() default false;
+
+    boolean badClientLogin() default false;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/annotations/SetupThermostat.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,45 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+@ThermostatSetupTarget
+@InheritAnnotations
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SetupThermostat {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/annotations/Test.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,43 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Test {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/annotations/ThermostatSetupTarget.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,45 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+@InheritAnnotations
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ThermostatSetupTarget {
+
+    String value() default "tested";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/services/AbstractProcessService.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,85 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.services;
+
+import org.thermostat.qa2.framework.NativeProcess;
+
+// Creates separate Thread which runs a process and waits for its termination,
+// to be able to set right running status then.
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public abstract class AbstractProcessService extends AbstractService {
+
+    
+    public NativeProcess process;
+    public Thread thread;
+    public abstract NativeProcess startServiceProcess() throws Exception;
+
+    @Override
+    public void startServiceImpl() throws Exception {
+        process = startServiceProcess();
+        thread = createWrappingThread(process);
+        thread.setDaemon(true);
+        setRunning(true);
+        thread.start();
+    }
+
+    @Override
+    public void stopServiceImpl() throws Exception {
+        process.destroy();
+        thread.join();
+    }
+
+    public abstract void runAfterInWrappingThread() throws Exception;
+
+    private Thread createWrappingThread(final NativeProcess process) {
+        return new Thread() {
+
+            @Override
+            public void run() {
+                try {
+                    process.waitFor();
+                } catch (Exception ex) {
+                } finally {
+                    try {
+                        runAfterInWrappingThread();
+                    } catch (Exception ex) {
+                    }
+                    setRunning(false);
+                }
+            }
+
+        };
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/services/AbstractService.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,127 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.services;
+
+import org.thermostat.qa2.framework.NativeProcess;
+import org.thermostat.qa2.framework.TestRunner;
+import org.thermostat.qa2.framework.utils.CommonUtilities;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public abstract class AbstractService implements Service {
+
+    private final Object lock = new Object();
+    boolean isRunning = false;
+
+    public abstract String getServiceName() throws Exception;
+
+    public abstract void startServiceImpl() throws Exception;
+
+    public abstract void stopServiceImpl() throws Exception;
+
+    @Override
+    public boolean isRunning() {
+        synchronized (lock) {
+            return isRunning;
+        }
+    }
+
+    protected void setRunning(boolean running) {
+        synchronized (lock) {
+            isRunning = running;
+        }
+    }
+
+    public boolean isShutdownHookEnabled() {
+        return true;
+    }
+
+    @Override
+    public void start() throws Exception {
+        CommonUtilities.printHeading("Starting " + getServiceName() + " ...");
+        if (isRunning()) {
+            System.out.println("INFO: " + getServiceName() + " is already running");
+            return;
+        }
+        if (isShutdownHookEnabled()) {
+            TestRunner.registerServiceCleanup(this);
+        }
+        try {
+            startServiceImpl();
+        } catch (Exception e) {
+            System.out.println("INFO: Error starting " + getServiceName() + "! ");
+            setRunning(false);
+            throw e;
+        }
+    }
+
+    @Override
+    public void stop() throws Exception {
+        CommonUtilities.printHeading("Stopping " + getServiceName() + " ...");
+        if (!isRunning()) {
+            System.out.println("INFO: " + getServiceName() + " is not running");
+            return;
+        }
+        try {
+            stopServiceImpl();
+        } catch (Exception e) {
+            System.out.println("INFO: Error stopping " + getServiceName() + "! ");
+            throw e;
+        } finally {
+            TestRunner.unregisterServiceCleanup(this);
+        }
+    }
+
+    protected void waitForListeningPort(int port, int timeout) throws Exception {
+        for (int i = 0; i < timeout; ++i) {
+            NativeProcess process = new NativeProcess("sh", "-c", "netstat -tln | grep -q :" + port + " &> /dev/null");
+            process.setLabel("Port checker");
+            process.start();
+            int ret = process.waitForRaw();
+
+            //int ret = NativeProcess.runRaw("sh", "-c", "netstat -tln | grep -q :" + port + " &> /dev/null");
+            if (ret == 0) {
+                return;
+            }
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+            }
+            if (!isRunning()) {
+                throw new Exception(getServiceName() + ": stopped while waiting for listening port " + port);
+            }
+        }
+        throw new Exception(getServiceName() + ": expired timeout " + timeout + " s waitng for listening port" + port);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/services/BackupService.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,89 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.services;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import org.thermostat.qa2.framework.ThermostatQAConfig;
+import org.thermostat.qa2.framework.utils.FileUtilities;
+import org.thermostat.qa2.framework.utils.ProcessUtilities;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public class BackupService extends AbstractService {
+
+    String[] files;
+    ArrayList<String> backupFiles = new ArrayList();
+    String backupDir;
+
+    public BackupService(String file) {
+        this(new String[]{file});
+    }
+
+    public BackupService(String[] files) {
+        this(files, ThermostatQAConfig.backupDir);
+    }
+
+    public BackupService(String[] files, String backupDir) {
+        this.files = files;
+        this.backupDir = backupDir;
+    }
+
+    @Override
+    public String getServiceName() throws Exception {
+        return "Backup service: " + Arrays.toString(files);
+    }
+
+    @Override
+    public void startServiceImpl() throws Exception {
+        setRunning(true);
+        for (String file : files) {
+            if (new File(file).exists()) {
+                String backupPath = FileUtilities.getUniqueFile(backupDir + File.separator + FileUtilities.getFileName(file));
+                ProcessUtilities.run("cp", "-arT", "--", file, backupPath);
+                backupFiles.add(backupPath);
+            }
+        }
+    }
+
+    @Override
+    public void stopServiceImpl() throws Exception {
+        for (int i = 0; i < backupFiles.size(); ++i) {
+            ProcessUtilities.run("rm", "-rf", "--", files[i]);
+            ProcessUtilities.run("mv", "--", backupFiles.get(i), files[i]);
+        }
+        setRunning(false);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/services/GnomeKeyring.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,77 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.services;
+
+import org.thermostat.qa2.framework.NativeProcess;
+import org.thermostat.qa2.framework.ThermostatQAConfig;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public class GnomeKeyring extends AbstractService {
+
+    public String configurationDirectory;
+    NativeProcess process;
+
+    public GnomeKeyring() {
+        this(ThermostatQAConfig.gnomeKeyringConfigDir);
+    }
+
+    public GnomeKeyring(String configurationDirectory) {
+        this.configurationDirectory = configurationDirectory;
+    }
+
+    @Override
+    public String getServiceName() throws Exception {
+        return "Gnome keyring";
+    }
+
+    @Override
+    public void startServiceImpl() throws Exception {
+        setRunning(true);
+        process = new NativeProcess("gnome-keyring-daemon");
+        process.setLabel(getServiceName());
+        process.setEnvironmentVariable("XDG_DATA_HOME", configurationDirectory);
+        process.start();
+        process.waitFor();  
+    }
+
+    @Override
+    public void stopServiceImpl() throws Exception {
+        setRunning(false);
+        process = new NativeProcess("killall", "gnome-keyring-daemon");
+        process.setLabel(getServiceName());
+        process.start();
+        process.waitFor(); 
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/services/Service.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,44 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.services;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public interface Service {
+
+    public void start() throws Exception;
+
+    public void stop() throws Exception;
+
+    public boolean isRunning();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/services/ShellService.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,115 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.services;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.thermostat.qa2.framework.NativeProcess;
+import org.thermostat.qa2.framework.Shell;
+import org.thermostat.qa2.framework.utils.FileUtilities;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public abstract class ShellService extends AbstractProcessService {
+    
+    String commands;
+    Map<String, String> environment = new HashMap();
+    String pidFile;
+    
+    public ShellService(String... commands) {
+        this.commands = "";
+        for (String command : commands) {
+            this.commands += command + " ";
+        }
+    }
+    
+    public void addEnvVariable(String name, String value) {
+        environment.put(name, value);
+    }
+    
+    @Override
+    public NativeProcess startServiceProcess() throws Exception {
+        pidFile = "." + File.separator + getServiceName().replace(" ", "_") + ".pid";
+        pidFile = FileUtilities.getUniqueFile(pidFile);
+        Shell shell = new Shell(File.separator + "bin" + File.separator + "bash");
+        shell.setLabel(getServiceName());
+        shell.start();
+        shell.writeln("function onExit {");
+        shell.writeln("    if [ -z \"${EXIT_CODE}\" ] && [ -n \"$!\" ] ; then");
+        shell.writeln("        echo \"killing " + getServiceName() + " processes ...\"");
+        shell.writeln("        pkill -P $!");
+        shell.writeln("        wait $!");
+        shell.writeln("    fi");
+        shell.writeln("}");
+        shell.writeln("trap onExit EXIT");
+        shell.writeln("Interupted=0");
+        shell.writeln("echo $$ > " + pidFile);
+        for (Entry<String, String> entry : environment.entrySet()) {
+            shell.writeln("export " + entry.getKey() + "=" + entry.getValue());
+        }
+        shell.writeln(commands + " &");
+        shell.writeln("PROCESS_PID=$!");
+//        shell.writeln("echo \"Agent PID: ${PROCESS_PID}\"");
+        shell.writeln("wait $!");
+        shell.writeln("EXIT_CODE=$?");
+        shell.writeln("exit ${EXIT_CODE}");
+        return shell;
+    }
+    
+    @Override
+    public void stopServiceImpl() throws Exception {
+        try {
+            List<String> pid = FileUtilities.getLineListFromFile(pidFile);
+            NativeProcess process = new NativeProcess("kill", pid.get(0));
+            process.setLabel(getServiceName());
+            process.start();
+            process.waitFor();
+        } catch (FileNotFoundException e) {
+            // service already stopped
+        }
+        thread.join();
+    }
+    
+    @Override
+    public void runAfterInWrappingThread() throws Exception {
+        NativeProcess process = new NativeProcess("rm", pidFile);
+        process.setLabel(getServiceName());
+        process.start();
+        process.waitForRaw();
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/services/ThermostatAgent.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,75 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.services;
+
+import java.io.File;
+import org.thermostat.qa2.framework.ThermostatQAConfig;
+import org.thermostat.qa2.framework.utils.CommonUtilities;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public class ThermostatAgent extends ShellService {
+
+    public ThermostatAgent() {
+        this("tested");
+    }
+
+    public ThermostatAgent(String targetThermostat) {
+        this(ThermostatQAConfig.getThermostatHome(targetThermostat), ThermostatQAConfig.getThermostatUserHome(targetThermostat));
+    }
+
+    public ThermostatAgent(String thermostatHome, String thermostatUserHome) {
+        super(thermostatHome + File.separator + "bin" + File.separator + "thermostat", "agent");
+        addEnvVariable("USER_THERMOSTAT_HOME", thermostatUserHome);
+    }
+
+    @Override
+    public String getServiceName() throws Exception {
+        return "Thermostat agent";
+    }
+
+    /**
+     *
+     * @return
+     */
+    int timeout = 30;
+    int port = ThermostatQAConfig.agentPort;
+
+    @Override
+    public void startServiceImpl() throws Exception {
+        super.startServiceImpl();
+        waitForListeningPort(port, timeout);
+        CommonUtilities.sleep(3000); // wait 3 more seconds
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/services/ThermostatGui.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,83 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.services;
+
+import java.io.File;
+import org.thermostat.qa2.framework.NativeProcess;
+import org.thermostat.qa2.framework.ThermostatQAConfig;
+import org.thermostat.qa2.framework.utils.CommonUtilities;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public class ThermostatGui extends ShellService {
+
+    public ThermostatGui() {
+        this("tested");
+    }
+
+    public ThermostatGui(String targetThermostat) {
+        this(ThermostatQAConfig.getThermostatHome(targetThermostat), ThermostatQAConfig.getThermostatUserHome(targetThermostat));
+    }
+
+    public ThermostatGui(String thermostatHome, String thermostatUserHome) {
+        super(thermostatHome + File.separator + "bin" + File.separator + "thermostat", "gui", "-J-Dawt.useSystemAAFontSettings=false", "-J-Dswing.aatext=false");
+        addEnvVariable("USER_THERMOSTAT_HOME", thermostatUserHome);
+    }
+
+    @Override
+    public String getServiceName() throws Exception {
+        return "Thermostat gui";
+    }
+
+    int timeout = 30;
+
+    @Override
+    public void startServiceImpl() throws Exception {
+        super.startServiceImpl();
+        for (int i = 0; i < timeout; ++i) {
+            NativeProcess process = new NativeProcess("xdotool", "search", "--onlyvisible", "-class", "Thermostat");
+            process.setLabel("Window checker");
+            process.start();
+            int ret = process.waitForRaw();
+            if (ret == 0) {
+                CommonUtilities.sleep(2000);
+                return;
+            }
+            Thread.sleep(1000);
+            if (!isRunning()) {
+                throw new Exception(getServiceName() + ": stopped while waiting for window ");
+            }
+        }
+        throw new Exception(getServiceName() + ": expired timeout " + timeout + " s waitng for window");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/services/ThermostatService.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,72 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.services;
+
+import java.io.File;
+import org.thermostat.qa2.framework.ThermostatQAConfig;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public class ThermostatService extends ShellService {
+
+    public ThermostatService() {
+        this("tested");
+    }
+
+    public ThermostatService(String targetThermostat) {
+        this(ThermostatQAConfig.getThermostatHome(targetThermostat), ThermostatQAConfig.getThermostatUserHome(targetThermostat));
+    }
+
+    public ThermostatService(String thermostatHome, String thermostatUserHome) {
+        super(thermostatHome + File.separator + "bin" + File.separator + "thermostat", "service");
+        addEnvVariable("USER_THERMOSTAT_HOME", thermostatUserHome);
+    }
+
+    @Override
+    public String getServiceName() throws Exception {
+        return "Thermostat service";
+    }
+
+    /**
+     *
+     * @return
+     */
+    int timeout = 30;
+    int port = ThermostatQAConfig.agentPort;
+
+    @Override
+    public void startServiceImpl() throws Exception {
+        super.startServiceImpl();
+        waitForListeningPort(port, timeout);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/services/ThermostatStorage.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,117 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.services;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import org.thermostat.qa2.framework.NativeProcess;
+import org.thermostat.qa2.framework.ThermostatQAConfig;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public class ThermostatStorage extends AbstractService {
+
+    public String thermostatHome;
+    public String thermostatUserHome;
+
+    public ThermostatStorage() {
+        this("tested");
+    }
+
+    public ThermostatStorage(String targetThermostat) {
+        this(ThermostatQAConfig.getThermostatHome(targetThermostat), ThermostatQAConfig.getThermostatUserHome(targetThermostat));
+    }
+
+    public ThermostatStorage(String thermostatHome, String thermostatUserHome) {
+        this.thermostatHome = thermostatHome;
+        this.thermostatUserHome = thermostatUserHome;
+    }
+
+    @Override
+    public String getServiceName() throws Exception {
+        return "Thermostat storage";
+    }
+
+    int mongoPort = ThermostatQAConfig.mongoPort;
+    int timeout = 30;
+
+    @Override
+    public void startServiceImpl() throws Exception {
+        setRunning(true);
+        startProcess = new NativeProcess(thermostatHome + File.separator + "bin" + File.separator + "thermostat", "storage", "--start");
+        startProcess.setLabel(getServiceName());
+        startProcess.setEnvironmentVariable("USER_THERMOSTAT_HOME", thermostatUserHome);
+        if (startStdoutBuffering) {
+            startProcess.setStdOutMode(NativeProcess.MODE_LOG_AND_BUFFER);
+        }
+        startProcess.start();
+        startProcess.waitFor();
+        waitForListeningPort(mongoPort, timeout);
+    }
+
+    @Override
+    public void stopServiceImpl() throws Exception {
+        setRunning(false);
+        stopProcess = new NativeProcess(thermostatHome + File.separator + "bin" + File.separator + "thermostat", "storage", "--stop");
+        stopProcess.setLabel(getServiceName());
+        stopProcess.setEnvironmentVariable("USER_THERMOSTAT_HOME", thermostatUserHome);
+        if (stopStdoutBuffering) {
+            stopProcess.setStdOutMode(NativeProcess.MODE_LOG_AND_BUFFER);
+        }
+        stopProcess.start();
+        stopProcess.waitFor();
+    }
+
+    boolean startStdoutBuffering = false;
+    boolean stopStdoutBuffering = false;
+    NativeProcess startProcess;
+    NativeProcess stopProcess;
+
+    public void setStartStdoutBuffering(boolean b) {
+        startStdoutBuffering = b;
+    }
+
+    public void setStopStdoutBuffering(boolean b) {
+        stopStdoutBuffering = b;
+    }
+
+    public List<String> getStartStdoutLines() throws IOException {
+        return startProcess.getStdoutLines();
+    }
+
+    public List<String> getStopStdoutLines() throws IOException {
+        return stopProcess.getStdoutLines();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/services/Tomcat.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,90 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.services;
+
+import java.io.File;
+import org.thermostat.qa2.framework.NativeProcess;
+import org.thermostat.qa2.framework.ThermostatQAConfig;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public class Tomcat extends AbstractService {
+
+    String tomcatHome;
+    String thermostatHome;
+
+    public Tomcat() {
+        this("tested");
+    }
+
+    public Tomcat(String targetThermostat) {
+        this(ThermostatQAConfig.tomcatHome, ThermostatQAConfig.getThermostatHome(targetThermostat));
+    }
+
+    public Tomcat(String tomcatHome, String thermostatHome) {
+        this.tomcatHome = tomcatHome;
+        this.thermostatHome = thermostatHome;
+    }
+
+    @Override
+    public String getServiceName() throws Exception {
+        return "Apache Tomcat";
+    }
+
+    int webPort = ThermostatQAConfig.webStoragePort;
+    int shutdownPort = 8005;
+    int timeout = 30;
+
+    @Override
+    public void startServiceImpl() throws Exception {
+        setRunning(true);
+        NativeProcess process = new org.thermostat.qa2.framework.NativeProcess(tomcatHome + File.separator + "bin" + File.separator + "startup.sh");
+        process.setLabel(getServiceName());
+        if (thermostatHome != null) {
+            process.setEnvironmentVariable("JAVA_OPTS", "-Djava.security.auth.login.config=" + thermostatHome + File.separator + "etc" + File.separator + "thermostat_jaas.conf");
+        }
+        process.start();
+        process.waitFor();
+        waitForListeningPort(webPort, timeout);
+        waitForListeningPort(shutdownPort, timeout);
+    }
+
+    @Override
+    public void stopServiceImpl() throws Exception {
+        setRunning(false);
+        NativeProcess process = new org.thermostat.qa2.framework.NativeProcess(tomcatHome + File.separator + "bin" + File.separator + "shutdown.sh");
+        process.setLabel(getServiceName());
+        process.start();
+        process.waitFor();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/streamprocessors/BufferingStreamProcessor.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,92 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.streamprocessors;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import org.thermostat.qa2.framework.SimpleLogger;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public class BufferingStreamProcessor extends LoggingStreamProcessor {
+
+    List<String> lineList = new ArrayList();
+    boolean log;
+    boolean closed = false;
+
+    public BufferingStreamProcessor(InputStream is, SimpleLogger l, boolean log) {
+        super(is, l);
+        this.log = log;
+    }
+
+    @Override
+    public void run() {
+        InputStreamReader isr = new InputStreamReader(is);
+        BufferedReader reader = new BufferedReader(isr);
+        String line;
+        try {
+            while ((line = reader.readLine()) != null) {
+                if (log) {
+                    logger.log(line);
+                }
+                bufferLine(line);
+            }
+        } catch (IOException ex) {
+        } finally {
+            try {
+                reader.close();
+            } catch (IOException ex) {
+            }
+            synchronized(this){
+                closed = true;
+            }
+        }
+    }
+
+    private synchronized void bufferLine(String line) {
+        lineList.add(line);
+    }
+
+    public synchronized List<String> getLineList() throws IOException {
+        List<String> list = lineList;
+        if(!closed){
+            lineList = new ArrayList();
+        }
+        return list;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/streamprocessors/LoggingStreamProcessor.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,69 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.streamprocessors;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import org.thermostat.qa2.framework.SimpleLogger;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public class LoggingStreamProcessor extends StreamProcessor {
+
+    SimpleLogger logger;
+    String s;
+
+    public LoggingStreamProcessor(InputStream is, SimpleLogger logger) {
+        super(is);
+        this.logger = logger;
+    }
+
+    @Override
+    public void run() {
+        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+        String line;
+        try {
+            while ((line = reader.readLine()) != null) {
+                logger.log(line);
+            }
+        } catch (IOException ex) {
+        } finally {
+            try {
+                reader.close();
+            } catch (IOException ex) {
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/streamprocessors/StreamProcessor.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,65 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.streamprocessors;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public class StreamProcessor implements Runnable {
+
+    public final InputStream is;
+
+    public StreamProcessor(InputStream is) {
+        this.is = is;
+    }
+
+    @Override
+    public void run() {
+        try {
+            while (is.read() >= 0);
+        } catch (IOException ex) {
+            Logger.getLogger(StreamProcessor.class.getName()).log(Level.SEVERE, null, ex);
+        } finally {
+            try {
+                is.close();
+            } catch (IOException ex) {
+                Logger.getLogger(StreamProcessor.class.getName()).log(Level.SEVERE, null, ex);
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/utils/AnnotationUtilities.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,121 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.utils;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import org.thermostat.qa2.framework.annotations.InheritAnnotations;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public class AnnotationUtilities {
+
+    public static Annotation[] getAnnotations(Object o) {
+        if (o instanceof Class) {
+            Class oc = (Class) o;
+            return oc.getAnnotations();
+        } else if (o instanceof AccessibleObject) {
+            AccessibleObject ao = (AccessibleObject) o;
+            return ao.getAnnotations();
+        } else {
+            throw new IllegalArgumentException("Annotation cannot be read from instance of class: " + o.getClass());
+        }
+    }
+
+    public static <T extends AccessibleObject> List getAnnotatedObjects(List<T> objects, Class annotationType) {
+        ArrayList<T> annotated = new ArrayList();
+        for (T o : objects) {
+            if (o.isAnnotationPresent(annotationType)) {
+                annotated.add(o);
+            }
+        }
+        return annotated;
+    }
+
+    public static <A extends Annotation> A findAnnotation(Object o, Class<A> c) {
+        if (o instanceof Class) {
+            Class oc = (Class) o;
+            return (A) oc.getAnnotation(c);
+        } else if (o instanceof AccessibleObject) {
+            AccessibleObject ao = (AccessibleObject) o;
+            return ao.getAnnotation(c);
+        } else {
+            throw new IllegalArgumentException("Annotation cannot be read from instance of class: " + o.getClass());
+        }
+    }
+
+    // also recursivly search in annotations annotating annotatations
+    public static <A extends Annotation> A findAnnotationWithInheritance(Object o, Class<A> c) {
+        A annotation = findAnnotation(o, c);
+        if (annotation != null) {
+            return annotation;
+        }
+        Annotation[] annotations = getAnnotations(o);
+        for (Annotation a : annotations) {
+            Class type = a.annotationType();
+            if (findAnnotation(type, InheritAnnotations.class) != null) {
+                annotation = findAnnotationWithInheritance(type, c);
+                if (annotation != null) {
+                    return annotation;
+                }
+            }
+        }
+        return null;
+    }
+
+    public static <A extends Annotation> A findAnnotationWithClassInheritance(AccessibleObject member, Class owningClass, Class<A> annotationClass) {
+        A annotation;
+        annotation = AnnotationUtilities.findAnnotation(member, annotationClass);
+        if (annotation != null) {
+            return annotation;
+        }
+        annotation = AnnotationUtilities.findAnnotation(owningClass, annotationClass);
+        if (annotation != null) {
+            return annotation;
+        }
+        annotation = AnnotationUtilities.findAnnotationWithInheritance(member, annotationClass);
+        if (annotation != null) {
+            return annotation;
+        }
+        annotation = AnnotationUtilities.findAnnotationWithInheritance(owningClass, annotationClass);
+        return annotation;
+    }
+
+    public static <A extends Annotation> A findAnnotationWithClassInheritance(Method method, Class<A> annotationClass) {
+        return findAnnotationWithClassInheritance(method, method.getDeclaringClass(), annotationClass);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/utils/CommonUtilities.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,85 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.utils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public class CommonUtilities {
+
+    public static boolean findInLineList(List<String> lines, String pattern, boolean exact) {
+        for (String line : lines) {
+            if (exact) {
+                if (line.equals(pattern)) {
+                    return true;
+                }
+            } else {
+                if (line.contains(pattern)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public static List<String> addLinesToList(List<String> list, BufferedReader br) throws IOException {
+        String line;
+        while ((line = br.readLine()) != null) {
+            list.add(line);
+        }
+        return list;
+    }
+
+    public static void sleep(long milis) {
+        try {
+            Thread.sleep(milis);
+        } catch (InterruptedException ex) {
+        }
+    }
+
+    public static void printHeading(String text) {
+        System.out.println("INFO: ------------------------------------------------------------");
+        System.out.println("INFO: " + text);
+        System.out.println("INFO: ------------------------------------------------------------");
+    }
+
+    public static List getLineList(BufferedReader br) throws IOException {
+        ArrayList<String> list = new ArrayList();
+        return CommonUtilities.addLinesToList(list, br);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/utils/FileUtilities.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,97 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.utils;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public class FileUtilities {
+
+    public static String getFileName(String s) {
+        int i = s.lastIndexOf(File.separator);
+        return i >= 0 ? s.substring(i + 1) : s;
+    }
+
+    public static String getUniqueFile(String file) {
+        String name = file;
+        int counter = 1;
+        while (new File(name).exists()) {
+            name = file + counter++;
+        }
+        return name;
+    }
+
+    public static void copyFile(String src, String dest) throws Exception {
+        ProcessUtilities.run("cp", "--", src, dest);
+    }
+
+    public static List<String> getLineListFromFile(String file) throws FileNotFoundException, IOException {
+        ArrayList<String> list = new ArrayList();
+        return addLinesFromFileToList(list, file);
+    }
+
+    public static List<String> addLinesFromFileToList(List<String> list, String file) throws FileNotFoundException, IOException {
+        FileReader fis = null;
+        BufferedReader br = null;
+        try {
+            fis = new FileReader(file);
+            br = new BufferedReader(fis);
+            return CommonUtilities.addLinesToList(list, br);
+        } finally {
+            if (br != null) {
+                try {
+                    br.close();
+                } catch (IOException ex) {
+                    Logger.getLogger(FileUtilities.class.getName()).log(Level.SEVERE, null, ex);
+                }
+            }
+            if (fis != null) {
+                try {
+                    fis.close();
+                } catch (IOException ex) {
+                    Logger.getLogger(FileUtilities.class.getName()).log(Level.SEVERE, null, ex);
+                }
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/utils/ProcessUtilities.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,112 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.utils;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import org.thermostat.qa2.framework.NativeProcess;
+import org.thermostat.qa2.framework.Shell;
+import org.thermostat.qa2.framework.ThermostatQAConfig;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public class ProcessUtilities {
+
+    public static void run(String... commands) throws Exception {
+        int ret = runRaw(commands);
+        if (ret != 0) {
+            throw new Exception(commands[0] + ": nonzero exit value " + ret);
+        }
+    }
+
+    public static List<String> runGetOutput(String... commands) throws Exception {
+        NativeProcess process = new NativeProcess(commands);
+        process.setStdOutMode(NativeProcess.MODE_LOG_AND_BUFFER);
+        process.start();
+        process.waitFor();
+        return process.getStdoutLines();
+    }
+
+    public static int runRaw(String... commands) throws IOException, InterruptedException {
+        NativeProcess process = new NativeProcess(commands);
+        process.start();
+        return process.waitForRaw();
+    }
+
+    public static void cloneHg(String url, String dst) throws Exception {
+        run("hg", "clone", url, dst);
+    }
+
+    public static void mavenCleanInstall(String path) throws Exception {
+        NativeProcess process = new NativeProcess("mvn", "-Dmaven.test.skip=true", "clean", "install");
+        process.setWorkingDirectory(path);
+        process.start();
+        process.waitFor();
+    }
+
+    public static void shellRun(String cmd) throws Exception {
+        Shell shell = new Shell(File.separator + "bin" + File.separator + "sh", "-c", cmd);
+        shell.start();
+        shell.waitFor();
+    }
+
+    public static List<String> shellRunGetOutput(String cmd) throws Exception {
+        Shell shell = new Shell(File.separator + "bin" + File.separator + "sh", "-c", cmd);
+        shell.setStdOutMode(NativeProcess.MODE_LOG_AND_BUFFER);
+        shell.start();
+        shell.waitFor();
+        return shell.getStdoutLines();
+    }
+
+    public static String createCommandElement(String s) {
+        return s.contains(" ") ? "\"" + s + "\"" : s;
+    }
+
+    public static Shell createMongoShell() {
+        String[] mongoCmds = {"mongo", "127.0.0.1:" + ThermostatQAConfig.mongoPort};
+        Shell mongo = new Shell(mongoCmds);
+        return mongo;
+    }
+
+    public static Shell createThermostatShell() {
+        return createThermostatShell("tested");
+    }
+
+    public static Shell createThermostatShell(String target) {
+        String[] thermostatCmds = {ThermostatQAConfig.getThermostatExecutablePath(target), "shell"};
+        Shell thermostat = new Shell(thermostatCmds);
+        return thermostat;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/thermostat/qa2/framework/utils/ThermostatUtilities.java	Tue Mar 10 13:38:36 2015 +0100
@@ -0,0 +1,132 @@
+/*
+ ThermostatQA - test framework for Thermostat Monitoring Tool
+
+ Copyright 2015 Red Hat, Inc.
+
+ This file is part of ThermostatQA
+
+ ThermostatQA is distributed under the GNU General Public License,
+ version 2 or any later version (with a special exception described
+ below, commonly known as the "Classpath Exception").
+
+ A copy of GNU General Public License (GPL) is included in this
+ distribution, in the file COPYING.
+
+ Linking ThermostatQA code with other modules is making a combined work
+ based on ThermostatQA.  Thus, the terms and conditions of the GPL
+ cover the whole combination.
+
+ As a special exception, the copyright holders of ThermostatQA 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 ThermostatQA code.  If you modify ThermostatQA, you may
+ extend this exception to your version of the software, but you are
+ not obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package org.thermostat.qa2.framework.utils;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import org.thermostat.qa2.framework.NativeProcess;
+import org.thermostat.qa2.framework.Shell;
+import org.thermostat.qa2.framework.ThermostatQAConfig;
+import org.thermostat.qa2.framework.services.ThermostatStorage;
+
+/**
+ *
+ * @author Zdeněk Žamberský
+ */
+public class ThermostatUtilities {
+
+    public static void setupDefaultMongoUsers(String target) throws Exception {
+        ThermostatStorage storage = new ThermostatStorage(target);
+        storage.start();
+        Map map = new Properties();
+        ThermostatQAConfig.Login agentLogin = ThermostatQAConfig.getAgentLogin();
+        ThermostatQAConfig.Login clientLogin = ThermostatQAConfig.getClientLogin();
+        map.put(agentLogin.getUsername(), agentLogin.getPassword());
+        map.put(clientLogin.getUsername(), clientLogin.getPassword());
+        setupMongoUsers("127.0.0.1:" + ThermostatQAConfig.mongoPort, map);
+        storage.stop();
+    }
+
+    public static void setupMongoUsers(String address, Map<String, String> users) throws Exception {
+        CommonUtilities.printHeading("Setting up mongo users ...");
+        Shell mongo = new Shell("mongo", address);
+        mongo.setLabel("Mongo users setup");
+        mongo.start();
+        mongo.writeln("use thermostat");
+        mongo.writeln("db.auth(\"mongodevuser\", \"mongodevpassword\")");
+        for (Map.Entry<String, String> entry : users.entrySet()) {
+            mongo.writeln("db.addUser(\"" + entry.getKey() + "\", \"" + entry.getValue() + "\")");
+        }
+        mongo.writeln("quit()");
+        mongo.waitFor();
+    }
+
+    public static void runThermostatSetup(String name) throws Exception {
+        runThermostatSetup(ThermostatQAConfig.getThermostatHome(name), ThermostatQAConfig.getThermostatUserHome(name));
+    }
+
+    public static void runThermostatSetup(String thermostatHome, String thermostatUserHome) throws Exception {
+        NativeProcess process;
+        CommonUtilities.printHeading("Running Thermostat setup ...");
+        String setupPath1 = thermostatHome + File.separator + "bin" + File.separator + "thermostat-devsetup";
+        String setupPath2 = thermostatHome + File.separator + "bin" + File.separator + "thermostat-setup-user-home";
+        if (new File(setupPath1).exists()) {
+            process = new NativeProcess(setupPath1);
+        } else if (new File(setupPath2).exists()) {
+            process = new NativeProcess(setupPath2);
+        } else {
+            throw new Exception("script to setup thermostat cannot be found in: " + thermostatHome + File.separator + "bin");
+        }
+        process.setLabel("Thermostat setup");
+        process.setEnvironmentVariable("USER_THERMOSTAT_HOME", thermostatUserHome);
+        process.start();
+        process.waitFor();
+    }
+
+    public static void copyThermostatConfigFiles(String setupTarget, boolean web, boolean badAgentLogin, boolean badClientLogin) throws Exception {
+        CommonUtilities.printHeading("Copying thermostat config files ...");
+        String thermostatHome = ThermostatQAConfig.getThermostatHome(setupTarget);
+        String thermostatUserHome = ThermostatQAConfig.getThermostatUserHome(setupTarget);
+        String[] confFiles = ThermostatQAConfig.getThermostatUserConfigFiles(setupTarget, web, badAgentLogin, badClientLogin);
+        String[] confFilesNames = ThermostatQAConfig.getThermostatUserConfigFilesNames();
+        for (int i = 0; i < confFiles.length; ++i) {
+            String file = confFiles[i];
+            String name = confFilesNames[i];
+            FileUtilities.copyFile(file, thermostatUserHome + File.separator + "etc" + File.separator + name);
+        }
+        confFiles = ThermostatQAConfig.getThermostatConfigFiles(setupTarget, web);
+        for (String file : confFiles) {
+            FileUtilities.copyFile(file, thermostatHome + File.separator + "etc" + File.separator + file.substring(file.lastIndexOf(File.separator) + 1));
+        }
+    }
+
+    public static List<String> getListVmsOutput() throws Exception {
+        ThermostatQAConfig.Login login = ThermostatQAConfig.getClientLogin();
+        return getListVmsOutput("tested", login.getUsername(), login.getPassword());
+    }
+
+    public static List<String> getListVmsOutput(String target, String username, String password) throws Exception {
+        String[] thermostatCmds = {ThermostatQAConfig.getThermostatExecutablePath(target), "shell"};
+        Shell thermostat = new Shell(thermostatCmds);
+        thermostat.setEnvironmentVariable("USER_THERMOSTAT_HOME", ThermostatQAConfig.getThermostatUserHome(target));
+        thermostat.setStdOutMode(NativeProcess.MODE_LOG_AND_BUFFER);
+        thermostat.start();
+        thermostat.writeln("list-vms");
+        thermostat.writeln(username);
+        thermostat.writeln(password);
+        thermostat.writeln("exit");
+        thermostat.waitFor();
+        return thermostat.getStdoutLines();
+    }
+
+}