diff options
Diffstat (limited to 'unittests')
32 files changed, 1617 insertions, 63 deletions
diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 94031accfec77..bdcb51675a0f8 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -18,14 +18,32 @@ function(add_lldb_unittest test_name) ${ARGN} ) + add_custom_command( + TARGET ${test_name} + POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/Inputs) + lldb_link_common_libs(${test_name} EXE) target_link_libraries(${test_name} ${CLANG_USED_LIBS} ${LLDB_SYSTEM_LIBS}) llvm_config(${test_name} ${LLVM_LINK_COMPONENTS}) endfunction() +function(add_unittest_inputs test_name inputs) + foreach (INPUT ${inputs}) + add_custom_command( + TARGET ${test_name} + POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy ${CMAKE_CURRENT_SOURCE_DIR}/Inputs/${INPUT} ${CMAKE_CURRENT_BINARY_DIR}/Inputs + COMMENT "Copying ${INPUT} to binary directory.") + endforeach() +endfunction() + +add_subdirectory(Core) add_subdirectory(Editline) add_subdirectory(Expression) add_subdirectory(Host) add_subdirectory(Interpreter) add_subdirectory(ScriptInterpreter) +add_subdirectory(Symbol) +add_subdirectory(SymbolFile) add_subdirectory(Utility) diff --git a/unittests/Core/CMakeLists.txt b/unittests/Core/CMakeLists.txt new file mode 100644 index 0000000000000..ad9def181de58 --- /dev/null +++ b/unittests/Core/CMakeLists.txt @@ -0,0 +1,4 @@ +add_lldb_unittest(LLDBCoreTests + DataExtractorTest.cpp + ScalarTest.cpp + ) diff --git a/unittests/Core/DataExtractorTest.cpp b/unittests/Core/DataExtractorTest.cpp new file mode 100644 index 0000000000000..f22883875055f --- /dev/null +++ b/unittests/Core/DataExtractorTest.cpp @@ -0,0 +1,39 @@ +//===-- DataExtractorTest.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0) +// Workaround for MSVC standard library bug, which fails to include <thread> when +// exceptions are disabled. +#include <eh.h> +#endif + +#include "gtest/gtest.h" + +#include "lldb/Core/DataExtractor.h" + +using namespace lldb_private; + +TEST(DataExtractorTest, GetBitfield) +{ + char buffer[] = { 0x01, 0x23, 0x45, 0x67 }; + DataExtractor LE(buffer, sizeof(buffer), lldb::eByteOrderLittle, sizeof(void *)); + DataExtractor BE(buffer, sizeof(buffer), lldb::eByteOrderBig, sizeof(void *)); + + lldb::offset_t offset; + + offset = 0; + ASSERT_EQ(buffer[1], LE.GetMaxU64Bitfield(&offset, sizeof(buffer), 8, 8)); + offset = 0; + ASSERT_EQ(buffer[1], BE.GetMaxU64Bitfield(&offset, sizeof(buffer), 8, 8)); + + offset = 0; + ASSERT_EQ(buffer[1], LE.GetMaxS64Bitfield(&offset, sizeof(buffer), 8, 8)); + offset = 0; + ASSERT_EQ(buffer[1], BE.GetMaxS64Bitfield(&offset, sizeof(buffer), 8, 8)); +} diff --git a/unittests/Core/ScalarTest.cpp b/unittests/Core/ScalarTest.cpp new file mode 100644 index 0000000000000..bf85f8e9623b0 --- /dev/null +++ b/unittests/Core/ScalarTest.cpp @@ -0,0 +1,105 @@ +//===-- ScalarTest.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0) +// Workaround for MSVC standard library bug, which fails to include <thread> when +// exceptions are disabled. +#include <eh.h> +#endif + +#include "gtest/gtest.h" + +#include "lldb/Core/Error.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Host/Endian.h" + +using namespace lldb_private; + +TEST(ScalarTest, RightShiftOperator) +{ + int a = 0x00001000; + int b = 0xFFFFFFFF; + int c = 4; + Scalar a_scalar(a); + Scalar b_scalar(b); + Scalar c_scalar(c); + ASSERT_EQ(a >> c, a_scalar >> c_scalar); + ASSERT_EQ(b >> c, b_scalar >> c_scalar); +} + +TEST(ScalarTest, GetBytes) +{ + int a = 0x01020304; + long long b = 0x0102030405060708LL; + float c = 1234567.89e42; + double d = 1234567.89e42; + char e[16] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; + char f[32] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 }; + Scalar a_scalar(a); + Scalar b_scalar(b); + Scalar c_scalar(c); + Scalar d_scalar(d); + Scalar e_scalar; + Scalar f_scalar; + DataExtractor e_data(e, sizeof(e), endian::InlHostByteOrder(), sizeof(void *)); + Error e_error = e_scalar.SetValueFromData(e_data, lldb::eEncodingUint, sizeof(e)); + DataExtractor f_data(f, sizeof(f), endian::InlHostByteOrder(), sizeof(void *)); + Error f_error = f_scalar.SetValueFromData(f_data, lldb::eEncodingUint, sizeof(f)); + ASSERT_EQ(0, memcmp(&a, a_scalar.GetBytes(), sizeof(a))); + ASSERT_EQ(0, memcmp(&b, b_scalar.GetBytes(), sizeof(b))); + ASSERT_EQ(0, memcmp(&c, c_scalar.GetBytes(), sizeof(c))); + ASSERT_EQ(0, memcmp(&d, d_scalar.GetBytes(), sizeof(d))); + ASSERT_EQ(0, e_error.Fail()); + ASSERT_EQ(0, memcmp(e, e_scalar.GetBytes(), sizeof(e))); + ASSERT_EQ(0, f_error.Fail()); + ASSERT_EQ(0, memcmp(f, f_scalar.GetBytes(), sizeof(f))); +} + +TEST(ScalarTest, CastOperations) +{ + long long a = 0xf1f2f3f4f5f6f7f8LL; + Scalar a_scalar(a); + ASSERT_EQ((signed char)a, a_scalar.SChar()); + ASSERT_EQ((unsigned char)a, a_scalar.UChar()); + ASSERT_EQ((signed short)a, a_scalar.SShort()); + ASSERT_EQ((unsigned short)a, a_scalar.UShort()); + ASSERT_EQ((signed int)a, a_scalar.SInt()); + ASSERT_EQ((unsigned int)a, a_scalar.UInt()); + ASSERT_EQ((signed long)a, a_scalar.SLong()); + ASSERT_EQ((unsigned long)a, a_scalar.ULong()); + ASSERT_EQ((signed long long)a, a_scalar.SLongLong()); + ASSERT_EQ((unsigned long long)a, a_scalar.ULongLong()); +} + +TEST(ScalarTest, ExtractBitfield) +{ + uint32_t len = sizeof(long long) * 8; + + long long a1 = 0xf1f2f3f4f5f6f7f8LL; + long long b1 = 0xff1f2f3f4f5f6f7fLL; + Scalar s_scalar(a1); + ASSERT_TRUE(s_scalar.ExtractBitfield(0, 0)); + ASSERT_EQ(0, memcmp(&a1, s_scalar.GetBytes(), sizeof(a1))); + ASSERT_TRUE(s_scalar.ExtractBitfield(len, 0)); + ASSERT_EQ(0, memcmp(&a1, s_scalar.GetBytes(), sizeof(a1))); + ASSERT_TRUE(s_scalar.ExtractBitfield(len - 4, 4)); + ASSERT_EQ(0, memcmp(&b1, s_scalar.GetBytes(), sizeof(b1))); + + unsigned long long a2 = 0xf1f2f3f4f5f6f7f8ULL; + unsigned long long b2 = 0x0f1f2f3f4f5f6f7fULL; + Scalar u_scalar(a2); + ASSERT_TRUE(u_scalar.ExtractBitfield(0, 0)); + ASSERT_EQ(0, memcmp(&a2, u_scalar.GetBytes(), sizeof(a2))); + ASSERT_TRUE(u_scalar.ExtractBitfield(len, 0)); + ASSERT_EQ(0, memcmp(&a2, u_scalar.GetBytes(), sizeof(a2))); + ASSERT_TRUE(u_scalar.ExtractBitfield(len - 4, 4)); + ASSERT_EQ(0, memcmp(&b2, u_scalar.GetBytes(), sizeof(b2))); +} diff --git a/unittests/Editline/EditlineTest.cpp b/unittests/Editline/EditlineTest.cpp index d82ef4c17c773..e2552ffdd3b83 100644 --- a/unittests/Editline/EditlineTest.cpp +++ b/unittests/Editline/EditlineTest.cpp @@ -1,4 +1,4 @@ -//===-- EditlineTest.cpp -----------------------------------------*- C++ -*-===// +//===-- EditlineTest.cpp ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -191,8 +191,9 @@ EditlineAdapter::SendLine (const std::string &line) eoln, eoln_length * sizeof (char)); - EXPECT_EQ (eoln_length * sizeof (char), input_bytes_written); - return eoln_length * sizeof (char) == input_bytes_written; + EXPECT_NE(-1, input_bytes_written) << strerror(errno); + EXPECT_EQ (eoln_length * sizeof (char), size_t(input_bytes_written)); + return eoln_length * sizeof (char) == size_t(input_bytes_written); } bool @@ -293,49 +294,55 @@ EditlineAdapter::ConsumeAllOutput () } } -TEST (EditlineTest, EditlineReceivesSingleLineText) +class EditlineTestFixture : public ::testing::Test { - setenv ("TERM", "vt100", 1); +private: + EditlineAdapter _el_adapter; + std::shared_ptr<std::thread> _sp_output_thread; - // Create an editline. - EditlineAdapter el_adapter; - EXPECT_TRUE (el_adapter.IsValid ()); - if (!el_adapter.IsValid ()) - return; +public: + void SetUp() + { + // We need a TERM set properly for editline to work as expected. + setenv("TERM", "vt100", 1); + + // Validate the editline adapter. + EXPECT_TRUE(_el_adapter.IsValid()); + if (!_el_adapter.IsValid()) + return; + + // Dump output. + _sp_output_thread.reset(new std::thread([&] { _el_adapter.ConsumeAllOutput(); })); + } + + void TearDown() + { + _el_adapter.CloseInput(); + if (_sp_output_thread) + _sp_output_thread->join(); + } - // Dump output. - std::thread el_output_thread( [&] { el_adapter.ConsumeAllOutput (); }); + EditlineAdapter &GetEditlineAdapter() { return _el_adapter; } +}; +TEST_F(EditlineTestFixture, EditlineReceivesSingleLineText) +{ // Send it some text via our virtual keyboard. const std::string input_text ("Hello, world"); - EXPECT_TRUE (el_adapter.SendLine (input_text)); + EXPECT_TRUE(GetEditlineAdapter().SendLine(input_text)); // Verify editline sees what we put in. std::string el_reported_line; bool input_interrupted = false; - const bool received_line = el_adapter.GetLine (el_reported_line, input_interrupted, TIMEOUT_MILLIS); + const bool received_line = GetEditlineAdapter().GetLine(el_reported_line, input_interrupted, TIMEOUT_MILLIS); EXPECT_TRUE (received_line); EXPECT_FALSE (input_interrupted); EXPECT_EQ (input_text, el_reported_line); - - el_adapter.CloseInput(); - el_output_thread.join(); } -TEST (EditlineTest, EditlineReceivesMultiLineText) +TEST_F(EditlineTestFixture, EditlineReceivesMultiLineText) { - setenv ("TERM", "vt100", 1); - - // Create an editline. - EditlineAdapter el_adapter; - EXPECT_TRUE (el_adapter.IsValid ()); - if (!el_adapter.IsValid ()) - return; - - // Stick editline output/error dumpers on separate threads. - std::thread el_output_thread( [&] { el_adapter.ConsumeAllOutput (); }); - // Send it some text via our virtual keyboard. std::vector<std::string> input_lines; input_lines.push_back ("int foo()"); @@ -344,25 +351,22 @@ TEST (EditlineTest, EditlineReceivesMultiLineText) input_lines.push_back ("}"); input_lines.push_back (""); - EXPECT_TRUE (el_adapter.SendLines (input_lines)); + EXPECT_TRUE(GetEditlineAdapter().SendLines(input_lines)); // Verify editline sees what we put in. lldb_private::StringList el_reported_lines; bool input_interrupted = false; - EXPECT_TRUE (el_adapter.GetLines (el_reported_lines, input_interrupted, TIMEOUT_MILLIS)); + EXPECT_TRUE(GetEditlineAdapter().GetLines(el_reported_lines, input_interrupted, TIMEOUT_MILLIS)); EXPECT_FALSE (input_interrupted); // Without any auto indentation support, our output should directly match our input. EXPECT_EQ (input_lines.size (), el_reported_lines.GetSize ()); if (input_lines.size () == el_reported_lines.GetSize ()) { - for (auto i = 0; i < input_lines.size(); ++i) + for (size_t i = 0; i < input_lines.size(); ++i) EXPECT_EQ (input_lines[i], el_reported_lines[i]); } - - el_adapter.CloseInput(); - el_output_thread.join(); } #endif diff --git a/unittests/Host/CMakeLists.txt b/unittests/Host/CMakeLists.txt index b4739e113f480..be0450874203b 100644 --- a/unittests/Host/CMakeLists.txt +++ b/unittests/Host/CMakeLists.txt @@ -1,4 +1,5 @@ add_lldb_unittest(HostTests + FileSpecTest.cpp SocketAddressTest.cpp SocketTest.cpp SymbolsTest.cpp diff --git a/unittests/Host/FileSpecTest.cpp b/unittests/Host/FileSpecTest.cpp new file mode 100644 index 0000000000000..4e619529773f3 --- /dev/null +++ b/unittests/Host/FileSpecTest.cpp @@ -0,0 +1,111 @@ +//===-- FileSpecTest.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "lldb/Host/FileSpec.h" + +using namespace lldb_private; + +TEST(FileSpecTest, FileAndDirectoryComponents) +{ + FileSpec fs_posix("/foo/bar", false, FileSpec::ePathSyntaxPosix); + EXPECT_STREQ("/foo/bar", fs_posix.GetCString()); + EXPECT_STREQ("/foo", fs_posix.GetDirectory().GetCString()); + EXPECT_STREQ("bar", fs_posix.GetFilename().GetCString()); + + FileSpec fs_windows("F:\\bar", false, FileSpec::ePathSyntaxWindows); + EXPECT_STREQ("F:\\bar", fs_windows.GetCString()); + // EXPECT_STREQ("F:\\", fs_windows.GetDirectory().GetCString()); // It returns "F:/" + EXPECT_STREQ("bar", fs_windows.GetFilename().GetCString()); + + FileSpec fs_posix_root("/", false, FileSpec::ePathSyntaxPosix); + EXPECT_STREQ("/", fs_posix_root.GetCString()); + EXPECT_EQ(nullptr, fs_posix_root.GetDirectory().GetCString()); + EXPECT_STREQ("/", fs_posix_root.GetFilename().GetCString()); + + FileSpec fs_windows_drive("F:", false, FileSpec::ePathSyntaxWindows); + EXPECT_STREQ("F:", fs_windows_drive.GetCString()); + EXPECT_EQ(nullptr, fs_windows_drive.GetDirectory().GetCString()); + EXPECT_STREQ("F:", fs_windows_drive.GetFilename().GetCString()); + + FileSpec fs_windows_root("F:\\", false, FileSpec::ePathSyntaxWindows); + EXPECT_STREQ("F:\\", fs_windows_root.GetCString()); + EXPECT_STREQ("F:", fs_windows_root.GetDirectory().GetCString()); + // EXPECT_STREQ("\\", fs_windows_root.GetFilename().GetCString()); // It returns "/" + + FileSpec fs_posix_long("/foo/bar/baz", false, FileSpec::ePathSyntaxPosix); + EXPECT_STREQ("/foo/bar/baz", fs_posix_long.GetCString()); + EXPECT_STREQ("/foo/bar", fs_posix_long.GetDirectory().GetCString()); + EXPECT_STREQ("baz", fs_posix_long.GetFilename().GetCString()); + + FileSpec fs_windows_long("F:\\bar\\baz", false, FileSpec::ePathSyntaxWindows); + EXPECT_STREQ("F:\\bar\\baz", fs_windows_long.GetCString()); + // EXPECT_STREQ("F:\\bar", fs_windows_long.GetDirectory().GetCString()); // It returns "F:/bar" + EXPECT_STREQ("baz", fs_windows_long.GetFilename().GetCString()); + + FileSpec fs_posix_trailing_slash("/foo/bar/", false, FileSpec::ePathSyntaxPosix); + EXPECT_STREQ("/foo/bar/.", fs_posix_trailing_slash.GetCString()); + EXPECT_STREQ("/foo/bar", fs_posix_trailing_slash.GetDirectory().GetCString()); + EXPECT_STREQ(".", fs_posix_trailing_slash.GetFilename().GetCString()); + + FileSpec fs_windows_trailing_slash("F:\\bar\\", false, FileSpec::ePathSyntaxWindows); + EXPECT_STREQ("F:\\bar\\.", fs_windows_trailing_slash.GetCString()); + // EXPECT_STREQ("F:\\bar", fs_windows_trailing_slash.GetDirectory().GetCString()); // It returns "F:/bar" + EXPECT_STREQ(".", fs_windows_trailing_slash.GetFilename().GetCString()); +} + +TEST(FileSpecTest, AppendPathComponent) +{ + FileSpec fs_posix("/foo", false, FileSpec::ePathSyntaxPosix); + fs_posix.AppendPathComponent("bar"); + EXPECT_STREQ("/foo/bar", fs_posix.GetCString()); + EXPECT_STREQ("/foo", fs_posix.GetDirectory().GetCString()); + EXPECT_STREQ("bar", fs_posix.GetFilename().GetCString()); + + FileSpec fs_windows("F:\\bar", false, FileSpec::ePathSyntaxWindows); + fs_windows.AppendPathComponent("baz"); + EXPECT_STREQ("F:\\bar\\baz", fs_windows.GetCString()); + // EXPECT_STREQ("F:\\bar", fs_windows.GetDirectory().GetCString()); // It returns "F:/bar" + EXPECT_STREQ("baz", fs_windows.GetFilename().GetCString()); + + FileSpec fs_posix_root("/", false, FileSpec::ePathSyntaxPosix); + fs_posix_root.AppendPathComponent("bar"); + EXPECT_STREQ("/bar", fs_posix_root.GetCString()); + EXPECT_STREQ("/", fs_posix_root.GetDirectory().GetCString()); + EXPECT_STREQ("bar", fs_posix_root.GetFilename().GetCString()); + + FileSpec fs_windows_root("F:\\", false, FileSpec::ePathSyntaxWindows); + fs_windows_root.AppendPathComponent("bar"); + EXPECT_STREQ("F:\\bar", fs_windows_root.GetCString()); + // EXPECT_STREQ("F:\\", fs_windows_root.GetDirectory().GetCString()); // It returns "F:/" + EXPECT_STREQ("bar", fs_windows_root.GetFilename().GetCString()); +} + +TEST(FileSpecTest, CopyByAppendingPathComponent) +{ + FileSpec fs = FileSpec("/foo", false, FileSpec::ePathSyntaxPosix).CopyByAppendingPathComponent("bar"); + EXPECT_STREQ("/foo/bar", fs.GetCString()); + EXPECT_STREQ("/foo", fs.GetDirectory().GetCString()); + EXPECT_STREQ("bar", fs.GetFilename().GetCString()); +} + +TEST(FileSpecTest, Equal) +{ + FileSpec backward("C:\\foo\\bar", false, FileSpec::ePathSyntaxWindows); + FileSpec forward("C:/foo/bar", false, FileSpec::ePathSyntaxWindows); + EXPECT_EQ(forward, backward); + + const bool full_match = true; + const bool remove_backup_dots = true; + EXPECT_TRUE(FileSpec::Equal(forward, backward, full_match, remove_backup_dots)); + EXPECT_TRUE(FileSpec::Equal(forward, backward, full_match, !remove_backup_dots)); + EXPECT_TRUE(FileSpec::Equal(forward, backward, !full_match, remove_backup_dots)); + EXPECT_TRUE(FileSpec::Equal(forward, backward, !full_match, !remove_backup_dots)); +} diff --git a/unittests/Host/SocketAddressTest.cpp b/unittests/Host/SocketAddressTest.cpp index bd6bda13f4498..6b27e04ce7027 100644 --- a/unittests/Host/SocketAddressTest.cpp +++ b/unittests/Host/SocketAddressTest.cpp @@ -33,11 +33,8 @@ TEST_F (SocketAddressTest, Set) ASSERT_EQ (0, sa.GetPort ()); ASSERT_TRUE (sa.SetToLocalhost (AF_INET6, 1139)); -#ifdef _WIN32 - ASSERT_STREQ ("0:0:0:0:0:0:0:1", sa.GetIPAddress ().c_str ()); -#else - ASSERT_STREQ ("::1", sa.GetIPAddress ().c_str ()); -#endif + ASSERT_TRUE(sa.GetIPAddress() == "::1" || sa.GetIPAddress() == "0:0:0:0:0:0:0:1") << "Address was: " + << sa.GetIPAddress(); ASSERT_EQ (1139, sa.GetPort ()); } diff --git a/unittests/Host/SocketTest.cpp b/unittests/Host/SocketTest.cpp index ebb2f319a9c5d..e3e5227447600 100644 --- a/unittests/Host/SocketTest.cpp +++ b/unittests/Host/SocketTest.cpp @@ -116,7 +116,11 @@ TEST_F (SocketTest, DecodeHostAndPort) EXPECT_FALSE (Socket::DecodeHostAndPort ("google.com:-1138", host_str, port_str, port, &error)); EXPECT_TRUE (error.Fail ()); EXPECT_STREQ ("invalid host:port specification: 'google.com:-1138'", error.AsCString ()); - + + EXPECT_FALSE(Socket::DecodeHostAndPort("google.com:65536", host_str, port_str, port, &error)); + EXPECT_TRUE(error.Fail()); + EXPECT_STREQ("invalid host:port specification: 'google.com:65536'", error.AsCString()); + EXPECT_TRUE (Socket::DecodeHostAndPort ("12345", host_str, port_str, port, &error)); EXPECT_STREQ ("", host_str.c_str ()); EXPECT_STREQ ("12345", port_str.c_str ()); @@ -128,6 +132,12 @@ TEST_F (SocketTest, DecodeHostAndPort) EXPECT_STREQ ("0", port_str.c_str ()); EXPECT_EQ (0, port); EXPECT_TRUE (error.Success ()); + + EXPECT_TRUE(Socket::DecodeHostAndPort("*:65535", host_str, port_str, port, &error)); + EXPECT_STREQ("*", host_str.c_str()); + EXPECT_STREQ("65535", port_str.c_str()); + EXPECT_EQ(65535, port); + EXPECT_TRUE(error.Success()); } #ifndef LLDB_DISABLE_POSIX diff --git a/unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp b/unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp index 605f0233e876a..c239a1601b32f 100644 --- a/unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp +++ b/unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp @@ -209,14 +209,13 @@ TEST_F(PythonDataObjectsTest, TestPythonBytes) PyObject *py_bytes = PyBytes_FromString(test_bytes); EXPECT_TRUE(PythonBytes::Check(py_bytes)); PythonBytes python_bytes(PyRefType::Owned, py_bytes); - EXPECT_EQ(PyObjectType::Bytes, python_bytes.GetObjectType()); #if PY_MAJOR_VERSION < 3 EXPECT_TRUE(PythonString::Check(py_bytes)); EXPECT_EQ(PyObjectType::String, python_bytes.GetObjectType()); #else EXPECT_FALSE(PythonString::Check(py_bytes)); - EXPECT_NE(PyObjectType::String, python_bytes.GetObjectType()); + EXPECT_EQ(PyObjectType::Bytes, python_bytes.GetObjectType()); #endif llvm::ArrayRef<uint8_t> bytes = python_bytes.GetBytes(); @@ -224,13 +223,26 @@ TEST_F(PythonDataObjectsTest, TestPythonBytes) EXPECT_EQ(0, ::memcmp(bytes.data(), test_bytes, bytes.size())); } +TEST_F(PythonDataObjectsTest, TestPythonByteArray) +{ + static const char *test_bytes = "PythonDataObjectsTest::TestPythonByteArray"; + llvm::StringRef orig_bytes(test_bytes); + PyObject *py_bytes = PyByteArray_FromStringAndSize(test_bytes, orig_bytes.size()); + EXPECT_TRUE(PythonByteArray::Check(py_bytes)); + PythonByteArray python_bytes(PyRefType::Owned, py_bytes); + EXPECT_EQ(PyObjectType::ByteArray, python_bytes.GetObjectType()); + + llvm::ArrayRef<uint8_t> after_bytes = python_bytes.GetBytes(); + EXPECT_EQ(after_bytes.size(), orig_bytes.size()); + EXPECT_EQ(0, ::memcmp(orig_bytes.data(), test_bytes, orig_bytes.size())); +} + TEST_F(PythonDataObjectsTest, TestPythonString) { // Test that strings behave correctly when wrapped by a PythonString. static const char *test_string = "PythonDataObjectsTest::TestPythonString1"; static const char *test_string2 = "PythonDataObjectsTest::TestPythonString2"; - static const char *test_string3 = "PythonDataObjectsTest::TestPythonString3"; #if PY_MAJOR_VERSION < 3 // Verify that `PythonString` works correctly when given a PyString object. @@ -252,8 +264,8 @@ TEST_F(PythonDataObjectsTest, TestPythonString) // Test that creating a `PythonString` object works correctly with the // string constructor - PythonString constructed_string(test_string3); - EXPECT_STREQ(test_string3, constructed_string.GetString().str().c_str()); + PythonString constructed_string(test_string2); + EXPECT_STREQ(test_string2, constructed_string.GetString().str().c_str()); } TEST_F(PythonDataObjectsTest, TestPythonStringToStr) @@ -275,7 +287,7 @@ TEST_F(PythonDataObjectsTest, TestPythonIntegerToStructuredInteger) { PythonInteger integer(7); auto int_sp = integer.CreateStructuredInteger(); - EXPECT_EQ(7, int_sp->GetValue()); + EXPECT_EQ(7U, int_sp->GetValue()); } TEST_F(PythonDataObjectsTest, TestPythonStringToStructuredString) @@ -290,7 +302,7 @@ TEST_F(PythonDataObjectsTest, TestPythonListValueEquality) { // Test that a list which is built through the native // Python API behaves correctly when wrapped by a PythonList. - static const int list_size = 2; + static const unsigned list_size = 2; static const long long_value0 = 5; static const char *const string_value1 = "String Index 1"; @@ -302,7 +314,7 @@ TEST_F(PythonDataObjectsTest, TestPythonListValueEquality) list_items[0].Reset(PythonInteger(long_value0)); list_items[1].Reset(PythonString(string_value1)); - for (int i = 0; i < list_size; ++i) + for (unsigned i = 0; i < list_size; ++i) list.SetItemAtIndex(i, list_items[i]); EXPECT_EQ(list_size, list.GetSize()); @@ -335,7 +347,7 @@ TEST_F(PythonDataObjectsTest, TestPythonListManipulation) list.AppendItem(integer); list.AppendItem(string); - EXPECT_EQ(2, list.GetSize()); + EXPECT_EQ(2U, list.GetSize()); // Verify that the values match PythonObject chk_value1 = list.GetItemAtIndex(0); @@ -366,17 +378,17 @@ TEST_F(PythonDataObjectsTest, TestPythonListToStructuredList) auto int_sp = array_sp->GetItemAtIndex(0)->GetAsInteger(); auto string_sp = array_sp->GetItemAtIndex(1)->GetAsString(); - EXPECT_EQ(long_value0, int_sp->GetValue()); + EXPECT_EQ(long_value0, long(int_sp->GetValue())); EXPECT_STREQ(string_value1, string_sp->GetValue().c_str()); } TEST_F(PythonDataObjectsTest, TestPythonTupleSize) { PythonTuple tuple(PyInitialValue::Empty); - EXPECT_EQ(0, tuple.GetSize()); + EXPECT_EQ(0U, tuple.GetSize()); tuple = PythonTuple(3); - EXPECT_EQ(3, tuple.GetSize()); + EXPECT_EQ(3U, tuple.GetSize()); } TEST_F(PythonDataObjectsTest, TestPythonTupleValues) @@ -402,7 +414,7 @@ TEST_F(PythonDataObjectsTest, TestPythonTupleInitializerList) PythonString string_value("Test"); PythonObject none_value(PyRefType::Borrowed, Py_None); PythonTuple tuple{ int_value, string_value, none_value }; - EXPECT_EQ(3, tuple.GetSize()); + EXPECT_EQ(3U, tuple.GetSize()); EXPECT_EQ(tuple.GetItemAtIndex(0).get(), int_value.get()); EXPECT_EQ(tuple.GetItemAtIndex(1).get(), string_value.get()); @@ -416,7 +428,7 @@ TEST_F(PythonDataObjectsTest, TestPythonTupleInitializerList2) PythonObject none_value(PyRefType::Borrowed, Py_None); PythonTuple tuple{ int_value.get(), string_value.get(), none_value.get() }; - EXPECT_EQ(3, tuple.GetSize()); + EXPECT_EQ(3U, tuple.GetSize()); EXPECT_EQ(tuple.GetItemAtIndex(0).get(), int_value.get()); EXPECT_EQ(tuple.GetItemAtIndex(1).get(), string_value.get()); @@ -440,7 +452,7 @@ TEST_F(PythonDataObjectsTest, TestPythonDictionaryValueEquality) { // Test that a dictionary which is built through the native // Python API behaves correctly when wrapped by a PythonDictionary. - static const int dict_entries = 2; + static const unsigned dict_entries = 2; const char *key_0 = "Key 0"; int key_1 = 1; const int value_0 = 0; @@ -458,7 +470,7 @@ TEST_F(PythonDataObjectsTest, TestPythonDictionaryValueEquality) EXPECT_TRUE(PythonDictionary::Check(py_dict)); PythonDictionary dict(PyRefType::Owned, py_dict); - for (int i = 0; i < dict_entries; ++i) + for (unsigned i = 0; i < dict_entries; ++i) PyDict_SetItem(py_dict, py_keys[i].get(), py_values[i].get()); EXPECT_EQ(dict.GetSize(), dict_entries); EXPECT_EQ(PyObjectType::Dictionary, dict.GetObjectType()); @@ -480,7 +492,7 @@ TEST_F(PythonDataObjectsTest, TestPythonDictionaryManipulation) { // Test that manipulation of a dictionary behaves correctly when wrapped // by a PythonDictionary. - static const int dict_entries = 2; + static const unsigned dict_entries = 2; const char *const key_0 = "Key 0"; const char *const key_1 = "Key 1"; @@ -527,7 +539,7 @@ TEST_F(PythonDataObjectsTest, TestPythonDictionaryToStructuredDictionary) dict.SetItemForKey(PythonString(string_key1), PythonInteger(int_value1)); auto dict_sp = dict.CreateStructuredDictionary(); - EXPECT_EQ(2, dict_sp->GetSize()); + EXPECT_EQ(2U, dict_sp->GetSize()); EXPECT_TRUE(dict_sp->HasKey(string_key0)); EXPECT_TRUE(dict_sp->HasKey(string_key1)); @@ -536,7 +548,7 @@ TEST_F(PythonDataObjectsTest, TestPythonDictionaryToStructuredDictionary) auto int_sp = dict_sp->GetValueForKey(string_key1)->GetAsInteger(); EXPECT_STREQ(string_value0, string_sp->GetValue().c_str()); - EXPECT_EQ(int_value1, int_sp->GetValue()); + EXPECT_EQ(int_value1, long(int_sp->GetValue())); } TEST_F(PythonDataObjectsTest, TestPythonCallableCheck) @@ -560,7 +572,7 @@ TEST_F(PythonDataObjectsTest, TestPythonCallableInvoke) EXPECT_TRUE(PythonList::Check(result.get())); auto list_result = result.AsType<PythonList>(); - EXPECT_EQ(3, list_result.GetSize()); + EXPECT_EQ(3U, list_result.GetSize()); EXPECT_EQ(one.get(), list_result.GetItemAtIndex(0).get()); EXPECT_EQ(two.get(), list_result.GetItemAtIndex(1).get()); EXPECT_EQ(three.get(), list_result.GetItemAtIndex(2).get()); @@ -582,4 +594,4 @@ TEST_F(PythonDataObjectsTest, TestObjectAttributes) PythonInteger numerator_attr = py_int.GetAttributeValue("numerator").AsType<PythonInteger>(); EXPECT_TRUE(numerator_attr.IsAllocated()); EXPECT_EQ(42, numerator_attr.GetInteger()); -}
\ No newline at end of file +} diff --git a/unittests/ScriptInterpreter/Python/PythonExceptionStateTests.cpp b/unittests/ScriptInterpreter/Python/PythonExceptionStateTests.cpp index a0a6f986cef6c..ddac220c79541 100644 --- a/unittests/ScriptInterpreter/Python/PythonExceptionStateTests.cpp +++ b/unittests/ScriptInterpreter/Python/PythonExceptionStateTests.cpp @@ -171,4 +171,4 @@ TEST_F(PythonExceptionStateTest, TestAutoRestoreChanged) EXPECT_TRUE(PythonExceptionState::HasErrorOccurred()); PyErr_Clear(); -}
\ No newline at end of file +} diff --git a/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp index 3d1727dc1c25a..5eb1c72598a8c 100644 --- a/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp +++ b/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp @@ -24,6 +24,7 @@ PythonTestSuite::SetUp() // ScriptInterpreterPython::Initialize() depends on HostInfo being // initializedso it can compute the python directory etc. ScriptInterpreterPython::Initialize(); + ScriptInterpreterPython::InitializePrivate(); // Although we don't care about concurrency for the purposes of running // this test suite, Python requires the GIL to be locked even for diff --git a/unittests/Symbol/CMakeLists.txt b/unittests/Symbol/CMakeLists.txt new file mode 100644 index 0000000000000..ef41f3fd62a87 --- /dev/null +++ b/unittests/Symbol/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_unittest(SymbolTests + TestClangASTContext.cpp + ) diff --git a/unittests/Symbol/TestClangASTContext.cpp b/unittests/Symbol/TestClangASTContext.cpp new file mode 100644 index 0000000000000..3f166ab9cc724 --- /dev/null +++ b/unittests/Symbol/TestClangASTContext.cpp @@ -0,0 +1,315 @@ +//===-- TestClangASTContext.cpp ---------------------------------------*- C++ -*-===// + +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "lldb/Host/HostInfo.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangUtil.h" +#include "lldb/Symbol/Declaration.h" +#include "lldb/Symbol/GoASTContext.h" + +using namespace clang; +using namespace lldb; +using namespace lldb_private; + +class TestClangASTContext : public testing::Test +{ +public: + static void + SetUpTestCase() + { + HostInfo::Initialize(); + } + + static void + TearDownTestCase() + { + HostInfo::Terminate(); + } + + virtual void + SetUp() override + { + std::string triple = HostInfo::GetTargetTriple(); + m_ast.reset(new ClangASTContext(triple.c_str())); + } + + virtual void + TearDown() override + { + m_ast.reset(); + } + +protected: + std::unique_ptr<ClangASTContext> m_ast; + + QualType + GetBasicQualType(BasicType type) const + { + return ClangUtil::GetQualType(m_ast->GetBasicTypeFromAST(type)); + } + + QualType + GetBasicQualType(const char *name) const + { + return ClangUtil::GetQualType(m_ast->GetBuiltinTypeByName(ConstString(name))); + } +}; + +TEST_F(TestClangASTContext, TestGetBasicTypeFromEnum) +{ + clang::ASTContext *context = m_ast->getASTContext(); + + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeBool), context->BoolTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeChar), context->CharTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeChar16), context->Char16Ty)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeChar32), context->Char32Ty)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeDouble), context->DoubleTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeDoubleComplex), context->DoubleComplexTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeFloat), context->FloatTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeFloatComplex), context->FloatComplexTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeHalf), context->HalfTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeInt), context->IntTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeInt128), context->Int128Ty)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeLong), context->LongTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeLongDouble), context->LongDoubleTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeLongDoubleComplex), context->LongDoubleComplexTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeLongLong), context->LongLongTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeNullPtr), context->NullPtrTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeObjCClass), context->getObjCClassType())); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeObjCID), context->getObjCIdType())); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeObjCSel), context->getObjCSelType())); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeShort), context->ShortTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeSignedChar), context->SignedCharTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeUnsignedChar), context->UnsignedCharTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeUnsignedInt), context->UnsignedIntTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeUnsignedInt128), context->UnsignedInt128Ty)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeUnsignedLong), context->UnsignedLongTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeUnsignedLongLong), context->UnsignedLongLongTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeUnsignedShort), context->UnsignedShortTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeVoid), context->VoidTy)); + EXPECT_TRUE(context->hasSameType(GetBasicQualType(eBasicTypeWChar), context->WCharTy)); +} + +TEST_F(TestClangASTContext, TestGetBasicTypeFromName) +{ + EXPECT_EQ(GetBasicQualType(eBasicTypeChar), GetBasicQualType("char")); + EXPECT_EQ(GetBasicQualType(eBasicTypeSignedChar), GetBasicQualType("signed char")); + EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedChar), GetBasicQualType("unsigned char")); + EXPECT_EQ(GetBasicQualType(eBasicTypeWChar), GetBasicQualType("wchar_t")); + EXPECT_EQ(GetBasicQualType(eBasicTypeSignedWChar), GetBasicQualType("signed wchar_t")); + EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedWChar), GetBasicQualType("unsigned wchar_t")); + EXPECT_EQ(GetBasicQualType(eBasicTypeShort), GetBasicQualType("short")); + EXPECT_EQ(GetBasicQualType(eBasicTypeShort), GetBasicQualType("short int")); + EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedShort), GetBasicQualType("unsigned short")); + EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedShort), GetBasicQualType("unsigned short int")); + EXPECT_EQ(GetBasicQualType(eBasicTypeInt), GetBasicQualType("int")); + EXPECT_EQ(GetBasicQualType(eBasicTypeInt), GetBasicQualType("signed int")); + EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedInt), GetBasicQualType("unsigned int")); + EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedInt), GetBasicQualType("unsigned")); + EXPECT_EQ(GetBasicQualType(eBasicTypeLong), GetBasicQualType("long")); + EXPECT_EQ(GetBasicQualType(eBasicTypeLong), GetBasicQualType("long int")); + EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedLong), GetBasicQualType("unsigned long")); + EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedLong), GetBasicQualType("unsigned long int")); + EXPECT_EQ(GetBasicQualType(eBasicTypeLongLong), GetBasicQualType("long long")); + EXPECT_EQ(GetBasicQualType(eBasicTypeLongLong), GetBasicQualType("long long int")); + EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedLongLong), GetBasicQualType("unsigned long long")); + EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedLongLong), GetBasicQualType("unsigned long long int")); + EXPECT_EQ(GetBasicQualType(eBasicTypeInt128), GetBasicQualType("__int128_t")); + EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedInt128), GetBasicQualType("__uint128_t")); + EXPECT_EQ(GetBasicQualType(eBasicTypeVoid), GetBasicQualType("void")); + EXPECT_EQ(GetBasicQualType(eBasicTypeBool), GetBasicQualType("bool")); + EXPECT_EQ(GetBasicQualType(eBasicTypeFloat), GetBasicQualType("float")); + EXPECT_EQ(GetBasicQualType(eBasicTypeDouble), GetBasicQualType("double")); + EXPECT_EQ(GetBasicQualType(eBasicTypeLongDouble), GetBasicQualType("long double")); + EXPECT_EQ(GetBasicQualType(eBasicTypeObjCID), GetBasicQualType("id")); + EXPECT_EQ(GetBasicQualType(eBasicTypeObjCSel), GetBasicQualType("SEL")); + EXPECT_EQ(GetBasicQualType(eBasicTypeNullPtr), GetBasicQualType("nullptr")); +} + +void +VerifyEncodingAndBitSize(clang::ASTContext *context, lldb::Encoding encoding, int bit_size) +{ + CompilerType type = ClangASTContext::GetBuiltinTypeForEncodingAndBitSize(context, encoding, bit_size); + EXPECT_TRUE(type.IsValid()); + + QualType qtype = ClangUtil::GetQualType(type); + EXPECT_FALSE(qtype.isNull()); + if (qtype.isNull()) + return; + + uint64_t actual_size = context->getTypeSize(qtype); + EXPECT_EQ(bit_size, actual_size); + + const clang::Type *type_ptr = qtype.getTypePtr(); + EXPECT_NE(nullptr, type_ptr); + if (!type_ptr) + return; + + EXPECT_TRUE(type_ptr->isBuiltinType()); + if (encoding == eEncodingSint) + EXPECT_TRUE(type_ptr->isSignedIntegerType()); + else if (encoding == eEncodingUint) + EXPECT_TRUE(type_ptr->isUnsignedIntegerType()); + else if (encoding == eEncodingIEEE754) + EXPECT_TRUE(type_ptr->isFloatingType()); +} + +TEST_F(TestClangASTContext, TestBuiltinTypeForEncodingAndBitSize) +{ + clang::ASTContext *context = m_ast->getASTContext(); + + // Make sure we can get types of every possible size in every possible encoding. + // We can't make any guarantee about which specific type we get, because the standard + // isn't that specific. We only need to make sure the compiler hands us some type that + // is both a builtin type and matches the requested bit size. + VerifyEncodingAndBitSize(context, eEncodingSint, 8); + VerifyEncodingAndBitSize(context, eEncodingSint, 16); + VerifyEncodingAndBitSize(context, eEncodingSint, 32); + VerifyEncodingAndBitSize(context, eEncodingSint, 64); + VerifyEncodingAndBitSize(context, eEncodingSint, 128); + + VerifyEncodingAndBitSize(context, eEncodingUint, 8); + VerifyEncodingAndBitSize(context, eEncodingUint, 16); + VerifyEncodingAndBitSize(context, eEncodingUint, 32); + VerifyEncodingAndBitSize(context, eEncodingUint, 64); + VerifyEncodingAndBitSize(context, eEncodingUint, 128); + + VerifyEncodingAndBitSize(context, eEncodingIEEE754, 32); + VerifyEncodingAndBitSize(context, eEncodingIEEE754, 64); +} + +TEST_F(TestClangASTContext, TestIsClangType) +{ + clang::ASTContext *context = m_ast->getASTContext(); + lldb::opaque_compiler_type_t bool_ctype = ClangASTContext::GetOpaqueCompilerType(context, lldb::eBasicTypeBool); + CompilerType bool_type(m_ast.get(), bool_ctype); + CompilerType record_type = m_ast->CreateRecordType(nullptr, lldb::eAccessPublic, "FooRecord", clang::TTK_Struct, + lldb::eLanguageTypeC_plus_plus, nullptr); + // Clang builtin type and record type should pass + EXPECT_TRUE(ClangUtil::IsClangType(bool_type)); + EXPECT_TRUE(ClangUtil::IsClangType(record_type)); + + // Default constructed type should fail + EXPECT_FALSE(ClangUtil::IsClangType(CompilerType())); + + // Go type should fail + GoASTContext go_ast; + CompilerType go_type(&go_ast, bool_ctype); + EXPECT_FALSE(ClangUtil::IsClangType(go_type)); +} + +TEST_F(TestClangASTContext, TestRemoveFastQualifiers) +{ + CompilerType record_type = m_ast->CreateRecordType(nullptr, lldb::eAccessPublic, "FooRecord", clang::TTK_Struct, + lldb::eLanguageTypeC_plus_plus, nullptr); + QualType qt; + + qt = ClangUtil::GetQualType(record_type); + EXPECT_EQ(0, qt.getLocalFastQualifiers()); + record_type = record_type.AddConstModifier(); + record_type = record_type.AddVolatileModifier(); + record_type = record_type.AddRestrictModifier(); + qt = ClangUtil::GetQualType(record_type); + EXPECT_NE(0, qt.getLocalFastQualifiers()); + record_type = ClangUtil::RemoveFastQualifiers(record_type); + qt = ClangUtil::GetQualType(record_type); + EXPECT_EQ(0, qt.getLocalFastQualifiers()); +} + +TEST_F(TestClangASTContext, TestConvertAccessTypeToAccessSpecifier) +{ + EXPECT_EQ(AS_none, ClangASTContext::ConvertAccessTypeToAccessSpecifier(eAccessNone)); + EXPECT_EQ(AS_none, ClangASTContext::ConvertAccessTypeToAccessSpecifier(eAccessPackage)); + EXPECT_EQ(AS_public, ClangASTContext::ConvertAccessTypeToAccessSpecifier(eAccessPublic)); + EXPECT_EQ(AS_private, ClangASTContext::ConvertAccessTypeToAccessSpecifier(eAccessPrivate)); + EXPECT_EQ(AS_protected, ClangASTContext::ConvertAccessTypeToAccessSpecifier(eAccessProtected)); +} + +TEST_F(TestClangASTContext, TestUnifyAccessSpecifiers) +{ + // Unifying two of the same type should return the same type + EXPECT_EQ(AS_public, ClangASTContext::UnifyAccessSpecifiers(AS_public, AS_public)); + EXPECT_EQ(AS_private, ClangASTContext::UnifyAccessSpecifiers(AS_private, AS_private)); + EXPECT_EQ(AS_protected, ClangASTContext::UnifyAccessSpecifiers(AS_protected, AS_protected)); + + // Otherwise the result should be the strictest of the two. + EXPECT_EQ(AS_private, ClangASTContext::UnifyAccessSpecifiers(AS_private, AS_public)); + EXPECT_EQ(AS_private, ClangASTContext::UnifyAccessSpecifiers(AS_private, AS_protected)); + EXPECT_EQ(AS_private, ClangASTContext::UnifyAccessSpecifiers(AS_public, AS_private)); + EXPECT_EQ(AS_private, ClangASTContext::UnifyAccessSpecifiers(AS_protected, AS_private)); + EXPECT_EQ(AS_protected, ClangASTContext::UnifyAccessSpecifiers(AS_protected, AS_public)); + EXPECT_EQ(AS_protected, ClangASTContext::UnifyAccessSpecifiers(AS_public, AS_protected)); + + // None is stricter than everything (by convention) + EXPECT_EQ(AS_none, ClangASTContext::UnifyAccessSpecifiers(AS_none, AS_public)); + EXPECT_EQ(AS_none, ClangASTContext::UnifyAccessSpecifiers(AS_none, AS_protected)); + EXPECT_EQ(AS_none, ClangASTContext::UnifyAccessSpecifiers(AS_none, AS_private)); + EXPECT_EQ(AS_none, ClangASTContext::UnifyAccessSpecifiers(AS_public, AS_none)); + EXPECT_EQ(AS_none, ClangASTContext::UnifyAccessSpecifiers(AS_protected, AS_none)); + EXPECT_EQ(AS_none, ClangASTContext::UnifyAccessSpecifiers(AS_private, AS_none)); +} + +TEST_F(TestClangASTContext, TestRecordHasFields) +{ + CompilerType int_type = ClangASTContext::GetBasicType(m_ast->getASTContext(), eBasicTypeInt); + + // Test that a record with no fields returns false + CompilerType empty_base = m_ast->CreateRecordType(nullptr, lldb::eAccessPublic, "EmptyBase", clang::TTK_Struct, + lldb::eLanguageTypeC_plus_plus, nullptr); + ClangASTContext::StartTagDeclarationDefinition(empty_base); + ClangASTContext::CompleteTagDeclarationDefinition(empty_base); + + RecordDecl *empty_base_decl = ClangASTContext::GetAsRecordDecl(empty_base); + EXPECT_NE(nullptr, empty_base_decl); + EXPECT_FALSE(ClangASTContext::RecordHasFields(empty_base_decl)); + + // Test that a record with direct fields returns true + CompilerType non_empty_base = m_ast->CreateRecordType(nullptr, lldb::eAccessPublic, "NonEmptyBase", + clang::TTK_Struct, lldb::eLanguageTypeC_plus_plus, nullptr); + ClangASTContext::StartTagDeclarationDefinition(non_empty_base); + FieldDecl *non_empty_base_field_decl = + m_ast->AddFieldToRecordType(non_empty_base, "MyField", int_type, eAccessPublic, 0); + ClangASTContext::CompleteTagDeclarationDefinition(non_empty_base); + RecordDecl *non_empty_base_decl = ClangASTContext::GetAsRecordDecl(non_empty_base); + EXPECT_NE(nullptr, non_empty_base_decl); + EXPECT_NE(nullptr, non_empty_base_field_decl); + EXPECT_TRUE(ClangASTContext::RecordHasFields(non_empty_base_decl)); + + // Test that a record with no direct fields, but fields in a base returns true + CompilerType empty_derived = m_ast->CreateRecordType(nullptr, lldb::eAccessPublic, "EmptyDerived", + clang::TTK_Struct, lldb::eLanguageTypeC_plus_plus, nullptr); + ClangASTContext::StartTagDeclarationDefinition(empty_derived); + CXXBaseSpecifier *non_empty_base_spec = + m_ast->CreateBaseClassSpecifier(non_empty_base.GetOpaqueQualType(), lldb::eAccessPublic, false, false); + bool result = m_ast->SetBaseClassesForClassType(empty_derived.GetOpaqueQualType(), &non_empty_base_spec, 1); + ClangASTContext::CompleteTagDeclarationDefinition(empty_derived); + EXPECT_TRUE(result); + CXXRecordDecl *empty_derived_non_empty_base_cxx_decl = m_ast->GetAsCXXRecordDecl(empty_derived.GetOpaqueQualType()); + RecordDecl *empty_derived_non_empty_base_decl = ClangASTContext::GetAsRecordDecl(empty_derived); + EXPECT_EQ(1, ClangASTContext::GetNumBaseClasses(empty_derived_non_empty_base_cxx_decl, false)); + EXPECT_TRUE(ClangASTContext::RecordHasFields(empty_derived_non_empty_base_decl)); + + // Test that a record with no direct fields, but fields in a virtual base returns true + CompilerType empty_derived2 = m_ast->CreateRecordType(nullptr, lldb::eAccessPublic, "EmptyDerived2", + clang::TTK_Struct, lldb::eLanguageTypeC_plus_plus, nullptr); + ClangASTContext::StartTagDeclarationDefinition(empty_derived2); + CXXBaseSpecifier *non_empty_vbase_spec = + m_ast->CreateBaseClassSpecifier(non_empty_base.GetOpaqueQualType(), lldb::eAccessPublic, true, false); + result = m_ast->SetBaseClassesForClassType(empty_derived2.GetOpaqueQualType(), &non_empty_vbase_spec, 1); + ClangASTContext::CompleteTagDeclarationDefinition(empty_derived2); + EXPECT_TRUE(result); + CXXRecordDecl *empty_derived_non_empty_vbase_cxx_decl = + m_ast->GetAsCXXRecordDecl(empty_derived2.GetOpaqueQualType()); + RecordDecl *empty_derived_non_empty_vbase_decl = ClangASTContext::GetAsRecordDecl(empty_derived2); + EXPECT_EQ(1, ClangASTContext::GetNumBaseClasses(empty_derived_non_empty_vbase_cxx_decl, false)); + EXPECT_TRUE(ClangASTContext::RecordHasFields(empty_derived_non_empty_vbase_decl)); +} diff --git a/unittests/SymbolFile/CMakeLists.txt b/unittests/SymbolFile/CMakeLists.txt new file mode 100644 index 0000000000000..dcf9ebdb11619 --- /dev/null +++ b/unittests/SymbolFile/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(PDB) diff --git a/unittests/SymbolFile/PDB/CMakeLists.txt b/unittests/SymbolFile/PDB/CMakeLists.txt new file mode 100644 index 0000000000000..fcfb5e3062c9d --- /dev/null +++ b/unittests/SymbolFile/PDB/CMakeLists.txt @@ -0,0 +1,12 @@ +add_lldb_unittest(SymbolFilePDBTests + SymbolFilePDBTests.cpp + ) + +set(test_inputs + test-pdb.exe + test-pdb.pdb + test-dwarf.exe + test-pdb-types.exe + test-pdb-types.pdb) + +add_unittest_inputs(SymbolFilePDBTests "${test_inputs}") diff --git a/unittests/SymbolFile/PDB/Inputs/test-dwarf.cpp b/unittests/SymbolFile/PDB/Inputs/test-dwarf.cpp new file mode 100644 index 0000000000000..f86ff3d875b4a --- /dev/null +++ b/unittests/SymbolFile/PDB/Inputs/test-dwarf.cpp @@ -0,0 +1,14 @@ +// Compile with "cl /c /Zi /GR- test.cpp" +// Link with "link test.obj /debug /nodefaultlib /entry:main /out:test.exe" + +int __cdecl _purecall(void) +{ + return 0; +} + +int +main(int argc, char **argv) +{ + + return 0; +} diff --git a/unittests/SymbolFile/PDB/Inputs/test-dwarf.exe b/unittests/SymbolFile/PDB/Inputs/test-dwarf.exe Binary files differnew file mode 100644 index 0000000000000..15e1910b4b8ba --- /dev/null +++ b/unittests/SymbolFile/PDB/Inputs/test-dwarf.exe diff --git a/unittests/SymbolFile/PDB/Inputs/test-pdb-alt.cpp b/unittests/SymbolFile/PDB/Inputs/test-pdb-alt.cpp new file mode 100644 index 0000000000000..d36f15e53fb71 --- /dev/null +++ b/unittests/SymbolFile/PDB/Inputs/test-pdb-alt.cpp @@ -0,0 +1,9 @@ +// Compile with "cl /c /Zi /GR- test-pdb-alt.cpp" +// Link with "link test-pdb.obj test-pdb-alt.obj /debug /nodefaultlib /entry:main /out:test-pdb.exe" + +#include "test-pdb.h" + +int bar(int n) +{ + return n-1; +} diff --git a/unittests/SymbolFile/PDB/Inputs/test-pdb-nested.h b/unittests/SymbolFile/PDB/Inputs/test-pdb-nested.h new file mode 100644 index 0000000000000..fc63b50d13c9d --- /dev/null +++ b/unittests/SymbolFile/PDB/Inputs/test-pdb-nested.h @@ -0,0 +1,9 @@ +#ifndef TEST_PDB_NESTED_H +#define TEST_PDB_NESTED_H + +inline int baz(int n) +{ + return n+1; +} + +#endif
\ No newline at end of file diff --git a/unittests/SymbolFile/PDB/Inputs/test-pdb-types.cpp b/unittests/SymbolFile/PDB/Inputs/test-pdb-types.cpp new file mode 100644 index 0000000000000..5e2fb77bc202d --- /dev/null +++ b/unittests/SymbolFile/PDB/Inputs/test-pdb-types.cpp @@ -0,0 +1,86 @@ +// Compile with "cl /c /Zi /GR- /EHsc test-pdb-types.cpp" +// Link with "link test-pdb-types.obj /debug /nodefaultlib /entry:main /out:test-pdb-types.exe" + +using namespace std; + +// Sizes of builtin types +static const int sizeof_char = sizeof(char); +static const int sizeof_uchar = sizeof(unsigned char); +static const int sizeof_short = sizeof(short); +static const int sizeof_ushort = sizeof(unsigned short); +static const int sizeof_int = sizeof(int); +static const int sizeof_uint = sizeof(unsigned int); +static const int sizeof_long = sizeof(long); +static const int sizeof_ulong = sizeof(unsigned long); +static const int sizeof_longlong = sizeof(long long); +static const int sizeof_ulonglong = sizeof(unsigned long long); +static const int sizeof_int64 = sizeof(__int64); +static const int sizeof_uint64 = sizeof(unsigned __int64); +static const int sizeof_float = sizeof(float); +static const int sizeof_double = sizeof(double); +static const int sizeof_bool = sizeof(bool); +static const int sizeof_wchar = sizeof(wchar_t); + +enum Enum +{ + EValue1 = 1, + EValue2 = 2, +}; + +enum ShortEnum : short +{ + ESValue1 = 1, + ESValue2 = 2 +}; + +namespace NS +{ +class NSClass +{ + float f; + double d; +}; +} + +class Class +{ +public: + class NestedClass + { + Enum e; + }; + ShortEnum se; +}; + +int +test_func(int a, int b) +{ + return a + b; +} + +typedef Class ClassTypedef; +typedef NS::NSClass NSClassTypedef; +int GlobalArray[10]; + +static const int sizeof_NSClass = sizeof(NS::NSClass); +static const int sizeof_Class = sizeof(Class); +static const int sizeof_NestedClass = sizeof(Class::NestedClass); +static const int sizeof_Enum = sizeof(Enum); +static const int sizeof_ShortEnum = sizeof(ShortEnum); +static const int sizeof_ClassTypedef = sizeof(ClassTypedef); +static const int sizeof_NSClassTypedef = sizeof(NSClassTypedef); +static const int sizeof_GlobalArray = sizeof(GlobalArray); + +int +main(int argc, char **argv) +{ + ShortEnum e1; + Enum e2; + Class c1; + Class::NestedClass c2; + NS::NSClass c3; + + ClassTypedef t1; + NSClassTypedef t2; + return test_func(1, 2); +} diff --git a/unittests/SymbolFile/PDB/Inputs/test-pdb-types.exe b/unittests/SymbolFile/PDB/Inputs/test-pdb-types.exe Binary files differnew file mode 100644 index 0000000000000..21a4bd2b4ef53 --- /dev/null +++ b/unittests/SymbolFile/PDB/Inputs/test-pdb-types.exe diff --git a/unittests/SymbolFile/PDB/Inputs/test-pdb-types.pdb b/unittests/SymbolFile/PDB/Inputs/test-pdb-types.pdb Binary files differnew file mode 100644 index 0000000000000..acf241bcb5d6c --- /dev/null +++ b/unittests/SymbolFile/PDB/Inputs/test-pdb-types.pdb diff --git a/unittests/SymbolFile/PDB/Inputs/test-pdb.cpp b/unittests/SymbolFile/PDB/Inputs/test-pdb.cpp new file mode 100644 index 0000000000000..c9bf057cfbfbe --- /dev/null +++ b/unittests/SymbolFile/PDB/Inputs/test-pdb.cpp @@ -0,0 +1,15 @@ +// Compile with "cl /c /Zi /GR- test-pdb.cpp" +// Link with "link test-pdb.obj /debug /nodefaultlib /entry:main /out:test-pdb.exe" + +#include "test-pdb.h" + +int __cdecl _purecall(void) +{ + return 0; +} + +int +main(int argc, char **argv) +{ + return foo(argc) + bar(argc); +} diff --git a/unittests/SymbolFile/PDB/Inputs/test-pdb.exe b/unittests/SymbolFile/PDB/Inputs/test-pdb.exe Binary files differnew file mode 100644 index 0000000000000..3a2c64504ed93 --- /dev/null +++ b/unittests/SymbolFile/PDB/Inputs/test-pdb.exe diff --git a/unittests/SymbolFile/PDB/Inputs/test-pdb.h b/unittests/SymbolFile/PDB/Inputs/test-pdb.h new file mode 100644 index 0000000000000..273343bb03b07 --- /dev/null +++ b/unittests/SymbolFile/PDB/Inputs/test-pdb.h @@ -0,0 +1,13 @@ +#ifndef TEST_PDB_H +#define TEST_PDB_H + +#include "test-pdb-nested.h" + +int bar(int n); + +inline int foo(int n) +{ + return baz(n)+1; +} + +#endif
\ No newline at end of file diff --git a/unittests/SymbolFile/PDB/Inputs/test-pdb.pdb b/unittests/SymbolFile/PDB/Inputs/test-pdb.pdb Binary files differnew file mode 100644 index 0000000000000..f43d334d215a7 --- /dev/null +++ b/unittests/SymbolFile/PDB/Inputs/test-pdb.pdb diff --git a/unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp b/unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp new file mode 100644 index 0000000000000..b303ae7bac4bc --- /dev/null +++ b/unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp @@ -0,0 +1,583 @@ +//===-- PythonDataObjectsTests.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/Config/config.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" + +#include "lldb/Core/Address.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/SymbolVendor.h" + +#include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h" +#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h" +#include "Plugins/SymbolFile/PDB/SymbolFilePDB.h" + +#if defined(_MSC_VER) +#include "lldb/Host/windows/windows.h" +#include <objbase.h> +#endif + +#include <algorithm> + +extern const char *TestMainArgv0; + +using namespace lldb_private; + +class SymbolFilePDBTests : public testing::Test +{ +public: + void + SetUp() override + { +// Initialize and TearDown the plugin every time, so we get a brand new +// AST every time so that modifications to the AST from each test don't +// leak into the next test. +#if defined(_MSC_VER) + ::CoInitializeEx(nullptr, COINIT_MULTITHREADED); +#endif + + HostInfo::Initialize(); + ObjectFilePECOFF::Initialize(); + SymbolFileDWARF::Initialize(); + ClangASTContext::Initialize(); + SymbolFilePDB::Initialize(); + + llvm::StringRef exe_folder = llvm::sys::path::parent_path(TestMainArgv0); + llvm::SmallString<128> inputs_folder = exe_folder; + llvm::sys::path::append(inputs_folder, "Inputs"); + + m_pdb_test_exe = inputs_folder; + m_dwarf_test_exe = inputs_folder; + m_types_test_exe = inputs_folder; + llvm::sys::path::append(m_pdb_test_exe, "test-pdb.exe"); + llvm::sys::path::append(m_dwarf_test_exe, "test-dwarf.exe"); + llvm::sys::path::append(m_types_test_exe, "test-pdb-types.exe"); + } + + void + TearDown() override + { + SymbolFilePDB::Terminate(); + ClangASTContext::Initialize(); + SymbolFileDWARF::Terminate(); + ObjectFilePECOFF::Terminate(); + HostInfo::Terminate(); + +#if defined(_MSC_VER) + ::CoUninitialize(); +#endif + } + +protected: + llvm::SmallString<128> m_pdb_test_exe; + llvm::SmallString<128> m_dwarf_test_exe; + llvm::SmallString<128> m_types_test_exe; + + bool + FileSpecMatchesAsBaseOrFull(const FileSpec &left, const FileSpec &right) const + { + // If the filenames don't match, the paths can't be equal + if (!left.FileEquals(right)) + return false; + // If BOTH have a directory, also compare the directories. + if (left.GetDirectory() && right.GetDirectory()) + return left.DirectoryEquals(right); + + // If one has a directory but not the other, they match. + return true; + } + + void + VerifyLineEntry(lldb::ModuleSP module, const SymbolContext &sc, const FileSpec &spec, LineTable <, uint32_t line, + lldb::addr_t addr) + { + LineEntry entry; + Address address; + EXPECT_TRUE(module->ResolveFileAddress(addr, address)); + + EXPECT_TRUE(lt.FindLineEntryByAddress(address, entry)); + EXPECT_EQ(line, entry.line); + EXPECT_EQ(address, entry.range.GetBaseAddress()); + + EXPECT_TRUE(FileSpecMatchesAsBaseOrFull(spec, entry.file)); + } + + bool + ContainsCompileUnit(const SymbolContextList &sc_list, const FileSpec &spec) const + { + for (size_t i = 0; i < sc_list.GetSize(); ++i) + { + const SymbolContext &sc = sc_list[i]; + if (FileSpecMatchesAsBaseOrFull(*sc.comp_unit, spec)) + return true; + } + return false; + } + + int + GetGlobalConstantInteger(const llvm::pdb::IPDBSession &session, llvm::StringRef var) const + { + auto global = session.getGlobalScope(); + auto results = + global->findChildren(llvm::pdb::PDB_SymType::Data, var, llvm::pdb::PDB_NameSearchFlags::NS_Default); + uint32_t count = results->getChildCount(); + if (count == 0) + return -1; + + auto item = results->getChildAtIndex(0); + auto symbol = llvm::dyn_cast<llvm::pdb::PDBSymbolData>(item.get()); + if (!symbol) + return -1; + llvm::pdb::Variant value = symbol->getValue(); + switch (value.Type) + { + case llvm::pdb::PDB_VariantType::Int16: + return value.Value.Int16; + case llvm::pdb::PDB_VariantType::Int32: + return value.Value.Int32; + case llvm::pdb::PDB_VariantType::UInt16: + return value.Value.UInt16; + case llvm::pdb::PDB_VariantType::UInt32: + return value.Value.UInt32; + default: + return 0; + } + } +}; + +#if defined(HAVE_DIA_SDK) +#define REQUIRES_DIA_SDK(TestName) TestName +#else +#define REQUIRES_DIA_SDK(TestName) DISABLED_##TestName +#endif + +TEST_F(SymbolFilePDBTests, TestAbilitiesForDWARF) +{ + // Test that when we have Dwarf debug info, SymbolFileDWARF is used. + FileSpec fspec(m_dwarf_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + EXPECT_NE(nullptr, plugin); + SymbolFile *symfile = plugin->GetSymbolFile(); + EXPECT_NE(nullptr, symfile); + EXPECT_EQ(symfile->GetPluginName(), SymbolFileDWARF::GetPluginNameStatic()); + + uint32_t expected_abilities = SymbolFile::kAllAbilities; + EXPECT_EQ(expected_abilities, symfile->CalculateAbilities()); +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestAbilitiesForPDB)) +{ + // Test that when we have PDB debug info, SymbolFilePDB is used. + FileSpec fspec(m_pdb_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + EXPECT_NE(nullptr, plugin); + SymbolFile *symfile = plugin->GetSymbolFile(); + EXPECT_NE(nullptr, symfile); + EXPECT_EQ(symfile->GetPluginName(), SymbolFilePDB::GetPluginNameStatic()); + + uint32_t expected_abilities = SymbolFile::CompileUnits | SymbolFile::LineTables; + EXPECT_EQ(expected_abilities, symfile->CalculateAbilities()); +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestResolveSymbolContextBasename)) +{ + // Test that attempting to call ResolveSymbolContext with only a basename finds all full paths + // with the same basename + FileSpec fspec(m_pdb_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + EXPECT_NE(nullptr, plugin); + SymbolFile *symfile = plugin->GetSymbolFile(); + + FileSpec header_spec("test-pdb.cpp", false); + SymbolContextList sc_list; + uint32_t result_count = symfile->ResolveSymbolContext(header_spec, 0, false, lldb::eSymbolContextCompUnit, sc_list); + EXPECT_EQ(1u, result_count); + EXPECT_TRUE(ContainsCompileUnit(sc_list, header_spec)); +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestResolveSymbolContextFullPath)) +{ + // Test that attempting to call ResolveSymbolContext with a full path only finds the one source + // file that matches the full path. + FileSpec fspec(m_pdb_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + EXPECT_NE(nullptr, plugin); + SymbolFile *symfile = plugin->GetSymbolFile(); + + FileSpec header_spec(R"spec(D:\src\llvm\tools\lldb\unittests\SymbolFile\PDB\Inputs\test-pdb.cpp)spec", false); + SymbolContextList sc_list; + uint32_t result_count = symfile->ResolveSymbolContext(header_spec, 0, false, lldb::eSymbolContextCompUnit, sc_list); + EXPECT_GE(1u, result_count); + EXPECT_TRUE(ContainsCompileUnit(sc_list, header_spec)); +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestLookupOfHeaderFileWithInlines)) +{ + // Test that when looking up a header file via ResolveSymbolContext (i.e. a file that was not by itself + // compiled, but only contributes to the combined code of other source files), a SymbolContext is returned + // for each compiland which has line contributions from the requested header. + FileSpec fspec(m_pdb_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + EXPECT_NE(nullptr, plugin); + SymbolFile *symfile = plugin->GetSymbolFile(); + + FileSpec header_specs[] = {FileSpec("test-pdb.h", false), FileSpec("test-pdb-nested.h", false)}; + FileSpec main_cpp_spec("test-pdb.cpp", false); + FileSpec alt_cpp_spec("test-pdb-alt.cpp", false); + for (const auto &hspec : header_specs) + { + SymbolContextList sc_list; + uint32_t result_count = symfile->ResolveSymbolContext(hspec, 0, true, lldb::eSymbolContextCompUnit, sc_list); + EXPECT_EQ(2u, result_count); + EXPECT_TRUE(ContainsCompileUnit(sc_list, main_cpp_spec)); + EXPECT_TRUE(ContainsCompileUnit(sc_list, alt_cpp_spec)); + } +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestLookupOfHeaderFileWithNoInlines)) +{ + // Test that when looking up a header file via ResolveSymbolContext (i.e. a file that was not by itself + // compiled, but only contributes to the combined code of other source files), that if check_inlines + // is false, no SymbolContexts are returned. + FileSpec fspec(m_pdb_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + EXPECT_NE(nullptr, plugin); + SymbolFile *symfile = plugin->GetSymbolFile(); + + FileSpec header_specs[] = {FileSpec("test-pdb.h", false), FileSpec("test-pdb-nested.h", false)}; + for (const auto &hspec : header_specs) + { + SymbolContextList sc_list; + uint32_t result_count = symfile->ResolveSymbolContext(hspec, 0, false, lldb::eSymbolContextCompUnit, sc_list); + EXPECT_EQ(0u, result_count); + } +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestLineTablesMatchAll)) +{ + // Test that when calling ResolveSymbolContext with a line number of 0, all line entries from + // the specified files are returned. + FileSpec fspec(m_pdb_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFile *symfile = plugin->GetSymbolFile(); + + FileSpec source_file("test-pdb.cpp", false); + FileSpec header1("test-pdb.h", false); + FileSpec header2("test-pdb-nested.h", false); + uint32_t cus = symfile->GetNumCompileUnits(); + EXPECT_EQ(2u, cus); + + SymbolContextList sc_list; + uint32_t scope = lldb::eSymbolContextCompUnit | lldb::eSymbolContextLineEntry; + + uint32_t count = symfile->ResolveSymbolContext(source_file, 0, true, scope, sc_list); + EXPECT_EQ(1u, count); + SymbolContext sc; + EXPECT_TRUE(sc_list.GetContextAtIndex(0, sc)); + + LineTable *lt = sc.comp_unit->GetLineTable(); + EXPECT_NE(nullptr, lt); + count = lt->GetSize(); + // We expect one extra entry for termination (per function) + EXPECT_EQ(16u, count); + + VerifyLineEntry(module, sc, source_file, *lt, 7, 0x401040); + VerifyLineEntry(module, sc, source_file, *lt, 8, 0x401043); + VerifyLineEntry(module, sc, source_file, *lt, 9, 0x401045); + + VerifyLineEntry(module, sc, source_file, *lt, 13, 0x401050); + VerifyLineEntry(module, sc, source_file, *lt, 14, 0x401054); + VerifyLineEntry(module, sc, source_file, *lt, 15, 0x401070); + + VerifyLineEntry(module, sc, header1, *lt, 9, 0x401090); + VerifyLineEntry(module, sc, header1, *lt, 10, 0x401093); + VerifyLineEntry(module, sc, header1, *lt, 11, 0x4010a2); + + VerifyLineEntry(module, sc, header2, *lt, 5, 0x401080); + VerifyLineEntry(module, sc, header2, *lt, 6, 0x401083); + VerifyLineEntry(module, sc, header2, *lt, 7, 0x401089); +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestLineTablesMatchSpecific)) +{ + // Test that when calling ResolveSymbolContext with a specific line number, only line entries + // which match the requested line are returned. + FileSpec fspec(m_pdb_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFile *symfile = plugin->GetSymbolFile(); + + FileSpec source_file("test-pdb.cpp", false); + FileSpec header1("test-pdb.h", false); + FileSpec header2("test-pdb-nested.h", false); + uint32_t cus = symfile->GetNumCompileUnits(); + EXPECT_EQ(2u, cus); + + SymbolContextList sc_list; + uint32_t scope = lldb::eSymbolContextCompUnit | lldb::eSymbolContextLineEntry; + + // First test with line 7, and verify that only line 7 entries are added. + uint32_t count = symfile->ResolveSymbolContext(source_file, 7, true, scope, sc_list); + EXPECT_EQ(1u, count); + SymbolContext sc; + EXPECT_TRUE(sc_list.GetContextAtIndex(0, sc)); + + LineTable *lt = sc.comp_unit->GetLineTable(); + EXPECT_NE(nullptr, lt); + count = lt->GetSize(); + // We expect one extra entry for termination + EXPECT_EQ(3u, count); + + VerifyLineEntry(module, sc, source_file, *lt, 7, 0x401040); + VerifyLineEntry(module, sc, header2, *lt, 7, 0x401089); + + sc_list.Clear(); + // Then test with line 9, and verify that only line 9 entries are added. + count = symfile->ResolveSymbolContext(source_file, 9, true, scope, sc_list); + EXPECT_EQ(1u, count); + EXPECT_TRUE(sc_list.GetContextAtIndex(0, sc)); + + lt = sc.comp_unit->GetLineTable(); + EXPECT_NE(nullptr, lt); + count = lt->GetSize(); + // We expect one extra entry for termination + EXPECT_EQ(3u, count); + + VerifyLineEntry(module, sc, source_file, *lt, 9, 0x401045); + VerifyLineEntry(module, sc, header1, *lt, 9, 0x401090); +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestSimpleClassTypes)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + const llvm::pdb::IPDBSession &session = symfile->GetPDBSession(); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + TypeMap results; + EXPECT_EQ(1u, symfile->FindTypes(sc, ConstString("Class"), nullptr, false, 0, searched_files, results)); + EXPECT_EQ(1u, results.GetSize()); + lldb::TypeSP udt_type = results.GetTypeAtIndex(0); + EXPECT_EQ(ConstString("Class"), udt_type->GetName()); + CompilerType compiler_type = udt_type->GetForwardCompilerType(); + EXPECT_TRUE(ClangASTContext::IsClassType(compiler_type.GetOpaqueQualType())); + EXPECT_EQ(uint64_t(GetGlobalConstantInteger(session, "sizeof_Class")), udt_type->GetByteSize()); +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestNestedClassTypes)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + const llvm::pdb::IPDBSession &session = symfile->GetPDBSession(); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + TypeMap results; + EXPECT_EQ(1u, symfile->FindTypes(sc, ConstString("Class::NestedClass"), nullptr, false, 0, searched_files, results)); + EXPECT_EQ(1u, results.GetSize()); + lldb::TypeSP udt_type = results.GetTypeAtIndex(0); + EXPECT_EQ(ConstString("Class::NestedClass"), udt_type->GetName()); + CompilerType compiler_type = udt_type->GetForwardCompilerType(); + EXPECT_TRUE(ClangASTContext::IsClassType(compiler_type.GetOpaqueQualType())); + EXPECT_EQ(uint64_t(GetGlobalConstantInteger(session, "sizeof_NestedClass")), udt_type->GetByteSize()); +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestClassInNamespace)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + const llvm::pdb::IPDBSession &session = symfile->GetPDBSession(); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + TypeMap results; + EXPECT_EQ(1u, symfile->FindTypes(sc, ConstString("NS::NSClass"), nullptr, false, 0, searched_files, results)); + EXPECT_EQ(1u, results.GetSize()); + lldb::TypeSP udt_type = results.GetTypeAtIndex(0); + EXPECT_EQ(ConstString("NS::NSClass"), udt_type->GetName()); + CompilerType compiler_type = udt_type->GetForwardCompilerType(); + EXPECT_TRUE(ClangASTContext::IsClassType(compiler_type.GetOpaqueQualType())); + EXPECT_EQ(GetGlobalConstantInteger(session, "sizeof_NSClass"), udt_type->GetByteSize()); +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestEnumTypes)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + const llvm::pdb::IPDBSession &session = symfile->GetPDBSession(); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + const char *EnumsToCheck[] = {"Enum", "ShortEnum"}; + for (auto Enum : EnumsToCheck) + { + TypeMap results; + EXPECT_EQ(1u, symfile->FindTypes(sc, ConstString(Enum), nullptr, false, 0, searched_files, results)); + EXPECT_EQ(1u, results.GetSize()); + lldb::TypeSP enum_type = results.GetTypeAtIndex(0); + EXPECT_EQ(ConstString(Enum), enum_type->GetName()); + CompilerType compiler_type = enum_type->GetFullCompilerType(); + EXPECT_TRUE(ClangASTContext::IsEnumType(compiler_type.GetOpaqueQualType())); + clang::EnumDecl *enum_decl = ClangASTContext::GetAsEnumDecl(compiler_type); + EXPECT_NE(nullptr, enum_decl); + EXPECT_EQ(2, std::distance(enum_decl->enumerator_begin(), enum_decl->enumerator_end())); + + std::string sizeof_var = "sizeof_"; + sizeof_var.append(Enum); + EXPECT_EQ(GetGlobalConstantInteger(session, sizeof_var.c_str()), enum_type->GetByteSize()); + } +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestArrayTypes)) +{ + // In order to get this test working, we need to support lookup by symbol name. Because array + // types themselves do not have names, only the symbols have names (i.e. the name of the array). +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestFunctionTypes)) +{ + // In order to get this test working, we need to support lookup by symbol name. Because array + // types themselves do not have names, only the symbols have names (i.e. the name of the array). +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestTypedefs)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + const llvm::pdb::IPDBSession &session = symfile->GetPDBSession(); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + TypeMap results; + + const char *TypedefsToCheck[] = {"ClassTypedef", "NSClassTypedef"}; + for (auto Typedef : TypedefsToCheck) + { + TypeMap results; + EXPECT_EQ(1u, symfile->FindTypes(sc, ConstString(Typedef), nullptr, false, 0, searched_files, results)); + EXPECT_EQ(1u, results.GetSize()); + lldb::TypeSP typedef_type = results.GetTypeAtIndex(0); + EXPECT_EQ(ConstString(Typedef), typedef_type->GetName()); + CompilerType compiler_type = typedef_type->GetFullCompilerType(); + ClangASTContext *clang_type_system = llvm::dyn_cast_or_null<ClangASTContext>(compiler_type.GetTypeSystem()); + EXPECT_TRUE(clang_type_system->IsTypedefType(compiler_type.GetOpaqueQualType())); + + std::string sizeof_var = "sizeof_"; + sizeof_var.append(Typedef); + EXPECT_EQ(GetGlobalConstantInteger(session, sizeof_var.c_str()), typedef_type->GetByteSize()); + } +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestRegexNameMatch)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + TypeMap results; + uint32_t num_results = symfile->FindTypes(sc, ConstString(".*"), nullptr, false, 0, searched_files, results); + EXPECT_GT(num_results, 1u); + EXPECT_EQ(num_results, results.GetSize()); +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestMaxMatches)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + TypeMap results; + uint32_t num_results = symfile->FindTypes(sc, ConstString(".*"), nullptr, false, 0, searched_files, results); + // Try to limit ourselves from 1 to 10 results, otherwise we could be doing this thousands of times. + // The idea is just to make sure that for a variety of values, the number of limited results always + // comes out to the number we are expecting. + uint32_t iterations = std::min(num_results, 10u); + for (uint32_t i = 1; i <= iterations; ++i) + { + uint32_t num_limited_results = symfile->FindTypes(sc, ConstString(".*"), nullptr, false, i, searched_files, results); + EXPECT_EQ(i, num_limited_results); + EXPECT_EQ(num_limited_results, results.GetSize()); + } +} + +TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestNullName)) +{ + FileSpec fspec(m_types_test_exe.c_str(), false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec); + + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFilePDB *symfile = static_cast<SymbolFilePDB *>(plugin->GetSymbolFile()); + SymbolContext sc; + llvm::DenseSet<SymbolFile *> searched_files; + TypeMap results; + uint32_t num_results = symfile->FindTypes(sc, ConstString(), nullptr, false, 0, searched_files, results); + EXPECT_EQ(0u, num_results); + EXPECT_EQ(0u, results.GetSize()); +} diff --git a/unittests/Utility/CMakeLists.txt b/unittests/Utility/CMakeLists.txt index 30936acce9dce..99677a47d7cc1 100644 --- a/unittests/Utility/CMakeLists.txt +++ b/unittests/Utility/CMakeLists.txt @@ -1,5 +1,8 @@ add_lldb_unittest(UtilityTests + ModuleCacheTest.cpp StringExtractorTest.cpp TaskPoolTest.cpp UriParserTest.cpp ) + +add_unittest_inputs(UtilityTests TestModule.so) diff --git a/unittests/Utility/Inputs/TestModule.c b/unittests/Utility/Inputs/TestModule.c new file mode 100644 index 0000000000000..12374e1f7c65a --- /dev/null +++ b/unittests/Utility/Inputs/TestModule.c @@ -0,0 +1,10 @@ +// Compile with $CC -nostdlib -shared TestModule.c -o TestModule.so +// The actual contents of the test module is not important here. I am using this because it +// produces an extremely tiny (but still perfectly valid) module. + +void +boom(void) +{ + char *BOOM; + *BOOM = 47; +} diff --git a/unittests/Utility/Inputs/TestModule.so b/unittests/Utility/Inputs/TestModule.so Binary files differnew file mode 100644 index 0000000000000..9e9bf0b6e17ea --- /dev/null +++ b/unittests/Utility/Inputs/TestModule.so diff --git a/unittests/Utility/ModuleCacheTest.cpp b/unittests/Utility/ModuleCacheTest.cpp new file mode 100644 index 0000000000000..53bfc882f2326 --- /dev/null +++ b/unittests/Utility/ModuleCacheTest.cpp @@ -0,0 +1,179 @@ +#include "gtest/gtest.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" + +#include "Plugins/ObjectFile/ELF/ObjectFileELF.h" +#include "Utility/ModuleCache.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Symbol/SymbolContext.h" + +extern const char *TestMainArgv0; + +using namespace lldb_private; +using namespace lldb; + +namespace +{ + +class ModuleCacheTest : public testing::Test +{ +public: + static void + SetUpTestCase(); + + static void + TearDownTestCase(); + +protected: + static FileSpec s_cache_dir; + static llvm::SmallString<128> s_test_executable; + + void + TryGetAndPut(const FileSpec &cache_dir, const char *hostname, bool expect_download); +}; +} + +FileSpec ModuleCacheTest::s_cache_dir; +llvm::SmallString<128> ModuleCacheTest::s_test_executable; + +static const char dummy_hostname[] = "dummy_hostname"; +static const char dummy_remote_dir[] = "bin"; +static const char module_name[] = "TestModule.so"; +static const char module_uuid[] = "F4E7E991-9B61-6AD4-0073-561AC3D9FA10-C043A476"; +static const uint32_t uuid_bytes = 20; +static const size_t module_size = 5602; + +static FileSpec +GetDummyRemotePath() +{ + FileSpec fs("/", false, FileSpec::ePathSyntaxPosix); + fs.AppendPathComponent(dummy_remote_dir); + fs.AppendPathComponent(module_name); + return fs; +} + +static FileSpec +GetUuidView(FileSpec spec) +{ + spec.AppendPathComponent(".cache"); + spec.AppendPathComponent(module_uuid); + spec.AppendPathComponent(module_name); + return spec; +} + +static FileSpec +GetSysrootView(FileSpec spec, const char *hostname) +{ + spec.AppendPathComponent(hostname); + spec.AppendPathComponent(dummy_remote_dir); + spec.AppendPathComponent(module_name); + return spec; +} + +void +ModuleCacheTest::SetUpTestCase() +{ + HostInfo::Initialize(); + ObjectFileELF::Initialize(); + + FileSpec tmpdir_spec; + HostInfo::GetLLDBPath(lldb::ePathTypeLLDBTempSystemDir, s_cache_dir); + + llvm::StringRef exe_folder = llvm::sys::path::parent_path(TestMainArgv0); + s_test_executable = exe_folder; + llvm::sys::path::append(s_test_executable, "Inputs", module_name); +} + +void +ModuleCacheTest::TearDownTestCase() +{ + ObjectFileELF::Terminate(); + HostInfo::Terminate(); +} + +static void +VerifyDiskState(const FileSpec &cache_dir, const char *hostname) +{ + FileSpec uuid_view = GetUuidView(cache_dir); + EXPECT_TRUE(uuid_view.Exists()) << "uuid_view is: " << uuid_view.GetCString(); + EXPECT_EQ(module_size, uuid_view.GetByteSize()); + + FileSpec sysroot_view = GetSysrootView(cache_dir, hostname); + EXPECT_TRUE(sysroot_view.Exists()) << "sysroot_view is: " << sysroot_view.GetCString(); + EXPECT_EQ(module_size, sysroot_view.GetByteSize()); +} + +void +ModuleCacheTest::TryGetAndPut(const FileSpec &cache_dir, const char *hostname, bool expect_download) +{ + ModuleCache mc; + ModuleSpec module_spec; + module_spec.GetFileSpec() = GetDummyRemotePath(); + module_spec.GetUUID().SetFromCString(module_uuid, uuid_bytes); + module_spec.SetObjectSize(module_size); + ModuleSP module_sp; + bool did_create; + bool download_called = false; + + Error error = mc.GetAndPut( + cache_dir, hostname, module_spec, + [this, &download_called](const ModuleSpec &module_spec, const FileSpec &tmp_download_file_spec) { + download_called = true; + EXPECT_STREQ(GetDummyRemotePath().GetCString(), module_spec.GetFileSpec().GetCString()); + std::error_code ec = llvm::sys::fs::copy_file(s_test_executable, tmp_download_file_spec.GetCString()); + EXPECT_FALSE(ec); + return Error(); + }, + [](const ModuleSP &module_sp, const FileSpec &tmp_download_file_spec) { return Error("Not supported."); }, + module_sp, &did_create); + EXPECT_EQ(expect_download, download_called); + + EXPECT_TRUE(error.Success()) << "Error was: " << error.AsCString(); + EXPECT_TRUE(did_create); + ASSERT_TRUE(bool(module_sp)); + + SymbolContextList sc_list; + EXPECT_EQ(1u, module_sp->FindFunctionSymbols(ConstString("boom"), eFunctionNameTypeFull, sc_list)); + EXPECT_STREQ(GetDummyRemotePath().GetCString(), module_sp->GetPlatformFileSpec().GetCString()); + EXPECT_STREQ(module_uuid, module_sp->GetUUID().GetAsString().c_str()); +} + +TEST_F(ModuleCacheTest, GetAndPut) +{ + FileSpec test_cache_dir = s_cache_dir; + test_cache_dir.AppendPathComponent("GetAndPut"); + + const bool expect_download = true; + TryGetAndPut(test_cache_dir, dummy_hostname, expect_download); + VerifyDiskState(test_cache_dir, dummy_hostname); +} + +TEST_F(ModuleCacheTest, GetAndPutUuidExists) +{ + FileSpec test_cache_dir = s_cache_dir; + test_cache_dir.AppendPathComponent("GetAndPutUuidExists"); + + FileSpec uuid_view = GetUuidView(test_cache_dir); + std::error_code ec = llvm::sys::fs::create_directories(uuid_view.GetDirectory().GetCString()); + ASSERT_FALSE(ec); + ec = llvm::sys::fs::copy_file(s_test_executable, uuid_view.GetCString()); + ASSERT_FALSE(ec); + + const bool expect_download = false; + TryGetAndPut(test_cache_dir, dummy_hostname, expect_download); + VerifyDiskState(test_cache_dir, dummy_hostname); +} + +TEST_F(ModuleCacheTest, GetAndPutStrangeHostname) +{ + FileSpec test_cache_dir = s_cache_dir; + test_cache_dir.AppendPathComponent("GetAndPutStrangeHostname"); + + const bool expect_download = true; + TryGetAndPut(test_cache_dir, "tab\tcolon:asterisk*", expect_download); + VerifyDiskState(test_cache_dir, "tab_colon_asterisk_"); +} |