/* * 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 #include #include #include #include #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 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 threads; vector 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 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 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 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 unique_lk(lock); cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; }); EXPECT_TRUE(triggerCalled); } TEST(MultiConditionTrigger, TestTriggerOnlyCalledOnce) { string t1 = "t1"; set 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 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 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