diff --git a/tests/util/fr.tpt.ttool.tests.util/.classpath b/tests/util/fr.tpt.ttool.tests.util/.classpath
new file mode 100644
index 0000000000000000000000000000000000000000..ac3a9c3963c0f22b1cdd2a92155880f908436418
--- /dev/null
+++ b/tests/util/fr.tpt.ttool.tests.util/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/tests/util/fr.tpt.ttool.tests.util/.project b/tests/util/fr.tpt.ttool.tests.util/.project
new file mode 100644
index 0000000000000000000000000000000000000000..7a3d164b32e075ae22952325540c1004151363e2
--- /dev/null
+++ b/tests/util/fr.tpt.ttool.tests.util/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>fr.tpt.ttool.tests.util</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/tests/util/fr.tpt.ttool.tests.util/.settings/org.eclipse.jdt.core.prefs b/tests/util/fr.tpt.ttool.tests.util/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..3a21537071bf4118b9e1ee864cb4bc258aa48211
--- /dev/null
+++ b/tests/util/fr.tpt.ttool.tests.util/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,11 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/tests/util/fr.tpt.ttool.tests.util/bin/.gitignore b/tests/util/fr.tpt.ttool.tests.util/bin/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..44fde9027b9415ffbb48dede03b1f60a754a152c
--- /dev/null
+++ b/tests/util/fr.tpt.ttool.tests.util/bin/.gitignore
@@ -0,0 +1 @@
+/fr/
diff --git a/tests/util/fr.tpt.ttool.tests.util/launch/TestRshClient.launch b/tests/util/fr.tpt.ttool.tests.util/launch/TestRshClient.launch
new file mode 100644
index 0000000000000000000000000000000000000000..be994b10555a84a3406900b134ff625d40877d85
--- /dev/null
+++ b/tests/util/fr.tpt.ttool.tests.util/launch/TestRshClient.launch
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/fr.tpt.ttool.tests.util/src/fr/tpt/ttool/tests/util/remote/TestRshClient.java"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="1"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value=""/>
+<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
+<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
+<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
+<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="fr.tpt.ttool.tests.util.remote.TestRshClient"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="fr.tpt.ttool.tests.util"/>
+</launchConfiguration>
diff --git a/tests/util/fr.tpt.ttool.tests.util/resources/helloWorld b/tests/util/fr.tpt.ttool.tests.util/resources/helloWorld
new file mode 100755
index 0000000000000000000000000000000000000000..4a59186b2ba9badf0ac77be4561e72ba237d8917
Binary files /dev/null and b/tests/util/fr.tpt.ttool.tests.util/resources/helloWorld differ
diff --git a/tests/util/fr.tpt.ttool.tests.util/resources/helloWorldNonStop b/tests/util/fr.tpt.ttool.tests.util/resources/helloWorldNonStop
new file mode 100755
index 0000000000000000000000000000000000000000..82cc5e028dbc9d54b72a0f6bc1a8b590d7b11c52
Binary files /dev/null and b/tests/util/fr.tpt.ttool.tests.util/resources/helloWorldNonStop differ
diff --git a/tests/util/fr.tpt.ttool.tests.util/src/fr/tpt/ttool/tests/util/remote/TestRshClient.java b/tests/util/fr.tpt.ttool.tests.util/src/fr/tpt/ttool/tests/util/remote/TestRshClient.java
new file mode 100644
index 0000000000000000000000000000000000000000..77eade0ebe36778aeae0f2076191dbd07c07618f
--- /dev/null
+++ b/tests/util/fr.tpt.ttool.tests.util/src/fr/tpt/ttool/tests/util/remote/TestRshClient.java
@@ -0,0 +1,324 @@
+package fr.tpt.ttool.tests.util.remote;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import launcher.LauncherException;
+import launcher.RshClient;
+import launcher.RshServer;
+import myutil.FileException;
+import myutil.FileUtils;
+
+public class TestRshClient {
+	
+	private static final String EXPECTED_COMMAND_OUTPUT = "!!!Hello World!!!" + System.lineSeparator();
+	private static final String TEST_PROGRAM_NAME = "helloWorld";
+	private static final String TEST_COMMAND = "./resources/" + TEST_PROGRAM_NAME;
+	private static final String TEST_COMMAND_NON_STOP = "./resources/helloWorldNonStop";
+	private static final String TEST_FILE_NAME = "./resources/test.txt";
+	private static final String TEST_FILE_DATA = "testDatafhkenomrcg ,jgh o";
+
+	
+	private RshClient client = null;
+	private static Thread serverThread = null;
+
+	@BeforeClass
+	public static void setUpBeforeClass()
+	throws Exception {
+        RshClient.PORT_NUMBER = 8080;
+        RshServer.PORT_NUMBER = RshClient.PORT_NUMBER;
+
+        final Runnable runnable = new Runnable() {
+			
+			@Override
+			public void run() {
+				new RshServer( null ).startServer();
+			}
+		};
+		
+		serverThread = new Thread( runnable );
+		serverThread.start();
+		Thread.sleep( 500 );
+	}
+
+	@Before
+	public void setUp()
+	throws Exception {
+		client = new RshClient( "localhost" );
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass()
+	throws Exception {
+		serverThread.interrupt();
+	}
+	
+	private void handleException( final Throwable th ) {
+		th.printStackTrace();
+		fail( th.getLocalizedMessage() );
+	}
+
+	@Test
+	public void testStopCommand() {
+        final Runnable runnable = new Runnable() {
+			
+			@Override
+			public void run() {
+				try {
+					client.setCmd( TEST_COMMAND_NON_STOP );
+					client.sendExecuteCommandRequest();
+					final Writer writer = new StringWriter();
+					client.writeCommandMessages( writer );
+				}
+				catch (LauncherException e) {
+					handleException( e );
+				}
+			}
+		};
+		
+		final Thread thread = new Thread( runnable );
+		thread.start();
+		
+		try {
+			
+			// Ensure the remote process has enough time to start
+			Thread.sleep( 200 );
+			client.stopCommand();
+
+			// Ensure the stop command has been processed on the server
+			Thread.sleep( 200 );
+			assertTrue( killUnexistentProcess() );
+		}
+		catch ( LauncherException ex ) {
+			handleException( ex );
+		}
+		catch ( InterruptedException ex ) {
+			handleException( ex );
+		}
+	}
+
+	private boolean killUnexistentProcess() {
+		try {
+			client.sendKillProcessRequest();
+			
+			return false;
+		}
+		catch ( LauncherException ex ) {
+			return RshClient.FAILED.equals( ex.getMessage() );
+		}
+	}
+
+	@Test
+	public void testGetId() {
+		try {
+			final int id = client.getId();
+			assertTrue( id == 1 );
+		}
+		catch ( LauncherException ex ) {
+			handleException( ex );
+		}
+	}
+
+	@Test
+	public void testFreeId() {
+		try {
+			client.freeId( 1 );
+		}
+		catch ( LauncherException ex ) {
+			handleException( ex );
+		}
+	}
+
+	@Test
+	public void testSendExecuteCommandRequest() {
+		client.setCmd( TEST_COMMAND );
+		
+		try {
+			client.sendExecuteCommandRequest();
+			final Writer writer = new StringWriter();
+			client.writeCommandMessages( writer );
+			assertTrue( ( EXPECTED_COMMAND_OUTPUT + System.lineSeparator() ).equals( writer.toString() ) );
+		}
+		catch ( LauncherException ex ) {
+			handleException( ex );
+		}
+	}
+
+	@Test
+	public void testSendExecuteCommandRequestBoolean() {
+		client.setCmd( TEST_COMMAND );
+		
+		try {
+			client.sendExecuteCommandRequest( true );
+			final Writer writer = new StringWriter();
+			client.writeCommandMessages( writer );
+			assertTrue( writer.toString().startsWith( EXPECTED_COMMAND_OUTPUT ) );
+
+			final Integer retCode = client.getProcessReturnCode();
+			assertTrue( retCode != null && retCode == 0  );
+		}
+		catch ( LauncherException ex ) {
+			handleException( ex );
+		}
+	}
+
+	@Test
+	public void testSendExecutePipedCommandsRequest() {
+		final String testFileName = "./resources/test_piped_commands.txt";
+		final String expectedData = "Test Passed!" + System.lineSeparator();
+		
+		try {
+			FileUtils.saveFile( testFileName, expectedData );
+			client.sendExecutePipedCommandsRequest( "echo " + testFileName, "xargs cat" );
+			final String data = client.getDataFromProcess();
+			
+			assertTrue( "Piped commands returned " + data, expectedData.equals( data ) );
+		}
+		catch ( LauncherException ex ) {
+			handleException( ex );
+		}
+		catch ( FileException ex ) {
+			handleException( ex );
+		}
+		finally {
+			new File( testFileName ).delete();
+		}
+	}
+
+	private boolean deleteTestFile() {
+		final File testFile = new File( TEST_FILE_NAME );
+		
+		if ( testFile.exists() ) {
+			assertTrue(  "Test file could not be deleted!", testFile.delete() );
+		}
+		
+		return true;
+	}
+
+	@Test
+	public void testSendFileData() {
+		deleteTestFile();
+		
+		try {
+			client.sendFileData( TEST_FILE_NAME, TEST_FILE_DATA );
+			
+			try {
+				final String readData = FileUtils.loadFile( TEST_FILE_NAME );
+				
+				assertTrue( ( TEST_FILE_DATA + System.lineSeparator() ).equals( readData ) );
+			}
+			catch ( FileException ex ) {
+				handleException( ex );
+			}
+		}
+		catch( LauncherException ex ) {
+			handleException( ex );
+		}
+	}
+
+	@Test
+	public void testGetFileData() {
+		deleteTestFile();
+
+		try {
+			FileUtils.saveFile( TEST_FILE_NAME, TEST_FILE_DATA );
+
+			final String readData = client.getFileData( TEST_FILE_NAME );
+			
+			assertTrue( TEST_FILE_DATA.equals( readData ) );
+		} 
+		catch ( FileException ex ) {
+			handleException( ex );
+		}
+		catch ( LauncherException ex ) {
+			handleException( ex );
+		}
+	}
+
+	@Test
+	public void testDeleteFile() {
+		deleteTestFile();
+
+		try {
+			FileUtils.saveFile( TEST_FILE_NAME, TEST_FILE_DATA );
+
+			client.deleteFile( TEST_FILE_NAME );
+			
+			assertFalse( new File( TEST_FILE_NAME ).exists() );
+		} 
+		catch ( FileException ex ) {
+			handleException( ex );
+		}
+		catch ( LauncherException ex ) {
+			handleException( ex );
+		}
+	}
+
+	@Test
+	public void testSendKillProcessRequest() {
+		client.setCmd( TEST_COMMAND_NON_STOP );
+		
+		try {
+			client.sendExecuteCommandRequest();
+			Thread.sleep( 200 );
+			client.sendKillProcessRequest();
+
+			Thread.sleep( 200 );
+			assertTrue( killUnexistentProcess() );
+		}
+		catch ( LauncherException ex ) {
+			handleException( ex );
+		}
+		catch ( InterruptedException ex ) {
+			handleException( ex );
+		}
+	}
+
+	@Test
+	public void testSendKillAllProcessRequest() {
+		client.setCmd( TEST_COMMAND_NON_STOP );
+		
+		try {
+			for ( int index = 0; index < 4; index++ ) {
+				client.sendExecuteCommandRequest();
+				Thread.sleep( 200 );
+			}
+			
+			client.sendKillAllProcessRequest();
+
+			Thread.sleep( 200 );
+			assertTrue( killUnexistentProcess() );
+		}
+		catch ( LauncherException ex ) {
+			handleException( ex );
+		}
+		catch ( InterruptedException ex ) {
+			handleException( ex );
+		}
+	}
+
+	@Test
+	public void testGetDataFromProcess() {
+		client.setCmd( TEST_COMMAND );
+		
+		try {
+			client.sendExecuteCommandRequest();
+			final String messageFromProcess = client.getDataFromProcess();
+			
+			assertTrue( ( EXPECTED_COMMAND_OUTPUT ).equals( messageFromProcess ) );
+		}
+		catch ( LauncherException ex ) {
+			handleException( ex );
+		}
+	}
+}