summaryrefslogtreecommitdiffstats
path: root/Tests/CMakeLib
diff options
context:
space:
mode:
Diffstat (limited to 'Tests/CMakeLib')
-rw-r--r--Tests/CMakeLib/CMakeLists.txt35
-rw-r--r--Tests/CMakeLib/DebuggerSample/CMakeLists.txt9
-rw-r--r--Tests/CMakeLib/DebuggerSample/script.cmake1
-rw-r--r--Tests/CMakeLib/testCMakePath.cxx441
-rw-r--r--Tests/CMakeLib/testCTestResourceAllocator.cxx16
-rw-r--r--Tests/CMakeLib/testCTestResourceSpec.cxx115
-rw-r--r--Tests/CMakeLib/testCommon.h30
-rw-r--r--Tests/CMakeLib/testDebugger.h103
-rw-r--r--Tests/CMakeLib/testDebuggerAdapter.cxx201
-rw-r--r--Tests/CMakeLib/testDebuggerAdapterPipe.cxx184
-rw-r--r--Tests/CMakeLib/testDebuggerBreakpointManager.cxx185
-rw-r--r--Tests/CMakeLib/testDebuggerNamedPipe.cxx218
-rw-r--r--Tests/CMakeLib/testDebuggerVariables.cxx214
-rw-r--r--Tests/CMakeLib/testDebuggerVariablesHelper.cxx588
-rw-r--r--Tests/CMakeLib/testDebuggerVariablesManager.cxx50
-rw-r--r--Tests/CMakeLib/testGccDepfileReader_data/deps1.txt30
-rw-r--r--Tests/CMakeLib/testGccDepfileReader_data/deps3.txt8
-rw-r--r--Tests/CMakeLib/testJSONHelpers.cxx203
-rw-r--r--Tests/CMakeLib/testList.cxx995
-rw-r--r--Tests/CMakeLib/testRST.expect28
-rw-r--r--Tests/CMakeLib/testRST.rst28
-rw-r--r--Tests/CMakeLib/testStringAlgorithms.cxx3
-rw-r--r--Tests/CMakeLib/testSystemTools.cxx47
-rw-r--r--Tests/CMakeLib/testUVProcessChain.cxx471
-rw-r--r--Tests/CMakeLib/testUVProcessChainHelper.cxx20
-rw-r--r--Tests/CMakeLib/testUVStreambuf.cxx97
-rw-r--r--Tests/CMakeLib/testVisualStudioSlnParser.cxx1
-rw-r--r--Tests/CMakeLib/testVisualStudioSlnParser_data/valid.sln-file16
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}