changeset 733:25496a5a18ce

Clean up and add more launcher tests Reviewed-by: jerboaa Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-October/003870.html
author Omair Majid <omajid@redhat.com>
date Wed, 24 Oct 2012 13:08:43 -0400
parents c0234110ca5f
children 869649c31bb0
files common/core/src/main/java/com/redhat/thermostat/test/StubBundleContext.java launcher/src/main/java/com/redhat/thermostat/launcher/DbServiceFactory.java launcher/src/main/java/com/redhat/thermostat/launcher/internal/LauncherImpl.java launcher/src/test/java/com/redhat/thermostat/launcher/LauncherTest.java launcher/src/test/java/com/redhat/thermostat/launcher/internal/CommandLineArgumentsParserTest.java launcher/src/test/java/com/redhat/thermostat/launcher/internal/LauncherTest.java tools/src/main/java/com/redhat/thermostat/tools/cli/ConnectCommand.java tools/src/test/java/com/redhat/thermostat/tools/cli/ConnectCommandTest.java
diffstat 8 files changed, 589 insertions(+), 516 deletions(-) [+]
line wrap: on
line diff
--- a/common/core/src/main/java/com/redhat/thermostat/test/StubBundleContext.java	Wed Oct 24 14:59:12 2012 +0200
+++ b/common/core/src/main/java/com/redhat/thermostat/test/StubBundleContext.java	Wed Oct 24 13:08:43 2012 -0400
@@ -83,6 +83,7 @@
         }
     }
 
+    private List<Bundle> bundles = new ArrayList<>();
     private List<ServiceInformation> registeredServices = new ArrayList<>();
     private List<ListenerSpec> registeredListeners = new ArrayList<>();
 
@@ -112,7 +113,10 @@
 
     @Override
     public Bundle getBundle(long id) {
-        throw new NotImplementedException();
+        if (id > Integer.MAX_VALUE) {
+            throw new NotImplementedException();
+        }
+        return bundles.get((int) id);
     }
 
     @Override
@@ -204,7 +208,12 @@
 
     @Override
     public ServiceReference getServiceReference(Class clazz) {
-        throw new NotImplementedException();
+        for (ServiceInformation info : registeredServices) {
+            if (info.serviceInterface.equals(clazz)) {
+                return new StubServiceReference(info);
+            }
+        }
+        return null;
     }
 
     @Override
@@ -250,6 +259,10 @@
      * Our custom methods
      */
 
+    public void setBundle(int i, Bundle bundle) {
+        bundles.add(i, bundle);
+    }
+
     public boolean isServiceRegistered(String serviceName, Class<?> implementationClass) {
         for (ServiceInformation info : registeredServices) {
             if (info.serviceInterface.equals(serviceName) && info.implementation.getClass().equals(implementationClass)) {
@@ -279,4 +292,6 @@
         StubServiceRegistration reg = (StubServiceRegistration) registration;
         return reg.getInfo().exportedReferences;
     }
+
+
 }
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/DbServiceFactory.java	Wed Oct 24 14:59:12 2012 +0200
+++ b/launcher/src/main/java/com/redhat/thermostat/launcher/DbServiceFactory.java	Wed Oct 24 13:08:43 2012 -0400
@@ -44,7 +44,7 @@
  */
 public class DbServiceFactory {
 
-    public static DbService createDbService(String username, String password, String dbUrl) {
+    public DbService createDbService(String username, String password, String dbUrl) {
         return DbServiceImpl.create(username, password, dbUrl);
     }
 }
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/LauncherImpl.java	Wed Oct 24 14:59:12 2012 +0200
+++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/LauncherImpl.java	Wed Oct 24 13:08:43 2012 -0400
@@ -46,6 +46,7 @@
 import org.apache.commons.cli.Options;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleException;
+import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
 
 import com.redhat.thermostat.bundles.OSGiRegistry;
@@ -67,7 +68,6 @@
 import com.redhat.thermostat.common.tools.ApplicationState;
 import com.redhat.thermostat.common.tools.BasicCommand;
 import com.redhat.thermostat.common.utils.LoggingUtils;
-import com.redhat.thermostat.common.utils.OSGIUtils;
 import com.redhat.thermostat.launcher.CommonCommandOptions;
 import com.redhat.thermostat.launcher.DbService;
 import com.redhat.thermostat.launcher.DbServiceFactory;
@@ -89,12 +89,20 @@
 
     private BundleContext context;
     private OSGiRegistry registry;
+    private final DbServiceFactory dbServiceFactory;
     
-    public LauncherImpl(BundleContext context, CommandContextFactory cmdCtxFactory,
-            OSGiRegistry registry) {
+    public LauncherImpl(BundleContext context, CommandContextFactory cmdCtxFactory, OSGiRegistry registry) {
+        this(context, cmdCtxFactory, registry, new LoggingInitializer(), new DbServiceFactory());
+    }
+
+    LauncherImpl(BundleContext context, CommandContextFactory cmdCtxFactory, OSGiRegistry registry,
+            LoggingInitializer loggingInitializer, DbServiceFactory dbServiceFactory) {
         this.context = context;
         this.cmdCtxFactory = cmdCtxFactory;
         this.registry = registry;
+        this.dbServiceFactory = dbServiceFactory;
+
+        loggingInitializer.initialize();
     }
 
     @Override
@@ -105,14 +113,9 @@
     @Override
     public synchronized void run(Collection<ActionListener<ApplicationState>> listeners) {
         usageCount++;
+        waitForArgs();
+
         try {
-            argsBarrier.acquire();
-        } catch (InterruptedException e) {
-            e.printStackTrace();
-        }
-        try {
-            // TODO move this logging init out where it's not part of every command launch
-            initLogging();
             if (hasNoArguments()) {
                 runHelpCommand();
             } else if (isVersionQuery()) {
@@ -138,6 +141,14 @@
         argsBarrier.release();
     }
 
+    private void waitForArgs() {
+        try {
+            argsBarrier.acquire();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+    }
+
     private boolean isLastLaunch() {
         return usageCount == 0;
     }
@@ -158,19 +169,6 @@
         this.prefs = prefs;
     }
 
-    private void initLogging() {
-        try {
-            LoggingUtils.loadGlobalLoggingConfig();
-        } catch (InvalidConfigurationException e) {
-            System.err.println("WARNING: Could not read global Thermostat logging configuration.");
-        }
-        try {
-            LoggingUtils.loadUserLoggingConfig();
-        } catch (InvalidConfigurationException e) {
-            // We intentionally ignore this.
-        }
-    }
-
     private boolean hasNoArguments() {
         return args.length == 0;
     }
@@ -267,25 +265,27 @@
         CommandContext ctx = cmdCtxFactory.createContext(args);
         
         if (prefs == null) {
-            prefs = new ClientPreferences(OSGIUtils.getInstance().getService(Keyring.class));
+            ServiceReference keyringReference = context.getServiceReference(Keyring.class);
+            Keyring keyring = (Keyring) context.getService(keyringReference);
+            prefs = new ClientPreferences(keyring);
         }
         
         if (cmd.isStorageRequired()) {
-            DbService service = OSGIUtils.getInstance().getServiceAllowNull(DbService.class);
-            if (service == null) {
+            ServiceReference dbServiceReference = context.getServiceReference(DbService.class);
+            if (dbServiceReference == null) {
                 String dbUrl = ctx.getArguments().getArgument(CommonCommandOptions.DB_URL_ARG);
                 if (dbUrl == null) {
                     dbUrl = prefs.getConnectionUrl();
                 }
                 String username = ctx.getArguments().getArgument(CommonCommandOptions.USERNAME_ARG);
                 String password = ctx.getArguments().getArgument(CommonCommandOptions.PASSWORD_ARG);
-                service = DbServiceFactory.createDbService(username, password, dbUrl);
+                DbService service = dbServiceFactory.createDbService(username, password, dbUrl);
                 try {
                     service.connect();
                 } catch (ConnectionException ex) {
                     throw new CommandException("Could not connect to: " + dbUrl, ex);
                 }
-                ServiceRegistration registration = OSGIUtils.getInstance().registerService(DbService.class, service);
+                ServiceRegistration registration = context.registerService(DbService.class, service, null);
                 service.setServiceRegistration(registration);
             }
         }
@@ -296,4 +296,18 @@
         return args[0].equals(Version.VERSION_OPTION);
     }
 
+    static class LoggingInitializer {
+        public void initialize() {
+            try {
+                LoggingUtils.loadGlobalLoggingConfig();
+            } catch (InvalidConfigurationException e) {
+                System.err.println("WARNING: Could not read global Thermostat logging configuration.");
+            }
+            try {
+                LoggingUtils.loadUserLoggingConfig();
+            } catch (InvalidConfigurationException e) {
+                // We intentionally ignore this.
+            }
+        }
+    }
 }
--- a/launcher/src/test/java/com/redhat/thermostat/launcher/LauncherTest.java	Wed Oct 24 14:59:12 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,480 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.launcher;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.isA;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.Matchers.any;
-
-import java.io.IOException;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.apache.commons.cli.Option;
-import org.apache.commons.cli.Options;
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.ServiceReference;
-import org.osgi.framework.ServiceRegistration;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
-
-import com.redhat.thermostat.bundles.OSGiRegistry;
-import com.redhat.thermostat.common.ActionListener;
-import com.redhat.thermostat.common.ActionNotifier;
-import com.redhat.thermostat.common.ApplicationInfo;
-import com.redhat.thermostat.common.Version;
-import com.redhat.thermostat.common.appctx.ApplicationContext;
-import com.redhat.thermostat.common.appctx.ApplicationContextUtil;
-import com.redhat.thermostat.common.cli.Arguments;
-import com.redhat.thermostat.common.cli.CommandContext;
-import com.redhat.thermostat.common.cli.CommandException;
-import com.redhat.thermostat.common.cli.CommandInfo;
-import com.redhat.thermostat.common.cli.CommandInfoNotFoundException;
-import com.redhat.thermostat.common.cli.CommandInfoSource;
-import com.redhat.thermostat.common.config.ClientPreferences;
-import com.redhat.thermostat.common.locale.LocaleResources;
-import com.redhat.thermostat.common.locale.Translate;
-import com.redhat.thermostat.common.tools.ApplicationState;
-import com.redhat.thermostat.common.tools.BasicCommand;
-import com.redhat.thermostat.common.utils.OSGIUtils;
-import com.redhat.thermostat.launcher.internal.HelpCommand;
-import com.redhat.thermostat.launcher.internal.LauncherImpl;
-import com.redhat.thermostat.test.TestCommandContextFactory;
-import com.redhat.thermostat.test.TestTimerFactory;
-import com.redhat.thermostat.test.cli.TestCommand;
-import com.redhat.thermostat.utils.keyring.Keyring;
-import com.redhat.thermostat.utils.keyring.KeyringProvider;
-
-@RunWith(PowerMockRunner.class)
-@PrepareForTest({FrameworkUtil.class, DbServiceFactory.class, HelpCommand.class})
-public class LauncherTest {
-    
-    private static String defaultKeyringProvider;
-    private static final String name1 = "test1";
-    private static final String name2 = "test2";
-    private static final String name3 = "test3";
-      
-    @BeforeClass
-    public static void beforeClassSetUp() {
-        defaultKeyringProvider = System.getProperty(KeyringProvider.KEYRING_FACTORY_PROPERTY);
-    }
-    
-    @AfterClass
-    public static void afterClassTearDown() {
-        if (defaultKeyringProvider != null) {
-            System.setProperty(KeyringProvider.KEYRING_FACTORY_PROPERTY, defaultKeyringProvider);
-        }
-    }
-    
-    private static class TestCmd1 implements TestCommand.Handle {
-
-        @Override
-        public void run(CommandContext ctx) {
-            Arguments args = ctx.getArguments();
-            ctx.getConsole().getOutput().print(args.getArgument("arg1") + ", " + args.getArgument("arg2"));
-        }
-    }
-
-    private static class TestCmd2 implements TestCommand.Handle {
-        @Override
-        public void run(CommandContext ctx) {
-            Arguments args = ctx.getArguments();
-            ctx.getConsole().getOutput().print(args.getArgument("arg4") + ": " + args.getArgument("arg3"));
-        }
-    }
-
-    private TestCommandContextFactory  ctxFactory;
-    private BundleContext bundleContext;
-    private TestTimerFactory timerFactory;
-    private OSGiRegistry registry;
-    private ActionNotifier<ApplicationState> notifier;
-
-    @Before
-    public void setUp() {
-
-        ApplicationContextUtil.resetApplicationContext();
-        timerFactory = new TestTimerFactory();
-        ApplicationContext.getInstance().setTimerFactory(timerFactory);
-        setupCommandContextFactory();
-
-        TestCommand cmd1 = new TestCommand(name1, new TestCmd1());
-        CommandInfo info1 = mock(CommandInfo.class);
-        when(info1.getName()).thenReturn(name1);
-        Options options1 = new Options();
-        Option opt1 = new Option(null, "arg1", true, null);
-        options1.addOption(opt1);
-        Option opt2 = new Option(null, "arg2", true, null);
-        options1.addOption(opt2);
-        cmd1.addOptions(opt1, opt2);
-        cmd1.setDescription("description 1");
-        when(info1.getDescription()).thenReturn("description 1");
-        when(info1.getOptions()).thenReturn(options1);
-        TestCommand cmd2 = new TestCommand("test2", new TestCmd2());
-        CommandInfo info2 = mock(CommandInfo.class);
-        when(info2.getName()).thenReturn(name2);
-        Options options2 = new Options();
-        Option opt3 = new Option(null, "arg3", true, null);
-        options2.addOption(opt3);
-        Option opt4 = new Option(null, "arg4", true, null);
-        options2.addOption(opt4);
-        cmd2.addOptions(opt3, opt4);
-        cmd2.setDescription("description 2");
-        when(info2.getDescription()).thenReturn("description 2");
-        when(info2.getOptions()).thenReturn(options2);
-
-        TestCommand cmd3 = new TestCommand(name3);
-        CommandInfo info3 = mock(CommandInfo.class);
-        when(info3.getName()).thenReturn(name3);
-        cmd3.setStorageRequired(true);
-        cmd3.setDescription("description 3");
-        when(info3.getDescription()).thenReturn("description 3");
-        when(info3.getOptions()).thenReturn(new Options());
-
-        BasicCommand basicCmd = mock(BasicCommand.class);
-        CommandInfo basicInfo = mock(CommandInfo.class);
-        when(basicCmd.getName()).thenReturn("basic");
-        when(basicInfo.getName()).thenReturn("basic");
-        when(basicCmd.getDescription()).thenReturn("nothing that means anything");
-        when(basicInfo.getDescription()).thenReturn("nothing that means anything");
-        when(basicCmd.isStorageRequired()).thenReturn(false);
-        Options options = new Options();
-        when(basicCmd.getOptions()).thenReturn(options);
-        when(basicInfo.getOptions()).thenReturn(options);
-        notifier = mock(ActionNotifier.class);
-        when(basicCmd.getNotifier()).thenReturn(notifier);
-        CommandInfo helpCommandInfo = mock(CommandInfo.class);
-        when(helpCommandInfo.getName()).thenReturn("help");
-        when(helpCommandInfo.getDescription()).thenReturn("print help information");
-        when(helpCommandInfo.getDependencyResourceNames()).thenReturn(new ArrayList<String>());
-        when(helpCommandInfo.getOptions()).thenReturn(new Options());
-        when(helpCommandInfo.getUsage()).thenReturn("thermostat help");
-
-        ctxFactory.getCommandRegistry().registerCommands(Arrays.asList(new HelpCommand(), cmd1, cmd2, cmd3, basicCmd));
-
-        registry = mock(OSGiRegistry.class);
-
-        CommandInfoSource infos = mock(CommandInfoSource.class);
-        when(infos.getCommandInfo(name1)).thenReturn(info1);
-        when(infos.getCommandInfo(name2)).thenReturn(info2);
-        when(infos.getCommandInfo(name3)).thenReturn(info3);
-        when(infos.getCommandInfo("basic")).thenReturn(basicInfo);
-        when(infos.getCommandInfo("help")).thenReturn(helpCommandInfo);
-        Collection<CommandInfo> infoList = new ArrayList<CommandInfo>();
-        infoList.add(helpCommandInfo);
-        infoList.add(basicInfo);
-        infoList.add(info1);
-        infoList.add(info2);
-        infoList.add(info3);
-        when(infos.getCommandInfos()).thenReturn(infoList);
-
-        PowerMockito.mockStatic(FrameworkUtil.class);
-        Bundle bundle = mock(Bundle.class);
-        BundleContext bCtx = mock(BundleContext.class);
-        when(bundle.getBundleContext()).thenReturn(bCtx);
-        ServiceReference infosRef = mock(ServiceReference.class);
-        when(bCtx.getServiceReference(CommandInfoSource.class)).thenReturn(infosRef);
-        when(bCtx.getService(infosRef)).thenReturn(infos);
-        when(FrameworkUtil.getBundle(isA(HelpCommand.class.getClass()))).thenReturn(bundle);
-    }
-
-    private void setupCommandContextFactory() {
-        Bundle sysBundle = mock(Bundle.class);
-        bundleContext = mock(BundleContext.class);
-        when(bundleContext.getBundle(0)).thenReturn(sysBundle);
-        ctxFactory = new TestCommandContextFactory(bundleContext);
-    }
-
-
-    @After
-    public void tearDown() {
-        ctxFactory = null;
-        ApplicationContextUtil.resetApplicationContext();
-    }
-
-    @Test
-    public void testMain() {
-        runAndVerifyCommand(new String[] {name1, "--arg1", "Hello", "--arg2", "World"}, "Hello, World");
-
-        ctxFactory.reset();
-
-        runAndVerifyCommand(new String[] {"test2", "--arg3", "Hello", "--arg4", "World"}, "World: Hello");
-    }
-
-    @Test
-    public void testMainNoArgs() {
-        String expected = "list of commands:\n\n"
-                        + " help          print help information\n"
-                        + " basic         nothing that means anything\n"
-                        + " test1         description 1\n"
-                        + " test2         description 2\n"
-                        + " test3         description 3\n";
-        runAndVerifyCommand(new String[0], expected);
-    }
-
-    @Test
-    public void verifySetLogLevel() {
-        runAndVerifyCommand(new String[] {name1, "--logLevel", "WARNING", "--arg1", "Hello", "--arg2", "World"}, "Hello, World");
-        Logger globalLogger = Logger.getLogger("com.redhat.thermostat");
-        assertEquals(Level.WARNING, globalLogger.getLevel());
-    }
-
-    @Test
-    public void testMainBadCommand1() {
-        String expected = "unknown command '--help'\n"
-            + "list of commands:\n\n"
-            + " help          print help information\n"
-            + " basic         nothing that means anything\n"
-            + " test1         description 1\n"
-            + " test2         description 2\n"
-            + " test3         description 3\n";
-        runAndVerifyCommand(new String[] {"--help"}, expected);
-    }
-
-    @Test
-    public void testMainBadCommand2() {
-        String expected = "unknown command '-help'\n"
-            + "list of commands:\n\n"
-            + " help          print help information\n"
-            + " basic         nothing that means anything\n"
-            + " test1         description 1\n"
-            + " test2         description 2\n"
-            + " test3         description 3\n";
-        runAndVerifyCommand(new String[] {"-help"}, expected);
-    }
-
-    @Test
-    public void testMainBadCommand3() {
-        String expected = "unknown command 'foobarbaz'\n"
-            + "list of commands:\n\n"
-            + " help          print help information\n"
-            + " basic         nothing that means anything\n"
-            + " test1         description 1\n"
-            + " test2         description 2\n"
-            + " test3         description 3\n";
-        runAndVerifyCommand(new String[] {"foobarbaz"}, expected);
-    }
-
-    @Test
-    public void testMainBadCommand4() {
-        String expected = "unknown command 'foo'\n"
-            + "list of commands:\n\n"
-            + " help          print help information\n"
-            + " basic         nothing that means anything\n"
-            + " test1         description 1\n"
-            + " test2         description 2\n"
-            + " test3         description 3\n";
-        runAndVerifyCommand(new String[] {"foo",  "--bar", "baz"}, expected);
-    }
-
-    @Test
-    public void testCommandInfoNotFound() throws CommandInfoNotFoundException, BundleException, IOException {
-        doThrow(new CommandInfoNotFoundException("foo")).when(registry).addBundlesFor("foo");
-        String expected = "unknown command 'foo'\n"
-                + "list of commands:\n\n"
-                + " help          print help information\n"
-                + " basic         nothing that means anything\n"
-                + " test1         description 1\n"
-                + " test2         description 2\n"
-                + " test3         description 3\n";
-            runAndVerifyCommand(new String[] {"foo"}, expected);
-    }
-
-    @Test
-    public void testMainExceptionInCommand() {
-        TestCommand errorCmd = new TestCommand("error", new TestCommand.Handle() {
-
-            @Override
-            public void run(CommandContext ctx) throws CommandException {
-                throw new CommandException("test error");
-            }
-
-        });
-        ctxFactory.getCommandRegistry().registerCommands(Arrays.asList(errorCmd));
-
-        LauncherImpl launcher = new LauncherImpl(bundleContext, ctxFactory, registry);
-        Keyring keyring = mock(Keyring.class);
-        launcher.setPreferences(new ClientPreferences(keyring));
-        launcher.setArgs(new String[] { "error" });
-        launcher.run();
-        assertEquals("test error\n", ctxFactory.getError());
-
-    }
-
-    private void runAndVerifyCommand(String[] args, String expected) {
-        LauncherImpl launcher = new LauncherImpl(bundleContext, ctxFactory, registry);
-        
-        Keyring keyring = mock(Keyring.class);
-        launcher.setPreferences(new ClientPreferences(keyring));
-        launcher.setArgs(args);
-        launcher.run();
-        assertEquals(expected, ctxFactory.getOutput());
-        assertTrue(timerFactory.isShutdown());
-    }
-
-    @Test
-    public void verifyStorageCommandSetsUpDAOFactory() {
-        LauncherImpl launcher = new LauncherImpl(bundleContext, ctxFactory, registry);
-        Keyring keyring = mock(Keyring.class);
-        Bundle sysBundle = mock(Bundle.class);
-        PowerMockito.mockStatic(FrameworkUtil.class);
-        when(FrameworkUtil.getBundle(OSGIUtils.class)).thenReturn(sysBundle);
-        when(sysBundle.getBundleContext()).thenReturn(bundleContext);
-        PowerMockito.mockStatic(DbServiceFactory.class);
-        String dbUrl = "mongo://fluff:12345";
-        DbService dbService = mock(DbService.class);
-        when(DbServiceFactory.createDbService(null, null, dbUrl)).thenReturn(dbService);
-        launcher.setPreferences(new ClientPreferences(keyring));
-        
-        launcher.setArgs(new String[] { "test3" , "--dbUrl", dbUrl });
-        launcher.run();
-        verify(dbService).connect();
-        verify(dbService).setServiceRegistration(any(ServiceRegistration.class));
-    }
-
-    @Test
-    public void verifyStorageCommandSetsUpDAOFactoryWithAuth() {
-        LauncherImpl launcher = new LauncherImpl(bundleContext, ctxFactory, registry);
-        Keyring keyring = mock(Keyring.class);
-        Bundle sysBundle = mock(Bundle.class);
-        PowerMockito.mockStatic(FrameworkUtil.class);
-        when(FrameworkUtil.getBundle(OSGIUtils.class)).thenReturn(sysBundle);
-        when(sysBundle.getBundleContext()).thenReturn(bundleContext);
-        PowerMockito.mockStatic(DbServiceFactory.class);
-        String dbUrl = "mongo://fluff:12345";
-        String testUser = "testUser";
-        String testPasswd = "testPassword";
-        DbService dbService = mock(DbService.class);
-        when(DbServiceFactory.createDbService(testUser, testPasswd, dbUrl)).thenReturn(dbService);
-        
-        launcher.setPreferences(new ClientPreferences(keyring));
-        
-        launcher.setArgs(new String[] { "test3" , "--dbUrl", dbUrl, "--username", testUser, "--password", testPasswd });
-        launcher.run();
-        verify(dbService).connect();
-        verify(dbService).setServiceRegistration(any(ServiceRegistration.class));
-    }
-
-    public void verifyPrefsAreUsed() {
-        ClientPreferences prefs = mock(ClientPreferences.class);
-        String dbUrl = "mongo://fluff:12345";
-        when(prefs.getConnectionUrl()).thenReturn(dbUrl);
-        LauncherImpl l = new LauncherImpl(bundleContext, ctxFactory, registry);
-        Bundle sysBundle = mock(Bundle.class);
-        PowerMockito.mockStatic(FrameworkUtil.class);
-        when(FrameworkUtil.getBundle(OSGIUtils.class)).thenReturn(sysBundle);
-        when(sysBundle.getBundleContext()).thenReturn(bundleContext);
-        PowerMockito.mockStatic(DbServiceFactory.class);
-        DbService dbService = mock(DbService.class);
-        // this makes sure that dbUrl is indeed retrieved from preferences
-        when(DbServiceFactory.createDbService(null, null, dbUrl)).thenReturn(dbService);
-        l.setPreferences(prefs);
-        l.setArgs(new String[] { "test3" });
-        l.run();
-        verify(dbService).connect();
-        verify(dbService).setServiceRegistration(any(ServiceRegistration.class));
-    }
-    
-    @Test
-    public void verifyVersionInfoQuery() {
-        int major = 0;
-        int minor = 3;
-        int micro = 0;
-        
-        ApplicationInfo appInfo = new ApplicationInfo();
-        Translate<LocaleResources> t = LocaleResources.createLocalizer();
-        String format = MessageFormat.format(
-                t.localize(LocaleResources.APPLICATION_VERSION_INFO),
-                appInfo.getName())
-                + " " + Version.VERSION_NUMBER_FORMAT;
-        
-        String expectedVersionInfo = String.format(format,
-                major, minor, micro) + "\n";
-        
-        String qualifier = "201207241700";
-        Bundle sysBundle = mock(Bundle.class);
-        Bundle framework = mock(Bundle.class);
-        org.osgi.framework.Version ver = org.osgi.framework.Version
-                .parseVersion(String.format(Version.VERSION_NUMBER_FORMAT,
-                        major, minor, micro) + "." + qualifier);
-        when(sysBundle.getVersion()).thenReturn(ver);
-        bundleContext = mock(BundleContext.class);
-        when(bundleContext.getBundle(0)).thenReturn(framework);
-        
-        PowerMockito.mockStatic(FrameworkUtil.class);
-        when(FrameworkUtil.getBundle(Version.class)).thenReturn(sysBundle);
-        LauncherImpl launcher = new LauncherImpl(bundleContext, ctxFactory, registry);
-        
-        launcher.setArgs(new String[] {Version.VERSION_OPTION});
-        launcher.run();
-        assertEquals(expectedVersionInfo, ctxFactory.getOutput());
-        assertTrue(timerFactory.isShutdown());
-    }
-
-    @Test
-    public void verifyListenersAdded() {
-        ActionListener<ApplicationState> listener = mock(ActionListener.class);
-        Collection<ActionListener<ApplicationState>> listeners = new ArrayList<>();
-        listeners.add(listener);
-        String[] args = new String[] {"basic"};
-        LauncherImpl launcher = new LauncherImpl(bundleContext, ctxFactory, registry);
-        Keyring keyring = mock(Keyring.class);
-        launcher.setPreferences(new ClientPreferences(keyring));
-
-        launcher.setArgs(args);
-        launcher.run(listeners);
-        verify(notifier).addActionListener(listener);
-    }
-}
--- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/CommandLineArgumentsParserTest.java	Wed Oct 24 14:59:12 2012 +0200
+++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/CommandLineArgumentsParserTest.java	Wed Oct 24 13:08:43 2012 -0400
@@ -37,6 +37,7 @@
 package com.redhat.thermostat.launcher.internal;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -148,4 +149,21 @@
     public void testMissingAdditionalArgument() throws CommandLineArgumentParseException {
         parser.parse(new String[] { "--test3" });
     }
+
+    @Test(expected = CommandLineArgumentParseException.class)
+    public void testRequiredArgumentEscaped() throws CommandLineArgumentParseException {
+        String[] rawArgs = new String[] { "--", "-t" };
+        parser.parse(rawArgs);
+    }
+
+    @Test
+    public void testEscapedArguments() throws CommandLineArgumentParseException {
+        String[] rawArgs = new String[] { "-t", "--", "--this-is-not-an-option" };
+        Arguments args = parser.parse(rawArgs);
+
+        assertTrue(args.hasArgument("t"));
+        assertFalse(args.hasArgument("--this-is-not-an-option"));
+        assertEquals("--this-is-not-an-option", args.getNonOptionArguments().get(0));
+    }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/LauncherTest.java	Wed Oct 24 13:08:43 2012 -0400
@@ -0,0 +1,494 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.launcher.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.isA;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import com.redhat.thermostat.bundles.OSGiRegistry;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.ActionNotifier;
+import com.redhat.thermostat.common.ApplicationInfo;
+import com.redhat.thermostat.common.Version;
+import com.redhat.thermostat.common.appctx.ApplicationContext;
+import com.redhat.thermostat.common.appctx.ApplicationContextUtil;
+import com.redhat.thermostat.common.cli.Arguments;
+import com.redhat.thermostat.common.cli.CommandContext;
+import com.redhat.thermostat.common.cli.CommandException;
+import com.redhat.thermostat.common.cli.CommandInfo;
+import com.redhat.thermostat.common.cli.CommandInfoNotFoundException;
+import com.redhat.thermostat.common.cli.CommandInfoSource;
+import com.redhat.thermostat.common.config.ClientPreferences;
+import com.redhat.thermostat.common.locale.LocaleResources;
+import com.redhat.thermostat.common.locale.Translate;
+import com.redhat.thermostat.common.tools.ApplicationState;
+import com.redhat.thermostat.common.tools.BasicCommand;
+import com.redhat.thermostat.launcher.DbService;
+import com.redhat.thermostat.launcher.DbServiceFactory;
+import com.redhat.thermostat.launcher.internal.HelpCommand;
+import com.redhat.thermostat.launcher.internal.LauncherImpl;
+import com.redhat.thermostat.launcher.internal.LauncherImpl.LoggingInitializer;
+import com.redhat.thermostat.test.StubBundleContext;
+import com.redhat.thermostat.test.TestCommandContextFactory;
+import com.redhat.thermostat.test.TestTimerFactory;
+import com.redhat.thermostat.test.cli.TestCommand;
+import com.redhat.thermostat.utils.keyring.Keyring;
+import com.redhat.thermostat.utils.keyring.KeyringProvider;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({FrameworkUtil.class, HelpCommand.class})
+public class LauncherTest {
+    
+    private static String defaultKeyringProvider;
+    private static final String name1 = "test1";
+    private static final String name2 = "test2";
+    private static final String name3 = "test3";
+      
+    @BeforeClass
+    public static void beforeClassSetUp() {
+        defaultKeyringProvider = System.getProperty(KeyringProvider.KEYRING_FACTORY_PROPERTY);
+    }
+    
+    @AfterClass
+    public static void afterClassTearDown() {
+        if (defaultKeyringProvider != null) {
+            System.setProperty(KeyringProvider.KEYRING_FACTORY_PROPERTY, defaultKeyringProvider);
+        }
+    }
+    
+    private static class TestCmd1 implements TestCommand.Handle {
+
+        @Override
+        public void run(CommandContext ctx) {
+            Arguments args = ctx.getArguments();
+            ctx.getConsole().getOutput().print(args.getArgument("arg1") + ", " + args.getArgument("arg2"));
+        }
+    }
+
+    private static class TestCmd2 implements TestCommand.Handle {
+        @Override
+        public void run(CommandContext ctx) {
+            Arguments args = ctx.getArguments();
+            ctx.getConsole().getOutput().print(args.getArgument("arg4") + ": " + args.getArgument("arg3"));
+        }
+    }
+
+    private TestCommandContextFactory  ctxFactory;
+    private StubBundleContext bundleContext;
+    private Bundle sysBundle;
+    private TestTimerFactory timerFactory;
+    private OSGiRegistry registry;
+    private LoggingInitializer loggingInitializer;
+    private DbServiceFactory dbServiceFactory;
+    private ActionNotifier<ApplicationState> notifier;
+
+    private LauncherImpl launcher;
+
+    @Before
+    public void setUp() {
+
+        ApplicationContextUtil.resetApplicationContext();
+        timerFactory = new TestTimerFactory();
+        ApplicationContext.getInstance().setTimerFactory(timerFactory);
+        setupCommandContextFactory();
+
+        TestCommand cmd1 = new TestCommand(name1, new TestCmd1());
+        CommandInfo info1 = mock(CommandInfo.class);
+        when(info1.getName()).thenReturn(name1);
+        Options options1 = new Options();
+        Option opt1 = new Option(null, "arg1", true, null);
+        options1.addOption(opt1);
+        Option opt2 = new Option(null, "arg2", true, null);
+        options1.addOption(opt2);
+        cmd1.addOptions(opt1, opt2);
+        cmd1.setDescription("description 1");
+        when(info1.getDescription()).thenReturn("description 1");
+        when(info1.getOptions()).thenReturn(options1);
+        TestCommand cmd2 = new TestCommand("test2", new TestCmd2());
+        CommandInfo info2 = mock(CommandInfo.class);
+        when(info2.getName()).thenReturn(name2);
+        Options options2 = new Options();
+        Option opt3 = new Option(null, "arg3", true, null);
+        options2.addOption(opt3);
+        Option opt4 = new Option(null, "arg4", true, null);
+        options2.addOption(opt4);
+        cmd2.addOptions(opt3, opt4);
+        cmd2.setDescription("description 2");
+        when(info2.getDescription()).thenReturn("description 2");
+        when(info2.getOptions()).thenReturn(options2);
+
+        TestCommand cmd3 = new TestCommand(name3);
+        CommandInfo info3 = mock(CommandInfo.class);
+        when(info3.getName()).thenReturn(name3);
+        cmd3.setStorageRequired(true);
+        cmd3.setDescription("description 3");
+        when(info3.getDescription()).thenReturn("description 3");
+        when(info3.getOptions()).thenReturn(new Options());
+
+        BasicCommand basicCmd = mock(BasicCommand.class);
+        CommandInfo basicInfo = mock(CommandInfo.class);
+        when(basicCmd.getName()).thenReturn("basic");
+        when(basicInfo.getName()).thenReturn("basic");
+        when(basicCmd.getDescription()).thenReturn("nothing that means anything");
+        when(basicInfo.getDescription()).thenReturn("nothing that means anything");
+        when(basicCmd.isStorageRequired()).thenReturn(false);
+        Options options = new Options();
+        when(basicCmd.getOptions()).thenReturn(options);
+        when(basicInfo.getOptions()).thenReturn(options);
+        notifier = mock(ActionNotifier.class);
+        when(basicCmd.getNotifier()).thenReturn(notifier);
+        CommandInfo helpCommandInfo = mock(CommandInfo.class);
+        when(helpCommandInfo.getName()).thenReturn("help");
+        when(helpCommandInfo.getDescription()).thenReturn("print help information");
+        when(helpCommandInfo.getDependencyResourceNames()).thenReturn(new ArrayList<String>());
+        when(helpCommandInfo.getOptions()).thenReturn(new Options());
+        when(helpCommandInfo.getUsage()).thenReturn("thermostat help");
+
+        ctxFactory.getCommandRegistry().registerCommands(Arrays.asList(new HelpCommand(), cmd1, cmd2, cmd3, basicCmd));
+
+        registry = mock(OSGiRegistry.class);
+
+        CommandInfoSource infos = mock(CommandInfoSource.class);
+        when(infos.getCommandInfo(name1)).thenReturn(info1);
+        when(infos.getCommandInfo(name2)).thenReturn(info2);
+        when(infos.getCommandInfo(name3)).thenReturn(info3);
+        when(infos.getCommandInfo("basic")).thenReturn(basicInfo);
+        when(infos.getCommandInfo("help")).thenReturn(helpCommandInfo);
+        Collection<CommandInfo> infoList = new ArrayList<CommandInfo>();
+        infoList.add(helpCommandInfo);
+        infoList.add(basicInfo);
+        infoList.add(info1);
+        infoList.add(info2);
+        infoList.add(info3);
+        when(infos.getCommandInfos()).thenReturn(infoList);
+
+        PowerMockito.mockStatic(FrameworkUtil.class);
+        Bundle bundle = mock(Bundle.class);
+        BundleContext bCtx = mock(BundleContext.class);
+        when(bundle.getBundleContext()).thenReturn(bCtx);
+        ServiceReference infosRef = mock(ServiceReference.class);
+        when(bCtx.getServiceReference(CommandInfoSource.class)).thenReturn(infosRef);
+        when(bCtx.getService(infosRef)).thenReturn(infos);
+        when(FrameworkUtil.getBundle(isA(HelpCommand.class.getClass()))).thenReturn(bundle);
+
+        loggingInitializer = mock(LoggingInitializer.class);
+        dbServiceFactory = mock(DbServiceFactory.class);
+
+        launcher = new LauncherImpl(bundleContext, ctxFactory, registry, loggingInitializer, dbServiceFactory);
+
+        Keyring keyring = mock(Keyring.class);
+        launcher.setPreferences(new ClientPreferences(keyring));
+    }
+
+    private void setupCommandContextFactory() {
+        sysBundle = mock(Bundle.class);
+        bundleContext = new StubBundleContext();
+        bundleContext.setBundle(0, sysBundle);
+        ctxFactory = new TestCommandContextFactory(bundleContext);
+    }
+
+
+    @After
+    public void tearDown() {
+        ctxFactory = null;
+        ApplicationContextUtil.resetApplicationContext();
+    }
+
+    @Test
+    public void testMain() {
+        runAndVerifyCommand(new String[] {name1, "--arg1", "Hello", "--arg2", "World"}, "Hello, World");
+
+        ctxFactory.reset();
+
+        runAndVerifyCommand(new String[] {"test2", "--arg3", "Hello", "--arg4", "World"}, "World: Hello");
+    }
+
+    @Test
+    public void testMainNoArgs() {
+        String expected = "list of commands:\n\n"
+                        + " help          print help information\n"
+                        + " basic         nothing that means anything\n"
+                        + " test1         description 1\n"
+                        + " test2         description 2\n"
+                        + " test3         description 3\n";
+        runAndVerifyCommand(new String[0], expected);
+    }
+
+    @Test
+    public void verifySetLogLevel() {
+        runAndVerifyCommand(new String[] {name1, "--logLevel", "WARNING", "--arg1", "Hello", "--arg2", "World"}, "Hello, World");
+        Logger globalLogger = Logger.getLogger("com.redhat.thermostat");
+        assertEquals(Level.WARNING, globalLogger.getLevel());
+    }
+
+    @Test
+    public void testMainBadCommand1() {
+        String expected = "unknown command '--help'\n"
+            + "list of commands:\n\n"
+            + " help          print help information\n"
+            + " basic         nothing that means anything\n"
+            + " test1         description 1\n"
+            + " test2         description 2\n"
+            + " test3         description 3\n";
+        runAndVerifyCommand(new String[] {"--help"}, expected);
+    }
+
+    @Test
+    public void testMainBadCommand2() {
+        String expected = "unknown command '-help'\n"
+            + "list of commands:\n\n"
+            + " help          print help information\n"
+            + " basic         nothing that means anything\n"
+            + " test1         description 1\n"
+            + " test2         description 2\n"
+            + " test3         description 3\n";
+        runAndVerifyCommand(new String[] {"-help"}, expected);
+    }
+
+    @Test
+    public void testMainBadCommand3() {
+        String expected = "unknown command 'foobarbaz'\n"
+            + "list of commands:\n\n"
+            + " help          print help information\n"
+            + " basic         nothing that means anything\n"
+            + " test1         description 1\n"
+            + " test2         description 2\n"
+            + " test3         description 3\n";
+        runAndVerifyCommand(new String[] {"foobarbaz"}, expected);
+    }
+
+    @Test
+    public void testMainBadCommand4() {
+        String expected = "unknown command 'foo'\n"
+            + "list of commands:\n\n"
+            + " help          print help information\n"
+            + " basic         nothing that means anything\n"
+            + " test1         description 1\n"
+            + " test2         description 2\n"
+            + " test3         description 3\n";
+        runAndVerifyCommand(new String[] {"foo",  "--bar", "baz"}, expected);
+    }
+
+    @Test
+    public void testCommandInfoNotFound() throws CommandInfoNotFoundException, BundleException, IOException {
+        doThrow(new CommandInfoNotFoundException("foo")).when(registry).addBundlesFor("foo");
+        String expected = "unknown command 'foo'\n"
+                + "list of commands:\n\n"
+                + " help          print help information\n"
+                + " basic         nothing that means anything\n"
+                + " test1         description 1\n"
+                + " test2         description 2\n"
+                + " test3         description 3\n";
+            runAndVerifyCommand(new String[] {"foo"}, expected);
+    }
+
+    @Test
+    public void testMainExceptionInCommand() {
+        TestCommand errorCmd = new TestCommand("error", new TestCommand.Handle() {
+
+            @Override
+            public void run(CommandContext ctx) throws CommandException {
+                throw new CommandException("test error");
+            }
+
+        });
+        ctxFactory.getCommandRegistry().registerCommands(Arrays.asList(errorCmd));
+
+        launcher.setArgs(new String[] { "error" });
+        launcher.run();
+        assertEquals("test error\n", ctxFactory.getError());
+
+    }
+
+    private void runAndVerifyCommand(String[] args, String expected) {
+        launcher.setArgs(args);
+        launcher.run();
+        assertEquals(expected, ctxFactory.getOutput());
+        assertTrue(timerFactory.isShutdown());
+    }
+
+    @Test
+    public void verifyStorageCommandSetsUpDAOFactory() {
+        String dbUrl = "mongo://fluff:12345";
+        DbService dbService = mock(DbService.class);
+        when(dbServiceFactory.createDbService(null, null, dbUrl)).thenReturn(dbService);
+        
+        launcher.setArgs(new String[] { "test3" , "--dbUrl", dbUrl });
+        launcher.run();
+
+        verify(dbService).connect();
+        verify(dbService).setServiceRegistration(any(ServiceRegistration.class));
+    }
+
+    @Test
+    public void verifyStorageCommandSetsUpDAOFactoryWithAuth() {
+        String dbUrl = "mongo://fluff:12345";
+        String testUser = "testUser";
+        String testPasswd = "testPassword";
+        DbService dbService = mock(DbService.class);
+        when(dbServiceFactory.createDbService(testUser, testPasswd, dbUrl)).thenReturn(dbService);
+        
+        launcher.setArgs(new String[] { "test3" , "--dbUrl", dbUrl, "--username", testUser, "--password", testPasswd });
+        launcher.run();
+        verify(dbService).connect();
+        verify(dbService).setServiceRegistration(any(ServiceRegistration.class));
+    }
+
+    @Test
+    public void verifyPrefsAreUsed() {
+        ClientPreferences prefs = mock(ClientPreferences.class);
+        String dbUrl = "mongo://fluff:12345";
+        when(prefs.getConnectionUrl()).thenReturn(dbUrl);
+
+        DbService dbService = mock(DbService.class);
+        // this makes sure that dbUrl is indeed retrieved from preferences
+        when(dbServiceFactory.createDbService(null, null, dbUrl)).thenReturn(dbService);
+        launcher.setPreferences(prefs);
+        launcher.setArgs(new String[] { "test3" });
+        launcher.run();
+        verify(dbService).connect();
+        verify(dbService).setServiceRegistration(any(ServiceRegistration.class));
+    }
+
+    @Ignore("needs a storage-requiring service")
+    @Test
+    public void verifyDbServiceIsRegistered() {
+        DbService dbService = mock(DbService.class);
+        when(dbServiceFactory.createDbService(anyString(), anyString(), anyString())).thenReturn(dbService);
+
+        launcher.setArgs(new String[] { "ignore" });
+        launcher.run();
+
+        assertTrue(bundleContext.isServiceRegistered(DbService.class.getName(), dbService.getClass()));
+    }
+
+    @Test
+    public void verifyVersionInfoQuery() {
+        int major = 0;
+        int minor = 3;
+        int micro = 0;
+        
+        ApplicationInfo appInfo = new ApplicationInfo();
+        Translate<LocaleResources> t = LocaleResources.createLocalizer();
+        String format = MessageFormat.format(
+                t.localize(LocaleResources.APPLICATION_VERSION_INFO),
+                appInfo.getName())
+                + " " + Version.VERSION_NUMBER_FORMAT;
+        
+        String expectedVersionInfo = String.format(format,
+                major, minor, micro) + "\n";
+        
+        String qualifier = "201207241700";
+
+        org.osgi.framework.Version ver = org.osgi.framework.Version
+                .parseVersion(String.format(Version.VERSION_NUMBER_FORMAT,
+                        major, minor, micro) + "." + qualifier);
+        when(sysBundle.getVersion()).thenReturn(ver);
+        
+        PowerMockito.mockStatic(FrameworkUtil.class);
+        when(FrameworkUtil.getBundle(Version.class)).thenReturn(sysBundle);
+        
+        launcher.setArgs(new String[] {Version.VERSION_OPTION});
+        launcher.run();
+
+        assertEquals(expectedVersionInfo, ctxFactory.getOutput());
+        assertTrue(timerFactory.isShutdown());
+    }
+
+    @Test
+    public void verifyListenersAdded() {
+        ActionListener<ApplicationState> listener = mock(ActionListener.class);
+        Collection<ActionListener<ApplicationState>> listeners = new ArrayList<>();
+        listeners.add(listener);
+        String[] args = new String[] {"basic"};
+
+        launcher.setArgs(args);
+        launcher.run(listeners);
+        verify(notifier).addActionListener(listener);
+    }
+
+    @Test
+    public void verifyLoggingIsInitialized() {
+        launcher.setArgs(new String[] { "ignore" });
+        launcher.run();
+
+        verify(loggingInitializer).initialize();
+    }
+
+    @Test
+    public void verifyShutdown() throws BundleException {
+        launcher.setArgs(new String[] { "ignore" });
+        launcher.run();
+
+        verify(sysBundle).stop();
+    }
+
+}
--- a/tools/src/main/java/com/redhat/thermostat/tools/cli/ConnectCommand.java	Wed Oct 24 14:59:12 2012 +0200
+++ b/tools/src/main/java/com/redhat/thermostat/tools/cli/ConnectCommand.java	Wed Oct 24 13:08:43 2012 -0400
@@ -66,7 +66,16 @@
     private static final String NAME = "connect";
     
     private ClientPreferences prefs;
-    
+    private DbServiceFactory dbServiceFactory;
+
+    public ConnectCommand() {
+        this(new DbServiceFactory());
+    }
+
+    ConnectCommand(DbServiceFactory dbServiceFactory) {
+        this.dbServiceFactory = dbServiceFactory;
+    }
+
     @SuppressWarnings("rawtypes")
     @Override
     public void run(CommandContext ctx) throws CommandException {
@@ -84,7 +93,7 @@
         }
         String username = ctx.getArguments().getArgument(CommonCommandOptions.USERNAME_ARG);
         String password = ctx.getArguments().getArgument(CommonCommandOptions.PASSWORD_ARG);
-        service = DbServiceFactory.createDbService(username, password, dbUrl);
+        service = dbServiceFactory.createDbService(username, password, dbUrl);
         try {
             service.connect();
         } catch (ConnectionException ex) {
--- a/tools/src/test/java/com/redhat/thermostat/tools/cli/ConnectCommandTest.java	Wed Oct 24 14:59:12 2012 +0200
+++ b/tools/src/test/java/com/redhat/thermostat/tools/cli/ConnectCommandTest.java	Wed Oct 24 13:08:43 2012 -0400
@@ -81,13 +81,16 @@
     private ConnectCommand cmd;
     private TestCommandContextFactory cmdCtxFactory;
     private BundleContext bundleContext;
+    private DbServiceFactory dbServiceFactory;
 
     @Before
     public void setUp() {
         ApplicationContextUtil.resetApplicationContext();
         setupCommandContextFactory();
 
-        cmd = new ConnectCommand();
+        dbServiceFactory = mock(DbServiceFactory.class);
+
+        cmd = new ConnectCommand(dbServiceFactory);
 
     }
 
@@ -130,11 +133,11 @@
         when(utils.getServiceAllowNull(DbService.class)).thenReturn(null);
 
         DbService dbService = mock(DbService.class);
-        PowerMockito.mockStatic(DbServiceFactory.class);
+
         String username = "testuser";
         String password = "testpassword";
         String dbUrl = "mongodb://10.23.122.1:12578";
-        when(DbServiceFactory.createDbService(eq(username), eq(password), eq(dbUrl))).thenReturn(dbService);
+        when(dbServiceFactory.createDbService(eq(username), eq(password), eq(dbUrl))).thenReturn(dbService);
         SimpleArguments args = new SimpleArguments();
         args.addArgument("dbUrl", dbUrl);
         args.addArgument("username", username);