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 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 35 using namespace std::chrono_literals; 36 37 struct PropertyChanges { 38 std::unordered_map<std::string, std::vector<std::string>> changes GUARDED_BY(mutex); 39 std::mutex mutex; 40 }; 41 42 static 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 49 static std::thread SpawnThread(PropertyMonitor* pm) { 50 return std::thread([pm]() { 51 pm->Run(); 52 }); 53 } 54 55 static 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 64 static 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 74 TEST(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 100 TEST(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 138 TEST(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 } 191