changeset 2610:2885a4a290d0

[PATCH] make windows named pipes the default (on windows) This patch makes Windows Named Pipes the default IPC implementation on Windows. It also adds JUnit tests for Windows named pipes IPC. Reviewed-by: sgehwolf Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-March/022315.html
author Simon Tooke <stooke@redhat.com>
date Tue, 07 Mar 2017 09:44:06 -0500
parents 3e0b81d9b291
children bdeeace47db8
files agent/ipc/server/src/main/java/com/redhat/thermostat/agent/ipc/server/internal/IPCConfigurationWriter.java agent/ipc/windows-named-pipes/common/Makefile agent/ipc/windows-named-pipes/common/pom.xml agent/ipc/windows-named-pipes/common/src/main/native/WinPipesNativeHelperStub.c agent/ipc/windows-named-pipes/common/src/test/java/com/redhat/thermostat/agent/ipc/winpipes/common/internal/AsyncMessageReaderTest.java agent/ipc/windows-named-pipes/common/src/test/java/com/redhat/thermostat/agent/ipc/winpipes/common/internal/WinPipeTest.java agent/ipc/windows-named-pipes/server/pom.xml agent/ipc/windows-named-pipes/server/src/main/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/AcceptThread.java agent/ipc/windows-named-pipes/server/src/main/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/ClientPipeInstance.java agent/ipc/windows-named-pipes/server/src/main/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/ReadPipeImpl.java agent/ipc/windows-named-pipes/server/src/main/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/WinPipesServerChannelImpl.java agent/ipc/windows-named-pipes/server/src/main/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/WindowsEventSelector.java agent/ipc/windows-named-pipes/server/src/main/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/WritePipeImpl.java agent/ipc/windows-named-pipes/server/src/test/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/AcceptThreadTest.java agent/ipc/windows-named-pipes/server/src/test/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/ClientHandlerTest.java agent/ipc/windows-named-pipes/server/src/test/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/ClientPipeInstanceTest.java agent/ipc/windows-named-pipes/server/src/test/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/ReadPipeImplTest.java agent/ipc/windows-named-pipes/server/src/test/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/WinPipesServerTransportTest.java agent/ipc/windows-named-pipes/server/src/test/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/WindowsEventSelectorTest.java agent/ipc/windows-named-pipes/server/src/test/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/WritePipeImplTest.java
diffstat 20 files changed, 1420 insertions(+), 144 deletions(-) [+]
line wrap: on
line diff
--- a/agent/ipc/server/src/main/java/com/redhat/thermostat/agent/ipc/server/internal/IPCConfigurationWriter.java	Mon Mar 06 11:11:51 2017 -0500
+++ b/agent/ipc/server/src/main/java/com/redhat/thermostat/agent/ipc/server/internal/IPCConfigurationWriter.java	Tue Mar 07 09:44:06 2017 -0500
@@ -96,7 +96,7 @@
         
         Properties props = helper.createProperties();
 
-        props.setProperty(PROP_IPC_TYPE, OS.IS_UNIX ? IPCType.UNIX_SOCKET.getConfigValue() : IPCType.TCP_SOCKET.getConfigValue());
+        props.setProperty(PROP_IPC_TYPE, OS.IS_UNIX ? IPCType.UNIX_SOCKET.getConfigValue() : IPCType.WINDOWS_NAMED_PIPES.getConfigValue());
 
         // unix socket will work without configuration (creates sockets in tmp directory
         // but tcpsocket always needs ports predefined (in the future, should support service discovery)
--- a/agent/ipc/windows-named-pipes/common/Makefile	Mon Mar 06 11:11:51 2017 -0500
+++ b/agent/ipc/windows-named-pipes/common/Makefile	Tue Mar 07 09:44:06 2017 -0500
@@ -12,7 +12,12 @@
 
 INCLUDE    = -I $(TARGET_DIR) -I "$(JAVA_HOME)/include/" -I "$(JAVA_HOME)/include/$(JNI_PLATFORM)"
 
+ifeq ($(JNI_PLATFORM),win32)
 WINHELPER_SOURCES    = src/main/native/WinPipesNativeHelper.c
+else
+WINHELPER_SOURCES    = src/main/native/WinPipesNativeHelperStub.c
+endif
+
 WINHELPER_TARGET     = $(TARGET_DIR)/WinPipesNativeHelper.c
 WINHELPER_OBJECTS    = $(WINHELPER_TARGET:.c=.o)
 WINHELPER_EXECUTABLE = $(SO_PREFIX)WinPipesNativeWrapper$(SO_SUFFIX)
--- a/agent/ipc/windows-named-pipes/common/pom.xml	Mon Mar 06 11:11:51 2017 -0500
+++ b/agent/ipc/windows-named-pipes/common/pom.xml	Tue Mar 07 09:44:06 2017 -0500
@@ -78,35 +78,6 @@
         <sharedlib.prefix/>
         <sharedlib.suffix>.dll</sharedlib.suffix>
       </properties>
-      <build>
-        <plugins>
-
-          <plugin>
-            <groupId>org.codehaus.mojo</groupId>
-            <artifactId>exec-maven-plugin</artifactId>
-            <executions>
-              <execution>
-                <phase>compile</phase>
-                <goals>
-                  <goal>exec</goal>
-                </goals>
-              </execution>
-            </executions>
-            <configuration>
-              <executable>make</executable>
-              <arguments>
-                <argument>all</argument>
-                <argument>CC=${c.compiler}</argument>
-                <argument>SO_PREFIX=${sharedlib.prefix}</argument>
-                <argument>SO_SUFFIX=${sharedlib.suffix}</argument>
-                <argument>EXTRA_CFLAGS=${cflags}</argument>
-                <argument>JNI_PLATFORM=${jni.platform}</argument>
-                <argument>PLATFORM_LIBS=${platform.libs}</argument>
-              </arguments>
-            </configuration>
-          </plugin>
-        </plugins>
-      </build>
     </profile>
   </profiles>
 
@@ -153,12 +124,43 @@
           </instructions>
         </configuration>
       </plugin>
+
+
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>exec-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <phase>compile</phase>
+            <goals>
+              <goal>exec</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <executable>make</executable>
+          <arguments>
+            <argument>all</argument>
+            <argument>CC=${c.compiler}</argument>
+            <argument>SO_PREFIX=${sharedlib.prefix}</argument>
+            <argument>SO_SUFFIX=${sharedlib.suffix}</argument>
+            <argument>EXTRA_CFLAGS=${cflags}</argument>
+            <argument>JNI_PLATFORM=${jni.platform}</argument>
+            <argument>PLATFORM_LIBS=${platform.libs}</argument>
+          </arguments>
+        </configuration>
+      </plugin>
+
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-surefire-plugin</artifactId>
         <!-- We don't want to depend on Thermostat annotations, 
              so we override parent pom's configuration -->
-        <configuration combine.self="override" />
+        <configuration combine.self="override">
+            <systemPropertyVariables>
+              <com.redhat.thermostat.shared.loader.testNativesHome>${project.build.directory}</com.redhat.thermostat.shared.loader.testNativesHome>
+            </systemPropertyVariables>
+        </configuration>
       </plugin>
       <!-- This is a dependency of the Byteman helper and that needs source
            level JDK 6 in order to be able to use the helper for JVMs running
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/ipc/windows-named-pipes/common/src/main/native/WinPipesNativeHelperStub.c	Tue Mar 07 09:44:06 2017 -0500
@@ -0,0 +1,380 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+#include "com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper.h"
+
+#if !defined(TRUE)
+# define TRUE 1
+#endif
+
+#define STUB_RETURN 0
+
+// the error values aren't important; they just have to be unique for the unit tests
+#define STUB_ERROR_CODE 100
+#define STUB_ERROR_CODE_BROKEN_PIPE 101
+#define STUB_ERROR_CODE_EOF 102
+#define STUB_ERROR_CODE_INCOMPLETE 103
+#define STUB_ERROR_CODE_MORE_DATA 104
+#define STUB_ERROR_CODE_PENDING 105
+#define STUB_ERROR_CODE_PIPE_BUSY 106
+#define STUB_ERROR_CODE_PIPE_CONNECTED 107
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    createNamedPipe0
+ * Signature: (Ljava/lang/String;II)J
+ */
+JNIEXPORT jlong JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_createNamedPipe0
+  (JNIEnv *env, jobject obj, jstring pipeName, jint instances, jint bufsize)
+{
+    return STUB_RETURN;
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    openNamedPipe0
+ * Signature: (Ljava/lang/String;)J
+ */
+JNIEXPORT jlong JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_openExistingNamedPipe0
+  (JNIEnv *env, jobject obj, jstring pipeName)
+{
+    return STUB_RETURN;
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    createEvent0
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_createEvent0
+  (JNIEnv *env, jobject obj, jboolean manual, jboolean initial)
+{
+    return STUB_RETURN;
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    resetEvent0
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_resetEvent0
+  (JNIEnv *env, jobject obj, jlong eventHandle)
+{
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    setEvent0
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_setEvent0
+  (JNIEnv *env, jobject obj, jlong eventHandle)
+{
+}
+
+JNIEXPORT jint JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_getLastError0
+  (JNIEnv *env, jobject obj)
+{
+    return STUB_RETURN;
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    connectNamedPipe0
+ * Signature: (JJ[D)Ljava/lang/String;
+ *
+ * return 0 if sucessfull, ERROR_IO_PENDING if still pending, or GetLastError() if failure
+ */
+JNIEXPORT jint JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_connectNamedPipe0
+  (JNIEnv *env, jobject obj, jlong pipeHandle, jobject ooverlapped)
+{
+    return STUB_RETURN;
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    disconnectnamedPipe0
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_disconnectNamedPipe0
+  (JNIEnv *env, jobject obj, jlong pipeHandle)
+{
+    return STUB_RETURN;
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    getNamedPipeClientProcessId0
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_getNamedPipeClientProcessId0
+  (JNIEnv *env, jobject obj, jlong pipeHandle)
+{
+    return STUB_RETURN;
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    closeHandle0
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_closeHandle0
+  (JNIEnv *env, jobject obj, jlong handle)
+{
+    return STUB_RETURN;
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    waitForMultipleObjects0
+ * Signature: (I[JZI)I
+ */
+JNIEXPORT jint JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_waitForMultipleObjects0
+  (JNIEnv *env, jobject obj, jint numObjects, jlongArray handles, jboolean waitForAll, jint millis)
+{
+    return STUB_RETURN;
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    getOverlappedResult0
+ * Signature: (JJ[DZ)J
+ */
+JNIEXPORT jint JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_getOverlappedResult0
+  (JNIEnv *env, jobject obj, jlong pipeHandle, jobject ooverlapped, jboolean wait)
+{
+    return STUB_RETURN;
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    readFileOverlapped0
+ * Signature: (JJ[D[BJ)J
+ */
+JNIEXPORT jboolean JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_readFileOverlapped0
+  (JNIEnv *env, jobject obj, jlong handle, jobject ooverlapped, jobject buffer, jint offset, jint bufsize)
+{
+    return STUB_RETURN;
+}
+
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    readFile0
+ * Signature: (J[BJ)J
+ */
+JNIEXPORT jint JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_readFile0
+  (JNIEnv *env, jobject obj, jlong handle, jbyteArray array, jint offset, jint bufsize)
+{
+    return STUB_RETURN;
+}
+
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    writeFileOverlapped0
+ * Signature: (JJ[D[BJ)J
+ */
+JNIEXPORT jboolean JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_writeFileOverlapped0
+  (JNIEnv *env, jobject obj, jlong handle, jobject ooverlapped, jobject buffer, jint offset, jint bufsize)
+{
+    return STUB_RETURN;
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    writeFile0
+ * Signature: (J[BJ)J
+ */
+JNIEXPORT jint JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_writeFile0
+  (JNIEnv *env, jobject obj, jlong handle, jobject array, jint offset, jint bufsize)
+{
+    return STUB_RETURN;
+}
+
+JNIEXPORT jlong JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_getConstantWaitObject0
+  (JNIEnv *env, jobject obj)
+{
+    return STUB_RETURN;
+}
+
+JNIEXPORT jlong JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_getConstantInfinite0
+  (JNIEnv *env, jobject obj)
+{
+    return STUB_RETURN;
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    getConstantErrorIOPending0
+ * Signature: ()J
+ */
+JNIEXPORT jint JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_getConstantErrorIOPending0
+  (JNIEnv *env, jobject obj)
+{
+    return STUB_ERROR_CODE_PENDING;
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    getConstantErrorIOIncomplete0
+ * Signature: ()J
+ */
+JNIEXPORT jint JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_getConstantErrorIOIncomplete0
+  (JNIEnv *env, jobject obj)
+{
+    return STUB_ERROR_CODE_INCOMPLETE;
+}
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    getConstantErrorHandleEOF0
+ * Signature: ()J
+ */
+JNIEXPORT jint JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_getConstantErrorHandleEOF0
+  (JNIEnv *env, jobject obj)
+{
+    return STUB_ERROR_CODE_EOF;
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    getConstantErrorMoreData0
+ * Signature: ()J
+ */
+JNIEXPORT jint JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_getConstantErrorMoreData0
+  (JNIEnv *env, jobject obj)
+{
+    return STUB_ERROR_CODE_MORE_DATA;
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    getConstantErrorPipeBusy0
+ * Signature: ()J
+ */
+JNIEXPORT jint JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_getConstantErrorPipeBusy0
+  (JNIEnv *env, jobject obj)
+{
+    return STUB_ERROR_CODE_PIPE_BUSY;
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    getConstantErrorPipeConnected0
+ * Signature: ()J
+ */
+JNIEXPORT jint JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_getConstantErrorPipeConnected0
+  (JNIEnv *env, jobject obj)
+{
+    return STUB_ERROR_CODE_PIPE_CONNECTED;
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    getConstantInvalidHandle0
+ * Signature: ()J
+ */
+JNIEXPORT jint JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_getConstantInvalidHandle0
+  (JNIEnv *env, jobject obj)
+{
+    return STUB_RETURN;
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    getConstantErrorBrokenPipe0
+ * Signature: ()J
+ */
+JNIEXPORT jint JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_getConstantErrorBrokenPipe0
+  (JNIEnv *env, jobject obj)
+{
+    return STUB_ERROR_CODE_BROKEN_PIPE;
+}
+
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    createDirectBuffer0
+ * Signature: (I)Ljava/nio/ByteBuffer;
+ */
+JNIEXPORT jobject JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_createDirectBuffer0
+  (JNIEnv *env, jobject obj, jint bufsize)
+{
+    return NULL;
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    createDirectOverlapStruct0
+ * Signature: ()Ljava/nio/ByteBuffer;
+ */
+JNIEXPORT jobject JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_createDirectOverlapStruct0
+  (JNIEnv *env, jobject obj, jlong eHandle)
+{
+    return NULL;
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    freeDirectBuffer0
+ * Signature: (Ljava/nio/ByteBuffer;)V
+ */
+JNIEXPORT void JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_freeDirectBuffer0
+  (JNIEnv *env, jobject obj, jobject bytebuffer)
+{
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    cancelIo0
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_cancelIo0
+  (JNIEnv *env, jobject obj, jlong pipeHandle)
+{
+    return STUB_RETURN;
+}
+
+/*
+ * Class:     com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper
+ * Method:    cancelIoEx0
+ * Signature: (JLjava/nio/ByteBuffer;)Z
+ */
+JNIEXPORT jboolean JNICALL Java_com_redhat_thermostat_agent_ipc_winpipes_common_internal_WinPipesNativeHelper_cancelIoEx0
+  (JNIEnv *env, jobject obj, jlong pipeHandle, jobject ooverlapped)
+{
+    return STUB_RETURN;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/ipc/windows-named-pipes/common/src/test/java/com/redhat/thermostat/agent/ipc/winpipes/common/internal/AsyncMessageReaderTest.java	Tue Mar 07 09:44:06 2017 -0500
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.ipc.winpipes.common.internal;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+public class AsyncMessageReaderTest {
+
+    private MessageListener messageListener;
+    private MessageLimits messageLimits;
+
+    @Before
+    public void setup() {
+        messageListener = mock(MessageListener.class);
+        messageLimits = mock(MessageLimits.class);
+    }
+
+    @Test
+    public void canConstruct() {
+        new AsyncMessageReader(messageListener);
+        new AsyncMessageReader(messageListener, messageLimits);
+    }
+
+    @Test
+    public void callsListener() {
+        final AsyncMessageReader rdr = new AsyncMessageReader(messageListener);
+        rdr.readFullMessage(ByteBuffer.wrap(new byte[]{0,1,2}));
+        verify(messageListener).messageRead(any(ByteBuffer.class));
+    }
+
+    @Test
+    public void testReadDataSingle() throws Exception {
+        final byte[] messageBytes = "Hello".getBytes(Charset.forName("UTF-8"));
+        final byte[] headerBytes = ChannelTestUtils.createHeader(messageBytes.length, false);
+
+        final AsyncMessageReader rdr = new AsyncMessageReader(messageListener);
+
+        rdr.process(ByteBuffer.wrap(headerBytes));
+        verify(messageListener, never()).messageRead(any(ByteBuffer.class));
+
+        rdr.process(ByteBuffer.wrap(messageBytes));
+        final ByteBuffer expected = ByteBuffer.wrap(messageBytes);
+        verify(messageListener).messageRead(expected);
+    }
+
+    @Test
+    public void testReadDataSingleJoined() throws Exception {
+        final byte[] messageBytes = "Hello".getBytes(Charset.forName("UTF-8"));
+        final byte[] headerBytes = ChannelTestUtils.createHeader(messageBytes.length, false);
+
+        final AsyncMessageReader rdr = new AsyncMessageReader(messageListener);
+
+        final ByteBuffer allBytes = ByteBuffer.allocate(headerBytes.length + messageBytes.length);
+        allBytes.put(headerBytes).put(messageBytes);
+        allBytes.flip();
+        rdr.process(allBytes);
+        final ByteBuffer expected = ByteBuffer.wrap(messageBytes);
+        verify(messageListener).messageRead(expected);
+    }
+
+    @Test
+    public void testReadDataMulti() throws Exception {
+        final byte[] fullMessageBytes = "Hello World.".getBytes(Charset.forName("UTF-8"));
+        final byte[] message1Bytes = Arrays.copyOfRange(fullMessageBytes, 0, 5);
+        final byte[] header1Bytes = ChannelTestUtils.createHeader(message1Bytes.length, true);
+        final byte[] message2Bytes = Arrays.copyOfRange(fullMessageBytes, 5, fullMessageBytes.length);
+        final byte[] header2Bytes = ChannelTestUtils.createHeader(message2Bytes.length, false);
+
+        // First first header, then all remaining data
+        final byte[] joined = ChannelTestUtils.joinByteArrays(message1Bytes, header2Bytes, message2Bytes);
+
+        final AsyncMessageReader rdr = new AsyncMessageReader(messageListener);
+
+        rdr.process(ByteBuffer.wrap(header1Bytes));
+        verify(messageListener, never()).messageRead(any(ByteBuffer.class));
+        rdr.process(ByteBuffer.wrap(joined));
+        ByteBuffer expected = ByteBuffer.wrap(fullMessageBytes);
+        verify(messageListener).messageRead(expected);
+    }
+    /**/
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/ipc/windows-named-pipes/common/src/test/java/com/redhat/thermostat/agent/ipc/winpipes/common/internal/WinPipeTest.java	Tue Mar 07 09:44:06 2017 -0500
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.ipc.winpipes.common.internal;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+
+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 static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class WinPipeTest {
+
+    private WinPipesNativeHelper mockHelper;
+    private final String PIPE_NAME = "pipeNameFoo";
+    private final WinPipe.Id PIPE_ID = new WinPipe.Id(PIPE_NAME);
+
+    @Before
+    public void setup() {
+        mockHelper = mock(WinPipesNativeHelper.class);
+        when(mockHelper.openNamedPipe(anyString())).thenReturn(WinPipesNativeHelper.INVALID_HANDLE);
+        when(mockHelper.openNamedPipe(eq(PIPE_NAME))).thenReturn(999L);
+        when(mockHelper.createNamedPipe(anyString(),anyInt(),anyInt())).thenReturn(WinPipesNativeHelper.INVALID_HANDLE);
+        when(mockHelper.createNamedPipe(eq(PIPE_NAME),anyInt(),anyInt())).thenReturn(999L);
+    }
+
+    @Test
+    public void canConstruct() {
+        final WinPipe p1 = new WinPipe(mockHelper, PIPE_NAME);
+        assertFalse(p1.isOpen());
+        new WinPipe(mockHelper, PIPE_ID);
+    }
+
+    @Test
+    public void testBadOpen() {
+        try {
+            final long badHandle = new WinPipe(mockHelper, "BADNAME").open();
+            fail("Expected IOException");
+        } catch (IOException ignored) {
+        }
+    }
+
+    @Test
+    public void testGoodOpen() throws IOException {
+        new WinPipe(mockHelper, PIPE_NAME).open();
+        verify(mockHelper).openNamedPipe(eq(PIPE_NAME));
+    }
+
+    @Test
+    public void testCreatePipe() {
+        final WinPipe p1 = new WinPipe(mockHelper, PIPE_NAME);
+        assertFalse(p1.isOpen());
+        final long goodHandle = p1.createWindowsNamedPipe(5, 7);
+        assertEquals(999L, goodHandle);
+        verify(mockHelper).createNamedPipe(eq(PIPE_NAME), eq(5), eq(7));
+    }
+
+    @Test
+    public void testClose() throws IOException {
+        final WinPipe p1 = new WinPipe(mockHelper, PIPE_NAME);
+        assertFalse(p1.isOpen());
+        long handle = p1.open();
+        assertTrue(p1.isOpen());
+        p1.close();
+        assertFalse(p1.isOpen());
+        verify(mockHelper).closeHandle(eq(handle));
+    }
+
+    @Test
+    public void testRead() {
+        final WinPipe p1 = new WinPipe(mockHelper, PIPE_NAME);
+        final byte[] messageBytes = "Hello".getBytes(Charset.forName("UTF-8"));
+        final ByteBuffer readBuff = ByteBuffer.allocate(messageBytes.length);
+        when(mockHelper.readFile(anyInt(), any(ByteBuffer.class))).thenReturn(messageBytes.length);
+        final int n1 = p1.read(readBuff);
+        assertEquals(messageBytes.length, n1);
+    }
+
+    @Test
+    public void testWrite() {
+        final WinPipe p1 = new WinPipe(mockHelper, PIPE_NAME);
+        final byte[] messageBytes = "Hello".getBytes(Charset.forName("UTF-8"));
+        final ByteBuffer writeBuff = ByteBuffer.wrap(messageBytes);
+        when(mockHelper.writeFile(anyInt(), any(ByteBuffer.class))).thenReturn(messageBytes.length);
+        final int n1 = p1.write(writeBuff);
+        assertEquals(messageBytes.length, n1);
+    }
+
+}
--- a/agent/ipc/windows-named-pipes/server/pom.xml	Mon Mar 06 11:11:51 2017 -0500
+++ b/agent/ipc/windows-named-pipes/server/pom.xml	Tue Mar 07 09:44:06 2017 -0500
@@ -112,6 +112,16 @@
           </instructions>
         </configuration>
       </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <systemPropertyVariables>
+            <com.redhat.thermostat.shared.loader.testNativesHome>${project.basedir}/../common/target</com.redhat.thermostat.shared.loader.testNativesHome>
+          </systemPropertyVariables>
+        </configuration>
+      </plugin>
     </plugins>
   </build>
 
--- a/agent/ipc/windows-named-pipes/server/src/main/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/AcceptThread.java	Mon Mar 06 11:11:51 2017 -0500
+++ b/agent/ipc/windows-named-pipes/server/src/main/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/AcceptThread.java	Tue Mar 07 09:44:06 2017 -0500
@@ -41,6 +41,7 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import com.redhat.thermostat.agent.ipc.server.ThermostatIPCCallbacks;
 import com.redhat.thermostat.agent.ipc.winpipes.common.internal.MessageLimits;
 import com.redhat.thermostat.agent.ipc.winpipes.common.internal.WinPipe;
 import com.redhat.thermostat.common.utils.LoggingUtils;
@@ -55,8 +56,10 @@
     // buffer size
     private static final int BUFSIZE = new MessageLimits().getBufferSize();
 
+    private final TestHelper testHelper;
+
     // number of simulataneous clients
-    private final int NUM_INSTANCES = 1;
+    private final int numInstances;
 
     private final WindowsEventSelector selector;
 
@@ -65,19 +68,26 @@
     
     private boolean shutdown;
 
-    AcceptThread(WinPipesServerChannelImpl channel, ExecutorService execService) {
+    AcceptThread(WinPipesServerChannelImpl channel, ExecutorService execService, int numInstances) {
+        this(channel, execService, numInstances, channel.getPipe(), new WindowsEventSelector(numInstances), new TestHelper());
+    }
+
+    // for testing
+    AcceptThread(WinPipesServerChannelImpl channel, ExecutorService execService, int numInstances, WinPipe pipe, WindowsEventSelector selector, TestHelper hlp) {
         this.channel = channel;
         this.execService = execService;
-        this.pipe = channel.getPipe();
+        this.pipe = pipe;
         this.shutdown = false;
-        this.selector = new WindowsEventSelector(NUM_INSTANCES);
-        this.instances = new ClientPipeInstance[NUM_INSTANCES];
+        this.selector = selector;
+        this.numInstances = numInstances;
+        this.instances = new ClientPipeInstance[numInstances];
+        this.testHelper = hlp;
     }
 
-    private void createInstances() throws IOException {
-        logger.info("AcceptThread '" + pipe.getPipeName() + "' creating " + NUM_INSTANCES + " pipe instances");
-        for (int i = 0; i < NUM_INSTANCES && !shutdown; i++) {
-            final ClientPipeInstance pi = new ClientPipeInstance(pipe.getPipeName(), NUM_INSTANCES, BUFSIZE, execService, channel.getCallbacks());
+    void createInstances() throws IOException {
+        logger.info("AcceptThread '" + pipe.getPipeName() + "' creating " + numInstances + " pipe instances");
+        for (int i = 0; i < numInstances && !shutdown; i++) {
+            final ClientPipeInstance pi = testHelper.createPipeInstance(pipe.getPipeName(), numInstances, BUFSIZE, execService, channel.getCallbacks());
             instances[i] = pi;
             pi.connectToNewClient();
             logger.fine("AcceptThread '" + pipe.getPipeName() + "' created " + pi);
@@ -139,4 +149,9 @@
         return shutdown;
     }
 
+    static class TestHelper {
+        ClientPipeInstance createPipeInstance(final String name, int instances, int bufsize, ExecutorService execService, ThermostatIPCCallbacks cb) throws IOException {
+            return new ClientPipeInstance(name, instances, bufsize, execService, cb);
+        }
+    }
 }
\ No newline at end of file
--- a/agent/ipc/windows-named-pipes/server/src/main/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/ClientPipeInstance.java	Mon Mar 06 11:11:51 2017 -0500
+++ b/agent/ipc/windows-named-pipes/server/src/main/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/ClientPipeInstance.java	Tue Mar 07 09:44:06 2017 -0500
@@ -43,7 +43,6 @@
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.WritableByteChannel;
-import java.util.ArrayList;
 import java.util.concurrent.ExecutorService;
 import java.util.logging.Logger;
 
@@ -91,8 +90,8 @@
             throw new IOException("can't create Windows named pipe " + name + " err=" + clientHandlerCreator.getLastError());
         }
 
-        this.readHandler = new ReadPipeImpl(this, name, pipeHandle, bufsize);
-        this.writeHandler = new WritePipeImpl(this, name, pipeHandle, bufsize);
+        this.readHandler = cpiHelper.createPipeReader(this, name, pipeHandle, bufsize);
+        this.writeHandler = cpiHelper.createPipeWriter(this, name, pipeHandle, bufsize);
     }
 
     public String toString() {
@@ -188,5 +187,14 @@
         int getLastError() {
             return helper.getLastError();
         }
+
+        ReadPipeImpl createPipeReader(ClientPipeInstance obj, String name, long pipeHandle, int bufsize) throws IOException {
+            return new ReadPipeImpl(obj, name, pipeHandle, bufsize);
+        }
+
+        WritePipeImpl createPipeWriter(ClientPipeInstance obj, String name, long pipeHandle, int bufsize) throws IOException {
+            return new WritePipeImpl(obj, name, pipeHandle, bufsize);
+        }
+
     }
 }
--- a/agent/ipc/windows-named-pipes/server/src/main/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/ReadPipeImpl.java	Mon Mar 06 11:11:51 2017 -0500
+++ b/agent/ipc/windows-named-pipes/server/src/main/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/ReadPipeImpl.java	Tue Mar 07 09:44:06 2017 -0500
@@ -49,7 +49,7 @@
 class ReadPipeImpl implements WindowsEventSelector.EventHandler {
 
     private static final Logger logger = LoggingUtils.getLogger(ReadPipeImpl.class);
-    private static WinPipesNativeHelper helper = WinPipesNativeHelper.INSTANCE;
+    private final WinPipesNativeHelper helper;
 
     enum ReadPipeState { UNKNOWN_STATE, CONNECTING_STATE, READING_STATE, ERROR_STATE, CLOSED_STATE }
 
@@ -68,6 +68,21 @@
         this.pipeName = pipeName;
         this.readState = ReadPipeState.UNKNOWN_STATE;
         this.pipeHandle = pipeHandle;
+        this.helper = WinPipesNativeHelper.INSTANCE;
+        this.readEventHandle = helper.createEvent();
+        if (this.readEventHandle == WinPipesNativeHelper.INVALID_HANDLE) {
+            throw new IOException("can't create a Windows event" + " err=" + helper.getLastError());
+        }
+        this.readOverlap = helper.createDirectOverlapStruct(readEventHandle);
+        this.readBuffer = helper.createDirectBuffer(bufsize);
+    }
+
+    ReadPipeImpl(PipeManager manager, String pipeName, long pipeHandle, int bufsize, WinPipesNativeHelper helper) throws IOException {
+        this.manager = manager;
+        this.pipeName = pipeName;
+        this.readState = ReadPipeState.UNKNOWN_STATE;
+        this.pipeHandle = pipeHandle;
+        this.helper = helper;
         this.readEventHandle = helper.createEvent();
         if (this.readEventHandle == WinPipesNativeHelper.INVALID_HANDLE) {
             throw new IOException("can't create a Windows event" + " err=" + helper.getLastError());
@@ -110,21 +125,21 @@
      */
     boolean connectToNewClient() throws IOException {
 
-        logger.info("connectToNewClient - entered " + this);
+        logger.finest("connectToNewClient - entered " + this);
         final int ret = helper.connectNamedPipe(pipeHandle, readOverlap);
-        logger.info("connectToNewClient on " + this + " returns " + ret);
+        logger.finest("connectToNewClient on " + this + " returns " + ret);
         if (ret == WinPipesNativeHelper.ERROR_IO_PENDING) {
             readState = ReadPipeState.CONNECTING_STATE;
         } else if (ret == WinPipesNativeHelper.ERROR_SUCCESS || ret == WinPipesNativeHelper.ERROR_PIPE_CONNECTED) {
             // if it's not pending, and no exception was thrown, then we must be connected
-            logger.info("connectToNewClient switching to READING_STATE");
+            logger.finest("connectToNewClient switching to READING_STATE");
             helper.resetEvent(readEventHandle);
             clientHandler = manager.handleNewClientConnection();
             readState = ReadPipeState.READING_STATE;
         } else {
             throw new IOException("connectNamedPipe(" + pipeName + ") returns err=" + ret);
         }
-        logger.info("connectToNewClient - exitting " + this);
+        logger.finest("connectToNewClient - exitting " + this);
         return readState == ReadPipeState.CONNECTING_STATE;
     }
 
@@ -134,7 +149,7 @@
      * - if there's more data expected for the current message, then enqueue a read.
      * @throws IOException if there's an i/o or protocol error
      */
-    private void enqueueRead() throws IOException {
+    void enqueueRead() throws IOException {
         logger.finest("enqueueRead() - entered " + this);
         readBuffer.position(0);
         readBuffer.limit(readBuffer.capacity());
@@ -161,7 +176,7 @@
      * @return true if queueNextOperation() should be called, false otherwise
      * @throws IOException if there were any errors interacting with the pipe
      */
-    private boolean handlePendingRead() throws IOException {
+    boolean handlePendingRead() throws IOException {
         logger.finest("handlePendingRead() - entered " + this);
         if (readState == ReadPipeState.READING_STATE) {
             logger.finest("handlePendingRead() waiting for overlapped result on " + this + " state=" + readState);
--- a/agent/ipc/windows-named-pipes/server/src/main/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/WinPipesServerChannelImpl.java	Mon Mar 06 11:11:51 2017 -0500
+++ b/agent/ipc/windows-named-pipes/server/src/main/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/WinPipesServerChannelImpl.java	Tue Mar 07 09:44:06 2017 -0500
@@ -75,7 +75,7 @@
         this.callbacks = callbacks;
         this.channelHelper = helper;
         ExecutorService execService = Executors.newFixedThreadPool(determineDefaultThreadPoolSize(), new CountingThreadFactory());
-        AcceptThread acceptThread = threadCreator.createAcceptThread(this, execService);
+        AcceptThread acceptThread = threadCreator.createAcceptThread(this, execService, 1);
         acceptThread.start();
     }
 
@@ -113,8 +113,8 @@
 
     /* For testing purposes */
     static class ThreadCreator {
-        AcceptThread createAcceptThread(WinPipesServerChannelImpl channel, ExecutorService execService) {
-            return new AcceptThread(channel, execService);
+        AcceptThread createAcceptThread(WinPipesServerChannelImpl channel, ExecutorService execService, int numInstances) {
+            return new AcceptThread(channel, execService, numInstances);
         }
     }
 
--- a/agent/ipc/windows-named-pipes/server/src/main/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/WindowsEventSelector.java	Mon Mar 06 11:11:51 2017 -0500
+++ b/agent/ipc/windows-named-pipes/server/src/main/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/WindowsEventSelector.java	Tue Mar 07 09:44:06 2017 -0500
@@ -57,7 +57,7 @@
 
     private static final Logger logger = LoggingUtils.getLogger(WindowsEventSelector.class);
 
-    private static final WinPipesNativeHelper helper = WinPipesNativeHelper.INSTANCE;
+    private WinPipesNativeHelper helper;
 
     private final Set<EventHandler> eventHandlers;
 
@@ -67,7 +67,13 @@
     private long[] eventHandles = null;
 
     WindowsEventSelector(int maxInstances) {
-        eventHandlers = new HashSet<>(maxInstances);
+        this(maxInstances, new HashSet<EventHandler>(maxInstances), WinPipesNativeHelper.INSTANCE);
+    }
+
+    WindowsEventSelector(int maxInstances, Set<EventHandler> ehSet, WinPipesNativeHelper helper) {
+        eventHandlers = ehSet;
+        this.helper = helper;
+        fixArray();
     }
 
     void add(EventHandler e) {
@@ -101,14 +107,18 @@
      */
     EventHandler waitForEvent() throws IOException {
         logger.finest("WinPipe waiting for one of " + eventHandles.length + " events");
-        final int pipeNum = helper.waitForMultipleObjects(eventHandles.length, eventHandles, false, (int)WinPipesNativeHelper.INFINITE) - (int)WinPipesNativeHelper.WAIT_OBJECT_0;
-        if (pipeNum >= 0 && pipeNum < eventHandles.length) {
-            logger.finest("WinPipe got event on handle " + ehArray.get(pipeNum) + " err=" + helper.getLastError());
-            return ehArray.get(pipeNum);
+        final int pipeIdx = helper.waitForMultipleObjects(eventHandles.length, eventHandles, false, (int) WinPipesNativeHelper.INFINITE) - (int) WinPipesNativeHelper.WAIT_OBJECT_0;
+        if (pipeIdx >= 0 && pipeIdx < eventHandles.length) {
+            logger.finest("WinPipe got event on handle " + ehArray.get(pipeIdx));
+            return ehArray.get(pipeIdx);
         } else {
-            final String msg = "WinPipe waitForMultipleObjects returned " + pipeNum + " err=" + helper.getLastError();
+            final String msg = "WinPipe waitForMultipleObjects returned " + pipeIdx + " err=" + helper.getLastError();
             logger.info(msg);
             throw new IOException(msg);
         }
     }
+
+    public String toString() {
+        return "WindowsEventSelector(" + eventHandlers + " array=" + ehArray + ")";
+    }
 }
--- a/agent/ipc/windows-named-pipes/server/src/main/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/WritePipeImpl.java	Mon Mar 06 11:11:51 2017 -0500
+++ b/agent/ipc/windows-named-pipes/server/src/main/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/WritePipeImpl.java	Tue Mar 07 09:44:06 2017 -0500
@@ -48,7 +48,7 @@
 class WritePipeImpl implements WindowsEventSelector.EventHandler {
 
     private static final Logger logger = LoggingUtils.getLogger(WritePipeImpl.class);
-    private static WinPipesNativeHelper helper = WinPipesNativeHelper.INSTANCE;
+    private WinPipesNativeHelper helper;
 
     enum WritePipeState { QUIET_STATE, WRITING_STATE, FLUSHING_WRITE, ERROR_STATE, CLOSED_STATE }
 
@@ -67,6 +67,23 @@
         this.pipeName = pipeName;
         this.writeState = WritePipeState.QUIET_STATE;
         this.pipeHandle = pipeHandle;
+        this.helper = WinPipesNativeHelper.INSTANCE;
+        this.writeEventHandle = helper.createEvent();
+        if (this.writeEventHandle == 0) {
+            throw new IOException(this.pipeName + ": can't create a Windows event" + " err=" + helper.getLastError());
+        }
+        this.writeQueue = new ArrayDeque<>();
+
+        this.writeOverlap = helper.createDirectOverlapStruct(writeEventHandle);
+        this.writeBuffer = helper.createDirectBuffer(bufsize);
+    }
+
+    WritePipeImpl(PipeManager manager, String pipeName, long pipeHandle, int bufsize, WinPipesNativeHelper hlp) throws IOException {
+        this.manager = manager;
+        this.pipeName = pipeName;
+        this.writeState = WritePipeState.QUIET_STATE;
+        this.pipeHandle = pipeHandle;
+        this.helper = hlp;
         this.writeEventHandle = helper.createEvent();
         if (this.writeEventHandle == 0) {
             throw new IOException(this.pipeName + ": can't create a Windows event" + " err=" + helper.getLastError());
@@ -81,6 +98,10 @@
         return "WritePipeImpl(h=" + pipeHandle + " '" + pipeName + "' " + writeState + " q=" + writeQueue.size() + ")";
     }
 
+    public WritePipeState getWriteState() {
+        return writeState;
+    }
+
     @Override
     public long getHandle() {
         return writeEventHandle;
@@ -129,7 +150,7 @@
      * @return true if queueNextOperation() should be called, false otherwise
      * @throws IOException if there were any errors interacting with the pipe
      */
-    private boolean handlePendingWrite() throws IOException {
+    boolean handlePendingWrite() throws IOException {
         logger.finest("handlePendingWrite() - entered " + this);
         if (writeState != WritePipeState.QUIET_STATE) {
             logger.finest("handlePendingWrite() waiting for overlapped result on " + this + " state=" + writeState);
@@ -163,7 +184,7 @@
      * @return true if an operation was enqueued
      * @throws IOException if an IO error occurred
      */
-    private boolean enqueueWrite() throws IOException {
+    boolean enqueueWrite() throws IOException {
         logger.finest("enqueueWrite() - entered " + this);
         if (writeBuffer.remaining() == 0 && writeQueue.isEmpty()) {
             if (writeState == WritePipeState.FLUSHING_WRITE) {
--- a/agent/ipc/windows-named-pipes/server/src/test/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/AcceptThreadTest.java	Mon Mar 06 11:11:51 2017 -0500
+++ b/agent/ipc/windows-named-pipes/server/src/test/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/AcceptThreadTest.java	Tue Mar 07 09:44:06 2017 -0500
@@ -36,46 +36,128 @@
 
 package com.redhat.thermostat.agent.ipc.winpipes.server.internal;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import java.io.IOException;
 import java.util.concurrent.ExecutorService;
 
+import com.redhat.thermostat.agent.ipc.winpipes.common.internal.WinPipe;
 import org.junit.Before;
 import org.junit.Test;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
 
 import com.redhat.thermostat.agent.ipc.server.ThermostatIPCCallbacks;
 
 public class AcceptThreadTest {
 
-    private ExecutorService execService;
-    private WinPipesServerChannelImpl serverChannel;
-    private ClientPipeInstance clientSock;
+    private final String PIPE_NAME = "SomePipeName";
+    private final static int BUFSIZE = 4096;
+
+    private ExecutorService execMock;
+    private WinPipesServerChannelImpl channelMock;
+    private WinPipe pipeMock;
+    private ClientPipeInstance clientMock;
     private ThermostatIPCCallbacks callbacks;
-    private ClientHandler handler;
     private AcceptThread thread;
+    private AcceptThread.TestHelper atMock;
+    private WindowsEventSelector selectorMock;
+    private WindowsEventSelector.EventHandler ehMock;
 
     @Before
     public void setUp() throws IOException {
-        
-        // Mock sockets
-        serverChannel = mock(WinPipesServerChannelImpl.class);
-        clientSock = mock(ClientPipeInstance.class);
-  //      when(serverSock.accept()).thenReturn(clientSock);
+
+        channelMock = mock(WinPipesServerChannelImpl.class);
+
+        clientMock = mock(ClientPipeInstance.class);
+        when(clientMock.connectToNewClient()).thenReturn(true);
+        when(clientMock.getName()).thenReturn(PIPE_NAME);
+
         callbacks = mock(ThermostatIPCCallbacks.class);
-        when(serverChannel.getCallbacks()).thenReturn(callbacks);
+        when(channelMock.getCallbacks()).thenReturn(callbacks);
 
-        
-        execService = mock(ExecutorService.class);
-        handler = mock(ClientHandler.class);
+        execMock = mock(ExecutorService.class);
+        pipeMock = mock(WinPipe.class);
+        when(pipeMock.getPipeName()).thenReturn(PIPE_NAME);
+        selectorMock = mock(WindowsEventSelector.class);
+        atMock = mock(AcceptThread.TestHelper.class);
+        when(atMock.createPipeInstance(eq(PIPE_NAME), eq(1), eq(BUFSIZE), eq(execMock), eq(callbacks))).thenReturn(clientMock);
+
+        ehMock = mock(WindowsEventSelector.EventHandler.class);
+    }
+
+    @Test
+    public void canCreate() {
+        final AcceptThread at = new AcceptThread(channelMock, execMock, 1, pipeMock, selectorMock, atMock);
+        assertFalse(at.isShutdown());
     }
 
+    @Test
+    public void testCreateInstances() {
+        final AcceptThread at = new AcceptThread(channelMock, execMock, 1, pipeMock, selectorMock, atMock);
+        try {
+            when(atMock.createPipeInstance(anyString(), anyInt(), anyInt(), eq(execMock), eq(callbacks))).thenReturn(clientMock);
+        } catch (IOException e) {
+            fail();
+        }
+        try {
+            at.createInstances();
+        } catch (IOException e) {
+            fail();
+        }
+        try {
+            verify(clientMock).connectToNewClient();
+        } catch (IOException e) {
+            fail();
+        }
+    }
+
+    @Test
+    public void testRun() {
+        final AcceptThread at = new AcceptThread(channelMock, execMock, 1, pipeMock, selectorMock, atMock);
+        try {
+            when(atMock.createPipeInstance(anyString(), anyInt(), anyInt(), eq(execMock), eq(callbacks))).thenReturn(clientMock);
+        } catch (IOException e) {
+            fail();
+        }
+        try {
+            when(selectorMock.waitForEvent()).thenReturn(ehMock);
+        } catch (IOException e) {
+            fail();
+        }
+
+        Thread testThread = new Thread() {
+            public void run() {
+                at.run();
+            }
+        };
+        testThread.start();
+        try {
+            Thread.sleep(1);
+            at.shutdown();
+        } catch (InterruptedException e) {
+            fail();
+        } catch (IOException e) {
+            fail();
+        }
+
+    }
+
+    @Test
+    public void testShutdown() {
+        final AcceptThread at = new AcceptThread(channelMock, execMock, 1, pipeMock, selectorMock, atMock);
+        try {
+            at.shutdown();
+        } catch (IOException e) {
+            fail();
+        }
+        assertTrue(at.isShutdown());
+    }
 }
+
--- a/agent/ipc/windows-named-pipes/server/src/test/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/ClientHandlerTest.java	Mon Mar 06 11:11:51 2017 -0500
+++ b/agent/ipc/windows-named-pipes/server/src/test/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/ClientHandlerTest.java	Tue Mar 07 09:44:06 2017 -0500
@@ -36,11 +36,8 @@
 
 package com.redhat.thermostat.agent.ipc.winpipes.server.internal;
 
-import static com.redhat.thermostat.shared.config.CommonPaths.THERMOSTAT_HOME;
-import static com.redhat.thermostat.shared.config.CommonPaths.USER_THERMOSTAT_HOME;
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -49,12 +46,8 @@
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
-import java.nio.channels.SelectionKey;
-import java.nio.channels.Selector;
 import java.util.concurrent.ExecutorService;
 
-import com.redhat.thermostat.shared.config.NativeLibraryResolver;
-import com.redhat.thermostat.shared.config.internal.CommonPathsImpl;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
@@ -78,10 +71,6 @@
     
     @Before
     public void setup() throws Exception {
-
-        System.setProperty(THERMOSTAT_HOME, ".");
-        System.setProperty(USER_THERMOSTAT_HOME, ".");
-        NativeLibraryResolver.setCommonPaths(new CommonPathsImpl());
         client = mock(ClientPipeInstance.class);
         callbacks = mock(ThermostatIPCCallbacks.class);
         reader = mock(AsyncMessageReader.class);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/ipc/windows-named-pipes/server/src/test/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/ClientPipeInstanceTest.java	Tue Mar 07 09:44:06 2017 -0500
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.ipc.winpipes.server.internal;
+
+import com.redhat.thermostat.agent.ipc.server.ThermostatIPCCallbacks;
+import com.redhat.thermostat.agent.ipc.winpipes.common.internal.WinPipesNativeHelper;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutorService;
+
+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 static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ClientPipeInstanceTest {
+
+    private final String PIPE_NAME = "SomePipeName";
+
+    private ClientPipeInstance cpiMock;
+    private ClientPipeInstance.ClientPipeInstanceHelper helperMock;
+    private ExecutorService execMock;
+    private ThermostatIPCCallbacks cbMock;
+    private ReadPipeImpl readerMock;
+    private WritePipeImpl writerMock;
+
+    @Before
+    public void setup() {
+        helperMock = mock(ClientPipeInstance.ClientPipeInstanceHelper.class);
+        when(helperMock.createNamedPipe(anyString(), anyInt(), anyInt())).thenReturn(WinPipesNativeHelper.INVALID_HANDLE);
+        when(helperMock.createNamedPipe(eq(PIPE_NAME), anyInt(), anyInt())).thenReturn(999L);
+        when(helperMock.disconnectNamedPipe(anyInt())).thenReturn(true);
+        readerMock = mock(ReadPipeImpl.class);
+        writerMock = mock(WritePipeImpl.class);
+        try {
+            when(helperMock.createPipeReader(any(ClientPipeInstance.class), eq(PIPE_NAME), anyLong(), anyInt())).thenReturn(readerMock);
+            when(helperMock.createPipeWriter(any(ClientPipeInstance.class), eq(PIPE_NAME), anyLong(), anyInt())).thenReturn(writerMock);
+        } catch (IOException e) {
+            fail();
+        }
+        execMock = mock(ExecutorService.class);
+        cbMock = mock(ThermostatIPCCallbacks.class);
+    }
+
+    @Test
+    public void testCanConstruct() {
+        ClientPipeInstance cpi;
+        try {
+            cpi = new ClientPipeInstance(PIPE_NAME, 5, 4096, execMock, cbMock, helperMock);
+        } catch (IOException e) {
+            fail();
+            return;
+        }
+        assertEquals(readerMock, cpi.getReadHandler());
+        assertEquals(writerMock, cpi.getWriteHandler());
+    }
+
+    @Test
+    public void testConnectNewClient() throws IOException {
+        ClientPipeInstance cpi = new ClientPipeInstance(PIPE_NAME, 5, 4096, execMock, cbMock, helperMock);
+        when(readerMock.getReadState()).thenReturn(ReadPipeImpl.ReadPipeState.READING_STATE);
+        cpi.connectToNewClient();
+        assertTrue(cpi.isOpen());
+    }
+
+    @Test
+    public void testClose() throws IOException {
+        ClientPipeInstance cpi = new ClientPipeInstance(PIPE_NAME, 5, 4096, execMock, cbMock, helperMock);
+        when(readerMock.getReadState()).thenReturn(ReadPipeImpl.ReadPipeState.READING_STATE);
+        cpi.connectToNewClient();
+        cpi.close();
+        assertFalse(cpi.isOpen());
+    }
+
+    @Test
+    public void testReset() throws IOException {
+        ClientPipeInstance cpi = new ClientPipeInstance(PIPE_NAME, 5, 4096, execMock, cbMock, helperMock);
+        when(readerMock.getReadState()).thenReturn(ReadPipeImpl.ReadPipeState.READING_STATE);
+        cpi.connectToNewClient();
+        cpi.resetPipe();
+        assertTrue(cpi.isOpen());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/ipc/windows-named-pipes/server/src/test/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/ReadPipeImplTest.java	Tue Mar 07 09:44:06 2017 -0500
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.ipc.winpipes.server.internal;
+
+import com.redhat.thermostat.agent.ipc.winpipes.common.internal.WinPipesNativeHelper;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import static com.redhat.thermostat.agent.ipc.winpipes.server.internal.ReadPipeImpl.ReadPipeState.CLOSED_STATE;
+import static com.redhat.thermostat.agent.ipc.winpipes.server.internal.ReadPipeImpl.ReadPipeState.READING_STATE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class ReadPipeImplTest {
+
+    private static final long GOOD_HANDLE = 999L;
+    private static final int BUFSIZE = 4096;
+    private static final String PIPE_NAME = "SomePipeName";
+
+    private PipeManager mgrMock;
+    private WinPipesNativeHelper nativeMock;
+    private ClientHandler clientHandlerMock;
+
+    @Before
+    public void setup() {
+        mgrMock = mock(PipeManager.class);
+        clientHandlerMock = mock(ClientHandler.class);
+        when(mgrMock.handleNewClientConnection()).thenReturn(clientHandlerMock);
+
+        nativeMock = mock(WinPipesNativeHelper.class);
+        try {
+            when(nativeMock.connectNamedPipe(anyLong(), any(ByteBuffer.class))).thenReturn(WinPipesNativeHelper.ERROR_BROKEN_PIPE);
+            when(nativeMock.connectNamedPipe(eq(GOOD_HANDLE), any(ByteBuffer.class))).thenReturn(WinPipesNativeHelper.ERROR_SUCCESS);
+            when(nativeMock.createEvent()).thenReturn(88L);
+            when(nativeMock.createDirectBuffer(anyInt())).thenReturn(ByteBuffer.allocate(BUFSIZE));
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    public void testCanConstruct() throws IOException {
+        new ReadPipeImpl(mgrMock, PIPE_NAME, GOOD_HANDLE, BUFSIZE, nativeMock);
+    }
+
+    @Test
+    public void testGoodConnect() throws IOException {
+        final ReadPipeImpl impl = new ReadPipeImpl(mgrMock, PIPE_NAME, GOOD_HANDLE, BUFSIZE, nativeMock);
+        final boolean ret = impl.connectToNewClient();
+        verify(nativeMock).connectNamedPipe(anyLong(), any(ByteBuffer.class));
+        assertFalse(ret);
+    }
+
+    @Test
+    public void testBadConnect() throws IOException {
+        final ReadPipeImpl impl = new ReadPipeImpl(mgrMock, PIPE_NAME, 22, BUFSIZE, nativeMock);
+        try {
+            impl.connectToNewClient();
+            fail();
+        } catch (IOException e) {
+            ;
+        }
+        verify(nativeMock).connectNamedPipe(anyLong(), any(ByteBuffer.class));
+    }
+
+    @Test
+    public void testEnqueueOperation() throws IOException {
+        final ReadPipeImpl impl = new ReadPipeImpl(mgrMock, PIPE_NAME, GOOD_HANDLE, BUFSIZE, nativeMock);
+        when(nativeMock.readFileOverlapped(eq(GOOD_HANDLE), any(ByteBuffer.class), any(ByteBuffer.class))).thenReturn(true);
+        impl.enqueueRead();
+        verify(mgrMock, never()).resetPipe();
+    }
+
+    @Test
+    public void testBadEnqueueOperation() throws IOException {
+        final ReadPipeImpl impl = new ReadPipeImpl(mgrMock, PIPE_NAME, GOOD_HANDLE, BUFSIZE, nativeMock);
+        when(nativeMock.readFileOverlapped(eq(GOOD_HANDLE), any(ByteBuffer.class), any(ByteBuffer.class))).thenReturn(false);
+        when(nativeMock.getLastError()).thenReturn(WinPipesNativeHelper.ERROR_BROKEN_PIPE);
+        impl.enqueueRead();
+        verify(mgrMock).resetPipe();
+    }
+
+    @Test
+    public void testHandlePendingOperation() throws IOException {
+        final ReadPipeImpl impl = new ReadPipeImpl(mgrMock, PIPE_NAME, GOOD_HANDLE, BUFSIZE, nativeMock);
+        final boolean ret = impl.connectToNewClient(); // force READING_STATE
+        impl.handlePendingRead();
+        verify(mgrMock, never()).resetPipe();
+        assertEquals(impl.getReadState(), READING_STATE);
+    }
+
+    @Test
+    public void testClose() throws IOException {
+        final ReadPipeImpl impl = new ReadPipeImpl(mgrMock, PIPE_NAME, GOOD_HANDLE, BUFSIZE, nativeMock);
+        final boolean ret = impl.connectToNewClient();
+        impl.close();
+        assertEquals(impl.getReadState(), CLOSED_STATE);
+    }
+}
--- a/agent/ipc/windows-named-pipes/server/src/test/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/WinPipesServerTransportTest.java	Mon Mar 06 11:11:51 2017 -0500
+++ b/agent/ipc/windows-named-pipes/server/src/test/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/WinPipesServerTransportTest.java	Tue Mar 07 09:44:06 2017 -0500
@@ -36,76 +36,26 @@
 
 package com.redhat.thermostat.agent.ipc.winpipes.server.internal;
 
-import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-import java.io.File;
 import java.nio.channels.spi.AbstractSelector;
 import java.nio.channels.spi.SelectorProvider;
-import java.nio.file.Path;
-import java.nio.file.attribute.FileAttribute;
-import java.nio.file.attribute.PosixFilePermission;
-import java.nio.file.attribute.UserPrincipalLookupService;
-import java.util.Set;
-import java.util.concurrent.ExecutorService;
 
-import com.redhat.thermostat.agent.ipc.winpipes.common.internal.WinPipesIPCProperties;
 import org.junit.Before;
 
-import com.redhat.thermostat.agent.ipc.server.ThermostatIPCCallbacks;
-import com.redhat.thermostat.agent.ipc.winpipes.server.internal.WinPipesServerTransport.ChannelUtils;
 
 public class WinPipesServerTransportTest {
-    
-    private static final String SERVER_NAME = "test";
-    
-    private WinPipesServerTransport transport;
+
     private SelectorProvider provider;
     private AbstractSelector selector;
-    private ExecutorService execService;
-    private PipenameValidator validator;
-    private Path socketDirPath;
-    private FileAttribute<Set<PosixFilePermission>> fileAttr;
-    private Path socketPath;
-    private ThermostatIPCCallbacks callbacks;
-    private ChannelUtils channelUtils;
-    private WinPipesServerChannelImpl channel;
-    private WinPipesIPCProperties props;
-    private UserPrincipalLookupService lookup;
 
-    @SuppressWarnings("unchecked")
+
     @Before
     public void setup() throws Exception {
         provider = mock(SelectorProvider.class);
         selector = mock(AbstractSelector.class);
         when(provider.openSelector()).thenReturn(selector);
-        
-        props = mock(WinPipesIPCProperties.class);
-        File sockDirFile = mock(File.class);
-      //  when(props.getPipePrefix()).thenReturn(sockDirFile);
-        socketDirPath = mock(Path.class);
-        when(socketDirPath.toAbsolutePath()).thenReturn(socketDirPath);
-        when(socketDirPath.normalize()).thenReturn(socketDirPath);
-        when(sockDirFile.toPath()).thenReturn(socketDirPath);
-        socketPath = mock(Path.class);
-        //when(socketDirPath.resolve(WinPipesServerTransport. + SERVER_NAME)).thenReturn(socketPath);
-        
-
-        
-        execService = mock(ExecutorService.class);
-        validator = mock(PipenameValidator.class);
-        when(validator.validate(any(String.class))).thenReturn(true);
-
-        
-        channelUtils = mock(ChannelUtils.class);
-        channel = mock(WinPipesServerChannelImpl.class);
-        File socketFile = mock(File.class);
-        when(socketFile.toPath()).thenReturn(socketPath);
-
-        
-        callbacks = mock(ThermostatIPCCallbacks.class);
-
     }
 
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/ipc/windows-named-pipes/server/src/test/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/WindowsEventSelectorTest.java	Tue Mar 07 09:44:06 2017 -0500
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.ipc.winpipes.server.internal;
+
+import com.redhat.thermostat.agent.ipc.winpipes.common.internal.WinPipesNativeHelper;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.LinkedHashSet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class WindowsEventSelectorTest {
+
+    private static final int MAX_INSTANCES = 5;
+    private WinPipesNativeHelper nativeMock;
+
+    @Before
+    public void setup() {
+        nativeMock = mock(WinPipesNativeHelper.class);
+        when(nativeMock.getLastError()).thenReturn(WinPipesNativeHelper.ERROR_SUCCESS);
+
+        /*
+         * The mock will return an index based on the size of the handle array.
+         * For the tests to work, assume the array is ordered in the order of insertion
+         * To do this, create the WindowsEventSelector with a LinkedHashSet instead of a simple HashSet
+         */
+        when(nativeMock.waitForMultipleObjects(eq(0), any(long[].class), anyBoolean(), anyInt())).thenReturn(-1);
+        when(nativeMock.waitForMultipleObjects(eq(1), any(long[].class), anyBoolean(), anyInt())).thenReturn((int) WinPipesNativeHelper.WAIT_OBJECT_0 + 0);
+        when(nativeMock.waitForMultipleObjects(eq(2), any(long[].class), anyBoolean(), anyInt())).thenReturn((int) WinPipesNativeHelper.WAIT_OBJECT_0 + 1);
+    }
+
+    private WindowsEventSelector create() {
+        return new WindowsEventSelector(MAX_INSTANCES, new LinkedHashSet<WindowsEventSelector.EventHandler>(), nativeMock);
+    }
+
+    @Test
+    public void testCanConstruct() {
+        create();
+    }
+
+    @Test
+    public void testWait() {
+        final WindowsEventSelector wes = create();
+        try {
+            wes.waitForEvent();
+            // no events added, so should throw an error
+            fail("expected IOException");
+        } catch (IOException ignored) {
+        }
+        verify(nativeMock).getLastError();
+        verify(nativeMock).waitForMultipleObjects(anyInt(), any(long[].class), anyBoolean(), anyInt());
+    }
+
+    @Test
+    public void testAdd() throws IOException {
+        final WindowsEventSelector wes = create();
+        final WindowsEventSelector.EventHandler eh1 = mock(WindowsEventSelector.EventHandler.class);
+        wes.add(eh1);
+        final WindowsEventSelector.EventHandler en = wes.waitForEvent();
+        assertEquals(eh1, en);
+        verify(nativeMock, never()).getLastError();
+        verify(nativeMock).waitForMultipleObjects(anyInt(), any(long[].class), anyBoolean(), anyInt());
+    }
+
+    @Test
+    public void testRemove() throws IOException {
+        final WindowsEventSelector wes = create();
+        final WindowsEventSelector.EventHandler eh1 = mock(WindowsEventSelector.EventHandler.class);
+        final WindowsEventSelector.EventHandler eh2 = mock(WindowsEventSelector.EventHandler.class);
+
+        wes.add(eh1);
+        final WindowsEventSelector.EventHandler en1 = wes.waitForEvent();
+        assertEquals(eh1, en1);
+
+        wes.add(eh2);
+        final WindowsEventSelector.EventHandler en2 = wes.waitForEvent();
+        assertEquals(eh2, en2);
+
+        wes.remove(eh1);
+        final WindowsEventSelector.EventHandler en3 = wes.waitForEvent();
+        assertEquals(eh2, en3);
+
+        verify(nativeMock, never()).getLastError();
+        verify(nativeMock, times(3)).waitForMultipleObjects(anyInt(), any(long[].class), anyBoolean(), anyInt());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/ipc/windows-named-pipes/server/src/test/java/com/redhat/thermostat/agent/ipc/winpipes/server/internal/WritePipeImplTest.java	Tue Mar 07 09:44:06 2017 -0500
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.ipc.winpipes.server.internal;
+
+import com.redhat.thermostat.agent.ipc.winpipes.common.internal.WinPipesNativeHelper;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class WritePipeImplTest {
+
+    private static final long GOOD_HANDLE = 999L;
+    private static final int BUFSIZE = 4096;
+    private static final String PIPE_NAME = "SomePipeName";
+
+    private PipeManager mgrMock;
+    private WinPipesNativeHelper nativeMock;
+    private ClientHandler clientHandlerMock;
+
+    @Before
+    public void setup() {
+        mgrMock = mock(PipeManager.class);
+        clientHandlerMock = mock(ClientHandler.class);
+        when(mgrMock.handleNewClientConnection()).thenReturn(clientHandlerMock);
+
+        nativeMock = mock(WinPipesNativeHelper.class);
+        try {
+            when(nativeMock.connectNamedPipe(anyLong(), any(ByteBuffer.class))).thenReturn(WinPipesNativeHelper.ERROR_BROKEN_PIPE);
+            when(nativeMock.connectNamedPipe(eq(GOOD_HANDLE), any(ByteBuffer.class))).thenReturn(WinPipesNativeHelper.ERROR_SUCCESS);
+            when(nativeMock.createEvent()).thenReturn(88L);
+            final ByteBuffer bb = ByteBuffer.allocate(BUFSIZE);
+            bb.limit(0);
+            when(nativeMock.createDirectBuffer(anyInt())).thenReturn(bb);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    public void testCanConstruct() throws IOException {
+        new WritePipeImpl(mgrMock, PIPE_NAME, GOOD_HANDLE, BUFSIZE, nativeMock);
+    }
+
+    @Test
+    public void testWrite() throws IOException {
+        final WritePipeImpl impl = new WritePipeImpl(mgrMock, PIPE_NAME, GOOD_HANDLE, BUFSIZE, nativeMock);
+        ByteBuffer bb = ByteBuffer.wrap("SomeData".getBytes());
+        impl.write(bb);
+        impl.enqueueWrite();
+        verify(nativeMock).writeFileOverlapped(anyLong(), any(ByteBuffer.class), any(ByteBuffer.class));
+        assertEquals(impl.getWriteState(), WritePipeImpl.WritePipeState.WRITING_STATE);
+    }
+
+    @Test
+    public void testEnqueueOperation() throws IOException {
+        final WritePipeImpl impl = new WritePipeImpl(mgrMock, PIPE_NAME, GOOD_HANDLE, BUFSIZE, nativeMock);
+        impl.enqueueWrite();
+        verify(nativeMock, never()).writeFileOverlapped(anyLong(), any(ByteBuffer.class), any(ByteBuffer.class));
+        assertEquals(impl.getWriteState(), WritePipeImpl.WritePipeState.QUIET_STATE);
+    }
+
+    @Test
+    public void testHandlePendingOperation() throws IOException {
+        final WritePipeImpl impl = new WritePipeImpl(mgrMock, PIPE_NAME, GOOD_HANDLE, BUFSIZE, nativeMock);
+        impl.handlePendingWrite();
+        verify(nativeMock, never()).getOverlappedResult(anyLong(), any(ByteBuffer.class), anyBoolean());
+        assertEquals(impl.getWriteState(), WritePipeImpl.WritePipeState.QUIET_STATE);
+    }
+
+    @Test
+    public void testClose() throws IOException {
+        final WritePipeImpl impl = new WritePipeImpl(mgrMock, PIPE_NAME, GOOD_HANDLE, BUFSIZE, nativeMock);
+        impl.close();
+        assertEquals(impl.getWriteState(), WritePipeImpl.WritePipeState.CLOSED_STATE);
+    }
+}
+