Я пишу тестовый драйвер интеграции для исполняемого файла в командной строке. Я управляю как драйвером, так и исполняемым файлом, поэтому я могу гарантировать гарантии их поведения - например, исполняемый файл никогда не читает из stdin, он просто принимает аргументы командной строки, делает свою работу, а затем записывает вывод в файл и stdout.Чтение из трубы случайно не работает
Я хочу зафиксировать как код выхода, так и стандартный вывод процесса для проверки.
Вот код, который я использую:
#include <Windows.h>
class Pipe {
HANDLE ReadHandle;
HANDLE writehandle;
public:
Pipe() {
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
CreatePipe(&ReadHandle, &writehandle, &saAttr, 0);
}
HANDLE WriteHandle() {
return writehandle;
}
std::string Contents() {
CloseHandle(writehandle);
DWORD dwRead;
CHAR chBuf[1024];
BOOL bSuccess = FALSE;
std::string result;
for (;;)
{
bSuccess = ReadFile(ReadHandle, chBuf, 1024, &dwRead, NULL);
if (!bSuccess) break;
result += std::string(chBuf, chBuf + dwRead);
if (dwRead < 1024)
break;
}
return result;
}
~Pipe() {
CloseHandle(ReadHandle);
}
};
Wide::Driver::ProcessResult Wide::Driver::StartAndWaitForProcess(std::string name, std::vector<std::string> args, Util::optional<unsigned> timeout)
{
ProcessResult result;
Pipe stdoutpipe;
PROCESS_INFORMATION info = { 0 };
STARTUPINFO startinfo = { sizeof(STARTUPINFO) };
std::string final_args = name;
for (auto arg : args)
final_args += " " + arg;
startinfo.hStdOutput = stdoutpipe.WriteHandle();
startinfo.hStdError = INVALID_HANDLE_VALUE;
startinfo.hStdInput = INVALID_HANDLE_VALUE;
startinfo.dwFlags |= STARTF_USESTDHANDLES;
auto proc = CreateProcess(
name.c_str(),
&final_args[0],
nullptr,
nullptr,
TRUE,
NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,
nullptr,
nullptr,
&startinfo,
&info
);
if (!proc) {
DWORD dw = GetLastError();
const char* message;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&message, 0, nullptr);
std::string err = message;
LocalFree((void*)message);
throw std::runtime_error(err);
}
if (timeout == 0)
timeout = INFINITE;
result.std_out = stdoutpipe.Contents();
if (WaitForSingleObject(info.hProcess, timeout ? *timeout : INFINITE) == WAIT_TIMEOUT)
TerminateProcess(info.hProcess, 1);
DWORD exit_code;
GetExitCodeProcess(info.hProcess, &exit_code);
CloseHandle(info.hProcess);
CloseHandle(info.hThread);
result.exitcode = exit_code;
if (exit_code != 0)
return result;
return result;
}
У меня есть 259 интеграционные тесты, которые я бегу на этом пути. Некоторые занимают больше времени, чем другие. Когда я запускаю пакет, примерно 1-3 будет терпеть неудачу - каждый раз каждый раз. Я посмотрел на результат в отладчике, и stdout отключен на полпути. Если я не пытаюсь захватить stdout, все тесты будут успешными каждый раз, поэтому я знаю, что он основан на захвате stdout.
Тайм-аут указан, но это очень щедрый 60 секунд - намного дольше, чем обычно проводятся тесты. Я создаю новый процесс для каждого теста.
Как я могу захватить stdout более надежным способом, не получая случайных сбоев?
Как последнее замечание, для запуска сбоя в отладчике требуется много времени, чтобы потребовать некоторое время для обслуживания любых запросов для получения дополнительной информации.
Да, ReadFile() на трубе всегда будет возвращаться, когда он достигнет конца соответствующего WriteFile().Если длина строк записи не превышает размер буфера, это означает, что он вернет меньше запрошенного количества байтов, хотя канал еще не закрыт, и я * думаю *, даже если все еще ожидающие данные из более позднего написать. –