summaryrefslogtreecommitdiff
path: root/unittests
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2016-01-06 20:12:03 +0000
committerDimitry Andric <dim@FreeBSD.org>2016-01-06 20:12:03 +0000
commit9e6d35490a6542f9c97607f93c2ef8ca8e03cbcc (patch)
treedd2a1ddf0476664c2b823409c36cbccd52662ca7 /unittests
parent3bd2e91faeb9eeec1aae82c64a3253afff551cfd (diff)
Notes
Diffstat (limited to 'unittests')
-rw-r--r--unittests/CMakeLists.txt31
-rw-r--r--unittests/Editline/CMakeLists.txt3
-rw-r--r--unittests/Editline/EditlineTest.cpp368
-rw-r--r--unittests/Expression/CMakeLists.txt3
-rw-r--r--unittests/Expression/GoParserTest.cpp250
-rw-r--r--unittests/Host/CMakeLists.txt5
-rw-r--r--unittests/Host/SocketAddressTest.cpp77
-rw-r--r--unittests/Host/SocketTest.cpp201
-rw-r--r--unittests/Host/SymbolsTest.cpp30
-rw-r--r--unittests/Interpreter/CMakeLists.txt7
-rw-r--r--unittests/Interpreter/TestArgs.cpp70
-rw-r--r--unittests/ScriptInterpreter/CMakeLists.txt3
-rw-r--r--unittests/ScriptInterpreter/Python/CMakeLists.txt8
-rw-r--r--unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp564
-rw-r--r--unittests/ScriptInterpreter/Python/PythonExceptionStateTests.cpp174
-rw-r--r--unittests/ScriptInterpreter/Python/PythonTestSuite.cpp42
-rw-r--r--unittests/ScriptInterpreter/Python/PythonTestSuite.h26
-rw-r--r--unittests/Utility/CMakeLists.txt5
-rw-r--r--unittests/Utility/StringExtractorTest.cpp406
-rw-r--r--unittests/Utility/TaskPoolTest.cpp62
-rw-r--r--unittests/Utility/UriParserTest.cpp159
-rw-r--r--unittests/gtest_common.h24
22 files changed, 2518 insertions, 0 deletions
diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt
new file mode 100644
index 0000000000000..94031accfec77
--- /dev/null
+++ b/unittests/CMakeLists.txt
@@ -0,0 +1,31 @@
+add_custom_target(LLDBUnitTests)
+set_target_properties(LLDBUnitTests PROPERTIES FOLDER "LLDB tests")
+
+include_directories(${LLDB_SOURCE_ROOT})
+
+set(LLDB_GTEST_COMMON_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/gtest_common.h)
+if (MSVC)
+ list(APPEND LLVM_COMPILE_FLAGS /FI ${LLDB_GTEST_COMMON_INCLUDE})
+else ()
+ list(APPEND LLVM_COMPILE_FLAGS -include ${LLDB_GTEST_COMMON_INCLUDE})
+endif ()
+
+include(${LLDB_PROJECT_ROOT}/cmake/LLDBDependencies.cmake)
+
+function(add_lldb_unittest test_name)
+ add_unittest(LLDBUnitTests
+ ${test_name}
+ ${ARGN}
+ )
+
+ 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()
+
+add_subdirectory(Editline)
+add_subdirectory(Expression)
+add_subdirectory(Host)
+add_subdirectory(Interpreter)
+add_subdirectory(ScriptInterpreter)
+add_subdirectory(Utility)
diff --git a/unittests/Editline/CMakeLists.txt b/unittests/Editline/CMakeLists.txt
new file mode 100644
index 0000000000000..eaa21c490d98a
--- /dev/null
+++ b/unittests/Editline/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_lldb_unittest(EditlineTests
+ EditlineTest.cpp
+ )
diff --git a/unittests/Editline/EditlineTest.cpp b/unittests/Editline/EditlineTest.cpp
new file mode 100644
index 0000000000000..d82ef4c17c773
--- /dev/null
+++ b/unittests/Editline/EditlineTest.cpp
@@ -0,0 +1,368 @@
+//===-- EditlineTest.cpp -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_DISABLE_LIBEDIT
+
+#define EDITLINE_TEST_DUMP_OUTPUT 0
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include <memory>
+#include <thread>
+
+#include "gtest/gtest.h"
+
+#include "lldb/Core/Error.h"
+#include "lldb/Core/StringList.h"
+#include "lldb/Host/Editline.h"
+#include "lldb/Host/Pipe.h"
+#include "lldb/Utility/PseudoTerminal.h"
+
+namespace
+{
+ const size_t TIMEOUT_MILLIS = 5000;
+}
+
+class FilePointer
+{
+public:
+
+ FilePointer () = delete;
+
+ FilePointer (const FilePointer&) = delete;
+
+ FilePointer (FILE *file_p)
+ : _file_p (file_p)
+ {
+ }
+
+ ~FilePointer ()
+ {
+ if (_file_p != nullptr)
+ {
+ const int close_result = fclose (_file_p);
+ EXPECT_EQ(0, close_result);
+ }
+ }
+
+ operator FILE* ()
+ {
+ return _file_p;
+ }
+
+private:
+
+ FILE *_file_p;
+
+};
+
+/**
+ Wraps an Editline class, providing a simple way to feed
+ input (as if from the keyboard) and receive output from Editline.
+ */
+class EditlineAdapter
+{
+public:
+
+ EditlineAdapter ();
+
+ void
+ CloseInput ();
+
+ bool
+ IsValid () const
+ {
+ return _editline_sp.get () != nullptr;
+ }
+
+ lldb_private::Editline&
+ GetEditline ()
+ {
+ return *_editline_sp;
+ }
+
+ bool
+ SendLine (const std::string &line);
+
+ bool
+ SendLines (const std::vector<std::string> &lines);
+
+ bool
+ GetLine (std::string &line, bool &interrupted, size_t timeout_millis);
+
+ bool
+ GetLines (lldb_private::StringList &lines, bool &interrupted, size_t timeout_millis);
+
+ void
+ ConsumeAllOutput ();
+
+private:
+
+ static bool
+ IsInputComplete (
+ lldb_private::Editline * editline,
+ lldb_private::StringList & lines,
+ void * baton);
+
+ std::unique_ptr<lldb_private::Editline> _editline_sp;
+
+ lldb_utility::PseudoTerminal _pty;
+ int _pty_master_fd;
+ int _pty_slave_fd;
+
+ std::unique_ptr<FilePointer> _el_slave_file;
+};
+
+EditlineAdapter::EditlineAdapter () :
+ _editline_sp (),
+ _pty (),
+ _pty_master_fd (-1),
+ _pty_slave_fd (-1),
+ _el_slave_file ()
+{
+ lldb_private::Error error;
+
+ // Open the first master pty available.
+ char error_string[256];
+ error_string[0] = '\0';
+ if (!_pty.OpenFirstAvailableMaster (O_RDWR, error_string, sizeof (error_string)))
+ {
+ fprintf(stderr, "failed to open first available master pty: '%s'\n", error_string);
+ return;
+ }
+
+ // Grab the master fd. This is a file descriptor we will:
+ // (1) write to when we want to send input to editline.
+ // (2) read from when we want to see what editline sends back.
+ _pty_master_fd = _pty.GetMasterFileDescriptor();
+
+ // Open the corresponding slave pty.
+ if (!_pty.OpenSlave (O_RDWR, error_string, sizeof (error_string)))
+ {
+ fprintf(stderr, "failed to open slave pty: '%s'\n", error_string);
+ return;
+ }
+ _pty_slave_fd = _pty.GetSlaveFileDescriptor();
+
+ _el_slave_file.reset (new FilePointer (fdopen (_pty_slave_fd, "rw")));
+ EXPECT_FALSE (nullptr == *_el_slave_file);
+ if (*_el_slave_file == nullptr)
+ return;
+
+ // Create an Editline instance.
+ _editline_sp.reset (new lldb_private::Editline("gtest editor", *_el_slave_file, *_el_slave_file, *_el_slave_file, false));
+ _editline_sp->SetPrompt ("> ");
+
+ // Hookup our input complete callback.
+ _editline_sp->SetIsInputCompleteCallback(IsInputComplete, this);
+}
+
+void
+EditlineAdapter::CloseInput ()
+{
+ if (_el_slave_file != nullptr)
+ _el_slave_file.reset (nullptr);
+}
+
+bool
+EditlineAdapter::SendLine (const std::string &line)
+{
+ // Ensure we're valid before proceeding.
+ if (!IsValid ())
+ return false;
+
+ // Write the line out to the pipe connected to editline's input.
+ ssize_t input_bytes_written =
+ ::write (_pty_master_fd,
+ line.c_str(),
+ line.length() * sizeof (std::string::value_type));
+
+ const char *eoln = "\n";
+ const size_t eoln_length = strlen(eoln);
+ input_bytes_written =
+ ::write (_pty_master_fd,
+ eoln,
+ eoln_length * sizeof (char));
+
+ EXPECT_EQ (eoln_length * sizeof (char), input_bytes_written);
+ return eoln_length * sizeof (char) == input_bytes_written;
+}
+
+bool
+EditlineAdapter::SendLines (const std::vector<std::string> &lines)
+{
+ for (auto &line : lines)
+ {
+#if EDITLINE_TEST_DUMP_OUTPUT
+ printf ("<stdin> sending line \"%s\"\n", line.c_str());
+#endif
+ if (!SendLine (line))
+ return false;
+ }
+ return true;
+}
+
+// We ignore the timeout for now.
+bool
+EditlineAdapter::GetLine (std::string &line, bool &interrupted, size_t /* timeout_millis */)
+{
+ // Ensure we're valid before proceeding.
+ if (!IsValid ())
+ return false;
+
+ _editline_sp->GetLine (line, interrupted);
+ return true;
+}
+
+bool
+EditlineAdapter::GetLines (lldb_private::StringList &lines, bool &interrupted, size_t /* timeout_millis */)
+{
+ // Ensure we're valid before proceeding.
+ if (!IsValid ())
+ return false;
+
+ _editline_sp->GetLines (1, lines, interrupted);
+ return true;
+}
+
+bool
+EditlineAdapter::IsInputComplete (
+ lldb_private::Editline * editline,
+ lldb_private::StringList & lines,
+ void * baton)
+{
+ // We'll call ourselves complete if we've received a balanced set of braces.
+ int start_block_count = 0;
+ int brace_balance = 0;
+
+ for (size_t i = 0; i < lines.GetSize (); ++i)
+ {
+ for (auto ch : lines[i])
+ {
+ if (ch == '{')
+ {
+ ++start_block_count;
+ ++brace_balance;
+ }
+ else if (ch == '}')
+ --brace_balance;
+ }
+ }
+
+ return (start_block_count > 0) && (brace_balance == 0);
+}
+
+void
+EditlineAdapter::ConsumeAllOutput ()
+{
+ FilePointer output_file (fdopen (_pty_master_fd, "r"));
+
+ int ch;
+ while ((ch = fgetc(output_file)) != EOF)
+ {
+#if EDITLINE_TEST_DUMP_OUTPUT
+ char display_str[] = { 0, 0, 0 };
+ switch (ch)
+ {
+ case '\t':
+ display_str[0] = '\\';
+ display_str[1] = 't';
+ break;
+ case '\n':
+ display_str[0] = '\\';
+ display_str[1] = 'n';
+ break;
+ case '\r':
+ display_str[0] = '\\';
+ display_str[1] = 'r';
+ break;
+ default:
+ display_str[0] = ch;
+ break;
+ }
+ printf ("<stdout> 0x%02x (%03d) (%s)\n", ch, ch, display_str);
+ // putc(ch, stdout);
+#endif
+ }
+}
+
+TEST (EditlineTest, EditlineReceivesSingleLineText)
+{
+ setenv ("TERM", "vt100", 1);
+
+ // Create an editline.
+ EditlineAdapter el_adapter;
+ EXPECT_TRUE (el_adapter.IsValid ());
+ if (!el_adapter.IsValid ())
+ return;
+
+ // Dump output.
+ std::thread el_output_thread( [&] { el_adapter.ConsumeAllOutput (); });
+
+ // Send it some text via our virtual keyboard.
+ const std::string input_text ("Hello, world");
+ EXPECT_TRUE (el_adapter.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);
+
+ 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)
+{
+ 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()");
+ input_lines.push_back ("{");
+ input_lines.push_back ("printf(\"Hello, world\");");
+ input_lines.push_back ("}");
+ input_lines.push_back ("");
+
+ EXPECT_TRUE (el_adapter.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_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)
+ EXPECT_EQ (input_lines[i], el_reported_lines[i]);
+ }
+
+ el_adapter.CloseInput();
+ el_output_thread.join();
+}
+
+#endif
diff --git a/unittests/Expression/CMakeLists.txt b/unittests/Expression/CMakeLists.txt
new file mode 100644
index 0000000000000..04bad141170b4
--- /dev/null
+++ b/unittests/Expression/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_lldb_unittest(ExpressionTests
+ GoParserTest.cpp
+ )
diff --git a/unittests/Expression/GoParserTest.cpp b/unittests/Expression/GoParserTest.cpp
new file mode 100644
index 0000000000000..76483979f3536
--- /dev/null
+++ b/unittests/Expression/GoParserTest.cpp
@@ -0,0 +1,250 @@
+//===-- GoParserTest.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 <sstream>
+
+#include "gtest/gtest.h"
+
+#include "lldb/Core/Error.h"
+#include "Plugins/ExpressionParser/Go/GoParser.h"
+
+using namespace lldb_private;
+
+namespace
+{
+struct ASTPrinter
+{
+ ASTPrinter(GoASTNode *n) { (*this)(n); }
+
+ void
+ operator()(GoASTNode *n)
+ {
+ if (n == nullptr)
+ {
+ m_stream << "nil ";
+ return;
+ }
+ m_stream << "(" << n->GetKindName() << " ";
+ n->WalkChildren(*this);
+ if (auto *nn = llvm::dyn_cast<GoASTAssignStmt>(n))
+ m_stream << nn->GetDefine() << " ";
+ if (auto *nn = llvm::dyn_cast<GoASTBasicLit>(n))
+ m_stream << nn->GetValue().m_value.str() << " ";
+ if (auto *nn = llvm::dyn_cast<GoASTBinaryExpr>(n))
+ m_stream << GoLexer::LookupToken(nn->GetOp()).str() << " ";
+ if (auto *nn = llvm::dyn_cast<GoASTIdent>(n))
+ m_stream << nn->GetName().m_value.str() << " ";
+ if (auto *nn = llvm::dyn_cast<GoASTBranchStmt>(n))
+ m_stream << GoLexer::LookupToken(nn->GetTok()).str() << " ";
+ if (auto *nn = llvm::dyn_cast<GoASTCallExpr>(n))
+ m_stream << (nn->GetEllipsis() ? "..." : "") << " ";
+ if (auto *nn = llvm::dyn_cast<GoASTChanType>(n))
+ m_stream << nn->GetDir() << " ";
+ if (auto *nn = llvm::dyn_cast<GoASTGenDecl>(n))
+ m_stream << GoLexer::LookupToken(nn->GetTok()).str() << " ";
+ if (auto *nn = llvm::dyn_cast<GoASTIncDecStmt>(n))
+ m_stream << GoLexer::LookupToken(nn->GetTok()).str() << " ";
+ if (auto *nn = llvm::dyn_cast<GoASTRangeStmt>(n))
+ m_stream << nn->GetDefine() << " ";
+ if (auto *nn = llvm::dyn_cast<GoASTSliceExpr>(n))
+ m_stream << nn->GetSlice3() << " ";
+ if (auto *nn = llvm::dyn_cast<GoASTUnaryExpr>(n))
+ m_stream << GoLexer::LookupToken(nn->GetOp()).str() << " ";
+ m_stream << ") ";
+ }
+
+ const std::string
+ str() const
+ {
+ return m_stream.str();
+ }
+ std::stringstream m_stream;
+};
+
+testing::AssertionResult
+CheckStatement(const char *_s, const char *c_expr, const char *sexpr, const char *code)
+{
+ GoParser parser(code);
+ std::unique_ptr<GoASTStmt> stmt(parser.Statement());
+ if (parser.Failed() || !stmt)
+ {
+ Error err;
+ parser.GetError(err);
+ return testing::AssertionFailure() << "Error parsing " << c_expr << "\n\t" << err.AsCString();
+ }
+ std::string actual_sexpr = ASTPrinter(stmt.get()).str();
+ if (actual_sexpr == sexpr)
+ return testing::AssertionSuccess();
+ return testing::AssertionFailure() << "Parsing: " << c_expr << "\nExpected: " << sexpr
+ << "\nGot: " << actual_sexpr;
+}
+} // namespace
+
+#define EXPECT_PARSE(s, c) EXPECT_PRED_FORMAT2(CheckStatement, s, c)
+
+TEST(GoParserTest, ParseBasicLiterals)
+{
+ EXPECT_PARSE("(ExprStmt (BasicLit 0 ) ) ", "0");
+ EXPECT_PARSE("(ExprStmt (BasicLit 42 ) ) ", "42");
+ EXPECT_PARSE("(ExprStmt (BasicLit 0600 ) ) ", "0600");
+ EXPECT_PARSE("(ExprStmt (BasicLit 0xBadFace ) ) ", "0xBadFace");
+ EXPECT_PARSE("(ExprStmt (BasicLit 170141183460469231731687303715884105727 ) ) ",
+ "170141183460469231731687303715884105727");
+
+ EXPECT_PARSE("(ExprStmt (BasicLit 0. ) ) ", "0.");
+ EXPECT_PARSE("(ExprStmt (BasicLit 72.40 ) ) ", "72.40");
+ EXPECT_PARSE("(ExprStmt (BasicLit 072.40 ) ) ", "072.40");
+ EXPECT_PARSE("(ExprStmt (BasicLit 2.71828 ) ) ", "2.71828");
+ EXPECT_PARSE("(ExprStmt (BasicLit 1.e+0 ) ) ", "1.e+0");
+ EXPECT_PARSE("(ExprStmt (BasicLit 6.67428e-11 ) ) ", "6.67428e-11");
+ EXPECT_PARSE("(ExprStmt (BasicLit 1E6 ) ) ", "1E6");
+ EXPECT_PARSE("(ExprStmt (BasicLit .12345E+6 ) ) ", ".12345E+6");
+
+ EXPECT_PARSE("(ExprStmt (BasicLit 0i ) ) ", "0i");
+ EXPECT_PARSE("(ExprStmt (BasicLit 011i ) ) ", "011i");
+ EXPECT_PARSE("(ExprStmt (BasicLit 0.i ) ) ", "0.i");
+ EXPECT_PARSE("(ExprStmt (BasicLit 2.71828i ) ) ", "2.71828i");
+ EXPECT_PARSE("(ExprStmt (BasicLit 6.67428e-11i ) ) ", "6.67428e-11i");
+ EXPECT_PARSE("(ExprStmt (BasicLit 1E6i ) ) ", "1E6i");
+ EXPECT_PARSE("(ExprStmt (BasicLit .12345E+6i ) ) ", ".12345E+6i");
+
+ EXPECT_PARSE("(ExprStmt (BasicLit 'a' ) ) ", "'a'");
+ EXPECT_PARSE("(ExprStmt (BasicLit '本' ) ) ", "'本'");
+ EXPECT_PARSE("(ExprStmt (BasicLit \"abc\" ) ) ", "\"abc\"");
+ EXPECT_PARSE("(ExprStmt (BasicLit `abc` ) ) ", "`abc`");
+ EXPECT_PARSE("(ExprStmt (BasicLit `ab\nc` ) ) ", "`ab\nc`");
+}
+
+TEST(GoParserTest, ParseOperand)
+{
+ EXPECT_PARSE("(ExprStmt (Ident a ) ) ", "a");
+ EXPECT_PARSE("(ExprStmt (Ident _x9 ) ) ", "_x9");
+ EXPECT_PARSE("(ExprStmt (Ident ThisVariableIsExported ) ) ", "ThisVariableIsExported");
+ EXPECT_PARSE("(ExprStmt (Ident αβ ) ) ", "αβ");
+
+ EXPECT_PARSE("(ExprStmt (SelectorExpr (Ident math ) (Ident Sin ) ) ) ", "math.Sin");
+}
+
+TEST(GoParserTest, ParseCompositeLiterals)
+{
+ EXPECT_PARSE("(ExprStmt (CompositeLit (Ident Point3D ) ) ) ", "Point3D{}");
+ EXPECT_PARSE("(ExprStmt (CompositeLit (Ident Line ) (Ident origin ) (CompositeLit (Ident Point3D ) (KeyValueExpr "
+ "(Ident y ) (UnaryExpr (BasicLit 4 ) - ) ) (KeyValueExpr (Ident z ) (BasicLit 12.3 ) ) ) ) ) ",
+ "Line{origin, Point3D{y: -4, z: 12.3}}");
+ EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 10 ) (Ident string ) ) ) ) ", "[10]string{}");
+ EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 6 ) (Ident int ) ) (BasicLit 1 ) (BasicLit 2 ) "
+ "(BasicLit 3 ) (BasicLit 5 ) ) ) ",
+ "[6]int {1, 2, 3, 5}");
+ EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType nil (Ident int ) ) (BasicLit 2 ) (BasicLit 3 ) (BasicLit 5 ) "
+ "(BasicLit 7 ) (BasicLit 9 ) (BasicLit 2147483647 ) ) ) ",
+ "[]int{2, 3, 5, 7, 9, 2147483647}");
+ EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 128 ) (Ident bool ) ) (KeyValueExpr (BasicLit 'a' ) "
+ "(Ident true ) ) (KeyValueExpr (BasicLit 'e' ) (Ident true ) ) (KeyValueExpr (BasicLit 'i' ) (Ident "
+ "true ) ) (KeyValueExpr (BasicLit 'o' ) (Ident true ) ) (KeyValueExpr (BasicLit 'u' ) (Ident true ) ) "
+ "(KeyValueExpr (BasicLit 'y' ) (Ident true ) ) ) ) ",
+ "[128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true}");
+ EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (BasicLit 10 ) (Ident float32 ) ) (UnaryExpr (BasicLit 1 ) - ) "
+ "(KeyValueExpr (BasicLit 4 ) (UnaryExpr (BasicLit 0.1 ) - ) ) (UnaryExpr (BasicLit 0.1 ) - ) "
+ "(KeyValueExpr (BasicLit 9 ) (UnaryExpr (BasicLit 1 ) - ) ) ) ) ",
+ "[10]float32{-1, 4: -0.1, -0.1, 9: -1}");
+}
+
+TEST(GoParserTest, ParseEllipsisArray)
+{
+ EXPECT_PARSE(
+ "(ExprStmt (CompositeLit (ArrayType (Ellipsis nil ) (Ident string ) ) (BasicLit `Sat` ) (BasicLit `Sun` ) ) ) ",
+ "[...]string {`Sat`, `Sun`}");
+ EXPECT_PARSE("(ExprStmt (CompositeLit (ArrayType (Ellipsis nil ) (Ident Point ) ) (CompositeLit nil (BasicLit 1.5 "
+ ") (UnaryExpr (BasicLit 3.5 ) - ) ) (CompositeLit nil (BasicLit 0 ) (BasicLit 0 ) ) ) ) ",
+ "[...]Point{{1.5, -3.5}, {0, 0}}");
+}
+
+TEST(GoParserTest, ParseMap)
+{
+ EXPECT_PARSE("(ExprStmt (CompositeLit (MapType (Ident string ) (Ident float32 ) ) (KeyValueExpr (BasicLit `C0` ) "
+ "(BasicLit 16.35 ) ) (KeyValueExpr (BasicLit `D0` ) (BasicLit 18.35 ) ) ) ) ",
+ "map[string]float32{`C0`: 16.35, `D0`: 18.35, }");
+}
+
+TEST(GoParserTest, UnaryExpr)
+{
+ EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) + ) ) ", "+x");
+ EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) - ) ) ", "-x");
+ EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) ! ) ) ", "!x");
+ EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) ^ ) ) ", "^x");
+ EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) & ) ) ", "&x");
+ EXPECT_PARSE("(ExprStmt (UnaryExpr (Ident x ) <- ) ) ", "<-x");
+ EXPECT_PARSE("(ExprStmt (StarExpr (Ident x ) ) ) ", "*x");
+}
+
+TEST(GoParserTest, BinaryExpr)
+{
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) || ) ) ", "a || b");
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) && ) ) ", "a && b");
+
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) == ) ) ", "a == b");
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) != ) ) ", "a != b");
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) < ) ) ", "a < b");
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) <= ) ) ", "a <= b");
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) > ) ) ", "a > b");
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) >= ) ) ", "a >= b");
+
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) + ) ) ", "a + b");
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) - ) ) ", "a - b");
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) | ) ) ", "a | b");
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) ^ ) ) ", "a ^ b");
+
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) * ) ) ", "a * b");
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) / ) ) ", "a / b");
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) % ) ) ", "a % b");
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) << ) ) ", "a << b");
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) >> ) ) ", "a >> b");
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) & ) ) ", "a & b");
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (Ident b ) &^ ) ) ", "a &^ b");
+
+ EXPECT_PARSE(
+ "(ExprStmt (BinaryExpr (BasicLit 23 ) (BinaryExpr (BasicLit 3 ) (IndexExpr (Ident x ) (Ident i ) ) * ) + ) ) ",
+ "23 + 3*x[i]");
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident a ) (UnaryExpr (UnaryExpr (Ident a ) + ) + ) + ) ) ", "a + + + a");
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (UnaryExpr (Ident a ) ^ ) (Ident b ) >> ) ) ", "^a >> b");
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (CallExpr (Ident f ) ) (CallExpr (Ident g ) ) || ) ) ", "f() || g()");
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (BinaryExpr (Ident x ) (BinaryExpr (Ident y ) (BasicLit 1 ) + ) == ) "
+ "(BinaryExpr (UnaryExpr (Ident chanPtr ) <- ) (BasicLit 0 ) > ) && ) ) ",
+ "x == y+1 && <-chanPtr > 0");
+}
+
+TEST(GoParserTest, PrimaryExpr)
+{
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident x ) (CallExpr (Ident f ) ) <= ) ) ", "x <= f()");
+ EXPECT_PARSE("(ExprStmt (BinaryExpr (Ident s ) (BasicLit `.txt` ) + ) ) ", "(s + `.txt`)");
+ EXPECT_PARSE("(ExprStmt (CallExpr (Ident f ) (BasicLit 3.1415 ) (Ident true ) ) ) ", "f(3.1415, true)");
+ EXPECT_PARSE("(ExprStmt (CallExpr (Ident f ) (BasicLit 3.1415 ) (Ident a ) ... ) ) ", "f(3.1415, a...)");
+ EXPECT_PARSE("(ExprStmt (IndexExpr (Ident m ) (BasicLit '1' ) ) ) ", "m['1']");
+ EXPECT_PARSE("(ExprStmt (SliceExpr (Ident s ) (Ident i ) (BinaryExpr (Ident j ) (BasicLit 1 ) + ) nil 0 ) ) ",
+ "s[i : j + 1]");
+ EXPECT_PARSE("(ExprStmt (SelectorExpr (Ident obj ) (Ident color ) ) ) ", "obj.color");
+ EXPECT_PARSE("(ExprStmt (CallExpr (SelectorExpr (IndexExpr (SelectorExpr (Ident f ) (Ident p ) ) (Ident i ) ) "
+ "(Ident x ) ) ) ) ",
+ "f.p[i].x()");
+}
+
+TEST(GoParserTest, Conversions)
+{
+ EXPECT_PARSE("(ExprStmt (StarExpr (CallExpr (Ident Point ) (Ident p ) ) ) ) ", "*Point(p)");
+ EXPECT_PARSE("(ExprStmt (CallExpr (StarExpr (Ident Point ) ) (Ident p ) ) ) ", "(*Point)(p)");
+ EXPECT_PARSE("(ExprStmt (UnaryExpr (CallExpr (ChanType (Ident int ) 0 ) (Ident c ) ) <- ) ) ", "<-chan int(c)");
+ EXPECT_PARSE("(ExprStmt (TypeAssertExpr (Ident y ) (SelectorExpr (Ident io ) (Ident Reader ) ) ) ) ",
+ "y.(io.Reader)");
+}
diff --git a/unittests/Host/CMakeLists.txt b/unittests/Host/CMakeLists.txt
new file mode 100644
index 0000000000000..b4739e113f480
--- /dev/null
+++ b/unittests/Host/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_lldb_unittest(HostTests
+ SocketAddressTest.cpp
+ SocketTest.cpp
+ SymbolsTest.cpp
+ )
diff --git a/unittests/Host/SocketAddressTest.cpp b/unittests/Host/SocketAddressTest.cpp
new file mode 100644
index 0000000000000..bd6bda13f4498
--- /dev/null
+++ b/unittests/Host/SocketAddressTest.cpp
@@ -0,0 +1,77 @@
+//===-- SocketAddressTest.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/SocketAddress.h"
+
+namespace
+{
+ class SocketAddressTest: public ::testing::Test
+ {
+ };
+}
+
+using namespace lldb_private;
+
+TEST_F (SocketAddressTest, Set)
+{
+ SocketAddress sa;
+ ASSERT_TRUE (sa.SetToLocalhost (AF_INET, 1138));
+ ASSERT_STREQ ("127.0.0.1", sa.GetIPAddress ().c_str ());
+ ASSERT_EQ (1138, sa.GetPort ());
+
+ ASSERT_TRUE (sa.SetToAnyAddress (AF_INET, 0));
+ ASSERT_STREQ ("0.0.0.0", sa.GetIPAddress ().c_str ());
+ 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_EQ (1139, sa.GetPort ());
+}
+
+#ifdef _WIN32
+
+// we need to test our inet_ntop implementation for Windows XP
+const char* inet_ntop (int af, const void * src,
+ char * dst, socklen_t size);
+
+TEST_F (SocketAddressTest, inet_ntop)
+{
+ const uint8_t address4[4] = {255, 0, 1, 100};
+ const uint8_t address6[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 255, 0};
+
+ char buffer[INET6_ADDRSTRLEN];
+ memset (buffer, 'x', sizeof (buffer));
+ EXPECT_STREQ ("1:203:405:607:809:a0b:c0d:ff00", inet_ntop (AF_INET6, address6, buffer, sizeof (buffer)));
+ memset (buffer, 'x', sizeof (buffer));
+ EXPECT_STREQ ("1:203:405:607:809:a0b:c0d:ff00", inet_ntop (AF_INET6, address6, buffer, 31));
+ memset (buffer, 'x', sizeof (buffer));
+ EXPECT_STREQ (nullptr, inet_ntop (AF_INET6, address6, buffer, 0));
+ memset (buffer, 'x', sizeof (buffer));
+ EXPECT_STREQ (nullptr, inet_ntop (AF_INET6, address6, buffer, 30));
+
+ memset (buffer, 'x', sizeof (buffer));
+ EXPECT_STREQ ("255.0.1.100", inet_ntop (AF_INET, address4, buffer, sizeof (buffer)));
+ memset (buffer, 'x', sizeof (buffer));
+ EXPECT_STREQ ("255.0.1.100", inet_ntop (AF_INET, address4, buffer, 12));
+ memset (buffer, 'x', sizeof (buffer));
+ EXPECT_STREQ (nullptr, inet_ntop (AF_INET, address4, buffer, 0));
+ memset (buffer, 'x', sizeof (buffer));
+ EXPECT_STREQ (nullptr, inet_ntop (AF_INET, address4, buffer, 11));
+}
+
+#endif
+
+
diff --git a/unittests/Host/SocketTest.cpp b/unittests/Host/SocketTest.cpp
new file mode 100644
index 0000000000000..ebb2f319a9c5d
--- /dev/null
+++ b/unittests/Host/SocketTest.cpp
@@ -0,0 +1,201 @@
+//===-- SocketTest.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 <cstdio>
+#include <functional>
+#include <thread>
+
+#include "gtest/gtest.h"
+
+#include "lldb/Host/Config.h"
+#include "lldb/Host/Socket.h"
+#include "lldb/Host/common/TCPSocket.h"
+#include "lldb/Host/common/UDPSocket.h"
+
+#ifndef LLDB_DISABLE_POSIX
+#include "lldb/Host/posix/DomainSocket.h"
+#endif
+
+using namespace lldb_private;
+
+class SocketTest : public testing::Test
+{
+ public:
+ void
+ SetUp() override
+ {
+#if defined(_MSC_VER)
+ WSADATA data;
+ ::WSAStartup(MAKEWORD(2, 2), &data);
+#endif
+ }
+
+ void
+ TearDown() override
+ {
+#if defined(_MSC_VER)
+ ::WSACleanup();
+#endif
+ }
+
+ protected:
+ static void
+ AcceptThread(Socket *listen_socket, const char *listen_remote_address, bool child_processes_inherit,
+ Socket **accept_socket, Error *error)
+ {
+ *error = listen_socket->Accept(listen_remote_address, child_processes_inherit, *accept_socket);
+ }
+
+ template<typename SocketType>
+ void
+ CreateConnectedSockets(const char *listen_remote_address, const std::function<std::string(const SocketType&)> &get_connect_addr, std::unique_ptr<SocketType> *a_up, std::unique_ptr<SocketType> *b_up)
+ {
+ bool child_processes_inherit = false;
+ Error error;
+ std::unique_ptr<SocketType> listen_socket_up(new SocketType(child_processes_inherit, error));
+ EXPECT_FALSE(error.Fail());
+ error = listen_socket_up->Listen(listen_remote_address, 5);
+ EXPECT_FALSE(error.Fail());
+ EXPECT_TRUE(listen_socket_up->IsValid());
+
+ Error accept_error;
+ Socket *accept_socket;
+ std::thread accept_thread(AcceptThread, listen_socket_up.get(), listen_remote_address, child_processes_inherit,
+ &accept_socket, &accept_error);
+
+ std::string connect_remote_address = get_connect_addr(*listen_socket_up);
+ std::unique_ptr<SocketType> connect_socket_up(new SocketType(child_processes_inherit, error));
+ EXPECT_FALSE(error.Fail());
+ error = connect_socket_up->Connect(connect_remote_address.c_str());
+ EXPECT_FALSE(error.Fail());
+ EXPECT_TRUE(connect_socket_up->IsValid());
+
+ a_up->swap(connect_socket_up);
+ EXPECT_TRUE(error.Success());
+ EXPECT_NE(nullptr, a_up->get());
+ EXPECT_TRUE((*a_up)->IsValid());
+
+ accept_thread.join();
+ b_up->reset(static_cast<SocketType*>(accept_socket));
+ EXPECT_TRUE(accept_error.Success());
+ EXPECT_NE(nullptr, b_up->get());
+ EXPECT_TRUE((*b_up)->IsValid());
+
+ listen_socket_up.reset();
+ }
+};
+
+TEST_F (SocketTest, DecodeHostAndPort)
+{
+ std::string host_str;
+ std::string port_str;
+ int32_t port;
+ Error error;
+ EXPECT_TRUE (Socket::DecodeHostAndPort ("localhost:1138", host_str, port_str, port, &error));
+ EXPECT_STREQ ("localhost", host_str.c_str ());
+ EXPECT_STREQ ("1138", port_str.c_str ());
+ EXPECT_EQ (1138, port);
+ EXPECT_TRUE (error.Success ());
+
+ 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_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_TRUE (Socket::DecodeHostAndPort ("12345", host_str, port_str, port, &error));
+ EXPECT_STREQ ("", host_str.c_str ());
+ EXPECT_STREQ ("12345", port_str.c_str ());
+ EXPECT_EQ (12345, port);
+ EXPECT_TRUE (error.Success ());
+
+ EXPECT_TRUE (Socket::DecodeHostAndPort ("*:0", host_str, port_str, port, &error));
+ EXPECT_STREQ ("*", host_str.c_str ());
+ EXPECT_STREQ ("0", port_str.c_str ());
+ EXPECT_EQ (0, port);
+ EXPECT_TRUE (error.Success ());
+}
+
+#ifndef LLDB_DISABLE_POSIX
+TEST_F (SocketTest, DomainListenConnectAccept)
+{
+ char* file_name_str = tempnam(nullptr, nullptr);
+ EXPECT_NE (nullptr, file_name_str);
+ const std::string file_name(file_name_str);
+ free(file_name_str);
+
+ std::unique_ptr<DomainSocket> socket_a_up;
+ std::unique_ptr<DomainSocket> socket_b_up;
+ CreateConnectedSockets<DomainSocket>(file_name.c_str(),
+ [=](const DomainSocket &)
+ {
+ return file_name;
+ },
+ &socket_a_up, &socket_b_up);
+}
+#endif
+
+TEST_F (SocketTest, TCPListen0ConnectAccept)
+{
+ std::unique_ptr<TCPSocket> socket_a_up;
+ std::unique_ptr<TCPSocket> socket_b_up;
+ CreateConnectedSockets<TCPSocket>("127.0.0.1:0",
+ [=](const TCPSocket &s)
+ {
+ char connect_remote_address[64];
+ snprintf(connect_remote_address, sizeof(connect_remote_address), "localhost:%u", s.GetLocalPortNumber());
+ return std::string(connect_remote_address);
+ },
+ &socket_a_up, &socket_b_up);
+}
+
+TEST_F (SocketTest, TCPGetAddress)
+{
+ std::unique_ptr<TCPSocket> socket_a_up;
+ std::unique_ptr<TCPSocket> socket_b_up;
+ CreateConnectedSockets<TCPSocket>("127.0.0.1:0",
+ [=](const TCPSocket &s)
+ {
+ char connect_remote_address[64];
+ snprintf(connect_remote_address, sizeof(connect_remote_address), "localhost:%u", s.GetLocalPortNumber());
+ return std::string(connect_remote_address);
+ },
+ &socket_a_up,
+ &socket_b_up);
+
+ EXPECT_EQ (socket_a_up->GetLocalPortNumber (), socket_b_up->GetRemotePortNumber ());
+ EXPECT_EQ (socket_b_up->GetLocalPortNumber (), socket_a_up->GetRemotePortNumber ());
+ EXPECT_NE (socket_a_up->GetLocalPortNumber (), socket_b_up->GetLocalPortNumber ());
+ EXPECT_STREQ ("127.0.0.1", socket_a_up->GetRemoteIPAddress ().c_str ());
+ EXPECT_STREQ ("127.0.0.1", socket_b_up->GetRemoteIPAddress ().c_str ());
+}
+
+TEST_F (SocketTest, UDPConnect)
+{
+ Socket* socket_a;
+ Socket* socket_b;
+
+ bool child_processes_inherit = false;
+ auto error = UDPSocket::Connect("127.0.0.1:0", child_processes_inherit, socket_a, socket_b);
+
+ std::unique_ptr<Socket> a_up(socket_a);
+ std::unique_ptr<Socket> b_up(socket_b);
+
+ EXPECT_TRUE(error.Success ());
+ EXPECT_TRUE(a_up->IsValid());
+ EXPECT_TRUE(b_up->IsValid());
+}
diff --git a/unittests/Host/SymbolsTest.cpp b/unittests/Host/SymbolsTest.cpp
new file mode 100644
index 0000000000000..cb59b2c56539b
--- /dev/null
+++ b/unittests/Host/SymbolsTest.cpp
@@ -0,0 +1,30 @@
+//===-- SymbolsTest.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/Symbols.h"
+#include "lldb/Core/ModuleSpec.h"
+
+using namespace lldb_private;
+
+TEST(SymbolsTest, LocateExecutableSymbolFileForUnknownExecutableAndUnknownSymbolFile)
+{
+ ModuleSpec module_spec;
+ FileSpec symbol_file_spec = Symbols::LocateExecutableSymbolFile(module_spec);
+ EXPECT_TRUE(symbol_file_spec.GetFilename().IsEmpty());
+}
+
+TEST(SymbolsTest, LocateExecutableSymbolFileForUnknownExecutableAndMissingSymbolFile)
+{
+ ModuleSpec module_spec;
+ // using a GUID here because the symbol file shouldn't actually exist on disk
+ module_spec.GetSymbolFileSpec().SetFile("4A524676-B24B-4F4E-968A-551D465EBAF1.so", false);
+ FileSpec symbol_file_spec = Symbols::LocateExecutableSymbolFile(module_spec);
+ EXPECT_TRUE(symbol_file_spec.GetFilename().IsEmpty());
+}
diff --git a/unittests/Interpreter/CMakeLists.txt b/unittests/Interpreter/CMakeLists.txt
new file mode 100644
index 0000000000000..4078476bc75a8
--- /dev/null
+++ b/unittests/Interpreter/CMakeLists.txt
@@ -0,0 +1,7 @@
+add_lldb_unittest(InterpreterTests
+ TestArgs.cpp
+ )
+
+target_link_libraries(InterpreterTests
+ ${PYTHON_LIBRARY}
+ )
diff --git a/unittests/Interpreter/TestArgs.cpp b/unittests/Interpreter/TestArgs.cpp
new file mode 100644
index 0000000000000..e2cf1c4d4a0a5
--- /dev/null
+++ b/unittests/Interpreter/TestArgs.cpp
@@ -0,0 +1,70 @@
+//===-- ArgsTest.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/Interpreter/Args.h"
+
+using namespace lldb_private;
+
+TEST(ArgsTest, TestSingleArg)
+{
+ Args args;
+ args.SetCommandString("arg");
+ EXPECT_EQ(1u, args.GetArgumentCount());
+ EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg");
+}
+
+TEST(ArgsTest, TestSingleQuotedArgWithSpace)
+{
+ Args args;
+ args.SetCommandString("\"arg with space\"");
+ EXPECT_EQ(1u, args.GetArgumentCount());
+ EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg with space");
+}
+
+TEST(ArgsTest, TestSingleArgWithQuotedSpace)
+{
+ Args args;
+ args.SetCommandString("arg\\ with\\ space");
+ EXPECT_EQ(1u, args.GetArgumentCount());
+ EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg with space");
+}
+
+TEST(ArgsTest, TestMultipleArgs)
+{
+ Args args;
+ args.SetCommandString("this has multiple args");
+ EXPECT_EQ(4u, args.GetArgumentCount());
+ EXPECT_STREQ(args.GetArgumentAtIndex(0), "this");
+ EXPECT_STREQ(args.GetArgumentAtIndex(1), "has");
+ EXPECT_STREQ(args.GetArgumentAtIndex(2), "multiple");
+ EXPECT_STREQ(args.GetArgumentAtIndex(3), "args");
+}
+
+TEST(ArgsTest, TestOverwriteArgs)
+{
+ Args args;
+ args.SetCommandString("this has multiple args");
+ EXPECT_EQ(4u, args.GetArgumentCount());
+ args.SetCommandString("arg");
+ EXPECT_EQ(1u, args.GetArgumentCount());
+ EXPECT_STREQ(args.GetArgumentAtIndex(0), "arg");
+}
+
+TEST(ArgsTest, TestAppendArg)
+{
+ Args args;
+ args.SetCommandString("first_arg");
+ EXPECT_EQ(1u, args.GetArgumentCount());
+ args.AppendArgument("second_arg");
+ EXPECT_EQ(2u, args.GetArgumentCount());
+ EXPECT_STREQ(args.GetArgumentAtIndex(0), "first_arg");
+ EXPECT_STREQ(args.GetArgumentAtIndex(1), "second_arg");
+}
diff --git a/unittests/ScriptInterpreter/CMakeLists.txt b/unittests/ScriptInterpreter/CMakeLists.txt
new file mode 100644
index 0000000000000..667e7a7da323c
--- /dev/null
+++ b/unittests/ScriptInterpreter/CMakeLists.txt
@@ -0,0 +1,3 @@
+if (NOT LLDB_DISABLE_PYTHON)
+ add_subdirectory(Python)
+endif()
diff --git a/unittests/ScriptInterpreter/Python/CMakeLists.txt b/unittests/ScriptInterpreter/Python/CMakeLists.txt
new file mode 100644
index 0000000000000..f011200e6475e
--- /dev/null
+++ b/unittests/ScriptInterpreter/Python/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_lldb_unittest(ScriptInterpreterPythonTests
+ PythonDataObjectsTests.cpp
+ PythonExceptionStateTests.cpp
+ PythonTestSuite.cpp
+ )
+
+ target_link_libraries(ScriptInterpreterPythonTests lldbPluginScriptInterpreterPython ${PYTHON_LIBRARY})
+ \ No newline at end of file
diff --git a/unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp b/unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp
new file mode 100644
index 0000000000000..5c6925179c6a0
--- /dev/null
+++ b/unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp
@@ -0,0 +1,564 @@
+//===-- 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 "lldb/Host/File.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "Plugins/ScriptInterpreter/Python/lldb-python.h"
+#include "Plugins/ScriptInterpreter/Python/PythonDataObjects.h"
+#include "Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h"
+
+#include "PythonTestSuite.h"
+
+using namespace lldb_private;
+
+class PythonDataObjectsTest : public PythonTestSuite
+{
+ public:
+ void
+ SetUp() override
+ {
+ PythonTestSuite::SetUp();
+
+ PythonString sys_module("sys");
+ m_sys_module.Reset(PyRefType::Owned, PyImport_Import(sys_module.get()));
+ m_main_module = PythonModule::MainModule();
+ m_builtins_module = PythonModule::BuiltinsModule();
+ }
+
+ void
+ TearDown() override
+ {
+ m_sys_module.Reset();
+ m_main_module.Reset();
+ m_builtins_module.Reset();
+
+ PythonTestSuite::TearDown();
+ }
+
+ protected:
+ PythonModule m_sys_module;
+ PythonModule m_main_module;
+ PythonModule m_builtins_module;
+};
+
+TEST_F(PythonDataObjectsTest, TestOwnedReferences)
+{
+ // After creating a new object, the refcount should be >= 1
+ PyObject *obj = PyLong_FromLong(3);
+ Py_ssize_t original_refcnt = obj->ob_refcnt;
+ EXPECT_LE(1, original_refcnt);
+
+ // If we take an owned reference, the refcount should be the same
+ PythonObject owned_long(PyRefType::Owned, obj);
+ EXPECT_EQ(original_refcnt, owned_long.get()->ob_refcnt);
+
+ // Take another reference and verify that the refcount increases by 1
+ PythonObject strong_ref(owned_long);
+ EXPECT_EQ(original_refcnt + 1, strong_ref.get()->ob_refcnt);
+
+ // If we reset the first one, the refcount should be the original value.
+ owned_long.Reset();
+ EXPECT_EQ(original_refcnt, strong_ref.get()->ob_refcnt);
+}
+
+TEST_F(PythonDataObjectsTest, TestResetting)
+{
+ PythonDictionary dict(PyInitialValue::Empty);
+
+ PyObject *new_dict = PyDict_New();
+ dict.Reset(PyRefType::Owned, new_dict);
+ EXPECT_EQ(new_dict, dict.get());
+
+ dict.Reset(PyRefType::Owned, nullptr);
+ EXPECT_EQ(nullptr, dict.get());
+
+ dict.Reset(PyRefType::Owned, PyDict_New());
+ EXPECT_NE(nullptr, dict.get());
+ dict.Reset();
+ EXPECT_EQ(nullptr, dict.get());
+}
+
+TEST_F(PythonDataObjectsTest, TestBorrowedReferences)
+{
+ PythonInteger long_value(PyRefType::Owned, PyLong_FromLong(3));
+ Py_ssize_t original_refcnt = long_value.get()->ob_refcnt;
+ EXPECT_LE(1, original_refcnt);
+
+ PythonInteger borrowed_long(PyRefType::Borrowed, long_value.get());
+ EXPECT_EQ(original_refcnt + 1, borrowed_long.get()->ob_refcnt);
+}
+
+TEST_F(PythonDataObjectsTest, TestGlobalNameResolutionNoDot)
+{
+ PythonObject sys_module = m_main_module.ResolveName("sys");
+ EXPECT_EQ(m_sys_module.get(), sys_module.get());
+ EXPECT_TRUE(sys_module.IsAllocated());
+ EXPECT_TRUE(PythonModule::Check(sys_module.get()));
+}
+
+TEST_F(PythonDataObjectsTest, TestModuleNameResolutionNoDot)
+{
+ PythonObject sys_path = m_sys_module.ResolveName("path");
+ PythonObject sys_version_info = m_sys_module.ResolveName("version_info");
+ EXPECT_TRUE(sys_path.IsAllocated());
+ EXPECT_TRUE(sys_version_info.IsAllocated());
+
+ EXPECT_TRUE(PythonList::Check(sys_path.get()));
+}
+
+TEST_F(PythonDataObjectsTest, TestTypeNameResolutionNoDot)
+{
+ PythonObject sys_version_info = m_sys_module.ResolveName("version_info");
+
+ PythonObject version_info_type(PyRefType::Owned, PyObject_Type(sys_version_info.get()));
+ EXPECT_TRUE(version_info_type.IsAllocated());
+ PythonObject major_version_field = version_info_type.ResolveName("major");
+ EXPECT_TRUE(major_version_field.IsAllocated());
+}
+
+TEST_F(PythonDataObjectsTest, TestInstanceNameResolutionNoDot)
+{
+ PythonObject sys_version_info = m_sys_module.ResolveName("version_info");
+ PythonObject major_version_field = sys_version_info.ResolveName("major");
+ PythonObject minor_version_field = sys_version_info.ResolveName("minor");
+
+ EXPECT_TRUE(major_version_field.IsAllocated());
+ EXPECT_TRUE(minor_version_field.IsAllocated());
+
+ PythonInteger major_version_value = major_version_field.AsType<PythonInteger>();
+ PythonInteger minor_version_value = minor_version_field.AsType<PythonInteger>();
+
+ EXPECT_EQ(PY_MAJOR_VERSION, major_version_value.GetInteger());
+ EXPECT_EQ(PY_MINOR_VERSION, minor_version_value.GetInteger());
+}
+
+TEST_F(PythonDataObjectsTest, TestGlobalNameResolutionWithDot)
+{
+ PythonObject sys_path = m_main_module.ResolveName("sys.path");
+ EXPECT_TRUE(sys_path.IsAllocated());
+ EXPECT_TRUE(PythonList::Check(sys_path.get()));
+
+ PythonInteger version_major = m_main_module.ResolveName(
+ "sys.version_info.major").AsType<PythonInteger>();
+ PythonInteger version_minor = m_main_module.ResolveName(
+ "sys.version_info.minor").AsType<PythonInteger>();
+ EXPECT_TRUE(version_major.IsAllocated());
+ EXPECT_TRUE(version_minor.IsAllocated());
+ EXPECT_EQ(PY_MAJOR_VERSION, version_major.GetInteger());
+ EXPECT_EQ(PY_MINOR_VERSION, version_minor.GetInteger());
+}
+
+TEST_F(PythonDataObjectsTest, TestDictionaryResolutionWithDot)
+{
+ // Make up a custom dictionary with "sys" pointing to the `sys` module.
+ PythonDictionary dict(PyInitialValue::Empty);
+ dict.SetItemForKey(PythonString("sys"), m_sys_module);
+
+ // Now use that dictionary to resolve `sys.version_info.major`
+ PythonInteger version_major = PythonObject::ResolveNameWithDictionary(
+ "sys.version_info.major", dict).AsType<PythonInteger>();
+ PythonInteger version_minor = PythonObject::ResolveNameWithDictionary(
+ "sys.version_info.minor", dict).AsType<PythonInteger>();
+ EXPECT_EQ(PY_MAJOR_VERSION, version_major.GetInteger());
+ EXPECT_EQ(PY_MINOR_VERSION, version_minor.GetInteger());
+}
+
+TEST_F(PythonDataObjectsTest, TestPythonInteger)
+{
+// Test that integers behave correctly when wrapped by a PythonInteger.
+
+#if PY_MAJOR_VERSION < 3
+ // Verify that `PythonInt` works correctly when given a PyInt object.
+ // Note that PyInt doesn't exist in Python 3.x, so this is only for 2.x
+ PyObject *py_int = PyInt_FromLong(12);
+ EXPECT_TRUE(PythonInteger::Check(py_int));
+ PythonInteger python_int(PyRefType::Owned, py_int);
+
+ EXPECT_EQ(PyObjectType::Integer, python_int.GetObjectType());
+ EXPECT_EQ(12, python_int.GetInteger());
+#endif
+
+ // Verify that `PythonInteger` works correctly when given a PyLong object.
+ PyObject *py_long = PyLong_FromLong(12);
+ EXPECT_TRUE(PythonInteger::Check(py_long));
+ PythonInteger python_long(PyRefType::Owned, py_long);
+ EXPECT_EQ(PyObjectType::Integer, python_long.GetObjectType());
+
+ // Verify that you can reset the value and that it is reflected properly.
+ python_long.SetInteger(40);
+ EXPECT_EQ(40, python_long.GetInteger());
+
+ // Test that creating a `PythonInteger` object works correctly with the
+ // int constructor.
+ PythonInteger constructed_int(7);
+ EXPECT_EQ(7, constructed_int.GetInteger());
+}
+
+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.
+ // Note that PyString doesn't exist in Python 3.x, so this is only for 2.x
+ PyObject *py_string = PyString_FromString(test_string);
+ EXPECT_TRUE(PythonString::Check(py_string));
+ PythonString python_string(PyRefType::Owned, py_string);
+
+ EXPECT_EQ(PyObjectType::String, python_string.GetObjectType());
+ EXPECT_STREQ(test_string, python_string.GetString().data());
+#else
+ // Verify that `PythonString` works correctly when given a PyUnicode object.
+ PyObject *py_unicode = PyUnicode_FromString(test_string);
+ EXPECT_TRUE(PythonString::Check(py_unicode));
+ PythonString python_unicode(PyRefType::Owned, py_unicode);
+ EXPECT_EQ(PyObjectType::String, python_unicode.GetObjectType());
+ EXPECT_STREQ(test_string, python_unicode.GetString().data());
+#endif
+
+ // 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());
+}
+
+TEST_F(PythonDataObjectsTest, TestPythonStringToStr)
+{
+ const char *c_str = "PythonDataObjectsTest::TestPythonStringToStr";
+
+ PythonString str(c_str);
+ EXPECT_STREQ(c_str, str.GetString().str().c_str());
+
+ PythonString str_str = str.Str();
+ EXPECT_STREQ(c_str, str_str.GetString().str().c_str());
+}
+
+TEST_F(PythonDataObjectsTest, TestPythonIntegerToStr)
+{
+}
+
+TEST_F(PythonDataObjectsTest, TestPythonIntegerToStructuredInteger)
+{
+ PythonInteger integer(7);
+ auto int_sp = integer.CreateStructuredInteger();
+ EXPECT_EQ(7, int_sp->GetValue());
+}
+
+TEST_F(PythonDataObjectsTest, TestPythonStringToStructuredString)
+{
+ static const char *test_string = "PythonDataObjectsTest::TestPythonStringToStructuredString";
+ PythonString constructed_string(test_string);
+ auto string_sp = constructed_string.CreateStructuredString();
+ EXPECT_STREQ(test_string, string_sp->GetStringValue().c_str());
+}
+
+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 long long_value0 = 5;
+ static const char *const string_value1 = "String Index 1";
+
+ PyObject *py_list = PyList_New(2);
+ EXPECT_TRUE(PythonList::Check(py_list));
+ PythonList list(PyRefType::Owned, py_list);
+
+ PythonObject list_items[list_size];
+ list_items[0].Reset(PythonInteger(long_value0));
+ list_items[1].Reset(PythonString(string_value1));
+
+ for (int i = 0; i < list_size; ++i)
+ list.SetItemAtIndex(i, list_items[i]);
+
+ EXPECT_EQ(list_size, list.GetSize());
+ EXPECT_EQ(PyObjectType::List, list.GetObjectType());
+
+ // Verify that the values match
+ PythonObject chk_value1 = list.GetItemAtIndex(0);
+ PythonObject chk_value2 = list.GetItemAtIndex(1);
+ EXPECT_TRUE(PythonInteger::Check(chk_value1.get()));
+ EXPECT_TRUE(PythonString::Check(chk_value2.get()));
+
+ PythonInteger chk_int(PyRefType::Borrowed, chk_value1.get());
+ PythonString chk_str(PyRefType::Borrowed, chk_value2.get());
+
+ EXPECT_EQ(long_value0, chk_int.GetInteger());
+ EXPECT_STREQ(string_value1, chk_str.GetString().str().c_str());
+}
+
+TEST_F(PythonDataObjectsTest, TestPythonListManipulation)
+{
+ // Test that manipulation of a PythonList behaves correctly when
+ // wrapped by a PythonDictionary.
+
+ static const long long_value0 = 5;
+ static const char *const string_value1 = "String Index 1";
+
+ PythonList list(PyInitialValue::Empty);
+ PythonInteger integer(long_value0);
+ PythonString string(string_value1);
+
+ list.AppendItem(integer);
+ list.AppendItem(string);
+ EXPECT_EQ(2, list.GetSize());
+
+ // Verify that the values match
+ PythonObject chk_value1 = list.GetItemAtIndex(0);
+ PythonObject chk_value2 = list.GetItemAtIndex(1);
+ EXPECT_TRUE(PythonInteger::Check(chk_value1.get()));
+ EXPECT_TRUE(PythonString::Check(chk_value2.get()));
+
+ PythonInteger chk_int(PyRefType::Borrowed, chk_value1.get());
+ PythonString chk_str(PyRefType::Borrowed, chk_value2.get());
+
+ EXPECT_EQ(long_value0, chk_int.GetInteger());
+ EXPECT_STREQ(string_value1, chk_str.GetString().str().c_str());
+}
+
+TEST_F(PythonDataObjectsTest, TestPythonListToStructuredList)
+{
+ static const long long_value0 = 5;
+ static const char *const string_value1 = "String Index 1";
+
+ PythonList list(PyInitialValue::Empty);
+ list.AppendItem(PythonInteger(long_value0));
+ list.AppendItem(PythonString(string_value1));
+
+ auto array_sp = list.CreateStructuredArray();
+ EXPECT_EQ(StructuredData::Type::eTypeInteger, array_sp->GetItemAtIndex(0)->GetType());
+ EXPECT_EQ(StructuredData::Type::eTypeString, array_sp->GetItemAtIndex(1)->GetType());
+
+ auto int_sp = array_sp->GetItemAtIndex(0)->GetAsInteger();
+ auto string_sp = array_sp->GetItemAtIndex(1)->GetAsString();
+
+ EXPECT_EQ(long_value0, 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());
+
+ tuple = PythonTuple(3);
+ EXPECT_EQ(3, tuple.GetSize());
+}
+
+TEST_F(PythonDataObjectsTest, TestPythonTupleValues)
+{
+ PythonTuple tuple(3);
+
+ PythonInteger int_value(1);
+ PythonString string_value("Test");
+ PythonObject none_value(PyRefType::Borrowed, Py_None);
+
+ tuple.SetItemAtIndex(0, int_value);
+ tuple.SetItemAtIndex(1, string_value);
+ tuple.SetItemAtIndex(2, none_value);
+
+ EXPECT_EQ(tuple.GetItemAtIndex(0).get(), int_value.get());
+ EXPECT_EQ(tuple.GetItemAtIndex(1).get(), string_value.get());
+ EXPECT_EQ(tuple.GetItemAtIndex(2).get(), none_value.get());
+}
+
+TEST_F(PythonDataObjectsTest, TestPythonTupleInitializerList)
+{
+ PythonInteger int_value(1);
+ 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(tuple.GetItemAtIndex(0).get(), int_value.get());
+ EXPECT_EQ(tuple.GetItemAtIndex(1).get(), string_value.get());
+ EXPECT_EQ(tuple.GetItemAtIndex(2).get(), none_value.get());
+}
+
+TEST_F(PythonDataObjectsTest, TestPythonTupleInitializerList2)
+{
+ PythonInteger int_value(1);
+ PythonString string_value("Test");
+ 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(tuple.GetItemAtIndex(0).get(), int_value.get());
+ EXPECT_EQ(tuple.GetItemAtIndex(1).get(), string_value.get());
+ EXPECT_EQ(tuple.GetItemAtIndex(2).get(), none_value.get());
+}
+
+TEST_F(PythonDataObjectsTest, TestPythonTupleToStructuredList)
+{
+ PythonInteger int_value(1);
+ PythonString string_value("Test");
+
+ PythonTuple tuple{ int_value.get(), string_value.get() };
+
+ auto array_sp = tuple.CreateStructuredArray();
+ EXPECT_EQ(tuple.GetSize(), array_sp->GetSize());
+ EXPECT_EQ(StructuredData::Type::eTypeInteger, array_sp->GetItemAtIndex(0)->GetType());
+ EXPECT_EQ(StructuredData::Type::eTypeString, array_sp->GetItemAtIndex(1)->GetType());
+}
+
+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;
+ const char *key_0 = "Key 0";
+ int key_1 = 1;
+ const int value_0 = 0;
+ const char *value_1 = "Value 1";
+
+ PythonObject py_keys[dict_entries];
+ PythonObject py_values[dict_entries];
+
+ py_keys[0].Reset(PythonString(key_0));
+ py_keys[1].Reset(PythonInteger(key_1));
+ py_values[0].Reset(PythonInteger(value_0));
+ py_values[1].Reset(PythonString(value_1));
+
+ PyObject *py_dict = PyDict_New();
+ EXPECT_TRUE(PythonDictionary::Check(py_dict));
+ PythonDictionary dict(PyRefType::Owned, py_dict);
+
+ for (int 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());
+
+ // Verify that the values match
+ PythonObject chk_value1 = dict.GetItemForKey(py_keys[0]);
+ PythonObject chk_value2 = dict.GetItemForKey(py_keys[1]);
+ EXPECT_TRUE(PythonInteger::Check(chk_value1.get()));
+ EXPECT_TRUE(PythonString::Check(chk_value2.get()));
+
+ PythonInteger chk_int(PyRefType::Borrowed, chk_value1.get());
+ PythonString chk_str(PyRefType::Borrowed, chk_value2.get());
+
+ EXPECT_EQ(value_0, chk_int.GetInteger());
+ EXPECT_STREQ(value_1, chk_str.GetString().str().c_str());
+}
+
+TEST_F(PythonDataObjectsTest, TestPythonDictionaryManipulation)
+{
+ // Test that manipulation of a dictionary behaves correctly when wrapped
+ // by a PythonDictionary.
+ static const int dict_entries = 2;
+
+ const char *const key_0 = "Key 0";
+ const char *const key_1 = "Key 1";
+ const long value_0 = 1;
+ const char *const value_1 = "Value 1";
+
+ PythonString keys[dict_entries];
+ PythonObject values[dict_entries];
+
+ keys[0].Reset(PythonString(key_0));
+ keys[1].Reset(PythonString(key_1));
+ values[0].Reset(PythonInteger(value_0));
+ values[1].Reset(PythonString(value_1));
+
+ PythonDictionary dict(PyInitialValue::Empty);
+ for (int i = 0; i < 2; ++i)
+ dict.SetItemForKey(keys[i], values[i]);
+
+ EXPECT_EQ(dict_entries, dict.GetSize());
+
+ // Verify that the keys and values match
+ PythonObject chk_value1 = dict.GetItemForKey(keys[0]);
+ PythonObject chk_value2 = dict.GetItemForKey(keys[1]);
+ EXPECT_TRUE(PythonInteger::Check(chk_value1.get()));
+ EXPECT_TRUE(PythonString::Check(chk_value2.get()));
+
+ PythonInteger chk_int(PyRefType::Borrowed, chk_value1.get());
+ PythonString chk_str(PyRefType::Borrowed, chk_value2.get());
+
+ EXPECT_EQ(value_0, chk_int.GetInteger());
+ EXPECT_STREQ(value_1, chk_str.GetString().str().c_str());
+}
+
+TEST_F(PythonDataObjectsTest, TestPythonDictionaryToStructuredDictionary)
+{
+ static const char *const string_key0 = "String Key 0";
+ static const char *const string_key1 = "String Key 1";
+
+ static const char *const string_value0 = "String Value 0";
+ static const long int_value1 = 7;
+
+ PythonDictionary dict(PyInitialValue::Empty);
+ dict.SetItemForKey(PythonString(string_key0), PythonString(string_value0));
+ dict.SetItemForKey(PythonString(string_key1), PythonInteger(int_value1));
+
+ auto dict_sp = dict.CreateStructuredDictionary();
+ EXPECT_EQ(2, dict_sp->GetSize());
+
+ EXPECT_TRUE(dict_sp->HasKey(string_key0));
+ EXPECT_TRUE(dict_sp->HasKey(string_key1));
+
+ auto string_sp = dict_sp->GetValueForKey(string_key0)->GetAsString();
+ 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());
+}
+
+TEST_F(PythonDataObjectsTest, TestPythonCallableCheck)
+{
+ PythonObject sys_exc_info = m_sys_module.ResolveName("exc_info");
+ PythonObject none(PyRefType::Borrowed, Py_None);
+
+ EXPECT_TRUE(PythonCallable::Check(sys_exc_info.get()));
+ EXPECT_FALSE(PythonCallable::Check(none.get()));
+}
+
+TEST_F(PythonDataObjectsTest, TestPythonCallableInvoke)
+{
+ auto list = m_builtins_module.ResolveName("list").AsType<PythonCallable>();
+ PythonInteger one(1);
+ PythonString two("two");
+ PythonTuple three = { one, two };
+
+ PythonTuple tuple_to_convert = { one, two, three };
+ PythonObject result = list({ tuple_to_convert });
+
+ EXPECT_TRUE(PythonList::Check(result.get()));
+ auto list_result = result.AsType<PythonList>();
+ EXPECT_EQ(3, 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());
+}
+
+TEST_F(PythonDataObjectsTest, TestPythonFile)
+{
+ File file(FileSystem::DEV_NULL, File::eOpenOptionRead);
+ PythonFile py_file(file, "r");
+ EXPECT_TRUE(PythonFile::Check(py_file.get()));
+}
+
+TEST_F(PythonDataObjectsTest, TestObjectAttributes)
+{
+ PythonInteger py_int(42);
+ EXPECT_TRUE(py_int.HasAttribute("numerator"));
+ EXPECT_FALSE(py_int.HasAttribute("this_should_not_exist"));
+
+ 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
new file mode 100644
index 0000000000000..a0a6f986cef6c
--- /dev/null
+++ b/unittests/ScriptInterpreter/Python/PythonExceptionStateTests.cpp
@@ -0,0 +1,174 @@
+//===-- PythonExceptionStateTest.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 "Plugins/ScriptInterpreter/Python/lldb-python.h"
+#include "Plugins/ScriptInterpreter/Python/PythonDataObjects.h"
+#include "Plugins/ScriptInterpreter/Python/PythonExceptionState.h"
+#include "Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h"
+
+#include "PythonTestSuite.h"
+
+using namespace lldb_private;
+
+class PythonExceptionStateTest : public PythonTestSuite
+{
+ public:
+ protected:
+ void
+ RaiseException()
+ {
+ PyErr_SetString(PyExc_RuntimeError, "PythonExceptionStateTest test error");
+ }
+};
+
+TEST_F(PythonExceptionStateTest, TestExceptionStateChecking)
+{
+ PyErr_Clear();
+ EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
+
+ RaiseException();
+ EXPECT_TRUE(PythonExceptionState::HasErrorOccurred());
+
+ PyErr_Clear();
+}
+
+TEST_F(PythonExceptionStateTest, TestAcquisitionSemantics)
+{
+ PyErr_Clear();
+ PythonExceptionState no_error(false);
+ EXPECT_FALSE(no_error.IsError());
+ EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
+
+ PyErr_Clear();
+ RaiseException();
+ PythonExceptionState error(false);
+ EXPECT_TRUE(error.IsError());
+ EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
+ error.Discard();
+
+ PyErr_Clear();
+ RaiseException();
+ error.Acquire(false);
+ EXPECT_TRUE(error.IsError());
+ EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
+
+ PyErr_Clear();
+}
+
+TEST_F(PythonExceptionStateTest, TestDiscardSemantics)
+{
+ PyErr_Clear();
+
+ // Test that discarding an exception does not restore the exception
+ // state even when auto-restore==true is set
+ RaiseException();
+ PythonExceptionState error(true);
+ EXPECT_TRUE(error.IsError());
+ EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
+
+ error.Discard();
+ EXPECT_FALSE(error.IsError());
+ EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
+}
+
+TEST_F(PythonExceptionStateTest, TestResetSemantics)
+{
+ PyErr_Clear();
+
+ // Resetting when auto-restore is true should restore.
+ RaiseException();
+ PythonExceptionState error(true);
+ EXPECT_TRUE(error.IsError());
+ EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
+ error.Reset();
+ EXPECT_FALSE(error.IsError());
+ EXPECT_TRUE(PythonExceptionState::HasErrorOccurred());
+
+ PyErr_Clear();
+
+ // Resetting when auto-restore is false should discard.
+ RaiseException();
+ PythonExceptionState error2(false);
+ EXPECT_TRUE(error2.IsError());
+ EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
+ error2.Reset();
+ EXPECT_FALSE(error2.IsError());
+ EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
+
+ PyErr_Clear();
+}
+
+TEST_F(PythonExceptionStateTest, TestManualRestoreSemantics)
+{
+ PyErr_Clear();
+ RaiseException();
+ PythonExceptionState error(false);
+ EXPECT_TRUE(error.IsError());
+ EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
+
+ error.Restore();
+ EXPECT_FALSE(error.IsError());
+ EXPECT_TRUE(PythonExceptionState::HasErrorOccurred());
+
+ PyErr_Clear();
+}
+
+TEST_F(PythonExceptionStateTest, TestAutoRestoreSemantics)
+{
+ PyErr_Clear();
+ // Test that using the auto-restore flag correctly restores the exception
+ // state on destruction, and not using the auto-restore flag correctly
+ // does NOT restore the state on destruction.
+ {
+ RaiseException();
+ PythonExceptionState error(false);
+ EXPECT_TRUE(error.IsError());
+ EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
+ }
+ EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
+
+ PyErr_Clear();
+ {
+ RaiseException();
+ PythonExceptionState error(true);
+ EXPECT_TRUE(error.IsError());
+ EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
+ }
+ EXPECT_TRUE(PythonExceptionState::HasErrorOccurred());
+
+ PyErr_Clear();
+}
+
+TEST_F(PythonExceptionStateTest, TestAutoRestoreChanged)
+{
+ // Test that if we re-acquire with different auto-restore semantics,
+ // that the new semantics are respected.
+ PyErr_Clear();
+
+ RaiseException();
+ PythonExceptionState error(false);
+ EXPECT_TRUE(error.IsError());
+
+ error.Reset();
+ EXPECT_FALSE(error.IsError());
+ EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
+
+ RaiseException();
+ error.Acquire(true);
+ EXPECT_TRUE(error.IsError());
+ EXPECT_FALSE(PythonExceptionState::HasErrorOccurred());
+
+ error.Reset();
+ EXPECT_FALSE(error.IsError());
+ 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
new file mode 100644
index 0000000000000..3d1727dc1c25a
--- /dev/null
+++ b/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
@@ -0,0 +1,42 @@
+//===-- PythonTestSuite.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 "Plugins/ScriptInterpreter/Python/lldb-python.h"
+#include "Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h"
+
+#include "PythonTestSuite.h"
+
+using namespace lldb_private;
+
+void
+PythonTestSuite::SetUp()
+{
+ HostInfoBase::Initialize();
+ // ScriptInterpreterPython::Initialize() depends on HostInfo being
+ // initializedso it can compute the python directory etc.
+ ScriptInterpreterPython::Initialize();
+
+ // Although we don't care about concurrency for the purposes of running
+ // this test suite, Python requires the GIL to be locked even for
+ // deallocating memory, which can happen when you call Py_DECREF or
+ // Py_INCREF. So acquire the GIL for the entire duration of this
+ // test suite.
+ m_gil_state = PyGILState_Ensure();
+}
+
+void
+PythonTestSuite::TearDown()
+{
+ PyGILState_Release(m_gil_state);
+
+ ScriptInterpreterPython::Terminate();
+}
diff --git a/unittests/ScriptInterpreter/Python/PythonTestSuite.h b/unittests/ScriptInterpreter/Python/PythonTestSuite.h
new file mode 100644
index 0000000000000..461fc1d5676c3
--- /dev/null
+++ b/unittests/ScriptInterpreter/Python/PythonTestSuite.h
@@ -0,0 +1,26 @@
+//===-- PythonTestSuite.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"
+
+using namespace lldb_private;
+
+class PythonTestSuite : public testing::Test
+{
+public:
+ void
+ SetUp() override;
+
+ void
+ TearDown() override;
+
+private:
+ PyGILState_STATE m_gil_state;
+};
+
diff --git a/unittests/Utility/CMakeLists.txt b/unittests/Utility/CMakeLists.txt
new file mode 100644
index 0000000000000..30936acce9dce
--- /dev/null
+++ b/unittests/Utility/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_lldb_unittest(UtilityTests
+ StringExtractorTest.cpp
+ TaskPoolTest.cpp
+ UriParserTest.cpp
+ )
diff --git a/unittests/Utility/StringExtractorTest.cpp b/unittests/Utility/StringExtractorTest.cpp
new file mode 100644
index 0000000000000..0eb6d1bda8bb4
--- /dev/null
+++ b/unittests/Utility/StringExtractorTest.cpp
@@ -0,0 +1,406 @@
+#include <limits.h>
+#include "gtest/gtest.h"
+
+#include "lldb/Utility/StringExtractor.h"
+
+namespace
+{
+ class StringExtractorTest: public ::testing::Test
+ {
+ };
+}
+
+TEST_F (StringExtractorTest, InitEmpty)
+{
+ const char kEmptyString[] = "";
+ StringExtractor ex (kEmptyString);
+
+ ASSERT_EQ (true, ex.IsGood());
+ ASSERT_EQ (0u, ex.GetFilePos());
+ ASSERT_STREQ (kEmptyString, ex.GetStringRef().c_str());
+ ASSERT_EQ (true, ex.Empty());
+ ASSERT_EQ (0u, ex.GetBytesLeft());
+ ASSERT_EQ (nullptr, ex.Peek());
+}
+
+TEST_F (StringExtractorTest, InitMisc)
+{
+ const char kInitMiscString[] = "Hello, StringExtractor!";
+ StringExtractor ex (kInitMiscString);
+
+ ASSERT_EQ (true, ex.IsGood());
+ ASSERT_EQ (0u, ex.GetFilePos());
+ ASSERT_STREQ (kInitMiscString, ex.GetStringRef().c_str());
+ ASSERT_EQ (false, ex.Empty());
+ ASSERT_EQ (sizeof(kInitMiscString)-1, ex.GetBytesLeft());
+ ASSERT_EQ (kInitMiscString[0], *ex.Peek());
+}
+
+TEST_F (StringExtractorTest, DecodeHexU8_Underflow)
+{
+ const char kEmptyString[] = "";
+ StringExtractor ex (kEmptyString);
+
+ ASSERT_EQ (-1, ex.DecodeHexU8());
+ ASSERT_EQ (true, ex.IsGood());
+ ASSERT_EQ (0u, ex.GetFilePos());
+ ASSERT_EQ (true, ex.Empty());
+ ASSERT_EQ (0u, ex.GetBytesLeft());
+ ASSERT_EQ (nullptr, ex.Peek());
+}
+
+TEST_F (StringExtractorTest, DecodeHexU8_Underflow2)
+{
+ const char kEmptyString[] = "1";
+ StringExtractor ex (kEmptyString);
+
+ ASSERT_EQ (-1, ex.DecodeHexU8());
+ ASSERT_EQ (true, ex.IsGood());
+ ASSERT_EQ (0u, ex.GetFilePos());
+ ASSERT_EQ (1u, ex.GetBytesLeft());
+ ASSERT_EQ ('1', *ex.Peek());
+}
+
+TEST_F (StringExtractorTest, DecodeHexU8_InvalidHex)
+{
+ const char kInvalidHex[] = "xa";
+ StringExtractor ex (kInvalidHex);
+
+ ASSERT_EQ (-1, ex.DecodeHexU8());
+ ASSERT_EQ (true, ex.IsGood());
+ ASSERT_EQ (0u, ex.GetFilePos());
+ ASSERT_EQ (2u, ex.GetBytesLeft());
+ ASSERT_EQ ('x', *ex.Peek());
+}
+
+TEST_F (StringExtractorTest, DecodeHexU8_InvalidHex2)
+{
+ const char kInvalidHex[] = "ax";
+ StringExtractor ex (kInvalidHex);
+
+ ASSERT_EQ (-1, ex.DecodeHexU8());
+ ASSERT_EQ (true, ex.IsGood());
+ ASSERT_EQ (0u, ex.GetFilePos());
+ ASSERT_EQ (2u, ex.GetBytesLeft());
+ ASSERT_EQ ('a', *ex.Peek());
+}
+
+TEST_F (StringExtractorTest, DecodeHexU8_Exact)
+{
+ const char kValidHexPair[] = "12";
+ StringExtractor ex (kValidHexPair);
+
+ ASSERT_EQ (0x12, ex.DecodeHexU8());
+ ASSERT_EQ (true, ex.IsGood());
+ ASSERT_EQ (2u, ex.GetFilePos());
+ ASSERT_EQ (0u, ex.GetBytesLeft());
+ ASSERT_EQ (nullptr, ex.Peek());
+}
+
+TEST_F (StringExtractorTest, DecodeHexU8_Extra)
+{
+ const char kValidHexPair[] = "1234";
+ StringExtractor ex (kValidHexPair);
+
+ ASSERT_EQ (0x12, ex.DecodeHexU8());
+ ASSERT_EQ (true, ex.IsGood());
+ ASSERT_EQ (2u, ex.GetFilePos());
+ ASSERT_EQ (2u, ex.GetBytesLeft());
+ ASSERT_EQ ('3', *ex.Peek());
+}
+
+TEST_F (StringExtractorTest, GetHexU8_Underflow)
+{
+ const char kEmptyString[] = "";
+ StringExtractor ex (kEmptyString);
+
+ ASSERT_EQ (0xab, ex.GetHexU8(0xab));
+ ASSERT_EQ (false, ex.IsGood());
+ ASSERT_EQ (UINT64_MAX, ex.GetFilePos());
+ ASSERT_EQ (true, ex.Empty());
+ ASSERT_EQ (0u, ex.GetBytesLeft());
+ ASSERT_EQ (nullptr, ex.Peek());
+}
+
+TEST_F (StringExtractorTest, GetHexU8_Underflow2)
+{
+ const char kOneNibble[] = "1";
+ StringExtractor ex (kOneNibble);
+
+ ASSERT_EQ (0xbc, ex.GetHexU8(0xbc));
+ ASSERT_EQ (false, ex.IsGood());
+ ASSERT_EQ (UINT64_MAX, ex.GetFilePos());
+ ASSERT_EQ (0u, ex.GetBytesLeft());
+ ASSERT_EQ (nullptr, ex.Peek());
+}
+
+TEST_F (StringExtractorTest, GetHexU8_InvalidHex)
+{
+ const char kInvalidHex[] = "xx";
+ StringExtractor ex (kInvalidHex);
+
+ ASSERT_EQ (0xcd, ex.GetHexU8(0xcd));
+ ASSERT_EQ (false, ex.IsGood());
+ ASSERT_EQ (UINT64_MAX, ex.GetFilePos());
+ ASSERT_EQ (0u, ex.GetBytesLeft());
+ ASSERT_EQ (nullptr, ex.Peek());
+}
+
+TEST_F (StringExtractorTest, GetHexU8_Exact)
+{
+ const char kValidHexPair[] = "12";
+ StringExtractor ex (kValidHexPair);
+
+ ASSERT_EQ (0x12, ex.GetHexU8(0x12));
+ ASSERT_EQ (true, ex.IsGood());
+ ASSERT_EQ (2u, ex.GetFilePos());
+ ASSERT_EQ (0u, ex.GetBytesLeft());
+ ASSERT_EQ (nullptr, ex.Peek());
+}
+
+TEST_F (StringExtractorTest, GetHexU8_Extra)
+{
+ const char kValidHexPair[] = "1234";
+ StringExtractor ex (kValidHexPair);
+
+ ASSERT_EQ (0x12, ex.GetHexU8(0x12));
+ ASSERT_EQ (true, ex.IsGood());
+ ASSERT_EQ (2u, ex.GetFilePos());
+ ASSERT_EQ (2u, ex.GetBytesLeft());
+ ASSERT_EQ ('3', *ex.Peek());
+}
+
+TEST_F (StringExtractorTest, GetHexU8_Underflow_NoEof)
+{
+ const char kEmptyString[] = "";
+ StringExtractor ex (kEmptyString);
+ const bool kSetEofOnFail = false;
+
+ ASSERT_EQ (0xab, ex.GetHexU8(0xab, kSetEofOnFail));
+ ASSERT_EQ (false, ex.IsGood()); // this result seems inconsistent with kSetEofOnFail == false
+ ASSERT_EQ (UINT64_MAX, ex.GetFilePos());
+ ASSERT_EQ (true, ex.Empty());
+ ASSERT_EQ (0u, ex.GetBytesLeft());
+ ASSERT_EQ (nullptr, ex.Peek());
+}
+
+TEST_F (StringExtractorTest, GetHexU8_Underflow2_NoEof)
+{
+ const char kOneNibble[] = "1";
+ StringExtractor ex (kOneNibble);
+ const bool kSetEofOnFail = false;
+
+ ASSERT_EQ (0xbc, ex.GetHexU8(0xbc, kSetEofOnFail));
+ ASSERT_EQ (true, ex.IsGood());
+ ASSERT_EQ (0u, ex.GetFilePos());
+ ASSERT_EQ (1u, ex.GetBytesLeft());
+ ASSERT_EQ ('1', *ex.Peek());
+}
+
+TEST_F (StringExtractorTest, GetHexU8_InvalidHex_NoEof)
+{
+ const char kInvalidHex[] = "xx";
+ StringExtractor ex (kInvalidHex);
+ const bool kSetEofOnFail = false;
+
+ ASSERT_EQ (0xcd, ex.GetHexU8(0xcd, kSetEofOnFail));
+ ASSERT_EQ (true, ex.IsGood());
+ ASSERT_EQ (0u, ex.GetFilePos());
+ ASSERT_EQ (2u, ex.GetBytesLeft());
+ ASSERT_EQ ('x', *ex.Peek());
+}
+
+TEST_F (StringExtractorTest, GetHexU8_Exact_NoEof)
+{
+ const char kValidHexPair[] = "12";
+ StringExtractor ex (kValidHexPair);
+ const bool kSetEofOnFail = false;
+
+ ASSERT_EQ (0x12, ex.GetHexU8(0x12, kSetEofOnFail));
+ ASSERT_EQ (true, ex.IsGood());
+ ASSERT_EQ (2u, ex.GetFilePos());
+ ASSERT_EQ (0u, ex.GetBytesLeft());
+ ASSERT_EQ (nullptr, ex.Peek());
+}
+
+TEST_F (StringExtractorTest, GetHexU8_Extra_NoEof)
+{
+ const char kValidHexPair[] = "1234";
+ StringExtractor ex (kValidHexPair);
+ const bool kSetEofOnFail = false;
+
+ ASSERT_EQ (0x12, ex.GetHexU8(0x12, kSetEofOnFail));
+ ASSERT_EQ (true, ex.IsGood());
+ ASSERT_EQ (2u, ex.GetFilePos());
+ ASSERT_EQ (2u, ex.GetBytesLeft());
+ ASSERT_EQ ('3', *ex.Peek());
+}
+
+TEST_F (StringExtractorTest, GetHexBytes)
+{
+ const char kHexEncodedBytes[] = "abcdef0123456789xyzw";
+ const size_t kValidHexPairs = 8;
+ StringExtractor ex(kHexEncodedBytes);
+
+ uint8_t dst[kValidHexPairs];
+ ASSERT_EQ(kValidHexPairs, ex.GetHexBytes (dst, kValidHexPairs, 0xde));
+ EXPECT_EQ(0xab,dst[0]);
+ EXPECT_EQ(0xcd,dst[1]);
+ EXPECT_EQ(0xef,dst[2]);
+ EXPECT_EQ(0x01,dst[3]);
+ EXPECT_EQ(0x23,dst[4]);
+ EXPECT_EQ(0x45,dst[5]);
+ EXPECT_EQ(0x67,dst[6]);
+ EXPECT_EQ(0x89,dst[7]);
+
+ ASSERT_EQ(true, ex.IsGood());
+ ASSERT_EQ(2*kValidHexPairs, ex.GetFilePos());
+ ASSERT_EQ(false, ex.Empty());
+ ASSERT_EQ(4u, ex.GetBytesLeft());
+ ASSERT_EQ('x', *ex.Peek());
+}
+
+TEST_F (StringExtractorTest, GetHexBytes_Underflow)
+{
+ const char kHexEncodedBytes[] = "abcdef0123456789xyzw";
+ const size_t kValidHexPairs = 8;
+ StringExtractor ex(kHexEncodedBytes);
+
+ uint8_t dst[12];
+ ASSERT_EQ(kValidHexPairs, ex.GetHexBytes (dst, sizeof(dst), 0xde));
+ EXPECT_EQ(0xab,dst[0]);
+ EXPECT_EQ(0xcd,dst[1]);
+ EXPECT_EQ(0xef,dst[2]);
+ EXPECT_EQ(0x01,dst[3]);
+ EXPECT_EQ(0x23,dst[4]);
+ EXPECT_EQ(0x45,dst[5]);
+ EXPECT_EQ(0x67,dst[6]);
+ EXPECT_EQ(0x89,dst[7]);
+ // these bytes should be filled with fail_fill_value 0xde
+ EXPECT_EQ(0xde,dst[8]);
+ EXPECT_EQ(0xde,dst[9]);
+ EXPECT_EQ(0xde,dst[10]);
+ EXPECT_EQ(0xde,dst[11]);
+
+ ASSERT_EQ(false, ex.IsGood());
+ ASSERT_EQ(UINT64_MAX, ex.GetFilePos());
+ ASSERT_EQ(false, ex.Empty());
+ ASSERT_EQ(0u, ex.GetBytesLeft());
+ ASSERT_EQ(0, ex.Peek());
+}
+
+TEST_F (StringExtractorTest, GetHexBytes_Partial)
+{
+ const char kHexEncodedBytes[] = "abcdef0123456789xyzw";
+ const size_t kReadBytes = 4;
+ StringExtractor ex(kHexEncodedBytes);
+
+ uint8_t dst[12];
+ memset(dst, 0xab, sizeof(dst));
+ ASSERT_EQ(kReadBytes, ex.GetHexBytes (dst, kReadBytes, 0xde));
+ EXPECT_EQ(0xab,dst[0]);
+ EXPECT_EQ(0xcd,dst[1]);
+ EXPECT_EQ(0xef,dst[2]);
+ EXPECT_EQ(0x01,dst[3]);
+ // these bytes should be unchanged
+ EXPECT_EQ(0xab,dst[4]);
+ EXPECT_EQ(0xab,dst[5]);
+ EXPECT_EQ(0xab,dst[6]);
+ EXPECT_EQ(0xab,dst[7]);
+ EXPECT_EQ(0xab,dst[8]);
+ EXPECT_EQ(0xab,dst[9]);
+ EXPECT_EQ(0xab,dst[10]);
+ EXPECT_EQ(0xab,dst[11]);
+
+ ASSERT_EQ(true, ex.IsGood());
+ ASSERT_EQ(kReadBytes*2, ex.GetFilePos());
+ ASSERT_EQ(false, ex.Empty());
+ ASSERT_EQ(12u, ex.GetBytesLeft());
+ ASSERT_EQ('2', *ex.Peek());
+}
+
+TEST_F (StringExtractorTest, GetHexBytesAvail)
+{
+ const char kHexEncodedBytes[] = "abcdef0123456789xyzw";
+ const size_t kValidHexPairs = 8;
+ StringExtractor ex(kHexEncodedBytes);
+
+ uint8_t dst[kValidHexPairs];
+ ASSERT_EQ(kValidHexPairs, ex.GetHexBytesAvail (dst, kValidHexPairs));
+ EXPECT_EQ(0xab,dst[0]);
+ EXPECT_EQ(0xcd,dst[1]);
+ EXPECT_EQ(0xef,dst[2]);
+ EXPECT_EQ(0x01,dst[3]);
+ EXPECT_EQ(0x23,dst[4]);
+ EXPECT_EQ(0x45,dst[5]);
+ EXPECT_EQ(0x67,dst[6]);
+ EXPECT_EQ(0x89,dst[7]);
+
+ ASSERT_EQ(true, ex.IsGood());
+ ASSERT_EQ(2*kValidHexPairs, ex.GetFilePos());
+ ASSERT_EQ(false, ex.Empty());
+ ASSERT_EQ(4u, ex.GetBytesLeft());
+ ASSERT_EQ('x', *ex.Peek());
+}
+
+TEST_F (StringExtractorTest, GetHexBytesAvail_Underflow)
+{
+ const char kHexEncodedBytes[] = "abcdef0123456789xyzw";
+ const size_t kValidHexPairs = 8;
+ StringExtractor ex(kHexEncodedBytes);
+
+ uint8_t dst[12];
+ memset(dst, 0xef, sizeof(dst));
+ ASSERT_EQ(kValidHexPairs, ex.GetHexBytesAvail (dst, sizeof(dst)));
+ EXPECT_EQ(0xab,dst[0]);
+ EXPECT_EQ(0xcd,dst[1]);
+ EXPECT_EQ(0xef,dst[2]);
+ EXPECT_EQ(0x01,dst[3]);
+ EXPECT_EQ(0x23,dst[4]);
+ EXPECT_EQ(0x45,dst[5]);
+ EXPECT_EQ(0x67,dst[6]);
+ EXPECT_EQ(0x89,dst[7]);
+ // these bytes should be unchanged
+ EXPECT_EQ(0xef,dst[8]);
+ EXPECT_EQ(0xef,dst[9]);
+ EXPECT_EQ(0xef,dst[10]);
+ EXPECT_EQ(0xef,dst[11]);
+
+ ASSERT_EQ(true, ex.IsGood());
+ ASSERT_EQ(kValidHexPairs*2, ex.GetFilePos());
+ ASSERT_EQ(false, ex.Empty());
+ ASSERT_EQ(4u, ex.GetBytesLeft());
+ ASSERT_EQ('x', *ex.Peek());
+}
+
+TEST_F (StringExtractorTest, GetHexBytesAvail_Partial)
+{
+ const char kHexEncodedBytes[] = "abcdef0123456789xyzw";
+ const size_t kReadBytes = 4;
+ StringExtractor ex(kHexEncodedBytes);
+
+ uint8_t dst[12];
+ memset(dst, 0xab, sizeof(dst));
+ ASSERT_EQ(kReadBytes, ex.GetHexBytesAvail (dst, kReadBytes));
+ EXPECT_EQ(0xab,dst[0]);
+ EXPECT_EQ(0xcd,dst[1]);
+ EXPECT_EQ(0xef,dst[2]);
+ EXPECT_EQ(0x01,dst[3]);
+ // these bytes should be unchanged
+ EXPECT_EQ(0xab,dst[4]);
+ EXPECT_EQ(0xab,dst[5]);
+ EXPECT_EQ(0xab,dst[6]);
+ EXPECT_EQ(0xab,dst[7]);
+ EXPECT_EQ(0xab,dst[8]);
+ EXPECT_EQ(0xab,dst[9]);
+ EXPECT_EQ(0xab,dst[10]);
+ EXPECT_EQ(0xab,dst[11]);
+
+ ASSERT_EQ(true, ex.IsGood());
+ ASSERT_EQ(kReadBytes*2, ex.GetFilePos());
+ ASSERT_EQ(false, ex.Empty());
+ ASSERT_EQ(12u, ex.GetBytesLeft());
+ ASSERT_EQ('2', *ex.Peek());
+}
+
+
diff --git a/unittests/Utility/TaskPoolTest.cpp b/unittests/Utility/TaskPoolTest.cpp
new file mode 100644
index 0000000000000..24431e2c78960
--- /dev/null
+++ b/unittests/Utility/TaskPoolTest.cpp
@@ -0,0 +1,62 @@
+#include "gtest/gtest.h"
+
+#include "lldb/Utility/TaskPool.h"
+
+TEST (TaskPoolTest, AddTask)
+{
+ auto fn = [](int x) { return x * x + 1; };
+
+ auto f1 = TaskPool::AddTask(fn, 1);
+ auto f2 = TaskPool::AddTask(fn, 2);
+ auto f3 = TaskPool::AddTask(fn, 3);
+ auto f4 = TaskPool::AddTask(fn, 4);
+
+ ASSERT_EQ (10, f3.get());
+ ASSERT_EQ ( 2, f1.get());
+ ASSERT_EQ (17, f4.get());
+ ASSERT_EQ ( 5, f2.get());
+}
+
+TEST (TaskPoolTest, RunTasks)
+{
+ std::vector<int> r(4);
+
+ auto fn = [](int x, int& y) { y = x * x + 1; };
+
+ TaskPool::RunTasks(
+ [fn, &r]() { fn(1, r[0]); },
+ [fn, &r]() { fn(2, r[1]); },
+ [fn, &r]() { fn(3, r[2]); },
+ [fn, &r]() { fn(4, r[3]); }
+ );
+
+ ASSERT_EQ ( 2, r[0]);
+ ASSERT_EQ ( 5, r[1]);
+ ASSERT_EQ (10, r[2]);
+ ASSERT_EQ (17, r[3]);
+}
+
+TEST (TaskPoolTest, TaskRunner)
+{
+ auto fn = [](int x) { return std::make_pair(x, x * x); };
+
+ TaskRunner<std::pair<int, int>> tr;
+ tr.AddTask(fn, 1);
+ tr.AddTask(fn, 2);
+ tr.AddTask(fn, 3);
+ tr.AddTask(fn, 4);
+
+ int count = 0;
+ while (true)
+ {
+ auto f = tr.WaitForNextCompletedTask();
+ if (!f.valid())
+ break;
+
+ ++count;
+ std::pair<int, int> v = f.get();
+ ASSERT_EQ (v.first * v.first, v.second);
+ }
+
+ ASSERT_EQ(4, count);
+}
diff --git a/unittests/Utility/UriParserTest.cpp b/unittests/Utility/UriParserTest.cpp
new file mode 100644
index 0000000000000..fe0a6a70f2122
--- /dev/null
+++ b/unittests/Utility/UriParserTest.cpp
@@ -0,0 +1,159 @@
+#include "gtest/gtest.h"
+#include "Utility/UriParser.h"
+
+namespace
+{
+ class UriParserTest: public ::testing::Test
+ {
+ };
+}
+
+// result strings (scheme/hostname/port/path) passed into UriParser::Parse
+// are initialized to kAsdf so we can verify that they are unmodified if the
+// URI is invalid
+static const char* kAsdf = "asdf";
+
+class UriTestCase
+{
+public:
+ UriTestCase(const char* uri, const char* scheme, const char* hostname, int port, const char* path) :
+ m_uri(uri),
+ m_result(true),
+ m_scheme(scheme),
+ m_hostname(hostname),
+ m_port(port),
+ m_path(path)
+ {
+ }
+
+ UriTestCase(const char* uri) :
+ m_uri(uri),
+ m_result(false),
+ m_scheme(kAsdf),
+ m_hostname(kAsdf),
+ m_port(1138),
+ m_path(kAsdf)
+ {
+ }
+
+ const char* m_uri;
+ bool m_result;
+ const char* m_scheme;
+ const char* m_hostname;
+ int m_port;
+ const char* m_path;
+};
+
+#define VALIDATE \
+ std::string scheme(kAsdf); \
+ std::string hostname(kAsdf); \
+ int port(1138); \
+ std::string path(kAsdf); \
+ EXPECT_EQ (testCase.m_result, UriParser::Parse(testCase.m_uri, scheme, hostname, port, path)); \
+ EXPECT_STREQ (testCase.m_scheme, scheme.c_str()); \
+ EXPECT_STREQ (testCase.m_hostname, hostname.c_str()); \
+ EXPECT_EQ (testCase.m_port, port); \
+ EXPECT_STREQ (testCase.m_path, path.c_str());
+
+TEST_F (UriParserTest, Minimal)
+{
+ const UriTestCase testCase("x://y", "x", "y", -1, "/");
+ VALIDATE
+}
+
+TEST_F (UriParserTest, MinimalPort)
+{
+ const UriTestCase testCase("x://y:1", "x", "y", 1, "/");
+ VALIDATE
+}
+
+TEST_F (UriParserTest, MinimalPath)
+{
+ const UriTestCase testCase("x://y/", "x", "y", -1, "/");
+ VALIDATE
+}
+
+TEST_F (UriParserTest, MinimalPortPath)
+{
+ const UriTestCase testCase("x://y:1/", "x", "y", 1, "/");
+ VALIDATE
+}
+
+TEST_F (UriParserTest, LongPath)
+{
+ const UriTestCase testCase("x://y/abc/def/xyz", "x", "y", -1, "/abc/def/xyz");
+ VALIDATE
+}
+
+TEST_F (UriParserTest, TypicalPortPath)
+{
+ const UriTestCase testCase("connect://192.168.100.132:5432/", "connect", "192.168.100.132", 5432, "/");
+ VALIDATE
+}
+
+TEST_F (UriParserTest, BracketedHostnamePort)
+{
+ const UriTestCase testCase("connect://[192.168.100.132]:5432/", "connect", "192.168.100.132", 5432, "/");
+ VALIDATE
+}
+
+TEST_F (UriParserTest, BracketedHostname)
+{
+ const UriTestCase testCase("connect://[192.168.100.132]", "connect", "192.168.100.132", -1, "/");
+ VALIDATE
+}
+
+TEST_F (UriParserTest, BracketedHostnameWithColon)
+{
+ const UriTestCase testCase("connect://[192.168.100.132:5555]:1234", "connect", "192.168.100.132:5555", 1234, "/");
+ VALIDATE
+}
+
+TEST_F (UriParserTest, SchemeHostSeparator)
+{
+ const UriTestCase testCase("x:/y");
+ VALIDATE
+}
+
+TEST_F (UriParserTest, SchemeHostSeparator2)
+{
+ const UriTestCase testCase("x:y");
+ VALIDATE
+}
+
+TEST_F (UriParserTest, SchemeHostSeparator3)
+{
+ const UriTestCase testCase("x//y");
+ VALIDATE
+}
+
+TEST_F (UriParserTest, SchemeHostSeparator4)
+{
+ const UriTestCase testCase("x/y");
+ VALIDATE
+}
+
+TEST_F (UriParserTest, BadPort)
+{
+ const UriTestCase testCase("x://y:a/");
+ VALIDATE
+}
+
+TEST_F (UriParserTest, BadPort2)
+{
+ const UriTestCase testCase("x://y:5432a/");
+ VALIDATE
+}
+
+TEST_F (UriParserTest, Empty)
+{
+ const UriTestCase testCase("");
+ VALIDATE
+}
+
+TEST_F (UriParserTest, PortOverflow)
+{
+ const UriTestCase testCase("x://y:0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789/");
+ VALIDATE
+}
+
diff --git a/unittests/gtest_common.h b/unittests/gtest_common.h
new file mode 100644
index 0000000000000..006c9596ca4f5
--- /dev/null
+++ b/unittests/gtest_common.h
@@ -0,0 +1,24 @@
+//===-- gtest_common.h ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(LLDB_GTEST_COMMON_H)
+#error "gtest_common.h should not be included manually."
+#else
+#define LLDB_GTEST_COMMON_H
+#endif
+
+// This header file is force included by all of LLDB's unittest compilation
+// units. Be very leary about putting anything in this file.
+
+#if defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0)
+// Due to a bug in <thread>, when _HAS_EXCEPTIONS == 0 the header will try to call
+// uncaught_exception() without having a declaration for it. The fix for this is
+// to manually #include <eh.h>, which contains this declaration.
+#include <eh.h>
+#endif