You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1244 lines
25 KiB
1244 lines
25 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
vststprocess.cxx
|
|
|
|
Abstract:
|
|
|
|
Implementation of test message classes
|
|
|
|
|
|
Brian Berkowitz [brianb] 05/24/2000
|
|
|
|
TBD:
|
|
|
|
|
|
Revision History:
|
|
|
|
Name Date Comments
|
|
brianb 05/24/2000 Created
|
|
|
|
--*/
|
|
|
|
|
|
#include <stdafx.h>
|
|
#include <vststprocess.hxx>
|
|
#include <vststmsg.hxx>
|
|
#include <tstiniconfig.hxx>
|
|
|
|
|
|
|
|
void LogUnexpectedFailure(LPCWSTR, ...);
|
|
|
|
|
|
CVsTstProcessList::CVsTstProcessList() :
|
|
m_processId(0L),
|
|
m_processList(NULL),
|
|
m_bcsProcessListInitialized(false),
|
|
m_bNoMoreProcessesAreCreated(false),
|
|
m_bShuttingDown(true)
|
|
{
|
|
}
|
|
|
|
CVsTstProcessList::~CVsTstProcessList()
|
|
{
|
|
m_bShuttingDown = true;
|
|
|
|
if (WaitForSingleObject(m_hThreadMonitor, INFINITE) == WAIT_FAILED)
|
|
{
|
|
LogUnexpectedFailure(L"WaitForSingleObject failed for reason %d.\n", GetLastError());
|
|
// wait for 1 minute for everything to shut down
|
|
Sleep(60000);
|
|
}
|
|
|
|
CloseHandle(m_hThreadMonitor);
|
|
if (m_bcsProcessListInitialized)
|
|
m_csProcessList.Term();
|
|
}
|
|
|
|
|
|
void CVsTstProcessList::LinkIntoProcessList(CVsTstProcess *process)
|
|
{
|
|
m_csProcessList.Lock();
|
|
VSTST_ASSERT(m_processList == NULL || m_processList->m_prev == NULL);
|
|
process->m_next = m_processList;
|
|
process->m_prev = NULL;
|
|
|
|
if (m_processList != NULL)
|
|
m_processList->m_prev = process;
|
|
|
|
m_processList = process;
|
|
|
|
m_csProcessList.Unlock();
|
|
}
|
|
|
|
|
|
void CVsTstProcessList::UnlinkFromProcessList(CVsTstProcess *process)
|
|
{
|
|
m_csProcessList.Lock();
|
|
VSTST_ASSERT(m_processList);
|
|
VSTST_ASSERT(m_processList->m_prev == NULL);
|
|
|
|
if (process == m_processList)
|
|
{
|
|
m_processList = process->m_next;
|
|
if (m_processList)
|
|
{
|
|
VSTST_ASSERT(m_processList->m_prev == process);
|
|
m_processList->m_prev = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VSTST_ASSERT(process->m_prev != NULL);
|
|
process->m_prev->m_next = process->m_next;
|
|
if (process->m_next)
|
|
{
|
|
VSTST_ASSERT(process->m_next->m_prev == process);
|
|
process->m_next->m_prev = process->m_prev;
|
|
}
|
|
}
|
|
|
|
m_csProcessList.Unlock();
|
|
delete process;
|
|
}
|
|
|
|
HRESULT CVsTstProcessList::Initialize
|
|
(
|
|
UINT maxLifetime,
|
|
HANDLE hevtTermination
|
|
)
|
|
{
|
|
try
|
|
{
|
|
m_csProcessList.Init();
|
|
m_bcsProcessListInitialized = true;
|
|
}
|
|
catch(...)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
time_t timeCur;
|
|
time(&timeCur);
|
|
m_timeTerminateTest = timeCur + maxLifetime;
|
|
m_hevtTermination = hevtTermination;
|
|
|
|
DWORD tid;
|
|
m_hThreadMonitor = CreateThread
|
|
(
|
|
NULL,
|
|
256*1024,
|
|
StartupMonitorThread,
|
|
this,
|
|
0,
|
|
&tid
|
|
);
|
|
|
|
if (m_hThreadMonitor == NULL)
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
DWORD CVsTstProcessList::StartupMonitorThread(void *pv)
|
|
{
|
|
CVsTstProcessList *processList = (CVsTstProcessList *) pv;
|
|
|
|
try
|
|
{
|
|
processList->MonitorFunc();
|
|
if (processList->m_hevtTermination)
|
|
{
|
|
if (!SetEvent(processList->m_hevtTermination))
|
|
LogUnexpectedFailure(L"SetEvent failed with error %d", GetLastError());
|
|
}
|
|
}
|
|
catch(...)
|
|
{
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
void CVsTstProcessList::MonitorFunc()
|
|
{
|
|
while(TRUE)
|
|
{
|
|
time_t timeNow;
|
|
Sleep(5000);
|
|
|
|
time(&timeNow);
|
|
if (timeNow > m_timeTerminateTest)
|
|
{
|
|
StartTermination();
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
m_csProcessList.Lock();
|
|
|
|
// check if there are no processes and no more proceses
|
|
// are created
|
|
if (m_processList == NULL && m_bNoMoreProcessesAreCreated)
|
|
{
|
|
m_csProcessList.Unlock();
|
|
break;
|
|
}
|
|
|
|
CVsTstProcess *process = m_processList;
|
|
while(process != NULL)
|
|
{
|
|
CVsTstProcess *processNext = process->m_next;
|
|
|
|
m_csProcessList.Unlock();
|
|
if (process->HasProcessExpired())
|
|
process->DoTerminateProcess(false);
|
|
|
|
m_csProcessList.Lock();
|
|
process = processNext;
|
|
}
|
|
|
|
m_csProcessList.Unlock();
|
|
}
|
|
}
|
|
}
|
|
|
|
// start process termination
|
|
void CVsTstProcessList::StartTermination()
|
|
{
|
|
m_bNoMoreProcessesAreCreated = true;
|
|
|
|
while(TRUE)
|
|
{
|
|
m_csProcessList.Lock();
|
|
CVsTstProcess *process = m_processList;
|
|
|
|
if (process == NULL)
|
|
{
|
|
m_csProcessList.Unlock();
|
|
return;
|
|
}
|
|
|
|
while(process != NULL)
|
|
{
|
|
CVsTstProcess *processNext = process->m_next;
|
|
m_csProcessList.Unlock();
|
|
process->DoTerminateProcess(m_bShuttingDown);
|
|
m_csProcessList.Lock();
|
|
process = processNext;
|
|
}
|
|
|
|
// unlock list before going to sleep
|
|
m_csProcessList.Unlock();
|
|
Sleep(5000);
|
|
}
|
|
}
|
|
|
|
|
|
// constructor
|
|
CVsTstProcess::CVsTstProcess(CVsTstProcessList *list) :
|
|
m_hProcess(NULL),
|
|
m_hevtGracefullyTerminate(NULL),
|
|
m_hevtNotifyProcessTermination(NULL),
|
|
m_pvPrivateData(NULL),
|
|
m_next(NULL),
|
|
m_prev(NULL),
|
|
m_bLinked(false),
|
|
m_processList(list),
|
|
m_bGracefullyTerminated(false)
|
|
{
|
|
}
|
|
|
|
// deastructor
|
|
CVsTstProcess::~CVsTstProcess()
|
|
{
|
|
if (m_bLinked)
|
|
m_processList->UnlinkFromProcessList(this);
|
|
|
|
if (m_hevtGracefullyTerminate)
|
|
CloseHandle(m_hevtGracefullyTerminate);
|
|
}
|
|
|
|
|
|
HRESULT CVsTstProcess::InitializeConformingExe
|
|
(
|
|
ULONGLONG processId,
|
|
VSTST_PROCESS_TYPE type,
|
|
VSTST_ACCOUNT_TYPE account,
|
|
LPCWSTR wszExecutableName,
|
|
LPCWSTR wszScenarioFile,
|
|
LPCWSTR wszSection,
|
|
DWORD seed,
|
|
UINT lifetime,
|
|
bool bAbnormalTermination,
|
|
HANDLE hevtNotify
|
|
)
|
|
{
|
|
WCHAR buf[256];
|
|
SECURITY_ATTRIBUTES attributes;
|
|
|
|
m_processId = processId;
|
|
m_type = type;
|
|
m_account = account;
|
|
m_bConforming = true;
|
|
|
|
attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
attributes.lpSecurityDescriptor = NULL;
|
|
attributes.bInheritHandle = true;
|
|
m_hevtGracefullyTerminate = CreateEvent(&attributes, TRUE, FALSE, NULL);
|
|
if (m_hevtGracefullyTerminate == NULL)
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
UINT pidLow = (UINT) (m_processId & 0xffffffff);
|
|
UINT pidHigh = (UINT) (m_processId >> 32);
|
|
swprintf
|
|
(
|
|
buf,
|
|
L"%s -pidLow=%d -pidHigh=%d -scenario={%s} -section={%s} -seed=%u -lifetime=%d -event=0x%08x",
|
|
wszExecutableName,
|
|
pidLow,
|
|
pidHigh,
|
|
wszScenarioFile,
|
|
wszSection,
|
|
seed,
|
|
lifetime,
|
|
m_hevtGracefullyTerminate
|
|
);
|
|
|
|
PROCESS_INFORMATION info;
|
|
STARTUPINFO startup;
|
|
memset(&startup, 0, sizeof(STARTUPINFO));
|
|
startup.cb = sizeof(STARTUPINFO);
|
|
if (!CreateProcess
|
|
(
|
|
NULL,
|
|
buf,
|
|
NULL,
|
|
NULL,
|
|
TRUE,
|
|
NORMAL_PRIORITY_CLASS,
|
|
NULL,
|
|
NULL,
|
|
&startup,
|
|
&info
|
|
))
|
|
{
|
|
DWORD dwErr = GetLastError();
|
|
CloseHandle(m_hevtGracefullyTerminate);
|
|
m_hevtGracefullyTerminate = NULL;
|
|
return HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
|
|
m_hProcess = info.hProcess;
|
|
m_hevtNotifyProcessTermination = hevtNotify;
|
|
m_bAbnormallyTerminate = bAbnormalTermination;
|
|
time(&m_timeProcessStart);
|
|
m_timeProcessTerminate = m_timeProcessStart + lifetime;
|
|
m_processList->LinkIntoProcessList(this);
|
|
return S_OK;
|
|
}
|
|
|
|
// startup a process associated with a non-conforming executable
|
|
HRESULT CVsTstProcess::InitializeNonConformingExe
|
|
(
|
|
ULONGLONG processId,
|
|
VSTST_PROCESS_TYPE type,
|
|
VSTST_ACCOUNT_TYPE account,
|
|
LPCWSTR wszCommandLine,
|
|
UINT lifetime,
|
|
HANDLE hevtNotify
|
|
)
|
|
{
|
|
m_processId = processId;
|
|
m_type = type;
|
|
m_account = account;
|
|
m_bConforming = false;
|
|
|
|
PROCESS_INFORMATION info;
|
|
STARTUPINFO startup;
|
|
|
|
memset(&startup, 0, sizeof(STARTUPINFO));
|
|
startup.cb = sizeof(STARTUPINFO);
|
|
if (!CreateProcess
|
|
(
|
|
NULL,
|
|
(LPWSTR) wszCommandLine,
|
|
NULL,
|
|
NULL,
|
|
TRUE,
|
|
NORMAL_PRIORITY_CLASS,
|
|
NULL,
|
|
NULL,
|
|
&startup,
|
|
&info
|
|
))
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
m_hProcess = info.hProcess;
|
|
m_hevtNotifyProcessTermination = hevtNotify;
|
|
m_bAbnormallyTerminate = true;
|
|
time(&m_timeProcessStart);
|
|
m_timeProcessTerminate = m_timeProcessStart + lifetime;
|
|
m_processList->LinkIntoProcessList(this);
|
|
return S_OK;
|
|
}
|
|
|
|
void CVsTstProcess::DoTerminateProcess(bool bAgressive)
|
|
{
|
|
if (m_bAbnormallyTerminate)
|
|
ForceTerminateProcess();
|
|
else if (!m_bGracefullyTerminated)
|
|
GracefullyTerminateProcess(bAgressive);
|
|
else
|
|
{
|
|
// see if process is terminated
|
|
DWORD dwErr = WaitForSingleObject(m_hProcess, 0);
|
|
|
|
if (dwErr == WAIT_FAILED)
|
|
ForceTerminateProcess();
|
|
else if (dwErr == WAIT_TIMEOUT)
|
|
{
|
|
time_t timeCur;
|
|
time(&timeCur);
|
|
if (m_timeProcessTerminationExpiration < timeCur)
|
|
// forcefully terminate the process
|
|
ForceTerminateProcess();
|
|
|
|
// if we are shutting down, give the process 10 seconds
|
|
// to shut down before we forcefully terminate it
|
|
if (bAgressive && m_timeProcessTerminationExpiration > timeCur + 10)
|
|
m_timeProcessTerminationExpiration = timeCur + 10;
|
|
}
|
|
else
|
|
CleanupProcess();
|
|
}
|
|
}
|
|
|
|
|
|
// has process expired
|
|
bool CVsTstProcess::HasProcessExpired()
|
|
{
|
|
// see if process is terminated
|
|
DWORD dwErr = WaitForSingleObject(m_hProcess, 0);
|
|
if (dwErr == WAIT_OBJECT_0)
|
|
{
|
|
CleanupProcess();
|
|
return false;
|
|
}
|
|
|
|
time_t timeVal;
|
|
|
|
time(&timeVal);
|
|
|
|
return timeVal > m_timeProcessTerminate;
|
|
}
|
|
|
|
|
|
// cleanup process at termination
|
|
void CVsTstProcess::CleanupProcess()
|
|
{
|
|
if (m_hevtNotifyProcessTermination)
|
|
{
|
|
if (!SetEvent(m_hevtNotifyProcessTermination))
|
|
LogUnexpectedFailure(L"SetEvent failed with error %d", GetLastError());
|
|
}
|
|
|
|
m_processList->UnlinkFromProcessList(this);
|
|
}
|
|
|
|
// forceably terminate the process
|
|
void CVsTstProcess::ForceTerminateProcess()
|
|
{
|
|
if (!TerminateProcess(m_hProcess, 0))
|
|
LogUnexpectedFailure(L"Terminate process failed with error %d", GetLastError());
|
|
|
|
CleanupProcess();
|
|
}
|
|
|
|
// try terminating the process gracefully
|
|
void CVsTstProcess::GracefullyTerminateProcess(bool bAgressive)
|
|
{
|
|
VSTST_ASSERT(m_hevtGracefullyTerminate);
|
|
|
|
if (!SetEvent(m_hevtGracefullyTerminate))
|
|
LogUnexpectedFailure(L"SetEvent failed with error %d", GetLastError());
|
|
|
|
m_bGracefullyTerminated = true;
|
|
time(&m_timeProcessStartTermination);
|
|
|
|
// allow 2 minutes for the process to gracefully terminate, 10 seconds if
|
|
// if we are agressive
|
|
m_timeProcessTerminationExpiration = m_timeProcessStartTermination + bAgressive ? 10 : 120;
|
|
}
|
|
|
|
|
|
HRESULT CVsTstProcessList::CreateConformingExe
|
|
(
|
|
VSTST_PROCESS_TYPE type,
|
|
VSTST_ACCOUNT_TYPE account,
|
|
LPCWSTR wszExecutableName,
|
|
LPCWSTR wszScenarioFile,
|
|
LPCWSTR wszSectionName,
|
|
DWORD seed,
|
|
UINT lifetime,
|
|
bool bAbnormalTerminate,
|
|
HANDLE hevtNotify,
|
|
ULONGLONG &processId
|
|
)
|
|
{
|
|
processId = AllocateProcessId();
|
|
CVsTstProcess *process = new CVsTstProcess(this);
|
|
if (process == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
HRESULT hr = process->InitializeConformingExe
|
|
(
|
|
processId,
|
|
type,
|
|
account,
|
|
wszExecutableName,
|
|
wszScenarioFile,
|
|
wszSectionName,
|
|
seed,
|
|
lifetime,
|
|
bAbnormalTerminate,
|
|
hevtNotify
|
|
);
|
|
|
|
if (FAILED(hr))
|
|
delete process;
|
|
|
|
return hr;
|
|
}
|
|
|
|
// create a process for a non-conforming executable
|
|
HRESULT CVsTstProcessList::CreateNonConformingExe
|
|
(
|
|
VSTST_PROCESS_TYPE type,
|
|
VSTST_ACCOUNT_TYPE account,
|
|
LPCWSTR wszCommandLine,
|
|
UINT lifetime,
|
|
HANDLE hevtNotify,
|
|
ULONGLONG &processId
|
|
)
|
|
{
|
|
processId = AllocateProcessId();
|
|
CVsTstProcess *process = new CVsTstProcess(this);
|
|
if (process == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
HRESULT hr = process->InitializeNonConformingExe
|
|
(
|
|
processId,
|
|
type,
|
|
account,
|
|
wszCommandLine,
|
|
lifetime,
|
|
hevtNotify
|
|
);
|
|
|
|
if (FAILED(hr))
|
|
delete process;
|
|
|
|
return hr;
|
|
}
|
|
|
|
// find a specific process
|
|
CVsTstProcess *CVsTstProcessList::FindProcess(ULONGLONG processId)
|
|
{
|
|
m_csProcessList.Lock();
|
|
CVsTstProcess *process = m_processList;
|
|
while(process != NULL)
|
|
{
|
|
if (process->m_processId == processId)
|
|
{
|
|
m_csProcessList.Unlock();
|
|
return process;
|
|
}
|
|
|
|
process = process->m_next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
// get private data associated with a process
|
|
void *CVsTstProcessList::GetProcessPrivateData(LONGLONG processId)
|
|
{
|
|
CVsTstProcess *process = FindProcess(processId);
|
|
|
|
if (process == NULL)
|
|
return NULL;
|
|
else
|
|
return process->GetPrivateData();
|
|
}
|
|
|
|
// set private data associated with a process
|
|
void CVsTstProcessList::SetProcessPrivateData(LONGLONG processId, void *pv)
|
|
{
|
|
CVsTstProcess *process = FindProcess(processId);
|
|
|
|
if (process != NULL)
|
|
process->SetPrivateData(pv);
|
|
}
|
|
|
|
// indicate that no more processes will be created
|
|
void CVsTstProcessList::EndOfProcessCreation()
|
|
{
|
|
m_bNoMoreProcessesAreCreated = true;
|
|
}
|
|
|
|
ULONGLONG CVsTstProcessList::AllocateProcessId()
|
|
{
|
|
m_csProcessList.Lock();
|
|
ULONGLONG processId = ++m_processId;
|
|
m_csProcessList.Unlock();
|
|
|
|
return processId;
|
|
}
|
|
|
|
static VSTST_CMDDESC x_rgCommands[] =
|
|
{
|
|
{
|
|
L"scenario",
|
|
VSTST_CT_STRING,
|
|
VSTST_CP_SCENARIOFILE
|
|
},
|
|
{
|
|
L"testseries",
|
|
VSTST_CT_STRING,
|
|
VSTST_CP_TESTSERIES
|
|
},
|
|
{
|
|
L"section",
|
|
VSTST_CT_STRING,
|
|
VSTST_CP_SECTIONNAME
|
|
},
|
|
{
|
|
L"seed",
|
|
VSTST_CT_UINT,
|
|
VSTST_CP_SEED
|
|
},
|
|
{
|
|
L"lifetime",
|
|
VSTST_CT_UINT,
|
|
VSTST_CP_LIFETIME,
|
|
},
|
|
{
|
|
L"event",
|
|
VSTST_CT_HANDLE,
|
|
VSTST_CP_TERMINATIONEVENT
|
|
},
|
|
{
|
|
L"pidLow",
|
|
VSTST_CT_UINT,
|
|
VSTST_CP_PIDLOW
|
|
},
|
|
{
|
|
L"pidHigh",
|
|
VSTST_CT_UINT,
|
|
VSTST_CP_PIDHIGH
|
|
}
|
|
|
|
};
|
|
|
|
static UINT x_cCommands = sizeof(x_rgCommands)/sizeof(x_rgCommands[0]);
|
|
|
|
// constructor
|
|
CVsTstParams::CVsTstParams() :
|
|
m_wszScenarioFile(NULL),
|
|
m_wszSectionName(NULL),
|
|
m_wszTestSeries(NULL),
|
|
m_supplied(0)
|
|
{
|
|
}
|
|
|
|
// destructor
|
|
CVsTstParams::~CVsTstParams()
|
|
{
|
|
delete m_wszScenarioFile;
|
|
delete m_wszSectionName;
|
|
}
|
|
|
|
// parse the command line
|
|
bool CVsTstParams::ParseCommandLine(WCHAR **argv, UINT argc)
|
|
{
|
|
for(UINT iarg = 1; iarg < argc; iarg++)
|
|
{
|
|
WCHAR *wsz = argv[iarg];
|
|
|
|
if (wsz[0] != '-')
|
|
return false;
|
|
|
|
wsz++;
|
|
|
|
WCHAR *pwc = wsz;
|
|
while(*pwc != L'\0' && *pwc != L'=')
|
|
pwc++;
|
|
|
|
if (*pwc != '=')
|
|
return false;
|
|
|
|
*pwc++ = '\0';
|
|
for(UINT icmd = 0; icmd < x_cCommands; icmd++)
|
|
{
|
|
if (wcscmp(wsz, x_rgCommands[icmd].wszParam) == 0)
|
|
break;
|
|
}
|
|
|
|
if (icmd == x_cCommands)
|
|
return false;
|
|
|
|
VSTST_CMDDESC *pCommand = &x_rgCommands[icmd];
|
|
UINT ulVal;
|
|
LPWSTR wszVal;
|
|
HANDLE hVal;
|
|
|
|
switch(pCommand->type)
|
|
{
|
|
default:
|
|
VSTST_ASSERT(FALSE && "shouldn't get here");
|
|
return false;
|
|
|
|
case VSTST_CT_UINT:
|
|
if (!ParseUINT(pwc, &ulVal))
|
|
return false;
|
|
|
|
switch(pCommand->param)
|
|
{
|
|
default:
|
|
VSTST_ASSERT(FALSE && "shouldn't get here");
|
|
return false;
|
|
|
|
case VSTST_CP_SEED:
|
|
SetSeed(ulVal);
|
|
break;
|
|
|
|
case VSTST_CP_LIFETIME:
|
|
SetLifetime(ulVal);
|
|
break;
|
|
|
|
case VSTST_CP_PIDLOW:
|
|
SetPidLow(ulVal);
|
|
break;
|
|
|
|
case VSTST_CP_PIDHIGH:
|
|
SetPidHigh(ulVal);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case VSTST_CT_HANDLE:
|
|
if (!ParseHandle(pwc, &hVal))
|
|
return false;
|
|
|
|
if (pCommand->param == VSTST_CP_TERMINATIONEVENT)
|
|
SetTerminationEvent(hVal);
|
|
else
|
|
{
|
|
VSTST_ASSERT(FALSE && "shouldn't get here");
|
|
return false;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case VSTST_CT_STRING:
|
|
if (!ParseString(pwc, &wszVal))
|
|
return false;
|
|
|
|
switch(pCommand->param)
|
|
{
|
|
default:
|
|
VSTST_ASSERT(FALSE && "shouldn't get here");
|
|
return false;
|
|
|
|
case VSTST_CP_SCENARIOFILE:
|
|
SetScenarioFile(wszVal);
|
|
break;
|
|
|
|
case VSTST_CP_TESTSERIES:
|
|
SetTestSeries(wszVal);
|
|
break;
|
|
|
|
case VSTST_CP_SECTIONNAME:
|
|
SetSectionName(wszVal);
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_hTestTerminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (m_hTerminationEvent == NULL)
|
|
return false;
|
|
|
|
m_supplied |= VSTST_CP_TESTTERMINATIONEVENT;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CVsTstParams::ParseUINT(LPCWSTR wsz, UINT *pulVal)
|
|
{
|
|
UINT ulVal = 0;
|
|
|
|
if (*wsz == L'\0')
|
|
return false;
|
|
|
|
do
|
|
{
|
|
WCHAR wc = *wsz++;
|
|
|
|
if (wc < L'0' || wc > L'9')
|
|
return false;
|
|
|
|
UINT ulValNew = ulVal * 10 + (wc - L'0');
|
|
if (ulValNew < ulVal)
|
|
return false;
|
|
|
|
ulVal = ulValNew;
|
|
} while(*wsz != L'\0');
|
|
|
|
*pulVal = ulVal;
|
|
return true;
|
|
}
|
|
|
|
// parse a string value of the form "..."
|
|
bool CVsTstParams::ParseString(LPCWSTR wsz, LPWSTR *pwszVal)
|
|
{
|
|
if (*wsz != L'{')
|
|
return false;
|
|
|
|
wsz++;
|
|
LPCWSTR wszStart = wsz;
|
|
|
|
while(*wsz != L'\0' && *wsz != '}')
|
|
wsz++;
|
|
|
|
if (*wsz != L'}')
|
|
return false;
|
|
|
|
UINT cwc = (UINT)( wsz - wszStart );
|
|
|
|
LPWSTR wszRet = new WCHAR[cwc + 1];
|
|
memcpy(wszRet, wszStart, cwc * sizeof(WCHAR));
|
|
wszRet[cwc] = L'\0';
|
|
*pwszVal = wszRet;
|
|
return true;
|
|
}
|
|
|
|
bool CVsTstParams::ParseHandle(LPCWSTR wsz, HANDLE *phVal)
|
|
{
|
|
UINT ulVal = 0;
|
|
|
|
if (*wsz++ != L'0')
|
|
return false;
|
|
|
|
if (*wsz++ != L'x')
|
|
return false;
|
|
|
|
if (*wsz == L'\0')
|
|
return false;
|
|
|
|
do
|
|
{
|
|
WCHAR wc = *wsz++;
|
|
UINT digit;
|
|
|
|
if (wc >= L'0' && wc <= L'9')
|
|
digit = wc - L'0';
|
|
else if (wc >= L'a' && wc <= L'f')
|
|
digit = wc - L'a' + 10;
|
|
else if (wc >= L'A' && wc <= L'F')
|
|
digit = wc - L'A' + 10;
|
|
else
|
|
return false;
|
|
|
|
if (ulVal & 0xf0000000)
|
|
return false;
|
|
|
|
ulVal = ulVal << 4 | digit;
|
|
} while(*wsz != L'\0');
|
|
|
|
*phVal = (HANDLE)(ULONGLONG) ulVal;
|
|
return true;
|
|
}
|
|
|
|
bool CVsTstParams::GetScenarioFileName(LPCWSTR *pwszScenarioFile)
|
|
{
|
|
if ((m_supplied & VSTST_CP_SCENARIOFILE) == 0)
|
|
{
|
|
*pwszScenarioFile = NULL;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
*pwszScenarioFile = m_wszScenarioFile;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool CVsTstParams::GetTestSeries(LPCWSTR *pwszTestSeries)
|
|
{
|
|
if ((m_supplied & VSTST_CP_TESTSERIES) == 0)
|
|
{
|
|
*pwszTestSeries = NULL;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
*pwszTestSeries = m_wszTestSeries;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool CVsTstParams::GetSectionName(LPCWSTR *pwszSectionName)
|
|
{
|
|
if ((m_supplied & VSTST_CP_SECTIONNAME) == 0)
|
|
{
|
|
*pwszSectionName = NULL;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
*pwszSectionName = m_wszSectionName;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
bool CVsTstParams::GetRandomSeed(UINT *pSeed)
|
|
{
|
|
if ((m_supplied & VSTST_CP_SEED) == 0)
|
|
return false;
|
|
else
|
|
{
|
|
*pSeed = m_seed;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool CVsTstParams::GetLifetime(UINT *pLifetime)
|
|
{
|
|
if ((m_supplied & VSTST_CP_LIFETIME) == 0)
|
|
return false;
|
|
else
|
|
{
|
|
*pLifetime = m_lifetime;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool CVsTstParams::GetTerminationEvent(HANDLE *pHandle)
|
|
{
|
|
if ((m_supplied & VSTST_CP_TERMINATIONEVENT) == 0)
|
|
{
|
|
*pHandle = NULL;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
*pHandle = m_hTerminationEvent;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool CVsTstParams::GetTestTerminationEvent(HANDLE *pHandle)
|
|
{
|
|
if ((m_supplied & VSTST_CP_TESTTERMINATIONEVENT) == 0)
|
|
{
|
|
*pHandle = NULL;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
*pHandle = m_hTestTerminationEvent;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool CVsTstParams::GetProcessId(ULONGLONG *pid)
|
|
{
|
|
if ((m_supplied & VSTST_CP_PIDLOW) == 0)
|
|
return false;
|
|
else if ((m_supplied & VSTST_CP_PIDHIGH) == 0)
|
|
{
|
|
*pid = m_pidLow;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
*pid = (((ULONGLONG) m_pidHigh) << 32) + m_pidLow;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void CVsTstParams::SetSeed(UINT ulVal)
|
|
{
|
|
if (ulVal != 0)
|
|
{
|
|
m_supplied |= VSTST_CP_SEED;
|
|
m_seed = ulVal;
|
|
}
|
|
}
|
|
|
|
void CVsTstParams::SetLifetime(UINT ulVal)
|
|
{
|
|
if (ulVal != 0)
|
|
{
|
|
m_supplied |= VSTST_CP_LIFETIME;
|
|
m_lifetime = ulVal;
|
|
}
|
|
}
|
|
|
|
void CVsTstParams::SetPidLow(UINT ulVal)
|
|
{
|
|
m_supplied |= VSTST_CP_PIDLOW;
|
|
m_pidLow = ulVal;
|
|
}
|
|
|
|
void CVsTstParams::SetPidHigh(UINT ulVal)
|
|
{
|
|
m_supplied |= VSTST_CP_PIDHIGH;
|
|
m_pidHigh = ulVal;
|
|
}
|
|
|
|
void CVsTstParams::SetTerminationEvent(HANDLE hEvent)
|
|
{
|
|
if (hEvent != NULL)
|
|
{
|
|
m_supplied |= VSTST_CP_TERMINATIONEVENT;
|
|
m_hTerminationEvent = hEvent;
|
|
}
|
|
}
|
|
|
|
void CVsTstParams::SetScenarioFile(LPWSTR wsz)
|
|
{
|
|
m_supplied |= VSTST_CP_SCENARIOFILE;
|
|
m_wszScenarioFile = wsz;
|
|
}
|
|
|
|
void CVsTstParams::SetTestSeries(LPWSTR wsz)
|
|
{
|
|
m_supplied |= VSTST_CP_TESTSERIES;
|
|
m_wszTestSeries = wsz;
|
|
}
|
|
|
|
|
|
void CVsTstParams::SetSectionName(LPWSTR wsz)
|
|
{
|
|
m_supplied |= VSTST_CP_SECTIONNAME;
|
|
m_wszSectionName = wsz;
|
|
}
|
|
|
|
|
|
// arguments to the test termination thread
|
|
typedef struct _TerminationThreadParams
|
|
{
|
|
CVsTstParams *pParams;
|
|
IVsTstRunningTest *pTest;
|
|
} TerminationThreadParams;
|
|
|
|
static LPCWSTR x_wszVssTestController = L"VssTestController.";
|
|
static LPCWSTR x_wszVssTestRequestor = L"VssTestRequestor.";
|
|
static LPCWSTR x_wszVssTestWriter = L"VssTestWriter.";
|
|
|
|
// toplevel routine to run a test
|
|
// it parses the parameters
|
|
// creates a configuration file parser
|
|
// creates a message pipe to the server
|
|
// creates a thread to terminate the test
|
|
// calls the supplied test
|
|
//
|
|
HRESULT CVsTstRunner::RunVsTest
|
|
(
|
|
WCHAR **argv,
|
|
UINT argc,
|
|
IVsTstRunningTest *pTest,
|
|
bool bCreateTerminationThread
|
|
)
|
|
{
|
|
CVsTstParams params;
|
|
params.ParseCommandLine(argv, argc);
|
|
LPCWSTR wszScenarioFile, wszSectionName, wszQualifier;
|
|
|
|
if (!params.GetScenarioFileName(&wszScenarioFile) ||
|
|
!params.GetSectionName(&wszSectionName))
|
|
{
|
|
VSTST_ASSERT(FALSE && "missing scenario");
|
|
return E_FAIL;
|
|
}
|
|
|
|
EVsTstINISectionType sectionType;
|
|
|
|
if (memcmp(wszSectionName, x_wszVssTestWriter, wcslen(x_wszVssTestWriter) * sizeof(WCHAR)) == 0)
|
|
{
|
|
sectionType = eVsTstSectionType_TestWriter;
|
|
wszQualifier = wszSectionName + wcslen(x_wszVssTestWriter);
|
|
}
|
|
else if (memcmp(wszSectionName, x_wszVssTestRequestor, wcslen(x_wszVssTestRequestor)) == 0)
|
|
{
|
|
sectionType = eVsTstSectionType_TestRequesterApp;
|
|
wszQualifier = wszSectionName + wcslen(x_wszVssTestRequestor);
|
|
}
|
|
else if (memcmp(wszSectionName, x_wszVssTestController, wcslen(x_wszVssTestController)) == 0)
|
|
{
|
|
sectionType = eVsTstSectionType_TestCoordinator;
|
|
wszQualifier = wszSectionName + wcslen(x_wszVssTestController);
|
|
}
|
|
else
|
|
{
|
|
VSTST_ASSERT(FALSE && "bad test type");
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
DWORD tid;
|
|
|
|
TerminationThreadParams *pTTParm = NULL;
|
|
HANDLE hTerminationThread = NULL;
|
|
|
|
if (bCreateTerminationThread)
|
|
{
|
|
pTTParm = new TerminationThreadParams;
|
|
if (pTTParm == NULL)
|
|
{
|
|
VSTST_ASSERT(FALSE && "Out of memory");
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
pTTParm->pParams = ¶ms;
|
|
pTTParm->pTest = pTest;
|
|
hTerminationThread = CreateThread
|
|
(
|
|
NULL,
|
|
32*1024,
|
|
CVsTstRunner::StartupTerminationThread,
|
|
pTTParm,
|
|
0,
|
|
&tid
|
|
);
|
|
|
|
if(hTerminationThread == NULL)
|
|
{
|
|
VSTST_ASSERT(FALSE && "Thread creation failed");
|
|
delete pTTParm;
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
InitMsgTypes();
|
|
unsigned cwc = (unsigned)wcslen(wszScenarioFile);
|
|
WCHAR *wszFileName = new WCHAR[cwc * 2];
|
|
if (!ExpandEnvironmentStrings(wszScenarioFile, wszFileName, cwc * 2))
|
|
{
|
|
VSTST_ASSERT(FALSE && "ExpandEnvironmentStrings failed");
|
|
LogUnexpectedFailure(L"ExpandEnvironmentStrings failed with error %d", GetLastError());
|
|
throw E_UNEXPECTED;
|
|
}
|
|
|
|
CVsTstINIConfig config(sectionType, wszQualifier, FALSE, wszFileName);
|
|
CVsTstClientMsg client;
|
|
ULONGLONG processId;
|
|
if (!params.GetProcessId(&processId))
|
|
{
|
|
VSTST_ASSERT(FALSE && "no process id");
|
|
throw(E_FAIL);
|
|
}
|
|
|
|
client.Init(processId, 1024, false);
|
|
hr = pTest->RunTest(&config, &client, ¶ms);
|
|
}
|
|
catch(...)
|
|
{
|
|
VSTST_ASSERT(FALSE && "unexpected exception");
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
HANDLE hTestTerminationEvent;
|
|
|
|
if (!params.GetTestTerminationEvent(&hTestTerminationEvent))
|
|
LogUnexpectedFailure(L"Test Termination Event wasn't created");
|
|
else if (!SetEvent(hTestTerminationEvent))
|
|
LogUnexpectedFailure(L"SetEvent failed with error %d", GetLastError());
|
|
else
|
|
{
|
|
if (hTerminationThread != NULL &&
|
|
WaitForSingleObject(hTerminationThread, INFINITE) == WAIT_FAILED)
|
|
LogUnexpectedFailure(L"WaitForSingleObject failed with error %d", GetLastError());
|
|
}
|
|
|
|
if (hTerminationThread != NULL)
|
|
CloseHandle(hTerminationThread);
|
|
|
|
return hr;
|
|
}
|
|
|
|
// termination thread routine waits until either
|
|
// test lifetime is exceeded
|
|
// termination event is set by controller
|
|
// test termination event is set when test completes
|
|
//
|
|
DWORD CVsTstRunner::StartupTerminationThread(void *pv)
|
|
{
|
|
TerminationThreadParams *pTTParms = (TerminationThreadParams *) pv;
|
|
CVsTstParams *pParams = pTTParms->pParams;
|
|
IVsTstRunningTest *pTest = pTTParms->pTest;
|
|
|
|
delete pTTParms;
|
|
|
|
HANDLE hTestTerminationEvent;
|
|
HANDLE hTerminationEvent = NULL;
|
|
if (!pParams->GetTestTerminationEvent(&hTestTerminationEvent))
|
|
{
|
|
LogUnexpectedFailure(L"No Test Termination event was created.");
|
|
return 0xffffffff;
|
|
}
|
|
|
|
pParams->GetTerminationEvent(&hTerminationEvent);
|
|
|
|
UINT ulLifetime;
|
|
bool bLifetime = pParams->GetLifetime(&ulLifetime);
|
|
|
|
// more than one months worth of lifetime is infinite
|
|
if (bLifetime && ulLifetime < 3600 * 24 * 30)
|
|
ulLifetime = ulLifetime * 1000;
|
|
else
|
|
ulLifetime = INFINITE;
|
|
|
|
HANDLE rghEvt[2];
|
|
unsigned cEvt = 1;
|
|
|
|
rghEvt[0] = hTestTerminationEvent;
|
|
if (hTerminationEvent != NULL)
|
|
{
|
|
rghEvt[1] = hTerminationEvent;
|
|
cEvt++;
|
|
}
|
|
|
|
DWORD dwWait = WaitForMultipleObjects(cEvt, rghEvt, FALSE, ulLifetime);
|
|
|
|
if (dwWait == WAIT_FAILED)
|
|
LogUnexpectedFailure(L"Wait failed for reason %d", GetLastError());
|
|
else if (dwWait == WAIT_TIMEOUT)
|
|
pTest->ShutdownTest(VSTST_SR_LIFETIME_EXCEEDED);
|
|
else if (dwWait == WAIT_OBJECT_0 + 1)
|
|
pTest->ShutdownTest(VSTST_SR_CONTROLLER_SIGNALLED);
|
|
|
|
return 0;
|
|
}
|
|
|