summaryrefslogtreecommitdiff
path: root/src/pal/tests/palsuite/threading/CreateProcessA
diff options
context:
space:
mode:
authorJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
committerJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
commit4b4aad7217d3292650e77eec2cf4c198ea9c3b4b (patch)
tree98110734c91668dfdbb126fcc0e15ddbd93738ca /src/pal/tests/palsuite/threading/CreateProcessA
parentfa45f57ed55137c75ac870356a1b8f76c84b229c (diff)
downloadcoreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.gz
coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.bz2
coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.zip
Imported Upstream version 1.1.0upstream/1.1.0
Diffstat (limited to 'src/pal/tests/palsuite/threading/CreateProcessA')
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/CMakeLists.txt5
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test1/CMakeLists.txt36
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test1/childProcess.c131
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test1/parentProcess.c201
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test1/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test2/CMakeLists.txt36
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test2/childprocess.c69
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test2/parentprocess.c243
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test2/test2.h72
-rw-r--r--src/pal/tests/palsuite/threading/CreateProcessA/test2/testinfo.dat20
10 files changed, 830 insertions, 0 deletions
diff --git a/src/pal/tests/palsuite/threading/CreateProcessA/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateProcessA/CMakeLists.txt
new file mode 100644
index 0000000000..ef14ea5352
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(test1)
+add_subdirectory(test2)
+
diff --git a/src/pal/tests/palsuite/threading/CreateProcessA/test1/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateProcessA/test1/CMakeLists.txt
new file mode 100644
index 0000000000..67e53edccd
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test1/CMakeLists.txt
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(TESTSOURCES
+ parentProcess.c
+)
+
+add_executable(paltest_createprocessa_test1
+ ${TESTSOURCES}
+)
+
+add_dependencies(paltest_createprocessa_test1 coreclrpal)
+
+target_link_libraries(paltest_createprocessa_test1
+ pthread
+ m
+ coreclrpal
+)
+
+
+set(HELPERSOURCES
+ childProcess.c
+)
+
+add_executable(paltest_createprocessa_test1_child
+ ${HELPERSOURCES}
+)
+
+add_dependencies(paltest_createprocessa_test1_child coreclrpal)
+
+target_link_libraries(paltest_createprocessa_test1_child
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateProcessA/test1/childProcess.c b/src/pal/tests/palsuite/threading/CreateProcessA/test1/childProcess.c
new file mode 100644
index 0000000000..ccbb050c04
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test1/childProcess.c
@@ -0,0 +1,131 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: CreateProcessA/test1/childprocess.c
+**
+** Purpose: Test to ensure CreateProcessA starts a new process. This test
+** launches a child process, and examines a file written by the child.
+** This code is the child code.
+**
+** Dependencies: GetCurrentDirectory
+** strlen
+** fopen
+** fclose
+** fprintf
+**
+
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+const char *szCommonFileA = "childdata.tmp";
+
+const char *szPathDelimA = "\\";
+
+const char *szCommonStringA = "058d2d057111a313aa82401c2e856002\0";
+
+/*
+ * Take two wide strings representing file and directory names
+ * (dirName, fileName), join the strings with the appropriate path
+ * delimiter and populate a wide character buffer (absPathName) with
+ * the resulting string.
+ *
+ * Returns: The number of wide characters in the resulting string.
+ * 0 is returned on Error.
+ */
+int
+mkAbsoluteFilenameA (
+ LPSTR dirName,
+ DWORD dwDirLength,
+ LPCSTR fileName,
+ DWORD dwFileLength,
+ LPSTR absPathName )
+{
+ extern const char *szPathDelimA;
+
+ DWORD sizeDN, sizeFN, sizeAPN;
+
+ sizeDN = strlen( dirName );
+ sizeFN = strlen( fileName );
+ sizeAPN = (sizeDN + 1 + sizeFN + 1);
+
+ /* insure ((dirName + DELIM + fileName + \0) =< _MAX_PATH ) */
+ if ( sizeAPN > _MAX_PATH )
+ {
+ return ( 0 );
+ }
+
+ strncpy(absPathName, dirName, dwDirLength +1);
+ strncpy(absPathName, szPathDelimA, 2);
+ strncpy(absPathName, fileName, dwFileLength +1);
+
+ return (sizeAPN);
+
+}
+
+int __cdecl main( int argc, char **argv )
+{
+
+ static FILE * fp;
+
+ DWORD dwFileLength;
+ DWORD dwDirLength;
+ DWORD dwSize;
+
+ char szDirNameA[_MAX_DIR];
+ char szAbsPathNameA[_MAX_PATH];
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ dwDirLength = GetCurrentDirectory( _MAX_PATH, szDirNameA );
+
+ if (0 == dwDirLength)
+ {
+ Fail ("GetCurrentDirectory call failed. Could not get "
+ "current working directory\n. Exiting.\n");
+ }
+
+ dwFileLength = strlen( szCommonFileA );
+
+ dwSize = mkAbsoluteFilenameA( szDirNameA, dwDirLength, szCommonFileA,
+ dwFileLength, szAbsPathNameA );
+
+ if (0 == dwSize)
+ {
+ Fail ("Palsuite Code: mkAbsoluteFilename() call failed. Could "
+ "not build absolute path name to file\n. Exiting.\n");
+ }
+
+ if ( NULL == ( fp = fopen ( szAbsPathNameA , "w+" ) ) )
+ {
+ /*
+ * A return value of NULL indicates an error condition or an
+ * EOF condition
+ */
+ Fail ("%s unable to open %s for writing. Exiting.\n", argv[0]
+ , szAbsPathNameA );
+ }
+
+ if ( 0 >= ( fprintf ( fp, "%s", szCommonStringA )))
+ {
+ Fail("%s unable to write to %s. Exiting.\n", argv[0]
+ , szAbsPathNameA );
+ }
+
+ if (0 != (fclose ( fp )))
+ {
+ Fail ("%s unable to close file %s. Pid may not be "
+ "written to file. Exiting.\n", argv[0], szAbsPathNameA );
+ }
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/CreateProcessA/test1/parentProcess.c b/src/pal/tests/palsuite/threading/CreateProcessA/test1/parentProcess.c
new file mode 100644
index 0000000000..b0c5808a7e
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test1/parentProcess.c
@@ -0,0 +1,201 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: CreateProcessA/test1/parentprocess.c
+**
+** Purpose: Test to ensure CreateProcessA starts a new process. This test
+** launches a child process, and examines a file written by the child.
+** This process (the parent process) reads the file created by the child and
+** compares the value the child wrote to the file. (a const char *)
+**
+** Dependencies: GetCurrentDirectory
+** strlen
+** WaitForSingleObject
+** fopen
+** fclose
+** Fail
+**
+
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+const char *szCommonFileA = "childdata.tmp";
+
+const char *szChildFileA = "paltest_createprocessa_test1_child";
+
+const char *szPathDelimA = "\\";
+
+const char *szCommonStringA = "058d2d057111a313aa82401c2e856002\0";
+
+/*
+ * Take two wide strings representing file and directory names
+ * (dirName, fileName), join the strings with the appropriate path
+ * delimiter and populate a wide character buffer (absPathName) with
+ * the resulting string.
+ *
+ * Returns: The number of wide characters in the resulting string.
+ * 0 is returned on Error.
+ */
+int
+mkAbsoluteFilenameA (
+ LPSTR dirName,
+ DWORD dwDirLength,
+ LPCSTR fileName,
+ DWORD dwFileLength,
+ LPSTR absPathName )
+{
+ extern const char *szPathDelimA;
+
+ DWORD sizeDN, sizeFN, sizeAPN;
+
+ sizeDN = strlen( dirName );
+ sizeFN = strlen( fileName );
+ sizeAPN = (sizeDN + 1 + sizeFN + 1);
+
+ /* insure ((dirName + DELIM + fileName + \0) =< _MAX_PATH ) */
+ if ( sizeAPN > _MAX_PATH )
+ {
+ return ( 0 );
+ }
+
+ strncpy(absPathName, dirName, dwDirLength +1);
+ strncpy(absPathName, szPathDelimA, 2);
+ strncpy(absPathName, fileName, dwFileLength +1);
+
+ return (sizeAPN);
+
+}
+
+int __cdecl main( int argc, char **argv )
+
+{
+
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ static FILE * fp;
+
+ DWORD dwFileLength;
+ DWORD dwDirLength;
+ DWORD dwSize;
+
+ size_t cslen;
+
+ char szReadStringA[256];
+
+ char szDirNameA[_MAX_DIR];
+ char absPathBuf[_MAX_PATH];
+ char *szAbsPathNameA;
+
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ ZeroMemory ( &si, sizeof(si) );
+ si.cb = sizeof(si);
+ ZeroMemory ( &pi, sizeof(pi) );
+
+ szAbsPathNameA=&absPathBuf[0];
+ dwFileLength = strlen( szChildFileA );
+
+ dwDirLength = GetCurrentDirectory(_MAX_PATH, szDirNameA);
+
+ if (0 == dwDirLength)
+ {
+ Fail ("GetCurrentDirectory call failed. Could not get "
+ "current working directory\n. Exiting.\n");
+ }
+
+ dwSize = mkAbsoluteFilenameA( szDirNameA, dwDirLength, szChildFileA,
+ dwFileLength, szAbsPathNameA );
+
+ if (0 == dwSize)
+ {
+ Fail ("Palsuite Code: mkAbsoluteFilename() call failed. Could "
+ "not build absolute path name to file\n. Exiting.\n");
+ }
+
+ if ( !CreateProcessA ( NULL,
+ szAbsPathNameA,
+ NULL,
+ NULL,
+ FALSE,
+ CREATE_NEW_CONSOLE,
+ NULL,
+ NULL,
+ &si,
+ &pi )
+ )
+ {
+ Fail ( "CreateProcess call failed. GetLastError returned %d\n",
+ GetLastError() );
+ }
+
+ WaitForSingleObject ( pi.hProcess, INFINITE );
+
+ szAbsPathNameA=&absPathBuf[0];
+
+ dwFileLength = strlen( szCommonFileA );
+
+ dwSize = mkAbsoluteFilenameA( szDirNameA, dwDirLength, szCommonFileA,
+ dwFileLength, szAbsPathNameA );
+
+ /* set the string length for the open call*/
+
+ if (0 == dwSize)
+ {
+ Fail ("Palsuite Code: mkAbsoluteFilename() call failed. Could "
+ "not build absolute path name to file\n. Exiting.\n");
+ }
+
+ if ( NULL == ( fp = fopen ( szAbsPathNameA , "r" ) ) )
+ {
+ Fail ("%s\nunable to open %s\nfor reading. Exiting.\n", argv[0],
+ szAbsPathNameA );
+ }
+
+ cslen = strlen ( szCommonStringA );
+
+ if ( NULL == fgets( szReadStringA, (cslen + 1), fp ))
+ {
+ /*
+ * A return value of NULL indicates an error condition or an
+ * EOF condition
+ */
+ Fail ("%s\nunable to read file\n%s\nszReadStringA is %s\n"
+ "Exiting.\n", argv[0], szAbsPathNameA,
+ szReadStringA );
+
+ }
+ if ( 0 != strncmp( szReadStringA, szCommonStringA, cslen ))
+ {
+ Fail ("string comparison failed.\n szReadStringA is %s and\n"
+ "szCommonStringA is %s\n", szReadStringA,
+ szCommonStringA );
+ }
+ else
+ {
+ Trace ("string comparison passed.\n");
+ }
+
+ if (0 != (fclose ( fp )))
+ {
+ Trace ("%s unable to close file %s. This may cause a file pointer "
+ "leak. Continuing.\n", argv[0], szAbsPathNameA );
+ }
+
+ /* Close process and thread handle */
+ CloseHandle ( pi.hProcess );
+ CloseHandle ( pi.hThread );
+
+ PAL_Terminate();
+ return ( PASS );
+
+}
diff --git a/src/pal/tests/palsuite/threading/CreateProcessA/test1/testinfo.dat b/src/pal/tests/palsuite/threading/CreateProcessA/test1/testinfo.dat
new file mode 100644
index 0000000000..02c25444fe
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test1/testinfo.dat
@@ -0,0 +1,17 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateProcessA
+Name = Positive Test for CreateProcessA
+TYPE = DEFAULT
+EXE1 = parentprocess
+EXE2 = childprocess
+Description
+= Test the CreateProcessA function. The test executes the childprocess
+= program. The childprocess program launches and writes a const char string
+= to a file childdata. The parent waits for the completion of childprocess
+= and then reads the string from the childdata file. If the string in the
+= file matches it's copy of the const char string, then the test succeeds.
diff --git a/src/pal/tests/palsuite/threading/CreateProcessA/test2/CMakeLists.txt b/src/pal/tests/palsuite/threading/CreateProcessA/test2/CMakeLists.txt
new file mode 100644
index 0000000000..b81ea2e978
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test2/CMakeLists.txt
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(TESTSOURCES
+ parentprocess.c
+)
+
+add_executable(paltest_createprocessa_test2
+ ${TESTSOURCES}
+)
+
+add_dependencies(paltest_createprocessa_test2 coreclrpal)
+
+target_link_libraries(paltest_createprocessa_test2
+ pthread
+ m
+ coreclrpal
+)
+
+
+set(HELPERSOURCES
+ childprocess.c
+)
+
+add_executable(paltest_createprocessa_test2_child
+ ${HELPERSOURCES}
+)
+
+add_dependencies(paltest_createprocessa_test2_child coreclrpal)
+
+target_link_libraries(paltest_createprocessa_test2_child
+ pthread
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/threading/CreateProcessA/test2/childprocess.c b/src/pal/tests/palsuite/threading/CreateProcessA/test2/childprocess.c
new file mode 100644
index 0000000000..baa20c2d3c
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test2/childprocess.c
@@ -0,0 +1,69 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: createprocessa/test2/childprocess.c
+**
+** Purpose: This child process reads a string from stdin
+** and writes it out to stdout & stderr
+**
+** Dependencies: memset
+** fgets
+** gputs
+**
+
+**
+**=========================================================*/
+
+#include <palsuite.h>
+#include "test2.h"
+
+
+
+int __cdecl main( int argc, char **argv )
+{
+ int iRetCode = EXIT_OK_CODE; /* preset exit code to OK */
+ char szBuf[BUF_LEN];
+
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return FAIL;
+ }
+
+ if (argc != 4)
+ {
+ return EXIT_ERR_CODE3;
+ }
+
+ if (strcmp(argv[1], szArg1) != 0
+ || strcmp(argv[2], szArg2) != 0
+ || strcmp(argv[3], szArg3) != 0)
+ {
+ return EXIT_ERR_CODE4;
+ }
+
+
+ memset(szBuf, 0, BUF_LEN);
+
+ /* Read the string that was written by the parent */
+ if (fgets(szBuf, BUF_LEN, stdin) == NULL)
+ {
+ return EXIT_ERR_CODE1;
+ }
+
+ /* Write the string out to the stdout & stderr pipes */
+ if (fputs(szBuf, stdout) == EOF
+ || fputs(szBuf, stderr) == EOF)
+ {
+ return EXIT_ERR_CODE2;
+ }
+
+ /* The exit code will indicate success or failure */
+ PAL_TerminateEx(iRetCode);
+
+ /* Return special exit code to indicate success or failure */
+ return iRetCode;
+}
diff --git a/src/pal/tests/palsuite/threading/CreateProcessA/test2/parentprocess.c b/src/pal/tests/palsuite/threading/CreateProcessA/test2/parentprocess.c
new file mode 100644
index 0000000000..ef3340c5d9
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test2/parentprocess.c
@@ -0,0 +1,243 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: createprocessa/test2/parentprocess.c
+**
+** Purpose: Test the following features of CreateProcessA:
+** - Check to see if hProcess & hThread are set in
+** return PROCESS_INFORMATION structure
+** - Check to see if stdin, stdout, & stderr handles
+** are used when STARTF_USESTDHANDLES is specified
+** in STARUPINFO flags and bInheritHandles = TRUE
+** - Check to see that proper arguments are passed to
+** child process
+**
+** Dependencies: CreatePipe
+** strcpy, strlen, strncmp, memset
+** WaitForSingleObject
+** WriteFile, ReadFile
+** GetExitCodeProcess
+**
+
+**
+**=========================================================*/
+
+#include <palsuite.h>
+#include "test2.h"
+
+
+
+int __cdecl main( int argc, char **argv )
+{
+
+ /*******************************************
+ * Declarations
+ *******************************************/
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ HANDLE hTestStdInR = NULL;
+ HANDLE hTestStdInW = NULL;
+ HANDLE hTestStdOutR = NULL;
+ HANDLE hTestStdOutW = NULL;
+ HANDLE hTestStdErrR = NULL;
+ HANDLE hTestStdErrW = NULL;
+
+ BOOL bRetVal = FALSE;
+ DWORD dwBytesWritten = 0;
+ DWORD dwBytesRead = 0;
+ DWORD dwExitCode = 0;
+
+ SECURITY_ATTRIBUTES pipeAttributes;
+
+ char szStdOutBuf[BUF_LEN];
+ char szStdErrBuf[BUF_LEN];
+ char szFullPathNameA[_MAX_PATH];
+
+
+ /*******************************************
+ * Initialization
+ *******************************************/
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ /*Setup SECURITY_ATTRIBUTES structure for CreatePipe*/
+ pipeAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
+ pipeAttributes.lpSecurityDescriptor = NULL;
+ pipeAttributes.bInheritHandle = TRUE;
+
+
+ /*Create a StdIn pipe for child*/
+ bRetVal = CreatePipe(&hTestStdInR, /* read handle*/
+ &hTestStdInW, /* write handle */
+ &pipeAttributes, /* security attributes*/
+ 1024); /* pipe size*/
+
+ if (bRetVal == FALSE)
+ {
+ Fail("ERROR: %ld :Unable to create stdin pipe\n", GetLastError());
+ }
+
+
+ /*Create a StdOut pipe for child*/
+ bRetVal = CreatePipe(&hTestStdOutR, /* read handle*/
+ &hTestStdOutW, /* write handle */
+ &pipeAttributes, /* security attributes*/
+ 0); /* pipe size*/
+
+ if (bRetVal == FALSE)
+ {
+ Fail("ERROR: %ld :Unable to create stdout pipe\n", GetLastError());
+ }
+
+
+ /*Create a StdErr pipe for child*/
+ bRetVal = CreatePipe(&hTestStdErrR, /* read handle*/
+ &hTestStdErrW, /* write handle */
+ &pipeAttributes, /* security attributes*/
+ 0); /* pipe size*/
+
+ if (bRetVal == FALSE)
+ {
+ Fail("ERROR: %ld :Unable to create stderr pipe\n", GetLastError());
+ }
+
+ /* Zero the data structure space */
+ ZeroMemory ( &pi, sizeof(pi) );
+ ZeroMemory ( &si, sizeof(si) );
+
+ /* Set the process flags and standard io handles */
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = hTestStdInR;
+ si.hStdOutput = hTestStdOutW;
+ si.hStdError = hTestStdErrW;
+
+ strcpy(szFullPathNameA, szChildFileA);
+ strcat(szFullPathNameA, szArgs);
+
+ /*******************************************
+ * Start Testing
+ *******************************************/
+
+ /* Launch the child */
+ if ( !CreateProcessA (NULL, szFullPathNameA, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi ))
+ {
+ Fail("ERROR: CreateProcess call failed. GetLastError returned %d\n",
+ GetLastError() );
+ }
+
+ /* Check the returned process information for validity */
+ if (pi.hProcess == 0 || pi.hThread == 0)
+ {
+ Fail("ERROR: CreateProcess Error: Process Handle = %u, Thread Handle = %u\n",
+ pi.hProcess, pi.hThread);
+ }
+
+ /* Write the Constructed string to stdin pipe for the child process */
+ if (WriteFile(hTestStdInW, szTestString, strlen(szTestString), &dwBytesWritten, NULL) == FALSE
+ || WriteFile(hTestStdInW, "\n", strlen("\n"), &dwBytesWritten, NULL) == FALSE)
+ {
+ Fail("ERROR: %ld :unable to write to write pipe handle "
+ "hTestStdInW=0x%lx\n", GetLastError(), hTestStdInW);
+ }
+
+ /* Wait for the child to finish, Max 20 seconds */
+ dwExitCode = WaitForSingleObject(pi.hProcess, 20000);
+
+ /* If the child failed then whole thing fails */
+ if (dwExitCode != WAIT_OBJECT_0)
+ {
+ TerminateProcess(pi.hProcess, 0);
+ Fail("ERROR: The child failed to run properly.\n");
+ }
+
+ /* Check for problems in the child process */
+ if (GetExitCodeProcess(pi.hProcess, &dwExitCode) == FALSE)
+ {
+ Fail("ERROR: Call to GetExitCodeProcess failed.\n");
+ }
+ else if (dwExitCode == EXIT_ERR_CODE1)
+ {
+ Fail("ERROR: The Child process could not reead the string "
+ "written to the stdin pipe.\n");
+ }
+ else if (dwExitCode == EXIT_ERR_CODE2)
+ {
+ Fail("ERROR: The Child process could not write the string "
+ "the stdout pipe or stderr pipe.\n");
+ }
+ else if (dwExitCode == EXIT_ERR_CODE3)
+ {
+ Fail("ERROR: The Child received the wrong number of "
+ "command line arguments.\n");
+ }
+ else if (dwExitCode == EXIT_ERR_CODE4)
+ {
+ Fail("ERROR: The Child received the wrong "
+ "command line arguments.\n");
+ }
+ else if (dwExitCode != EXIT_OK_CODE)
+ {
+ Fail("ERROR: Unexpected exit code returned: %u. Child process "
+ "did not complete its part of the test.\n", dwExitCode);
+ }
+
+
+ /* The child ran ok, so check to see if we received the proper */
+ /* strings through the pipes. */
+
+ /* clear our buffers */
+ memset(szStdOutBuf, 0, BUF_LEN);
+ memset(szStdErrBuf, 0, BUF_LEN);
+
+ /* Read the data back from the child process stdout */
+ bRetVal = ReadFile(hTestStdOutR, /* handle to read pipe*/
+ szStdOutBuf, /* buffer to write to*/
+ BUF_LEN, /* number of bytes to read*/
+ &dwBytesRead, /* number of bytes read*/
+ NULL); /* overlapped buffer*/
+
+ /*Read the data back from the child process stderr */
+ bRetVal = ReadFile(hTestStdErrR, /* handle to read pipe*/
+ szStdErrBuf, /* buffer to write to*/
+ BUF_LEN, /* number of bytes to read*/
+ &dwBytesRead, /* number of bytes read*/
+ NULL); /* overlapped buffer*/
+
+
+ /* Confirm that we recieved the same string that we originally */
+ /* wrote to the child and was received on both stdout & stderr.*/
+ if (strncmp(szTestString, szStdOutBuf, strlen(szTestString)) != 0
+ || strncmp(szTestString, szStdErrBuf, strlen(szTestString)) != 0)
+ {
+ Fail("ERROR: The data read back from child does not match "
+ "what was written. STDOUT: %s STDERR: %s\n",
+ szStdOutBuf, szStdErrBuf);
+ }
+
+
+ /*******************************************
+ * Clean Up
+ *******************************************/
+
+ /* Close process and thread handle */
+ CloseHandle ( pi.hProcess );
+ CloseHandle ( pi.hThread );
+
+ CloseHandle(hTestStdInR);
+ CloseHandle(hTestStdInW);
+ CloseHandle(hTestStdOutR);
+ CloseHandle(hTestStdOutW);
+ CloseHandle(hTestStdErrR);
+ CloseHandle(hTestStdErrW);
+
+ PAL_Terminate();
+ return ( PASS );
+}
diff --git a/src/pal/tests/palsuite/threading/CreateProcessA/test2/test2.h b/src/pal/tests/palsuite/threading/CreateProcessA/test2/test2.h
new file mode 100644
index 0000000000..8cdff3b939
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test2/test2.h
@@ -0,0 +1,72 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: test2.h
+**
+
+**
+**===========================================================*/
+
+
+const char *szChildFileA = "paltest_createprocessa_test2_child";
+const char *szArgs = " A B C";
+const char *szArg1 = "A";
+const char *szArg2 = "B";
+const char *szArg3 = "C";
+
+const char *szPathDelimA = "\\";
+
+const char *szTestString = "Copyright (c) Microsoft";
+
+const DWORD EXIT_OK_CODE = 100;
+const DWORD EXIT_ERR_CODE1 = 101;
+const DWORD EXIT_ERR_CODE2 = 102;
+const DWORD EXIT_ERR_CODE3 = 103;
+const DWORD EXIT_ERR_CODE4 = 104;
+const DWORD EXIT_ERR_CODE5 = 105;
+
+#define BUF_LEN 64
+
+/*
+ * Take two wide strings representing file and directory names
+ * (dirName, fileName), join the strings with the appropriate path
+ * delimiter and populate a wide character buffer (absPathName) with
+ * the resulting string.
+ *
+ * Returns: The number of wide characters in the resulting string.
+ * 0 is returned on Error.
+ */
+int
+mkAbsoluteFilenameA (
+ LPSTR dirName,
+ DWORD dwDirLength,
+ LPCSTR fileName,
+ DWORD dwFileLength,
+ LPSTR absPathName )
+{
+ extern const char *szPathDelimA;
+
+ DWORD sizeDN;
+ DWORD sizeFN;
+ DWORD sizeAPN;
+
+ sizeDN = strlen( dirName );
+ sizeFN = strlen( fileName );
+ sizeAPN = (sizeDN + 1 + sizeFN + 1);
+
+ /* insure ((dirName + DELIM + fileName + \0) =< _MAX_PATH ) */
+ if ( sizeAPN > _MAX_PATH )
+ {
+ return ( 0 );
+ }
+
+ strncpy(absPathName, dirName, dwDirLength +1);
+ strcat(absPathName, szPathDelimA);
+ strcat(absPathName, fileName);
+
+ return (sizeAPN);
+
+}
diff --git a/src/pal/tests/palsuite/threading/CreateProcessA/test2/testinfo.dat b/src/pal/tests/palsuite/threading/CreateProcessA/test2/testinfo.dat
new file mode 100644
index 0000000000..23fcdf93ae
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CreateProcessA/test2/testinfo.dat
@@ -0,0 +1,20 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+# See the LICENSE file in the project root for more information.
+
+Version = 1.0
+Section = threading
+Function = CreateProcessA
+Name = PROCESS_INFORMATION and HANDLE Inheritance
+TYPE = DEFAULT
+EXE1 = parentprocess
+EXE2 = childprocess
+Description
+= Test the following features of CreateProcessA:
+= - Check to see if hProcess & hThread are set in
+= return PROCESS_INFORMATION structure
+= - Check to see if stdin, stdout, & stderr handles
+= are used when STARTF_USESTDHANDLES is specified
+= in STARUPINFO flags and bInheritHandles = TRUE
+= - Check to see that proper arguments are passed to
+= child process