| Index: base/threading/thread_unittest.cc | 
| diff --git a/base/threading/thread_unittest.cc b/base/threading/thread_unittest.cc | 
| index dfa04c6ac48fce2e445daccb4e14911bda951757..52ba3a891a77a8170a45a0c3821f2874aa34acf0 100644 | 
| --- a/base/threading/thread_unittest.cc | 
| +++ b/base/threading/thread_unittest.cc | 
| @@ -9,14 +9,26 @@ | 
| #include <vector> | 
|  | 
| #include "base/bind.h" | 
| -#include "base/location.h" | 
| +#include "base/macros.h" | 
| +#include "base/message_loop/message_loop.h" | 
| +#include "base/run_loop.h" | 
| #include "base/single_thread_task_runner.h" | 
| #include "base/synchronization/waitable_event.h" | 
| #include "base/third_party/dynamic_annotations/dynamic_annotations.h" | 
| +#include "base/threading/platform_thread.h" | 
| #include "build/build_config.h" | 
| #include "testing/gtest/include/gtest/gtest.h" | 
| #include "testing/platform_test.h" | 
|  | 
| +// Death tests misbehave on Android. | 
| +// TODO(gab): Remove this when https://codereview.chromium.org/2162053006 | 
| +// lands. | 
| +#if DCHECK_IS_ON() && defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID) | 
| +#define EXPECT_DCHECK_DEATH(statement, regex) EXPECT_DEATH(statement, regex) | 
| +#else | 
| +#define EXPECT_DCHECK_DEATH(statement, regex) | 
| +#endif | 
| + | 
| using base::Thread; | 
|  | 
| typedef PlatformTest ThreadTest; | 
| @@ -43,8 +55,11 @@ class SleepInsideInitThread : public Thread { | 
| init_called_ = true; | 
| } | 
| bool InitCalled() { return init_called_; } | 
| + | 
| private: | 
| bool init_called_; | 
| + | 
| +  DISALLOW_COPY_AND_ASSIGN(SleepInsideInitThread); | 
| }; | 
|  | 
| enum ThreadEvent { | 
| @@ -81,6 +96,8 @@ class CaptureToEventList : public Thread { | 
|  | 
| private: | 
| EventList* event_list_; | 
| + | 
| +  DISALLOW_COPY_AND_ASSIGN(CaptureToEventList); | 
| }; | 
|  | 
| // Observer that writes a value into |event_list| when a message loop has been | 
| @@ -101,6 +118,8 @@ class CapturingDestructionObserver | 
|  | 
| private: | 
| EventList* event_list_; | 
| + | 
| +  DISALLOW_COPY_AND_ASSIGN(CapturingDestructionObserver); | 
| }; | 
|  | 
| // Task that adds a destruction observer to the current message loop. | 
| @@ -164,10 +183,47 @@ TEST_F(ThreadTest, StartWithOptions_StackSize) { | 
| event.Wait(); | 
| } | 
|  | 
| -TEST_F(ThreadTest, TwoTasks) { | 
| +TEST_F(ThreadTest, StartWithOptions_NonJoinable) { | 
| +  Thread a("StartNonJoinable"); | 
| +  Thread::Options options; | 
| +  options.joinable = false; | 
| +  EXPECT_TRUE(a.StartWithOptions(options)); | 
| +  EXPECT_TRUE(a.message_loop()); | 
| +  EXPECT_TRUE(a.IsRunning()); | 
| + | 
| +  // Without this call this test is racy. The above IsRunning() succeeds because | 
| +  // of an early-return condition while between Start() and Stop(), after | 
| +  // invoking Stop() below this early-return condition is no longer satisfied | 
| +  // and the real |is_running_| bit has to be checked. It could still be false | 
| +  // if the message loop hasn't started for real in practice. This is only a | 
| +  // requirement for this test because the non-joinable thread makes it possible | 
| +  // to return from Stop() before the thread has even started. | 
| +  EXPECT_TRUE(a.WaitUntilThreadStarted()); | 
| + | 
| +  // Make the thread block until |block_event| is signaled. | 
| +  base::WaitableEvent block_event( | 
| +      base::WaitableEvent::ResetPolicy::AUTOMATIC, | 
| +      base::WaitableEvent::InitialState::NOT_SIGNALED); | 
| +  a.task_runner()->PostTask( | 
| +      FROM_HERE, | 
| +      base::Bind(&base::WaitableEvent::Wait, base::Unretained(&block_event))); | 
| + | 
| +  // Stop() shouldn't block despite the thread still being alive. | 
| +  a.Stop(); | 
| +  EXPECT_TRUE(a.IsRunning()); | 
| + | 
| +  // Unblock the task and give a bit of extra time to unwind QuitWhenIdle(). | 
| +  block_event.Signal(); | 
| +  base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20)); | 
| + | 
| +  // The thread should now have stopped on its own. | 
| +  EXPECT_FALSE(a.IsRunning()); | 
| +} | 
| + | 
| +TEST_F(ThreadTest, TwoTasksOnJoinableThread) { | 
| bool was_invoked = false; | 
| { | 
| -    Thread a("TwoTasks"); | 
| +    Thread a("TwoTasksOnJoinableThread"); | 
| EXPECT_TRUE(a.Start()); | 
| EXPECT_TRUE(a.message_loop()); | 
|  | 
| @@ -231,6 +287,39 @@ TEST_F(ThreadTest, StartTwice) { | 
| EXPECT_FALSE(a.IsRunning()); | 
| } | 
|  | 
| +TEST_F(ThreadTest, StartTwiceNonJoinableNotAllowed) { | 
| +  Thread a("StartTwiceNonJoinable"); | 
| + | 
| +  Thread::Options options; | 
| +  options.joinable = false; | 
| +  EXPECT_TRUE(a.StartWithOptions(options)); | 
| +  EXPECT_TRUE(a.message_loop()); | 
| +  EXPECT_TRUE(a.IsRunning()); | 
| + | 
| +  // Signaled when last task on |a| is processed. | 
| +  base::WaitableEvent last_task_event( | 
| +      base::WaitableEvent::ResetPolicy::AUTOMATIC, | 
| +      base::WaitableEvent::InitialState::NOT_SIGNALED); | 
| +  a.task_runner()->PostTask(FROM_HERE, | 
| +                            base::Bind(&base::WaitableEvent::Signal, | 
| +                                       base::Unretained(&last_task_event))); | 
| + | 
| +  // Stop() is non-blocking, but Yield() to |a|, wait for last task to be | 
| +  // processed and a little more for QuitWhenIdle() to unwind before considering | 
| +  // the thread "stopped". | 
| +  a.Stop(); | 
| +  base::PlatformThread::YieldCurrentThread(); | 
| +  last_task_event.Wait(); | 
| +  base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20)); | 
| + | 
| +  // This test assumes that the above was sufficient to let the thread fully | 
| +  // stop. | 
| +  ASSERT_FALSE(a.IsRunning()); | 
| + | 
| +  // Restarting it should not be allowed. | 
| +  EXPECT_DCHECK_DEATH(a.Start(), ""); | 
| +} | 
| + | 
| TEST_F(ThreadTest, ThreadName) { | 
| Thread a("ThreadName"); | 
| EXPECT_TRUE(a.Start()); | 
| @@ -332,3 +421,49 @@ TEST_F(ThreadTest, MultipleWaitUntilThreadStarted) { | 
| EXPECT_TRUE(a.WaitUntilThreadStarted()); | 
| EXPECT_TRUE(a.WaitUntilThreadStarted()); | 
| } | 
| + | 
| +namespace { | 
| + | 
| +// A Thread which uses a MessageLoop on the stack. It won't start a real | 
| +// underlying thread (instead its messages can be processed by a RunLoop on the | 
| +// stack). | 
| +class ExternalMessageLoopThread : public Thread { | 
| + public: | 
| +  ExternalMessageLoopThread() : Thread("ExternalMessageLoopThread") {} | 
| + | 
| +  ~ExternalMessageLoopThread() override { Stop(); } | 
| + | 
| +  void InstallMessageLoop() { SetMessageLoop(&external_message_loop_); } | 
| + | 
| + private: | 
| +  base::MessageLoop external_message_loop_; | 
| + | 
| +  DISALLOW_COPY_AND_ASSIGN(ExternalMessageLoopThread); | 
| +}; | 
| + | 
| +}  // namespace | 
| + | 
| +TEST_F(ThreadTest, ExternalMessageLoop) { | 
| +  ExternalMessageLoopThread a; | 
| +  EXPECT_FALSE(a.message_loop()); | 
| +  EXPECT_FALSE(a.IsRunning()); | 
| + | 
| +  a.InstallMessageLoop(); | 
| +  EXPECT_TRUE(a.message_loop()); | 
| +  EXPECT_TRUE(a.IsRunning()); | 
| + | 
| +  bool ran = false; | 
| +  a.task_runner()->PostTask( | 
| +      FROM_HERE, base::Bind([](bool* toggled) { *toggled = true; }, &ran)); | 
| +  base::RunLoop().RunUntilIdle(); | 
| +  EXPECT_TRUE(ran); | 
| + | 
| +  a.Stop(); | 
| +  EXPECT_FALSE(a.message_loop()); | 
| +  EXPECT_FALSE(a.IsRunning()); | 
| + | 
| +  // Confirm that running any remaining tasks posted from Stop() goes smoothly | 
| +  // (e.g. https://codereview.chromium.org/2135413003/#ps300001 crashed if | 
| +  // StopSoon() posted Thread::ThreadQuitHelper() while |run_loop_| was null). | 
| +  base::RunLoop().RunUntilIdle(); | 
| +} | 
|  |