diff --git a/gclc-process/pom.xml b/gclc-process/pom.xml index 1ef1398..f7dceb4 100644 --- a/gclc-process/pom.xml +++ b/gclc-process/pom.xml @@ -51,7 +51,7 @@ net.bigeon gclc - 2.0.9 + 2.0.10 diff --git a/gclc-process/src/main/java/net/bigeon/gclc/process/CommandForeground.java b/gclc-process/src/main/java/net/bigeon/gclc/process/CommandForeground.java index eb22e00..83f04be 100644 --- a/gclc-process/src/main/java/net/bigeon/gclc/process/CommandForeground.java +++ b/gclc-process/src/main/java/net/bigeon/gclc/process/CommandForeground.java @@ -132,16 +132,23 @@ public class CommandForeground extends ParametrizedCommand { protected void doExecute(final ConsoleOutput out, final ConsoleInput in, final CommandParameters parameters) throws CommandRunException { String string = parameters.get("pid"); - final List additionals = parameters.getAdditionals(); - if (string == null && !additionals.isEmpty()) { + if (string == null) { + final List additionals = parameters.getAdditionals(); + if (additionals.isEmpty()) { + throw new CommandRunException(CommandRunExceptionType.USAGE, + "Missing process id"); + } string = additionals.get(0); } - if (string == null) { + Integer pid; + try { + pid = Integer.valueOf(string); + } catch (final NumberFormatException e) { throw new CommandRunException(CommandRunExceptionType.USAGE, - "Missing process id"); + "PID should be an integer"); } // Join the command. - final Task cmd = pool.get(string); + final Task cmd = pool.get(pid); if (!(cmd instanceof ForkTask)) { throw new CommandRunException("No such forked process"); } @@ -151,7 +158,8 @@ public class CommandForeground extends ParametrizedCommand { delai = Long.parseLong(delaiOpt) * MILLIS_IN_A_SEC; } if (delai < 0) { - throw new CommandRunException("Join delai cannot be negative"); + throw new CommandRunException(CommandRunExceptionType.USAGE, + "Join delai cannot be negative"); } ((ForkTask) cmd).join(out, in, delai); } diff --git a/gclc-process/src/main/java/net/bigeon/gclc/process/CommandFork.java b/gclc-process/src/main/java/net/bigeon/gclc/process/CommandFork.java index 2804bee..c0fa690 100644 --- a/gclc-process/src/main/java/net/bigeon/gclc/process/CommandFork.java +++ b/gclc-process/src/main/java/net/bigeon/gclc/process/CommandFork.java @@ -4,6 +4,7 @@ */ package net.bigeon.gclc.process; +import java.text.MessageFormat; /*- * #%L * process @@ -43,6 +44,7 @@ import net.bigeon.gclc.command.Command; import net.bigeon.gclc.command.ICommand; import net.bigeon.gclc.command.ICommandProvider; import net.bigeon.gclc.exception.CommandRunException; +import net.bigeon.gclc.exception.CommandRunExceptionType; import net.bigeon.gclc.manager.ConsoleInput; import net.bigeon.gclc.manager.ConsoleOutput; @@ -95,12 +97,18 @@ public class CommandFork extends Command { public void execute(final ConsoleOutput out, final ConsoleInput in, final String... args) throws CommandRunException { if (args.length < 1) { - throw new CommandRunException("No command to fork"); + throw new CommandRunException(CommandRunExceptionType.USAGE, + "No command to fork"); + } + final String string = args[0]; + final ICommand cmd = provider.get(string); + if (cmd == null) { + throw new CommandRunException(CommandRunExceptionType.USAGE, + MessageFormat.format("No such command {0}", string)); } - final ICommand cmd = provider.get(args[0]); final String[] inner = Arrays.copyOfRange(args, 1, args.length); final ForkTask task = new ForkCommandTask(cmd, inner, lines); - final Thread th = new Thread(task); + final Thread th = new Thread(task, MessageFormat.format("fork [{0}]", string)); pool.add(task); th.start(); } diff --git a/gclc-process/src/main/java/net/bigeon/gclc/process/ForkTask.java b/gclc-process/src/main/java/net/bigeon/gclc/process/ForkTask.java index 9d48a81..c8d57cb 100644 --- a/gclc-process/src/main/java/net/bigeon/gclc/process/ForkTask.java +++ b/gclc-process/src/main/java/net/bigeon/gclc/process/ForkTask.java @@ -85,7 +85,9 @@ public abstract class ForkTask implements Task { /** THe listeners. */ private final Set listeners = new HashSet<>(); /** The running state. */ - private boolean running = false; + private boolean running = true; + /** The running state. */ + private boolean started = false; /** The connecting input for this task */ protected final ConnectingConsoleInput in = new ConnectingConsoleInput(); /** The connecting output for this task */ @@ -169,7 +171,7 @@ public abstract class ForkTask implements Task { @Override public final void run() { synchronized (runLock) { - running = true; + started = true; } try { doRun(); @@ -192,4 +194,11 @@ public abstract class ForkTask implements Task { runLock.notifyAll(); } } + + /** @return the started */ + public boolean isStarted() { + synchronized (runLock) { + return started; + } + } } diff --git a/gclc-process/src/main/java/net/bigeon/gclc/process/ProcessClear.java b/gclc-process/src/main/java/net/bigeon/gclc/process/ProcessClear.java index 05bab25..fd81192 100644 --- a/gclc-process/src/main/java/net/bigeon/gclc/process/ProcessClear.java +++ b/gclc-process/src/main/java/net/bigeon/gclc/process/ProcessClear.java @@ -92,7 +92,7 @@ public final class ProcessClear extends Command { @Override public void execute(final ConsoleOutput out, final ConsoleInput in, final String... args) { - for (final String id : pool.getPIDs()) { + for (final Integer id : pool.getPIDs()) { if (!pool.get(id).isRunning()) { pool.remove(id); } @@ -103,14 +103,14 @@ public final class ProcessClear extends Command { * @see fr.bigeon.gclc.command.ICommand#tip() */ @Override public String tip() { - return "Request a process to stop (softly)"; + return "Remove non running processes from pool"; } /* (non-Javadoc) * @see fr.bigeon.gclc.command.Command#usageDetail() */ @Override protected String usageDetail() { - return null; + return " All processes in the pool have their running status checked and the non running ones are removed from the list of tasks."; } } diff --git a/gclc-process/src/main/java/net/bigeon/gclc/process/ProcessKill.java b/gclc-process/src/main/java/net/bigeon/gclc/process/ProcessKill.java index e592588..eacd34b 100644 --- a/gclc-process/src/main/java/net/bigeon/gclc/process/ProcessKill.java +++ b/gclc-process/src/main/java/net/bigeon/gclc/process/ProcessKill.java @@ -69,6 +69,8 @@ package net.bigeon.gclc.process; * #L% */ import net.bigeon.gclc.command.Command; +import net.bigeon.gclc.exception.CommandRunException; +import net.bigeon.gclc.exception.CommandRunExceptionType; import net.bigeon.gclc.manager.ConsoleInput; import net.bigeon.gclc.manager.ConsoleOutput; @@ -91,8 +93,12 @@ public final class ProcessKill extends Command { * ConsoleOutput, fr.bigeon.gclc.manager.ConsoleInput, java.lang.String[]) */ @Override public void execute(final ConsoleOutput out, final ConsoleInput in, - final String... args) { - pool.get(args[0]).setRunning(false); + final String... args) throws CommandRunException { + if (args.length < 1) { + throw new CommandRunException(CommandRunExceptionType.USAGE, + "A pid should be specified."); + } + pool.get(Integer.valueOf(args[0])).setRunning(false); } /* (non-Javadoc) @@ -106,7 +112,13 @@ public final class ProcessKill extends Command { * @see fr.bigeon.gclc.command.Command#usageDetail() */ @Override protected String usageDetail() { - return null; + return " is the identification of the process to request the stop to in the pool"; } + /* (non-Javadoc) + * @see net.bigeon.gclc.command.Command#usagePattern() */ + @Override + protected String usagePattern() { + return super.usagePattern() + " "; + } } diff --git a/gclc-process/src/main/java/net/bigeon/gclc/process/ProcessList.java b/gclc-process/src/main/java/net/bigeon/gclc/process/ProcessList.java index 5b83c10..2294b93 100644 --- a/gclc-process/src/main/java/net/bigeon/gclc/process/ProcessList.java +++ b/gclc-process/src/main/java/net/bigeon/gclc/process/ProcessList.java @@ -99,9 +99,9 @@ public final class ProcessList extends Command { @Override public void execute(final ConsoleOutput out, final ConsoleInput in, final String... args) throws CommandRunException { - final ArrayList pids = new ArrayList<>(pool.getPIDs()); + final ArrayList pids = new ArrayList<>(pool.getPIDs()); Collections.sort(pids); - for (final String string : pids) { + for (final Integer string : pids) { try { out.println(MessageFormat.format("{0}\t{1}", string, //$NON-NLS-1$ pool.get(string).getName())); @@ -124,7 +124,7 @@ public final class ProcessList extends Command { * @see fr.bigeon.gclc.command.Command#usageDetail() */ @Override protected String usageDetail() { - return null; + return " No argument is considered by this command"; } } diff --git a/gclc-process/src/main/java/net/bigeon/gclc/process/TaskPool.java b/gclc-process/src/main/java/net/bigeon/gclc/process/TaskPool.java index fb8948d..1d61e6d 100644 --- a/gclc-process/src/main/java/net/bigeon/gclc/process/TaskPool.java +++ b/gclc-process/src/main/java/net/bigeon/gclc/process/TaskPool.java @@ -78,7 +78,7 @@ import java.util.Map; * @author Emmanuel Bigeon */ public final class TaskPool { /** The running processes. */ - private final Map running = new HashMap<>(); + private final Map running = new HashMap<>(); /** The count for process id. */ private int count = 0; /** The lock for pid attribution synchronization. */ @@ -99,11 +99,11 @@ public final class TaskPool { * * @param cmd the process * @return the pid */ - public String add(final Task cmd) { + public int add(final Task cmd) { if (cmd == null) { throw new IllegalArgumentException("Task cannot be null"); //$NON-NLS-1$ } - final String pid; + final int pid; synchronized (lock) { pid = getPID(); running.put(pid, cmd); @@ -124,21 +124,21 @@ public final class TaskPool { * * @param pid the task id * @return the task, if any, associated to this id */ - public Task get(final String pid) { + public Task get(final int pid) { synchronized (lock) { - return running.get(pid); + return running.get(Integer.valueOf(pid)); } } /** Get the next process id. * * @return the process id */ - private String getPID() { + private int getPID() { synchronized (lock) { - String pid; + int pid; do { - pid = Integer.toString(count++); - } while (running.containsKey(pid)); + pid = count++; + } while (running.containsKey(Integer.valueOf(pid))); return pid; } } @@ -146,7 +146,7 @@ public final class TaskPool { /** Get the running processes' identifiers. * * @return the pids */ - public Collection getPIDs() { + public Collection getPIDs() { return new HashSet<>(running.keySet()); } @@ -158,10 +158,10 @@ public final class TaskPool { /** Remove a task from the pool * * @param pid the task id */ - public void remove(final String pid) { + public void remove(final int pid) { synchronized (lock) { - running.remove(pid); - count = Math.min(count, Integer.parseInt(pid)); + running.remove(Integer.valueOf(pid)); + count = Math.min(count, (pid)); } } diff --git a/gclc-process/src/main/java/net/bigeon/gclc/process/io/ConnectingConsoleInput.java b/gclc-process/src/main/java/net/bigeon/gclc/process/io/ConnectingConsoleInput.java index 9cf6a66..b33dea8 100644 --- a/gclc-process/src/main/java/net/bigeon/gclc/process/io/ConnectingConsoleInput.java +++ b/gclc-process/src/main/java/net/bigeon/gclc/process/io/ConnectingConsoleInput.java @@ -90,15 +90,21 @@ public final class ConnectingConsoleInput implements ConsoleInput { private boolean close = false; /** The prompt string. */ private StringProvider prompt = EMPTY_STRING; - /** If the input is currently in prompting state. */ + /** If the input is currently in prompting state. + *

+ * To change it you should be in a promptLock. */ private boolean prompting = false; - /** The synchronization lock for the rompting status. */ + /** The synchronization lock for the prompting status. */ private final Object promptLock = new Object(); /** The synchronization lock for the connection status. */ private final Object connectionLock = new Object(); - /** The connected console input. */ + /** The connected console input. + *

+ * To use it, you should be in a promptLock and connectionLock. */ private ConsoleInput connected = null; - /** The connection state. */ + /** The connection state. + *

+ * To read or modify it, you should be in a connectionLock synchronize block. */ private boolean disconnection = false; /* (non-Javadoc) @@ -185,7 +191,11 @@ public final class ConnectingConsoleInput implements ConsoleInput { if (!prompting) { return null; } - if (connected == null) { + boolean connect = true; + synchronized (connectionLock) { + connect = connected != null; + } + if (!connect) { try { promptLock.wait(); } catch (final InterruptedException e) { @@ -193,8 +203,8 @@ public final class ConnectingConsoleInput implements ConsoleInput { Thread.currentThread().interrupt(); } } else { - final String res = connected.prompt(message); synchronized (connectionLock) { + final String res = connected.prompt(message); if (disconnection) { disconnection = false; } else if (prompting) { @@ -219,14 +229,14 @@ public final class ConnectingConsoleInput implements ConsoleInput { synchronized (promptLock) { prompting = true; } - while (true) { + do { synchronized (promptLock) { if (!prompting) { return null; } if (connected == null) { try { - promptLock.wait(); + promptLock.wait(timeout); } catch (final InterruptedException e) { LOGGER.log(Level.WARNING, "Inerruption of console thread", e); Thread.currentThread().interrupt(); @@ -245,19 +255,15 @@ public final class ConnectingConsoleInput implements ConsoleInput { } } } - } + } while (System.currentTimeMillis() < end); + return null; } /* (non-Javadoc) * @see fr.bigeon.gclc.manager.ConsoleInput#setPrompt(java.lang.String) */ @Override public void setPrompt(final String prompt) { - this.prompt = new StringProvider() { - @Override - public String apply() { - return prompt; - } - }; + this.prompt = new ConstantString(prompt); } /* (non-Javadoc) diff --git a/gclc-process/src/test/java/net/bigeon/gclc/process/CommandForegroundTest.java b/gclc-process/src/test/java/net/bigeon/gclc/process/CommandForegroundTest.java new file mode 100644 index 0000000..39c0f24 --- /dev/null +++ b/gclc-process/src/test/java/net/bigeon/gclc/process/CommandForegroundTest.java @@ -0,0 +1,90 @@ +/** + * + */ +package net.bigeon.gclc.process; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import org.junit.Test; + +import net.bigeon.gclc.exception.CommandRunException; +import net.bigeon.gclc.exception.CommandRunExceptionType; +import net.bigeon.gclc.process.mocks.ForkTaskMock; + +/** + * @author Emmanuel Bigeon + * + */ +public class CommandForegroundTest { + + /** + * Test method for {@link net.bigeon.gclc.process.CommandForeground#CommandForeground(java.lang.String, net.bigeon.gclc.process.TaskPool)}. + */ + @Test + public void testCommandForeground() { + final CommandForeground pl = new CommandForeground("fg", new TaskPool()); + assertEquals("Command name should be kept as specified", "fg", + pl.getCommandName()); + } + + /** Test method for + * {@link net.bigeon.gclc.process.CommandForeground#doExecute(net.bigeon.gclc.manager.ConsoleOutput, net.bigeon.gclc.manager.ConsoleInput, net.bigeon.gclc.command.CommandParameters)}. + * + * @throws CommandRunException if a valid command failed */ + @Test + public void testDoExecuteConsoleOutputConsoleInputCommandParameters() + throws CommandRunException { + final TaskPool pool = new TaskPool(false); + final CommandForeground pl = new CommandForeground("fg", pool); + + try { + pl.execute(null, null, "-delai", "1"); + fail("PID should be mandatory"); + } catch (final CommandRunException e) { + assertEquals("No pid specified is a usage error", + CommandRunExceptionType.USAGE, e.getType()); + } + try { + pl.execute(null, null, "-pid", "invalid", "-delai", "1"); + fail("PID should be a number"); + } catch (final CommandRunException e) { + assertEquals("Invalid pid specified is a usage error", + CommandRunExceptionType.USAGE, e.getType()); + } + try { + pl.execute(null, null, "-pid", "2", "-delai", "1"); + fail("PID should exist"); + } catch (final CommandRunException e) { + assertEquals("Inexistent pid specified is a run error", + CommandRunExceptionType.EXECUTION, e.getType()); + } + + final ForkTaskMock cmd = new ForkTaskMock(); + final int id = pool.add(cmd); + pl.execute(null, null, "-pid", Integer.toString(id), "-delai", "1"); + + try { + pl.execute(null, null, "-pid", Integer.toString(id), "-delai", "-1"); + fail("delai should be a posistive number"); + } catch (final CommandRunException e) { + assertEquals("Negative delai specified is a usage error", + CommandRunExceptionType.USAGE, e.getType()); + } + + assert cmd.getRunCall() == 0; + } + + /** + * Test method for {@link net.bigeon.gclc.process.CommandForeground#tip()}. + */ + @Test + public void testTip() { + final CommandForeground pl = new CommandForeground("fg", new TaskPool()); + assertNotNull("Command tip should be defined", pl.tip()); + assertNotNull("Command usage should be defined", pl.usagePattern()); + assertNotNull("Command usage should be defined", pl.usageDetail()); + } + +} diff --git a/gclc-process/src/test/java/net/bigeon/gclc/process/CommandForkTest.java b/gclc-process/src/test/java/net/bigeon/gclc/process/CommandForkTest.java new file mode 100644 index 0000000..ecdea66 --- /dev/null +++ b/gclc-process/src/test/java/net/bigeon/gclc/process/CommandForkTest.java @@ -0,0 +1,86 @@ +/** + * + */ +package net.bigeon.gclc.process; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import org.junit.Test; + +import net.bigeon.gclc.command.ICommandProvider; +import net.bigeon.gclc.command.SubedCommand; +import net.bigeon.gclc.exception.CommandRunException; +import net.bigeon.gclc.exception.CommandRunExceptionType; +import net.bigeon.gclc.exception.InvalidCommandName; +import net.bigeon.gclc.process.mocks.CommandMock; + +/** + * @author Emmanuel Bigeon + * + */ +public class CommandForkTest { + + /** + * Test method for {@link net.bigeon.gclc.process.CommandFork#CommandFork(java.lang.String, net.bigeon.gclc.command.ICommandProvider, net.bigeon.gclc.process.TaskPool)}. + */ + @Test + public void testCommandForkStringICommandProviderTaskPool() { + final TaskPool pool = new TaskPool(); + final ICommandProvider provider = new SubedCommand("test"); + final CommandFork pl = new CommandFork("fork", provider, pool); + assertEquals("Command name should be preserved", "fork", pl.getCommandName()); + } + + /** Test method for + * {@link net.bigeon.gclc.process.CommandFork#execute(net.bigeon.gclc.manager.ConsoleOutput, net.bigeon.gclc.manager.ConsoleInput, java.lang.String[])}. + * + * @throws InvalidCommandName if the test init failed + * @throws CommandRunException */ + @Test + public void testExecute() throws InvalidCommandName, CommandRunException { + final TaskPool pool = new TaskPool(false); + final ICommandProvider provider = new SubedCommand("test"); + final CommandFork pl = new CommandFork("fork", provider, pool); + + try { + pl.execute(null, null); + fail("No command specified should through an exception"); + } catch (final CommandRunException e) { + assertEquals("No command specified is a usage error", + CommandRunExceptionType.USAGE, e.getType()); + } + try { + pl.execute(null, null, "invalid"); + fail("Invalid command specified should through an exception"); + } catch (final CommandRunException e) { + assertEquals("Invalid command specified is a usage error", + CommandRunExceptionType.USAGE, e.getType()); + } + + final CommandMock mock = new CommandMock(); + provider.add(mock); + + pl.execute(null, null, mock.getCommandName()); + + final ForkTask task = (ForkTask) pool.get(pool.getPIDs().iterator().next()); + task.join(null, null, 1000); + + assertEquals("Command should be executed when forked", 1, mock.getExecuteCall()); + } + + /** + * Test method for {@link net.bigeon.gclc.process.CommandFork#tip()}. + */ + @Test + public void testTip() { + final TaskPool pool = new TaskPool(); + final ICommandProvider provider = new SubedCommand("test"); + final CommandFork pl = new CommandFork("fork", provider, pool); + assertNotNull("Command tip should be defined", pl.tip()); + assertNotNull("Command usage should be defined", pl.usagePattern()); + assertNotNull("Command usage should be defined", pl.usageDetail()); + } + +} diff --git a/gclc-process/src/test/java/net/bigeon/gclc/process/ForkCommandTaskTest.java b/gclc-process/src/test/java/net/bigeon/gclc/process/ForkCommandTaskTest.java new file mode 100644 index 0000000..1810418 --- /dev/null +++ b/gclc-process/src/test/java/net/bigeon/gclc/process/ForkCommandTaskTest.java @@ -0,0 +1,35 @@ +/** + * + */ +package net.bigeon.gclc.process; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import net.bigeon.gclc.process.mocks.CommandMock; + +/** + * @author Emmanuel Bigeon + * + */ +public class ForkCommandTaskTest { + + /** Test method for + * {@link net.bigeon.gclc.process.ForkCommandTask#ForkCommandTask(net.bigeon.gclc.command.ICommand, java.lang.String[], int)}. + * + * @throws InterruptedException if there is an error in the thread join */ + @Test + public void testForkCommandTask() throws InterruptedException { + final CommandMock cmd = new CommandMock(); + final ForkCommandTask task = new ForkCommandTask(cmd, new String[0], 1); + + final Thread th = new Thread(task); + th.start(); + th.join(); + + assertEquals("Task should be executed once", 1, cmd.getExecuteCall()); + assertEquals("Names should be consistent between task and command", + cmd.getCommandName(), task.getName()); + } +} diff --git a/gclc-process/src/test/java/net/bigeon/gclc/process/ForkTaskTest.java b/gclc-process/src/test/java/net/bigeon/gclc/process/ForkTaskTest.java new file mode 100644 index 0000000..0078348 --- /dev/null +++ b/gclc-process/src/test/java/net/bigeon/gclc/process/ForkTaskTest.java @@ -0,0 +1,62 @@ +/** + * + */ +package net.bigeon.gclc.process; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.io.IOException; + +import org.junit.Test; + +import net.bigeon.gclc.exception.CommandRunException; +import net.bigeon.gclc.exception.CommandRunExceptionType; +import net.bigeon.gclc.utils.PipedConsoleInput; +import net.bigeon.gclc.utils.PipedConsoleOutput; + +/** @author Emmanuel Bigeon */ +public class ForkTaskTest { + + @Test + public void test() throws IOException { + final ForkTask task = new ForkTask(5) { + + @Override + public String getName() { + return "name"; + } + + @Override + protected void doRun() throws CommandRunException { + String msg; + try { + msg = in.prompt(); + } catch (final IOException e) { + throw new CommandRunException(CommandRunExceptionType.INTERACTION, + "Unable to prompt user"); + } + if ("ok".equals(msg)) { + out.println("Message"); + } else { + out.println("fail"); + } + } + }; + + final Thread execThread = new Thread(task); + execThread.start(); + + try (PipedConsoleOutput pco = new PipedConsoleOutput(); + PipedConsoleInput pci = new PipedConsoleInput(null)) { + pci.type("ok"); + while (!pco.available()) { + task.join(pco, pci, 1000); + } + assertEquals("Execution should work", "Message", pco.readNextLine()); + } + assertFalse("Running state should be updated by task on its completion", + task.isRunning()); + } + +} diff --git a/gclc-process/src/test/java/net/bigeon/gclc/process/ProcessClearTest.java b/gclc-process/src/test/java/net/bigeon/gclc/process/ProcessClearTest.java new file mode 100644 index 0000000..c115d9b --- /dev/null +++ b/gclc-process/src/test/java/net/bigeon/gclc/process/ProcessClearTest.java @@ -0,0 +1,53 @@ +/** + * + */ +package net.bigeon.gclc.process; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import net.bigeon.gclc.process.mocks.TaskMock; + +/** + * @author Emmanuel Bigeon + * + */ +public class ProcessClearTest { + + /** + * Test method for {@link net.bigeon.gclc.process.ProcessClear#ProcessClear(java.lang.String, net.bigeon.gclc.process.TaskPool)}. + */ + @Test + public void testProcessClear() { + final ProcessClear pl = new ProcessClear("clear", new TaskPool()); + assertEquals("Command name should be kept as specified", "clear", + pl.getCommandName()); + } + + /** + * Test method for {@link net.bigeon.gclc.process.ProcessClear#execute(net.bigeon.gclc.manager.ConsoleOutput, net.bigeon.gclc.manager.ConsoleInput, java.lang.String[])}. + */ + @Test + public void testExecute() { + final TaskPool pool = new TaskPool(); + final TaskMock task = new TaskMock(); + pool.add(task); + final ProcessClear pl = new ProcessClear("clear", pool); + pl.execute(null, null); + assertTrue("cleared pool should be empty", pool.getPIDs().isEmpty()); + } + + /** + * Test method for {@link net.bigeon.gclc.process.ProcessClear#tip()}. + */ + @Test + public void testTip() { + final ProcessClear pl = new ProcessClear("clear", new TaskPool()); + assertNotNull("Command tip should be defined", pl.tip()); + assertNotNull("Command usage should be defined", pl.usageDetail()); + } + +} diff --git a/gclc-process/src/test/java/net/bigeon/gclc/process/ProcessKillTest.java b/gclc-process/src/test/java/net/bigeon/gclc/process/ProcessKillTest.java new file mode 100644 index 0000000..62ad5a9 --- /dev/null +++ b/gclc-process/src/test/java/net/bigeon/gclc/process/ProcessKillTest.java @@ -0,0 +1,65 @@ +/** + * + */ +package net.bigeon.gclc.process; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import java.io.IOException; + +import org.junit.Test; + +import net.bigeon.gclc.exception.CommandRunException; +import net.bigeon.gclc.exception.CommandRunExceptionType; +import net.bigeon.gclc.process.mocks.TaskMock; + +/** @author Emmanuel Bigeon */ +public class ProcessKillTest { + + /** Test method for + * {@link net.bigeon.gclc.process.ProcessKill#ProcessKill(java.lang.String, net.bigeon.gclc.process.TaskPool)}. */ + @Test + public void testProcessKill() { + final ProcessList pl = new ProcessList("kill", new TaskPool()); + assertEquals("Command name should be kept as specified", "kill", + pl.getCommandName()); + } + + /** Test method for + * {@link net.bigeon.gclc.process.ProcessKill#execute(net.bigeon.gclc.manager.ConsoleOutput, net.bigeon.gclc.manager.ConsoleInput, java.lang.String[])}. + * + * @throws CommandRunException if an error occured in test + * @throws IOException if an error occured with the console output */ + @Test + public void testExecute() throws CommandRunException { + final TaskPool pool = new TaskPool(); + final TaskMock task = new TaskMock(); + final int id = pool.add(task); + final ProcessKill pl = new ProcessKill("kill", pool); + task.reset(); + try { + pl.execute(null, null); + fail("Execution with no pid should raise exception"); + } catch (final CommandRunException e) { + assertEquals("Usage exception expected", CommandRunExceptionType.USAGE, + e.getType()); + } + pl.execute(null, null, Integer.toString(id)); + assertEquals("Task should be shutdown", 1, task.getNumberSetRunning()); + task.reset(); + pl.execute(null, null, Integer.toString(id)); + assertEquals("Task should be shutdown", 1, task.getNumberSetRunning()); + } + + /** Test method for {@link net.bigeon.gclc.process.ProcessKill#tip()}. */ + @Test + public void testTip() { + final ProcessKill pl = new ProcessKill("kill", new TaskPool()); + assertNotNull("Command tip should be defined", pl.tip()); + assertNotNull("Command usage should be defined", pl.usagePattern()); + assertNotNull("Command usage should be defined", pl.usageDetail()); + } + +} diff --git a/gclc-process/src/test/java/net/bigeon/gclc/process/ProcessListTest.java b/gclc-process/src/test/java/net/bigeon/gclc/process/ProcessListTest.java new file mode 100644 index 0000000..7a3cd05 --- /dev/null +++ b/gclc-process/src/test/java/net/bigeon/gclc/process/ProcessListTest.java @@ -0,0 +1,67 @@ +/** + * + */ +package net.bigeon.gclc.process; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; + +import org.junit.Test; + +import net.bigeon.gclc.exception.CommandRunException; +import net.bigeon.gclc.exception.CommandRunExceptionType; +import net.bigeon.gclc.process.mocks.TaskMock; +import net.bigeon.gclc.utils.PipedConsoleOutput; + +/** @author Emmanuel Bigeon */ +public class ProcessListTest { + + /** Test method for + * {@link net.bigeon.gclc.process.ProcessList#ProcessList(java.lang.String, net.bigeon.gclc.process.TaskPool)}. */ + @Test + public void testProcessList() { + final ProcessList pl = new ProcessList("list", new TaskPool()); + assertEquals("Command name should be kept as specified", "list", + pl.getCommandName()); + } + + /** Test method for + * {@link net.bigeon.gclc.process.ProcessList#execute(net.bigeon.gclc.manager.ConsoleOutput, net.bigeon.gclc.manager.ConsoleInput, java.lang.String[])}. + * + * @throws CommandRunException if an error occured + * @throws IOException if an error in initialization of console occured */ + @Test + public void testExecute() throws CommandRunException, IOException { + final TaskPool pool = new TaskPool(); + final TaskMock task = new TaskMock(); + pool.add(task); + final ProcessList pl = new ProcessList("list", pool); + final PipedConsoleOutput out = new PipedConsoleOutput(); + pl.execute(out, null); + + assertTrue("Task should be contained in list", + out.readNextLine().contains(task.getName())); + + out.close(); + + try { + pl.execute(out, null); + fail("Printing element to closed console should raise interaction exception"); + } catch (final CommandRunException e) { + assertEquals("Console printing error should be an interaction error", + CommandRunExceptionType.INTERACTION, e.getType()); + } + } + + /** Test method for {@link net.bigeon.gclc.process.ProcessList#tip()}. */ + @Test + public void testTip() { + final ProcessList pl = new ProcessList("list", new TaskPool()); + assertNotNull("Command tip should be defined", pl.tip()); + assertNotNull("Command usage should be defined", pl.usageDetail()); + } +} diff --git a/gclc-process/src/test/java/net/bigeon/gclc/process/TaskPoolTest.java b/gclc-process/src/test/java/net/bigeon/gclc/process/TaskPoolTest.java new file mode 100644 index 0000000..3acbad4 --- /dev/null +++ b/gclc-process/src/test/java/net/bigeon/gclc/process/TaskPoolTest.java @@ -0,0 +1,77 @@ +package net.bigeon.gclc.process; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import net.bigeon.gclc.process.mocks.TaskMock; + +public class TaskPoolTest { + + public TaskMock task = new TaskMock(); + public TaskPool pool = new TaskPool(false); + + @Test + public void testTaskPool() { + assertFalse("task pool should not auto clear if specified", + pool.isAutoClearing()); + pool = new TaskPool(); + assertTrue("Default task pool should not auto clear", pool.isAutoClearing()); + } + + @Test + public void testAdd() { + final int id = pool.add(task); + assertTrue("Added task pid should be in pool", pool.getPIDs().contains(id)); + assertEquals("Added task should be in pool", task, pool.get(id)); + } + + @Test + public void testGet() { + assertNull("Inexistent task id should provide null as task in pool", + pool.get(60)); + final int id = pool.add(task); + assertEquals("Added task should be in pool at its id", task, pool.get(id)); + assertNull("Added task should be in pool at its id", pool.get(id + 1)); + } + + @Test + public void testGetPIDs() { + assertTrue("new pool should be empty", pool.getPIDs().isEmpty()); + final int id = pool.add(task); + assertNull("Added task should be in pool at its id", pool.get(id + 1)); + assertEquals( + "Pool task addition should change the pid collection size accordingly", 1, + pool.getPIDs().size()); + } + + @Test + public void testRemove() { + assertTrue("new pool should be empty", pool.getPIDs().isEmpty()); + pool.remove(60); + assertTrue("removal of inexistent task should be NOP", pool.getPIDs().isEmpty()); + final int id = pool.add(task); + assertEquals( + "Pool task addition should change the pid collection size accordingly", 1, + pool.getPIDs().size()); + pool.remove(id + 1); + assertEquals("removal of inexistent task should be NOP", 1, + pool.getPIDs().size()); + pool.remove(id); + assertTrue("removal of task should reduce the number of tasks", + pool.getPIDs().isEmpty()); + } + + @Test + public void testShutdown() { + final int id = pool.add(task); + task.reset(); + pool.shutdown(); + assertEquals("Shutdown should request shut down of tasks, only once", 1, + task.getNumberSetRunning()); + } + +} diff --git a/gclc-process/src/test/java/net/bigeon/gclc/process/io/ConnectingConsoleInputTest.java b/gclc-process/src/test/java/net/bigeon/gclc/process/io/ConnectingConsoleInputTest.java new file mode 100644 index 0000000..548a89d --- /dev/null +++ b/gclc-process/src/test/java/net/bigeon/gclc/process/io/ConnectingConsoleInputTest.java @@ -0,0 +1,65 @@ +package net.bigeon.gclc.process.io; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; + +import org.junit.Test; + +import net.bigeon.gclc.tools.ConstantString; +import net.bigeon.gclc.utils.PipedConsoleInput; + +public class ConnectingConsoleInputTest { + + @Test + public void testConnect() throws IOException { + final ConnectingConsoleInput in = new ConnectingConsoleInput(); + final PipedConsoleInput pci = new PipedConsoleInput(null); + assertNull("Inputs should not transmit before connection", in.prompt(1000)); + in.connect(pci); + pci.type("Some message"); + assertEquals("Messages should be buffered", "Some message", in.prompt()); + } + + @Test + public void testDisconnect() throws IOException { + final ConnectingConsoleInput in = new ConnectingConsoleInput(); + final PipedConsoleInput pci = new PipedConsoleInput(null); + assertNull("Inputs should not transmit before connection", in.prompt(1000)); + in.connect(pci); + pci.type("Some message"); + assertEquals("Messages should be buffered", "Some message", in.prompt()); + in.disconnect(); + assertNull("Disconnected inputs should not transmit anymore", in.prompt(1000)); + } + + @Test + public void testIsClosed() throws IOException { + final ConnectingConsoleInput in = new ConnectingConsoleInput(); + assertFalse("New input should not be closed", in.isClosed()); + final PipedConsoleInput pci = new PipedConsoleInput(null); + in.connect(pci); + pci.close(); + assertFalse("Connected input closing should not close connecting", in.isClosed()); + final PipedConsoleInput pci2 = new PipedConsoleInput(null); + in.connect(pci2); + in.close(); + assertTrue("Input should close on request", in.isClosed()); + assertFalse("Connected input should not be closed by connecting closure", + pci2.isClosed()); + } + + @Test + public void testCoveragePrompt() { + final ConnectingConsoleInput in = new ConnectingConsoleInput(); + in.setPrompt("test"); + assertEquals("Prompt should be set correctly", "test", + ((ConstantString) in.getPrompt()).apply()); + final ConstantString prompt = new ConstantString("other"); + in.setPrompt(prompt); + assertEquals("Prompt should be set correctly", prompt, in.getPrompt()); + } +} diff --git a/gclc-process/src/test/java/net/bigeon/gclc/process/io/ConnectingConsoleOutputTest.java b/gclc-process/src/test/java/net/bigeon/gclc/process/io/ConnectingConsoleOutputTest.java new file mode 100644 index 0000000..e919b0c --- /dev/null +++ b/gclc-process/src/test/java/net/bigeon/gclc/process/io/ConnectingConsoleOutputTest.java @@ -0,0 +1,88 @@ +package net.bigeon.gclc.process.io; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; + +import org.junit.Test; + +import net.bigeon.gclc.utils.PipedConsoleOutput; + +public class ConnectingConsoleOutputTest { + + @Test + public void testConnect() throws IOException { + final ConnectingConsoleOutput out = new ConnectingConsoleOutput( + ConnectingConsoleOutput.QUEUE, 10); + final PipedConsoleOutput output = new PipedConsoleOutput(); + out.println("Some message"); + assertFalse("Message should not be transmitted", output.available()); + // Connect + out.connect(output); + + // Connected outputs should receive data + final String res = output.readNextLine(); + assertEquals("Message should be correctly transmitted", "Some message", res); + } + + @Test + public void testDisconnect() throws IOException { + final ConnectingConsoleOutput out = new ConnectingConsoleOutput( + ConnectingConsoleOutput.QUEUE, 10); + final PipedConsoleOutput output = new PipedConsoleOutput(); + // NOP disconnection when no connection + out.disconnect(); + // Connect + out.connect(output); + + // Connected outputs should receive data + out.println("Some message"); + final String res = output.readNextLine(); + assertEquals("Message should be correctly transmitted", "Some message", res); + out.disconnect(); + out.println("Some message"); + assertFalse("Message should not be transmitted", output.available()); + } + + @Test + public void testIsClosed() throws IOException { + final ConnectingConsoleOutput out = new ConnectingConsoleOutput( + ConnectingConsoleOutput.QUEUE, 10); + final PipedConsoleOutput output = new PipedConsoleOutput(); + assertFalse("New output should not be closed", out.isClosed()); + // Connect + out.connect(output); + + output.close(); + + assertFalse("Output should not close when connected outputs do", out.isClosed()); + final PipedConsoleOutput output2 = new PipedConsoleOutput(); + out.connect(output2); + out.close(); + assertTrue("Output should close on request", out.isClosed()); + assertFalse("Output should not close connected output when they do", + output2.isClosed()); + } + + @Test + public void testPrint() throws IOException { + final ConnectingConsoleOutput out = new ConnectingConsoleOutput( + ConnectingConsoleOutput.PERSIST, 10); + + out.print("Some text"); + out.println(); + final PipedConsoleOutput output = new PipedConsoleOutput(); + out.connect(output); + String res = output.readNextLine(); + assertEquals("Message should be correctly transmitted", "Some text", res); + out.disconnect(); + + // Persist should be reprinted on any connect + out.connect(output); + res = output.readNextLine(); + assertEquals("Message should be correctly transmitted", "Some text", res); + out.disconnect(); + } +} diff --git a/gclc-process/src/test/java/net/bigeon/gclc/process/mocks/CommandMock.java b/gclc-process/src/test/java/net/bigeon/gclc/process/mocks/CommandMock.java new file mode 100644 index 0000000..42dbf24 --- /dev/null +++ b/gclc-process/src/test/java/net/bigeon/gclc/process/mocks/CommandMock.java @@ -0,0 +1,60 @@ +/** + * + */ +package net.bigeon.gclc.process.mocks; + +import java.io.IOException; + +import net.bigeon.gclc.command.ICommand; +import net.bigeon.gclc.exception.CommandRunException; +import net.bigeon.gclc.manager.ConsoleInput; +import net.bigeon.gclc.manager.ConsoleOutput; + +/** @author Emmanuel Bigeon */ +public class CommandMock implements ICommand { + + private int executeCall = 0; + private int tipCall = 0; + private int helpCall = 0; + private int commandNameCall = 0; + + /* (non-Javadoc) + * @see net.bigeon.gclc.command.ICommand#execute(net.bigeon.gclc.manager. + * ConsoleOutput, net.bigeon.gclc.manager.ConsoleInput, java.lang.String[]) */ + @Override + public void execute(final ConsoleOutput out, final ConsoleInput in, + final String... args) throws CommandRunException { + executeCall++; + } + + /* (non-Javadoc) + * @see net.bigeon.gclc.command.ICommand#getCommandName() */ + @Override + public String getCommandName() { + commandNameCall++; + return "name"; + } + + /* (non-Javadoc) + * @see + * net.bigeon.gclc.command.ICommand#help(net.bigeon.gclc.manager.ConsoleOutput, + * java.lang.String[]) */ + @Override + public void help(final ConsoleOutput output, final String... args) + throws IOException { + helpCall++; + } + + /* (non-Javadoc) + * @see net.bigeon.gclc.command.ICommand#tip() */ + @Override + public String tip() { + tipCall++; + return "tip"; + } + + /** @return the executeCall */ + public int getExecuteCall() { + return executeCall; + } +} diff --git a/gclc-process/src/test/java/net/bigeon/gclc/process/mocks/ForkTaskMock.java b/gclc-process/src/test/java/net/bigeon/gclc/process/mocks/ForkTaskMock.java new file mode 100644 index 0000000..5124749 --- /dev/null +++ b/gclc-process/src/test/java/net/bigeon/gclc/process/mocks/ForkTaskMock.java @@ -0,0 +1,43 @@ +/** + * + */ +package net.bigeon.gclc.process.mocks; + +import net.bigeon.gclc.exception.CommandRunException; +import net.bigeon.gclc.process.ForkTask; +import net.bigeon.gclc.process.Task; + +/** + * @author Emmanuel Bigeon + * + */ +public class ForkTaskMock extends ForkTask implements Task { + + private int runCall = 0; + + /** @param lines */ + public ForkTaskMock() { + super(10); + } + + /* (non-Javadoc) + * @see net.bigeon.gclc.process.Task#getName() + */ + @Override + public String getName() { + return "name"; + } + + /* (non-Javadoc) + * @see net.bigeon.gclc.process.ForkTask#doRun() + */ + @Override + protected void doRun() throws CommandRunException { + runCall++; + } + + /** @return the runCall */ + public int getRunCall() { + return runCall; + } +} diff --git a/gclc-process/src/test/java/net/bigeon/gclc/process/mocks/TaskMock.java b/gclc-process/src/test/java/net/bigeon/gclc/process/mocks/TaskMock.java new file mode 100644 index 0000000..a11c215 --- /dev/null +++ b/gclc-process/src/test/java/net/bigeon/gclc/process/mocks/TaskMock.java @@ -0,0 +1,57 @@ +package net.bigeon.gclc.process.mocks; + +import net.bigeon.gclc.process.InterruptionListener; +import net.bigeon.gclc.process.Task; + +public class TaskMock implements Task { + + private int runCall = 0; + private int setRunningCall = 0; + + @Override + public void run() { + runCall++; + } + + @Override + public void addInterruptionListener(final InterruptionListener listener) { + // TODO Auto-generated method stub + + } + + @Override + public String getName() { + return "name"; + } + + @Override + public boolean isRunning() { + // TODO Auto-generated method stub + return false; + } + + @Override + public void rmInterruptionListener(final InterruptionListener listener) { + // TODO Auto-generated method stub + + } + + @Override + public void setRunning(final boolean running) { + setRunningCall++; + } + + /** + * + */ + public void reset() { + runCall = 0; + setRunningCall = 0; + } + + /** @return */ + public Object getNumberSetRunning() { + return setRunningCall; + } + +} diff --git a/gclc-process/src/test/java/net/bigeon/gclc/process/mocks/package-info.java b/gclc-process/src/test/java/net/bigeon/gclc/process/mocks/package-info.java new file mode 100644 index 0000000..b114098 --- /dev/null +++ b/gclc-process/src/test/java/net/bigeon/gclc/process/mocks/package-info.java @@ -0,0 +1,4 @@ +/** Mock definitions for testing. + * + * @author Emmanuel Bigeon */ +package net.bigeon.gclc.process.mocks;