blob: 12fe846b98b3c75511c23b888d7f0d587741e21e [file] [log] [blame]
// Copyright (c) 2020 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 "chrome/services/sharing/nearby/platform_v2/condition_variable.h"
#include "base/bind.h"
#include "base/containers/flat_set.h"
#include "base/run_loop.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
#include "base/task_runner.h"
#include "base/test/bind_test_util.h"
#include "base/test/gtest_util.h"
#include "base/test/task_environment.h"
#include "base/threading/thread_restrictions.h"
#include "base/unguessable_token.h"
#include "chrome/services/sharing/nearby/platform_v2/mutex.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace location {
namespace nearby {
namespace chrome {
class ConditionVariableTest : public testing::Test {
protected:
void WaitOnConditionVariableFromParallelSequence(
base::RunLoop& run_loop,
const base::UnguessableToken& attempt_id) {
base::RunLoop wait_run_loop;
auto callback = base::BindLambdaForTesting([&]() {
base::ScopedAllowBaseSyncPrimitivesForTesting allow_sync_primitives;
wait_run_loop.Quit();
mutex_.Lock();
condition_variable_.Wait();
mutex_.Unlock();
{
base::AutoLock al(coordination_lock_);
successful_run_attempts_.insert(attempt_id);
}
run_loop.Quit();
});
task_runner_->PostTask(FROM_HERE, std::move(callback));
// Wait until callback has started.
wait_run_loop.Run();
}
bool HasSuccessfullyRunWithAttemptId(
const base::UnguessableToken& attempt_id) {
base::AutoLock al(coordination_lock_);
return base::Contains(successful_run_attempts_, attempt_id);
}
void NotifyConditionVariable() {
mutex_.Lock();
condition_variable_.Notify();
mutex_.Unlock();
}
base::test::TaskEnvironment task_environment_;
private:
Mutex mutex_;
ConditionVariable condition_variable_{&mutex_};
scoped_refptr<base::TaskRunner> task_runner_ =
base::ThreadPool::CreateTaskRunner({base::MayBlock()});
base::Lock coordination_lock_;
base::flat_set<base::UnguessableToken> successful_run_attempts_;
};
TEST_F(ConditionVariableTest, SingleSequence_BlocksOnWaitAndUnblocksOnNotify) {
base::RunLoop run_loop;
base::UnguessableToken attempt_id = base::UnguessableToken::Create();
WaitOnConditionVariableFromParallelSequence(run_loop, attempt_id);
ASSERT_FALSE(HasSuccessfullyRunWithAttemptId(attempt_id));
// Should unblock after notify().
NotifyConditionVariable();
run_loop.Run();
EXPECT_TRUE(HasSuccessfullyRunWithAttemptId(attempt_id));
}
TEST_F(ConditionVariableTest,
MultipleSequences_BlocksOnWaitAndUnblocksOnNotify) {
base::RunLoop run_loop_1;
base::UnguessableToken attempt_id_1 = base::UnguessableToken::Create();
WaitOnConditionVariableFromParallelSequence(run_loop_1, attempt_id_1);
base::RunLoop run_loop_2;
base::UnguessableToken attempt_id_2 = base::UnguessableToken::Create();
WaitOnConditionVariableFromParallelSequence(run_loop_2, attempt_id_2);
base::RunLoop run_loop_3;
base::UnguessableToken attempt_id_3 = base::UnguessableToken::Create();
WaitOnConditionVariableFromParallelSequence(run_loop_3, attempt_id_3);
ASSERT_FALSE(HasSuccessfullyRunWithAttemptId(attempt_id_1));
ASSERT_FALSE(HasSuccessfullyRunWithAttemptId(attempt_id_2));
ASSERT_FALSE(HasSuccessfullyRunWithAttemptId(attempt_id_3));
// All should unblock after notify().
NotifyConditionVariable();
run_loop_1.Run();
run_loop_2.Run();
run_loop_3.Run();
EXPECT_TRUE(HasSuccessfullyRunWithAttemptId(attempt_id_1));
EXPECT_TRUE(HasSuccessfullyRunWithAttemptId(attempt_id_2));
EXPECT_TRUE(HasSuccessfullyRunWithAttemptId(attempt_id_3));
}
} // namespace chrome
} // namespace nearby
} // namespace location