blob: 825553e8cb656eb0a6afab64989e62e2b640cbd2 [file] [log] [blame]
// 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 "components/sync/engine/ui_model_worker.h"
#include <memory>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/location.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/test_timeouts.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace syncer {
namespace {
// Makes a Closure into a WorkCallback.
// Does |work| and checks that we're on the |thread_verifier| thread.
SyncerError DoWork(
const scoped_refptr<base::SingleThreadTaskRunner>& thread_verifier,
base::Closure work) {
DCHECK(thread_verifier->BelongsToCurrentThread());
work.Run();
return SyncerError(SyncerError::SYNCER_OK);
}
// Converts |work| to a WorkCallback that will verify that it's run on the
// thread it was constructed on.
WorkCallback ClosureToWorkCallback(base::Closure work) {
return base::BindOnce(&DoWork, base::ThreadTaskRunnerHandle::Get(), work);
}
// Increments |counter|.
void IncrementCounter(int* counter) {
++*counter;
}
class SyncUIModelWorkerTest : public testing::Test {
public:
SyncUIModelWorkerTest() : sync_thread_("SyncThreadForTest") {
sync_thread_.Start();
worker_ = new UIModelWorker(base::ThreadTaskRunnerHandle::Get());
}
void PostWorkToSyncThread(base::Closure work) {
sync_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(base::IgnoreResult(
&UIModelWorker::DoWorkAndWaitUntilDone),
worker_, ClosureToWorkCallback(work)));
}
protected:
std::unique_ptr<base::test::ScopedTaskEnvironment> task_environment_ =
std::make_unique<base::test::ScopedTaskEnvironment>();
base::Thread sync_thread_;
scoped_refptr<UIModelWorker> worker_;
};
} // namespace
TEST_F(SyncUIModelWorkerTest, ScheduledWorkRunsOnUILoop) {
base::RunLoop run_loop;
PostWorkToSyncThread(run_loop.QuitClosure());
// This won't quit until the QuitClosure is run.
run_loop.Run();
}
TEST_F(SyncUIModelWorkerTest, MultipleDoWork) {
constexpr int kNumWorkCallbacks = 10;
int counter = 0;
for (int i = 0; i < kNumWorkCallbacks; ++i) {
PostWorkToSyncThread(
base::Bind(&IncrementCounter, base::Unretained(&counter)));
}
base::RunLoop run_loop;
PostWorkToSyncThread(run_loop.QuitClosure());
run_loop.Run();
EXPECT_EQ(kNumWorkCallbacks, counter);
}
TEST_F(SyncUIModelWorkerTest, JoinSyncThreadAfterUIMessageLoopDestruction) {
PostWorkToSyncThread(base::DoNothing());
// Wait to allow the sync thread to post the WorkCallback to the UI
// MessageLoop. This is racy. If the WorkCallback isn't posted fast enough,
// this test doesn't verify that UIModelWorker behaves properly when the UI
// MessageLoop is destroyed. However, it doesn't fail (no flakes).
base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
// The sync thread shouldn't wait for the WorkCallback to run on the UI thread
// after the UI MessageLoop is gone.
task_environment_.reset();
sync_thread_.Stop();
}
TEST_F(SyncUIModelWorkerTest, JoinSyncThreadAfterRequestStop) {
PostWorkToSyncThread(base::DoNothing());
// Wait to allow the sync thread to post the WorkCallback to the UI
// MessageLoop. This is racy. If the WorkCallback isn't posted fast enough,
// this test doesn't verify that UIModelWorker behaves properly when
// RequestStop() is called. However, it doesn't fail (no flakes).
base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
// The sync thread shouldn't wait for the WorkCallback to run on the UI thread
// after RequestStop() is called.
worker_->RequestStop();
sync_thread_.Stop();
}
} // namespace syncer