191 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			191 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * 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 "property_monitor.h"
 | |
| 
 | |
| #include <stdlib.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| #include <chrono>
 | |
| #include <functional>
 | |
| #include <mutex>
 | |
| #include <string>
 | |
| #include <thread>
 | |
| #include <unordered_map>
 | |
| #include <vector>
 | |
| 
 | |
| #include <android-base/logging.h>
 | |
| #include <android-base/properties.h>
 | |
| #include <android-base/thread_annotations.h>
 | |
| #include <gtest/gtest.h>
 | |
| 
 | |
| using namespace std::chrono_literals;
 | |
| 
 | |
| struct PropertyChanges {
 | |
|     std::unordered_map<std::string, std::vector<std::string>> changes GUARDED_BY(mutex);
 | |
|     std::mutex mutex;
 | |
| };
 | |
| 
 | |
| static std::string ManglePropertyName(std::string name) {
 | |
|     name.push_back('.');
 | |
|     name.append(std::to_string(gettid()));
 | |
|     name.append(std::to_string(rand()));
 | |
|     return name;
 | |
| }
 | |
| 
 | |
| static std::thread SpawnThread(PropertyMonitor* pm) {
 | |
|     return std::thread([pm]() {
 | |
|         pm->Run();
 | |
|     });
 | |
| }
 | |
| 
 | |
| static std::function<void()> RegisterExitCallback(PropertyMonitor* pm) {
 | |
|     std::string prop_name = ManglePropertyName("debug.property_monitor_test.exit");
 | |
|     android::base::SetProperty(prop_name, "0");
 | |
|     pm->Add(prop_name, [](std::string value) { return value != "1"; });
 | |
|     return [prop_name]() {
 | |
|         android::base::SetProperty(prop_name, "1");
 | |
|     };
 | |
| }
 | |
| 
 | |
| static void RegisterCallback(PropertyMonitor* pm, PropertyChanges* output,
 | |
|                              std::string property_name) {
 | |
|     pm->Add(property_name, [output, property_name](std::string value) {
 | |
|         std::lock_guard<std::mutex> lock(output->mutex);
 | |
|         LOG(INFO) << property_name << " = " << value;
 | |
|         output->changes[property_name].emplace_back(std::move(value));
 | |
|         return true;
 | |
|     });
 | |
| }
 | |
| 
 | |
| TEST(PropertyMonitorTest, initial) {
 | |
|     PropertyMonitor pm;
 | |
|     PropertyChanges output;
 | |
| 
 | |
|     auto exit_fn = RegisterExitCallback(&pm);
 | |
| 
 | |
|     std::string foo = ManglePropertyName("debug.property_monitor_test.initial");
 | |
|     std::string never_set = ManglePropertyName("debug.property_monitor_test.never_set");
 | |
|     RegisterCallback(&pm, &output, foo);
 | |
|     android::base::SetProperty(foo, "foo");
 | |
| 
 | |
|     RegisterCallback(&pm, &output, never_set);
 | |
| 
 | |
|     auto thread = SpawnThread(&pm);
 | |
| 
 | |
|     exit_fn();
 | |
|     thread.join();
 | |
| 
 | |
|     std::lock_guard<std::mutex> lock(output.mutex);
 | |
|     ASSERT_EQ(2UL, output.changes.size());
 | |
|     ASSERT_EQ(2UL, output.changes[foo].size());
 | |
|     ASSERT_EQ("", output.changes[foo][0]);
 | |
|     ASSERT_EQ("foo", output.changes[foo][1]);
 | |
|     ASSERT_EQ("", output.changes[never_set][0]);
 | |
| }
 | |
| 
 | |
| TEST(PropertyMonitorTest, change) {
 | |
|     PropertyMonitor pm;
 | |
|     PropertyChanges output;
 | |
| 
 | |
|     auto exit_fn = RegisterExitCallback(&pm);
 | |
| 
 | |
|     std::string foo = ManglePropertyName("debug.property_monitor_test.foo");
 | |
| 
 | |
|     RegisterCallback(&pm, &output, foo);
 | |
|     android::base::SetProperty(foo, "foo");
 | |
| 
 | |
|     auto thread = SpawnThread(&pm);
 | |
|     std::this_thread::sleep_for(100ms);
 | |
| 
 | |
|     {
 | |
|         std::lock_guard<std::mutex> lock(output.mutex);
 | |
|         ASSERT_EQ(1UL, output.changes.size());
 | |
|         ASSERT_EQ(2UL, output.changes[foo].size());
 | |
|         ASSERT_EQ("", output.changes[foo][0]);
 | |
|         ASSERT_EQ("foo", output.changes[foo][1]);
 | |
|     }
 | |
| 
 | |
|     android::base::SetProperty(foo, "bar");
 | |
|     std::this_thread::sleep_for(100ms);
 | |
| 
 | |
|     {
 | |
|         std::lock_guard<std::mutex> lock(output.mutex);
 | |
|         ASSERT_EQ(1UL, output.changes.size());
 | |
|         ASSERT_EQ(3UL, output.changes[foo].size());
 | |
|         ASSERT_EQ("", output.changes[foo][0]);
 | |
|         ASSERT_EQ("foo", output.changes[foo][1]);
 | |
|         ASSERT_EQ("bar", output.changes[foo][2]);
 | |
|     }
 | |
| 
 | |
|     exit_fn();
 | |
|     thread.join();
 | |
| }
 | |
| 
 | |
| TEST(PropertyMonitorTest, multiple) {
 | |
|     PropertyMonitor pm;
 | |
|     PropertyChanges output;
 | |
| 
 | |
|     auto exit_fn = RegisterExitCallback(&pm);
 | |
| 
 | |
|     std::string foo = ManglePropertyName("debug.property_monitor_test.foo");
 | |
|     std::string bar = ManglePropertyName("debug.property_monitor_test.bar");
 | |
| 
 | |
|     RegisterCallback(&pm, &output, foo);
 | |
|     RegisterCallback(&pm, &output, bar);
 | |
| 
 | |
|     android::base::SetProperty(foo, "foo");
 | |
|     android::base::SetProperty(bar, "bar");
 | |
| 
 | |
|     auto thread = SpawnThread(&pm);
 | |
|     std::this_thread::sleep_for(100ms);
 | |
| 
 | |
|     {
 | |
|         std::lock_guard<std::mutex> lock(output.mutex);
 | |
|         ASSERT_EQ(2UL, output.changes.size());
 | |
| 
 | |
|         ASSERT_EQ(2UL, output.changes[foo].size());
 | |
|         ASSERT_EQ("", output.changes[foo][0]);
 | |
|         ASSERT_EQ("foo", output.changes[foo][1]);
 | |
| 
 | |
|         ASSERT_EQ(2UL, output.changes[bar].size());
 | |
|         ASSERT_EQ("", output.changes[bar][0]);
 | |
|         ASSERT_EQ("bar", output.changes[bar][1]);
 | |
|     }
 | |
| 
 | |
|     android::base::SetProperty(foo, "bar");
 | |
|     android::base::SetProperty(bar, "foo");
 | |
|     std::this_thread::sleep_for(100ms);
 | |
| 
 | |
|     {
 | |
|         std::lock_guard<std::mutex> lock(output.mutex);
 | |
|         ASSERT_EQ(2UL, output.changes.size());
 | |
| 
 | |
|         ASSERT_EQ(3UL, output.changes[foo].size());
 | |
|         ASSERT_EQ("", output.changes[foo][0]);
 | |
|         ASSERT_EQ("foo", output.changes[foo][1]);
 | |
|         ASSERT_EQ("bar", output.changes[foo][2]);
 | |
| 
 | |
|         ASSERT_EQ(3UL, output.changes[bar].size());
 | |
|         ASSERT_EQ("", output.changes[foo][0]);
 | |
|         ASSERT_EQ("bar", output.changes[bar][1]);
 | |
|         ASSERT_EQ("foo", output.changes[bar][2]);
 | |
|     }
 | |
| 
 | |
|     exit_fn();
 | |
|     thread.join();
 | |
| }
 |