diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2016-01-06 20:12:03 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2016-01-06 20:12:03 +0000 |
commit | 9e6d35490a6542f9c97607f93c2ef8ca8e03cbcc (patch) | |
tree | dd2a1ddf0476664c2b823409c36cbccd52662ca7 /unittests | |
parent | 3bd2e91faeb9eeec1aae82c64a3253afff551cfd (diff) |
Notes
Diffstat (limited to 'unittests')
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 |