diff options
Diffstat (limited to 'Tests/CMakeLib')
-rw-r--r-- | Tests/CMakeLib/CMakeLists.txt | 14 | ||||
-rw-r--r-- | Tests/CMakeLib/testCMExtAlgorithm.cxx | 1 | ||||
-rw-r--r-- | Tests/CMakeLib/testCMakePath.cxx | 441 | ||||
-rw-r--r-- | Tests/CMakeLib/testDebuggerAdapterPipe.cxx | 6 | ||||
-rw-r--r-- | Tests/CMakeLib/testDebuggerNamedPipe.cxx | 9 | ||||
-rw-r--r-- | Tests/CMakeLib/testEncoding.cxx | 1 | ||||
-rw-r--r-- | Tests/CMakeLib/testGccDepfileReader.cxx | 1 | ||||
-rw-r--r-- | Tests/CMakeLib/testList.cxx | 1 | ||||
-rw-r--r-- | Tests/CMakeLib/testOptional.cxx | 1 | ||||
-rw-r--r-- | Tests/CMakeLib/testString.cxx | 2 | ||||
-rw-r--r-- | Tests/CMakeLib/testUTF8.cxx | 107 | ||||
-rw-r--r-- | Tests/CMakeLib/testUVProcessChain.cxx | 368 | ||||
-rw-r--r-- | Tests/CMakeLib/testUVProcessChainHelper.cxx | 8 | ||||
-rw-r--r-- | Tests/CMakeLib/testUVStreambuf.cxx | 151 |
14 files changed, 953 insertions, 158 deletions
diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt index 5c14de2..225a1e7 100644 --- a/Tests/CMakeLib/CMakeLists.txt +++ b/Tests/CMakeLib/CMakeLists.txt @@ -31,6 +31,7 @@ set(CMakeLib_TESTS testCMExtAlgorithm.cxx testCMExtEnumSet.cxx testList.cxx + testCMakePath.cxx ) if(CMake_ENABLE_DEBUGGER) list(APPEND CMakeLib_TESTS @@ -63,10 +64,18 @@ if(WIN32) endif() configure_file(testXMLParser.h.in testXMLParser.h @ONLY) +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/testUVProcessChainInput.txt" "HELLO WORLD!") create_test_sourcelist(CMakeLib_TEST_SRCS CMakeLibTests.cxx ${CMakeLib_TESTS}) add_executable(CMakeLibTests ${CMakeLib_TEST_SRCS}) -target_link_libraries(CMakeLibTests CMakeLib CTestLib) +target_link_libraries(CMakeLibTests PRIVATE CTestLib CMakeLib) +if(CMake_BUILD_PCH) + target_precompile_headers(CMakeLibTests PRIVATE "<iostream>" "<cm3p/uv.h>") + target_compile_definitions(CMakeLibTests PRIVATE "NOMINMAX") +endif() +if(WIN32) + target_compile_definitions(CMakeLibTests PRIVATE WIN32_LEAN_AND_MEAN) +endif() set_property(TARGET CMakeLibTests PROPERTY C_CLANG_TIDY "") set_property(TARGET CMakeLibTests PROPERTY CXX_CLANG_TIDY "") @@ -102,4 +111,7 @@ if(CMake_ENABLE_DEBUGGER) add_test(NAME CMakeLib.testDebuggerNamedPipe-${case} COMMAND testDebuggerNamedPipe ${testDebuggerNamedPipe_${case}_ARGS}) set_property(TEST CMakeLib.testDebuggerNamedPipe-${case} PROPERTY TIMEOUT 300) endforeach() + if(WIN32) + target_compile_definitions(testDebuggerNamedPipe PRIVATE WIN32_LEAN_AND_MEAN) + endif() endif() diff --git a/Tests/CMakeLib/testCMExtAlgorithm.cxx b/Tests/CMakeLib/testCMExtAlgorithm.cxx index c909f24..53b0302 100644 --- a/Tests/CMakeLib/testCMExtAlgorithm.cxx +++ b/Tests/CMakeLib/testCMExtAlgorithm.cxx @@ -1,6 +1,5 @@ #include <iostream> #include <memory> -#include <type_traits> #include <utility> #include <vector> diff --git a/Tests/CMakeLib/testCMakePath.cxx b/Tests/CMakeLib/testCMakePath.cxx new file mode 100644 index 0000000..aa17e50 --- /dev/null +++ b/Tests/CMakeLib/testCMakePath.cxx @@ -0,0 +1,441 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <iostream> +#include <string> +#include <utility> + +#include <cm/string_view> +#include <cmext/string_view> + +#include "cmCMakePath.h" + +namespace { + +void checkResult(bool success) +{ + if (!success) { + std::cout << " => failed"; + } + std::cout << std::endl; +} + +bool testConstructors() +{ + std::cout << "testConstructors()"; + + bool result = true; + + { + cmCMakePath path; + if (!path.String().empty() || path != cmCMakePath{}) { + result = false; + } + } + { + cmCMakePath path{ "aa/bb" }; + if (path.String() != "aa/bb") { + result = false; + } + } + { + std::string s{ "aa/bb" }; + cmCMakePath path{ s }; + if (path.String() != "aa/bb") { + result = false; + } + } + { + cmCMakePath path{ "aa/bb"_s }; + if (path.String() != "aa/bb") { + result = false; + } + } + { + cmCMakePath path1{ "aa/bb" }; + cmCMakePath path2("aa/bb"_s); + + if (path1 != path2) { + result = false; + } + if (path1.String() != "aa/bb") { + result = false; + } + if (path1.String() != path2.String()) { + result = false; + } + } + { + cmCMakePath path1{ "aa/bb" }; + cmCMakePath path2{ path1 }; + + if (path1 != path2) { + result = false; + } + if (path1.String() != "aa/bb") { + result = false; + } + if (path1.String() != path2.String()) { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testAssign() +{ + std::cout << "testAssign()"; + + bool result = true; + + { + cmCMakePath path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2 = path1; + if (path1 != path2) { + result = false; + } + if (path1.String() != "aa/bb") { + result = false; + } + if (path1.String() != path2.String()) { + result = false; + } + } + { + cmCMakePath path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2 = std::move(path1); + if (path2.String() != "aa/bb") { + result = false; + } + } + { + std::string path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2 = path1; + if (path2.String() != "aa/bb") { + result = false; + } + } + { + std::string path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2 = std::move(path1); + if (path2.String() != "aa/bb") { + result = false; + } + } + { + cm::string_view path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2 = path1; + if (path2.String() != "aa/bb") { + result = false; + } + } + { + char path1[] = "aa/bb"; + cmCMakePath path2{ "cc/dd" }; + + path2 = path1; + if (path2.String() != "aa/bb") { + result = false; + } + } + { + std::string path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2.Assign(path1); + if (path2.String() != path1) { + result = false; + } + } + { + std::string path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2.Assign(std::move(path1)); + if (path2.String() != "aa/bb") { + result = false; + } + } + { + cm::string_view path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2.Assign(path1); + if (path2.String() != path1) { + result = false; + } + } + { + char path1[] = "aa/bb"; + cmCMakePath path2{ "cc/dd" }; + + path2.Assign(path1); + if (path2.String() != path1) { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testConcat() +{ + std::cout << "testConcat()"; + + bool result = true; + + { + cmCMakePath path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2 += path1; + + if (path2.String() != "cc/ddaa/bb") { + result = false; + } + } + { + cmCMakePath path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2 += std::move(path1); + if (path2.String() != "cc/ddaa/bb") { + result = false; + } + } + { + std::string path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2 += path1; + if (path2.String() != "cc/ddaa/bb") { + result = false; + } + } + { + std::string path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2 += std::move(path1); + if (path2.String() != "cc/ddaa/bb") { + result = false; + } + } + { + cm::string_view path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2 += path1; + if (path2.String() != "cc/ddaa/bb") { + result = false; + } + } + { + char path1[] = "aa/bb"; + cmCMakePath path2{ "cc/dd" }; + + path2 += path1; + if (path2.String() != "cc/ddaa/bb") { + result = false; + } + } + { + cmCMakePath path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2.Concat(path1); + if (path2.String() != "cc/ddaa/bb") { + result = false; + } + } + { + std::string path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2.Concat(path1); + if (path2.String() != "cc/ddaa/bb") { + result = false; + } + } + { + std::string path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2.Concat(std::move(path1)); + if (path2.String() != "cc/ddaa/bb") { + result = false; + } + } + { + cm::string_view path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2.Concat(path1); + if (path2.String() != "cc/ddaa/bb") { + result = false; + } + } + { + char path1[] = "aa/bb"; + cmCMakePath path2{ "cc/dd" }; + + path2.Concat(path1); + if (path2.String() != "cc/ddaa/bb") { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testAppend() +{ + std::cout << "testAppend()"; + + bool result = true; + + { + cmCMakePath path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2 /= path1; + + if (path2.String() != "cc/dd/aa/bb") { + result = false; + } + } + { + cmCMakePath path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2 /= std::move(path1); + if (path2.String() != "cc/dd/aa/bb") { + result = false; + } + } + { + std::string path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2 /= path1; + if (path2.String() != "cc/dd/aa/bb") { + result = false; + } + } + { + std::string path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2 /= std::move(path1); + if (path2.String() != "cc/dd/aa/bb") { + result = false; + } + } + { + cm::string_view path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2 /= path1; + if (path2.String() != "cc/dd/aa/bb") { + result = false; + } + } + { + char path1[] = "aa/bb"; + cmCMakePath path2{ "cc/dd" }; + + path2 /= path1; + if (path2.String() != "cc/dd/aa/bb") { + result = false; + } + } + { + cmCMakePath path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2.Append(path1); + if (path2.String() != "cc/dd/aa/bb") { + result = false; + } + } + { + std::string path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2.Append(path1); + if (path2.String() != "cc/dd/aa/bb") { + result = false; + } + } + { + std::string path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2.Append(std::move(path1)); + if (path2.String() != "cc/dd/aa/bb") { + result = false; + } + } + { + cm::string_view path1{ "aa/bb" }; + cmCMakePath path2{ "cc/dd" }; + + path2.Append(path1); + if (path2.String() != "cc/dd/aa/bb") { + result = false; + } + } + { + char path1[] = "aa/bb"; + cmCMakePath path2{ "cc/dd" }; + + path2.Append(path1); + if (path2.String() != "cc/dd/aa/bb") { + result = false; + } + } + + checkResult(result); + + return result; +} +} + +int testCMakePath(int /*unused*/, char* /*unused*/[]) +{ + int result = 0; + + if (!testConstructors()) { + result = 1; + } + if (!testAssign()) { + result = 1; + } + if (!testConcat()) { + result = 1; + } + if (!testAppend()) { + result = 1; + } + + return result; +} diff --git a/Tests/CMakeLib/testDebuggerAdapterPipe.cxx b/Tests/CMakeLib/testDebuggerAdapterPipe.cxx index 643661d..c0f2e9b 100644 --- a/Tests/CMakeLib/testDebuggerAdapterPipe.cxx +++ b/Tests/CMakeLib/testDebuggerAdapterPipe.cxx @@ -19,13 +19,15 @@ #include <cm3p/cppdap/types.h> #include "cmDebuggerAdapter.h" -#include "cmDebuggerPipeConnection.h" #include "cmDebuggerProtocol.h" #include "cmVersionConfig.h" #ifdef _WIN32 # include "cmCryptoHash.h" +# include "cmDebuggerWindowsPipeConnection.h" # include "cmSystemTools.h" +#else +# include "cmDebuggerPosixPipeConnection.h" #endif #include "testCommon.h" @@ -128,7 +130,7 @@ bool testProtocolWithPipes() auto client2Debugger = std::make_shared<cmDebugger::cmDebuggerPipeClient>(namedPipe); - client2Debugger->Start(); + client2Debugger->WaitForConnection(); client->bind(client2Debugger, client2Debugger); diff --git a/Tests/CMakeLib/testDebuggerNamedPipe.cxx b/Tests/CMakeLib/testDebuggerNamedPipe.cxx index ec91706..1ae3f64 100644 --- a/Tests/CMakeLib/testDebuggerNamedPipe.cxx +++ b/Tests/CMakeLib/testDebuggerNamedPipe.cxx @@ -16,7 +16,12 @@ #include "cmsys/RegularExpression.hxx" -#include "cmDebuggerPipeConnection.h" +#ifdef _WIN32 +# include "cmDebuggerWindowsPipeConnection.h" +#else +# include "cmDebuggerPosixPipeConnection.h" +#endif + #include "cmSystemTools.h" #ifdef _WIN32 @@ -104,7 +109,7 @@ int runTest(int argc, char* argv[]) attempt++; try { client = std::make_shared<cmDebugger::cmDebuggerPipeClient>(namedPipe); - client->Start(); + client->WaitForConnection(); std::cout << "cmDebuggerPipeClient connected.\n"; break; diff --git a/Tests/CMakeLib/testEncoding.cxx b/Tests/CMakeLib/testEncoding.cxx index 4936898..460d845 100644 --- a/Tests/CMakeLib/testEncoding.cxx +++ b/Tests/CMakeLib/testEncoding.cxx @@ -1,4 +1,5 @@ #include <iostream> +#include <iterator> #include <string> #include "cmsys/FStream.hxx" diff --git a/Tests/CMakeLib/testGccDepfileReader.cxx b/Tests/CMakeLib/testGccDepfileReader.cxx index d46e8f3..fb19c14 100644 --- a/Tests/CMakeLib/testGccDepfileReader.cxx +++ b/Tests/CMakeLib/testGccDepfileReader.cxx @@ -1,6 +1,5 @@ #include <cstddef> // IWYU pragma: keep #include <iostream> -#include <memory> #include <string> #include <utility> #include <vector> diff --git a/Tests/CMakeLib/testList.cxx b/Tests/CMakeLib/testList.cxx index 6d6c218..8822806 100644 --- a/Tests/CMakeLib/testList.cxx +++ b/Tests/CMakeLib/testList.cxx @@ -4,7 +4,6 @@ #include <iostream> #include <stdexcept> #include <string> -#include <type_traits> #include <utility> #include <vector> diff --git a/Tests/CMakeLib/testOptional.cxx b/Tests/CMakeLib/testOptional.cxx index 785f031..933ab70 100644 --- a/Tests/CMakeLib/testOptional.cxx +++ b/Tests/CMakeLib/testOptional.cxx @@ -1,5 +1,4 @@ #include <iostream> -#include <type_traits> #include <vector> #include <cm/optional> diff --git a/Tests/CMakeLib/testString.cxx b/Tests/CMakeLib/testString.cxx index af34a2f..3509266 100644 --- a/Tests/CMakeLib/testString.cxx +++ b/Tests/CMakeLib/testString.cxx @@ -1,14 +1,12 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#include <cstddef> // IWYU pragma: keep #include <cstring> #include <iostream> #include <iterator> #include <sstream> #include <stdexcept> #include <string> -#include <type_traits> #include <utility> #include <cm/string_view> diff --git a/Tests/CMakeLib/testUTF8.cxx b/Tests/CMakeLib/testUTF8.cxx index fc0b539..180d29d 100644 --- a/Tests/CMakeLib/testUTF8.cxx +++ b/Tests/CMakeLib/testUTF8.cxx @@ -1,67 +1,57 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ +#include <cm/string_view> + #include <stdio.h> #include <cm_utf8.h> -typedef char test_utf8_char[5]; - -static void test_utf8_char_print(test_utf8_char const c) -{ - unsigned char const* d = reinterpret_cast<unsigned char const*>(c); -#ifndef __clang_analyzer__ // somehow thinks arguments are not initialized - printf("[0x%02X,0x%02X,0x%02X,0x%02X]", static_cast<int>(d[0]), - static_cast<int>(d[1]), static_cast<int>(d[2]), - static_cast<int>(d[3])); -#endif -} +using test_utf8_char = const cm::string_view; -static void byte_array_print(char const* s) +static void byte_array_print(test_utf8_char s) { - unsigned char const* d = reinterpret_cast<unsigned char const*>(s); bool started = false; printf("["); - for (; *d; ++d) { + for (char c : s) { if (started) { printf(","); } started = true; - printf("0x%02X", static_cast<int>(*d)); + printf("0x%02X", static_cast<unsigned char>(c)); } printf("]"); } struct test_utf8_entry { - int n; test_utf8_char str; unsigned int chr; }; static test_utf8_entry const good_entry[] = { - { 1, "\x20\x00\x00\x00", 0x0020 }, /* Space. */ - { 2, "\xC2\xA9\x00\x00", 0x00A9 }, /* Copyright. */ - { 3, "\xE2\x80\x98\x00", 0x2018 }, /* Open-single-quote. */ - { 3, "\xE2\x80\x99\x00", 0x2019 }, /* Close-single-quote. */ - { 4, "\xF0\xA3\x8E\xB4", 0x233B4 }, /* Example from RFC 3629. */ - { 3, "\xED\x80\x80\x00", 0xD000 }, /* Valid 0xED prefixed codepoint. */ - { 4, "\xF4\x8F\xBF\xBF", 0x10FFFF }, /* Highest valid RFC codepoint. */ - { 0, { 0, 0, 0, 0, 0 }, 0 } + { "\x20", 0x0020 }, /* Space. */ + { "\xC2\xA9", 0x00A9 }, /* Copyright. */ + { "\xE2\x80\x98", 0x2018 }, /* Open-single-quote. */ + { "\xE2\x80\x99", 0x2019 }, /* Close-single-quote. */ + { "\xF0\xA3\x8E\xB4", 0x233B4 }, /* Example from RFC 3629. */ + { "\xED\x80\x80", 0xD000 }, /* Valid 0xED prefixed codepoint. */ + { "\xF4\x8F\xBF\xBF", 0x10FFFF }, /* Highest valid RFC codepoint. */ + { {}, 0 } }; static test_utf8_char const bad_chars[] = { - "\x80\x00\x00\x00", /* Leading continuation byte. */ - "\xC0\x80\x00\x00", /* Overlong encoding. */ - "\xC1\x80\x00\x00", /* Overlong encoding. */ - "\xC2\x00\x00\x00", /* Missing continuation byte. */ - "\xE0\x00\x00\x00", /* Missing continuation bytes. */ - "\xE0\x80\x80\x00", /* Overlong encoding. */ + "\x80", /* Leading continuation byte. */ + "\xC0\x80", /* Overlong encoding. */ + "\xC1\x80", /* Overlong encoding. */ + "\xC2", /* Missing continuation byte. */ + "\xE0", /* Missing continuation bytes. */ + "\xE0\x80\x80", /* Overlong encoding. */ "\xF0\x80\x80\x80", /* Overlong encoding. */ - "\xED\xA0\x80\x00", /* UTF-16 surrogate half. */ - "\xED\xBF\xBF\x00", /* UTF-16 surrogate half. */ + "\xED\xA0\x80", /* UTF-16 surrogate half. */ + "\xED\xBF\xBF", /* UTF-16 surrogate half. */ "\xF4\x90\x80\x80", /* Lowest out-of-range codepoint. */ "\xF5\x80\x80\x80", /* Prefix forces out-of-range codepoints. */ - { 0, 0, 0, 0, 0 } + {} }; static char const* good_strings[] = { "", "ASCII", "\xC2\xA9 Kitware", 0 }; @@ -71,49 +61,50 @@ static char const* bad_strings[] = { 0 }; -static void report_good(bool passed, test_utf8_char const c) +static void report_good(bool passed, test_utf8_char c) { printf("%s: decoding good ", passed ? "pass" : "FAIL"); - test_utf8_char_print(c); - printf(" (%s) ", c); + byte_array_print(c); + printf(" (%s) ", c.data()); } -static void report_bad(bool passed, test_utf8_char const c) +static void report_bad(bool passed, test_utf8_char c) { printf("%s: decoding bad ", passed ? "pass" : "FAIL"); - test_utf8_char_print(c); + byte_array_print(c); printf(" "); } -static bool decode_good(test_utf8_entry const entry) +static bool decode_good(test_utf8_entry const& entry) { + const auto& s = entry.str; unsigned int uc; if (const char* e = - cm_utf8_decode_character(entry.str, entry.str + 4, &uc)) { - int used = static_cast<int>(e - entry.str); + cm_utf8_decode_character(s.data(), s.data() + s.size(), &uc)) { + int used = static_cast<int>(e - s.data()); if (uc != entry.chr) { - report_good(false, entry.str); + report_good(false, s); printf("expected 0x%04X, got 0x%04X\n", entry.chr, uc); return false; } - if (used != entry.n) { - report_good(false, entry.str); - printf("had %d bytes, used %d\n", entry.n, used); + if (used != int(s.size())) { + report_good(false, s); + printf("had %d bytes, used %d\n", int(s.size()), used); return false; } - report_good(true, entry.str); + report_good(true, s); printf("got 0x%04X\n", uc); return true; } - report_good(false, entry.str); + report_good(false, s); printf("failed\n"); return false; } -static bool decode_bad(test_utf8_char const s) +static bool decode_bad(test_utf8_char s) { unsigned int uc = 0xFFFFu; - const char* e = cm_utf8_decode_character(s, s + 4, &uc); + const char* e = cm_utf8_decode_character(s.data(), s.data() + s.size(), &uc); if (e) { report_bad(false, s); printf("expected failure, got 0x%04X\n", uc); @@ -124,23 +115,23 @@ static bool decode_bad(test_utf8_char const s) return true; } -static void report_valid(bool passed, char const* s) +static void report_valid(bool passed, test_utf8_char s) { printf("%s: validity good ", passed ? "pass" : "FAIL"); byte_array_print(s); - printf(" (%s) ", s); + printf(" (%s) ", s.data()); } -static void report_invalid(bool passed, char const* s) +static void report_invalid(bool passed, test_utf8_char s) { printf("%s: validity bad ", passed ? "pass" : "FAIL"); byte_array_print(s); printf(" "); } -static bool is_valid(const char* s) +static bool is_valid(test_utf8_char s) { - bool valid = cm_utf8_is_valid(s) != 0; + bool valid = cm_utf8_is_valid(s.data()) != 0; if (!valid) { report_valid(false, s); printf("expected valid, reported as invalid\n"); @@ -151,9 +142,9 @@ static bool is_valid(const char* s) return true; } -static bool is_invalid(const char* s) +static bool is_invalid(test_utf8_char s) { - bool valid = cm_utf8_is_valid(s) != 0; + bool valid = cm_utf8_is_valid(s.data()) != 0; if (valid) { report_invalid(false, s); printf("expected invalid, reported as valid\n"); @@ -167,7 +158,7 @@ static bool is_invalid(const char* s) int testUTF8(int /*unused*/, char* /*unused*/[]) { int result = 0; - for (test_utf8_entry const* e = good_entry; e->n; ++e) { + for (test_utf8_entry const* e = good_entry; !e->str.empty(); ++e) { if (!decode_good(*e)) { result = 1; } @@ -175,7 +166,7 @@ int testUTF8(int /*unused*/, char* /*unused*/[]) result = 1; } } - for (test_utf8_char const* c = bad_chars; (*c)[0]; ++c) { + for (test_utf8_char* c = bad_chars; !(*c).empty(); ++c) { if (!decode_bad(*c)) { result = 1; } diff --git a/Tests/CMakeLib/testUVProcessChain.cxx b/Tests/CMakeLib/testUVProcessChain.cxx index 7027689..aab084b 100644 --- a/Tests/CMakeLib/testUVProcessChain.cxx +++ b/Tests/CMakeLib/testUVProcessChain.cxx @@ -1,10 +1,10 @@ #include <algorithm> #include <csignal> +#include <cstdio> #include <functional> #include <iostream> #include <sstream> #include <string> -#include <type_traits> #include <utility> #include <vector> @@ -12,15 +12,17 @@ #include <cm3p/uv.h> +#include "cm_fileno.hxx" + #include "cmGetPipes.h" #include "cmStringAlgorithms.h" #include "cmUVHandlePtr.h" #include "cmUVProcessChain.h" +#include "cmUVStream.h" #include "cmUVStreambuf.h" struct ExpectedStatus { - bool Finished; bool MatchExitStatus; bool MatchTermSignal; cmUVProcessChain::Status Status; @@ -28,38 +30,6 @@ struct ExpectedStatus std::string ExceptionString; }; -static const std::vector<ExpectedStatus> status1 = { - { false, false, false, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" }, - { false, false, false, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" }, - { false, false, false, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" }, -}; - -static const std::vector<ExpectedStatus> status2 = { - { true, true, true, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" }, - { false, false, false, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" }, - { false, false, false, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" }, -}; - -static const std::vector<ExpectedStatus> status3 = { - { true, true, true, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" }, - { true, true, true, { 1, 0 }, cmUVProcessChain::ExceptionCode::None, "" }, -#ifdef _WIN32 - { true, - true, - true, - { STATUS_ACCESS_VIOLATION, 0 }, - cmUVProcessChain::ExceptionCode::Fault, - "Access violation" }, -#else - { true, - false, - true, - { 0, SIGABRT }, - cmUVProcessChain::ExceptionCode::Other, - "Subprocess aborted" }, -#endif -}; - static const char* ExceptionCodeToString(cmUVProcessChain::ExceptionCode code) { switch (code) { @@ -73,6 +43,8 @@ static const char* ExceptionCodeToString(cmUVProcessChain::ExceptionCode code) return "Interrupt"; case cmUVProcessChain::ExceptionCode::Numerical: return "Numerical"; + case cmUVProcessChain::ExceptionCode::Spawn: + return "Spawn"; case cmUVProcessChain::ExceptionCode::Other: return "Other"; default: @@ -83,9 +55,10 @@ static const char* ExceptionCodeToString(cmUVProcessChain::ExceptionCode code) bool operator==(const cmUVProcessChain::Status* actual, const ExpectedStatus& expected) { - if (!expected.Finished) { - return !actual; - } else if (!actual) { + if (expected.Status.SpawnResult != actual->SpawnResult) { + return false; + } + if (expected.Status.Finished != actual->Finished) { return false; } if (expected.MatchExitStatus && @@ -96,7 +69,7 @@ bool operator==(const cmUVProcessChain::Status* actual, expected.Status.TermSignal != actual->TermSignal) { return false; } - if (expected.Finished && + if (expected.Status.Finished && std::make_pair(expected.ExceptionCode, expected.ExceptionString) != actual->GetException()) { return false; @@ -150,39 +123,96 @@ static void printResults( { std::cout << "Expected: " << std::endl; for (auto const& e : expected) { - if (e.Finished) { - std::cout << " ExitStatus: " - << printExpected(e.MatchExitStatus, e.Status.ExitStatus) - << ", TermSignal: " - << printExpected(e.MatchTermSignal, e.Status.TermSignal) - << ", ExceptionCode: " - << printExpected(e.Finished, - ExceptionCodeToString(e.ExceptionCode)) - << ", ExceptionString: \"" - << printExpected(e.Finished, e.ExceptionString) << '"' - << std::endl; - } else { - std::cout << " null" << std::endl; - } + std::cout << " SpawnResult: " << e.Status.SpawnResult + << ", Finished: " << e.Status.Finished << ", ExitStatus: " + << printExpected(e.MatchExitStatus, e.Status.ExitStatus) + << ", TermSignal: " + << printExpected(e.MatchTermSignal, e.Status.TermSignal) + << ", ExceptionCode: " + << printExpected(e.Status.Finished, + ExceptionCodeToString(e.ExceptionCode)) + << ", ExceptionString: \"" + << printExpected(e.Status.Finished, e.ExceptionString) << '"' + << std::endl; } std::cout << "Actual:" << std::endl; for (auto const& a : actual) { - if (a) { - auto exception = a->GetException(); - std::cout << " ExitStatus: " << a->ExitStatus - << ", TermSignal: " << a->TermSignal << ", ExceptionCode: " - << ExceptionCodeToString(exception.first) - << ", ExceptionString: \"" << exception.second << '"' - << std::endl; - } else { - std::cout << " null" << std::endl; - } + auto exception = a->GetException(); + std::cout << " SpawnResult: " << a->SpawnResult + << ", Finished: " << a->Finished + << ", ExitStatus: " << a->ExitStatus + << ", TermSignal: " << a->TermSignal + << ", ExceptionCode: " << ExceptionCodeToString(exception.first) + << ", ExceptionString: \"" << exception.second << '"' + << std::endl; } } static bool checkExecution(cmUVProcessChainBuilder& builder, std::unique_ptr<cmUVProcessChain>& chain) { + static const std::vector<ExpectedStatus> status1 = { + { false, + false, + { 0, false, 0, 0 }, + cmUVProcessChain::ExceptionCode::None, + "" }, + { false, + false, + { 0, false, 0, 0 }, + cmUVProcessChain::ExceptionCode::None, + "" }, + { false, + false, + { 0, false, 0, 0 }, + cmUVProcessChain::ExceptionCode::None, + "" }, + }; + + static const std::vector<ExpectedStatus> status2 = { + { true, + true, + { 0, true, 0, 0 }, + cmUVProcessChain::ExceptionCode::None, + "" }, + { false, + false, + { 0, false, 0, 0 }, + cmUVProcessChain::ExceptionCode::None, + "" }, + { false, + false, + { 0, false, 0, 0 }, + cmUVProcessChain::ExceptionCode::None, + "" }, + }; + + static const std::vector<ExpectedStatus> status3 = { + { true, + true, + { 0, true, 0, 0 }, + cmUVProcessChain::ExceptionCode::None, + "" }, + { true, + true, + { 0, true, 1, 0 }, + cmUVProcessChain::ExceptionCode::None, + "" }, +#ifdef _WIN32 + { true, + true, + { 0, true, STATUS_ACCESS_VIOLATION, 0 }, + cmUVProcessChain::ExceptionCode::Fault, + "Access violation" }, +#else + { false, + true, + { 0, true, 0, SIGABRT }, + cmUVProcessChain::ExceptionCode::Other, + "Subprocess aborted" }, +#endif + }; + std::vector<const cmUVProcessChain::Status*> status; chain = cm::make_unique<cmUVProcessChain>(builder.Start()); @@ -201,7 +231,7 @@ static bool checkExecution(cmUVProcessChainBuilder& builder, return false; } - if (chain->Wait(6000)) { + if (chain->Wait(9000)) { std::cout << "Wait() returned true, should be false" << std::endl; return false; } @@ -273,16 +303,19 @@ bool testUVProcessChainBuiltin(const char* helperCommand) return false; } - if (!chain->OutputStream()) { - std::cout << "OutputStream() was null, expecting not null" << std::endl; + if (chain->OutputStream() < 0) { + std::cout << "OutputStream() was invalid, expecting valid" << std::endl; return false; } - if (!chain->ErrorStream()) { - std::cout << "ErrorStream() was null, expecting not null" << std::endl; + if (chain->ErrorStream() < 0) { + std::cout << "ErrorStream() was invalid, expecting valid" << std::endl; return false; } - if (!checkOutput(*chain->OutputStream(), *chain->ErrorStream())) { + cmUVPipeIStream output(chain->GetLoop(), chain->OutputStream()); + cmUVPipeIStream error(chain->GetLoop(), chain->ErrorStream()); + + if (!checkOutput(output, error)) { return false; } @@ -302,12 +335,12 @@ bool testUVProcessChainBuiltinMerged(const char* helperCommand) return false; } - if (!chain->OutputStream()) { - std::cout << "OutputStream() was null, expecting not null" << std::endl; + if (chain->OutputStream() < 0) { + std::cout << "OutputStream() was invalid, expecting valid" << std::endl; return false; } - if (!chain->ErrorStream()) { - std::cout << "ErrorStream() was null, expecting not null" << std::endl; + if (chain->ErrorStream() < 0) { + std::cout << "ErrorStream() was invalid, expecting valid" << std::endl; return false; } if (chain->OutputStream() != chain->ErrorStream()) { @@ -316,7 +349,9 @@ bool testUVProcessChainBuiltinMerged(const char* helperCommand) return false; } - std::string merged = getInput(*chain->OutputStream()); + cmUVPipeIStream mergedStream(chain->GetLoop(), chain->OutputStream()); + + std::string merged = getInput(mergedStream); auto qemuErrorPos = merged.find("qemu:"); if (qemuErrorPos != std::string::npos) { merged.resize(qemuErrorPos); @@ -370,12 +405,12 @@ bool testUVProcessChainExternal(const char* helperCommand) return false; } - if (chain->OutputStream()) { - std::cout << "OutputStream() was not null, expecting null" << std::endl; + if (chain->OutputStream() >= 0) { + std::cout << "OutputStream() was valid, expecting invalid" << std::endl; return false; } - if (chain->ErrorStream()) { - std::cout << "ErrorStream() was not null, expecting null" << std::endl; + if (chain->ErrorStream() >= 0) { + std::cout << "ErrorStream() was valid, expecting invalid" << std::endl; return false; } @@ -418,12 +453,12 @@ bool testUVProcessChainNone(const char* helperCommand) return false; } - if (chain->OutputStream()) { - std::cout << "OutputStream() was not null, expecting null" << std::endl; + if (chain->OutputStream() >= 0) { + std::cout << "OutputStream() was valid, expecting invalid" << std::endl; return false; } - if (chain->ErrorStream()) { - std::cout << "ErrorStream() was not null, expecting null" << std::endl; + if (chain->ErrorStream() >= 0) { + std::cout << "ErrorStream() was valid, expecting invalid" << std::endl; return false; } @@ -445,7 +480,8 @@ bool testUVProcessChainCwdUnchanged(const char* helperCommand) return false; } - auto cwd = getInput(*chain.OutputStream()); + cmUVPipeIStream output(chain.GetLoop(), chain.OutputStream()); + auto cwd = getInput(output); if (!cmHasLiteralSuffix(cwd, "/Tests/CMakeLib")) { std::cout << "Working directory was \"" << cwd << "\", expected to end in \"/Tests/CMakeLib\"" << std::endl; @@ -471,7 +507,8 @@ bool testUVProcessChainCwdChanged(const char* helperCommand) return false; } - auto cwd = getInput(*chain.OutputStream()); + cmUVPipeIStream output(chain.GetLoop(), chain.OutputStream()); + auto cwd = getInput(output); if (!cmHasLiteralSuffix(cwd, "/Tests")) { std::cout << "Working directory was \"" << cwd << "\", expected to end in \"/Tests\"" << std::endl; @@ -481,6 +518,156 @@ bool testUVProcessChainCwdChanged(const char* helperCommand) return true; } +bool testUVProcessChainSpawnFail(const char* helperCommand) +{ + static const std::vector<ExpectedStatus> status1 = { + { false, + false, + { 0, false, 0, 0 }, + cmUVProcessChain::ExceptionCode::None, + "" }, + { false, + false, + { UV_ENOENT, true, 0, 0 }, + cmUVProcessChain::ExceptionCode::Spawn, + uv_strerror(UV_ENOENT) }, +#ifdef _WIN32 + { true, + true, + { 0, true, STATUS_ACCESS_VIOLATION, 0 }, + cmUVProcessChain::ExceptionCode::Fault, + "Access violation" }, +#else + { false, + true, + { 0, true, 0, SIGABRT }, + cmUVProcessChain::ExceptionCode::Other, + "Subprocess aborted" }, +#endif + }; + + static const std::vector<ExpectedStatus> status2 = { +#ifdef _WIN32 + { true, + true, + { 0, true, 0, 0 }, + cmUVProcessChain::ExceptionCode::None, + "" }, +#else + { false, + true, + { 0, true, 0, SIGPIPE }, + cmUVProcessChain::ExceptionCode::Other, + "SIGPIPE" }, +#endif + { false, + false, + { UV_ENOENT, true, 0, 0 }, + cmUVProcessChain::ExceptionCode::Spawn, + uv_strerror(UV_ENOENT) }, +#ifdef _WIN32 + { true, + true, + { 0, true, STATUS_ACCESS_VIOLATION, 0 }, + cmUVProcessChain::ExceptionCode::Fault, + "Access violation" }, +#else + { false, + true, + { 0, true, 0, SIGABRT }, + cmUVProcessChain::ExceptionCode::Other, + "Subprocess aborted" }, +#endif + }; + + std::vector<const cmUVProcessChain::Status*> status; + + cmUVProcessChainBuilder builder; + builder.AddCommand({ helperCommand, "echo" }) + .AddCommand({ "this_command_is_for_cmake_and_should_never_exist" }) + .AddCommand({ helperCommand, "dedup" }) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); + + auto chain = builder.Start(); + if (!chain.Valid()) { + std::cout << "Valid() returned false, should be true" << std::endl; + return false; + } + + // Some platforms, like Solaris 10, take a long time to report a trapped + // subprocess to the parent process (about 1.7 seconds in the case of + // Solaris 10.) Wait 3 seconds to give it enough time. + if (chain.Wait(3000)) { + std::cout << "Wait() did not time out" << std::endl; + return false; + } + + status = chain.GetStatus(); + if (!resultsMatch(status, status1)) { + std::cout << "GetStatus() did not produce expected output" << std::endl; + printResults(status, status1); + return false; + } + + if (!chain.Wait()) { + std::cout << "Wait() timed out" << std::endl; + return false; + } + + status = chain.GetStatus(); + if (!resultsMatch(status, status2)) { + std::cout << "GetStatus() did not produce expected output" << std::endl; + printResults(status, status2); + return false; + } + + return true; +} + +bool testUVProcessChainInputFile(const char* helperCommand) +{ + std::unique_ptr<FILE, int (*)(FILE*)> f( + fopen("testUVProcessChainInput.txt", "rb"), fclose); + + cmUVProcessChainBuilder builder; + builder.AddCommand({ helperCommand, "dedup" }) + .SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT, + cm_fileno(f.get())) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT); + + auto chain = builder.Start(); + + if (!chain.Wait()) { + std::cout << "Wait() timed out" << std::endl; + return false; + } + + cmUVPipeIStream stream(chain.GetLoop(), chain.OutputStream()); + std::string output = getInput(stream); + if (output != "HELO WRD!") { + std::cout << "Output was \"" << output << "\", expected \"HELO WRD!\"" + << std::endl; + return false; + } + + return true; +} + +bool testUVProcessChainWait0(const char* helperCommand) +{ + cmUVProcessChainBuilder builder; + builder.AddCommand({ helperCommand, "echo" }); + + auto chain = builder.Start(); + if (!chain.Wait(0)) { + std::cout << "Wait(0) returned false, should be true" << std::endl; + return false; + } + + return true; +} + int testUVProcessChain(int argc, char** const argv) { if (argc < 2) { @@ -518,5 +705,20 @@ int testUVProcessChain(int argc, char** const argv) return -1; } + if (!testUVProcessChainSpawnFail(argv[1])) { + std::cout << "While executing testUVProcessChainSpawnFail().\n"; + return -1; + } + + if (!testUVProcessChainInputFile(argv[1])) { + std::cout << "While executing testUVProcessChainInputFile().\n"; + return -1; + } + + if (!testUVProcessChainWait0(argv[1])) { + std::cout << "While executing testUVProcessChainWait0().\n"; + return -1; + } + return 0; } diff --git a/Tests/CMakeLib/testUVProcessChainHelper.cxx b/Tests/CMakeLib/testUVProcessChainHelper.cxx index 99743e7..b53cac4 100644 --- a/Tests/CMakeLib/testUVProcessChainHelper.cxx +++ b/Tests/CMakeLib/testUVProcessChainHelper.cxx @@ -7,10 +7,6 @@ #include <string> #include <thread> -#ifdef _WIN32 -# include <windows.h> -#endif - #include "cmSystemTools.h" static std::string getStdin() @@ -32,13 +28,13 @@ int main(int argc, char** argv) std::string command = argv[1]; if (command == "echo") { - std::this_thread::sleep_for(std::chrono::milliseconds(3000)); + std::this_thread::sleep_for(std::chrono::milliseconds(6000)); std::cout << "HELLO world!" << std::flush; std::cerr << "1" << std::flush; return 0; } if (command == "capitalize") { - std::this_thread::sleep_for(std::chrono::milliseconds(9000)); + std::this_thread::sleep_for(std::chrono::milliseconds(12000)); std::string input = getStdin(); for (auto& c : input) { c = static_cast<char>(std::toupper(c)); diff --git a/Tests/CMakeLib/testUVStreambuf.cxx b/Tests/CMakeLib/testUVStreambuf.cxx index f9ed6af..af06a8e 100644 --- a/Tests/CMakeLib/testUVStreambuf.cxx +++ b/Tests/CMakeLib/testUVStreambuf.cxx @@ -3,11 +3,14 @@ #include <string> #include <vector> +#include <cmext/algorithm> + #include <cm3p/uv.h> #include <stdint.h> #include "cmGetPipes.h" #include "cmUVHandlePtr.h" +#include "cmUVStream.h" #include "cmUVStreambuf.h" #define TEST_STR_LINE_1 "This string must be exactly 128 characters long so" @@ -437,6 +440,139 @@ end: return success; } +bool testUVPipeIStream() +{ + int pipe[] = { -1, -1 }; + if (cmGetPipes(pipe) < 0) { + std::cout << "cmGetPipes() returned an error" << std::endl; + return false; + } + + cm::uv_loop_ptr loop; + loop.init(); + cm::uv_pipe_ptr pipeSink; + pipeSink.init(*loop, 0); + uv_pipe_open(pipeSink, pipe[1]); + + std::string str = "Hello world!\n"; + uv_write_t writeReq; + uv_buf_t buf; + buf.base = &str.front(); + buf.len = str.length(); + uv_write(&writeReq, pipeSink, &buf, 1, nullptr); + uv_run(loop, UV_RUN_DEFAULT); + + cmUVPipeIStream pin(*loop, pipe[0]); + std::string line; + std::getline(pin, line); + if (line != "Hello world!") { + std::cout << "Line was \"" << line << "\", should be \"Hello world!\"" + << std::endl; + return false; + } + + return true; +} + +bool testUVStreamRead() +{ + int pipe[] = { -1, -1 }; + if (cmGetPipes(pipe) < 0) { + std::cout << "cmGetPipes() returned an error" << std::endl; + return false; + } + + cm::uv_loop_ptr loop; + loop.init(); + cm::uv_pipe_ptr pipeSink; + pipeSink.init(*loop, 0); + uv_pipe_open(pipeSink, pipe[1]); + + std::string str = "Hello world!"; + uv_write_t writeReq; + uv_buf_t buf; + buf.base = &str.front(); + buf.len = str.length(); + uv_write(&writeReq, pipeSink, &buf, 1, nullptr); + uv_run(loop, UV_RUN_DEFAULT); + pipeSink.reset(); + + cm::uv_pipe_ptr pipeSource; + pipeSource.init(*loop, 0); + uv_pipe_open(pipeSource, pipe[0]); + + std::string output; + bool finished = false; + auto handle = cmUVStreamRead( + pipeSource, + [&output](std::vector<char> data) { cm::append(output, data); }, + [&output, &finished]() { + if (output != "Hello world!") { + std::cout << "Output was \"" << output + << "\", should be \"Hello world!\"" << std::endl; + return; + } + finished = true; + }); + uv_run(loop, UV_RUN_DEFAULT); + + if (!finished) { + std::cout << "finished was not set" << std::endl; + return false; + } + + return true; +} + +bool testUVStreamReadLeak() +{ + int pipe[] = { -1, -1 }; + if (cmGetPipes(pipe) < 0) { + std::cout << "cmGetPipes() returned an error" << std::endl; + return false; + } + + cm::uv_loop_ptr loop; + loop.init(); + cm::uv_pipe_ptr pipeSink; + pipeSink.init(*loop, 0); + uv_pipe_open(pipeSink, pipe[1]); + + std::string str = "Hello world!"; + uv_write_t writeReq; + uv_buf_t buf; + buf.base = &str.front(); + buf.len = str.length(); + uv_write(&writeReq, pipeSink, &buf, 1, nullptr); + uv_run(loop, UV_RUN_DEFAULT); + pipeSink.reset(); + + cm::uv_pipe_ptr pipeSource; + pipeSource.init(*loop, 0); + uv_pipe_open(pipeSource, pipe[0]); + + std::string output; + bool finished = false; + auto handle = cmUVStreamRead( + pipeSource, + [&output](std::vector<char> data) { cm::append(output, data); }, + [&output, &finished]() { + if (output != "Hello world!") { + std::cout << "Output was \"" << output + << "\", should be \"Hello world!\"" << std::endl; + return; + } + finished = true; + }); + + if (finished) { + std::cout << "finished was set" << std::endl; + return false; + } + + return true; +} + int testUVStreambuf(int argc, char** const argv) { if (argc < 2) { @@ -454,5 +590,20 @@ int testUVStreambuf(int argc, char** const argv) return -1; } + if (!testUVPipeIStream()) { + std::cout << "While executing testUVPipeIStream().\n"; + return -1; + } + + if (!testUVStreamRead()) { + std::cout << "While executing testUVStreamRead().\n"; + return -1; + } + + if (!testUVStreamReadLeak()) { + std::cout << "While executing testUVStreamReadLeak().\n"; + return -1; + } + return 0; } |