1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "utils/MultiConditionTrigger.h"
17 
18 #include <gtest/gtest.h>
19 
20 #include <chrono>
21 #include <set>
22 #include <thread>
23 #include <vector>
24 
25 #ifdef __ANDROID__
26 
27 using namespace std;
28 using std::this_thread::sleep_for;
29 
30 namespace android {
31 namespace os {
32 namespace statsd {
33 
34 TEST(MultiConditionTrigger, TestMultipleConditions) {
35     int numConditions = 5;
36     string t1 = "t1", t2 = "t2", t3 = "t3", t4 = "t4", t5 = "t5";
37     set<string> conditionNames = {t1, t2, t3, t4, t5};
38 
39     mutex lock;
40     condition_variable cv;
41     bool triggerCalled = false;
42 
43     // Mark done as true and notify in the done.
44     MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled] {
45         {
46             lock_guard lg(lock);
47             triggerCalled = true;
48         }
49         cv.notify_all();
50     });
51 
52     vector<thread> threads;
53     vector<int> done(numConditions, 0);
54 
55     int i = 0;
56     for (const string& conditionName : conditionNames) {
57         threads.emplace_back([&done, &conditionName, &trigger, i] {
58             sleep_for(chrono::milliseconds(3));
59             done[i] = 1;
60             trigger.markComplete(conditionName);
61         });
62         i++;
63     }
64 
65     unique_lock<mutex> unique_lk(lock);
66     cv.wait(unique_lk, [&triggerCalled] {
67         return triggerCalled;
68     });
69 
70     for (i = 0; i < numConditions; i++) {
71         EXPECT_EQ(done[i], 1);
72     }
73 
74     for (i = 0; i < numConditions; i++) {
75         threads[i].join();
76     }
77 }
78 
79 TEST(MultiConditionTrigger, TestNoConditions) {
80     mutex lock;
81     condition_variable cv;
82     bool triggerCalled = false;
83 
84     MultiConditionTrigger trigger({}, [&lock, &cv, &triggerCalled] {
85         {
86             lock_guard lg(lock);
87             triggerCalled = true;
88         }
89         cv.notify_all();
90     });
91 
92     unique_lock<mutex> unique_lk(lock);
93     cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; });
94     EXPECT_TRUE(triggerCalled);
95     // Ensure that trigger occurs immediately if no events need to be completed.
96 }
97 
98 TEST(MultiConditionTrigger, TestMarkCompleteCalledBySameCondition) {
99     string t1 = "t1", t2 = "t2";
100     set<string> conditionNames = {t1, t2};
101 
102     mutex lock;
103     condition_variable cv;
104     bool triggerCalled = false;
105 
106     MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled] {
107         {
108             lock_guard lg(lock);
109             triggerCalled = true;
110         }
111         cv.notify_all();
112     });
113 
114     trigger.markComplete(t1);
115     trigger.markComplete(t1);
116 
117     // Ensure that the trigger still hasn't fired.
118     {
119         lock_guard lg(lock);
120         EXPECT_FALSE(triggerCalled);
121     }
122 
123     trigger.markComplete(t2);
124     unique_lock<mutex> unique_lk(lock);
125     cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; });
126     EXPECT_TRUE(triggerCalled);
127 }
128 
129 TEST(MultiConditionTrigger, TestTriggerOnlyCalledOnce) {
130     string t1 = "t1";
131     set<string> conditionNames = {t1};
132 
133     mutex lock;
134     condition_variable cv;
135     bool triggerCalled = false;
136     int triggerCount = 0;
137 
138     MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled, &triggerCount] {
139         {
140             lock_guard lg(lock);
141             triggerCount++;
142             triggerCalled = true;
143         }
144         cv.notify_all();
145     });
146 
147     trigger.markComplete(t1);
148 
149     // Ensure that the trigger fired.
150     {
151         unique_lock<mutex> unique_lk(lock);
152         cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; });
153         EXPECT_TRUE(triggerCalled);
154         EXPECT_EQ(triggerCount, 1);
155         triggerCalled = false;
156     }
157 
158     trigger.markComplete(t1);
159 
160     // Ensure that the trigger does not fire again.
161     {
162         unique_lock<mutex> unique_lk(lock);
163         cv.wait_for(unique_lk, chrono::milliseconds(5), [&triggerCalled] { return triggerCalled; });
164         EXPECT_FALSE(triggerCalled);
165         EXPECT_EQ(triggerCount, 1);
166     }
167 }
168 
169 }  // namespace statsd
170 }  // namespace os
171 }  // namespace android
172 #else
173 GTEST_LOG_(INFO) << "This test does nothing.\n";
174 #endif
175