| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stdarg.h> |
| #include <string.h> |
| |
| #include "base/android/path_utils.h" |
| #include "base/files/file_path.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/memory/singleton.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/message_loop/message_pump_android.h" |
| #include "base/path_service.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/test/multiprocess_test.h" |
| #include "starboard/types.h" |
| |
| namespace { |
| |
| base::FilePath* g_test_data_dir = nullptr; |
| |
| struct RunState { |
| RunState(base::MessagePump::Delegate* delegate, int run_depth) |
| : delegate(delegate), |
| run_depth(run_depth), |
| should_quit(false) { |
| } |
| |
| base::MessagePump::Delegate* delegate; |
| |
| // Used to count how many Run() invocations are on the stack. |
| int run_depth; |
| |
| // Used to flag that the current Run() invocation should return ASAP. |
| bool should_quit; |
| }; |
| |
| RunState* g_state = nullptr; |
| |
| // A singleton WaitableEvent wrapper so we avoid a busy loop in |
| // MessagePumpForUIStub. Other platforms use the native event loop which blocks |
| // when there are no pending messages. |
| class Waitable { |
| public: |
| static Waitable* GetInstance() { |
| return base::Singleton<Waitable, |
| base::LeakySingletonTraits<Waitable>>::get(); |
| } |
| |
| // Signals that there are more work to do. |
| void Signal() { waitable_event_.Signal(); } |
| |
| // Blocks until more work is scheduled. |
| void Block() { waitable_event_.Wait(); } |
| |
| void Quit() { |
| g_state->should_quit = true; |
| Signal(); |
| } |
| |
| private: |
| friend struct base::DefaultSingletonTraits<Waitable>; |
| |
| Waitable() |
| : waitable_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| base::WaitableEvent::InitialState::NOT_SIGNALED) {} |
| |
| base::WaitableEvent waitable_event_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Waitable); |
| }; |
| |
| // The MessagePumpForUI implementation for test purpose. |
| class MessagePumpForUIStub : public base::MessagePumpForUI { |
| public: |
| MessagePumpForUIStub() : base::MessagePumpForUI() { Waitable::GetInstance(); } |
| ~MessagePumpForUIStub() override {} |
| |
| bool IsTestImplementation() const override { return true; } |
| |
| // In tests, there isn't a native thread, as such RunLoop::Run() should be |
| // used to run the loop instead of attaching and delegating to the native |
| // loop. As such, this override ignores the Attach() request. |
| void Attach(base::MessagePump::Delegate* delegate) override {} |
| |
| void Run(base::MessagePump::Delegate* delegate) override { |
| // The following was based on message_pump_glib.cc, except we're using a |
| // WaitableEvent since there are no native message loop to use. |
| RunState state(delegate, g_state ? g_state->run_depth + 1 : 1); |
| |
| RunState* previous_state = g_state; |
| g_state = &state; |
| |
| // When not nested we can use the real implementation, otherwise fall back |
| // to the stub implementation. |
| if (g_state->run_depth > 1) { |
| RunNested(delegate); |
| } else { |
| MessagePumpForUI::Run(delegate); |
| } |
| |
| g_state = previous_state; |
| } |
| |
| void RunNested(base::MessagePump::Delegate* delegate) { |
| bool more_work_is_plausible = true; |
| |
| for (;;) { |
| if (!more_work_is_plausible) { |
| Waitable::GetInstance()->Block(); |
| if (g_state->should_quit) |
| break; |
| } |
| |
| more_work_is_plausible = g_state->delegate->DoWork(); |
| if (g_state->should_quit) |
| break; |
| |
| base::TimeTicks delayed_work_time; |
| more_work_is_plausible |= |
| g_state->delegate->DoDelayedWork(&delayed_work_time); |
| if (g_state->should_quit) |
| break; |
| |
| if (more_work_is_plausible) |
| continue; |
| |
| more_work_is_plausible = g_state->delegate->DoIdleWork(); |
| if (g_state->should_quit) |
| break; |
| |
| more_work_is_plausible |= !delayed_work_time.is_null(); |
| } |
| } |
| |
| void Quit() override { |
| CHECK(g_state); |
| if (g_state->run_depth > 1) { |
| Waitable::GetInstance()->Quit(); |
| } else { |
| MessagePumpForUI::Quit(); |
| } |
| } |
| |
| void ScheduleWork() override { |
| if (g_state && g_state->run_depth > 1) { |
| Waitable::GetInstance()->Signal(); |
| } else { |
| MessagePumpForUI::ScheduleWork(); |
| } |
| } |
| |
| void ScheduleDelayedWork(const base::TimeTicks& delayed_work_time) override { |
| if (g_state && g_state->run_depth > 1) { |
| Waitable::GetInstance()->Signal(); |
| } else { |
| MessagePumpForUI::ScheduleDelayedWork(delayed_work_time); |
| } |
| } |
| }; |
| |
| std::unique_ptr<base::MessagePump> CreateMessagePumpForUIStub() { |
| return std::unique_ptr<base::MessagePump>(new MessagePumpForUIStub()); |
| }; |
| |
| // Provides the test path for DIR_SOURCE_ROOT and DIR_ANDROID_APP_DATA. |
| bool GetTestProviderPath(int key, base::FilePath* result) { |
| switch (key) { |
| // TODO(agrieve): Stop overriding DIR_ANDROID_APP_DATA. |
| // https://crbug.com/617734 |
| // Instead DIR_ASSETS should be used to discover assets file location in |
| // tests. |
| case base::DIR_ANDROID_APP_DATA: |
| case base::DIR_ASSETS: |
| case base::DIR_SOURCE_ROOT: |
| CHECK(g_test_data_dir != nullptr); |
| *result = *g_test_data_dir; |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| void InitPathProvider(int key) { |
| base::FilePath path; |
| // If failed to override the key, that means the way has not been registered. |
| if (GetTestProviderPath(key, &path) && |
| !base::PathService::Override(key, path)) { |
| base::PathService::RegisterProvider(&GetTestProviderPath, key, key + 1); |
| } |
| } |
| |
| } // namespace |
| |
| namespace base { |
| |
| void InitAndroidTestLogging() { |
| logging::LoggingSettings settings; |
| settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; |
| logging::InitLogging(settings); |
| // To view log output with IDs and timestamps use "adb logcat -v threadtime". |
| logging::SetLogItems(false, // Process ID |
| false, // Thread ID |
| false, // Timestamp |
| false); // Tick count |
| } |
| |
| void InitAndroidTestPaths(const FilePath& test_data_dir) { |
| if (g_test_data_dir) { |
| CHECK(test_data_dir == *g_test_data_dir); |
| return; |
| } |
| g_test_data_dir = new FilePath(test_data_dir); |
| InitPathProvider(DIR_SOURCE_ROOT); |
| InitPathProvider(DIR_ANDROID_APP_DATA); |
| InitPathProvider(DIR_ASSETS); |
| } |
| |
| void InitAndroidTestMessageLoop() { |
| if (!MessageLoop::InitMessagePumpForUIFactory(&CreateMessagePumpForUIStub)) |
| LOG(INFO) << "MessagePumpForUIFactory already set, unable to override."; |
| } |
| |
| } // namespace base |