void FileTests::TestWriteFileSuspended() { HANDLE const hOut = GetStdOutputHandle(); VERIFY_IS_NOT_NULL(hOut, L"Verify we have the standard output handle."); HANDLE const hIn = GetStdInputHandle(); VERIFY_IS_NOT_NULL(hIn, L"Verify we have the standard input handle."); CONSOLE_SCREEN_BUFFER_INFOEX csbiexOriginal = { 0 }; csbiexOriginal.cbSize = sizeof(csbiexOriginal); VERIFY_WIN32_BOOL_SUCCEEDED(GetConsoleScreenBufferInfoEx(hOut, &csbiexOriginal), L"Retrieve screen buffer properties at beginning of test."); DWORD dwMode = 0; VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleMode(hOut, dwMode), L"Set console mode for test."); COORD const coordZero = { 0 }; VERIFY_ARE_EQUAL(coordZero, csbiexOriginal.dwCursorPosition, L"Cursor should be at 0,0 in fresh buffer."); VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(hOut, "abc", 3, nullptr, nullptr), L"Test first write success."); PauseHelper(hIn); auto BlockedWrite = std::async([&] { Log::Comment(L"Background WriteFile scheduled."); VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(hOut, "def", 3, nullptr, nullptr), L"Test second write success."); }); UnpauseHelper(hIn); BlockedWrite.wait(); }
void FileTests::TestReadFileBasicEmpty() { HANDLE const hIn = GetStdInputHandle(); VERIFY_IS_NOT_NULL(hIn, L"Verify we have the standard input handle."); DWORD dwMode = 0; VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleMode(hIn, dwMode), L"Set input mode for test."); VERIFY_WIN32_BOOL_SUCCEEDED(FlushConsoleInputBuffer(hIn), L"Flush input buffer in preparation for test."); char ch = '\0'; Log::Comment(L"Queue background blocking read file operation."); auto BackgroundRead = std::async([&] { DWORD dwRead = 0; VERIFY_WIN32_BOOL_SUCCEEDED(ReadFile(hIn, &ch, 1, &dwRead, nullptr), L"Read file was successful."); VERIFY_ARE_EQUAL(0u, dwRead, L"We should have read nothing back. It should just return from Ctrl+Z"); }); char const chExpected = '\x1a'; // ctrl+z character Log::Comment(L"Send a key into the console."); SendFullKeyStrokeHelper(hIn, chExpected); Log::Comment(L"Wait for background to unblock."); BackgroundRead.wait(); VERIFY_ARE_EQUAL('\0', ch); }
void FileTests::TestWriteFileDisableNewlineAutoReturn() { bool fDisableAutoReturn; VERIFY_SUCCEEDED(TestData::TryGetValue(L"fDisableAutoReturn", fDisableAutoReturn)); bool fProcessedOn; VERIFY_SUCCEEDED(TestData::TryGetValue(L"fProcessedOn", fProcessedOn)); HANDLE const hOut = GetStdOutputHandle(); VERIFY_IS_NOT_NULL(hOut, L"Verify we have the standard output handle."); CONSOLE_SCREEN_BUFFER_INFOEX csbiexOriginal = { 0 }; csbiexOriginal.cbSize = sizeof(csbiexOriginal); VERIFY_WIN32_BOOL_SUCCEEDED(GetConsoleScreenBufferInfoEx(hOut, &csbiexOriginal), L"Retrieve screen buffer properties at beginning of test."); DWORD dwMode = 0; WI_SetFlagIf(dwMode, DISABLE_NEWLINE_AUTO_RETURN, fDisableAutoReturn); WI_SetFlagIf(dwMode, ENABLE_PROCESSED_OUTPUT, fProcessedOn); VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleMode(hOut, dwMode), L"Set console mode for test."); COORD const coordZero = { 0 }; VERIFY_ARE_EQUAL(coordZero, csbiexOriginal.dwCursorPosition, L"Cursor should be at 0,0 in fresh buffer."); CONSOLE_SCREEN_BUFFER_INFOEX csbiexBefore = { 0 }; csbiexBefore.cbSize = sizeof(csbiexBefore); CONSOLE_SCREEN_BUFFER_INFOEX csbiexAfter = { 0 }; csbiexAfter.cbSize = sizeof(csbiexAfter); COORD coordExpected = { 0 }; WriteFileHelper(hOut, csbiexBefore, csbiexAfter, "abc", 3); coordExpected = csbiexBefore.dwCursorPosition; coordExpected.X += 3; VERIFY_ARE_EQUAL(coordExpected, csbiexAfter.dwCursorPosition, L"Cursor should have moved right to the end of the text written."); WriteFileHelper(hOut, csbiexBefore, csbiexAfter, "\n", 1); if (fProcessedOn) { if (fDisableAutoReturn) { coordExpected = csbiexBefore.dwCursorPosition; coordExpected.Y += 1; } else { coordExpected = csbiexBefore.dwCursorPosition; coordExpected.Y += 1; coordExpected.X = 0; } } else { coordExpected = csbiexBefore.dwCursorPosition; coordExpected.X += 1; } VERIFY_ARE_EQUAL(coordExpected, csbiexAfter.dwCursorPosition, L"Cursor should move to expected position."); }
void FileTests::TestWriteFileVTProcessing() { bool fVtOn; VERIFY_SUCCEEDED(TestData::TryGetValue(L"fVtOn", fVtOn)); bool fProcessedOn; VERIFY_SUCCEEDED(TestData::TryGetValue(L"fProcessedOn", fProcessedOn)); HANDLE const hOut = GetStdOutputHandle(); VERIFY_IS_NOT_NULL(hOut, L"Verify we have the standard output handle."); CONSOLE_SCREEN_BUFFER_INFOEX csbiexOriginal = { 0 }; csbiexOriginal.cbSize = sizeof(csbiexOriginal); VERIFY_WIN32_BOOL_SUCCEEDED(GetConsoleScreenBufferInfoEx(hOut, &csbiexOriginal), L"Retrieve screen buffer properties at beginning of test."); DWORD dwFlags = 0; WI_SetFlagIf(dwFlags, ENABLE_VIRTUAL_TERMINAL_PROCESSING, fVtOn); WI_SetFlagIf(dwFlags, ENABLE_PROCESSED_OUTPUT, fProcessedOn); VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleMode(hOut, dwFlags), L"Turn on relevant flags for test."); COORD const coordZero = { 0 }; VERIFY_ARE_EQUAL(coordZero, csbiexOriginal.dwCursorPosition, L"Cursor should be at 0,0 in fresh buffer."); PCSTR pszTestString = "\x1b" "[14m"; DWORD const cchTest = (DWORD)strlen(pszTestString); CONSOLE_SCREEN_BUFFER_INFOEX csbiexBefore = { 0 }; csbiexBefore.cbSize = sizeof(csbiexBefore); CONSOLE_SCREEN_BUFFER_INFOEX csbiexAfter = { 0 }; csbiexAfter.cbSize = sizeof(csbiexAfter); WriteFileHelper(hOut, csbiexBefore, csbiexAfter, pszTestString, cchTest); // We only expect characters to be processed and not printed if both processed mode and VT mode are on. bool const fProcessedNotPrinted = fProcessedOn && fVtOn; if (fProcessedNotPrinted) { PCSTR pszReadBackExpected = " "; DWORD const cchReadBackExpected = (DWORD)strlen(pszReadBackExpected); VERIFY_ARE_EQUAL(csbiexBefore.dwCursorPosition, csbiexAfter.dwCursorPosition, L"Verify cursor didn't move because the VT sequence was processed instead of printed."); wistd::unique_ptr<char[]> pszReadBack; ReadBackHelper(hOut, coordZero, cchReadBackExpected, pszReadBack); VERIFY_ARE_EQUAL(String(pszReadBackExpected), String(pszReadBack.get()), L"Verify that nothing was printed into the buffer."); } else { COORD coordExpected = csbiexBefore.dwCursorPosition; coordExpected.X += (SHORT)cchTest; VERIFY_ARE_EQUAL(coordExpected, csbiexAfter.dwCursorPosition, L"Verify cursor moved as characters should have been emitted, not consumed."); wistd::unique_ptr<char[]> pszReadBack; ReadBackHelper(hOut, coordZero, cchTest, pszReadBack); VERIFY_ARE_EQUAL(String(pszTestString), String(pszReadBack.get()), L"Verify that original test string was printed into the buffer."); } }
void FileTests::TestWriteFileWrapEOL() { bool fFlagOn; VERIFY_SUCCEEDED(TestData::TryGetValue(L"fFlagOn", fFlagOn)); HANDLE const hOut = GetStdOutputHandle(); VERIFY_IS_NOT_NULL(hOut, L"Verify we have the standard output handle."); CONSOLE_SCREEN_BUFFER_INFOEX csbiexOriginal = { 0 }; csbiexOriginal.cbSize = sizeof(csbiexOriginal); VERIFY_WIN32_BOOL_SUCCEEDED(GetConsoleScreenBufferInfoEx(hOut, &csbiexOriginal), L"Retrieve screen buffer properties at beginning of test."); if (fFlagOn) { VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleMode(hOut, ENABLE_WRAP_AT_EOL_OUTPUT), L"Set wrap at EOL."); } else { VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleMode(hOut, 0), L"Make sure wrap at EOL is off."); } COORD const coordZero = { 0 }; VERIFY_ARE_EQUAL(coordZero, csbiexOriginal.dwCursorPosition, L"Cursor should be at 0,0 in fresh buffer."); // Fill first row of the buffer with Z characters until 1 away from the end. for (SHORT i = 0; i < csbiexOriginal.dwSize.X - 1; i++) { WriteFile(hOut, "Z", 1, nullptr, nullptr); } CONSOLE_SCREEN_BUFFER_INFOEX csbiexBefore = { 0 }; csbiexBefore.cbSize = sizeof(csbiexBefore); CONSOLE_SCREEN_BUFFER_INFOEX csbiexAfter = { 0 }; csbiexAfter.cbSize = sizeof(csbiexAfter); COORD coordExpected = { 0 }; VERIFY_WIN32_BOOL_SUCCEEDED(GetConsoleScreenBufferInfoEx(hOut, &csbiexBefore), L"Get cursor position information before attempting to wrap at end of line."); VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(hOut, "Y", 1, nullptr, nullptr), L"Write of final character in line succeeded."); VERIFY_WIN32_BOOL_SUCCEEDED(GetConsoleScreenBufferInfoEx(hOut, &csbiexAfter), L"Get cursor position information after attempting to wrap at end of line."); if (fFlagOn) { Log::Comment(L"Cursor should go down a row if we tried to print at end of line."); coordExpected = csbiexBefore.dwCursorPosition; coordExpected.Y++; coordExpected.X = 0; } else { Log::Comment(L"Cursor shouldn't move when printing at end of line."); coordExpected = csbiexBefore.dwCursorPosition; } VERIFY_ARE_EQUAL(coordExpected, csbiexAfter.dwCursorPosition, L"Verify cursor moved as expected based on flag state."); }
void FileTests::TestUtf8WriteFileInvalid() { Log::Comment(L"Backup original console codepage."); UINT const uiOriginalCP = GetConsoleOutputCP(); auto restoreOriginalCP = wil::scope_exit([&] { Log::Comment(L"Restore original console codepage."); SetConsoleOutputCP(uiOriginalCP); }); HANDLE const hOut = GetStdOutputHandle(); VERIFY_IS_NOT_NULL(hOut, L"Verify we have the standard output handle."); VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleOutputCP(CP_UTF8), L"Set output codepage to UTF8"); DWORD dwWritten; DWORD dwExpectedWritten; char* str; DWORD cbStr; // \x80 is an invalid UTF-8 continuation // \x40 is the @ symbol which is valid. str = "\x80\x40"; cbStr = (DWORD)strlen(str); dwWritten = 0; dwExpectedWritten = cbStr; VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(hOut, str, cbStr, &dwWritten, nullptr)); VERIFY_ARE_EQUAL(dwExpectedWritten, dwWritten); // \x80 is an invalid UTF-8 continuation // \x40 is the @ symbol which is valid. str = "\x80\x40\x40"; cbStr = (DWORD)strlen(str); dwWritten = 0; dwExpectedWritten = cbStr; VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(hOut, str, cbStr, &dwWritten, nullptr)); VERIFY_ARE_EQUAL(dwExpectedWritten, dwWritten); // \x80 is an invalid UTF-8 continuation // \x40 is the @ symbol which is valid. str = "\x80\x80\x80\x40"; cbStr = (DWORD)strlen(str); dwWritten = 0; dwExpectedWritten = cbStr; VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(hOut, str, cbStr, &dwWritten, nullptr)); VERIFY_ARE_EQUAL(dwExpectedWritten, dwWritten); }
void FileTests::TestWriteFileRaw() { // \x7 is bell // \x8 is backspace // \x9 is tab // \xa is linefeed // \xd is carriage return // All should be ignored/printed in raw mode. PCSTR strTest = "z\x7y\x8z\x9y\xaz\xdy"; DWORD const cchTest = (DWORD)strlen(strTest); String strReadBackExpected(strTest); HANDLE const hOut = GetStdOutputHandle(); VERIFY_IS_NOT_NULL(hOut, L"Verify we have the standard output handle."); CONSOLE_SCREEN_BUFFER_INFOEX csbiexBefore = { 0 }; csbiexBefore.cbSize = sizeof(csbiexBefore); VERIFY_WIN32_BOOL_SUCCEEDED(GetConsoleScreenBufferInfoEx(hOut, &csbiexBefore), L"Retrieve screen buffer properties before writing."); VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleMode(hOut, 0), L"Set raw write mode."); COORD const coordZero = { 0 }; VERIFY_ARE_EQUAL(coordZero, csbiexBefore.dwCursorPosition, L"Cursor should be at 0,0 in fresh buffer."); DWORD dwWritten = 0; VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(hOut, strTest, cchTest, &dwWritten, nullptr), L"Write text into buffer using WriteFile"); VERIFY_ARE_EQUAL(cchTest, dwWritten); CONSOLE_SCREEN_BUFFER_INFOEX csbiexAfter = { 0 }; csbiexAfter.cbSize = sizeof(csbiexAfter); VERIFY_WIN32_BOOL_SUCCEEDED(GetConsoleScreenBufferInfoEx(hOut, &csbiexAfter), L"Retrieve screen buffer properties after writing."); csbiexBefore.dwCursorPosition.X += (SHORT)cchTest; VERIFY_ARE_EQUAL(csbiexBefore.dwCursorPosition, csbiexAfter.dwCursorPosition, L"Verify cursor moved expected number of squares for the write length."); DWORD const cbReadBackBuffer = cchTest + 2; // +1 so we can read back a "space" that should be after what we wrote. +1 more so this can be null terminated for String class comparison. wistd::unique_ptr<char[]> strReadBack = wil::make_unique_failfast<char[]>(cbReadBackBuffer); ZeroMemory(strReadBack.get(), cbReadBackBuffer * sizeof(char)); DWORD dwRead = 0; VERIFY_WIN32_BOOL_SUCCEEDED(ReadConsoleOutputCharacterA(hOut, strReadBack.get(), cchTest + 1, coordZero, &dwRead), L"Read back the data in the buffer."); // +1 to read back the space that should be after the text we wrote strReadBackExpected += " "; // add in the space that should appear after the written text (buffer should be space filled when empty) VERIFY_ARE_EQUAL(strReadBackExpected, String(strReadBack.get()), L"Ensure that the buffer contents match what we expected based on what we wrote."); }
// Caller must free ppsz if not null. void ConvertWToA(_In_ PCWSTR pwsz, _Out_ char** ppsz) { *ppsz = nullptr; UINT const cp = CP_ACP; DWORD const dwBytesNeeded = WideCharToMultiByte(cp, 0, pwsz, -1, nullptr, 0, nullptr, nullptr); VERIFY_WIN32_BOOL_SUCCEEDED(dwBytesNeeded, L"Verify that WC2MB could detect bytes needed for conversion."); char* psz = new char[dwBytesNeeded]; VERIFY_IS_NOT_NULL(psz, L"Verify we could allocate necessary bytes for conversion."); VERIFY_WIN32_BOOL_SUCCEEDED(WideCharToMultiByte(cp, 0, pwsz, -1, psz, dwBytesNeeded, nullptr, nullptr), L"Verify that WC2MB did the conversion successfully."); *ppsz = psz; }
void FileTests::TestReadFileLine() { HANDLE const hIn = GetStdInputHandle(); VERIFY_IS_NOT_NULL(hIn, L"Verify we have the standard input handle."); DWORD dwMode = ENABLE_LINE_INPUT; VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleMode(hIn, dwMode), L"Set input mode for test."); VERIFY_WIN32_BOOL_SUCCEEDED(FlushConsoleInputBuffer(hIn), L"Flush input buffer in preparation for test."); char ch = '\0'; Log::Comment(L"Queue background blocking read file operation."); auto BackgroundRead = std::async([&] { DWORD dwRead = 0; VERIFY_WIN32_BOOL_SUCCEEDED(ReadFile(hIn, &ch, 1, &dwRead, nullptr), L"Read file was successful."); VERIFY_ARE_EQUAL(1u, dwRead, L"Verify we read 1 character."); }); char const chExpected = 'a'; Log::Comment(L"Send a key into the console."); SendFullKeyStrokeHelper(hIn, chExpected); auto status = BackgroundRead.wait_for(std::chrono::milliseconds(250)); VERIFY_ARE_EQUAL(std::future_status::timeout, status, L"We should still be waiting for a result."); VERIFY_ARE_EQUAL('\0', ch, L"Character shouldn't be filled by background read yet."); Log::Comment(L"Send a line feed character, we should stay blocked."); SendFullKeyStrokeHelper(hIn, '\n'); status = BackgroundRead.wait_for(std::chrono::milliseconds(250)); VERIFY_ARE_EQUAL(std::future_status::timeout, status, L"We should still be waiting for a result."); VERIFY_ARE_EQUAL('\0', ch, L"Character shouldn't be filled by background read yet."); Log::Comment(L"Now send a carriage return into the console to signify the end of the input line."); SendFullKeyStrokeHelper(hIn, '\r'); Log::Comment(L"Wait for background thread to unblock."); BackgroundRead.wait(); VERIFY_ARE_EQUAL(chExpected, ch); }
void FileTests::TestReadFileBasicSync() { HANDLE const hIn = GetStdInputHandle(); VERIFY_IS_NOT_NULL(hIn, L"Verify we have the standard input handle."); DWORD dwMode = 0; VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleMode(hIn, dwMode), L"Set input mode for test."); VERIFY_WIN32_BOOL_SUCCEEDED(FlushConsoleInputBuffer(hIn), L"Flush input buffer in preparation for test."); char const chExpected = 'a'; Log::Comment(L"Send a key into the console."); SendFullKeyStrokeHelper(hIn, chExpected); char ch = '\0'; Log::Comment(L"Read with synchronous blocking read."); DWORD dwRead = 0; VERIFY_WIN32_BOOL_SUCCEEDED(ReadFile(hIn, &ch, 1, &dwRead, nullptr), L"Read file was successful."); VERIFY_ARE_EQUAL(1u, dwRead, L"Verify we read 1 character."); VERIFY_ARE_EQUAL(chExpected, ch); }
void FileTests::TestReadFileLineSync() { HANDLE const hIn = GetStdInputHandle(); VERIFY_IS_NOT_NULL(hIn, L"Verify we have the standard input handle."); DWORD dwMode = ENABLE_LINE_INPUT; VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleMode(hIn, dwMode), L"Set input mode for test."); VERIFY_WIN32_BOOL_SUCCEEDED(FlushConsoleInputBuffer(hIn), L"Flush input buffer in preparation for test."); char const chExpected = 'a'; Log::Comment(L"Send a key into the console followed by a carriage return."); SendFullKeyStrokeHelper(hIn, chExpected); SendFullKeyStrokeHelper(hIn, '\r'); char ch = '\0'; Log::Comment(L"Read back the input with a synchronous blocking read."); DWORD dwRead = 0; VERIFY_WIN32_BOOL_SUCCEEDED(ReadFile(hIn, &ch, 1, nullptr, nullptr), L"Read file was successful."); VERIFY_ARE_EQUAL(0u, dwRead, L"Verify we read 0 characters."); VERIFY_ARE_EQUAL(chExpected, ch); }
Awaitable<void> ReplicatorPerfTest::Run( __in wstring const & testFolder, __in int concurrentTransactions, __in int totalTransactions, __in Data::Log::LogManager & logManager) { #ifndef PERF_TEST UNREFERENCED_PARAMETER(testFolder); UNREFERENCED_PARAMETER(concurrentTransactions); UNREFERENCED_PARAMETER(totalTransactions); UNREFERENCED_PARAMETER(logManager); #else Replica::SPtr replica = Replica::Create( pId_, rId_, testFolder, logManager, underlyingSystem_->PagedAllocator()); co_await replica->OpenAsync(); FABRIC_EPOCH epoch1; epoch1.DataLossNumber = 1; epoch1.ConfigurationNumber = 1; epoch1.Reserved = nullptr; co_await replica->ChangeRoleAsync(epoch1, FABRIC_REPLICA_ROLE_PRIMARY); replica->SetReadStatus(FABRIC_SERVICE_PARTITION_ACCESS_STATUS_GRANTED); replica->SetWriteStatus(FABRIC_SERVICE_PARTITION_ACCESS_STATUS_GRANTED); KUri::CSPtr stateProviderName = GetStateProviderName(0); { Transaction::SPtr txn; replica->TxnReplicator->CreateTransaction(txn); KFinally([&] {txn->Dispose(); }); NTSTATUS status = co_await replica->TxnReplicator->AddAsync(*txn, *stateProviderName, L"ReplicatorPerfTest"); VERIFY_IS_TRUE(NT_SUCCESS(status)); co_await txn->CommitAsync(); } { IStateProvider2::SPtr stateProvider2; NTSTATUS status = replica->TxnReplicator->Get(*stateProviderName, stateProvider2); VERIFY_IS_TRUE(NT_SUCCESS(status)); VERIFY_IS_NOT_NULL(stateProvider2); VERIFY_ARE_EQUAL(*stateProviderName, stateProvider2->GetName()); IStore<int, int>::SPtr store = dynamic_cast<IStore<int, int>*>(stateProvider2.RawPtr()); Stopwatch s; s.Start(); KArray<Awaitable<void>> tasks(underlyingSystem_->PagedAllocator(), concurrentTransactions, 0); for (int i = 0; i < concurrentTransactions; i++) { status = tasks.Append(DoWorkOnKey(store, replica, totalTransactions / concurrentTransactions, i)); KInvariant(NT_SUCCESS(status)); } co_await TaskUtilities<Awaitable<void>>::WhenAll(tasks); s.Stop(); int64 txPerSec = ((totalTransactions * 1000) / s.ElapsedMilliseconds); Trace.WriteInfo( TraceComponent, "{0}: Tx/Sec is {1}", prId_->TraceId, txPerSec); } replica->SetReadStatus(FABRIC_SERVICE_PARTITION_ACCESS_STATUS_NOT_PRIMARY); replica->SetWriteStatus(FABRIC_SERVICE_PARTITION_ACCESS_STATUS_NOT_PRIMARY); co_await replica->CloseAsync(); #endif co_return; }
void FileTests::TestWriteFileProcessed() { // \x7 is bell // \x8 is backspace // \x9 is tab // \xa is linefeed // \xd is carriage return // All should cause activity in processed mode. HANDLE const hOut = GetStdOutputHandle(); VERIFY_IS_NOT_NULL(hOut, L"Verify we have the standard output handle."); CONSOLE_SCREEN_BUFFER_INFOEX csbiexOriginal = { 0 }; csbiexOriginal.cbSize = sizeof(csbiexOriginal); VERIFY_WIN32_BOOL_SUCCEEDED(GetConsoleScreenBufferInfoEx(hOut, &csbiexOriginal), L"Retrieve screen buffer properties at beginning of test."); VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleMode(hOut, ENABLE_PROCESSED_OUTPUT), L"Set processed write mode."); COORD const coordZero = { 0 }; VERIFY_ARE_EQUAL(coordZero, csbiexOriginal.dwCursorPosition, L"Cursor should be at 0,0 in fresh buffer."); // Declare variables needed for each character test. CONSOLE_SCREEN_BUFFER_INFOEX csbiexBefore = { 0 }; CONSOLE_SCREEN_BUFFER_INFOEX csbiexAfter = { 0 }; COORD coordExpected = { 0 }; PCSTR pszTest; DWORD cchTest; PCSTR pszReadBackExpected; DWORD cchReadBack; wistd::unique_ptr<char[]> pszReadBack; // 1. Test bell (\x7) { pszTest = "z\x7"; cchTest = (DWORD)strlen(pszTest); pszReadBackExpected = "z "; cchReadBack = (DWORD)strlen(pszReadBackExpected); // Write z and a bell. Cursor should move once as bell should have made audible noise (can't really test) and not moved or printed anything. WriteFileHelper(hOut, csbiexBefore, csbiexAfter, pszTest, cchTest); coordExpected = csbiexBefore.dwCursorPosition; coordExpected.X += 1; VERIFY_ARE_EQUAL(coordExpected, csbiexAfter.dwCursorPosition, L"Verify cursor moved once for printable character and not for bell."); // Read back written data. ReadBackHelper(hOut, csbiexBefore.dwCursorPosition, cchReadBack, pszReadBack); VERIFY_ARE_EQUAL(String(pszReadBackExpected), String(pszReadBack.get()), L"Verify text matches what we expected to be written into the buffer."); } // 2. Test backspace (\x8) { pszTest = "yx\x8"; cchTest = (DWORD)strlen(pszTest); pszReadBackExpected = "yx "; cchReadBack = (DWORD)strlen(pszReadBackExpected); // Write two characters and a backspace. Cursor should move only one forward as the backspace should have moved the cursor back one after printing the second character. // The backspace character itself is typically non-destructive so it should only affect the cursor, not the buffer contents. WriteFileHelper(hOut, csbiexBefore, csbiexAfter, pszTest, cchTest); coordExpected = csbiexBefore.dwCursorPosition; coordExpected.X += 1; VERIFY_ARE_EQUAL(coordExpected, csbiexAfter.dwCursorPosition, L"Verify cursor moved twice forward for printable characters and once backward for backspace."); // Read back written data. ReadBackHelper(hOut, csbiexBefore.dwCursorPosition, cchReadBack, pszReadBack); VERIFY_ARE_EQUAL(String(pszReadBackExpected), String(pszReadBack.get()), L"Verify text matches what we expected to be written into the buffer."); } // 3. Test tab (\x9) { // The tab character will space pad out the buffer to the next multiple-of-8 boundary. // NOTE: This is dependent on the previous tests running first. pszTest = "\x9"; cchTest = (DWORD)strlen(pszTest); pszReadBackExpected = " "; cchReadBack = (DWORD)strlen(pszReadBackExpected); // Write tab character. Cursor should move out to the next multiple-of-8 and leave space characters in its wake. WriteFileHelper(hOut, csbiexBefore, csbiexAfter, pszTest, cchTest); coordExpected = csbiexBefore.dwCursorPosition; coordExpected.X = 8; VERIFY_ARE_EQUAL(coordExpected, csbiexAfter.dwCursorPosition, L"Verify cursor moved forward to position 8 for tab."); // Read back written data. ReadBackHelper(hOut, csbiexBefore.dwCursorPosition, cchReadBack, pszReadBack); VERIFY_ARE_EQUAL(String(pszReadBackExpected), String(pszReadBack.get()), L"Verify text matches what we expected to be written into the buffer."); } // 4. Test linefeed (\xa) { // The line feed character should move us down to the next line. pszTest = "\xaQ"; cchTest = (DWORD)strlen(pszTest); pszReadBackExpected = "Q "; cchReadBack = (DWORD)strlen(pszReadBackExpected); // Write line feed character. Cursor should move down a line and then the Q from our string should be printed. WriteFileHelper(hOut, csbiexBefore, csbiexAfter, pszTest, cchTest); coordExpected.X = 1; coordExpected.Y = 1; VERIFY_ARE_EQUAL(coordExpected, csbiexAfter.dwCursorPosition, L"Verify cursor moved down a line and then one character over for linefeed + Q."); // Read back written data from the 2nd line. COORD coordRead; coordRead.Y = 1; coordRead.X = 0; ReadBackHelper(hOut, coordRead, cchReadBack, pszReadBack); VERIFY_ARE_EQUAL(String(pszReadBackExpected), String(pszReadBack.get()), L"Verify text matches what we expected to be written into the buffer."); } // 5. Test carriage return (\xd) { // The carriage return character should move us to the front of the line. pszTest = "J\xd"; cchTest = (DWORD)strlen(pszTest); pszReadBackExpected = "QJ "; // J written, then move to beginning of line. cchReadBack = (DWORD)strlen(pszReadBackExpected); // Write text and carriage return character. Cursor should end up at the beginning of this line. The J should have been printed in the line before we moved. WriteFileHelper(hOut, csbiexBefore, csbiexAfter, pszTest, cchTest); coordExpected = csbiexBefore.dwCursorPosition; coordExpected.X = 0; VERIFY_ARE_EQUAL(coordExpected, csbiexAfter.dwCursorPosition, L"Verify cursor moved to beginning of line for carriage return character."); // Read back text written from the 2nd line. ReadBackHelper(hOut, csbiexAfter.dwCursorPosition, cchReadBack, pszReadBack); VERIFY_ARE_EQUAL(String(pszReadBackExpected), String(pszReadBack.get()), L"Verify text matches what we expected to be written into the buffer."); } // 6. Print a character over the top of the existing { // After the carriage return, try typing on top of the Q with a K pszTest = "K"; cchTest = (DWORD)strlen(pszTest); pszReadBackExpected = "KJ "; // NOTE: This is based on the previous test(s). cchReadBack = (DWORD)strlen(pszReadBackExpected); // Write text. Cursor should end up on top of the J. WriteFileHelper(hOut, csbiexBefore, csbiexAfter, pszTest, cchTest); coordExpected = csbiexBefore.dwCursorPosition; coordExpected.X += 1; VERIFY_ARE_EQUAL(coordExpected, csbiexAfter.dwCursorPosition, L"Verify cursor moved over one for printing character."); // Read back text written from the 2nd line. ReadBackHelper(hOut, csbiexBefore.dwCursorPosition, cchReadBack, pszReadBack); VERIFY_ARE_EQUAL(String(pszReadBackExpected), String(pszReadBack.get()), L"Verify text matches what we expected to be written into the buffer."); } }