/**
 * Copyright (c) 2012-2013 Linagora
 * 
 * This program/library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or (at your
 * option) any later version.
 * 
 * This program/library 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 Lesser General Public License
 * for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program/library; If not, see <http://www.gnu.org/licenses/>
 * for the GNU Lesser General Public License version 2.1.
 */
package org.ow2.petals.cli;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.PrintStream;
import java.io.Writer;
import java.net.URL;
import java.util.Enumeration;
import java.util.logging.Logger;
import java.util.zip.ZipException;

import org.apache.commons.io.IOUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.ow2.petals.admin.ContainerAdministrationMock;
import org.ow2.petals.admin.PetalsAdministrationFactoryMock;
import org.ow2.petals.admin.api.ContainerAdministration;
import org.ow2.petals.admin.api.PetalsAdministrationFactory;
import org.ow2.petals.admin.api.exception.ContainerAdministrationException;
import org.ow2.petals.admin.api.exception.DuplicatedServiceException;
import org.ow2.petals.admin.api.exception.MissingServiceException;
import org.ow2.petals.cli.api.command.Command;
import org.ow2.petals.cli.api.command.exception.CommandBadArgumentNumberException;
import org.ow2.petals.cli.api.command.exception.CommandException;
import org.ow2.petals.cli.api.command.exception.CommandMissingArgumentException;
import org.ow2.petals.cli.api.exception.DuplicatedCommandException;
import org.ow2.petals.cli.shell.PetalsInteractiveCliTest;
import org.ow2.petals.cli.shell.ShellFactory;
import org.ow2.petals.cli.shell.command.AbstractCommand;
import org.ow2.petals.cli.shell.command.Connect;
import org.ow2.petals.cli.shell.command.Deploy;
import org.ow2.petals.cli.shell.command.Help;
import org.ow2.petals.cli.shell.command.ListArtifacts;
import org.ow2.petals.cli.shell.command.Version;

/**
 * Unit test of {@link Main}.
 * @author Christophe DENEUX - Linagora
 */
public class MainTest {

    /**
     * Assertion exception used by threads
     */
    private volatile AssertionError assertion;

    @Rule
    public final TemporaryFolder tempFolder = new TemporaryFolder();

    @Before
    public void beforeTest() {
        System.setProperty(PetalsAdministrationFactory.PROPERTY_NAME,
                PetalsAdministrationFactoryMock.class.getName());
    }

    @After
    public void afterTest() {
        System.setProperty(PetalsAdministrationFactory.PROPERTY_NAME, "");
    }

    /**
     * <p>
     * Check the main entry point against an command line containing:
     * <ul>
     * <li>the command mode without connection arguments,</li>
     * <li>no operand flag,</li>
     * <li>no command,</li>
     * </ul>
     * as following:<br/>
     * <code>> ./petals-cli.sh -c</code>
     * </p>
     * <p>
     * Expected results:
     * <ul>
     * <li>error because of the missing command,</li>
     * <li>no stack trace is printed,</li>
     * <li>usage of Petals CLI is printed.</li>
     * </ul>
     * </p>
     */
    @Test
    public final void testRun_Command_Error_00() {

        final PrintStream standardConsoleOutput = System.out;
        final PrintStream standardConsoleError = System.err;

        try {
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));
            final ByteArrayOutputStream outBAIS = new ByteArrayOutputStream();
            System.setOut(new PrintStream(outBAIS));

            // The main class MUST be instanced after to have set the console streams !!
            final Main main = new Main();

            final int exitCode = main.run(new String[] { "-" + ShellFactory.COMMAND_SHORT_OPTION });

            final String error = errBAIS.toString();
            final String output = outBAIS.toString();
            assertEquals("invalid exit code", 1, exitCode);
            assertFalse("No error message print", error.isEmpty());
            assertTrue("It's not the expected error message.",
                    error.contains(Constants.CommandMessages.MISSING_COMMAND));
            assertFalse("Stack trace is present in error message", error.contains(Main.class.getName()));
            assertFalse("Stack trace is present in error message", error.contains("Caused by"));
            assertTrue("Usage header is missing", output.contains(Main.USAGE_HEADER));
            assertTrue("Usage footer is missing", output.contains(Main.USAGE_FOOTER));
        }
        finally {
            System.setOut(standardConsoleOutput);
            System.setErr(standardConsoleError);
        }
    }

    /**
     * <p>
     * Check the main entry point against an command line containing:
     * <ul>
     * <li>the command mode without connection arguments,</li>
     * <li>the operand flag,</li>
     * <li>no command,</li>
     * </ul>
     * as following:<br/>
     * <code>> ./petals-cli.sh -c --</code>
     * </p>
     * <p>
     * Expected results:
     * <ul>
     * <li>error because of the missing command,</li>
     * <li>no stack trace is printed,</li>
     * <li>usage of Petals CLI is printed.</li>
     * </ul>
     * </p>
     */
    @Test
    public final void testRun_Command_Error_01() {

        final PrintStream standardConsoleOutput = System.out;
        final PrintStream standardConsoleError = System.err;

        try {
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));
            final ByteArrayOutputStream outBAIS = new ByteArrayOutputStream();
            System.setOut(new PrintStream(outBAIS));

            // The main class MUST be instanced after to have set the console streams !!
            final Main main = new Main();

            final int exitCode = main
                    .run(new String[] { "-" + ShellFactory.COMMAND_SHORT_OPTION, "--" });

            final String error = errBAIS.toString();
            final String output = outBAIS.toString();
            assertEquals("invalid exit code", 1, exitCode);
            assertFalse("No error message print", error.isEmpty());
            assertTrue("It's not the expected error message.",
                    error.contains(Constants.CommandMessages.MISSING_COMMAND));
            assertFalse("Stack trace is present in error message", error.contains(Main.class.getName()));
            assertFalse("Stack trace is present in error message", error.contains("Caused by"));
            assertTrue("Usage header is missing", output.contains(Main.USAGE_HEADER));
            assertTrue("Usage footer is missing", output.contains(Main.USAGE_FOOTER));
        }
        finally {
            System.setOut(standardConsoleOutput);
            System.setErr(standardConsoleError);
        }
    }

    /**
     * <p>
     * Check the main entry point against an command line containing:
     * <ul>
     * <li>the command mode without connection arguments,</li>
     * <li>a command needing no argument,</li>
     * </ul>
     * as following:<br/>
     * <code>> ./petals-cli.sh -c -- help</code>
     * </p>
     * <p>
     * Expected results:
     * <ul>
     * <li>no error because connection arguments are not required,</li>
     * <li>no stack trace is printed,</li>
     * <li>usage of Petals CLI is printed.</li>
     * </ul>
     * </p>
     */
    @Test
    public final void testRun_Command_Error_02() {

        final PrintStream standardConsoleOutput = System.out;
        final PrintStream standardConsoleError = System.err;

        try {
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));
            final ByteArrayOutputStream outBAIS = new ByteArrayOutputStream();
            System.setOut(new PrintStream(outBAIS));

            // The main class MUST be instanced after to have set the console streams !!
            final Main main = new Main();
            final AbstractCommand command = new Help();

            final int exitCode = main.run(new String[] { "-" + ShellFactory.COMMAND_SHORT_OPTION, "--",
                    command.getName() });

            final String error = errBAIS.toString();
            final String output = outBAIS.toString();
            assertEquals("invalid exit code", 0, exitCode);
            assertTrue("Error message print", error.isEmpty());
            assertFalse("Usage header is print", output.contains(Main.USAGE_HEADER));
            assertFalse("Usage footer is print", output.contains(Main.USAGE_FOOTER));
        }
        finally {
            System.setOut(standardConsoleOutput);
            System.setErr(standardConsoleError);
        }
    }

    /**
     * <p>
     * Check the main entry point against an command line containing:
     * <ul>
     * <li>the command mode without connection arguments,</li>
     * <li>a command containing an unrecognized option,</li>
     * </ul>
     * as following:<br/>
     * <code>> ./petals-cli.sh -c -- list -q unrecognizedOption</code>
     * </p>
     * <p>
     * Expected results:
     * <ul>
     * <li>error because of the unrecognized argument of the command,</li>
     * <li>no stack trace is printed,</li>
     * <li>usage of Petals CLI is printed.</li>
     * </ul>
     * </p>
     */
    @Test
    public final void testRun_Command_Error_03() {

        final PrintStream standardConsoleOutput = System.out;
        final PrintStream standardConsoleError = System.err;

        try {
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));
            final ByteArrayOutputStream outBAIS = new ByteArrayOutputStream();
            System.setOut(new PrintStream(outBAIS));

            // The main class MUST be instanced after to have set the console streams !!
            final Main main = new Main();

            final AbstractCommand command = new ListArtifacts();
            final String unrecognizedOption = "q";

            final int exitCode = main.run(new String[] {
                    "-" + ShellFactory.COMMAND_SHORT_OPTION, "--",
                    command.getName(),
                    "-" + unrecognizedOption, "unrecognizedOption" });

            final String error = errBAIS.toString();
            assertEquals("invalid exit code", 1, exitCode);
            assertFalse("No error message print", error.isEmpty());
            assertFalse("Stack trace is present in error message", error.contains(Main.class.getName()));
            assertFalse("Stack trace is present in error message", error.contains("Caused by"));
            assertTrue("Command usage is missing", error.contains(command.getUsage()));
        }
        finally {
            System.setOut(standardConsoleOutput);
            System.setErr(standardConsoleError);
        }
    }

    /**
     * <p>
     * Check the main entry point against an command line containing:
     * <ul>
     * <li>the command mode with connection arguments,</li>
     * <li>a command containing an unrecognized option,</li>
     * </ul>
     * as following:<br/>
     * <code>> ./petals-cli.sh -h localhost -n 7700 -u petals -p petals -c -- list -q unrecognizedOption</code>
     * </p>
     * <p>
     * Note: the connection <b>is not a real</b> connection.
     * </p>
     * <p>
     * Expected results:
     * <ul>
     * <li>error about the command because of unrecognized argument,</li>
     * <li>no stack trace is printed,</li>
     * <li>usage of the command is printed.</li>
     * </ul>
     * </p>
     */
    @Test
    public final void testRun_Command_Error_04() {

        final PrintStream standardConsoleError = System.err;

        try {
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));

            // The main class MUST be instanced after to have set the console streams !!
            final Main main = new Main();

            final AbstractCommand command = new ListArtifacts();
            final String unrecognizedOption = "q";

            final int exitCode = main.run(new String[] {
                    "-h", "localhost",
                    "-n", "7700",
                    "-u", "petals",
                    "-p", "petals",
                    "-" + ShellFactory.COMMAND_SHORT_OPTION, "--",
                    command.getName(),
                    "-" + unrecognizedOption, "unrecognizedOption" });

            final String error = errBAIS.toString();
            assertEquals("invalid exit code", 1, exitCode);
            assertFalse("No error message print", error.isEmpty());
            assertTrue("The command is missing in error message",
                    error.startsWith("ERROR on command '" + command.getName() + "':"));
            assertTrue(
                    "Bad arguments error not detected",
                    error.contains(CommandBadArgumentNumberException.BAD_NUMBER_OF_ARGUMENTS_OR_INCOMPATIBLE_ARGUMENTS));
            assertFalse("Stack trace is present in error message", error.contains(Main.class.getName()));
            assertFalse("Stack trace is present in error message", error.contains("Caused by"));
            assertTrue("The command usage is missing in error message",
                    error.contains(command.getUsage()));
        }
        finally {
            System.setErr(standardConsoleError);
        }

    }

    /**
     * <p>
     * Check the main entry point against an command line containing:
     * <ul>
     * <li>the command mode with connection arguments,</li>
     * <li>a command containing a missing argument for an option,</li>
     * </ul>
     * as following:<br/>
     * <code>> ./petals-cli.sh -h localhost -n 7700 -u petals -p petals -c -- list -t</code>
     * </p>
     * <p>
     * Expected results:
     * <ul>
     * <li>error because of the missing argument of the option,</li>
     * <li>no stack trace is printed,</li>
     * <li>usage of the command is printed.</li>
     * </ul>
     * </p>
     */
    @Test
    public final void testRun_Command_Error_05() {

        final PrintStream standardConsoleOutput = System.out;
        final PrintStream standardConsoleError = System.err;

        try {
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));
            final ByteArrayOutputStream outBAIS = new ByteArrayOutputStream();
            System.setOut(new PrintStream(outBAIS));

            // The main class MUST be instanced after to have set the console streams !!
            final Main main = new Main();

            final AbstractCommand command = new ListArtifacts();
            final String optionWithMissingArg = "t";

            final int exitCode = main.run(new String[] {
                    "-h", "localhost",
                    "-n", "7700",
                    "-u", "petals",
                    "-p", "petals",
                    "-" + ShellFactory.COMMAND_SHORT_OPTION, "--",
                    command.getName(), "-" + optionWithMissingArg });

            final String error = errBAIS.toString();
            final String output = outBAIS.toString();
            assertEquals("invalid exit code", 1, exitCode);
            assertFalse("No error message print", error.isEmpty());
            assertTrue("The command is missing in error message",
                    error.startsWith("ERROR on command '" + command.getName() + "':"));
            assertFalse("The exception thrown is present in error message",
                    error.contains(CommandMissingArgumentException.class.getName()));
            assertTrue(
                    "The error is not the expected one",
                    error.contains(CommandMissingArgumentException.MISSING_ARGUMENT + ": "
                            + optionWithMissingArg));
            assertTrue("Command usage is missing", error.contains(command.getUsage()));
            assertFalse("Stack trace is present in error message", error.contains(Main.class.getName()));
            assertFalse("Stack trace is present in error message", error.contains("Caused by"));
            assertFalse("Petals CLI usage header is present", output.contains(Main.USAGE_HEADER));
            assertFalse("Petals CLI usage footer is present", output.contains(Main.USAGE_FOOTER));
        }
        finally {
            System.setOut(standardConsoleOutput);
            System.setErr(standardConsoleError);
        }
    }

    /**
     * <p>
     * Check the main entry point against an command line containing:
     * <ul>
     * <li>the flag debug is enabled,</li>
     * <li>the command mode with connection arguments,</li>
     * <li>a command containing a missing argument for an option,</li>
     * </ul>
     * as following:<br/>
     * <code>> ./petals-cli.sh -h localhost -n 7700 -u petals -p petals -d -c -- list -t</code>
     * </p>
     * <p>
     * Expected results:
     * <ul>
     * <li>error because of the missing argument of the option,</li>
     * <li>no stack trace is printed,</li>
     * <li>usage of the command is printed.</li>
     * </ul>
     * </p>
     */
    @Test
    public final void testRun_Command_Error_06() {

        final PrintStream standardConsoleOutput = System.out;
        final PrintStream standardConsoleError = System.err;

        try {
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));
            final ByteArrayOutputStream outBAIS = new ByteArrayOutputStream();
            System.setOut(new PrintStream(outBAIS));

            // The main class MUST be instanced after to have set the console streams !!
            final Main main = new Main();

            final AbstractCommand command = new ListArtifacts();
            final String optionWithMissingArg = "t";

            final int exitCode = main.run(new String[] {
                    "-h", "localhost",
                    "-n", "7700",
                    "-u", "petals",
                    "-p", "petals",
                    "-d",
                    "-" + ShellFactory.COMMAND_SHORT_OPTION, "--",
                    command.getName(), "-" + optionWithMissingArg });

            final String error = errBAIS.toString();
            final String output = outBAIS.toString();
            assertEquals("invalid exit code", 1, exitCode);
            assertFalse("No error message print", error.isEmpty());
            assertTrue("The command is missing in error message",
                    error.startsWith("ERROR on command '" + command.getName() + "':"));
            assertFalse("The exception thrown is present in error message",
                    error.contains(CommandMissingArgumentException.class.getName()));
            assertTrue(
                    "The error is not the expected one",
                    error.contains(CommandMissingArgumentException.MISSING_ARGUMENT + ": "
                            + optionWithMissingArg));
            assertTrue("Command usage is missing", error.contains(command.getUsage()));
            assertFalse("Stack trace is present in error message", error.contains(Main.class.getName()));
            assertFalse("Stack trace is present in error message", error.contains("Caused by"));
            assertFalse("Petals CLI usage header is present", output.contains(Main.USAGE_HEADER));
            assertFalse("Petals CLI usage footer is present", output.contains(Main.USAGE_FOOTER));
        }
        finally {
            System.setOut(standardConsoleOutput);
            System.setErr(standardConsoleError);
        }
    }

    /**
     * <p>
     * Use case of <a
     * href="http://jira.petalslink.com/browse/PETALSESBCLI-41">PETALSCLI-41</a>
     * </p>
     * <p>
     * Note: the connection <b>is not a real</b> connection.
     * </p>
     *
     * @see <a
     *      href="http://jira.petalslink.com/browse/PETALSESBCLI-41">PETALSCLI-41</a>
     */
    @Test
    public final void testRun_Command_PETALSCLI_41() throws IOException, CommandException {

        final PrintStream standardConsoleError = System.err;

        try {
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));

            // The main class MUST be instanced after to have set the console streams !!
            final Main main = new Main();

            final AbstractCommand command = new Deploy();
            final File tmpFile = File.createTempFile("component-petals-cli", "zip");

            final int exitCode = main.run(new String[] {
                    "-h", "localhost",
                    "-n", "7700",
                    "-u", "petals",
                    "-p", "petals",
                    "-" + ShellFactory.COMMAND_SHORT_OPTION, "--",
                    command.getName(),
                    "-u", tmpFile.toURI().toURL().toExternalForm(),
                    "-D", "myParam=myValue"});

            final String error = errBAIS.toString();
            assertEquals("invalid exit code", 2, exitCode);
            assertFalse("No error message print", error.isEmpty());
            assertTrue("The command is missing in error message",
                    error.startsWith("ERROR on command '" + command.getName() + "':"));
            assertTrue("The error is not relative to the zip file",
                    error.contains(ZipException.class.getName()));
        }
        finally {
            System.setErr(standardConsoleError);
        }

    }

    /**
     * <p>
     * Check the main entry point against an command line containing:
     * <ul>
     * <li>the flag debug is enabled,</li>
     * <li>the command mode with connection arguments to an unknown host,</li>
     * <li>a command needing no argument,</li>
     * </ul>
     * as following:<br/>
     * <code>> ./petals-cli.sh -h unknown -n 7700 -u petals -p petals -d -c -- list</code>
     * </p>
     * <p>
     * Note: the connection <b>is not a real</b> connection, but raises an
     * exception on the host 'unknown'.
     * </p>
     * <p>
     * Expected results:
     * <ul>
     * <li>error about the connection to an unknown host,</li>
     * <li>error statcktrace is printed,</li>
     * <li>no usage is printed.</li>
     * </ul>
     * </p>
     */
    @Test
    public final void testRun_Command_Error_07() {

        final PrintStream standardConsoleError = System.err;

        try {
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));

            // The main class MUST be instanced after to have set the console streams !!
            final Main main = new Main();

            final AbstractCommand command = new ListArtifacts();

            final int exitCode = main.run(new String[] {
                    "-h", "unknown",
                    "-n", "7700",
                    "-u", "petals",
                    "-p", "petals",
                    "-d",
                    "-" + ShellFactory.COMMAND_SHORT_OPTION, "--",
                    command.getName() });

            final String error = errBAIS.toString();
            assertEquals("invalid exit code", 1, exitCode);
            assertFalse("No error message print", error.isEmpty());
            assertTrue("The exception throwing is missing in error message",
                    error.contains(ContainerAdministrationException.class.getName()));
            assertTrue("Stack trace is missing in error message",
                    error.contains(Main.class.getName()));
        }
        finally {
            System.setErr(standardConsoleError);
        }
    }

    /**
     * <p>
     * Check the main entry point against an command line containing:
     * <ul>
     * <li>the flag debug is disable,</li>
     * <li>connection defines on the command line,</li>
     * <li>the interactive mode,</li>
     * <li>the connection is done with 'connect' without args, and connection is
     * confirmed,</li>
     * <li>the command 'list' without arguments is executed after a 'connect'.</li>
     * </ul>
     * as following:<br/>
     * <code>
     * > ./petals-cli.sh -h localhost -n 7700 -u petals -p petals -C<br/>
     * petals-cli> connect
     * petals-cli@localhost:7700> list
     * </code>
     * </p>
     * <p>
     * Note: the connection <b>is not a real</b> connection.
     * </p>
     * <p>
     * Expected results:
     * <ul>
     * <li>Petals CLI is connected before entering the command,</li>
     * <li>the prompt displays connection information,</li>
     * <li>no error,</li>
     * <li>no error stacktrace is printed,</li>
     * <li>no usage is printed,</li>
     * <li>commands correctly executed.</li>
     * </ul>
     * </p>
     */
    @Test
    public final void testRun_Interactive_00() throws IOException, InterruptedException {

        final PrintStream standardConsoleOutput = System.out;
        final PrintStream standardConsoleError = System.err;
        final InputStream standardConsoleInput = System.in;

        try {
        	// Enable the hack for Windows?
        	PetalsInteractiveCliTest.hackForWindows( true );

        	// Go on...
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));
            final ByteArrayOutputStream outBAIS = new ByteArrayOutputStream();
            System.setOut(new PrintStream(outBAIS));
            final PipedOutputStream pos = new PipedOutputStream();
            System.setIn(new PipedInputStream(pos));

            // The main class MUST be instanced after to have set the console streams !!
            final Main main = new Main();

            this.assertion = null;
            final Runnable writer = new Runnable() {

                @Override
                public void run() {
                    try {
                        try {
                            // Wait that Petals CLI starts
                            Thread.sleep(1000);

                            final String output1 = outBAIS.toString();
                            assertFalse("Prompt is empty", output1.isEmpty());
                            assertTrue("Invalid prompt", output1.contains("petals-cli>"));
                            assertFalse("Invalid prompt", output1.contains("localhost:7700"));

                            // Connect
                            pos.write("connect\n".getBytes());
                            // Wait the execution of the command
                            Thread.sleep(1000);
                            final String output2 = outBAIS.toString().substring(output1.length());
                            assertFalse("Prompt is empty", output2.isEmpty());
                            assertTrue(
                                    "Confirmation message is missing",
                                    output2.contains("Would you like to connect to petals:*****@127.0.0.1:7700? (y/n)"));

                            // Confirmation
                            pos.write("y\n".getBytes());
                            // Wait the execution of the command
                            Thread.sleep(1000);
                            final String output3 = outBAIS.toString().substring(
                                    output1.length() + output2.length());
                            assertFalse("Prompt is empty", output3.isEmpty());
                            assertTrue("Petals CLI is not connected",
                                    output3.contains("petals-cli@127.0.0.1:7700>"));

                            // Enter the command
                            pos.write("list\n".getBytes());
                            // Wait the execution of the command
                            Thread.sleep(1000);
                            final String error = errBAIS.toString();
                            assertTrue("Error message print", error.isEmpty());

                        } catch (final Exception e) {
                            fail("An exception occurs: " + e.getMessage());
                        }
                    } catch (final AssertionError e) {
                        MainTest.this.assertion = e;
                    }
                }
            };

            final Runnable petalsCli = new Runnable() {

                @Override
                public void run() {

                    try {
                        final int exitCode = main.run(new String[] { "-h", "localhost", "-n", "7700",
                                "-u", "petals", "-p", "petals", "-C" });

                        assertEquals("invalid exit code", 0, exitCode);
                    } catch (final AssertionError e) {
                        MainTest.this.assertion = e;
                    }

                }
            };

            final Thread petalsCliThread = new Thread(petalsCli, "petalsCliThread");
            final Thread writerThread = new Thread(writer, "writerThread");
            petalsCliThread.start();
            writerThread.start();
            writerThread.join();
            pos.write("exit\n".getBytes());
            pos.close();
            petalsCliThread.join();

            if (this.assertion != null) {
                throw this.assertion;
            }
        }
        finally {
            System.setOut(standardConsoleOutput);
            System.setErr(standardConsoleError);
            System.setIn(standardConsoleInput);

            // Disable the hack for Windows
        	PetalsInteractiveCliTest.hackForWindows( false );
        }
    }

    /**
     * <p>
     * Check the main entry point against an command line containing:
     * <ul>
     * <li>the flag debug is enabled,</li>
     * <li>the interactive mode,</li>
     * <li>the command to connect to an unknown Petals node.,</li>
     * </ul>
     * as following:<br/>
     * <code>
     * > ./petals-cli.sh -d -C<br/>
     * petals-cli> connect -h unknown -n 7700 -u petals -p petals<br/>
     * petals-cli>
     * </code>
     * </p>
     * <p>
     * Note: the connection <b>is not a real</b> connection, but raises an
     * exception on the host 'unknown'.
     * </p>
     * <p>
     * Expected results:
     * <ul>
     * <li>error on the command 'connect' about the connection to an unknown
     * host,</li>
     * <li>error stack trace is printed,</li>
     * <li>no usage is printed,</li>
     * <li>the command flow is not interrupted (because entered by the user that
     * saw the error)</li>
     * <li>the prompt is printed after error.</li>
     * </ul>
     * </p>
     */
    @Test
    public final void testRun_Interactive_Error_01() throws IOException, InterruptedException {

        final PrintStream standardConsoleOutput = System.out;
        final PrintStream standardConsoleError = System.err;
        final InputStream standardConsoleInput = System.in;
        try {
        	// Enable the hack for Windows?
        	PetalsInteractiveCliTest.hackForWindows( true );

        	// Go on...
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));
            final ByteArrayOutputStream outBAIS = new ByteArrayOutputStream();
            System.setOut(new PrintStream(outBAIS));
            final PipedOutputStream pos = new PipedOutputStream();
            System.setIn(new PipedInputStream(pos));

            // The main class MUST be instanced after to have set the console
            // streams !!
            final Main main = new Main();

            this.assertion = null;
            final Runnable writer = new Runnable() {

                @Override
                public void run() {
                    try {
                        try {
                            // Wait that Petals CLI starts
                            Thread.sleep(1000);

                            final String output1 = outBAIS.toString();
                            assertFalse("Prompt is empty", output1.isEmpty());
                            assertTrue("Petals CLI is connected", output1.contains("petals-cli>"));
                            // Enter the command realizing the connection
                            pos.write(String.format(
                                    "connect -h unknown -n 7700 -u petals -p petals%n").getBytes());
                            // Wait the execution of the command
                            Thread.sleep(1000);
                            final String error1 = errBAIS.toString();
                            assertFalse("No error message print", error1.isEmpty());
                            assertTrue("The command is missing in error message",
                                    error1.startsWith("ERROR on command '" + new Connect().getName() + "':"));
                            assertTrue("The exception throwing is missing in error message",
                                    error1.contains(ContainerAdministrationException.class.getName()));
                            assertTrue("Stack trace is missing in error message", error1.contains("Caused by"));
                            assertTrue("Stack trace is missing in error message", error1.contains(Main.class.getName()));

                            final String output2 = outBAIS.toString().substring(output1.length());
                            assertTrue("Petals CLI is connected", output2.endsWith("petals-cli>"));

                        } catch (final Exception e) {
                            fail("An exception occurs: " + e.getMessage());
                        }
                    } catch (final AssertionError e) {
                        MainTest.this.assertion = e;
                    }
                }
            };

            final Runnable petalsCli = new Runnable() {
                @Override
                public void run() {

                    try {
                        final int exitCode = main.run(new String[] { "-d", "-C" });
                        assertEquals("invalid exit code", 0, exitCode);

                    } catch (final AssertionError e) {
                        MainTest.this.assertion = e;
                    }

                }
            };

            final Thread petalsCliThread = new Thread(petalsCli, "petalsCliThread");
            final Thread writerThread = new Thread(writer, "writerThread");
            petalsCliThread.start();
            writerThread.start();
            writerThread.join();
            pos.write("exit\n".getBytes());
            pos.close();
            petalsCliThread.join();

            if (this.assertion != null) {
                throw this.assertion;
            }
        }
        finally {
            System.setOut(standardConsoleOutput);
            System.setErr(standardConsoleError);
            System.setIn(standardConsoleInput);

            // Disable the hack for Windows
        	PetalsInteractiveCliTest.hackForWindows( false );
        }
    }

    /**
     * <p>
     * Check the main entry point against an command line containing:
     * <ul>
     * <li>the flag debug is disable,</li>
     * <li>the inlined mode,</li>
     * <li>the command to connect to an existing Petals node,</li>
     * <li>a command needing no argument,</li>
     * </ul>
     * as following:<br/>
     * <code>
     * > ./petals-cli.sh - << EOF<br/>
     * connect -h localhost -n 7700 -u petals -p petals<br/>
     * list
     * EOF
     * </code>
     * </p>
     * <p>
     * Note: the connection <b>is not a real</b> connection.
     * </p>
     * <p>
     * Expected results:
     * <ul>
     * <li>no error,</li>
     * <li>no error stacktrace is printed,</li>
     * <li>no usage is printed,</li>
     * <li>commands correctly executed.</li>
     * </ul>
     * </p>
     */
    @Test
    public final void testRun_Inlined_00() {

        final PrintStream standardConsoleError = System.err;

        try {
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));

            // The main class MUST be instanced after to have set the console streams !!
            final Main main = new Main();

            final StringBuilder commandFlow = new StringBuilder(
                    "connect -h localhost -n 7700 -u petals -p petals").append(System.getProperty("line.separator")).append("list")
                    .append(System.getProperty("line.separator"));
            final ByteArrayInputStream inBAIS = new ByteArrayInputStream(commandFlow.toString()
                    .getBytes());
            System.setIn(inBAIS);

            final int exitCode = main.run(new String[] { "-" });

            final String error = errBAIS.toString();
            assertEquals("invalid exit code", 0, exitCode);
            assertTrue("Error message print", error.isEmpty());
        }
        finally {
            System.setErr(standardConsoleError);
        }
    }

    /**
     * <p>
     * Check the main entry point against an command line containing:
     * <ul>
     * <li>the flag debug is disable,</li>
     * <li>connection defines on the command line,</li>
     * <li>the inlined mode,</li>
     * <li>a command needing no argument,</li>
     * </ul>
     * as following:<br/>
     * <code>
     * > ./petals-cli.sh -h localhost -n 7700 -u petals -p petals - << EOF<br/>
     * list
     * EOF
     * </code>
     * </p>
     * <p>
     * Note: the connection <b>is not a real</b> connection.
     * </p>
     * <p>
     * Expected results:
     * <ul>
     * <li>no error,</li>
     * <li>no error stacktrace is printed,</li>
     * <li>no usage is printed,</li>
     * <li>commands correctly executed.</li>
     * </ul>
     * </p>
     */
    @Test
    public final void testRun_Inlined_02() {

        final PrintStream standardConsoleError = System.err;

        try {
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));

            // The main class MUST be instanced after to have set the console streams !!
            final Main main = new Main();

            final StringBuilder commandFlow = new StringBuilder("list").append(System.getProperty("line.separator"));
            final ByteArrayInputStream inBAIS = new ByteArrayInputStream(commandFlow.toString()
                    .getBytes());
            System.setIn(inBAIS);

            final int exitCode = main.run(new String[] { "-h", "localhost", "-n", "7700", "-u",
                    "petals", "-p", "petals", "-" });

            final String error = errBAIS.toString();
            assertEquals("invalid exit code", 0, exitCode);
            assertTrue("Error message print", error.isEmpty());
        }
        finally {
            System.setErr(standardConsoleError);
        }
    }

    /**
     * <p>
     * Check the main entry point against an command line containing:
     * <ul>
     * <li>the flag debug is disable,</li>
     * <li>the inlined mode,</li>
     * <li>the command to connect to an unknown Petals node,</li>
     * <li>a command needing no argument,</li>
     * </ul>
     * as following:<br/>
     * <code>
     * > ./petals-cli.sh - << EOF<br/>
     * connect -h unknown -n 7700 -u petals -p petals<br/>
     * list
     * EOF
     * </code>
     * </p>
     * <p>
     * Note: the connection <b>is not a real</b> connection, but raises an
     * exception on the host 'unknown'.
     * </p>
     * <p>
     * Expected results:
     * <ul>
     * <li>error on the command 'connect' about the connection to an unknown
     * host,</li>
     * <li>no error stacktrace is printed,</li>
     * <li>no usage is printed,</li>
     * <li>command flow is interrupted on command in error.</li>
     * </ul>
     * </p>
     */
    @Test
    public final void testRun_Inlined_Error_00() {

        final PrintStream standardConsoleError = System.err;

        try {
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));

            // The main class MUST be instanced after to have set the console streams !!
            final Main main = new Main();

            final StringBuilder commandFlow = new StringBuilder(
                    "connect -h unknown -n 7700 -u petals -p petals").append(System.getProperty("line.separator")).append("list")
                    .append(System.getProperty("line.separator"));
            final ByteArrayInputStream inBAIS = new ByteArrayInputStream(commandFlow.toString()
                    .getBytes());
            System.setIn(inBAIS);

            final int exitCode = main.run(new String[] { "-" });

            final String error = errBAIS.toString();
            assertEquals("invalid exit code", 1, exitCode);
            assertFalse("No error message print", error.isEmpty());
            assertTrue("Error associated to the unknown host", error.contains("Unknown Host"));
            assertFalse("Stack trace is present in error message", error.contains(Main.class.getName()));
            assertFalse("Stack trace is present in error message", error.contains("Caused by"));
        }
        finally {
            System.setErr(standardConsoleError);
        }
    }

    /**
     * <p>
     * Check the main entry point against an command line containing:
     * <ul>
     * <li>the flag debug is enabled,</li>
     * <li>the inlined mode,</li>
     * <li>the command to connect to an unknown Petals node,</li>
     * <li>a command needing no argument,</li>
     * </ul>
     * as following:<br/>
     * <code>
     * > ./petals-cli.sh -d - << EOF<br/>
     * connect -h unknown -n 7700 -u petals -p petals<br/>
     * list
     * EOF
     * </code>
     * </p>
     * <p>
     * Note: the connection <b>is not a real</b> connection, but raises an
     * exception on the host 'unknown'.
     * </p>
     * <p>
     * Expected results:
     * <ul>
     * <li>error on the command 'connect' about the connection to an unknown
     * host,</li>
     * <li>error stacktrace is printed,</li>
     * <li>no usage is printed,</li>
     * <li>command flow is interrupted on command in error.</li>
     * </ul>
     * </p>
     */
    @Test
    public final void testRun_Inlined_Error_01() {

        final PrintStream standardConsoleError = System.err;

        try {
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));

            // The main class MUST be instanced after to have set the console streams !!
            final Main main = new Main();

            final StringBuilder commandFlow = new StringBuilder(
                    "connect -h unknown -n 7700 -u petals -p petals").append(System.getProperty("line.separator")).
                    append("list").append(System.getProperty("line.separator"));
            final ByteArrayInputStream inBAIS = new ByteArrayInputStream(commandFlow.toString()
                    .getBytes());
            System.setIn(inBAIS);

            final int exitCode = main.run(new String[] {
                    "-d",
                    "-" });

            final String error = errBAIS.toString();
            assertEquals("invalid exit code", 1, exitCode);
            assertFalse("No error message print", error.isEmpty());
            assertTrue("The exception throwing is missing in error message",
                    error.contains(ContainerAdministrationException.class.getName()));
            assertTrue("Stack trace is missing in error message", error.contains("Caused by"));
            assertTrue("Stack trace is missing in error message", error.contains(Main.class.getName()));
        }
        finally {
            System.setErr(standardConsoleError);
        }
    }

    /**
     * <p>
     * Check the main entry point against an command line containing:
     * <ul>
     * <li>the flag debug is disable,</li>
     * <li>the file mode,</li>
     * <li>the command to connect to an existing Petals node,</li>
     * <li>a command needing no argument,</li>
     * </ul>
     * as following:<br/>
     * <code>
     * > cat petals-file.cmd<br/>
     * connect -h localhost -n 7700 -u petals -p petals<br/>
     * list<br/>
     * > ./petals-cli.sh petals-file.cmd
     * </code>
     * </p>
     * <p>
     * Note: the connection <b>is not a real</b> connection.
     * </p>
     * <p>
     * Expected results:
     * <ul>
     * <li>no error,</li>
     * <li>no error stacktrace is printed,</li>
     * <li>no usage is printed,</li>
     * <li>commands correctly executed.</li>
     * </ul>
     * </p>
     */
    @Test
    public final void testRun_File_00() throws IOException {

        final PrintStream standardConsoleError = System.err;

        try {
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));

            // The main class MUST be instanced after to have set the console streams !!
            final Main main = new Main();

            final File commandFile = File.createTempFile("petals-cli", "cmd");
            try {
                final OutputStream os = new FileOutputStream(commandFile);
                final Writer sw = new OutputStreamWriter(os);
                final StringBuilder commandFlow = new StringBuilder(
                        "connect -h localhost -n 7700 -u petals -p petals").append(System.getProperty("line.separator")).append("list")
                        .append(System.getProperty("line.separator"));
                sw.write(commandFlow.toString());

                final int exitCode = main.run(new String[] { commandFile.getAbsolutePath() });

                final String error = errBAIS.toString();
                assertEquals("invalid exit code", 0, exitCode);
                assertTrue("Error message print", error.isEmpty());

            } finally {
                commandFile.delete();
            }
        }
        finally {
            System.setErr(standardConsoleError);
        }
    }

    /**
     * <p>
     * Check the main entry point against an command line containing:
     * <ul>
     * <li>the flag debug is disable,</li>
     * <li>connection defines on the command line,</li>
     * <li>the file mode,</li>
     * <li>a command needing no argument,</li>
     * </ul>
     * as following:<br/>
     * <code>
     * > cat petals-file.cmd<br/>
     * list<br/>
     * > ./petals-cli.sh -h localhost -n 7700 -u petals -p petals petals-file.cmd
     * </code>
     * </p>
     * <p>
     * Note: the connection <b>is not a real</b> connection.
     * </p>
     * <p>
     * Expected results:
     * <ul>
     * <li>no error,</li>
     * <li>no error stacktrace is printed,</li>
     * <li>no usage is printed,</li>
     * <li>commands correctly executed.</li>
     * </ul>
     * </p>
     */
    @Test
    public final void testRun_File_02() throws IOException {

        final PrintStream standardConsoleError = System.err;

        try {
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));

            // The main class MUST be instanced after to have set the console streams !!
            final Main main = new Main();

            final File commandFile = File.createTempFile("petals-cli", "cmd");
            try {
                final OutputStream os = new FileOutputStream(commandFile);
                final Writer sw = new OutputStreamWriter(os);
                final StringBuilder commandFlow = new StringBuilder("list").append(System.getProperty("line.separator"));
                sw.write(commandFlow.toString());
                sw.close();

                final int exitCode = main.run(new String[] { "-h", "localhost", "-n", "7700", "-u",
                        "petals", "-p", "petals", commandFile.getAbsolutePath() });

                final String error = errBAIS.toString();
                assertEquals("invalid exit code", 0, exitCode);
                assertTrue("Error message print", error.isEmpty());

            } finally {
                commandFile.delete();
            }
        }
        finally {
            System.setErr(standardConsoleError);
        }
    }

    /**
     * <p>
     * Check the main entry point against an command line containing:
     * <ul>
     * <li>the flag debug is disable,</li>
     * <li>the file mode,</li>
     * <li>the command to connect to an unknown Petals node,</li>
     * <li>a command needing no argument,</li>
     * </ul>
     * as following:<br/>
     * <code>
     * > cat petals-file.cmd<br/>
     * connect -h unknown -n 7700 -u petals -p petals<br/>
     * list<br/>
     * > ./petals-cli.sh petals-file.cmd
     * </code>
     * </p>
     * <p>
     * Note: the connection <b>is not a real</b> connection, but raises an
     * exception on the host 'unknown'.
     * </p>
     * <p>
     * Expected results:
     * <ul>
     * <li>error on the command 'connect' about the connection to an unknown
     * host,</li>
     * <li>no error stack trace is printed,</li>
     * <li>no usage is printed,</li>
     * <li>command flow is interrupted on command in error.</li>
     * </ul>
     * </p>
     */
    @Test
    public final void testRun_File_Error_00() throws IOException {

        final PrintStream standardConsoleError = System.err;

        try {
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));

            // The main class MUST be instanced after to have set the console streams !!
            final Main main = new Main();

            final File commandFile = File.createTempFile("petals-cli", "cmd");
            try {
                final OutputStream os = new FileOutputStream(commandFile);
                final Writer sw = new OutputStreamWriter(os);
                final StringBuilder commandFlow = new StringBuilder(
                        "connect -h unknown -n 7700 -u petals -p petals").append(System.getProperty("line.separator")).append("list")
                        .append(System.getProperty("line.separator"));
                sw.write(commandFlow.toString());
                sw.close();

                final int exitCode = main.run(new String[] { commandFile.getAbsolutePath() });

                final String error = errBAIS.toString();
                assertEquals("invalid exit code", 1, exitCode);
                assertFalse("No error message print", error.isEmpty());
                assertTrue("Error associated to the unknown host", error.contains("Unknown Host"));
                assertFalse("Stack trace is present in error message",
                        error.contains(Main.class.getName()));
                assertFalse("Stack trace is present in error message", error.contains("Caused by"));

            } finally {
                commandFile.delete();
            }
        }
        finally {
            System.setErr(standardConsoleError);
        }
    }

    /**
     * <p>
     * Check the main entry point against an command line containing:
     * <ul>
     * <li>the flag debug is enabled,</li>
     * <li>the file mode,</li>
     * <li>the command to connect to an unknown Petals node,</li>
     * <li>a command needing no argument,</li>
     * </ul>
     * as following:<br/>
     * <code>
     * > cat petals-file.cmd<br/>
     * connect -h unknown -n 7700 -u petals -p petals<br/>
     * list<br/>
     * > ./petals-cli.sh -d petals-file.cmd
     * </code>
     * </p>
     * <p>
     * Note: the connection <b>is not a real</b> connection, but raises an
     * exception on the host 'unknown'.
     * </p>
     * <p>
     * Expected results:
     * <ul>
     * <li>error on the command 'connect' about the connection to an unknown
     * host,</li>
     * <li>error stack trace is printed,</li>
     * <li>no usage is printed,</li>
     * <li>command flow is interrupted on command in error.</li>
     * </ul>
     * </p>
     */
    @Test
    public final void testRun_File_Error_01() throws IOException {

        final PrintStream standardConsoleError = System.err;
        try {
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));

            // The main class MUST be instanced after to have set the console streams !!
            final Main main = new Main();

            final File commandFile = File.createTempFile("petals-cli", "cmd");
            try {
                final OutputStream os = new FileOutputStream(commandFile);
                final Writer sw = new OutputStreamWriter(os);
                final StringBuilder commandFlow = new StringBuilder(
                        "connect -h unknown -n 7700 -u petals -p petals").append(System.getProperty("line.separator")).append("list")
                        .append(System.getProperty("line.separator"));
                sw.write(commandFlow.toString());
                sw.close();

                final int exitCode = main.run(new String[] { "-d", commandFile.getAbsolutePath() });

                final String error = errBAIS.toString();
                assertEquals("invalid exit code", 1, exitCode);
                assertFalse("No error message print", error.isEmpty());
                assertTrue("The exception throwing is missing in error message",
                        error.contains(ContainerAdministrationException.class.getName()));
                assertTrue("Stack trace is missing in error message", error.contains("Caused by"));
                assertTrue("Stack trace is missing in error message",
                        error.contains(Main.class.getName()));

            } finally {
                commandFile.delete();
            }
        }
        finally {
            System.setErr(standardConsoleError);
        }
    }

    /**
     * <p>
     * Unit test about the default logging system configuration.
     * </p>
     * <p>
     * Expected results: Template of error message is: <code>ERROR: xxxxxxxx</code>.
     * </p>
     */
    @Test
    public final void testLog_00() throws IOException {

        final PrintStream standardConsoleError = System.err;

        try {
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));

            // The main class MUST be instanced after to have set the console streams !!
            final Main main = new Main();

            main.initLogging();

            final String errorMessage = "My error message";
            Logger.getLogger(this.getClass().getCanonicalName()).severe(errorMessage);
            final String warnMessage = "My warn message";
            Logger.getLogger(this.getClass().getCanonicalName()).warning(warnMessage);
            final String infoMessage = "My info message";
            Logger.getLogger(this.getClass().getCanonicalName()).info(infoMessage);

            final String error = errBAIS.toString();
            assertTrue("Unexpected format", error.startsWith("ERROR: " + errorMessage + System.getProperty("line.separator")));
            assertTrue("Warn level is not printed", error.contains(warnMessage));
            assertFalse("Info level is printed", error.contains(infoMessage));
        }
        finally {
            System.setErr(standardConsoleError);
        }
    }

    /**
     * <p>
     * Unit test about a logging system configuration provided at runtime.
     * </p>
     * <p>
     * Expected results: the configuration provided is correctly used.
     * </p>
     */
    @Test
    public final void testLog_01() throws IOException {

        final PrintStream standardConsoleError = System.err;

        try {
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));

            final File logConfFile = File.createTempFile("petals-cli-log", ".properties");
            final OutputStream osLogConfFile = new FileOutputStream(logConfFile);
            final InputStream isLogConfFile = Thread.currentThread().getContextClassLoader()
                    .getResourceAsStream("test-log-conf.properties");
            try {
                IOUtils.copy(isLogConfFile, osLogConfFile);
            } finally {
                osLogConfFile.close();
                isLogConfFile.close();
            }

            try {
                // The main class MUST be instanced after to have set the console streams !!
                final Main main = new Main();
                System.setProperty("java.util.logging.config.file", logConfFile.getAbsolutePath());
                main.initLogging();

                final String errorMessage = "My error message";
                Logger.getLogger(this.getClass().getCanonicalName()).severe(errorMessage);
                final String warnMessage = "My warn message";
                Logger.getLogger(this.getClass().getCanonicalName()).warning(warnMessage);
                final String infoMessage = "My info message";
                Logger.getLogger(this.getClass().getCanonicalName()).info(infoMessage);

                final String error = errBAIS.toString();
                assertTrue("Unexpected format", error.startsWith("ERROR: " + errorMessage + System.getProperty("line.separator")));
                assertTrue("Warn level is not printed", error.contains(warnMessage));
                assertTrue("Info level is not printed", error.contains(infoMessage));
            } finally {
                logConfFile.delete();
            }
        }
        finally {
            System.setErr(standardConsoleError);
        }
    }

    /**
     * <p>
     * Check the main entry point against an command line containing:
     * <ul>
     * <li>the command mode without connection arguments,</li>
     * <li>the operand flag,</li>
     * <li>a command needing an operand flag completed with an argument,</li>
     * </ul>
     * as following:<br/>
     * <code>> ./petals-cli.sh -c -- mycommand -- -z</code>
     * </p>
     * <p>
     * Expected results: no error
     * </p>
     */
    @Test
    public final void testRun_Command_00() {

        final PrintStream standardConsoleOutput = System.out;
        final PrintStream standardConsoleError = System.err;

        final ClassLoader oldClassloader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(new ClassLoader(oldClassloader) {

            @Override
            public Enumeration<URL> getResources(final String name) throws IOException {
                if (name.equals("META-INF/services/" + Command.class.getName())) {
                    return super.getResources("services/my-command");
                } else {
                    return super.getResources(name);
                }
            }
        });

        try {
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));
            final ByteArrayOutputStream outBAIS = new ByteArrayOutputStream();
            System.setOut(new PrintStream(outBAIS));

            // The main class MUST be instanced after to have set the console
            // streams !!
            final Main main = new Main();

            final int exitCode = main.run(new String[] { "-" + ShellFactory.COMMAND_SHORT_OPTION,
                    "--", "dummy", "--", "-z" });

            final String error = errBAIS.toString();
            final String output = outBAIS.toString();
            assertEquals("invalid exit code", 0, exitCode);
            assertTrue("Error message print", error.isEmpty());
            assertFalse("Usage header is not print", output.contains(Main.USAGE_HEADER));
            assertFalse("Usage footer is not print", output.contains(Main.USAGE_FOOTER));
        } finally {
            System.setOut(standardConsoleOutput);
            System.setErr(standardConsoleError);

            Thread.currentThread().setContextClassLoader(oldClassloader);
        }
    }

    /**
     * <p>
     * Check the main entry point against default connection parameters, an
     * command line containing:
     * <ul>
     * <li>the command mode without connection arguments,</li>
     * <li>the operand flag,</li>
     * <li>a command requiring a connection,</li>
     * </ul>
     * as following:<br/>
     * <code>> ./petals-cli.sh -c -- version</code>
     * </p>
     * <p>
     * Expected results: no error
     * </p>
     */
    @Test
    public final void testDefaultConnection_00() {

        final PrintStream standardConsoleOutput = System.out;
        final PrintStream standardConsoleError = System.err;

        try {
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));
            final ByteArrayOutputStream outBAIS = new ByteArrayOutputStream();
            System.setOut(new PrintStream(outBAIS));

            // The main class MUST be instanced after to have set the console
            // streams !!
            final Main main = new Main();

            final int exitCode = main.run(new String[] { "-" + ShellFactory.COMMAND_SHORT_OPTION,
                    "--", "version" });

            final String error = errBAIS.toString();
            final String output = outBAIS.toString();
            assertEquals("invalid exit code", 0, exitCode);
            assertTrue("Error message print", error.isEmpty());
            assertFalse("Usage header is not print", output.contains(Main.USAGE_HEADER));
            assertFalse("Usage footer is not print", output.contains(Main.USAGE_FOOTER));
        } finally {
            System.setOut(standardConsoleOutput);
            System.setErr(standardConsoleError);
        }
    }

    /**
     * Test the correct disconnection when:
     * <ul>
     * <li>a connection is established using the yes flag,</li>
     * <li>a command requiring the connection is executed (ex: 'version' that
     * requires a connection in the implementation of the Petals Admin API
     * mock),</li>
     * <li>the command 'disconnect' or 'exit' is not executed before to end the
     * Petals CLI script,</li>
     * <li>using the inlined mode.</li>
     * </ul>
     * </p>
     * <p>
     * Expected results:
     * <ul>
     * <li>No error occurs,</li>
     * <li>The connection is established on 'connect',</li>
     * <li>The connection is correctly disconnected.</li>
     * </ul>
     * </p>
     */
    @Test
    public void testRun_Inlined_Disconnection_00() throws ContainerAdministrationException,
            DuplicatedServiceException, MissingServiceException {

        final PrintStream standardConsoleOutput = System.out;
        final PrintStream standardConsoleError = System.err;

        try {
            final ByteArrayOutputStream outBAIS = new ByteArrayOutputStream();
            System.setOut(new PrintStream(outBAIS));
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));

            // The main class MUST be instanced after to have set the console
            // streams !!
            final Main main = new Main();

            final StringBuilder commandFlow = new StringBuilder("connect -y")
                    .append(System.getProperty("line.separator")).append("version")
                    .append(System.getProperty("line.separator"));
            final ByteArrayInputStream inBAIS = new ByteArrayInputStream(commandFlow.toString()
                    .getBytes());
            System.setIn(inBAIS);

            final int exitCode = main.run(new String[] { "-h", "localhost", "-n", "7700", "-u",
                    "petals", "-p", "petals", "-" });


            final String error = errBAIS.toString();
            final String output = outBAIS.toString();
            assertTrue("Connection not established",
                    output.contains(ContainerAdministrationMock.SYSTEM_INFO));
            assertTrue("An error occurs", error.isEmpty());
            assertEquals("Invalid exit code", 0, exitCode);
            final ContainerAdministration containerAdministration = PetalsAdministrationFactory
                    .newInstance().newContainerAdministration();
            assertFalse("Connection not disconnected", containerAdministration.isConnected());
        } finally {
            System.setOut(standardConsoleOutput);
            System.setErr(standardConsoleError);
        }

    }

    /**
     * Test the correct disconnection when:
     * <ul>
     * <li>a connection is established using the yes flag,</li>
     * <li>a command requiring the connection is executed (ex: 'version' that
     * requires a connection in the implementation of the Petals Admin API
     * mock),</li>
     * <li>the command 'disconnect' or 'exit' is not executed before to end the
     * Petals CLI script,</li>
     * <li>using a non-interactive shell.</li>
     * </ul>
     * </p>
     * <p>
     * Expected results:
     * <ul>
     * <li>No error occurs,</li>
     * <li>The connection is established on 'connect',</li>
     * <li>The connection is correctly disconnected.</li>
     * </ul>
     * </p>
     */
    @Test
    public void testRun_File_Disconnection_00() throws CommandException,
            DuplicatedCommandException,
            IllegalArgumentException, IOException, DuplicatedServiceException,
            MissingServiceException, ContainerAdministrationException {

        final PrintStream standardConsoleOut = System.out;
        final PrintStream standardConsoleError = System.err;

        final Command commandConnect = new Connect();
        final Command commandVersion = new Version();

        // Create the Petals CLI script
        final File petalsCliScript = this.tempFolder.newFile("petals-cli.cmd");
        final OutputStream osScript = new FileOutputStream(petalsCliScript);
        final Writer pwScript = new OutputStreamWriter(osScript);
        try {
            pwScript.write(commandConnect.getName());
            pwScript.write(" -y\n");
            pwScript.write(commandVersion.getName());
            pwScript.write('\n');
        } finally {
            pwScript.close();
            osScript.close();
        }

        try {
            final ByteArrayOutputStream outBAIS = new ByteArrayOutputStream();
            System.setOut(new PrintStream(outBAIS));
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));

            // The main class MUST be instanced after to have set the console
            // streams !!
            final Main main = new Main();

            final int exitCode = main.run(new String[] { "-" + ShellFactory.FILE_LONG_OPTION,
                    petalsCliScript.getAbsolutePath() });

            final String error = errBAIS.toString();
            final String output = outBAIS.toString();

            assertTrue("Connection not established",
                    output.contains(ContainerAdministrationMock.SYSTEM_INFO));
            assertTrue("An error occurs", error.isEmpty());
            assertEquals("Invalid exit code", 0, exitCode);
            final ContainerAdministration containerAdministration = PetalsAdministrationFactory
                    .newInstance().newContainerAdministration();
            assertFalse("Connection not disconnected", containerAdministration.isConnected());

        } finally {
            System.setOut(standardConsoleOut);
            System.setErr(standardConsoleError);
        }
    }

    /**
     * Test the correct disconnection when:
     * <ul>
     * <li>using the command line shell,</li>
     * <li>a automatic connection is established,</li>
     * <li>a command requiring the connection is executed (ex: 'version' that
     * requires a connection in the implementation of the Petals Admin API
     * mock).</li>
     * </ul>
     * </p>
     * <p>
     * Expected results:
     * <ul>
     * <li>No error occurs,</li>
     * <li>The connection is established before to execute 'version',</li>
     * <li>The connection is correctly disconnected when command line ends.</li>
     * </ul>
     * </p>
     */
    @Test
    public void testRun_Command_Disconnection_00() throws CommandException,
            DuplicatedCommandException,
            IllegalArgumentException, IOException, DuplicatedServiceException,
            MissingServiceException, ContainerAdministrationException {

        final PrintStream standardConsoleOut = System.out;
        final PrintStream standardConsoleError = System.err;

        final Command commandVersion = new Version();

        try {
            final ByteArrayOutputStream outBAIS = new ByteArrayOutputStream();
            System.setOut(new PrintStream(outBAIS));
            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));

            // The main class MUST be instanced after to have set the console
            // streams !!
            final Main main = new Main();

            final int exitCode = main.run(new String[] { "-" + ShellFactory.COMMAND_SHORT_OPTION,
                    "--", commandVersion.getName() });

            final String error = errBAIS.toString();
            final String output = outBAIS.toString();

            assertTrue("Connection not established",
                    output.contains(ContainerAdministrationMock.SYSTEM_INFO));
            assertTrue("An error occurs", error.isEmpty());
            assertEquals("Invalid exit code", 0, exitCode);
            final ContainerAdministration containerAdministration = PetalsAdministrationFactory
                    .newInstance().newContainerAdministration();
            assertFalse("Connection not disconnected", containerAdministration.isConnected());

        } finally {
            System.setOut(standardConsoleOut);
            System.setErr(standardConsoleError);
        }
    }

    /**
     * Test the correct disconnection when:
     * <ul>
     * <li>a connection is established without using the yes flag but confirming
     * connection values,</li>
     * <li>a command requiring the connection is executed (ex: 'version' that
     * requires a connection in the implementation of the Petals Admin API
     * mock),</li>
     * <li>the command 'disconnect' or 'exit' is not executed before to end the
     * Petals CLI script,</li>
     * <li>using an interactive shell.</li>
     * </ul>
     * </p>
     * <p>
     * Expected results:
     * <ul>
     * <li>No error occurs,</li>
     * <li>The connection is established on 'connect',</li>
     * <li>The connection is correctly disconnected.</li>
     * </ul>
     * </p>
     */
    @Test()
    public void testRun_Interactive_Disconnection_00() throws CommandException,
            DuplicatedServiceException,
            MissingServiceException, ContainerAdministrationException, IOException,
            InterruptedException {

        final PrintStream standardConsoleOutput = System.out;
        final PrintStream standardConsoleError = System.err;
        final InputStream standardConsoleInput = System.in;

        try {
            // Enable the hack for Windows?
            PetalsInteractiveCliTest.hackForWindows(true);

            final ByteArrayOutputStream errBAIS = new ByteArrayOutputStream();
            System.setErr(new PrintStream(errBAIS));
            final ByteArrayOutputStream outBAIS = new ByteArrayOutputStream();
            System.setOut(new PrintStream(outBAIS));
            final PipedOutputStream pos = new PipedOutputStream();
            System.setIn(new PipedInputStream(pos));

            // The main class MUST be instanced after to have set the console
            // streams !!
            final Main main = new Main();

            this.assertion = null;
            final ContainerAdministration containerAdministration = PetalsAdministrationFactory
                    .newInstance().newContainerAdministration();
            final Runnable writer = new Runnable() {

                @Override
                public void run() {
                    try {
                        try {
                            // Wait that Petals CLI starts
                            Thread.sleep(1000);

                            final String output1 = outBAIS.toString();
                            final String error1 = errBAIS.toString();
                            assertFalse("Prompt is empty", output1.isEmpty());
                            assertTrue("An error occurs", error1.isEmpty());
                            assertTrue("Invalid prompt", output1.contains("petals-cli>"));
                            assertFalse("Invalid prompt", output1.contains("localhost:7700"));

                            // Connect
                            pos.write("connect\n".getBytes());
                            // Wait the execution of the command
                            Thread.sleep(1000);
                            final String output2 = outBAIS.toString().substring(output1.length());
                            final String error2 = errBAIS.toString().substring(error1.length());
                            assertFalse("Prompt is empty", output2.isEmpty());
                            assertTrue("An error occurs", error2.isEmpty());
                            assertTrue(
                                    "Confirmation message is missing",
                                    output2.contains("Would you like to connect to petals:*****@127.0.0.1:7700? (y/n)"));

                            // Confirmation
                            pos.write("y\n".getBytes());
                            // Wait the execution of the confirmation
                            Thread.sleep(1000);
                            final String output3 = outBAIS.toString().substring(
                                    output1.length() + output2.length());
                            final String error3 = errBAIS.toString().substring(
                                    error1.length() + error2.length());
                            assertFalse("Prompt is empty", output3.isEmpty());
                            assertTrue("An error occurs", error3.isEmpty());
                            assertTrue("Petals CLI is not connected",
                                    output3.contains("petals-cli@127.0.0.1:7700>"));
                            assertTrue("Connection not established",
                                    containerAdministration.isConnected());

                            // Enter the command
                            pos.write("version\n".getBytes());
                            // Wait the execution of the command
                            Thread.sleep(1000);
                            final String output4 = outBAIS.toString().substring(
                                    output1.length() + output2.length() + output3.length());
                            final String error4 = errBAIS.toString().substring(
                                    error1.length() + error2.length() + error3.length());
                            assertTrue("An error occurs", error4.isEmpty());
                            assertTrue("Unexpected version information",
                                    output4.contains(ContainerAdministrationMock.SYSTEM_INFO));

                        } catch (final Exception e) {
                            fail("An exception occurs: " + e.getMessage());
                        }
                    } catch (final AssertionError e) {
                        MainTest.this.assertion = e;
                    }
                }
            };

            final Runnable petalsCli = new Runnable() {

                @Override
                public void run() {

                    try {
                        final int exitCode = main.run(new String[] { "-h", "localhost", "-n",
                                "7700", "-u", "petals", "-p", "petals", "-C" });

                        try {
                            assertEquals("invalid exit code", 0, exitCode);
                            assertFalse("Connection not disconnected",
                                    containerAdministration.isConnected());
                        } catch (final Exception e) {
                            fail("An exception occurs: " + e.getMessage());
                        }

                    } catch (final AssertionError e) {
                        MainTest.this.assertion = e;
                    }

                }
            };

            final Thread petalsCliThread = new Thread(petalsCli, "petalsCliThread");
            final Thread writerThread = new Thread(writer, "writerThread");
            petalsCliThread.start();
            writerThread.start();
            writerThread.join();
            pos.close();
            petalsCliThread.join();

            if (this.assertion != null) {
                throw this.assertion;
            }
        } finally {
            System.setOut(standardConsoleOutput);
            System.setErr(standardConsoleError);
            System.setIn(standardConsoleInput);

            // Disable the hack for Windows
            PetalsInteractiveCliTest.hackForWindows(false);
        }
    }
}
