You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
175 lines
4.5 KiB
175 lines
4.5 KiB
7 months ago
|
/*
|
||
|
* 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<int> done(numConditions, 0);
|
||
|
|
||
|
int i = 0;
|
||
|
for (const string& conditionName : conditionNames) {
|
||
|
threads.emplace_back([&done, &conditionName, &trigger, i] {
|
||
|
sleep_for(chrono::milliseconds(3));
|
||
|
done[i] = 1;
|
||
|
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
|