blob: 29c441ee07b1db4f8f70f5f45ce0cd190c8d7b65 [file] [log] [blame]
Josh Gao822f6742021-02-03 22:12:41 -08001/*
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
17#include "property_monitor.h"
18
19#include <stdlib.h>
20#include <unistd.h>
21
22#include <chrono>
23#include <functional>
24#include <mutex>
25#include <string>
26#include <thread>
27#include <unordered_map>
28#include <vector>
29
30#include <android-base/logging.h>
31#include <android-base/properties.h>
32#include <android-base/thread_annotations.h>
33#include <gtest/gtest.h>
34
35using namespace std::chrono_literals;
36
37struct PropertyChanges {
38 std::unordered_map<std::string, std::vector<std::string>> changes GUARDED_BY(mutex);
39 std::mutex mutex;
40};
41
42static std::string ManglePropertyName(std::string name) {
43 name.push_back('.');
44 name.append(std::to_string(gettid()));
45 name.append(std::to_string(rand()));
46 return name;
47}
48
49static std::thread SpawnThread(PropertyMonitor* pm) {
50 return std::thread([pm]() {
51 pm->Run();
52 });
53}
54
55static std::function<void()> RegisterExitCallback(PropertyMonitor* pm) {
56 std::string prop_name = ManglePropertyName("debug.property_monitor_test.exit");
57 android::base::SetProperty(prop_name, "0");
58 pm->Add(prop_name, [](std::string value) { return value != "1"; });
59 return [prop_name]() {
60 android::base::SetProperty(prop_name, "1");
61 };
62}
63
64static void RegisterCallback(PropertyMonitor* pm, PropertyChanges* output,
65 std::string property_name) {
66 pm->Add(property_name, [output, property_name](std::string value) {
67 std::lock_guard<std::mutex> lock(output->mutex);
68 LOG(INFO) << property_name << " = " << value;
69 output->changes[property_name].emplace_back(std::move(value));
70 return true;
71 });
72}
73
74TEST(PropertyMonitorTest, initial) {
75 PropertyMonitor pm;
76 PropertyChanges output;
77
78 auto exit_fn = RegisterExitCallback(&pm);
79
80 std::string foo = ManglePropertyName("debug.property_monitor_test.initial");
81 std::string never_set = ManglePropertyName("debug.property_monitor_test.never_set");
82 RegisterCallback(&pm, &output, foo);
83 android::base::SetProperty(foo, "foo");
84
85 RegisterCallback(&pm, &output, never_set);
86
87 auto thread = SpawnThread(&pm);
88
89 exit_fn();
90 thread.join();
91
92 std::lock_guard<std::mutex> lock(output.mutex);
93 ASSERT_EQ(2UL, output.changes.size());
94 ASSERT_EQ(2UL, output.changes[foo].size());
95 ASSERT_EQ("", output.changes[foo][0]);
96 ASSERT_EQ("foo", output.changes[foo][1]);
97 ASSERT_EQ("", output.changes[never_set][0]);
98}
99
100TEST(PropertyMonitorTest, change) {
101 PropertyMonitor pm;
102 PropertyChanges output;
103
104 auto exit_fn = RegisterExitCallback(&pm);
105
106 std::string foo = ManglePropertyName("debug.property_monitor_test.foo");
107
108 RegisterCallback(&pm, &output, foo);
109 android::base::SetProperty(foo, "foo");
110
111 auto thread = SpawnThread(&pm);
112 std::this_thread::sleep_for(100ms);
113
114 {
115 std::lock_guard<std::mutex> lock(output.mutex);
116 ASSERT_EQ(1UL, output.changes.size());
117 ASSERT_EQ(2UL, output.changes[foo].size());
118 ASSERT_EQ("", output.changes[foo][0]);
119 ASSERT_EQ("foo", output.changes[foo][1]);
120 }
121
122 android::base::SetProperty(foo, "bar");
123 std::this_thread::sleep_for(100ms);
124
125 {
126 std::lock_guard<std::mutex> lock(output.mutex);
127 ASSERT_EQ(1UL, output.changes.size());
128 ASSERT_EQ(3UL, output.changes[foo].size());
129 ASSERT_EQ("", output.changes[foo][0]);
130 ASSERT_EQ("foo", output.changes[foo][1]);
131 ASSERT_EQ("bar", output.changes[foo][2]);
132 }
133
134 exit_fn();
135 thread.join();
136}
137
138TEST(PropertyMonitorTest, multiple) {
139 PropertyMonitor pm;
140 PropertyChanges output;
141
142 auto exit_fn = RegisterExitCallback(&pm);
143
144 std::string foo = ManglePropertyName("debug.property_monitor_test.foo");
145 std::string bar = ManglePropertyName("debug.property_monitor_test.bar");
146
147 RegisterCallback(&pm, &output, foo);
148 RegisterCallback(&pm, &output, bar);
149
150 android::base::SetProperty(foo, "foo");
151 android::base::SetProperty(bar, "bar");
152
153 auto thread = SpawnThread(&pm);
154 std::this_thread::sleep_for(100ms);
155
156 {
157 std::lock_guard<std::mutex> lock(output.mutex);
158 ASSERT_EQ(2UL, output.changes.size());
159
160 ASSERT_EQ(2UL, output.changes[foo].size());
161 ASSERT_EQ("", output.changes[foo][0]);
162 ASSERT_EQ("foo", output.changes[foo][1]);
163
164 ASSERT_EQ(2UL, output.changes[bar].size());
165 ASSERT_EQ("", output.changes[bar][0]);
166 ASSERT_EQ("bar", output.changes[bar][1]);
167 }
168
169 android::base::SetProperty(foo, "bar");
170 android::base::SetProperty(bar, "foo");
171 std::this_thread::sleep_for(100ms);
172
173 {
174 std::lock_guard<std::mutex> lock(output.mutex);
175 ASSERT_EQ(2UL, output.changes.size());
176
177 ASSERT_EQ(3UL, output.changes[foo].size());
178 ASSERT_EQ("", output.changes[foo][0]);
179 ASSERT_EQ("foo", output.changes[foo][1]);
180 ASSERT_EQ("bar", output.changes[foo][2]);
181
182 ASSERT_EQ(3UL, output.changes[bar].size());
183 ASSERT_EQ("", output.changes[foo][0]);
184 ASSERT_EQ("bar", output.changes[bar][1]);
185 ASSERT_EQ("foo", output.changes[bar][2]);
186 }
187
188 exit_fn();
189 thread.join();
190}