blob: db402a0dd658a2c2f630ac9089de97cb4bd60a53 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "utils/MultiConditionTrigger.h"
#include <gtest/gtest.h>
#include <chrono>
#include <set>
#include <thread>
#include <vector>
#ifdef __ANDROID__
using namespace std;
using std::this_thread::sleep_for;
namespace android {
namespace os {
namespace statsd {
TEST(MultiConditionTrigger, TestMultipleConditions) {
int numConditions = 5;
string t1 = "t1", t2 = "t2", t3 = "t3", t4 = "t4", t5 = "t5";
set<string> conditionNames = {t1, t2, t3, t4, t5};
mutex lock;
condition_variable cv;
bool triggerCalled = false;
// Mark done as true and notify in the done.
MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled] {
{
lock_guard lg(lock);
triggerCalled = true;
}
cv.notify_all();
});
vector<thread> threads;
vector<bool> done(numConditions, false);
int i = 0;
for (const string& conditionName : conditionNames) {
threads.emplace_back([&done, &conditionName, &trigger, i] {
sleep_for(chrono::milliseconds(3));
done[i] = true;
trigger.markComplete(conditionName);
});
i++;
}
unique_lock<mutex> unique_lk(lock);
cv.wait(unique_lk, [&triggerCalled] {
return triggerCalled;
});
for (i = 0; i < numConditions; i++) {
EXPECT_EQ(done[i], 1);
}
for (i = 0; i < numConditions; i++) {
threads[i].join();
}
}
TEST(MultiConditionTrigger, TestNoConditions) {
mutex lock;
condition_variable cv;
bool triggerCalled = false;
MultiConditionTrigger trigger({}, [&lock, &cv, &triggerCalled] {
{
lock_guard lg(lock);
triggerCalled = true;
}
cv.notify_all();
});
unique_lock<mutex> unique_lk(lock);
cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; });
EXPECT_TRUE(triggerCalled);
// Ensure that trigger occurs immediately if no events need to be completed.
}
TEST(MultiConditionTrigger, TestMarkCompleteCalledBySameCondition) {
string t1 = "t1", t2 = "t2";
set<string> conditionNames = {t1, t2};
mutex lock;
condition_variable cv;
bool triggerCalled = false;
MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled] {
{
lock_guard lg(lock);
triggerCalled = true;
}
cv.notify_all();
});
trigger.markComplete(t1);
trigger.markComplete(t1);
// Ensure that the trigger still hasn't fired.
{
lock_guard lg(lock);
EXPECT_FALSE(triggerCalled);
}
trigger.markComplete(t2);
unique_lock<mutex> unique_lk(lock);
cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; });
EXPECT_TRUE(triggerCalled);
}
TEST(MultiConditionTrigger, TestTriggerOnlyCalledOnce) {
string t1 = "t1";
set<string> conditionNames = {t1};
mutex lock;
condition_variable cv;
bool triggerCalled = false;
int triggerCount = 0;
MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled, &triggerCount] {
{
lock_guard lg(lock);
triggerCount++;
triggerCalled = true;
}
cv.notify_all();
});
trigger.markComplete(t1);
// Ensure that the trigger fired.
{
unique_lock<mutex> unique_lk(lock);
cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; });
EXPECT_TRUE(triggerCalled);
EXPECT_EQ(triggerCount, 1);
triggerCalled = false;
}
trigger.markComplete(t1);
// Ensure that the trigger does not fire again.
{
unique_lock<mutex> unique_lk(lock);
cv.wait_for(unique_lk, chrono::milliseconds(5), [&triggerCalled] { return triggerCalled; });
EXPECT_FALSE(triggerCalled);
EXPECT_EQ(triggerCount, 1);
}
}
} // namespace statsd
} // namespace os
} // namespace android
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif