diff options
Diffstat (limited to 'Tests/CMakeLib')
28 files changed, 4062 insertions, 275 deletions
diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt index 612d4b4..4454f49 100644 --- a/Tests/CMakeLib/CMakeLists.txt +++ b/Tests/CMakeLib/CMakeLists.txt @@ -30,12 +30,25 @@ set(CMakeLib_TESTS testCMExtMemory.cxx testCMExtAlgorithm.cxx testCMExtEnumSet.cxx + testList.cxx + testCMakePath.cxx ) +if(CMake_ENABLE_DEBUGGER) + list(APPEND CMakeLib_TESTS + testDebuggerAdapter.cxx + testDebuggerAdapterPipe.cxx + testDebuggerBreakpointManager.cxx + testDebuggerVariables.cxx + testDebuggerVariablesHelper.cxx + testDebuggerVariablesManager.cxx + ) +endif() if (CMake_TEST_FILESYSTEM_PATH OR NOT CMake_HAVE_CXX_FILESYSTEM) list(APPEND CMakeLib_TESTS testCMFilesystemPath.cxx) endif() add_executable(testUVProcessChainHelper testUVProcessChainHelper.cxx) +target_link_libraries(testUVProcessChainHelper CMakeLib) set(testRST_ARGS ${CMAKE_CURRENT_SOURCE_DIR}) set(testUVProcessChain_ARGS $<TARGET_FILE:testUVProcessChainHelper>) @@ -51,10 +64,15 @@ 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() set_property(TARGET CMakeLibTests PROPERTY C_CLANG_TIDY "") set_property(TARGET CMakeLibTests PROPERTY CXX_CLANG_TIDY "") @@ -76,3 +94,18 @@ add_subdirectory(PseudoMemcheck) add_executable(testAffinity testAffinity.cxx) target_link_libraries(testAffinity CMakeLib) + +if(CMake_ENABLE_DEBUGGER) + add_executable(testDebuggerNamedPipe testDebuggerNamedPipe.cxx) + target_link_libraries(testDebuggerNamedPipe PRIVATE CMakeLib) + set(testDebuggerNamedPipe_Project_ARGS + "$<TARGET_FILE:cmake>" ${CMAKE_CURRENT_SOURCE_DIR}/DebuggerSample ${CMAKE_CURRENT_BINARY_DIR}/DebuggerSample + ) + set(testDebuggerNamedPipe_Script_ARGS + "$<TARGET_FILE:cmake>" ${CMAKE_CURRENT_SOURCE_DIR}/DebuggerSample/script.cmake + ) + foreach(case Project Script) + add_test(NAME CMakeLib.testDebuggerNamedPipe-${case} COMMAND testDebuggerNamedPipe ${testDebuggerNamedPipe_${case}_ARGS}) + set_property(TEST CMakeLib.testDebuggerNamedPipe-${case} PROPERTY TIMEOUT 300) + endforeach() +endif() diff --git a/Tests/CMakeLib/DebuggerSample/CMakeLists.txt b/Tests/CMakeLib/DebuggerSample/CMakeLists.txt new file mode 100644 index 0000000..8f8603a --- /dev/null +++ b/Tests/CMakeLib/DebuggerSample/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.26) +project(DebuggerSample NONE) +message("Hello CMake Debugger") + +# There are concerns that because the debugger uses libuv for pipe +# communication, libuv may register a SIGCHILD handler that interferes with +# the existing handler used by kwsys process management. Test this case with a +# simple external process. +execute_process(COMMAND "${CMAKE_COMMAND}" -E echo test) diff --git a/Tests/CMakeLib/DebuggerSample/script.cmake b/Tests/CMakeLib/DebuggerSample/script.cmake new file mode 100644 index 0000000..4c0c00a --- /dev/null +++ b/Tests/CMakeLib/DebuggerSample/script.cmake @@ -0,0 +1 @@ +message(STATUS "This is an example script") 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/testCTestResourceAllocator.cxx b/Tests/CMakeLib/testCTestResourceAllocator.cxx index 72e06e5..3a2e524 100644 --- a/Tests/CMakeLib/testCTestResourceAllocator.cxx +++ b/Tests/CMakeLib/testCTestResourceAllocator.cxx @@ -5,12 +5,16 @@ #include "cmCTestResourceAllocator.h" #include "cmCTestResourceSpec.h" - -static const cmCTestResourceSpec spec{ { { - /* clang-format off */ - { "gpus", { { "0", 4 }, { "1", 8 }, { "2", 0 }, { "3", 8 } } }, - /* clang-format on */ -} } }; +#include "cmJSONState.h" + +static const cmCTestResourceSpec spec{ + { { + /* clang-format off */ + { "gpus", { { "0", 4 }, { "1", 8 }, { "2", 0 }, { "3", 8 } }, }, + /* clang-format on */ + } }, + cmJSONState() +}; static bool testInitializeFromResourceSpec() { diff --git a/Tests/CMakeLib/testCTestResourceSpec.cxx b/Tests/CMakeLib/testCTestResourceSpec.cxx index b49f8ff..4a0021f 100644 --- a/Tests/CMakeLib/testCTestResourceSpec.cxx +++ b/Tests/CMakeLib/testCTestResourceSpec.cxx @@ -3,89 +3,72 @@ #include <vector> #include "cmCTestResourceSpec.h" +#include "cmJSONState.h" struct ExpectedSpec { std::string Path; - cmCTestResourceSpec::ReadFileResult ParseResult; + bool ParseResult; cmCTestResourceSpec Expected; }; static const std::vector<ExpectedSpec> expectedResourceSpecs = { { "spec1.json", - cmCTestResourceSpec::ReadFileResult::READ_OK, + true, { { { - { "gpus", - { - { "2", 4 }, - { "e", 1 }, - } }, - { "threads", {} }, - } } } }, - { "spec2.json", cmCTestResourceSpec::ReadFileResult::READ_OK, {} }, - { "spec3.json", - cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC, - {} }, - { "spec4.json", - cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC, - {} }, - { "spec5.json", - cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC, - {} }, - { "spec6.json", - cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC, - {} }, - { "spec7.json", - cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE_TYPE, - {} }, - { "spec8.json", cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE, {} }, - { "spec9.json", cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE, {} }, - { "spec10.json", cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE, {} }, - { "spec11.json", cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE, {} }, - { "spec12.json", cmCTestResourceSpec::ReadFileResult::INVALID_ROOT, {} }, - { "spec13.json", cmCTestResourceSpec::ReadFileResult::JSON_PARSE_ERROR, {} }, - { "spec14.json", cmCTestResourceSpec::ReadFileResult::READ_OK, {} }, - { "spec15.json", cmCTestResourceSpec::ReadFileResult::READ_OK, {} }, - { "spec16.json", cmCTestResourceSpec::ReadFileResult::READ_OK, {} }, - { "spec17.json", cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE, {} }, - { "spec18.json", cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE, {} }, - { "spec19.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} }, - { "spec20.json", cmCTestResourceSpec::ReadFileResult::READ_OK, {} }, - { "spec21.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} }, - { "spec22.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} }, - { "spec23.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} }, - { "spec24.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} }, - { "spec25.json", - cmCTestResourceSpec::ReadFileResult::UNSUPPORTED_VERSION, - {} }, - { "spec26.json", - cmCTestResourceSpec::ReadFileResult::UNSUPPORTED_VERSION, - {} }, - { "spec27.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} }, - { "spec28.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} }, - { "spec29.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} }, - { "spec30.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} }, - { "spec31.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} }, - { "spec32.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} }, - { "spec33.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} }, - { "spec34.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} }, - { "spec35.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} }, - { "spec36.json", cmCTestResourceSpec::ReadFileResult::NO_VERSION, {} }, - { "noexist.json", cmCTestResourceSpec::ReadFileResult::FILE_NOT_FOUND, {} }, + { "gpus", + { + { "2", 4 }, + { "e", 1 }, + } }, + { "threads", {} }, + } }, + cmJSONState() } }, + { "spec2.json", true, {} }, + { "spec3.json", false, {} }, + { "spec4.json", false, {} }, + { "spec5.json", false, {} }, + { "spec6.json", false, {} }, + { "spec7.json", false, {} }, + { "spec8.json", false, {} }, + { "spec9.json", false, {} }, + { "spec10.json", false, {} }, + { "spec11.json", false, {} }, + { "spec12.json", false, {} }, + { "spec13.json", false, {} }, + { "spec14.json", true, {} }, + { "spec15.json", true, {} }, + { "spec16.json", true, {} }, + { "spec17.json", false, {} }, + { "spec18.json", false, {} }, + { "spec19.json", false, {} }, + { "spec20.json", true, {} }, + { "spec21.json", false, {} }, + { "spec22.json", false, {} }, + { "spec23.json", false, {} }, + { "spec24.json", false, {} }, + { "spec25.json", false, {} }, + { "spec26.json", false, {} }, + { "spec27.json", false, {} }, + { "spec28.json", false, {} }, + { "spec29.json", false, {} }, + { "spec30.json", false, {} }, + { "spec31.json", false, {} }, + { "spec32.json", false, {} }, + { "spec33.json", false, {} }, + { "spec34.json", false, {} }, + { "spec35.json", false, {} }, + { "spec36.json", false, {} }, + { "noexist.json", false, {} }, }; -static bool testSpec(const std::string& path, - cmCTestResourceSpec::ReadFileResult expectedResult, +static bool testSpec(const std::string& path, bool expectedResult, const cmCTestResourceSpec& expected) { cmCTestResourceSpec actual; auto result = actual.ReadFromJSONFile(path); if (result != expectedResult) { - std::cout << "ReadFromJSONFile(\"" << path << "\") returned \"" - << cmCTestResourceSpec::ResultToString(result) - << "\", should be \"" - << cmCTestResourceSpec::ResultToString(expectedResult) << "\"" - << std::endl; + std::cout << "ReadFromJSONFile(\"" << path << "\") failed \"" << std::endl; return false; } diff --git a/Tests/CMakeLib/testCommon.h b/Tests/CMakeLib/testCommon.h new file mode 100644 index 0000000..bd2d54e --- /dev/null +++ b/Tests/CMakeLib/testCommon.h @@ -0,0 +1,30 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <functional> +#include <iostream> +#include <vector> + +#define ASSERT_TRUE(x) \ + do { \ + if (!(x)) { \ + std::cout << "ASSERT_TRUE(" #x ") failed on line " << __LINE__ << "\n"; \ + return false; \ + } \ + } while (false) + +inline int runTests(std::vector<std::function<bool()>> const& tests) +{ + for (auto const& test : tests) { + if (!test()) { + return 1; + } + std::cout << "."; + } + + std::cout << " Passed" << std::endl; + return 0; +} + +#define BOOL_STRING(b) ((b) ? "TRUE" : "FALSE") diff --git a/Tests/CMakeLib/testDebugger.h b/Tests/CMakeLib/testDebugger.h new file mode 100644 index 0000000..d8d2caa --- /dev/null +++ b/Tests/CMakeLib/testDebugger.h @@ -0,0 +1,103 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <memory> +#include <vector> + +#include "cmDebuggerAdapter.h" +#include "cmDebuggerProtocol.h" +#include "cmListFileCache.h" +#include "cmMessenger.h" +#include <cmcppdap/include/dap/io.h> +#include <cmcppdap/include/dap/session.h> +#include <cmcppdap/include/dap/types.h> + +#include "testCommon.h" + +#define ASSERT_VARIABLE(x, expectedName, expectedValue, expectedType) \ + do { \ + ASSERT_TRUE(x.name == expectedName); \ + ASSERT_TRUE(x.value == expectedValue); \ + if (expectedType == nullptr) { \ + ASSERT_TRUE(x.type == dap::optional<dap::string>()); \ + } else { \ + ASSERT_TRUE(x.type == dap::optional<dap::string>(expectedType)); \ + if (std::string(expectedType) == "collection") { \ + ASSERT_TRUE(x.variablesReference != 0); \ + } \ + } \ + ASSERT_TRUE(x.evaluateName.has_value() == false); \ + } while (false) + +#define ASSERT_VARIABLE_REFERENCE(x, expectedName, expectedValue, \ + expectedType, expectedReference) \ + do { \ + ASSERT_VARIABLE(x, expectedName, expectedValue, expectedType); \ + ASSERT_TRUE(x.variablesReference == (expectedReference)); \ + } while (false) + +#define ASSERT_VARIABLE_REFERENCE_NOT_ZERO(x, expectedName, expectedValue, \ + expectedType) \ + do { \ + ASSERT_VARIABLE(x, expectedName, expectedValue, expectedType); \ + ASSERT_TRUE(x.variablesReference != 0); \ + } while (false) + +#define ASSERT_BREAKPOINT(x, expectedId, expectedLine, sourcePath, \ + isVerified) \ + do { \ + ASSERT_TRUE(x.id.has_value()); \ + ASSERT_TRUE(x.id.value() == expectedId); \ + ASSERT_TRUE(x.line.has_value()); \ + ASSERT_TRUE(x.line.value() == expectedLine); \ + ASSERT_TRUE(x.source.has_value()); \ + ASSERT_TRUE(x.source.value().path.has_value()); \ + ASSERT_TRUE(x.source.value().path.value() == sourcePath); \ + ASSERT_TRUE(x.verified == isVerified); \ + } while (false) + +class DebuggerTestHelper +{ + std::shared_ptr<dap::ReaderWriter> Client2Debugger = dap::pipe(); + std::shared_ptr<dap::ReaderWriter> Debugger2Client = dap::pipe(); + +public: + std::unique_ptr<dap::Session> Client = dap::Session::create(); + std::unique_ptr<dap::Session> Debugger = dap::Session::create(); + void bind() + { + auto client2server = dap::pipe(); + auto server2client = dap::pipe(); + Client->bind(server2client, client2server); + Debugger->bind(client2server, server2client); + } + std::vector<cmListFileFunction> CreateListFileFunctions(const char* str, + const char* filename) + { + cmMessenger messenger; + cmListFileBacktrace backtrace; + cmListFile listfile; + listfile.ParseString(str, filename, &messenger, backtrace); + return listfile.Functions; + } +}; + +class ScopedThread +{ +public: + template <class... Args> + explicit ScopedThread(Args&&... args) + : Thread(std::forward<Args>(args)...) + { + } + + ~ScopedThread() + { + if (Thread.joinable()) + Thread.join(); + } + +private: + std::thread Thread; +}; diff --git a/Tests/CMakeLib/testDebuggerAdapter.cxx b/Tests/CMakeLib/testDebuggerAdapter.cxx new file mode 100644 index 0000000..e66d990 --- /dev/null +++ b/Tests/CMakeLib/testDebuggerAdapter.cxx @@ -0,0 +1,201 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include <chrono> +#include <cstdio> +#include <functional> +#include <future> +#include <memory> +#include <string> +#include <vector> + +#include <cm3p/cppdap/future.h> +#include <cm3p/cppdap/io.h> +#include <cm3p/cppdap/optional.h> +#include <cm3p/cppdap/protocol.h> +#include <cm3p/cppdap/session.h> +#include <cm3p/cppdap/types.h> + +#include "cmDebuggerAdapter.h" +#include "cmDebuggerProtocol.h" +#include "cmVersionConfig.h" + +#include "testCommon.h" +#include "testDebugger.h" + +class DebuggerLocalConnection : public cmDebugger::cmDebuggerConnection +{ +public: + DebuggerLocalConnection() + : ClientToDebugger(dap::pipe()) + , DebuggerToClient(dap::pipe()) + { + } + + bool StartListening(std::string& errorMessage) override + { + errorMessage = ""; + return true; + } + void WaitForConnection() override {} + + std::shared_ptr<dap::Reader> GetReader() override + { + return ClientToDebugger; + }; + + std::shared_ptr<dap::Writer> GetWriter() override + { + return DebuggerToClient; + } + + std::shared_ptr<dap::ReaderWriter> ClientToDebugger; + std::shared_ptr<dap::ReaderWriter> DebuggerToClient; +}; + +bool runTest(std::function<bool(dap::Session&)> onThreadExitedEvent) +{ + std::promise<bool> debuggerAdapterInitializedPromise; + std::future<bool> debuggerAdapterInitializedFuture = + debuggerAdapterInitializedPromise.get_future(); + + std::promise<bool> initializedEventReceivedPromise; + std::future<bool> initializedEventReceivedFuture = + initializedEventReceivedPromise.get_future(); + + std::promise<bool> exitedEventReceivedPromise; + std::future<bool> exitedEventReceivedFuture = + exitedEventReceivedPromise.get_future(); + + std::promise<bool> terminatedEventReceivedPromise; + std::future<bool> terminatedEventReceivedFuture = + terminatedEventReceivedPromise.get_future(); + + std::promise<bool> threadStartedPromise; + std::future<bool> threadStartedFuture = threadStartedPromise.get_future(); + + std::promise<bool> threadExitedPromise; + std::future<bool> threadExitedFuture = threadExitedPromise.get_future(); + + std::promise<bool> disconnectResponseReceivedPromise; + std::future<bool> disconnectResponseReceivedFuture = + disconnectResponseReceivedPromise.get_future(); + + auto futureTimeout = std::chrono::seconds(60); + + auto connection = std::make_shared<DebuggerLocalConnection>(); + std::unique_ptr<dap::Session> client = dap::Session::create(); + client->registerHandler([&](const dap::InitializedEvent& e) { + (void)e; + initializedEventReceivedPromise.set_value(true); + }); + client->registerHandler([&](const dap::ExitedEvent& e) { + (void)e; + exitedEventReceivedPromise.set_value(true); + }); + client->registerHandler([&](const dap::TerminatedEvent& e) { + (void)e; + terminatedEventReceivedPromise.set_value(true); + }); + client->registerHandler([&](const dap::ThreadEvent& e) { + if (e.reason == "started") { + threadStartedPromise.set_value(true); + } else if (e.reason == "exited") { + threadExitedPromise.set_value(true); + } + }); + + client->bind(connection->DebuggerToClient, connection->ClientToDebugger); + + ScopedThread debuggerThread([&]() -> int { + std::shared_ptr<cmDebugger::cmDebuggerAdapter> debuggerAdapter = + std::make_shared<cmDebugger::cmDebuggerAdapter>( + connection, dap::file(stdout, false)); + + debuggerAdapterInitializedPromise.set_value(true); + debuggerAdapter->ReportExitCode(0); + + // Ensure the disconnectResponse has been received before + // destructing debuggerAdapter. + ASSERT_TRUE(disconnectResponseReceivedFuture.wait_for(futureTimeout) == + std::future_status::ready); + return 0; + }); + + dap::CMakeInitializeRequest initializeRequest; + auto initializeResponse = client->send(initializeRequest).get(); + ASSERT_TRUE(initializeResponse.response.cmakeVersion.full == CMake_VERSION); + ASSERT_TRUE(initializeResponse.response.cmakeVersion.major == + CMake_VERSION_MAJOR); + ASSERT_TRUE(initializeResponse.response.cmakeVersion.minor == + CMake_VERSION_MINOR); + ASSERT_TRUE(initializeResponse.response.cmakeVersion.patch == + CMake_VERSION_PATCH); + ASSERT_TRUE(initializeResponse.response.supportsExceptionInfoRequest); + ASSERT_TRUE( + initializeResponse.response.exceptionBreakpointFilters.has_value()); + + dap::LaunchRequest launchRequest; + auto launchResponse = client->send(launchRequest).get(); + ASSERT_TRUE(!launchResponse.error); + + dap::ConfigurationDoneRequest configurationDoneRequest; + auto configurationDoneResponse = + client->send(configurationDoneRequest).get(); + ASSERT_TRUE(!configurationDoneResponse.error); + + ASSERT_TRUE(debuggerAdapterInitializedFuture.wait_for(futureTimeout) == + std::future_status::ready); + ASSERT_TRUE(initializedEventReceivedFuture.wait_for(futureTimeout) == + std::future_status::ready); + ASSERT_TRUE(threadStartedFuture.wait_for(futureTimeout) == + std::future_status::ready); + ASSERT_TRUE(threadExitedFuture.wait_for(futureTimeout) == + std::future_status::ready); + + if (onThreadExitedEvent) { + ASSERT_TRUE(onThreadExitedEvent(*client)); + } + + ASSERT_TRUE(exitedEventReceivedFuture.wait_for(futureTimeout) == + std::future_status::ready); + ASSERT_TRUE(terminatedEventReceivedFuture.wait_for(futureTimeout) == + std::future_status::ready); + + dap::DisconnectRequest disconnectRequest; + auto disconnectResponse = client->send(disconnectRequest).get(); + disconnectResponseReceivedPromise.set_value(true); + ASSERT_TRUE(!disconnectResponse.error); + + return true; +} + +bool testBasicProtocol() +{ + return runTest(nullptr); +} + +bool testThreadsRequestAfterThreadExitedEvent() +{ + return runTest([](dap::Session& session) -> bool { + // Try requesting threads again after receiving the thread exited event. + // Some clients do this to ensure that their thread list is up-to-date. + dap::ThreadsRequest threadsRequest; + auto threadsResponse = session.send(threadsRequest).get(); + ASSERT_TRUE(!threadsResponse.error); + + // CMake only has one DAP thread. Once that thread exits, there should be + // no threads left. + ASSERT_TRUE(threadsResponse.response.threads.empty()); + + return true; + }); +} + +int testDebuggerAdapter(int, char*[]) +{ + return runTests(std::vector<std::function<bool()>>{ + testBasicProtocol, + testThreadsRequestAfterThreadExitedEvent, + }); +} diff --git a/Tests/CMakeLib/testDebuggerAdapterPipe.cxx b/Tests/CMakeLib/testDebuggerAdapterPipe.cxx new file mode 100644 index 0000000..643661d --- /dev/null +++ b/Tests/CMakeLib/testDebuggerAdapterPipe.cxx @@ -0,0 +1,184 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include <chrono> +#include <cstdio> +#include <functional> +#include <future> +#include <iostream> +#include <memory> +#include <stdexcept> +#include <string> +#include <vector> + +#include <cm3p/cppdap/future.h> +#include <cm3p/cppdap/io.h> +#include <cm3p/cppdap/optional.h> +#include <cm3p/cppdap/protocol.h> +#include <cm3p/cppdap/session.h> +#include <cm3p/cppdap/types.h> + +#include "cmDebuggerAdapter.h" +#include "cmDebuggerPipeConnection.h" +#include "cmDebuggerProtocol.h" +#include "cmVersionConfig.h" + +#ifdef _WIN32 +# include "cmCryptoHash.h" +# include "cmSystemTools.h" +#endif + +#include "testCommon.h" +#include "testDebugger.h" + +bool testProtocolWithPipes() +{ + std::promise<void> debuggerConnectionCreatedPromise; + std::future<void> debuggerConnectionCreatedFuture = + debuggerConnectionCreatedPromise.get_future(); + + std::future<void> startedListeningFuture; + + std::promise<bool> debuggerAdapterInitializedPromise; + std::future<bool> debuggerAdapterInitializedFuture = + debuggerAdapterInitializedPromise.get_future(); + + std::promise<bool> initializedEventReceivedPromise; + std::future<bool> initializedEventReceivedFuture = + initializedEventReceivedPromise.get_future(); + + std::promise<bool> exitedEventReceivedPromise; + std::future<bool> exitedEventReceivedFuture = + exitedEventReceivedPromise.get_future(); + + std::promise<bool> terminatedEventReceivedPromise; + std::future<bool> terminatedEventReceivedFuture = + terminatedEventReceivedPromise.get_future(); + + std::promise<bool> threadStartedPromise; + std::future<bool> threadStartedFuture = threadStartedPromise.get_future(); + + std::promise<bool> threadExitedPromise; + std::future<bool> threadExitedFuture = threadExitedPromise.get_future(); + + std::promise<bool> disconnectResponseReceivedPromise; + std::future<bool> disconnectResponseReceivedFuture = + disconnectResponseReceivedPromise.get_future(); + + auto futureTimeout = std::chrono::seconds(60); + +#ifdef _WIN32 + std::string namedPipe = R"(\\.\pipe\LOCAL\CMakeDebuggerPipe2_)" + + cmCryptoHash(cmCryptoHash::AlgoSHA256) + .HashString(cmSystemTools::GetCurrentWorkingDirectory()); +#else + std::string namedPipe = "CMakeDebuggerPipe2"; +#endif + + std::unique_ptr<dap::Session> client = dap::Session::create(); + client->registerHandler([&](const dap::InitializedEvent& e) { + (void)e; + initializedEventReceivedPromise.set_value(true); + }); + client->registerHandler([&](const dap::ExitedEvent& e) { + (void)e; + exitedEventReceivedPromise.set_value(true); + }); + client->registerHandler([&](const dap::TerminatedEvent& e) { + (void)e; + terminatedEventReceivedPromise.set_value(true); + }); + client->registerHandler([&](const dap::ThreadEvent& e) { + if (e.reason == "started") { + threadStartedPromise.set_value(true); + } else if (e.reason == "exited") { + threadExitedPromise.set_value(true); + } + }); + + ScopedThread debuggerThread([&]() -> int { + try { + auto connection = + std::make_shared<cmDebugger::cmDebuggerPipeConnection>(namedPipe); + startedListeningFuture = connection->StartedListening.get_future(); + debuggerConnectionCreatedPromise.set_value(); + std::shared_ptr<cmDebugger::cmDebuggerAdapter> debuggerAdapter = + std::make_shared<cmDebugger::cmDebuggerAdapter>( + connection, dap::file(stdout, false)); + + debuggerAdapterInitializedPromise.set_value(true); + debuggerAdapter->ReportExitCode(0); + + // Ensure the disconnectResponse has been received before + // destructing debuggerAdapter. + ASSERT_TRUE(disconnectResponseReceivedFuture.wait_for(futureTimeout) == + std::future_status::ready); + return 0; + } catch (const std::runtime_error& error) { + std::cerr << "Error: Failed to create debugger adapter.\n"; + std::cerr << error.what() << "\n"; + return -1; + } + }); + + ASSERT_TRUE(debuggerConnectionCreatedFuture.wait_for(futureTimeout) == + std::future_status::ready); + ASSERT_TRUE(startedListeningFuture.wait_for(futureTimeout) == + std::future_status::ready); + + auto client2Debugger = + std::make_shared<cmDebugger::cmDebuggerPipeClient>(namedPipe); + client2Debugger->Start(); + client2Debugger->WaitForConnection(); + client->bind(client2Debugger, client2Debugger); + + dap::CMakeInitializeRequest initializeRequest; + auto response = client->send(initializeRequest); + auto initializeResponse = response.get(); + ASSERT_TRUE(!initializeResponse.error); + ASSERT_TRUE(initializeResponse.response.cmakeVersion.full == CMake_VERSION); + ASSERT_TRUE(initializeResponse.response.cmakeVersion.major == + CMake_VERSION_MAJOR); + ASSERT_TRUE(initializeResponse.response.cmakeVersion.minor == + CMake_VERSION_MINOR); + ASSERT_TRUE(initializeResponse.response.cmakeVersion.patch == + CMake_VERSION_PATCH); + ASSERT_TRUE(initializeResponse.response.supportsExceptionInfoRequest); + ASSERT_TRUE( + initializeResponse.response.exceptionBreakpointFilters.has_value()); + dap::LaunchRequest launchRequest; + auto launchResponse = client->send(launchRequest).get(); + ASSERT_TRUE(!launchResponse.error); + + dap::ConfigurationDoneRequest configurationDoneRequest; + auto configurationDoneResponse = + client->send(configurationDoneRequest).get(); + ASSERT_TRUE(!configurationDoneResponse.error); + + ASSERT_TRUE(debuggerAdapterInitializedFuture.wait_for(futureTimeout) == + std::future_status::ready); + ASSERT_TRUE(initializedEventReceivedFuture.wait_for(futureTimeout) == + std::future_status::ready); + ASSERT_TRUE(terminatedEventReceivedFuture.wait_for(futureTimeout) == + std::future_status::ready); + ASSERT_TRUE(threadStartedFuture.wait_for(futureTimeout) == + std::future_status::ready); + ASSERT_TRUE(threadExitedFuture.wait_for(futureTimeout) == + std::future_status::ready); + ASSERT_TRUE(exitedEventReceivedFuture.wait_for(futureTimeout) == + std::future_status::ready); + + dap::DisconnectRequest disconnectRequest; + auto disconnectResponse = client->send(disconnectRequest).get(); + disconnectResponseReceivedPromise.set_value(true); + ASSERT_TRUE(!disconnectResponse.error); + + return true; +} + +int testDebuggerAdapterPipe(int, char*[]) +{ + return runTests(std::vector<std::function<bool()>>{ + testProtocolWithPipes, + }); +} diff --git a/Tests/CMakeLib/testDebuggerBreakpointManager.cxx b/Tests/CMakeLib/testDebuggerBreakpointManager.cxx new file mode 100644 index 0000000..f654442 --- /dev/null +++ b/Tests/CMakeLib/testDebuggerBreakpointManager.cxx @@ -0,0 +1,185 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include <atomic> +#include <chrono> +#include <functional> +#include <future> +#include <memory> +#include <string> +#include <vector> + +#include <cm3p/cppdap/future.h> +#include <cm3p/cppdap/optional.h> +#include <cm3p/cppdap/protocol.h> +#include <cm3p/cppdap/session.h> +#include <cm3p/cppdap/types.h> + +#include "cmDebuggerBreakpointManager.h" +#include "cmDebuggerSourceBreakpoint.h" // IWYU pragma: keep +#include "cmListFileCache.h" + +#include "testCommon.h" +#include "testDebugger.h" + +static bool testHandleBreakpointRequestBeforeFileIsLoaded() +{ + // Arrange + DebuggerTestHelper helper; + cmDebugger::cmDebuggerBreakpointManager breakpointManager( + helper.Debugger.get()); + helper.bind(); + dap::SetBreakpointsRequest setBreakpointRequest; + std::string sourcePath = "C:/CMakeLists.txt"; + setBreakpointRequest.source.path = sourcePath; + dap::array<dap::SourceBreakpoint> sourceBreakpoints(3); + sourceBreakpoints[0].line = 1; + sourceBreakpoints[1].line = 2; + sourceBreakpoints[2].line = 3; + setBreakpointRequest.breakpoints = sourceBreakpoints; + + // Act + auto got = helper.Client->send(setBreakpointRequest).get(); + + // Assert + auto& response = got.response; + ASSERT_TRUE(!got.error); + ASSERT_TRUE(response.breakpoints.size() == sourceBreakpoints.size()); + ASSERT_BREAKPOINT(response.breakpoints[0], 0, sourceBreakpoints[0].line, + sourcePath, false); + ASSERT_BREAKPOINT(response.breakpoints[1], 1, sourceBreakpoints[1].line, + sourcePath, false); + ASSERT_BREAKPOINT(response.breakpoints[2], 2, sourceBreakpoints[2].line, + sourcePath, false); + ASSERT_TRUE(breakpointManager.GetBreakpointCount() == 3); + + // setBreakpoints should override any existing breakpoints + setBreakpointRequest.breakpoints.value().clear(); + helper.Client->send(setBreakpointRequest).get(); + ASSERT_TRUE(breakpointManager.GetBreakpointCount() == 0); + + return true; +} + +static bool testHandleBreakpointRequestAfterFileIsLoaded() +{ + // Arrange + DebuggerTestHelper helper; + std::atomic<bool> notExpectBreakpointEvents(true); + helper.Client->registerHandler([&](const dap::BreakpointEvent&) { + notExpectBreakpointEvents.store(false); + }); + + cmDebugger::cmDebuggerBreakpointManager breakpointManager( + helper.Debugger.get()); + helper.bind(); + std::string sourcePath = "C:/CMakeLists.txt"; + std::vector<cmListFileFunction> functions = helper.CreateListFileFunctions( + "# Comment1\nset(var1 foo)\n# Comment2\nset(var2\nbar)\n", + sourcePath.c_str()); + + breakpointManager.SourceFileLoaded(sourcePath, functions); + dap::SetBreakpointsRequest setBreakpointRequest; + setBreakpointRequest.source.path = sourcePath; + dap::array<dap::SourceBreakpoint> sourceBreakpoints(5); + sourceBreakpoints[0].line = 1; + sourceBreakpoints[1].line = 2; + sourceBreakpoints[2].line = 3; + sourceBreakpoints[3].line = 4; + sourceBreakpoints[4].line = 5; + setBreakpointRequest.breakpoints = sourceBreakpoints; + + // Act + auto got = helper.Client->send(setBreakpointRequest).get(); + + // Assert + auto& response = got.response; + ASSERT_TRUE(!got.error); + ASSERT_TRUE(response.breakpoints.size() == sourceBreakpoints.size()); + // Line 1 is a comment. Move it to next valid function, which is line 2. + ASSERT_BREAKPOINT(response.breakpoints[0], 0, 2, sourcePath, true); + ASSERT_BREAKPOINT(response.breakpoints[1], 1, sourceBreakpoints[1].line, + sourcePath, true); + // Line 3 is a comment. Move it to next valid function, which is line 4. + ASSERT_BREAKPOINT(response.breakpoints[2], 2, 4, sourcePath, true); + ASSERT_BREAKPOINT(response.breakpoints[3], 3, sourceBreakpoints[3].line, + sourcePath, true); + // Line 5 is the 2nd part of line 4 function. No valid function after line 5, + // show the breakpoint at line 4. + ASSERT_BREAKPOINT(response.breakpoints[4], 4, sourceBreakpoints[3].line, + sourcePath, true); + + ASSERT_TRUE(notExpectBreakpointEvents.load()); + ASSERT_TRUE(breakpointManager.GetBreakpointCount() == 5); + + // setBreakpoints should override any existing breakpoints + setBreakpointRequest.breakpoints.value().clear(); + helper.Client->send(setBreakpointRequest).get(); + ASSERT_TRUE(breakpointManager.GetBreakpointCount() == 0); + + return true; +} + +static bool testSourceFileLoadedAfterHandleBreakpointRequest() +{ + // Arrange + DebuggerTestHelper helper; + std::vector<dap::BreakpointEvent> breakpointEvents; + std::atomic<int> remainingBreakpointEvents(5); + std::promise<void> allBreakpointEventsReceivedPromise; + std::future<void> allBreakpointEventsReceivedFuture = + allBreakpointEventsReceivedPromise.get_future(); + helper.Client->registerHandler([&](const dap::BreakpointEvent& event) { + breakpointEvents.emplace_back(event); + if (--remainingBreakpointEvents == 0) { + allBreakpointEventsReceivedPromise.set_value(); + } + }); + cmDebugger::cmDebuggerBreakpointManager breakpointManager( + helper.Debugger.get()); + helper.bind(); + dap::SetBreakpointsRequest setBreakpointRequest; + std::string sourcePath = "C:/CMakeLists.txt"; + setBreakpointRequest.source.path = sourcePath; + dap::array<dap::SourceBreakpoint> sourceBreakpoints(5); + sourceBreakpoints[0].line = 1; + sourceBreakpoints[1].line = 2; + sourceBreakpoints[2].line = 3; + sourceBreakpoints[3].line = 4; + sourceBreakpoints[4].line = 5; + setBreakpointRequest.breakpoints = sourceBreakpoints; + std::vector<cmListFileFunction> functions = helper.CreateListFileFunctions( + "# Comment1\nset(var1 foo)\n# Comment2\nset(var2\nbar)\n", + sourcePath.c_str()); + auto got = helper.Client->send(setBreakpointRequest).get(); + + // Act + breakpointManager.SourceFileLoaded(sourcePath, functions); + ASSERT_TRUE(allBreakpointEventsReceivedFuture.wait_for( + std::chrono::seconds(10)) == std::future_status::ready); + + // Assert + ASSERT_TRUE(breakpointEvents.size() > 0); + // Line 1 is a comment. Move it to next valid function, which is line 2. + ASSERT_BREAKPOINT(breakpointEvents[0].breakpoint, 0, 2, sourcePath, true); + ASSERT_BREAKPOINT(breakpointEvents[1].breakpoint, 1, + sourceBreakpoints[1].line, sourcePath, true); + // Line 3 is a comment. Move it to next valid function, which is line 4. + ASSERT_BREAKPOINT(breakpointEvents[2].breakpoint, 2, 4, sourcePath, true); + ASSERT_BREAKPOINT(breakpointEvents[3].breakpoint, 3, + sourceBreakpoints[3].line, sourcePath, true); + // Line 5 is the 2nd part of line 4 function. No valid function after line 5, + // show the breakpoint at line 4. + ASSERT_BREAKPOINT(breakpointEvents[4].breakpoint, 4, + sourceBreakpoints[3].line, sourcePath, true); + return true; +} + +int testDebuggerBreakpointManager(int, char*[]) +{ + return runTests(std::vector<std::function<bool()>>{ + testHandleBreakpointRequestBeforeFileIsLoaded, + testHandleBreakpointRequestAfterFileIsLoaded, + testSourceFileLoadedAfterHandleBreakpointRequest, + }); +} diff --git a/Tests/CMakeLib/testDebuggerNamedPipe.cxx b/Tests/CMakeLib/testDebuggerNamedPipe.cxx new file mode 100644 index 0000000..d2b0728 --- /dev/null +++ b/Tests/CMakeLib/testDebuggerNamedPipe.cxx @@ -0,0 +1,218 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include <chrono> +#include <cstdio> +#include <exception> +#include <iostream> +#include <memory> +#include <sstream> +#include <stdexcept> +#include <string> +#include <thread> +#include <vector> + +#include <cm3p/cppdap/io.h> + +#include "cmsys/RegularExpression.hxx" + +#include "cmDebuggerPipeConnection.h" +#include "cmSystemTools.h" + +#ifdef _WIN32 +# include "cmCryptoHash.h" +#endif + +static void sendCommands(std::shared_ptr<dap::ReaderWriter> const& debugger, + int delayMs, + std::vector<std::string> const& initCommands) +{ + for (const auto& command : initCommands) { + std::string contentLength = "Content-Length:"; + contentLength += std::to_string(command.size()) + "\r\n\r\n"; + debugger->write(contentLength.c_str(), contentLength.size()); + if (!debugger->write(command.c_str(), command.size())) { + std::cout << "debugger write error" << std::endl; + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds(delayMs)); + } +} + +/** \brief Test CMake debugger named pipe. + * + * Test CMake debugger named pipe by + * 1. Create a named pipe for DAP traffic between the client and the debugger. + * 2. Create a client thread to wait for the debugger connection. + * - Once the debugger is connected, send the minimum required commands to + * get debugger going. + * - Wait for the CMake to complete the cache generation + * - Send the disconnect command. + * - Read and store the debugger's responses for validation. + * 3. Run the CMake command with debugger on and wait for it to complete. + * 4. Validate the response to ensure we are getting the expected responses. + * + */ +int runTest(int argc, char* argv[]) +{ + if (argc < 3) { + std::cout << "Usage:\n"; + std::cout << "\t(project mode) TestDebuggerNamedPipe <CMakePath> " + "<SourceFolder> <OutputFolder>\n"; + std::cout << "\t(script mode) TestDebuggerNamedPipe <CMakePath> " + "<ScriptPath>\n"; + return 1; + } + + bool scriptMode = argc == 3; + +#ifdef _WIN32 + std::string namedPipe = R"(\\.\pipe\LOCAL\CMakeDebuggerPipe_)" + + cmCryptoHash(cmCryptoHash::AlgoSHA256) + .HashString(scriptMode ? argv[2] : argv[3]); +#else + std::string namedPipe = + std::string("CMakeDebuggerPipe") + (scriptMode ? "Script" : "Project"); +#endif + + std::vector<std::string> cmakeCommand; + cmakeCommand.emplace_back(argv[1]); + cmakeCommand.emplace_back("--debugger"); + cmakeCommand.emplace_back("--debugger-pipe"); + cmakeCommand.emplace_back(namedPipe); + + if (scriptMode) { + cmakeCommand.emplace_back("-P"); + cmakeCommand.emplace_back(argv[2]); + } else { + cmakeCommand.emplace_back("-S"); + cmakeCommand.emplace_back(argv[2]); + cmakeCommand.emplace_back("-B"); + cmakeCommand.emplace_back(argv[3]); + } + + // Capture debugger response stream. + std::stringstream debuggerResponseStream; + + // Start the debugger client process. + std::thread clientThread([&]() { + // Poll until the pipe server is running. Clients can also look for a magic + // string in the CMake output, but this is easier for the test case. + std::shared_ptr<cmDebugger::cmDebuggerPipeClient> client; + int attempt = 0; + do { + attempt++; + try { + client = std::make_shared<cmDebugger::cmDebuggerPipeClient>(namedPipe); + client->Start(); + client->WaitForConnection(); + std::cout << "cmDebuggerPipeClient connected.\n"; + break; + } catch (std::runtime_error&) { + std::cout << "Failed attempt " << attempt + << " to connect to pipe server. Retrying.\n"; + client.reset(); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + } while (attempt < 50); // 10 seconds + + if (attempt >= 50) { + return -1; + } + + // Send init commands to get debugger going. + sendCommands( + client, 400, + { "{\"arguments\":{\"adapterID\":\"\"},\"command\":\"initialize\"," + "\"seq\":" + "1,\"type\":\"request\"}", + "{\"arguments\":{},\"command\":\"launch\",\"seq\":2,\"type\":" + "\"request\"}", + "{\"arguments\":{},\"command\":\"configurationDone\",\"seq\":3," + "\"type\":" + "\"request\"}" }); + + // Look for "exitCode" as a sign that configuration has completed and + // it's now safe to disconnect. + for (;;) { + char buffer[1]; + size_t result = client->read(buffer, 1); + if (result != 1) { + std::cout << "debugger read error: " << result << std::endl; + break; + } + debuggerResponseStream << buffer[0]; + if (debuggerResponseStream.str().find("exitCode") != std::string::npos) { + break; + } + } + + // Send disconnect command. + sendCommands( + client, 200, + { "{\"arguments\":{},\"command\":\"disconnect\",\"seq\":4,\"type\":" + "\"request\"}" }); + + // Read any remaining debugger responses. + for (;;) { + char buffer[1]; + size_t result = client->read(buffer, 1); + if (result != 1) { + std::cout << "debugger read error: " << result << std::endl; + break; + } + debuggerResponseStream << buffer[0]; + } + + client->close(); + + return 0; + }); + + if (!cmSystemTools::RunSingleCommand(cmakeCommand, nullptr, nullptr, nullptr, + nullptr, cmSystemTools::OUTPUT_MERGE)) { + std::cout << "Error running command" << std::endl; + return -1; + } + + clientThread.join(); + + auto debuggerResponse = debuggerResponseStream.str(); + + std::vector<std::string> expectedResponses = { + R"("event" : "initialized".*"type" : "event")", + R"("command" : "launch".*"success" : true.*"type" : "response")", + R"("command" : "configurationDone".*"success" : true.*"type" : "response")", + R"("reason" : "started".*"threadId" : 1.*"event" : "thread".*"type" : "event")", + R"("reason" : "exited".*"threadId" : 1.*"event" : "thread".*"type" : "event")", + R"("exitCode" : 0.*"event" : "exited".*"type" : "event")", + R"("command" : "disconnect".*"success" : true.*"type" : "response")" + }; + + for (auto& regexString : expectedResponses) { + cmsys::RegularExpression regex(regexString); + if (!regex.find(debuggerResponse)) { + std::cout << "Expected response not found: " << regexString << std::endl; + std::cout << debuggerResponse << std::endl; + return -1; + } + } + + return 0; +} + +int main(int argc, char* argv[]) +{ + try { + return runTest(argc, argv); + } catch (const std::exception& ex) { + std::cout << "An exception occurred: " << ex.what() << std::endl; + return -1; + } catch (const std::string& ex) { + std::cout << "An exception occurred: " << ex << std::endl; + return -1; + } catch (...) { + std::cout << "An unknown exception occurred" << std::endl; + return -1; + } +} diff --git a/Tests/CMakeLib/testDebuggerVariables.cxx b/Tests/CMakeLib/testDebuggerVariables.cxx new file mode 100644 index 0000000..0d8d18d --- /dev/null +++ b/Tests/CMakeLib/testDebuggerVariables.cxx @@ -0,0 +1,214 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include <cstdint> +#include <functional> +#include <memory> +#include <string> +#include <unordered_set> +#include <vector> + +#include <cm3p/cppdap/optional.h> +#include <cm3p/cppdap/protocol.h> +#include <cm3p/cppdap/types.h> + +#include "cmDebuggerVariables.h" +#include "cmDebuggerVariablesManager.h" + +#include "testCommon.h" +#include "testDebugger.h" + +static dap::VariablesRequest CreateVariablesRequest(int64_t reference) +{ + dap::VariablesRequest variableRequest; + variableRequest.variablesReference = reference; + return variableRequest; +} + +static bool testUniqueIds() +{ + auto variablesManager = + std::make_shared<cmDebugger::cmDebuggerVariablesManager>(); + + std::unordered_set<int64_t> variableIds; + bool noDuplicateIds = true; + for (int i = 0; i < 10000 && noDuplicateIds; ++i) { + auto variable = + cmDebugger::cmDebuggerVariables(variablesManager, "Locals", true, []() { + return std::vector<cmDebugger::cmDebuggerVariableEntry>(); + }); + + if (variableIds.find(variable.GetId()) != variableIds.end()) { + noDuplicateIds = false; + } + variableIds.insert(variable.GetId()); + } + + ASSERT_TRUE(noDuplicateIds); + + return true; +} + +static bool testConstructors() +{ + auto variablesManager = + std::make_shared<cmDebugger::cmDebuggerVariablesManager>(); + + auto parent = std::make_shared<cmDebugger::cmDebuggerVariables>( + variablesManager, "Parent", true, [=]() { + return std::vector<cmDebugger::cmDebuggerVariableEntry>{ + { "ParentKey", "ParentValue", "ParentType" } + }; + }); + + auto children1 = std::make_shared<cmDebugger::cmDebuggerVariables>( + variablesManager, "Children1", true, [=]() { + return std::vector<cmDebugger::cmDebuggerVariableEntry>{ + { "ChildKey1", "ChildValue1", "ChildType1" }, + { "ChildKey2", "ChildValue2", "ChildType2" } + }; + }); + + parent->AddSubVariables(children1); + + auto children2 = std::make_shared<cmDebugger::cmDebuggerVariables>( + variablesManager, "Children2", true); + + auto grandChildren21 = std::make_shared<cmDebugger::cmDebuggerVariables>( + variablesManager, "GrandChildren21", true); + grandChildren21->SetValue("GrandChildren21 Value"); + children2->AddSubVariables(grandChildren21); + parent->AddSubVariables(children2); + + dap::array<dap::Variable> variables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(parent->GetId())); + ASSERT_TRUE(variables.size() == 3); + ASSERT_VARIABLE_REFERENCE(variables[0], "Children1", "", "collection", + children1->GetId()); + ASSERT_VARIABLE_REFERENCE(variables[1], "Children2", "", "collection", + children2->GetId()); + ASSERT_VARIABLE(variables[2], "ParentKey", "ParentValue", "ParentType"); + + variables = variablesManager->HandleVariablesRequest( + CreateVariablesRequest(children1->GetId())); + ASSERT_TRUE(variables.size() == 2); + ASSERT_VARIABLE(variables[0], "ChildKey1", "ChildValue1", "ChildType1"); + ASSERT_VARIABLE(variables[1], "ChildKey2", "ChildValue2", "ChildType2"); + + variables = variablesManager->HandleVariablesRequest( + CreateVariablesRequest(children2->GetId())); + ASSERT_TRUE(variables.size() == 1); + ASSERT_VARIABLE_REFERENCE(variables[0], "GrandChildren21", + "GrandChildren21 Value", "collection", + grandChildren21->GetId()); + + return true; +} + +static bool testIgnoreEmptyStringEntries() +{ + auto variablesManager = + std::make_shared<cmDebugger::cmDebuggerVariablesManager>(); + + auto vars = std::make_shared<cmDebugger::cmDebuggerVariables>( + variablesManager, "Variables", true, []() { + return std::vector<cmDebugger::cmDebuggerVariableEntry>{ + { "IntValue1", 5 }, { "StringValue1", "" }, + { "StringValue2", "foo" }, { "StringValue3", "" }, + { "StringValue4", "bar" }, { "StringValue5", "" }, + { "IntValue2", int64_t(99) }, { "BooleanTrue", true }, + { "BooleanFalse", false }, + }; + }); + + vars->SetIgnoreEmptyStringEntries(true); + vars->SetEnableSorting(false); + + dap::array<dap::Variable> variables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(vars->GetId())); + ASSERT_TRUE(variables.size() == 6); + ASSERT_VARIABLE(variables[0], "IntValue1", "5", "int"); + ASSERT_VARIABLE(variables[1], "StringValue2", "foo", "string"); + ASSERT_VARIABLE(variables[2], "StringValue4", "bar", "string"); + ASSERT_VARIABLE(variables[3], "IntValue2", "99", "int"); + ASSERT_VARIABLE(variables[4], "BooleanTrue", "TRUE", "bool"); + ASSERT_VARIABLE(variables[5], "BooleanFalse", "FALSE", "bool"); + + return true; +} + +static bool testSortTheResult() +{ + auto variablesManager = + std::make_shared<cmDebugger::cmDebuggerVariablesManager>(); + + auto vars = std::make_shared<cmDebugger::cmDebuggerVariables>( + variablesManager, "Variables", true, []() { + return std::vector<cmDebugger::cmDebuggerVariableEntry>{ + { "4", "4" }, { "2", "2" }, { "1", "1" }, { "3", "3" }, { "5", "5" }, + }; + }); + + dap::array<dap::Variable> variables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(vars->GetId())); + ASSERT_TRUE(variables.size() == 5); + ASSERT_VARIABLE(variables[0], "1", "1", "string"); + ASSERT_VARIABLE(variables[1], "2", "2", "string"); + ASSERT_VARIABLE(variables[2], "3", "3", "string"); + ASSERT_VARIABLE(variables[3], "4", "4", "string"); + ASSERT_VARIABLE(variables[4], "5", "5", "string"); + + vars->SetEnableSorting(false); + + variables = variablesManager->HandleVariablesRequest( + CreateVariablesRequest(vars->GetId())); + ASSERT_TRUE(variables.size() == 5); + ASSERT_VARIABLE(variables[0], "4", "4", "string"); + ASSERT_VARIABLE(variables[1], "2", "2", "string"); + ASSERT_VARIABLE(variables[2], "1", "1", "string"); + ASSERT_VARIABLE(variables[3], "3", "3", "string"); + ASSERT_VARIABLE(variables[4], "5", "5", "string"); + + return true; +} + +static bool testNoSupportsVariableType() +{ + auto variablesManager = + std::make_shared<cmDebugger::cmDebuggerVariablesManager>(); + + auto vars = std::make_shared<cmDebugger::cmDebuggerVariables>( + variablesManager, "Variables", false, []() { + return std::vector<cmDebugger::cmDebuggerVariableEntry>{ { "test", + "value" } }; + }); + + auto subvars = std::make_shared<cmDebugger::cmDebuggerVariables>( + variablesManager, "Children", false); + + vars->AddSubVariables(subvars); + + dap::array<dap::Variable> variables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(vars->GetId())); + + ASSERT_TRUE(variables.size() == 2); + ASSERT_VARIABLE(variables[0], "Children", "", nullptr); + ASSERT_VARIABLE(variables[1], "test", "value", nullptr); + + return true; +} + +int testDebuggerVariables(int, char*[]) +{ + return runTests(std::vector<std::function<bool()>>{ + testUniqueIds, + testConstructors, + testIgnoreEmptyStringEntries, + testSortTheResult, + testNoSupportsVariableType, + }); +} diff --git a/Tests/CMakeLib/testDebuggerVariablesHelper.cxx b/Tests/CMakeLib/testDebuggerVariablesHelper.cxx new file mode 100644 index 0000000..d61b73b --- /dev/null +++ b/Tests/CMakeLib/testDebuggerVariablesHelper.cxx @@ -0,0 +1,588 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include <functional> +#include <memory> +#include <set> +#include <string> +#include <utility> +#include <vector> + +#include <cm3p/cppdap/optional.h> +#include <cm3p/cppdap/protocol.h> +#include <cm3p/cppdap/types.h> +#include <stddef.h> +#include <stdint.h> + +#include "cmDebuggerStackFrame.h" +#include "cmDebuggerVariables.h" +#include "cmDebuggerVariablesHelper.h" +#include "cmDebuggerVariablesManager.h" +#include "cmFileSet.h" +#include "cmGlobalGenerator.h" +#include "cmListFileCache.h" +#include "cmMakefile.h" +#include "cmPolicies.h" +#include "cmPropertyMap.h" +#include "cmState.h" +#include "cmStateDirectory.h" +#include "cmStateSnapshot.h" +#include "cmStateTypes.h" +#include "cmTarget.h" +#include "cmTest.h" +#include "cmake.h" + +#include "testCommon.h" +#include "testDebugger.h" + +static dap::VariablesRequest CreateVariablesRequest(int64_t reference) +{ + dap::VariablesRequest variableRequest; + variableRequest.variablesReference = reference; + return variableRequest; +} + +struct Dummies +{ + std::shared_ptr<cmake> CMake; + std::shared_ptr<cmMakefile> Makefile; + std::shared_ptr<cmGlobalGenerator> GlobalGenerator; +}; + +static Dummies CreateDummies( + std::string targetName, + std::string currentSourceDirectory = "c:/CurrentSourceDirectory", + std::string currentBinaryDirectory = "c:/CurrentBinaryDirectory") +{ + Dummies dummies; + dummies.CMake = + std::make_shared<cmake>(cmake::RoleProject, cmState::Project); + cmState* state = dummies.CMake->GetState(); + dummies.GlobalGenerator = + std::make_shared<cmGlobalGenerator>(dummies.CMake.get()); + cmStateSnapshot snapshot = state->CreateBaseSnapshot(); + snapshot.GetDirectory().SetCurrentSource(currentSourceDirectory); + snapshot.GetDirectory().SetCurrentBinary(currentBinaryDirectory); + dummies.Makefile = + std::make_shared<cmMakefile>(dummies.GlobalGenerator.get(), snapshot); + dummies.Makefile->CreateNewTarget(targetName, cmStateEnums::EXECUTABLE); + return dummies; +} + +static bool testCreateFromPolicyMap() +{ + auto variablesManager = + std::make_shared<cmDebugger::cmDebuggerVariablesManager>(); + + cmPolicies::PolicyMap policyMap; + policyMap.Set(cmPolicies::CMP0000, cmPolicies::NEW); + policyMap.Set(cmPolicies::CMP0003, cmPolicies::WARN); + policyMap.Set(cmPolicies::CMP0005, cmPolicies::OLD); + auto vars = cmDebugger::cmDebuggerVariablesHelper::Create( + variablesManager, "Locals", true, policyMap); + + dap::array<dap::Variable> variables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(vars->GetId())); + ASSERT_TRUE(variables.size() == 3); + ASSERT_VARIABLE(variables[0], "CMP0000", "NEW", "string"); + ASSERT_VARIABLE(variables[1], "CMP0003", "WARN", "string"); + ASSERT_VARIABLE(variables[2], "CMP0005", "OLD", "string"); + + return true; +} + +static bool testCreateFromPairVector() +{ + auto variablesManager = + std::make_shared<cmDebugger::cmDebuggerVariablesManager>(); + + std::vector<std::pair<std::string, std::string>> pairs = { + { "Foo1", "Bar1" }, { "Foo2", "Bar2" } + }; + + auto vars = cmDebugger::cmDebuggerVariablesHelper::CreateIfAny( + variablesManager, "Locals", true, pairs); + + dap::array<dap::Variable> variables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(vars->GetId())); + + ASSERT_TRUE(vars->GetValue() == std::to_string(pairs.size())); + ASSERT_TRUE(variables.size() == 2); + ASSERT_VARIABLE(variables[0], "Foo1", "Bar1", "string"); + ASSERT_VARIABLE(variables[1], "Foo2", "Bar2", "string"); + + auto none = cmDebugger::cmDebuggerVariablesHelper::CreateIfAny( + variablesManager, "Locals", true, + std::vector<std::pair<std::string, std::string>>()); + + ASSERT_TRUE(none == nullptr); + + return true; +} + +static bool testCreateFromSet() +{ + auto variablesManager = + std::make_shared<cmDebugger::cmDebuggerVariablesManager>(); + + std::set<std::string> set = { "Foo", "Bar" }; + + auto vars = cmDebugger::cmDebuggerVariablesHelper::CreateIfAny( + variablesManager, "Locals", true, set); + + dap::array<dap::Variable> variables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(vars->GetId())); + + ASSERT_TRUE(vars->GetValue() == std::to_string(set.size())); + ASSERT_TRUE(variables.size() == 2); + ASSERT_VARIABLE(variables[0], "[0]", "Bar", "string"); + ASSERT_VARIABLE(variables[1], "[1]", "Foo", "string"); + + auto none = cmDebugger::cmDebuggerVariablesHelper::CreateIfAny( + variablesManager, "Locals", true, std::set<std::string>()); + + ASSERT_TRUE(none == nullptr); + + return true; +} + +static bool testCreateFromStringVector() +{ + auto variablesManager = + std::make_shared<cmDebugger::cmDebuggerVariablesManager>(); + + std::vector<std::string> list = { "Foo", "Bar" }; + + auto vars = cmDebugger::cmDebuggerVariablesHelper::CreateIfAny( + variablesManager, "Locals", true, list); + + dap::array<dap::Variable> variables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(vars->GetId())); + + ASSERT_TRUE(vars->GetValue() == std::to_string(list.size())); + ASSERT_TRUE(variables.size() == 2); + ASSERT_VARIABLE(variables[0], "[0]", "Foo", "string"); + ASSERT_VARIABLE(variables[1], "[1]", "Bar", "string"); + + auto none = cmDebugger::cmDebuggerVariablesHelper::CreateIfAny( + variablesManager, "Locals", true, std::vector<std::string>()); + + ASSERT_TRUE(none == nullptr); + + return true; +} + +static bool testCreateFromTarget() +{ + auto variablesManager = + std::make_shared<cmDebugger::cmDebuggerVariablesManager>(); + + auto dummies = CreateDummies("Foo"); + + auto vars = cmDebugger::cmDebuggerVariablesHelper::CreateIfAny( + variablesManager, "Locals", true, dummies.Makefile->GetOrderedTargets()); + + dap::array<dap::Variable> variables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(vars->GetId())); + + ASSERT_TRUE(variables.size() == 1); + ASSERT_VARIABLE(variables[0], "Foo", "EXECUTABLE", "collection"); + + variables = variablesManager->HandleVariablesRequest( + CreateVariablesRequest(variables[0].variablesReference)); + + ASSERT_TRUE(variables.size() == 15); + ASSERT_VARIABLE(variables[0], "GlobalGenerator", "Generic", "collection"); + ASSERT_VARIABLE(variables[1], "IsAIX", "FALSE", "bool"); + ASSERT_VARIABLE(variables[2], "IsAndroidGuiExecutable", "FALSE", "bool"); + ASSERT_VARIABLE(variables[3], "IsAppBundleOnApple", "FALSE", "bool"); + ASSERT_VARIABLE(variables[4], "IsDLLPlatform", "FALSE", "bool"); + ASSERT_VARIABLE(variables[5], "IsExecutableWithExports", "FALSE", "bool"); + ASSERT_VARIABLE(variables[6], "IsFrameworkOnApple", "FALSE", "bool"); + ASSERT_VARIABLE(variables[7], "IsImported", "FALSE", "bool"); + ASSERT_VARIABLE(variables[8], "IsImportedGloballyVisible", "FALSE", "bool"); + ASSERT_VARIABLE(variables[9], "IsPerConfig", "TRUE", "bool"); + ASSERT_VARIABLE(variables[10], "Makefile", + dummies.Makefile->GetDirectoryId().String, "collection"); + ASSERT_VARIABLE(variables[11], "Name", "Foo", "string"); + ASSERT_VARIABLE(variables[12], "PolicyMap", "", "collection"); + ASSERT_VARIABLE(variables[13], "Properties", + std::to_string(dummies.Makefile->GetOrderedTargets()[0] + ->GetProperties() + .GetList() + .size()), + "collection"); + ASSERT_VARIABLE(variables[14], "Type", "EXECUTABLE", "string"); + + auto none = cmDebugger::cmDebuggerVariablesHelper::CreateIfAny( + variablesManager, "Locals", true, std::vector<cmTarget*>()); + + ASSERT_TRUE(none == nullptr); + + return true; +} + +static bool testCreateFromGlobalGenerator() +{ + auto variablesManager = + std::make_shared<cmDebugger::cmDebuggerVariablesManager>(); + + auto dummies = CreateDummies("Foo"); + + auto vars = cmDebugger::cmDebuggerVariablesHelper::CreateIfAny( + variablesManager, "Locals", true, dummies.GlobalGenerator.get()); + + dap::array<dap::Variable> variables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(vars->GetId())); + + ASSERT_TRUE(variables.size() == 10); + ASSERT_VARIABLE(variables[0], "AllTargetName", "ALL_BUILD", "string"); + ASSERT_VARIABLE(variables[1], "ForceUnixPaths", "FALSE", "bool"); + ASSERT_VARIABLE(variables[2], "InstallTargetName", "INSTALL", "string"); + ASSERT_VARIABLE(variables[3], "IsMultiConfig", "FALSE", "bool"); + ASSERT_VARIABLE(variables[4], "MakefileEncoding", "None", "string"); + ASSERT_VARIABLE(variables[5], "Name", "Generic", "string"); + ASSERT_VARIABLE(variables[6], "NeedSymbolicMark", "FALSE", "bool"); + ASSERT_VARIABLE(variables[7], "PackageTargetName", "PACKAGE", "string"); + ASSERT_VARIABLE(variables[8], "TestTargetName", "RUN_TESTS", "string"); + ASSERT_VARIABLE(variables[9], "UseLinkScript", "FALSE", "bool"); + + auto none = cmDebugger::cmDebuggerVariablesHelper::CreateIfAny( + variablesManager, "Locals", true, + static_cast<cmGlobalGenerator*>(nullptr)); + + ASSERT_TRUE(none == nullptr); + + return true; +} + +static bool testCreateFromTests() +{ + auto variablesManager = + std::make_shared<cmDebugger::cmDebuggerVariablesManager>(); + + auto dummies = CreateDummies("Foo"); + cmTest test1 = cmTest(dummies.Makefile.get()); + test1.SetName("Test1"); + test1.SetOldStyle(false); + test1.SetCommandExpandLists(true); + test1.SetCommand(std::vector<std::string>{ "Foo1", "arg1" }); + test1.SetProperty("Prop1", "Prop1"); + cmTest test2 = cmTest(dummies.Makefile.get()); + test2.SetName("Test2"); + test2.SetOldStyle(false); + test2.SetCommandExpandLists(false); + test2.SetCommand(std::vector<std::string>{ "Bar1", "arg1", "arg2" }); + test2.SetProperty("Prop2", "Prop2"); + test2.SetProperty("Prop3", "Prop3"); + + std::vector<cmTest*> tests = { &test1, &test2 }; + + auto vars = cmDebugger::cmDebuggerVariablesHelper::CreateIfAny( + variablesManager, "Locals", true, tests); + + dap::array<dap::Variable> variables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(vars->GetId())); + + ASSERT_TRUE(vars->GetValue() == std::to_string(tests.size())); + ASSERT_TRUE(variables.size() == 2); + ASSERT_VARIABLE_REFERENCE_NOT_ZERO(variables[0], test1.GetName(), "", + "collection"); + ASSERT_VARIABLE_REFERENCE_NOT_ZERO(variables[1], test2.GetName(), "", + "collection"); + + dap::array<dap::Variable> testVariables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(variables[0].variablesReference)); + ASSERT_TRUE(testVariables.size() == 5); + ASSERT_VARIABLE_REFERENCE_NOT_ZERO(testVariables[0], "Command", + std::to_string(test1.GetCommand().size()), + "collection"); + ASSERT_VARIABLE(testVariables[1], "CommandExpandLists", + BOOL_STRING(test1.GetCommandExpandLists()), "bool"); + ASSERT_VARIABLE(testVariables[2], "Name", test1.GetName(), "string"); + ASSERT_VARIABLE(testVariables[3], "OldStyle", + BOOL_STRING(test1.GetOldStyle()), "bool"); + ASSERT_VARIABLE_REFERENCE_NOT_ZERO(testVariables[4], "Properties", "1", + "collection"); + + dap::array<dap::Variable> commandVariables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(testVariables[0].variablesReference)); + ASSERT_TRUE(commandVariables.size() == test1.GetCommand().size()); + for (size_t i = 0; i < commandVariables.size(); ++i) { + ASSERT_VARIABLE(commandVariables[i], "[" + std::to_string(i) + "]", + test1.GetCommand()[i], "string"); + } + + dap::array<dap::Variable> propertiesVariables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(testVariables[4].variablesReference)); + ASSERT_TRUE(propertiesVariables.size() == 1); + ASSERT_VARIABLE(propertiesVariables[0], "Prop1", "Prop1", "string"); + + testVariables = variablesManager->HandleVariablesRequest( + CreateVariablesRequest(variables[1].variablesReference)); + ASSERT_TRUE(testVariables.size() == 5); + ASSERT_VARIABLE_REFERENCE_NOT_ZERO(testVariables[0], "Command", + std::to_string(test2.GetCommand().size()), + "collection"); + ASSERT_VARIABLE(testVariables[1], "CommandExpandLists", + BOOL_STRING(test2.GetCommandExpandLists()), "bool"); + ASSERT_VARIABLE(testVariables[2], "Name", test2.GetName(), "string"); + ASSERT_VARIABLE(testVariables[3], "OldStyle", + BOOL_STRING(test2.GetOldStyle()), "bool"); + ASSERT_VARIABLE_REFERENCE_NOT_ZERO(testVariables[4], "Properties", "2", + "collection"); + + commandVariables = variablesManager->HandleVariablesRequest( + CreateVariablesRequest(testVariables[0].variablesReference)); + ASSERT_TRUE(commandVariables.size() == test2.GetCommand().size()); + for (size_t i = 0; i < commandVariables.size(); ++i) { + ASSERT_VARIABLE(commandVariables[i], "[" + std::to_string(i) + "]", + test2.GetCommand()[i], "string"); + } + + propertiesVariables = variablesManager->HandleVariablesRequest( + CreateVariablesRequest(testVariables[4].variablesReference)); + ASSERT_TRUE(propertiesVariables.size() == 2); + ASSERT_VARIABLE(propertiesVariables[0], "Prop2", "Prop2", "string"); + ASSERT_VARIABLE(propertiesVariables[1], "Prop3", "Prop3", "string"); + + auto none = cmDebugger::cmDebuggerVariablesHelper::CreateIfAny( + variablesManager, "Locals", true, std::vector<cmTest*>()); + + ASSERT_TRUE(none == nullptr); + + return true; +} + +static bool testCreateFromMakefile() +{ + auto variablesManager = + std::make_shared<cmDebugger::cmDebuggerVariablesManager>(); + + auto dummies = CreateDummies("Foo"); + auto snapshot = dummies.Makefile->GetStateSnapshot(); + auto state = dummies.Makefile->GetState(); + state->SetSourceDirectory("c:/HomeDirectory"); + state->SetBinaryDirectory("c:/HomeOutputDirectory"); + auto vars = cmDebugger::cmDebuggerVariablesHelper::CreateIfAny( + variablesManager, "Locals", true, dummies.Makefile.get()); + + dap::array<dap::Variable> variables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(vars->GetId())); + + ASSERT_TRUE(variables.size() == 12); + ASSERT_VARIABLE(variables[0], "AppleSDKType", "MacOS", "string"); + ASSERT_VARIABLE(variables[1], "CurrentBinaryDirectory", + snapshot.GetDirectory().GetCurrentBinary(), "string"); + ASSERT_VARIABLE(variables[2], "CurrentSourceDirectory", + snapshot.GetDirectory().GetCurrentSource(), "string"); + ASSERT_VARIABLE(variables[3], "DefineFlags", " ", "string"); + ASSERT_VARIABLE(variables[4], "DirectoryId", + dummies.Makefile->GetDirectoryId().String, "string"); + ASSERT_VARIABLE(variables[5], "HomeDirectory", state->GetSourceDirectory(), + "string"); + ASSERT_VARIABLE(variables[6], "HomeOutputDirectory", + state->GetBinaryDirectory(), "string"); + ASSERT_VARIABLE(variables[7], "IsRootMakefile", "TRUE", "bool"); + ASSERT_VARIABLE(variables[8], "PlatformIs32Bit", "FALSE", "bool"); + ASSERT_VARIABLE(variables[9], "PlatformIs64Bit", "FALSE", "bool"); + ASSERT_VARIABLE(variables[10], "PlatformIsAppleEmbedded", "FALSE", "bool"); + ASSERT_VARIABLE(variables[11], "PlatformIsx32", "FALSE", "bool"); + + auto none = cmDebugger::cmDebuggerVariablesHelper::CreateIfAny( + variablesManager, "Locals", true, static_cast<cmMakefile*>(nullptr)); + + ASSERT_TRUE(none == nullptr); + + return true; +} + +static bool testCreateFromStackFrame() +{ + auto variablesManager = + std::make_shared<cmDebugger::cmDebuggerVariablesManager>(); + auto dummies = CreateDummies("Foo"); + + cmListFileFunction lff = cmListFileFunction("set", 99, 99, {}); + auto frame = std::make_shared<cmDebugger::cmDebuggerStackFrame>( + dummies.Makefile.get(), "c:/CMakeLists.txt", lff); + + dummies.CMake->AddCacheEntry("CMAKE_BUILD_TYPE", "Debug", "Build Type", + cmStateEnums::CacheEntryType::STRING); + + auto locals = cmDebugger::cmDebuggerVariablesHelper::Create( + variablesManager, "Locals", true, frame); + + dap::array<dap::Variable> variables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(locals->GetId())); + + ASSERT_TRUE(variables.size() == 5); + ASSERT_VARIABLE(variables[0], "CacheVariables", "1", "collection"); + ASSERT_VARIABLE(variables[1], "CurrentLine", std::to_string(lff.Line()), + "int"); + ASSERT_VARIABLE(variables[2], "Directories", "2", "collection"); + ASSERT_VARIABLE(variables[3], "Locals", "2", "collection"); + ASSERT_VARIABLE(variables[4], "Targets", "1", "collection"); + + dap::array<dap::Variable> cacheVariables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(variables[0].variablesReference)); + ASSERT_TRUE(cacheVariables.size() == 1); + ASSERT_VARIABLE(cacheVariables[0], "CMAKE_BUILD_TYPE:STRING", "Debug", + "collection"); + + dap::array<dap::Variable> directoriesVariables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(variables[2].variablesReference)); + ASSERT_TRUE(directoriesVariables.size() == 2); + ASSERT_VARIABLE( + directoriesVariables[0], "CMAKE_CURRENT_BINARY_DIR", + dummies.Makefile->GetStateSnapshot().GetDirectory().GetCurrentBinary(), + "string"); + ASSERT_VARIABLE( + directoriesVariables[1], "CMAKE_CURRENT_SOURCE_DIR", + dummies.Makefile->GetStateSnapshot().GetDirectory().GetCurrentSource(), + "string"); + + dap::array<dap::Variable> propertiesVariables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(cacheVariables[0].variablesReference)); + ASSERT_TRUE(propertiesVariables.size() == 3); + ASSERT_VARIABLE(propertiesVariables[0], "HELPSTRING", "Build Type", + "string"); + ASSERT_VARIABLE(propertiesVariables[1], "TYPE", "STRING", "string"); + ASSERT_VARIABLE(propertiesVariables[2], "VALUE", "Debug", "string"); + + return true; +} + +static bool testCreateFromBTStringVector() +{ + auto variablesManager = + std::make_shared<cmDebugger::cmDebuggerVariablesManager>(); + + std::vector<BT<std::string>> list(2); + list[0].Value = "Foo"; + list[1].Value = "Bar"; + + auto vars = cmDebugger::cmDebuggerVariablesHelper::CreateIfAny( + variablesManager, "Locals", true, list); + + dap::array<dap::Variable> variables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(vars->GetId())); + + ASSERT_TRUE(vars->GetValue() == std::to_string(list.size())); + ASSERT_TRUE(variables.size() == 2); + ASSERT_VARIABLE(variables[0], "[0]", "Foo", "string"); + ASSERT_VARIABLE(variables[1], "[1]", "Bar", "string"); + + auto none = cmDebugger::cmDebuggerVariablesHelper::CreateIfAny( + variablesManager, "Locals", true, std::vector<std::string>()); + + ASSERT_TRUE(none == nullptr); + + return true; +} + +static bool testCreateFromFileSet() +{ + auto variablesManager = + std::make_shared<cmDebugger::cmDebuggerVariablesManager>(); + + cmake cm(cmake::RoleScript, cmState::Unknown); + cmFileSet fileSet(cm, "Foo", "HEADERS", cmFileSetVisibility::Public); + BT<std::string> directory; + directory.Value = "c:/"; + fileSet.AddDirectoryEntry(directory); + BT<std::string> file; + file.Value = "c:/foo.cxx"; + fileSet.AddFileEntry(file); + + auto vars = cmDebugger::cmDebuggerVariablesHelper::CreateIfAny( + variablesManager, "Locals", true, &fileSet); + + dap::array<dap::Variable> variables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(vars->GetId())); + + ASSERT_TRUE(variables.size() == 5); + ASSERT_VARIABLE_REFERENCE_NOT_ZERO(variables[0], "Directories", "1", + "collection"); + ASSERT_VARIABLE_REFERENCE_NOT_ZERO(variables[1], "Files", "1", "collection"); + ASSERT_VARIABLE(variables[2], "Name", "Foo", "string"); + ASSERT_VARIABLE(variables[3], "Type", "HEADERS", "string"); + ASSERT_VARIABLE(variables[4], "Visibility", "Public", "string"); + + dap::array<dap::Variable> directoriesVariables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(variables[0].variablesReference)); + ASSERT_TRUE(directoriesVariables.size() == 1); + ASSERT_VARIABLE(directoriesVariables[0], "[0]", directory.Value, "string"); + + dap::array<dap::Variable> filesVariables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(variables[1].variablesReference)); + ASSERT_TRUE(filesVariables.size() == 1); + ASSERT_VARIABLE(filesVariables[0], "[0]", file.Value, "string"); + + return true; +} + +static bool testCreateFromFileSets() +{ + auto variablesManager = + std::make_shared<cmDebugger::cmDebuggerVariablesManager>(); + + cmake cm(cmake::RoleScript, cmState::Unknown); + cmFileSet fileSet(cm, "Foo", "HEADERS", cmFileSetVisibility::Public); + BT<std::string> directory; + directory.Value = "c:/"; + fileSet.AddDirectoryEntry(directory); + BT<std::string> file; + file.Value = "c:/foo.cxx"; + fileSet.AddFileEntry(file); + + auto fileSets = std::vector<cmFileSet*>{ &fileSet }; + auto vars = cmDebugger::cmDebuggerVariablesHelper::CreateIfAny( + variablesManager, "Locals", true, fileSets); + + dap::array<dap::Variable> variables = + variablesManager->HandleVariablesRequest( + CreateVariablesRequest(vars->GetId())); + + ASSERT_TRUE(variables.size() == 1); + ASSERT_VARIABLE_REFERENCE_NOT_ZERO(variables[0], "Foo", "", "collection"); + + return true; +} + +int testDebuggerVariablesHelper(int, char*[]) +{ + return runTests(std::vector<std::function<bool()>>{ + testCreateFromPolicyMap, + testCreateFromPairVector, + testCreateFromSet, + testCreateFromStringVector, + testCreateFromTarget, + testCreateFromGlobalGenerator, + testCreateFromMakefile, + testCreateFromStackFrame, + testCreateFromTests, + testCreateFromBTStringVector, + testCreateFromFileSet, + testCreateFromFileSets, + }); +} diff --git a/Tests/CMakeLib/testDebuggerVariablesManager.cxx b/Tests/CMakeLib/testDebuggerVariablesManager.cxx new file mode 100644 index 0000000..3013b9f --- /dev/null +++ b/Tests/CMakeLib/testDebuggerVariablesManager.cxx @@ -0,0 +1,50 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include <functional> +#include <memory> +#include <vector> + +#include <cm3p/cppdap/protocol.h> +#include <cm3p/cppdap/types.h> +#include <stdint.h> + +#include "cmDebuggerVariables.h" +#include "cmDebuggerVariablesManager.h" + +#include "testCommon.h" + +static bool testVariablesRegistration() +{ + auto variablesManager = + std::make_shared<cmDebugger::cmDebuggerVariablesManager>(); + + int64_t line = 5; + auto local = std::make_shared<cmDebugger::cmDebuggerVariables>( + variablesManager, "Local", true, [=]() { + return std::vector<cmDebugger::cmDebuggerVariableEntry>{ { "CurrentLine", + line } }; + }); + + dap::VariablesRequest variableRequest; + variableRequest.variablesReference = local->GetId(); + + dap::array<dap::Variable> variables = + variablesManager->HandleVariablesRequest(variableRequest); + + ASSERT_TRUE(variables.size() == 1); + + local.reset(); + + variables = variablesManager->HandleVariablesRequest(variableRequest); + ASSERT_TRUE(variables.size() == 0); + + return true; +} + +int testDebuggerVariablesManager(int, char*[]) +{ + return runTests(std::vector<std::function<bool()>>{ + testVariablesRegistration, + }); +} diff --git a/Tests/CMakeLib/testGccDepfileReader_data/deps1.txt b/Tests/CMakeLib/testGccDepfileReader_data/deps1.txt index fd2679f..4207b58 100644 --- a/Tests/CMakeLib/testGccDepfileReader_data/deps1.txt +++ b/Tests/CMakeLib/testGccDepfileReader_data/deps1.txt @@ -1,26 +1,26 @@ --RULES-- main.o --DEPENDENCIES-- -main.cpp +/usr/include/features.h /usr/include/stdc-predef.h /usr/include/stdio.h /usr/include/x86_64-linux-gnu/bits/libc-header-start.h -/usr/include/features.h -/usr/include/x86_64-linux-gnu/sys/cdefs.h -/usr/include/x86_64-linux-gnu/bits/wordsize.h /usr/include/x86_64-linux-gnu/bits/long-double.h -/usr/include/x86_64-linux-gnu/gnu/stubs.h -/usr/include/x86_64-linux-gnu/gnu/stubs-64.h -/usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h -/usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h +/usr/include/x86_64-linux-gnu/bits/stdio_lim.h +/usr/include/x86_64-linux-gnu/bits/sys_errlist.h /usr/include/x86_64-linux-gnu/bits/types.h -/usr/include/x86_64-linux-gnu/bits/typesizes.h +/usr/include/x86_64-linux-gnu/bits/types/FILE.h +/usr/include/x86_64-linux-gnu/bits/types/__FILE.h +/usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h -/usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h -/usr/include/x86_64-linux-gnu/bits/types/__FILE.h -/usr/include/x86_64-linux-gnu/bits/types/FILE.h -/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h -/usr/include/x86_64-linux-gnu/bits/stdio_lim.h -/usr/include/x86_64-linux-gnu/bits/sys_errlist.h +/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h +/usr/include/x86_64-linux-gnu/bits/typesizes.h +/usr/include/x86_64-linux-gnu/bits/wordsize.h +/usr/include/x86_64-linux-gnu/gnu/stubs-64.h +/usr/include/x86_64-linux-gnu/gnu/stubs.h +/usr/include/x86_64-linux-gnu/sys/cdefs.h +/usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h +/usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h +main.cpp diff --git a/Tests/CMakeLib/testGccDepfileReader_data/deps3.txt b/Tests/CMakeLib/testGccDepfileReader_data/deps3.txt index 448f69c..8d82c60 100644 --- a/Tests/CMakeLib/testGccDepfileReader_data/deps3.txt +++ b/Tests/CMakeLib/testGccDepfileReader_data/deps3.txt @@ -1,11 +1,11 @@ --RULES-- main.o --DEPENDENCIES-- -main.cpp -foo#bar.h -foo\#bar.h foo bar.h +foo#bar.h +foo$bar.h foo\ bar.h +foo\#bar.h foo\\ bar.h foo\\\\ -foo$bar.h +main.cpp diff --git a/Tests/CMakeLib/testJSONHelpers.cxx b/Tests/CMakeLib/testJSONHelpers.cxx index 053c163..50f0386 100644 --- a/Tests/CMakeLib/testJSONHelpers.cxx +++ b/Tests/CMakeLib/testJSONHelpers.cxx @@ -10,6 +10,7 @@ #include <cm3p/json/value.h> #include "cmJSONHelpers.h" +#include "cmJSONState.h" #define ASSERT_TRUE(x) \ do { \ @@ -31,59 +32,65 @@ struct InheritedStruct : public ObjectStruct std::string Field3; }; -enum class ErrorCode +namespace ErrorMessages { +using ErrorGenerator = + std::function<void(const Json::Value*, cmJSONState* state)>; +ErrorGenerator ErrorGeneratorBuilder(std::string errorMessage) { - Success, - InvalidInt, - InvalidBool, - InvalidString, - InvalidSubObject, - InvalidObject, - InvalidArray, - MissingRequired, + return [errorMessage](const Json::Value* value, cmJSONState* state) -> void { + state->AddErrorAtValue(errorMessage, value); + }; +}; +ErrorGenerator InvalidArray = ErrorGeneratorBuilder("Invalid Array"); +ErrorGenerator MissingRequired = ErrorGeneratorBuilder("Missing Required"); +ErrorGenerator InvalidMap = ErrorGeneratorBuilder("Invalid Map"); +ErrorGenerator InvalidObject(JsonErrors::ObjectError /*errorType*/, + const Json::Value::Members& extraFields) +{ + return [extraFields](const Json::Value* value, cmJSONState* state) -> void { + state->AddErrorAtValue("Invalid Object", value); + }; +}; }; -using JSONHelperBuilder = cmJSONHelperBuilder<ErrorCode>; +using JSONHelperBuilder = cmJSONHelperBuilder; -auto const IntHelper = - JSONHelperBuilder::Int(ErrorCode::Success, ErrorCode::InvalidInt, 1); +auto const IntHelper = JSONHelperBuilder::Int(1); auto const RequiredIntHelper = - JSONHelperBuilder::Required<int>(ErrorCode::MissingRequired, IntHelper); -auto const UIntHelper = - JSONHelperBuilder::UInt(ErrorCode::Success, ErrorCode::InvalidInt, 1); -auto const BoolHelper = - JSONHelperBuilder::Bool(ErrorCode::Success, ErrorCode::InvalidBool, false); -auto const StringHelper = JSONHelperBuilder::String( - ErrorCode::Success, ErrorCode::InvalidString, "default"); + JSONHelperBuilder::Required<int>(ErrorMessages::MissingRequired, IntHelper); +auto const UIntHelper = JSONHelperBuilder::UInt(1); +auto const BoolHelper = JSONHelperBuilder::Bool(false); +auto const StringHelper = JSONHelperBuilder::String("default"); auto const RequiredStringHelper = JSONHelperBuilder::Required<std::string>( - ErrorCode::MissingRequired, StringHelper); + ErrorMessages::MissingRequired, StringHelper); auto const StringVectorHelper = JSONHelperBuilder::Vector<std::string>( - ErrorCode::Success, ErrorCode::InvalidArray, StringHelper); + ErrorMessages::InvalidArray, StringHelper); auto const StringVectorFilterHelper = JSONHelperBuilder::VectorFilter<std::string>( - ErrorCode::Success, ErrorCode::InvalidArray, StringHelper, + ErrorMessages::InvalidArray, StringHelper, [](const std::string& value) { return value != "ignore"; }); -auto const StringMapHelper = JSONHelperBuilder::Map<std::string>( - ErrorCode::Success, ErrorCode::InvalidObject, StringHelper); +auto const StringMapHelper = + JSONHelperBuilder::Map<std::string>(ErrorMessages::InvalidMap, StringHelper); auto const StringMapFilterHelper = JSONHelperBuilder::MapFilter<std::string>( - ErrorCode::Success, ErrorCode::InvalidObject, StringHelper, + ErrorMessages::InvalidMap, StringHelper, [](const std::string& key) { return key != "ignore"; }); auto const OptionalStringHelper = - JSONHelperBuilder::Optional<std::string>(ErrorCode::Success, StringHelper); + JSONHelperBuilder::Optional<std::string>(StringHelper); bool testInt() { Json::Value v(2); + cmJSONState state; int i = 0; - ASSERT_TRUE(IntHelper(i, &v) == ErrorCode::Success); + ASSERT_TRUE(IntHelper(i, &v, &state)); ASSERT_TRUE(i == 2); i = 0; v = Json::nullValue; - ASSERT_TRUE(IntHelper(i, &v) == ErrorCode::InvalidInt); + ASSERT_TRUE(!IntHelper(i, &v, &state)); i = 0; - ASSERT_TRUE(IntHelper(i, nullptr) == ErrorCode::Success); + ASSERT_TRUE(IntHelper(i, nullptr, &state)); ASSERT_TRUE(i == 1); return true; @@ -92,16 +99,16 @@ bool testInt() bool testUInt() { Json::Value v(2); + cmJSONState state; unsigned int i = 0; - ASSERT_TRUE(UIntHelper(i, &v) == ErrorCode::Success); + ASSERT_TRUE(UIntHelper(i, &v, &state)); ASSERT_TRUE(i == 2); - i = 0; v = Json::nullValue; - ASSERT_TRUE(UIntHelper(i, &v) == ErrorCode::InvalidInt); + ASSERT_TRUE(!UIntHelper(i, &v, &state)); i = 0; - ASSERT_TRUE(UIntHelper(i, nullptr) == ErrorCode::Success); + ASSERT_TRUE(UIntHelper(i, nullptr, &state)); ASSERT_TRUE(i == 1); return true; @@ -110,21 +117,22 @@ bool testUInt() bool testBool() { Json::Value v(true); + cmJSONState state; bool b = false; - ASSERT_TRUE(BoolHelper(b, &v) == ErrorCode::Success); + ASSERT_TRUE(BoolHelper(b, &v, &state)); ASSERT_TRUE(b); b = false; v = false; - ASSERT_TRUE(BoolHelper(b, &v) == ErrorCode::Success); + ASSERT_TRUE(BoolHelper(b, &v, &state)); ASSERT_TRUE(!b); b = false; v = 4; - ASSERT_TRUE(BoolHelper(b, &v) == ErrorCode::InvalidBool); + ASSERT_TRUE(!BoolHelper(b, &v, &state)); b = true; - ASSERT_TRUE(BoolHelper(b, nullptr) == ErrorCode::Success); + ASSERT_TRUE(BoolHelper(b, nullptr, &state)); ASSERT_TRUE(!b); return true; @@ -133,16 +141,17 @@ bool testBool() bool testString() { Json::Value v("str"); + cmJSONState state; std::string str = ""; - ASSERT_TRUE(StringHelper(str, &v) == ErrorCode::Success); + ASSERT_TRUE(StringHelper(str, &v, &state)); ASSERT_TRUE(str == "str"); str = ""; v = Json::nullValue; - ASSERT_TRUE(StringHelper(str, &v) == ErrorCode::InvalidString); + ASSERT_TRUE(!StringHelper(str, &v, &state)); str = ""; - ASSERT_TRUE(StringHelper(str, nullptr) == ErrorCode::Success); + ASSERT_TRUE(StringHelper(str, nullptr, &state)); ASSERT_TRUE(str == "default"); return true; @@ -150,17 +159,15 @@ bool testString() bool testObject() { - auto const subhelper = - JSONHelperBuilder::Object<ObjectStruct>(ErrorCode::Success, - ErrorCode::InvalidSubObject) - .Bind("subfield"_s, &ObjectStruct::Field2, IntHelper); - auto const helper = JSONHelperBuilder::Object<ObjectStruct>( - ErrorCode::Success, ErrorCode::InvalidObject) + auto const subhelper = JSONHelperBuilder::Object<ObjectStruct>().Bind( + "subfield"_s, &ObjectStruct::Field2, IntHelper); + auto const helper = JSONHelperBuilder::Object<ObjectStruct>() .Bind("field1"_s, &ObjectStruct::Field1, StringHelper) .Bind("field2"_s, subhelper) .Bind<std::string>("field3"_s, nullptr, StringHelper); Json::Value v(Json::objectValue); + cmJSONState state; v["field1"] = "Hello"; v["field2"] = Json::objectValue; v["field2"]["subfield"] = 2; @@ -168,38 +175,38 @@ bool testObject() v["extra"] = "extra"; ObjectStruct s1; - ASSERT_TRUE(helper(s1, &v) == ErrorCode::Success); + ASSERT_TRUE(helper(s1, &v, &state)); ASSERT_TRUE(s1.Field1 == "Hello"); ASSERT_TRUE(s1.Field2 == 2); v["field2"]["subfield"] = "wrong"; ObjectStruct s2; - ASSERT_TRUE(helper(s2, &v) == ErrorCode::InvalidInt); + ASSERT_TRUE(!helper(s2, &v, &state)); v["field2"].removeMember("subfield"); ObjectStruct s3; - ASSERT_TRUE(helper(s3, &v) == ErrorCode::InvalidSubObject); + ASSERT_TRUE(!helper(s3, &v, &state)); v.removeMember("field2"); ObjectStruct s4; - ASSERT_TRUE(helper(s4, &v) == ErrorCode::InvalidObject); + ASSERT_TRUE(!helper(s4, &v, &state)); v["field2"] = Json::objectValue; v["field2"]["subfield"] = 2; v["field3"] = 3; ObjectStruct s5; - ASSERT_TRUE(helper(s5, &v) == ErrorCode::InvalidString); + ASSERT_TRUE(!helper(s5, &v, &state)); v.removeMember("field3"); ObjectStruct s6; - ASSERT_TRUE(helper(s6, &v) == ErrorCode::InvalidObject); + ASSERT_TRUE(!helper(s6, &v, &state)); v = "Hello"; ObjectStruct s7; - ASSERT_TRUE(helper(s7, &v) == ErrorCode::InvalidObject); + ASSERT_TRUE(!helper(s7, &v, &state)); ObjectStruct s8; - ASSERT_TRUE(helper(s8, nullptr) == ErrorCode::InvalidObject); + ASSERT_TRUE(!helper(s8, nullptr, &state)); return true; } @@ -207,47 +214,48 @@ bool testObject() bool testObjectInherited() { auto const helper = - JSONHelperBuilder::Object<InheritedStruct>(ErrorCode::Success, - ErrorCode::InvalidObject) + JSONHelperBuilder::Object<InheritedStruct>(ErrorMessages::InvalidObject, + true) .Bind("field1"_s, &InheritedStruct::Field1, StringHelper) .Bind("field2"_s, &InheritedStruct::Field2, IntHelper) .Bind("field3"_s, &InheritedStruct::Field3, StringHelper); Json::Value v(Json::objectValue); + cmJSONState state; v["field1"] = "Hello"; v["field2"] = 2; v["field3"] = "world!"; v["extra"] = "extra"; InheritedStruct s1; - ASSERT_TRUE(helper(s1, &v) == ErrorCode::Success); + ASSERT_TRUE(helper(s1, &v, &state)); ASSERT_TRUE(s1.Field1 == "Hello"); ASSERT_TRUE(s1.Field2 == 2); ASSERT_TRUE(s1.Field3 == "world!"); v["field2"] = "wrong"; InheritedStruct s2; - ASSERT_TRUE(helper(s2, &v) == ErrorCode::InvalidInt); + ASSERT_TRUE(!helper(s2, &v, &state)); v.removeMember("field2"); InheritedStruct s3; - ASSERT_TRUE(helper(s3, &v) == ErrorCode::InvalidObject); + ASSERT_TRUE(!helper(s3, &v, &state)); v["field2"] = 2; v["field3"] = 3; InheritedStruct s4; - ASSERT_TRUE(helper(s4, &v) == ErrorCode::InvalidString); + ASSERT_TRUE(!helper(s4, &v, &state)); v.removeMember("field3"); InheritedStruct s5; - ASSERT_TRUE(helper(s5, &v) == ErrorCode::InvalidObject); + ASSERT_TRUE(!helper(s5, &v, &state)); v = "Hello"; InheritedStruct s6; - ASSERT_TRUE(helper(s6, &v) == ErrorCode::InvalidObject); + ASSERT_TRUE(!helper(s6, &v, &state)); InheritedStruct s7; - ASSERT_TRUE(helper(s7, nullptr) == ErrorCode::InvalidObject); + ASSERT_TRUE(!helper(s7, nullptr, &state)); return true; } @@ -255,22 +263,23 @@ bool testObjectInherited() bool testObjectNoExtra() { auto const helper = JSONHelperBuilder::Object<ObjectStruct>( - ErrorCode::Success, ErrorCode::InvalidObject, false) + ErrorMessages::InvalidObject, false) .Bind("field1"_s, &ObjectStruct::Field1, StringHelper) .Bind("field2"_s, &ObjectStruct::Field2, IntHelper); Json::Value v(Json::objectValue); + cmJSONState state; v["field1"] = "Hello"; v["field2"] = 2; ObjectStruct s1; - ASSERT_TRUE(helper(s1, &v) == ErrorCode::Success); + ASSERT_TRUE(helper(s1, &v, &state)); ASSERT_TRUE(s1.Field1 == "Hello"); ASSERT_TRUE(s1.Field2 == 2); v["extra"] = "world!"; ObjectStruct s2; - ASSERT_TRUE(helper(s2, &v) == ErrorCode::InvalidObject); + ASSERT_TRUE(!helper(s2, &v, &state)); return true; } @@ -278,31 +287,31 @@ bool testObjectNoExtra() bool testObjectOptional() { auto const helper = - JSONHelperBuilder::Object<ObjectStruct>(ErrorCode::Success, - ErrorCode::InvalidObject) + JSONHelperBuilder::Object<ObjectStruct>(ErrorMessages::InvalidObject, true) .Bind("field1"_s, &ObjectStruct::Field1, StringHelper, false) .Bind("field2"_s, &ObjectStruct::Field2, IntHelper, false) .Bind<std::string>("field3_s", nullptr, StringHelper, false); Json::Value v(Json::objectValue); + cmJSONState state; v["field1"] = "Hello"; v["field2"] = 2; v["field3"] = "world!"; v["extra"] = "extra"; ObjectStruct s1; - ASSERT_TRUE(helper(s1, &v) == ErrorCode::Success); + ASSERT_TRUE(helper(s1, &v, &state)); ASSERT_TRUE(s1.Field1 == "Hello"); ASSERT_TRUE(s1.Field2 == 2); v = Json::objectValue; ObjectStruct s2; - ASSERT_TRUE(helper(s2, &v) == ErrorCode::Success); + ASSERT_TRUE(helper(s2, &v, &state)); ASSERT_TRUE(s2.Field1 == "default"); ASSERT_TRUE(s2.Field2 == 1); ObjectStruct s3; - ASSERT_TRUE(helper(s3, nullptr) == ErrorCode::Success); + ASSERT_TRUE(helper(s3, nullptr, &state)); ASSERT_TRUE(s3.Field1 == "default"); ASSERT_TRUE(s3.Field2 == 1); @@ -312,25 +321,26 @@ bool testObjectOptional() bool testVector() { Json::Value v(Json::arrayValue); + cmJSONState state; v.append("Hello"); v.append("world!"); v.append("ignore"); std::vector<std::string> l{ "default" }; std::vector<std::string> expected{ "Hello", "world!", "ignore" }; - ASSERT_TRUE(StringVectorHelper(l, &v) == ErrorCode::Success); + ASSERT_TRUE(StringVectorHelper(l, &v, &state)); ASSERT_TRUE(l == expected); v[1] = 2; l = { "default" }; - ASSERT_TRUE(StringVectorHelper(l, &v) == ErrorCode::InvalidString); + ASSERT_TRUE(!StringVectorHelper(l, &v, &state)); v = "Hello"; l = { "default" }; - ASSERT_TRUE(StringVectorHelper(l, &v) == ErrorCode::InvalidArray); + ASSERT_TRUE(!StringVectorHelper(l, &v, &state)); l = { "default" }; - ASSERT_TRUE(StringVectorHelper(l, nullptr) == ErrorCode::Success); + ASSERT_TRUE(StringVectorHelper(l, nullptr, &state)); ASSERT_TRUE(l.empty()); return true; @@ -339,6 +349,7 @@ bool testVector() bool testVectorFilter() { Json::Value v(Json::arrayValue); + cmJSONState state; v.append("Hello"); v.append("world!"); v.append("ignore"); @@ -348,19 +359,19 @@ bool testVectorFilter() "Hello", "world!", }; - ASSERT_TRUE(StringVectorFilterHelper(l, &v) == ErrorCode::Success); + ASSERT_TRUE(StringVectorFilterHelper(l, &v, &state)); ASSERT_TRUE(l == expected); v[1] = 2; l = { "default" }; - ASSERT_TRUE(StringVectorFilterHelper(l, &v) == ErrorCode::InvalidString); + ASSERT_TRUE(!StringVectorFilterHelper(l, &v, &state)); v = "Hello"; l = { "default" }; - ASSERT_TRUE(StringVectorFilterHelper(l, &v) == ErrorCode::InvalidArray); + ASSERT_TRUE(!StringVectorFilterHelper(l, &v, &state)); l = { "default" }; - ASSERT_TRUE(StringVectorFilterHelper(l, nullptr) == ErrorCode::Success); + ASSERT_TRUE(StringVectorFilterHelper(l, nullptr, &state)); ASSERT_TRUE(l.empty()); return true; @@ -372,20 +383,21 @@ bool testMap() v["field1"] = "Hello"; v["field2"] = "world!"; v["ignore"] = "ignore"; + cmJSONState state; std::map<std::string, std::string> m{ { "key", "default" } }; std::map<std::string, std::string> expected{ { "field1", "Hello" }, { "field2", "world!" }, { "ignore", "ignore" } }; - ASSERT_TRUE(StringMapHelper(m, &v) == ErrorCode::Success); + ASSERT_TRUE(StringMapHelper(m, &v, &state)); ASSERT_TRUE(m == expected); v = Json::arrayValue; m = { { "key", "default" } }; - ASSERT_TRUE(StringMapHelper(m, &v) == ErrorCode::InvalidObject); + ASSERT_TRUE(!StringMapHelper(m, &v, &state)); m = { { "key", "default" } }; - ASSERT_TRUE(StringMapHelper(m, nullptr) == ErrorCode::Success); + ASSERT_TRUE(StringMapHelper(m, nullptr, &state)); ASSERT_TRUE(m.empty()); return true; @@ -394,6 +406,7 @@ bool testMap() bool testMapFilter() { Json::Value v(Json::objectValue); + cmJSONState state; v["field1"] = "Hello"; v["field2"] = "world!"; v["ignore"] = "ignore"; @@ -401,15 +414,15 @@ bool testMapFilter() std::map<std::string, std::string> m{ { "key", "default" } }; std::map<std::string, std::string> expected{ { "field1", "Hello" }, { "field2", "world!" } }; - ASSERT_TRUE(StringMapFilterHelper(m, &v) == ErrorCode::Success); + ASSERT_TRUE(StringMapFilterHelper(m, &v, &state)); ASSERT_TRUE(m == expected); v = Json::arrayValue; m = { { "key", "default" } }; - ASSERT_TRUE(StringMapFilterHelper(m, &v) == ErrorCode::InvalidObject); + ASSERT_TRUE(!StringMapFilterHelper(m, &v, &state)); m = { { "key", "default" } }; - ASSERT_TRUE(StringMapFilterHelper(m, nullptr) == ErrorCode::Success); + ASSERT_TRUE(StringMapFilterHelper(m, nullptr, &state)); ASSERT_TRUE(m.empty()); return true; @@ -418,13 +431,14 @@ bool testMapFilter() bool testOptional() { Json::Value v = "Hello"; + cmJSONState state; cm::optional<std::string> str{ "default" }; - ASSERT_TRUE(OptionalStringHelper(str, &v) == ErrorCode::Success); + ASSERT_TRUE(OptionalStringHelper(str, &v, &state)); ASSERT_TRUE(str == "Hello"); str.emplace("default"); - ASSERT_TRUE(OptionalStringHelper(str, nullptr) == ErrorCode::Success); + ASSERT_TRUE(OptionalStringHelper(str, nullptr, &state)); ASSERT_TRUE(str == cm::nullopt); return true; @@ -433,25 +447,24 @@ bool testOptional() bool testRequired() { Json::Value v = "Hello"; - std::string str = "default"; int i = 1; - ASSERT_TRUE(RequiredStringHelper(str, &v) == ErrorCode::Success); + cmJSONState state; + ASSERT_TRUE(RequiredStringHelper(str, &v, &state)); ASSERT_TRUE(str == "Hello"); - ASSERT_TRUE(RequiredIntHelper(i, &v) == ErrorCode::InvalidInt); + ASSERT_TRUE(!RequiredIntHelper(i, &v, &state)); v = 2; str = "default"; i = 1; - ASSERT_TRUE(RequiredStringHelper(str, &v) == ErrorCode::InvalidString); - ASSERT_TRUE(RequiredIntHelper(i, &v) == ErrorCode::Success); + ASSERT_TRUE(!RequiredStringHelper(str, &v, &state)); + ASSERT_TRUE(RequiredIntHelper(i, &v, &state)); ASSERT_TRUE(i == 2); str = "default"; i = 1; - ASSERT_TRUE(RequiredStringHelper(str, nullptr) == - ErrorCode::MissingRequired); - ASSERT_TRUE(RequiredIntHelper(i, nullptr) == ErrorCode::MissingRequired); + ASSERT_TRUE(!RequiredStringHelper(str, nullptr, &state)); + ASSERT_TRUE(!RequiredIntHelper(i, nullptr, &state)); return true; } diff --git a/Tests/CMakeLib/testList.cxx b/Tests/CMakeLib/testList.cxx new file mode 100644 index 0000000..f6ec720 --- /dev/null +++ b/Tests/CMakeLib/testList.cxx @@ -0,0 +1,995 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include <iostream> +#include <stdexcept> +#include <string> +#include <type_traits> +#include <utility> +#include <vector> + +#include <cmext/string_view> + +#include "cmList.h" + +namespace { + +void checkResult(bool success) +{ + if (!success) { + std::cout << " => failed"; + } + std::cout << std::endl; +} + +bool testConstructors() +{ + std::cout << "testConstructors()"; + + bool result = true; + + { + cmList list; + if (!list.empty() || list != cmList{}) { + result = false; + } + } + { + cmList list{ "aa;bb" }; + if (list.size() != 2 || list.to_string() != "aa;bb") { + result = false; + } + } + { + cmList list1{ "aa", "bb" }; + cmList list2("aa;bb"_s); + + if (list1.size() != 2 || list2.size() != 2 || list1 != list2) { + result = false; + } + if (list1.to_string() != "aa;bb") { + result = false; + } + if (list1.to_string() != list2.to_string()) { + result = false; + } + } + { + std::vector<std::string> v{ "aa", "bb", "cc" }; + cmList list(v.begin(), v.end()); + if (list.size() != 3 || list.to_string() != "aa;bb;cc") { + result = false; + } + } + { + std::vector<std::string> values{ "aa;bb", "cc", "dd;ee" }; + cmList list1(values.begin(), values.end()); + cmList list2(values.begin(), values.end(), cmList::ExpandElements::No); + + if (list1.size() != 5 || list1.to_string() != "aa;bb;cc;dd;ee") { + result = false; + } + if (list2.size() != 3 || list2.to_string() != "aa;bb;cc;dd;ee") { + result = false; + } + } + { + std::vector<std::string> values{ "aa;bb;;cc", "", "dd;ee" }; + cmList list1(values.begin(), values.end(), cmList::ExpandElements::No, + cmList::EmptyElements::No); + cmList list2(values.begin(), values.end(), cmList::ExpandElements::No, + cmList::EmptyElements::Yes); + cmList list3(values.begin(), values.end(), cmList::ExpandElements::Yes, + cmList::EmptyElements::No); + cmList list4(values.begin(), values.end(), cmList::ExpandElements::Yes, + cmList::EmptyElements::Yes); + + if (list1.size() != 2 || list1.to_string() != "aa;bb;;cc;dd;ee") { + result = false; + } + if (list2.size() != 3 || list2.to_string() != "aa;bb;;cc;;dd;ee") { + result = false; + } + if (list3.size() != 5 || list3.to_string() != "aa;bb;cc;dd;ee") { + result = false; + } + if (list4.size() != 7 || list4.to_string() != "aa;bb;;cc;;dd;ee") { + result = false; + } + } + { + std::vector<std::string> values{ "aa;bb", "cc", "dd;ee" }; + cmList list1(values); + cmList list2(values, cmList::ExpandElements::No); + + if (list1.size() != 5 || list1.to_string() != "aa;bb;cc;dd;ee") { + result = false; + } + if (list2.size() != 3 || list2.to_string() != "aa;bb;cc;dd;ee") { + result = false; + } + } + { + std::vector<std::string> values{ "aa", "bb", "cc", "dd", "ee" }; + cmList list(std::move(values)); + + if (list.size() != 5 || list.to_string() != "aa;bb;cc;dd;ee") { + result = false; + } + if (!values.empty()) { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testAssign() +{ + std::cout << "testAssign()"; + + bool result = true; + + { + cmList list1{ "aa", "bb" }; + cmList list2{ "cc", "dd" }; + + list2 = list1; + if (list1.size() != 2 || list2.size() != 2 || list1 != list2) { + result = false; + } + if (list1.to_string() != "aa;bb") { + result = false; + } + if (list1.to_string() != list2.to_string()) { + result = false; + } + } + { + cmList list1{ "aa", "bb" }; + cmList list2{ "cc", "dd" }; + + list2 = std::move(list1); + if (!list1.empty() || list2.size() != 2) { + result = false; + } + if (list2.to_string() != "aa;bb") { + result = false; + } + } + { + std::vector<std::string> v{ "aa", "bb" }; + cmList list{ "cc", "dd" }; + + list = std::move(v); + if (!v.empty() || list.size() != 2) { + result = false; + } + if (list.to_string() != "aa;bb") { + result = false; + } + } + { + cmList list{ "cc", "dd" }; + + list = "aa;bb"_s; + if (list.size() != 2) { + result = false; + } + if (list.to_string() != "aa;bb") { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testConversions() +{ + std::cout << "testConversions()"; + + bool result = true; + + { + cmList list("a;b;c"_s); + std::string s = list.to_string(); + + if (s != "a;b;c") { + result = false; + } + } + { + cmList list("a;b;c"_s); + std::vector<std::string> v = list; + + if (list.size() != 3 || v.size() != 3) { + result = false; + } + } + { + cmList list("a;b;c"_s); + std::vector<std::string> v = std::move(list); + + // Microsoft compiler is not able to handle correctly the move semantics + // so the initial list is not moved, so do not check its size... + if (v.size() != 3) { + result = false; + } + } + { + cmList list("a;b;c"_s); + std::vector<std::string> v; + + // compiler is not able to select the cmList conversion operator + // and the std::vector assignment operator using the move semantics + // v = std::move(list); + v = std::move(list.data()); + + if (!list.empty() || v.size() != 3) { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testAccess() +{ + std::cout << "testAccess()"; + + bool result = true; + + { + cmList list{ "a", "b", "c" }; + if (list.get_item(1) != "b") { + result = false; + } + } + { + cmList list{ "a", "b", "c" }; + if (list.get_item(-3) != "a") { + result = false; + } + } + { + try { + cmList list{ "a", "b", "c" }; + if (list.get_item(4) != "a") { + result = false; + } + } catch (std::out_of_range&) { + } + } + { + try { + cmList list{ "a", "b", "c" }; + if (list.get_item(-4) != "a") { + result = false; + } + } catch (std::out_of_range&) { + } + } + { + cmList list{ "a", "b", "c", "d", "e" }; + auto sublist = list.sublist(list.begin() + 1, list.begin() + 3); + if (sublist.size() != 2 || sublist != cmList{ "b", "c" }) { + result = false; + } + } + { + cmList list{ "a", "b", "c", "d", "e" }; + auto sublist = list.sublist(1, 2); + if (sublist.size() != 2 || sublist != cmList{ "b", "c" }) { + result = false; + } + + sublist = list.sublist(1, cmList::npos); + if (sublist.size() != 4 || sublist != cmList{ "b", "c", "d", "e" }) { + result = false; + } + } + { + cmList list{ "a", "b", "c", "d", "e", "f" }; + auto sublist = list.get_items({ 1, 3, 5 }); + if (sublist.size() != 3 || sublist != cmList{ "b", "d", "f" }) { + result = false; + } + } + { + cmList list{ "a", "b", "c", "d", "e", "f" }; + auto sublist = list.get_items({ 1, -3, 5, -3 }); + if (sublist.size() != 4 || sublist != cmList{ "b", "d", "f", "d" }) { + result = false; + } + } + { + cmList list{ "a", "b", "c", "d", "e", "f" }; + try { + if (list.get_items({ 1, -3, 5, -3, 10 }).size() != 5) { + result = false; + } + } catch (std::out_of_range&) { + } + } + { + cmList list{ "a", "b", "c", "d", "e", "f" }; + + if (list.find("b") != 1) { + result = false; + } + if (list.find("x") != cmList::npos) { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testModifiers() +{ + std::cout << "testModifiers()"; + + bool result = true; + + { + cmList list{ "1;2;3;4;5" }; + + auto it = list.insert(list.begin() + 2, "6;7;8"_s); + if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") { + result = false; + } + if (*it != "6") { + result = false; + } + } + { + cmList list{ "1;2;3;4;5" }; + + auto it = + list.insert(list.begin() + 2, "6;7;8"_s, cmList::ExpandElements::No); + if (list.size() != 6 || list.to_string() != "1;2;6;7;8;3;4;5") { + result = false; + } + if (*it != "6;7;8") { + result = false; + } + } + { + cmList list{ "1;2;3;4;5" }; + cmList v{ "6", "7", "8" }; + + auto it = list.insert(list.begin() + 2, v); + if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") { + result = false; + } + if (*it != "6") { + result = false; + } + } + { + cmList list{ "1;2;3;4;5" }; + cmList v{ "6", "7", "8" }; + + auto it = list.insert(list.begin() + 2, std::move(v)); + if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") { + result = false; + } + if (*it != "6") { + result = false; + } + + if (!v.empty()) { + result = false; + } + } + { + cmList list{ "1;2;3;4;5" }; + std::vector<std::string> v{ "6", "7", "8" }; + + auto it = list.insert(list.begin() + 2, v); + if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") { + result = false; + } + if (*it != "6") { + result = false; + } + } + { + cmList list{ "1;2;3;4;5" }; + std::vector<std::string> v{ "6;7", "8" }; + + auto it = list.insert(list.begin() + 2, v); + if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") { + result = false; + } + if (*it != "6") { + result = false; + } + } + { + cmList list{ "1;2;3;4;5" }; + std::vector<std::string> v{ "6;7", "8" }; + + auto it = list.insert(list.begin() + 2, v, cmList::ExpandElements::No); + if (list.size() != 7 || list.to_string() != "1;2;6;7;8;3;4;5") { + result = false; + } + if (*it != "6;7") { + result = false; + } + } + { + cmList list{ "1;2;3;4;5" }; + std::vector<std::string> v{ "6;;7", "8" }; + + auto it = list.insert(list.begin() + 2, v); + if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") { + result = false; + } + if (*it != "6") { + result = false; + } + } + { + cmList list{ "1;2;3;4;5" }; + std::vector<std::string> v{ "6;;7", "8" }; + + auto it = list.insert(list.begin() + 2, v, cmList::EmptyElements::Yes); + if (list.size() != 9 || list.to_string() != "1;2;6;;7;8;3;4;5") { + result = false; + } + if (*it != "6") { + result = false; + } + } + { + cmList list{ "1;2;3;4;5" }; + std::vector<std::string> v{ "6", "7", "8" }; + + auto it = list.insert(list.begin() + 2, std::move(v)); + if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") { + result = false; + } + if (*it != "6") { + result = false; + } + + if (!v.empty()) { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testRemoveItems() +{ + std::cout << "testRemoveItems()"; + + bool result = true; + + { + cmList list("a;b;c;d;e;f;g;h"_s); + + list.remove_items({ 1, 3, 5 }); + + if (list.size() != 5 || list.to_string() != "a;c;e;g;h") { + result = false; + } + } + { + cmList list("a;b;c;b;a;d;e;f"_s); + + list.remove_items({ "a", "b", "h" }); + + if (list.size() != 4 || list.to_string() != "c;d;e;f") { + result = false; + } + } + { + cmList list("a;b;c;d;e;f;g;h"_s); + std::vector<cmList::index_type> remove{ 1, 3, 5 }; + + list.remove_items(remove.begin(), remove.end()); + + if (list.size() != 5 || list.to_string() != "a;c;e;g;h") { + result = false; + } + } + { + cmList list("a;b;c;b;a;d;e;f"_s); + std::vector<std::string> remove{ "b", "a", "h" }; + + list.remove_items(remove.begin(), remove.end()); + + if (list.size() != 4 || list.to_string() != "c;d;e;f") { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testRemoveDuplicates() +{ + std::cout << "testRemoveDuplicates()"; + + bool result = true; + + { + cmList list("b;c;b;a;a;c;b;a;c;b"_s); + + list.remove_duplicates(); + + if (list.size() != 3 || list.to_string() != "b;c;a") { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testFilter() +{ + std::cout << "testFilter()"; + + bool result = true; + + { + cmList list{ "AA", "Aa", "aA" }; + + list.filter("^A", cmList::FilterMode::INCLUDE); + if (list.size() != 2 || list.to_string() != "AA;Aa") { + result = false; + } + } + { + cmList list{ "AA", "Aa", "aA" }; + + list.filter("^A", cmList::FilterMode::EXCLUDE); + if (list.size() != 1 || list.to_string() != "aA") { + result = false; + } + } + { + cmList list{ "AA", "Aa", "aA" }; + + try { + list.filter("^(A", cmList::FilterMode::EXCLUDE); + if (list.size() != 1) { + result = false; + } + } catch (const std::invalid_argument&) { + } + } + + checkResult(result); + + return result; +} + +bool testReverse() +{ + std::cout << "testReverse()"; + + bool result = true; + + { + cmList list{ "a", "b", "c" }; + if (list.reverse().to_string() != "c;b;a") { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testSort() +{ + std::cout << "testSort()"; + + bool result = true; + + using SortConfiguration = cmList::SortConfiguration; + + { + cmList list{ "A", "D", "C", "B", "A" }; + + list.sort(); + if (list.to_string() != "A;A;B;C;D") { + result = false; + } + + list.sort({ SortConfiguration::OrderMode::DESCENDING, + SortConfiguration::CompareMethod::DEFAULT, + SortConfiguration::CaseSensitivity::DEFAULT }); + if (list.to_string() != "D;C;B;A;A") { + result = false; + } + } + { + SortConfiguration sortCfg; + cmList list{ "1.0", "1.1", "2.5", "10.2" }; + + list.sort(sortCfg); + if (list.to_string() != "1.0;1.1;10.2;2.5") { + result = false; + } + + sortCfg.Compare = SortConfiguration::CompareMethod::NATURAL; + list.sort(sortCfg); + if (list.to_string() != "1.0;1.1;2.5;10.2") { + result = false; + } + + sortCfg.Order = SortConfiguration::OrderMode::DESCENDING; + list.sort(sortCfg); + if (list.to_string() != "10.2;2.5;1.1;1.0") { + result = false; + } + } + { + SortConfiguration sortCfg; + cmList list{ "/zz/bb.cc", "/xx/yy/dd.cc", "/aa/cc.aa" }; + + list.sort(sortCfg); + if (list.to_string() != "/aa/cc.aa;/xx/yy/dd.cc;/zz/bb.cc") { + result = false; + } + + sortCfg.Compare = SortConfiguration::CompareMethod::FILE_BASENAME; + if (list.sort(sortCfg).to_string() != "/zz/bb.cc;/aa/cc.aa;/xx/yy/dd.cc") { + result = false; + } + } + { + SortConfiguration sortCfg; + cmList list{ "c/B", "a/c", "B/a" }; + + if (list.sort().to_string() != "B/a;a/c;c/B") { + result = false; + } + + sortCfg.Case = SortConfiguration::CaseSensitivity::INSENSITIVE; + if (list.sort(sortCfg).to_string() != "a/c;B/a;c/B") { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testTransform() +{ + std::cout << "testTransform()"; + + bool result = true; + + using AT = cmList::TransformSelector::AT; + using FOR = cmList::TransformSelector::FOR; + using REGEX = cmList::TransformSelector::REGEX; + + { + cmList list({ "AA", "BB", "CC", "DD", "EE" }); + + list.transform(cmList::TransformAction::APPEND, "-X"); + if (list.to_string() != "AA-X;BB-X;CC-X;DD-X;EE-X") { + result = false; + } + } + { + cmList list({ "AA", "BB", "CC", "DD", "EE" }); + + list.transform(cmList::TransformAction::PREPEND, "X-"); + if (list.to_string() != "X-AA;X-BB;X-CC;X-DD;X-EE") { + result = false; + } + } + { + cmList list({ "AA", "BB", "CC", "DD", "EE" }); + + list.transform(cmList::TransformAction::TOLOWER); + if (list.to_string() != "aa;bb;cc;dd;ee") { + result = false; + } + } + { + cmList list({ "aa", "bb", "cc", "dd", "ee" }); + + list.transform(cmList::TransformAction::TOUPPER); + if (list.to_string() != "AA;BB;CC;DD;EE") { + result = false; + } + } + { + cmList list({ " AA", "BB ", " CC ", "DD", "EE" }); + + list.transform(cmList::TransformAction::STRIP); + if (list.to_string() != "AA;BB;CC;DD;EE") { + result = false; + } + } + { + cmList list({ "$<CONFIG>AA", "BB$<OR>", "C$<AND>C", "$<OR>DD$<AND>", + "$<>E$<>E$<>" }); + + list.transform(cmList::TransformAction::GENEX_STRIP); + if (list.to_string() != "AA;BB;CC;DD;EE") { + result = false; + } + } + { + cmList list({ "ABC", "BBCB", "BCCCBC", "BCBCDD", "EBCBCEBC" }); + + list.transform(cmList::TransformAction::REPLACE, "^BC|BC$", "X"); + if (list.to_string() != "AX;BBCB;XCCX;XXDD;EBCBCEX") { + result = false; + } + } + { + auto atSelector = cmList::TransformSelector::New<AT>({ 1, 2, 4 }); + cmList list({ "AA", "BB", "CC", "DD", "EE" }); + + list.transform(cmList::TransformAction::TOLOWER, std::move(atSelector)); + if (list.to_string() != "AA;bb;cc;DD;ee") { + result = false; + } + } + { + auto atSelector = cmList::TransformSelector::New<AT>({ 1, 2, -1 }); + cmList list({ "AA", "BB", "CC", "DD", "EE" }); + + list.transform(cmList::TransformAction::TOLOWER, std::move(atSelector)); + if (list.to_string() != "AA;bb;cc;DD;ee") { + result = false; + } + } + { + auto forSelector = cmList::TransformSelector::New<FOR>({ 1, 3 }); + cmList list({ "AA", "BB", "CC", "DD", "EE" }); + + list.transform(cmList::TransformAction::TOLOWER, std::move(forSelector)); + if (list.to_string() != "AA;bb;cc;dd;EE") { + result = false; + } + } + { + auto forSelector = cmList::TransformSelector::New<FOR>({ 0, 4, 2 }); + cmList list({ "AA", "BB", "CC", "DD", "EE" }); + + list.transform(cmList::TransformAction::TOLOWER, std::move(forSelector)); + if (list.to_string() != "aa;BB;cc;DD;ee") { + result = false; + } + } + { + auto regexSelector = cmList::TransformSelector::New<REGEX>("^(A|D|E)"); + cmList list({ "AA", "BB", "CC", "DD", "EE" }); + + list.transform(cmList::TransformAction::TOLOWER, std::move(regexSelector)); + if (list.to_string() != "aa;BB;CC;dd;ee") { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testStaticModifiers() +{ + std::cout << "testStaticModifiers()"; + + bool result = true; + + { + std::vector<std::string> v{ "a", "b", "c" }; + cmList::assign(v, "d;e"_s); + + if (v.size() != 2 || v[0] != "d" || v[1] != "e") { + result = false; + } + } + { + std::vector<std::string> v{ "a", "b", "c" }; + cmList::append(v, "d;;e"_s); + + if (v.size() != 5 || v[3] != "d" || v[4] != "e") { + result = false; + } + } + { + std::vector<std::string> v{ "a", "b", "c" }; + cmList::append(v, "d;;e"_s, cmList::EmptyElements::Yes); + + if (v.size() != 6 || v[3] != "d" || !v[4].empty() || v[5] != "e") { + result = false; + } + } + { + std::vector<std::string> v{ "a", "b", "c" }; + cmList::prepend(v, "d;e"_s); + + if (v.size() != 5 || v[0] != "d" || v[1] != "e") { + result = false; + } + } + { + std::vector<std::string> v{ "a", "b", "c" }; + cmList::prepend(v, "d;;e"_s, cmList::EmptyElements::Yes); + + if (v.size() != 6 || v[0] != "d" || !v[1].empty() || v[2] != "e") { + result = false; + } + } + { + std::string list{ "a;b;c" }; + cmList::append(list, "d;e"_s); + + if (list != "a;b;c;d;e") { + result = false; + } + } + { + std::string list; + cmList::append(list, "d;e"_s); + + if (list != "d;e") { + result = false; + } + } + { + std::string list{ "a;b;c" }; + cmList::append(list, ""); + + if (list != "a;b;c;") { + result = false; + } + } + { + std::string list{ "a;b;c" }; + std::vector<std::string> v{ "d", "e" }; + cmList::append(list, v.begin(), v.end()); + + if (list != "a;b;c;d;e") { + result = false; + } + } + { + std::string list{ "a;b;c" }; + std::vector<std::string> v; + cmList::append(list, v.begin(), v.end()); + + if (list != "a;b;c") { + result = false; + } + } + { + std::string list; + std::vector<std::string> v{ "d", "e" }; + cmList::append(list, v.begin(), v.end()); + + if (list != "d;e") { + result = false; + } + } + { + std::string list{ "a;b;c" }; + cmList::prepend(list, "d;e"); + + if (list != "d;e;a;b;c") { + result = false; + } + } + { + std::string list; + cmList::prepend(list, "d;e"); + + if (list != "d;e") { + result = false; + } + } + { + std::string list{ "a;b;c" }; + cmList::prepend(list, ""); + + if (list != ";a;b;c") { + result = false; + } + } + { + std::string list{ "a;b;c" }; + std::vector<std::string> v{ "d", "e" }; + cmList::prepend(list, v.begin(), v.end()); + + if (list != "d;e;a;b;c") { + result = false; + } + } + { + std::string list{ "a;b;c" }; + std::vector<std::string> v; + cmList::prepend(list, v.begin(), v.end()); + + if (list != "a;b;c") { + result = false; + } + } + { + std::string list; + std::vector<std::string> v{ "d", "e" }; + cmList::prepend(list, v.begin(), v.end()); + + if (list != "d;e") { + result = false; + } + } + + checkResult(result); + + return result; +} +} + +int testList(int /*unused*/, char* /*unused*/[]) +{ + int result = 0; + + if (!testConstructors()) { + result = 1; + } + if (!testAssign()) { + result = 1; + } + if (!testConversions()) { + result = 1; + } + if (!testAccess()) { + result = 1; + } + if (!testModifiers()) { + result = 1; + } + if (!testRemoveItems()) { + result = 1; + } + if (!testRemoveDuplicates()) { + result = 1; + } + if (!testFilter()) { + result = 1; + } + if (!testReverse()) { + result = 1; + } + if (!testSort()) { + result = 1; + } + if (!testTransform()) { + result = 1; + } + if (!testStaticModifiers()) { + result = 1; + } + + return result; +} diff --git a/Tests/CMakeLib/testRST.expect b/Tests/CMakeLib/testRST.expect index 5e3cdb1..424b7d4 100644 --- a/Tests/CMakeLib/testRST.expect +++ b/Tests/CMakeLib/testRST.expect @@ -26,10 +26,15 @@ Generator expression ``$<SOME_GENEX:...>`` with brackets and parameter. Generator expression ``some genex`` with space and target. Generator expression ``$<SOME_GENEX>`` with brackets, space, and target. Generator expression ``$<SOME_GENEX:...>`` with brackets, parameter, space, and target. -Inline literal ``~!@#$%^&*( )_+-=\\[]{}'":;,<>.?/``. +Inline cref ``Link Dest``. +Inline cref ``Link_Dest_<Placeholder>``. +Inline cref ``Link Text``. +Inline cref ``Link_Text_<Placeholder>``. +Inline link Link Dest. Inline link Link Text. Inline link Link Text <With \-escaped Brackets>. Inline literal ``__`` followed by inline link Link Text. +Inline literal ``~!@#$%^&*( )_+-=\\[]{}'":;,<>.?/``. First TOC entry. @@ -46,7 +51,8 @@ Bracket Comment Content Bracket Comment Content ] -.. cmake:command:: some_cmd +.. cmake:command:: + some_cmd Command some_cmd description. @@ -54,7 +60,8 @@ Bracket Comment Content Command other_cmd description. -.. cmake:envvar:: some_var +.. cmake:envvar:: + some_var Environment variable some_var description. @@ -62,7 +69,8 @@ Bracket Comment Content Environment variable other_var description. -.. cmake:genex:: SOME_GENEX +.. cmake:genex:: + SOME_GENEX Generator expression SOME_GENEX description. @@ -70,7 +78,17 @@ Bracket Comment Content Generator expression $<OTHER_GENEX> description. -.. cmake:variable:: some_var +.. cmake:signature:: + some_command(SOME_SIGNATURE) + + Command some_command SOME_SIGNATURE description. + +.. signature:: other_command(OTHER_SIGNATURE) + + Command other_command OTHER_SIGNATURE description. + +.. cmake:variable:: + some_var Variable some_var description. diff --git a/Tests/CMakeLib/testRST.rst b/Tests/CMakeLib/testRST.rst index 4139801..3a38407 100644 --- a/Tests/CMakeLib/testRST.rst +++ b/Tests/CMakeLib/testRST.rst @@ -33,10 +33,15 @@ Generator expression :genex:`$<SOME_GENEX:...>` with brackets and parameter. Generator expression :genex:`some genex <SOME_GENEX>` with space and target. Generator expression :genex:`$<SOME_GENEX> <SOME_GENEX>` with brackets, space, and target. Generator expression :genex:`$<SOME_GENEX:...> <SOME_GENEX>` with brackets, parameter, space, and target. -Inline literal ``~!@#$%^&*( )_+-=\\[]{}'":;,<>.?/``. +Inline cref :cref:`Link Dest`. +Inline cref :cref:`Link_Dest_<Placeholder>`. +Inline cref :cref:`Link Text <ExternalDest>`. +Inline cref :cref:`Link_Text_<Placeholder> <ExternalDest>`. +Inline link `Link Dest`_. Inline link `Link Text <ExternalDest>`_. Inline link `Link Text \<With \\-escaped Brackets\> <ExternalDest>`_. Inline literal ``__`` followed by inline link `Link Text <InternalDest_>`_. +Inline literal ``~!@#$%^&*( )_+-=\\[]{}'":;,<>.?/``. .. |not replaced| replace:: not replaced through toctree .. |not replaced in literal| replace:: replaced in parsed literal @@ -49,7 +54,8 @@ Inline literal ``__`` followed by inline link `Link Text <InternalDest_>`_. .. cmake-module:: testRSTmod.cmake -.. cmake:command:: some_cmd +.. cmake:command:: + some_cmd Command some_cmd description. @@ -57,7 +63,8 @@ Inline literal ``__`` followed by inline link `Link Text <InternalDest_>`_. Command other_cmd description. -.. cmake:envvar:: some_var +.. cmake:envvar:: + some_var Environment variable some_var description. @@ -65,7 +72,8 @@ Inline literal ``__`` followed by inline link `Link Text <InternalDest_>`_. Environment variable other_var description. -.. cmake:genex:: SOME_GENEX +.. cmake:genex:: + SOME_GENEX Generator expression SOME_GENEX description. @@ -73,7 +81,17 @@ Inline literal ``__`` followed by inline link `Link Text <InternalDest_>`_. Generator expression $<OTHER_GENEX> description. -.. cmake:variable:: some_var +.. cmake:signature:: + some_command(SOME_SIGNATURE) + + Command some_command SOME_SIGNATURE description. + +.. signature:: other_command(OTHER_SIGNATURE) + + Command other_command OTHER_SIGNATURE description. + +.. cmake:variable:: + some_var Variable some_var description. diff --git a/Tests/CMakeLib/testStringAlgorithms.cxx b/Tests/CMakeLib/testStringAlgorithms.cxx index 1bb23df..78442ba 100644 --- a/Tests/CMakeLib/testStringAlgorithms.cxx +++ b/Tests/CMakeLib/testStringAlgorithms.cxx @@ -1,12 +1,11 @@ /* 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 "cmConfigure.h" // IWYU pragma: keep #include <iostream> #include <sstream> #include <string> -#include <type_traits> #include <utility> #include <vector> diff --git a/Tests/CMakeLib/testSystemTools.cxx b/Tests/CMakeLib/testSystemTools.cxx index 754205e..0f0c025 100644 --- a/Tests/CMakeLib/testSystemTools.cxx +++ b/Tests/CMakeLib/testSystemTools.cxx @@ -36,6 +36,53 @@ int testSystemTools(int /*unused*/, char* /*unused*/[]) "cmSystemTools::UpperCase"); // ---------------------------------------------------------------------- + // Test cmSystemTools::VersionCompare + cmAssert(cmSystemTools::VersionCompareEqual("", ""), + "VersionCompareEqual empty string"); + cmAssert(!cmSystemTools::VersionCompareGreater("", ""), + "VersionCompareGreater empty string"); + cmAssert(cmSystemTools::VersionCompareEqual("1", "1a"), + "VersionCompareEqual letters"); + cmAssert(!cmSystemTools::VersionCompareGreater("1", "1a"), + "VersionCompareGreater letters"); + cmAssert(cmSystemTools::VersionCompareEqual("001", "1"), + "VersionCompareEqual leading zeros equal"); + cmAssert(!cmSystemTools::VersionCompareGreater("001", "1"), + "VersionCompareGreater leading zeros equal"); + cmAssert(!cmSystemTools::VersionCompareEqual("002", "1"), + "VersionCompareEqual leading zeros greater"); + cmAssert(cmSystemTools::VersionCompareGreater("002", "1"), + "VersionCompareGreater leading zeros greater"); + cmAssert(!cmSystemTools::VersionCompareEqual("6.2.1", "6.3.1"), + "VersionCompareEqual components less"); + cmAssert(!cmSystemTools::VersionCompareGreater("6.2.1", "6.3.1"), + "VersionCompareGreater components less"); + cmAssert(!cmSystemTools::VersionCompareEqual("6.2.1", "6.2"), + "VersionCompareEqual different length"); + cmAssert(cmSystemTools::VersionCompareGreater("6.2.1", "6.2"), + "VersionCompareGreater different length"); + cmAssert( + !cmSystemTools::VersionCompareEqual( + "3.14159265358979323846264338327950288419716939937510582097494459230", + "3.14159265358979323846264338327950288419716939937510582097494459231"), + "VersionCompareEqual long number"); + cmAssert( + !cmSystemTools::VersionCompareGreater( + "3.14159265358979323846264338327950288419716939937510582097494459230", + "3.14159265358979323846264338327950288419716939937510582097494459231"), + "VersionCompareGreater long number"); + cmAssert( + !cmSystemTools::VersionCompareEqual( + "3.141592653589793238462643383279502884197169399375105820974944592307", + "3.14159265358979323846264338327950288419716939937510582097494459231"), + "VersionCompareEqual more digits"); + cmAssert( + cmSystemTools::VersionCompareGreater( + "3.141592653589793238462643383279502884197169399375105820974944592307", + "3.14159265358979323846264338327950288419716939937510582097494459231"), + "VersionCompareGreater more digits"); + + // ---------------------------------------------------------------------- // Test cmSystemTools::strverscmp cmAssert(cmSystemTools::strverscmp("", "") == 0, "strverscmp empty string"); cmAssert(cmSystemTools::strverscmp("abc", "") > 0, diff --git a/Tests/CMakeLib/testUVProcessChain.cxx b/Tests/CMakeLib/testUVProcessChain.cxx index c924083..7630aa0 100644 --- a/Tests/CMakeLib/testUVProcessChain.cxx +++ b/Tests/CMakeLib/testUVProcessChain.cxx @@ -1,9 +1,12 @@ #include <algorithm> #include <csignal> +#include <cstdio> #include <functional> #include <iostream> #include <sstream> #include <string> +#include <type_traits> +#include <utility> #include <vector> #include <cm/memory> @@ -11,46 +14,50 @@ #include <cm3p/uv.h> #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; + cmUVProcessChain::ExceptionCode ExceptionCode; + std::string ExceptionString; }; -static const std::vector<ExpectedStatus> status1 = { - { false, false, false, { 0, 0 } }, - { false, false, false, { 0, 0 } }, - { false, false, false, { 0, 0 } }, -}; - -static const std::vector<ExpectedStatus> status2 = { - { true, true, true, { 0, 0 } }, - { false, false, false, { 0, 0 } }, - { false, false, false, { 0, 0 } }, -}; - -static const std::vector<ExpectedStatus> status3 = { - { true, true, true, { 0, 0 } }, - { true, true, true, { 1, 0 } }, -#ifdef _WIN32 - { true, true, true, { 2, 0 } }, -#else - { true, false, true, { 0, SIGABRT } }, -#endif -}; +static const char* ExceptionCodeToString(cmUVProcessChain::ExceptionCode code) +{ + switch (code) { + case cmUVProcessChain::ExceptionCode::None: + return "None"; + case cmUVProcessChain::ExceptionCode::Fault: + return "Fault"; + case cmUVProcessChain::ExceptionCode::Illegal: + return "Illegal"; + case cmUVProcessChain::ExceptionCode::Interrupt: + return "Interrupt"; + case cmUVProcessChain::ExceptionCode::Numerical: + return "Numerical"; + case cmUVProcessChain::ExceptionCode::Spawn: + return "Spawn"; + case cmUVProcessChain::ExceptionCode::Other: + return "Other"; + default: + return ""; + } +} 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 && @@ -61,6 +68,11 @@ bool operator==(const cmUVProcessChain::Status* actual, expected.Status.TermSignal != actual->TermSignal) { return false; } + if (expected.Status.Finished && + std::make_pair(expected.ExceptionCode, expected.ExceptionString) != + actual->GetException()) { + return false; + } return true; } @@ -110,30 +122,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) - << 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) { - std::cout << " ExitStatus: " << a->ExitStatus - << ", TermSignal: " << a->TermSignal << 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()); @@ -147,8 +225,12 @@ static bool checkExecution(cmUVProcessChainBuilder& builder, printResults(status, status1); return false; } + if (chain->Finished()) { + std::cout << "Finished() returned true, should be false" << std::endl; + return false; + } - if (chain->Wait(6000)) { + if (chain->Wait(9000)) { std::cout << "Wait() returned true, should be false" << std::endl; return false; } @@ -158,6 +240,10 @@ static bool checkExecution(cmUVProcessChainBuilder& builder, printResults(status, status2); return false; } + if (chain->Finished()) { + std::cout << "Finished() returned true, should be false" << std::endl; + return false; + } if (!chain->Wait()) { std::cout << "Wait() returned false, should be true" << std::endl; @@ -169,6 +255,10 @@ static bool checkExecution(cmUVProcessChainBuilder& builder, printResults(status, status3); return false; } + if (!chain->Finished()) { + std::cout << "Finished() returned false, should be true" << std::endl; + return false; + } return true; } @@ -212,16 +302,76 @@ 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() < 0) { + std::cout << "ErrorStream() was invalid, expecting valid" << std::endl; + return false; + } + + cmUVPipeIStream output(chain->GetLoop(), chain->OutputStream()); + cmUVPipeIStream error(chain->GetLoop(), chain->ErrorStream()); + + if (!checkOutput(output, error)) { + return false; + } + + return true; +} + +bool testUVProcessChainBuiltinMerged(const char* helperCommand) +{ + cmUVProcessChainBuilder builder; + std::unique_ptr<cmUVProcessChain> chain; + builder.AddCommand({ helperCommand, "echo" }) + .AddCommand({ helperCommand, "capitalize" }) + .AddCommand({ helperCommand, "dedup" }) + .SetMergedBuiltinStreams(); + + if (!checkExecution(builder, chain)) { + return false; + } + + 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()) { + std::cout << "OutputStream() and ErrorStream() expected to be the same" + << std::endl; + return false; + } + + cmUVPipeIStream mergedStream(chain->GetLoop(), chain->OutputStream()); - if (!checkOutput(*chain->OutputStream(), *chain->ErrorStream())) { + std::string merged = getInput(mergedStream); + auto qemuErrorPos = merged.find("qemu:"); + if (qemuErrorPos != std::string::npos) { + merged.resize(qemuErrorPos); + } + if (merged.length() != cmStrLen("HELO WRD!123") || + merged.find('1') == std::string::npos || + merged.find('2') == std::string::npos || + merged.find('3') == std::string::npos) { + std::cout << "Expected output to contain '1', '2', and '3', was \"" + << merged << "\"" << std::endl; + return false; + } + std::string output; + for (auto const& c : merged) { + if (c != '1' && c != '2' && c != '3') { + output += c; + } + } + if (output != "HELO WRD!") { + std::cout << "Output was \"" << output << "\", expected \"HELO WRD!\"" + << std::endl; return false; } @@ -254,12 +404,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; } @@ -302,12 +452,200 @@ 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() >= 0) { + std::cout << "ErrorStream() was valid, expecting invalid" << std::endl; + return false; + } + + return true; +} + +bool testUVProcessChainCwdUnchanged(const char* helperCommand) +{ + cmUVProcessChainBuilder builder; + builder.AddCommand({ helperCommand, "pwd" }) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); + + auto chain = builder.Start(); + chain.Wait(); + if (chain.GetStatus().front()->ExitStatus != 0) { + std::cout << "Exit status was " << chain.GetStatus().front()->ExitStatus + << ", expecting 0" << std::endl; + return false; + } + + 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; + return false; + } + + return true; +} + +bool testUVProcessChainCwdChanged(const char* helperCommand) +{ + cmUVProcessChainBuilder builder; + builder.AddCommand({ helperCommand, "pwd" }) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR) + .SetWorkingDirectory(".."); + + auto chain = builder.Start(); + chain.Wait(); + if (chain.GetStatus().front()->ExitStatus != 0) { + std::cout << "Exit status was " << chain.GetStatus().front()->ExitStatus + << ", expecting 0" << std::endl; + return false; + } + + 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; + return false; + } + + 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, fileno(f.get())) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT); + + auto chain = builder.Start(); + + if (!chain.Wait()) { + std::cout << "Wait() timed out" << std::endl; return false; } - if (chain->ErrorStream()) { - std::cout << "ErrorStream() was not null, expecting null" << std::endl; + + 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; } @@ -326,6 +664,11 @@ int testUVProcessChain(int argc, char** const argv) return -1; } + if (!testUVProcessChainBuiltinMerged(argv[1])) { + std::cout << "While executing testUVProcessChainBuiltinMerged().\n"; + return -1; + } + if (!testUVProcessChainExternal(argv[1])) { std::cout << "While executing testUVProcessChainExternal().\n"; return -1; @@ -336,5 +679,25 @@ int testUVProcessChain(int argc, char** const argv) return -1; } + if (!testUVProcessChainCwdUnchanged(argv[1])) { + std::cout << "While executing testUVProcessChainCwdUnchanged().\n"; + return -1; + } + + if (!testUVProcessChainCwdChanged(argv[1])) { + std::cout << "While executing testUVProcessChainCwdChanged().\n"; + 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; + } + return 0; } diff --git a/Tests/CMakeLib/testUVProcessChainHelper.cxx b/Tests/CMakeLib/testUVProcessChainHelper.cxx index bc0ef8e..fcc45b0 100644 --- a/Tests/CMakeLib/testUVProcessChainHelper.cxx +++ b/Tests/CMakeLib/testUVProcessChainHelper.cxx @@ -7,6 +7,12 @@ #include <string> #include <thread> +#ifdef _WIN32 +# include <windows.h> +#endif + +#include "cmSystemTools.h" + static std::string getStdin() { char buffer[1024]; @@ -26,13 +32,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)); @@ -59,14 +65,18 @@ int main(int argc, char** argv) } // On Windows, the exit code of abort() is different between debug and - // release builds, and does not yield a term_signal in libuv in either - // case. For the sake of simplicity, we just return another non-zero code. + // release builds. Instead, simulate an access violation. #ifdef _WIN32 - return 2; + return STATUS_ACCESS_VIOLATION; #else std::abort(); #endif } + if (command == "pwd") { + std::string cwd = cmSystemTools::GetCurrentWorkingDirectory(); + std::cout << cwd << std::flush; + return 0; + } return -1; } diff --git a/Tests/CMakeLib/testUVStreambuf.cxx b/Tests/CMakeLib/testUVStreambuf.cxx index f9ed6af..f3977d4 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,90 @@ 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; + 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; +} + int testUVStreambuf(int argc, char** const argv) { if (argc < 2) { @@ -454,5 +541,15 @@ 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 testUVPipeIStream().\n"; + return -1; + } + return 0; } diff --git a/Tests/CMakeLib/testVisualStudioSlnParser.cxx b/Tests/CMakeLib/testVisualStudioSlnParser.cxx index c1bf3d4..3485bac 100644 --- a/Tests/CMakeLib/testVisualStudioSlnParser.cxx +++ b/Tests/CMakeLib/testVisualStudioSlnParser.cxx @@ -80,7 +80,6 @@ int testVisualStudioSlnParser(int, char*[]) "cmsysProcessFwd9x", "cmsysTestDynload", "cmsysTestProcess", - "cmsysTestSharedForward", "cmsysTestsC", "cmsysTestsCxx", "cmsys_c", diff --git a/Tests/CMakeLib/testVisualStudioSlnParser_data/valid.sln-file b/Tests/CMakeLib/testVisualStudioSlnParser_data/valid.sln-file index 395b953..1f148fc 100644 --- a/Tests/CMakeLib/testVisualStudioSlnParser_data/valid.sln-file +++ b/Tests/CMakeLib/testVisualStudioSlnParser_data/valid.sln-file @@ -21,7 +21,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ALL_BUILD", "ALL_BUILD.vcxp {29D5FCAF-20D0-4DEF-8529-F035C249E996} = {29D5FCAF-20D0-4DEF-8529-F035C249E996} {A0421DCA-AC3E-42D0-94AC-379A21A1E591} = {A0421DCA-AC3E-42D0-94AC-379A21A1E591} {C6AF7E57-CE57-4462-AE1D-BF520701480E} = {C6AF7E57-CE57-4462-AE1D-BF520701480E} - {F2CAAAB3-9568-4284-B8E3-13955183A6D7} = {F2CAAAB3-9568-4284-B8E3-13955183A6D7} {D8294E4A-03C5-43D7-AE35-15603F502DC0} = {D8294E4A-03C5-43D7-AE35-15603F502DC0} {A4921D15-411F-436A-B6F3-F8381652A8E1} = {A4921D15-411F-436A-B6F3-F8381652A8E1} {60BEB3AF-B4EF-4363-8747-C40177BC2D9C} = {60BEB3AF-B4EF-4363-8747-C40177BC2D9C} @@ -220,12 +219,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cmsysTestProcess", "Source\ {60BEB3AF-B4EF-4363-8747-C40177BC2D9C} = {60BEB3AF-B4EF-4363-8747-C40177BC2D9C} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cmsysTestSharedForward", "Source\kwsys\cmsysTestSharedForward.vcxproj", "{F2CAAAB3-9568-4284-B8E3-13955183A6D7}" - ProjectSection(ProjectDependencies) = postProject - {90BC31D7-A3E8-4F04-8049-2236C239A044} = {90BC31D7-A3E8-4F04-8049-2236C239A044} - {60BEB3AF-B4EF-4363-8747-C40177BC2D9C} = {60BEB3AF-B4EF-4363-8747-C40177BC2D9C} - EndProjectSection -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cmsysTestsC", "Source\kwsys\cmsysTestsC.vcxproj", "{D8294E4A-03C5-43D7-AE35-15603F502DC0}" ProjectSection(ProjectDependencies) = postProject {90BC31D7-A3E8-4F04-8049-2236C239A044} = {90BC31D7-A3E8-4F04-8049-2236C239A044} @@ -528,14 +521,6 @@ Global {C6AF7E57-CE57-4462-AE1D-BF520701480E}.MinSizeRel|x64.Build.0 = MinSizeRel|x64 {C6AF7E57-CE57-4462-AE1D-BF520701480E}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64 {C6AF7E57-CE57-4462-AE1D-BF520701480E}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64 - {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.Debug|x64.ActiveCfg = Debug|x64 - {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.Debug|x64.Build.0 = Debug|x64 - {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.Release|x64.ActiveCfg = Release|x64 - {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.Release|x64.Build.0 = Release|x64 - {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64 - {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.MinSizeRel|x64.Build.0 = MinSizeRel|x64 - {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64 - {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64 {D8294E4A-03C5-43D7-AE35-15603F502DC0}.Debug|x64.ActiveCfg = Debug|x64 {D8294E4A-03C5-43D7-AE35-15603F502DC0}.Debug|x64.Build.0 = Debug|x64 {D8294E4A-03C5-43D7-AE35-15603F502DC0}.Release|x64.ActiveCfg = Release|x64 @@ -667,7 +652,6 @@ Global {29D5FCAF-20D0-4DEF-8529-F035C249E996} = {EF1DFA45-6F7A-4760-8EB5-69A8A221FC54} {A0421DCA-AC3E-42D0-94AC-379A21A1E591} = {EF1DFA45-6F7A-4760-8EB5-69A8A221FC54} {C6AF7E57-CE57-4462-AE1D-BF520701480E} = {EF1DFA45-6F7A-4760-8EB5-69A8A221FC54} - {F2CAAAB3-9568-4284-B8E3-13955183A6D7} = {EF1DFA45-6F7A-4760-8EB5-69A8A221FC54} {D8294E4A-03C5-43D7-AE35-15603F502DC0} = {EF1DFA45-6F7A-4760-8EB5-69A8A221FC54} {A4921D15-411F-436A-B6F3-F8381652A8E1} = {EF1DFA45-6F7A-4760-8EB5-69A8A221FC54} {60BEB3AF-B4EF-4363-8747-C40177BC2D9C} = {EF1DFA45-6F7A-4760-8EB5-69A8A221FC54} |