summaryrefslogtreecommitdiffstats
path: root/lldb/unittests
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/unittests')
-rw-r--r--lldb/unittests/CMakeLists.txt23
-rw-r--r--lldb/unittests/Host/CMakeLists.txt5
-rw-r--r--lldb/unittests/Host/SocketAddressTest.cpp77
-rw-r--r--lldb/unittests/Host/SocketTest.cpp132
-rw-r--r--lldb/unittests/Host/SocketTestMock.cpp64
-rw-r--r--lldb/unittests/Plugins/CMakeLists.txt1
-rw-r--r--lldb/unittests/Plugins/Process/CMakeLists.txt3
-rw-r--r--lldb/unittests/Plugins/Process/Linux/CMakeLists.txt4
-rw-r--r--lldb/unittests/Plugins/Process/Linux/Makefile19
-rw-r--r--lldb/unittests/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp821
-rw-r--r--lldb/unittests/Plugins/Process/Linux/ThreadStateCoordinatorTestMock.cpp32
-rw-r--r--lldb/unittests/Utility/CMakeLists.txt4
-rw-r--r--lldb/unittests/Utility/StringExtractorTest.cpp406
-rw-r--r--lldb/unittests/Utility/UriParserTest.cpp135
-rw-r--r--lldb/unittests/gtest_common.h32
15 files changed, 1758 insertions, 0 deletions
diff --git a/lldb/unittests/CMakeLists.txt b/lldb/unittests/CMakeLists.txt
new file mode 100644
index 00000000000..e9b37700a85
--- /dev/null
+++ b/lldb/unittests/CMakeLists.txt
@@ -0,0 +1,23 @@
+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 ()
+
+# add_lldb_unittest(test_dirname file1.cpp file2.cpp)
+#
+# Will compile the list of files together and link against the liblldb
+function(add_lldb_unittest test_name)
+ add_unittest(LLDBUnitTests ${test_name} ${ARGN})
+ target_link_libraries(${test_name} liblldb)
+endfunction()
+
+add_subdirectory(Host)
+add_subdirectory(Plugins)
+add_subdirectory(Utility)
diff --git a/lldb/unittests/Host/CMakeLists.txt b/lldb/unittests/Host/CMakeLists.txt
new file mode 100644
index 00000000000..227e48c1fd9
--- /dev/null
+++ b/lldb/unittests/Host/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_lldb_unittest(HostTests
+ SocketAddressTest.cpp
+ SocketTest.cpp
+ SocketTestMock.cpp
+ )
diff --git a/lldb/unittests/Host/SocketAddressTest.cpp b/lldb/unittests/Host/SocketAddressTest.cpp
new file mode 100644
index 00000000000..bd6bda13f44
--- /dev/null
+++ b/lldb/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/lldb/unittests/Host/SocketTest.cpp b/lldb/unittests/Host/SocketTest.cpp
new file mode 100644
index 00000000000..deed222c8c1
--- /dev/null
+++ b/lldb/unittests/Host/SocketTest.cpp
@@ -0,0 +1,132 @@
+//===-- SocketTest.cpp ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <thread>
+
+#include "gtest/gtest.h"
+
+#include "lldb/Host/Socket.h"
+
+class SocketTest: public ::testing::Test
+{
+};
+
+using namespace lldb_private;
+
+void AcceptThread (Socket* listen_socket,
+ const char* listen_remote_address,
+ bool child_processes_inherit,
+ Socket** accept_socket,
+ Error* error)
+{
+ *error = listen_socket->BlockingAccept (listen_remote_address, child_processes_inherit, *accept_socket);
+}
+
+void CreateConnectedSockets (std::unique_ptr<Socket>* a_up, std::unique_ptr<Socket>* b_up)
+{
+ Predicate<uint16_t> port_predicate;
+ // Used when binding to port zero to wait for the thread
+ // that creates the socket, binds and listens to resolve
+ // the port number.
+
+ port_predicate.SetValue (0, eBroadcastNever);
+
+ bool child_processes_inherit = false;
+ Socket *socket = nullptr;
+ const char* listen_remote_address = "localhost:0";
+ Error error = Socket::TcpListen (listen_remote_address, child_processes_inherit, socket, &port_predicate);
+ std::unique_ptr<Socket> listen_socket_up (socket);
+ socket = nullptr;
+ EXPECT_FALSE (error.Fail ());
+ EXPECT_NE (nullptr, listen_socket_up.get ());
+ 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);
+
+ char connect_remote_address[64];
+ snprintf (connect_remote_address, sizeof (connect_remote_address), "localhost:%u", port_predicate.GetValue ());
+ error = Socket::TcpConnect (connect_remote_address, child_processes_inherit, socket);
+ a_up->reset (socket);
+ socket = nullptr;
+ EXPECT_TRUE (error.Success ());
+ EXPECT_NE (nullptr, a_up->get ());
+ EXPECT_TRUE ((*a_up)->IsValid ());
+
+ accept_thread.join ();
+ b_up->reset (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 ());
+
+}
+
+TEST_F (SocketTest, Listen0ConnectAccept)
+{
+ std::unique_ptr<Socket> socket_a_up;
+ std::unique_ptr<Socket> socket_b_up;
+ CreateConnectedSockets (&socket_a_up, &socket_b_up);
+}
+
+TEST_F (SocketTest, GetAddress)
+{
+ std::unique_ptr<Socket> socket_a_up;
+ std::unique_ptr<Socket> socket_b_up;
+ CreateConnectedSockets (&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 ());
+}
+
+
+
diff --git a/lldb/unittests/Host/SocketTestMock.cpp b/lldb/unittests/Host/SocketTestMock.cpp
new file mode 100644
index 00000000000..d457c5161d3
--- /dev/null
+++ b/lldb/unittests/Host/SocketTestMock.cpp
@@ -0,0 +1,64 @@
+//===-- SocketTestMock.cpp --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// This file provides a few necessary functions to link SocketTest.cpp
+// Bringing in the real implementations results in a cascade of dependencies
+// that pull in all of lldb.
+
+#include "lldb/Core/Log.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+using namespace lldb_private;
+
+void
+lldb_private::Log::Error (char const*, ...)
+{
+}
+
+void
+lldb_private::Log::Printf (char const*, ...)
+{
+}
+
+Log*
+lldb_private::GetLogIfAnyCategoriesSet (unsigned int)
+{
+ return nullptr;
+}
+
+#include "lldb/Host/FileSystem.h"
+
+#ifdef _WIN32
+
+Error
+FileSystem::Unlink(const char *path)
+{
+ Error error;
+ BOOL result = ::DeleteFile(path);
+ if (!result)
+ error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
+ return error;
+}
+
+#else
+
+Error
+FileSystem::Unlink (const char *path)
+{
+ Error error;
+ if (::unlink (path) == -1)
+ error.SetErrorToErrno ();
+ return error;
+}
+
+#endif
+
diff --git a/lldb/unittests/Plugins/CMakeLists.txt b/lldb/unittests/Plugins/CMakeLists.txt
new file mode 100644
index 00000000000..422f203f227
--- /dev/null
+++ b/lldb/unittests/Plugins/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(Process)
diff --git a/lldb/unittests/Plugins/Process/CMakeLists.txt b/lldb/unittests/Plugins/Process/CMakeLists.txt
new file mode 100644
index 00000000000..bf3426c02d0
--- /dev/null
+++ b/lldb/unittests/Plugins/Process/CMakeLists.txt
@@ -0,0 +1,3 @@
+if (CMAKE_SYSTEM_NAME MATCHES "Linux")
+ add_subdirectory(Linux)
+endif()
diff --git a/lldb/unittests/Plugins/Process/Linux/CMakeLists.txt b/lldb/unittests/Plugins/Process/Linux/CMakeLists.txt
new file mode 100644
index 00000000000..3d71f9a463a
--- /dev/null
+++ b/lldb/unittests/Plugins/Process/Linux/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_lldb_unittest(ProcessLinuxTests
+ ThreadStateCoordinatorTest.cpp
+ ThreadStatecoordinatorMock.cpp
+ )
diff --git a/lldb/unittests/Plugins/Process/Linux/Makefile b/lldb/unittests/Plugins/Process/Linux/Makefile
new file mode 100644
index 00000000000..6817352888c
--- /dev/null
+++ b/lldb/unittests/Plugins/Process/Linux/Makefile
@@ -0,0 +1,19 @@
+THIS_FILE_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))/
+
+LEVEL := $(realpath $(THIS_FILE_DIR)../../../../make)
+
+CFLAGS_EXTRAS := -D__STDC_LIMIT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_CONSTANT_MACROS
+ENABLE_THREADS := YES
+CXX_SOURCES := $(wildcard *.cpp) \
+ $(realpath $(LEVEL)/../../source/Plugins/Process/Linux/ThreadStateCoordinator.cpp) \
+ $(realpath $(LEVEL)/../../source/Core/Error.cpp)
+MAKE_DSYM := NO
+
+OS := $(shell uname -s)
+
+# $(info OS $(OS))
+ifeq ($(OS),Linux)
+ LD_EXTRAS := -lncurses -ldl
+endif
+
+include $(LEVEL)/Makefile.rules
diff --git a/lldb/unittests/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp b/lldb/unittests/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp
new file mode 100644
index 00000000000..9e7cc8eb4ca
--- /dev/null
+++ b/lldb/unittests/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp
@@ -0,0 +1,821 @@
+#include <limits.h>
+#include "gtest/gtest.h"
+
+#include "lldb/Core/Error.h"
+#include "Plugins/Process/Linux/ThreadStateCoordinator.h"
+
+using namespace lldb_private;
+
+namespace
+{
+ const ThreadStateCoordinator::ThreadIDSet EMPTY_THREAD_ID_SET;
+
+ void
+ NOPLogger (const char *format, va_list args)
+ {
+ // Do nothing.
+ }
+
+ void
+ StdoutLogger (const char *format, va_list args)
+ {
+ // Print to stdout.
+ vprintf (format, args);
+ printf ("\n");
+ }
+
+ // These are single-line macros so that IDE integration of gtest results puts
+ // the error markers on the correct failure point within the gtest.
+
+#define ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS() ASSERT_EQ (ThreadStateCoordinator::eventLoopResultContinue, m_coordinator.ProcessNextEvent ()); \
+if (HasError ()) { printf ("unexpected error in processing of event, error: %s\n", m_error_string.c_str ()); } \
+ASSERT_EQ (false, HasError ());
+
+#define ASSERT_PROCESS_NEXT_EVENT_FAILS() ASSERT_EQ (ThreadStateCoordinator::eventLoopResultContinue, m_coordinator.ProcessNextEvent ()); \
+ASSERT_EQ (true, HasError ());
+
+ class ThreadStateCoordinatorTest: public ::testing::Test
+ {
+ protected:
+ // Constants.
+ const lldb::tid_t TRIGGERING_TID = 4105;
+ const lldb::tid_t PENDING_STOP_TID = 3;
+ const lldb::tid_t PENDING_STOP_TID_02 = 29016;
+ const lldb::tid_t NEW_THREAD_TID = 1234;
+
+ // Member variables.
+ bool m_error_called = false;
+ std::string m_error_string;
+
+ ThreadStateCoordinator m_coordinator;
+
+ bool m_deferred_notification_called;
+ lldb::tid_t m_deferred_notification_tid;
+
+ ThreadStateCoordinator::ThreadIDSet m_requested_stop_tids;
+
+ // Constructors.
+ ThreadStateCoordinatorTest () :
+ m_error_called (false),
+ m_error_string (),
+ m_coordinator (StdoutLogger),
+ m_deferred_notification_called (false),
+ m_deferred_notification_tid (0),
+ m_requested_stop_tids ()
+ {
+ }
+
+ // Member functions.
+
+ // Error handling.
+ ThreadStateCoordinator::ErrorFunction
+ GetErrorFunction ()
+ {
+ return [this] (const std::string &error_string)
+ {
+ m_error_called = true;
+ m_error_string = error_string;
+ printf ("received error: %s (test might be expecting)\n", error_string.c_str ());
+ };
+ }
+
+ bool
+ HasError () const
+ {
+ return m_error_called;
+ }
+
+ // Deferred notification reception.
+ ThreadStateCoordinator::ThreadIDFunction
+ GetDeferredStopNotificationFunction ()
+ {
+ return [this] (lldb::tid_t triggered_tid)
+ {
+ m_deferred_notification_called = true;
+ m_deferred_notification_tid = triggered_tid;
+ };
+ }
+
+ bool
+ DidFireDeferredNotification () const
+ {
+ return m_deferred_notification_called;
+ }
+
+ lldb::tid_t
+ GetDeferredNotificationTID () const
+ {
+ return m_deferred_notification_tid;
+ }
+
+ // Stop request call reception.
+ ThreadStateCoordinator::StopThreadFunction
+ GetStopRequestFunction ()
+ {
+ return [this] (lldb::tid_t stop_tid)
+ {
+ m_requested_stop_tids.insert (stop_tid);
+ return Error();
+ };
+ }
+
+ ThreadStateCoordinator::ThreadIDSet::size_type
+ GetRequestedStopCount () const
+ {
+ return m_requested_stop_tids.size();
+ }
+
+ ThreadStateCoordinator::ResumeThreadFunction
+ GetResumeThreadFunction (lldb::tid_t& resumed_tid, int& resume_call_count)
+ {
+ return [this, &resumed_tid, &resume_call_count] (lldb::tid_t tid, bool)
+ {
+ resumed_tid = tid;
+ ++resume_call_count;
+ return Error();
+ };
+ }
+
+ bool
+ DidRequestStopForTid (lldb::tid_t tid)
+ {
+ return m_requested_stop_tids.find (tid) != m_requested_stop_tids.end ();
+ }
+
+ // Test state initialization helpers.
+ void
+ SetupKnownRunningThread (lldb::tid_t tid)
+ {
+ NotifyThreadCreate (tid, false);
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+ }
+
+ void
+ SetupKnownStoppedThread (lldb::tid_t tid)
+ {
+ NotifyThreadCreate (tid, true);
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+ }
+
+ // Convenience wrappers for ThreadStateCoordinator, using defaults for expected arguments
+ // that plug into the test case handlers.
+ void
+ CallAfterThreadsStop (lldb::tid_t deferred_tid,
+ const ThreadStateCoordinator::ThreadIDSet &pending_stop_wait_tids)
+ {
+ m_coordinator.CallAfterThreadsStop (deferred_tid,
+ pending_stop_wait_tids,
+ GetStopRequestFunction (),
+ GetDeferredStopNotificationFunction (),
+ GetErrorFunction ());
+ }
+
+ void
+ CallAfterRunningThreadsStop (lldb::tid_t deferred_tid)
+ {
+ m_coordinator.CallAfterRunningThreadsStop (deferred_tid,
+ GetStopRequestFunction (),
+ GetDeferredStopNotificationFunction (),
+ GetErrorFunction ());
+ }
+
+ void
+ NotifyThreadCreate (lldb::tid_t stopped_tid, bool thread_is_stopped)
+ {
+ m_coordinator.NotifyThreadCreate (stopped_tid, thread_is_stopped, GetErrorFunction ());
+ }
+
+ void
+ NotifyThreadStop (lldb::tid_t stopped_tid)
+ {
+ m_coordinator.NotifyThreadStop (stopped_tid, false, GetErrorFunction ());
+ }
+
+ void
+ NotifyThreadDeath (lldb::tid_t tid)
+ {
+ m_coordinator.NotifyThreadDeath (tid, GetErrorFunction ());
+ }
+ };
+}
+
+TEST_F (ThreadStateCoordinatorTest, StopCoordinatorWorksNoPriorEvents)
+{
+ m_coordinator.StopCoordinator ();
+ ASSERT_EQ (ThreadStateCoordinator::eventLoopResultStop, m_coordinator.ProcessNextEvent ());
+ ASSERT_EQ (false, HasError ());
+}
+
+TEST_F (ThreadStateCoordinatorTest, NotifyThreadCreateSignalsErrorOnAlreadyKnownThread)
+{
+ // Let the coordinator know about our thread.
+ SetupKnownStoppedThread (TRIGGERING_TID);
+
+ // Notify the thread was created - again.
+ NotifyThreadCreate (TRIGGERING_TID, true);
+
+ // This should error out.
+ ASSERT_PROCESS_NEXT_EVENT_FAILS ();
+}
+
+
+TEST_F (ThreadStateCoordinatorTest, NotifyThreadDeathSignalsErrorOnUnknownThread)
+{
+ const lldb::tid_t UNKNOWN_TID = 678;
+
+ // Notify an unknown thread has died.
+ NotifyThreadDeath (UNKNOWN_TID);
+
+ // This should error out.
+ ASSERT_PROCESS_NEXT_EVENT_FAILS ();
+}
+
+TEST_F (ThreadStateCoordinatorTest, NotifyThreadStopSignalsErrorOnUnknownThread)
+{
+ const lldb::tid_t UNKNOWN_TID = 678;
+
+ // Notify an unknown thread has stopped.
+ NotifyThreadStop (UNKNOWN_TID);
+ ASSERT_PROCESS_NEXT_EVENT_FAILS ();
+}
+
+TEST_F (ThreadStateCoordinatorTest, CallAfterTheadsStopSignalsErrorOnUnknownDeferredThread)
+{
+ const lldb::tid_t UNKNOWN_TRIGGER_TID = 678;
+
+ // Defer notify for an unknown thread.
+ CallAfterThreadsStop (UNKNOWN_TRIGGER_TID,
+ EMPTY_THREAD_ID_SET);
+
+ // Shouldn't have fired yet.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+
+ // Event should fail because trigger tid is unknown.
+ ASSERT_PROCESS_NEXT_EVENT_FAILS ();
+
+ // Shouldn't have fired due to error.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+}
+
+TEST_F (ThreadStateCoordinatorTest, CallAfterTheadsStopSignalsErrorOnUnknownPendingStopThread)
+{
+ // Let the coordinator know about our thread.
+ SetupKnownStoppedThread (TRIGGERING_TID);
+
+ // Defer notify for an unknown thread.
+ const lldb::tid_t UNKNOWN_PENDING_STOP_TID = 7890;
+ ThreadStateCoordinator::ThreadIDSet pending_stop_tids { UNKNOWN_PENDING_STOP_TID };
+
+ CallAfterThreadsStop (TRIGGERING_TID,
+ pending_stop_tids);
+
+ // Shouldn't have fired yet.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+
+ // Event should fail because trigger tid is unknown.
+ ASSERT_PROCESS_NEXT_EVENT_FAILS ();
+
+ // Shouldn't have triggered deferred notification due to error.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+
+ // Shouldn't have triggered stop request due to unknown tid.
+ ASSERT_EQ (0, GetRequestedStopCount ());
+}
+
+TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenNoPendingStops)
+{
+ // Let the coordinator know about our thread.
+ SetupKnownStoppedThread (TRIGGERING_TID);
+
+ // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
+ CallAfterThreadsStop (TRIGGERING_TID,
+ EMPTY_THREAD_ID_SET);
+
+ // Notification trigger shouldn't go off yet.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+
+ // Process next event. This will pick up the call after threads stop event.
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+
+ // Now the trigger should have fired, since there were no threads that needed to first stop.
+ ASSERT_EQ (true, DidFireDeferredNotification ());
+ ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
+}
+
+TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenOnePendingStop)
+{
+ // Let the coordinator know about our thread.
+ SetupKnownStoppedThread (TRIGGERING_TID);
+
+ // Let the coordinator know about a currently-running thread we'll wait on.
+ SetupKnownRunningThread (PENDING_STOP_TID);
+
+ ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_STOP_TID };
+
+ // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
+ CallAfterThreadsStop (TRIGGERING_TID,
+ pending_stop_tids);
+
+ // Neither trigger should have gone off yet.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+ ASSERT_EQ (false, DidRequestStopForTid (PENDING_STOP_TID));
+
+ // Process next event.
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+
+ // Now the request thread stop should have been called for the pending stop.
+ ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID));
+
+ // But we still shouldn't have the deferred signal call go off yet. Need to wait for the stop to be reported.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+
+ // Now report the that the pending stop occurred.
+ NotifyThreadStop (PENDING_STOP_TID);
+
+ // Shouldn't take effect until after next processing step.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+
+ // Process next event.
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+
+ // Deferred signal notification should have fired now.
+ ASSERT_EQ (true, DidFireDeferredNotification ());
+ ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
+}
+
+TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenTwoPendingStops)
+{
+ // Setup threads.
+ SetupKnownStoppedThread (TRIGGERING_TID);
+ SetupKnownRunningThread (PENDING_STOP_TID);
+ SetupKnownRunningThread (PENDING_STOP_TID_02);
+
+ ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_STOP_TID, PENDING_STOP_TID_02 };
+
+ // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
+ CallAfterThreadsStop (TRIGGERING_TID,
+ pending_stop_tids);
+
+ // Neither trigger should have gone off yet.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+ ASSERT_EQ (0, GetRequestedStopCount ());
+
+ // Process next event.
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+
+ // Now the request thread stops should have been called for the pending stop tids.
+ ASSERT_EQ (2, GetRequestedStopCount ());
+ ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID));
+ ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID_02));
+
+ // But we still shouldn't have the deferred signal call go off yet. Need to wait for the stop to be reported.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+
+ // Report the that the first pending stop occurred.
+ NotifyThreadStop (PENDING_STOP_TID);
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+
+ // Shouldn't take effect until after both pending threads are notified.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+
+ // Report the that the first pending stop occurred.
+ NotifyThreadStop (PENDING_STOP_TID_02);
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+
+ // Deferred signal notification should have fired now.
+ ASSERT_EQ (true, DidFireDeferredNotification ());
+ ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
+}
+
+TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenPendingAlreadyStopped)
+{
+ // Setup threads.
+ SetupKnownStoppedThread (TRIGGERING_TID);
+ SetupKnownRunningThread (PENDING_STOP_TID);
+
+ ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_STOP_TID };
+
+ // Tell m_coordinator the pending stop tid is already stopped.
+ NotifyThreadStop (PENDING_STOP_TID);
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+
+ // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
+ CallAfterThreadsStop (TRIGGERING_TID,
+ pending_stop_tids);
+
+ // Neither trigger should have gone off yet.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+ ASSERT_EQ (false, DidRequestStopForTid (PENDING_STOP_TID));
+
+ // Process next event.
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+
+ // The pending stop should *not* fire because the m_coordinator knows it has already stopped.
+ ASSERT_EQ (false, DidRequestStopForTid (PENDING_STOP_TID));
+
+ // The deferred signal notification should have fired since all requirements were met.
+ ASSERT_EQ (true, DidFireDeferredNotification ());
+ ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
+}
+
+TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenTwoPendingOneAlreadyStopped)
+{
+ SetupKnownStoppedThread (TRIGGERING_TID);
+ SetupKnownRunningThread (PENDING_STOP_TID);
+ SetupKnownRunningThread (PENDING_STOP_TID_02);
+
+ ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_STOP_TID, PENDING_STOP_TID_02 };
+
+ // Tell coordinator the pending stop tid is already stopped.
+ NotifyThreadStop (PENDING_STOP_TID);
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+
+ // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
+ CallAfterThreadsStop (TRIGGERING_TID,
+ pending_stop_tids);
+
+ // Neither trigger should have gone off yet.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+ ASSERT_EQ (0, GetRequestedStopCount ());
+
+ // Process next event.
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+
+ // The pending stop should only fire for one of the threads, the one that wasn't already stopped.
+ ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID_02));
+ ASSERT_EQ (false, DidRequestStopForTid (PENDING_STOP_TID));
+
+ // The deferred signal notification should not yet have fired since all pending thread stops have not yet occurred.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+
+ // Notify final thread has stopped.
+ NotifyThreadStop (PENDING_STOP_TID_02);
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+
+ // The deferred signal notification should have fired since all requirements were met.
+ ASSERT_EQ (true, DidFireDeferredNotification ());
+ ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
+}
+
+TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenOnePendingThreadDies)
+{
+ SetupKnownStoppedThread (TRIGGERING_TID);
+ SetupKnownRunningThread (PENDING_STOP_TID);
+
+ ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_STOP_TID };
+
+ // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
+ CallAfterThreadsStop (TRIGGERING_TID,
+ pending_stop_tids);
+
+ // Neither trigger should have gone off yet.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+ ASSERT_EQ (0, GetRequestedStopCount ());
+
+ // Process next event.
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+
+ // Now the request thread stop should have been called for the pending stop.
+ ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID));
+
+ // But we still shouldn't have the deferred signal call go off yet. Need to wait for the death to be reported.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+
+ // Now report the that the thread with pending stop dies.
+ NotifyThreadDeath (PENDING_STOP_TID);
+
+ // Shouldn't take effect until after next processing step.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+
+ // Process next event.
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+
+ // Deferred signal notification should have fired now.
+ ASSERT_EQ (true, DidFireDeferredNotification ());
+ ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
+}
+
+TEST_F (ThreadStateCoordinatorTest, ExistingPendingNotificationRequiresStopFromNewThread)
+{
+ SetupKnownStoppedThread (TRIGGERING_TID);
+ SetupKnownRunningThread (PENDING_STOP_TID);
+
+ ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_STOP_TID };
+
+ // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
+ CallAfterThreadsStop (TRIGGERING_TID,
+ pending_stop_tids);
+
+ // Neither trigger should have gone off yet.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+ ASSERT_EQ (0, GetRequestedStopCount ());
+
+ // Process next event.
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+
+ // Now the request thread stop should have been called for the pending stop.
+ ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID));
+
+ // But we still shouldn't have the deferred signal call go off yet.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+
+ // Indicate a new thread has just been created.
+ SetupKnownRunningThread (NEW_THREAD_TID);
+
+ // We should have just received a stop request for the new thread id.
+ ASSERT_EQ (2, GetRequestedStopCount ());
+ ASSERT_EQ (true, DidRequestStopForTid (NEW_THREAD_TID));
+
+ // Now report the original pending tid stopped. This should no longer
+ // trigger the pending notification because we should now require the
+ // new thread to stop too.
+ NotifyThreadStop (PENDING_STOP_TID);
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+
+ // Now notify the new thread stopped.
+ NotifyThreadStop (NEW_THREAD_TID);
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+
+ // Deferred signal notification should have fired now.
+ ASSERT_EQ (true, DidFireDeferredNotification ());
+ ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
+}
+
+TEST_F (ThreadStateCoordinatorTest, DeferredNotificationRemovedByResetForExec)
+{
+ SetupKnownStoppedThread (TRIGGERING_TID);
+
+ // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
+ CallAfterThreadsStop (TRIGGERING_TID,
+ EMPTY_THREAD_ID_SET);
+
+ // Notification trigger shouldn't go off yet.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+
+ // Now indicate an exec occurred, which will invalidate all state about the process and threads.
+ m_coordinator.ResetForExec ();
+
+ // Verify the deferred stop notification does *not* fire with the next
+ // process. It will handle the reset and not the deferred signaling, which
+ // should now be removed.
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+}
+
+TEST_F (ThreadStateCoordinatorTest, RequestThreadResumeSignalsErrorOnUnknownThread)
+{
+ const lldb::tid_t UNKNOWN_TID = 411;
+
+ // Request a resume.
+ lldb::tid_t resumed_tid = 0;
+ int resume_call_count = 0;
+
+ m_coordinator.RequestThreadResume (UNKNOWN_TID,
+ GetResumeThreadFunction(resumed_tid, resume_call_count),
+ GetErrorFunction ());
+ // Shouldn't be called yet.
+ ASSERT_EQ (0, resume_call_count);
+
+ // Process next event. This should fail since the coordinator doesn't know about the thread.
+ ASSERT_PROCESS_NEXT_EVENT_FAILS ();
+ ASSERT_EQ (0, resume_call_count);
+}
+
+TEST_F (ThreadStateCoordinatorTest, RequestThreadResumeCallsCallbackWhenThreadIsStopped)
+{
+ // Initialize thread to be in stopped state.
+ SetupKnownStoppedThread (NEW_THREAD_TID);
+
+ // Request a resume.
+ lldb::tid_t resumed_tid = 0;
+ int resume_call_count = 0;
+
+ m_coordinator.RequestThreadResume (NEW_THREAD_TID,
+ GetResumeThreadFunction(resumed_tid, resume_call_count),
+ GetErrorFunction ());
+ // Shouldn't be called yet.
+ ASSERT_EQ (0, resume_call_count);
+
+ // Process next event. After that, the resume request call should have fired.
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+ ASSERT_EQ (1, resume_call_count);
+ ASSERT_EQ (NEW_THREAD_TID, resumed_tid);
+}
+
+TEST_F (ThreadStateCoordinatorTest, RequestThreadResumeSkipsCallbackOnSecondResumeAttempt)
+{
+ // Initialize thread to be in stopped state.
+ SetupKnownStoppedThread (NEW_THREAD_TID);
+
+ // Request a resume.
+ lldb::tid_t resumed_tid = 0;
+ int resume_call_count = 0;
+
+ m_coordinator.RequestThreadResume (NEW_THREAD_TID,
+ GetResumeThreadFunction(resumed_tid, resume_call_count),
+ GetErrorFunction ());
+ // Shouldn't be called yet.
+ ASSERT_EQ (0, resume_call_count);
+
+ // Process next event. After that, the resume request call should have fired.
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+ ASSERT_EQ (1, resume_call_count);
+ ASSERT_EQ (NEW_THREAD_TID, resumed_tid);
+
+ // Make a second resume request.
+ const int initial_resume_call_count = resume_call_count;
+ m_coordinator.RequestThreadResume (NEW_THREAD_TID,
+ GetResumeThreadFunction(resumed_tid, resume_call_count),
+ GetErrorFunction ());
+
+ // Process next event. This should fail since the thread should already be running.
+ ASSERT_PROCESS_NEXT_EVENT_FAILS ();
+
+ // And the resume count should not have increased.
+ ASSERT_EQ (initial_resume_call_count, resume_call_count);
+}
+
+TEST_F (ThreadStateCoordinatorTest, RequestThreadResumeSignalsErrorOnAlreadyRunningThread)
+{
+ const lldb::tid_t TEST_TID = 1234;
+ SetupKnownRunningThread (NEW_THREAD_TID);
+
+ // Request a resume.
+ lldb::tid_t resumed_tid = 0;
+ int resume_call_count = 0;
+
+ m_coordinator.RequestThreadResume (TEST_TID,
+ GetResumeThreadFunction(resumed_tid, resume_call_count),
+ GetErrorFunction ());
+
+ // Shouldn't be called yet.
+ ASSERT_EQ (0, resume_call_count);
+
+ // Process next event. Should be an error.
+ ASSERT_PROCESS_NEXT_EVENT_FAILS ();
+
+ // The resume request should not have gone off because we think it is already running.
+ ASSERT_EQ (0, resume_call_count);
+}
+
+TEST_F (ThreadStateCoordinatorTest, ResumedThreadAlreadyMarkedDoesNotHoldUpPendingStopNotification)
+{
+ // We're going to test this scenario:
+ // * Deferred notification waiting on two threads, A and B. A and B currently running.
+ // * Thread A stops.
+ // * Thread A resumes.
+ // * Thread B stops.
+ //
+ // Here we could have forced A to stop again (after the Thread A resumes) because we had a pending stop nofication awaiting
+ // all those threads to stop. However, we are going to explicitly not try to restop A - somehow
+ // that seems wrong and possibly buggy since for that to happen, we would have intentionally called
+ // a resume after the stop. Instead, we'll just log and indicate this looks suspicous. We can revisit
+ // that decision after we see if/when that happens.
+ const lldb::tid_t PENDING_TID_A = 2;
+ const lldb::tid_t PENDING_TID_B = 89;
+
+ SetupKnownStoppedThread (TRIGGERING_TID);
+ SetupKnownRunningThread (PENDING_TID_A);
+ SetupKnownRunningThread (PENDING_TID_B);
+
+ ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_TID_A, PENDING_TID_B };
+
+ // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
+ CallAfterThreadsStop (TRIGGERING_TID,
+ pending_stop_tids);
+
+ // Neither trigger should have gone off yet.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+ ASSERT_EQ (0, GetRequestedStopCount ());
+
+ // Execute CallAfterThreadsStop.
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+
+ // Both TID A and TID B should have had stop requests made.
+ ASSERT_EQ (2, GetRequestedStopCount ());
+ ASSERT_EQ (true, DidRequestStopForTid (PENDING_TID_A));
+ ASSERT_EQ (true, DidRequestStopForTid (PENDING_TID_B));
+
+ // But we still shouldn't have the deferred signal call go off yet.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+
+ // Report thread A stopped.
+ NotifyThreadStop (PENDING_TID_A);
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+
+ // Now report thread A is resuming. Ensure the resume is called.
+ lldb::tid_t resumed_tid = 0;
+ int resume_call_count = 0;
+ m_coordinator.RequestThreadResume (PENDING_TID_A,
+ GetResumeThreadFunction(resumed_tid, resume_call_count),
+ GetErrorFunction ());
+ ASSERT_EQ (0, resume_call_count);
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+ ASSERT_EQ (1, resume_call_count);
+ ASSERT_EQ (PENDING_TID_A, resumed_tid);
+
+ // Report thread B stopped.
+ NotifyThreadStop (PENDING_TID_B);
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+
+ // After notifying thread b stopped, we now have thread a resumed but thread b stopped.
+ // However, since thread a had stopped, we now have had both requirements stopped at some point.
+ // For now we'll expect this will fire the pending deferred stop notification.
+ ASSERT_EQ (true, DidFireDeferredNotification ());
+ ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
+}
+
+TEST_F (ThreadStateCoordinatorTest, CallAfterRunningThreadsStopFiresWhenNoRunningThreads)
+{
+ // Let the coordinator know about our thread.
+ SetupKnownStoppedThread (TRIGGERING_TID);
+
+ // Notify we have a trigger that needs to be fired when all running threads have stopped.
+ CallAfterRunningThreadsStop (TRIGGERING_TID);
+
+ // Notification trigger shouldn't go off yet.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+
+ // Process next event. This will pick up the call after threads stop event.
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+
+ // Now the trigger should have fired, since there were no threads that needed to first stop.
+ ASSERT_EQ (true, DidFireDeferredNotification ());
+ ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
+
+ // And no stop requests should have been made.
+ ASSERT_EQ (0, GetRequestedStopCount ());
+}
+
+TEST_F (ThreadStateCoordinatorTest, CallAfterRunningThreadsStopRequestsTwoPendingStops)
+{
+ // Let the coordinator know about our threads.
+ SetupKnownStoppedThread (TRIGGERING_TID);
+ SetupKnownRunningThread (PENDING_STOP_TID);
+ SetupKnownRunningThread (PENDING_STOP_TID_02);
+
+ // Notify we have a trigger that needs to be fired when all running threads have stopped.
+ CallAfterRunningThreadsStop (TRIGGERING_TID);
+
+ // Notification trigger shouldn't go off yet.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+
+ // Process next event. This will pick up the call after threads stop event.
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+
+ // We should have two stop requests for the two threads currently running.
+ ASSERT_EQ (2, GetRequestedStopCount ());
+ ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID));
+ ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID_02));
+
+ // But the deferred stop notification should not have fired yet.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+
+ // Now notify the two threads stopped.
+ NotifyThreadStop (PENDING_STOP_TID);
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+
+ NotifyThreadStop (PENDING_STOP_TID_02);
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+
+ // Now the trigger should have fired, since there were no threads that needed to first stop.
+ ASSERT_EQ (true, DidFireDeferredNotification ());
+ ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
+}
+
+TEST_F (ThreadStateCoordinatorTest, CallAfterRunningThreadsStopRequestsStopTwoOtherThreadsOneRunning)
+{
+ // Let the coordinator know about our threads. PENDING_STOP_TID_02 will already be stopped.
+ SetupKnownStoppedThread (TRIGGERING_TID);
+ SetupKnownRunningThread (PENDING_STOP_TID);
+ SetupKnownStoppedThread (PENDING_STOP_TID_02);
+
+ // Notify we have a trigger that needs to be fired when all running threads have stopped.
+ CallAfterRunningThreadsStop (TRIGGERING_TID);
+
+ // Notification trigger shouldn't go off yet.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+
+ // Process next event. This will pick up the call after threads stop event.
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+
+ // We should have two stop requests for the two threads currently running.
+ ASSERT_EQ (1, GetRequestedStopCount ());
+ ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID));
+
+ // But the deferred stop notification should not have fired yet.
+ ASSERT_EQ (false, DidFireDeferredNotification ());
+
+ // Now notify the two threads stopped.
+ NotifyThreadStop (PENDING_STOP_TID);
+ ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
+
+ // Now the trigger should have fired, since there were no threads that needed to first stop.
+ ASSERT_EQ (true, DidFireDeferredNotification ());
+ ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
+}
diff --git a/lldb/unittests/Plugins/Process/Linux/ThreadStateCoordinatorTestMock.cpp b/lldb/unittests/Plugins/Process/Linux/ThreadStateCoordinatorTestMock.cpp
new file mode 100644
index 00000000000..73693609b15
--- /dev/null
+++ b/lldb/unittests/Plugins/Process/Linux/ThreadStateCoordinatorTestMock.cpp
@@ -0,0 +1,32 @@
+//===-- ThreadStateCoordinatorTestMock.cpp ----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// This file provides a few necessary functions to link
+// ThreadStateCoordinatorTest.cpp Bringing in the real implementations results
+// in a cascade of dependencies that pull in all of lldb.
+
+#include "lldb/Core/Log.h"
+
+using namespace lldb_private;
+
+void
+lldb_private::Log::Error (char const*, ...)
+{
+}
+
+void
+lldb_private::Log::Printf (char const*, ...)
+{
+}
+
+Log*
+lldb_private::GetLogIfAnyCategoriesSet (unsigned int)
+{
+ return nullptr;
+}
diff --git a/lldb/unittests/Utility/CMakeLists.txt b/lldb/unittests/Utility/CMakeLists.txt
new file mode 100644
index 00000000000..fea0f53fdd9
--- /dev/null
+++ b/lldb/unittests/Utility/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_lldb_unittest(UtilityTests
+ StringExtractorTest.cpp
+ UriParserTest.cpp
+ )
diff --git a/lldb/unittests/Utility/StringExtractorTest.cpp b/lldb/unittests/Utility/StringExtractorTest.cpp
new file mode 100644
index 00000000000..cbb3816b79b
--- /dev/null
+++ b/lldb/unittests/Utility/StringExtractorTest.cpp
@@ -0,0 +1,406 @@
+#include <limits.h>
+#include "gtest/gtest.h"
+
+#include "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 (0, ex.GetFilePos());
+ ASSERT_STREQ (kEmptyString, ex.GetStringRef().c_str());
+ ASSERT_EQ (true, ex.Empty());
+ ASSERT_EQ (0, 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 (0, 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 (0, ex.GetFilePos());
+ ASSERT_EQ (true, ex.Empty());
+ ASSERT_EQ (0, 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 (0, ex.GetFilePos());
+ ASSERT_EQ (1, 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 (0, ex.GetFilePos());
+ ASSERT_EQ (2, 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 (0, ex.GetFilePos());
+ ASSERT_EQ (2, 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 (2, ex.GetFilePos());
+ ASSERT_EQ (0, 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 (2, ex.GetFilePos());
+ ASSERT_EQ (2, 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 (0, 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 (0, 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 (0, 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 (2, ex.GetFilePos());
+ ASSERT_EQ (0, 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 (2, ex.GetFilePos());
+ ASSERT_EQ (2, 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 (0, 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 (0, ex.GetFilePos());
+ ASSERT_EQ (1, 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 (0, ex.GetFilePos());
+ ASSERT_EQ (2, 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 (2, ex.GetFilePos());
+ ASSERT_EQ (0, 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 (2, ex.GetFilePos());
+ ASSERT_EQ (2, 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(4, 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(0, 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(12, 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(4, 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(4, 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(12, ex.GetBytesLeft());
+ ASSERT_EQ('2', *ex.Peek());
+}
+
+
diff --git a/lldb/unittests/Utility/UriParserTest.cpp b/lldb/unittests/Utility/UriParserTest.cpp
new file mode 100644
index 00000000000..a11dd8e12b0
--- /dev/null
+++ b/lldb/unittests/Utility/UriParserTest.cpp
@@ -0,0 +1,135 @@
+#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, TypicalPortPath)
+{
+ const UriTestCase testCase("connect://192.168.100.132:5432/", "connect", "192.168.100.132", 5432, "/");
+ 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/lldb/unittests/gtest_common.h b/lldb/unittests/gtest_common.h
new file mode 100644
index 00000000000..228dfb0d328
--- /dev/null
+++ b/lldb/unittests/gtest_common.h
@@ -0,0 +1,32 @@
+//===-- 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)
+// MSVC's STL implementation tries to work well with /EHs-c- and
+// _HAS_EXCEPTIONS=0. But <thread> in particular doesn't work with it, because
+// it relies on <concrt.h> which tries to throw an exception without checking
+// for _HAS_EXCEPTIONS=0. This causes the linker to require a definition of
+// __uncaught_exception(), but the STL doesn't define this function when
+// _HAS_EXCEPTIONS=0. The workaround here is to just provide a stub
+// implementation to get it to link.
+inline bool
+__uncaught_exception()
+{
+ return true;
+}
+#endif
OpenPOWER on IntegriCloud