diff options
author | Wei Wang <wvw@google.com> | 2018-10-19 21:57:04 -0700 |
---|---|---|
committer | Wei Wang <wvw@google.com> | 2018-10-20 19:30:29 -0700 |
commit | a4823198aa21009c3a617bfed785d27de567afae (patch) | |
tree | 97563d7cc09bd895dec903161381390cdfe276aa | |
parent | c65981918a178cc2df961780b8a55560a096ed99 (diff) | |
download | extras-a4823198aa21009c3a617bfed785d27de567afae.tar.gz |
libperfmgr: Add a new type of node to manage property
PowerHAL sometimes need set properties, adding a new type of node to
support this usage.
Change-Id: I0c04df831d10f16a37f6ae5ae234a9ef34fc5f54
Merged-In: I18a4a50993e262c59460dc5e6cbace22b2b50047
Fixes: 116731391
Bug: 110166984
Test: Add new unit tests and libperfmgr_test passing
-rw-r--r-- | libperfmgr/Android.bp | 5 | ||||
-rw-r--r-- | libperfmgr/FileNode.cc | 104 | ||||
-rw-r--r-- | libperfmgr/HintManager.cc | 47 | ||||
-rw-r--r-- | libperfmgr/Node.cc | 82 | ||||
-rw-r--r-- | libperfmgr/PropertyNode.cc | 78 | ||||
-rw-r--r-- | libperfmgr/RequestGroup.cc | 2 | ||||
-rw-r--r-- | libperfmgr/config_schema.json | 10 | ||||
-rw-r--r-- | libperfmgr/include/perfmgr/FileNode.h | 55 | ||||
-rw-r--r-- | libperfmgr/include/perfmgr/Node.h | 22 | ||||
-rw-r--r-- | libperfmgr/include/perfmgr/PropertyNode.h | 48 | ||||
-rw-r--r-- | libperfmgr/include/perfmgr/RequestGroup.h | 2 | ||||
-rw-r--r-- | libperfmgr/tests/FileNodeTest.cc (renamed from libperfmgr/tests/NodeTest.cc) | 48 | ||||
-rw-r--r-- | libperfmgr/tests/HintManagerTest.cc | 144 | ||||
-rw-r--r-- | libperfmgr/tests/NodeLooperThreadTest.cc | 13 | ||||
-rw-r--r-- | libperfmgr/tests/PropertyNodeTest.cc | 220 |
15 files changed, 719 insertions, 161 deletions
diff --git a/libperfmgr/Android.bp b/libperfmgr/Android.bp index effc057e..5fae64fc 100644 --- a/libperfmgr/Android.bp +++ b/libperfmgr/Android.bp @@ -39,6 +39,8 @@ cc_library { srcs: [ "RequestGroup.cc", "Node.cc", + "FileNode.cc", + "PropertyNode.cc", "NodeLooperThread.cc", "HintManager.cc", ] @@ -50,7 +52,8 @@ cc_test { static_libs: ["libperfmgr"], srcs: [ "tests/RequestGroupTest.cc", - "tests/NodeTest.cc", + "tests/FileNodeTest.cc", + "tests/PropertyNodeTest.cc", "tests/NodeLooperThreadTest.cc", "tests/HintManagerTest.cc", ] diff --git a/libperfmgr/FileNode.cc b/libperfmgr/FileNode.cc new file mode 100644 index 00000000..1d051977 --- /dev/null +++ b/libperfmgr/FileNode.cc @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2017 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 specic language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "libperfmgr" + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> + +#include "perfmgr/FileNode.h" + +namespace android { +namespace perfmgr { + +FileNode::FileNode(std::string name, std::string node_path, + std::vector<RequestGroup> req_sorted, + std::size_t default_val_index, bool reset_on_init, + bool hold_fd) + : Node(name, node_path, std::move(req_sorted), default_val_index, + reset_on_init), + hold_fd_(hold_fd) { + if (reset_on_init) { + Update(); + } +} + +std::chrono::milliseconds FileNode::Update(bool log_error) { + std::size_t value_index = default_val_index_; + std::chrono::milliseconds expire_time = std::chrono::milliseconds::max(); + + // Find the highest outstanding request's expire time + for (std::size_t i = 0; i < req_sorted_.size(); i++) { + if (req_sorted_[i].GetExpireTime(&expire_time)) { + value_index = i; + break; + } + } + + // Update node only if request index changes + if (value_index != current_val_index_) { + const std::string& req_value = req_sorted_[value_index].GetRequestValue(); + + fd_.reset(TEMP_FAILURE_RETRY( + open(node_path_.c_str(), O_WRONLY | O_CLOEXEC | O_TRUNC))); + + if (fd_ == -1 || !android::base::WriteStringToFd(req_value, fd_)) { + if (log_error) { + LOG(WARNING) << "Failed to write to node: " << node_path_ + << " with value: " << req_value << ", fd: " << fd_; + } + // Retry in 500ms or sooner + expire_time = std::min(expire_time, std::chrono::milliseconds(500)); + } else { + // For regular file system, we need fsync + fsync(fd_); + // Some dev node requires file to remain open during the entire hint + // duration e.g. /dev/cpu_dma_latency, so fd_ is intentionally kept + // open during any requested value other than default one. If + // request a default value, node will write the value and then + // release the fd. + if ((!hold_fd_) || value_index == default_val_index_) { + fd_.reset(); + } + // Update current index only when succeed + current_val_index_ = value_index; + } + } + return expire_time; +} + +bool FileNode::GetHoldFd() const { + return hold_fd_; +} + +void FileNode::DumpToFd(int fd) const { + std::string node_value; + if (!android::base::ReadFileToString(node_path_, &node_value)) { + LOG(ERROR) << "Failed to read node path: " << node_path_; + } + node_value = android::base::Trim(node_value); + std::string buf(android::base::StringPrintf( + "%s\t%s\t%zu\t%s\n", name_.c_str(), node_path_.c_str(), + current_val_index_, node_value.c_str())); + if (!android::base::WriteStringToFd(buf, fd)) { + LOG(ERROR) << "Failed to dump fd: " << fd; + } +} + +} // namespace perfmgr +} // namespace android diff --git a/libperfmgr/HintManager.cc b/libperfmgr/HintManager.cc index f4e64f8e..cdce043d 100644 --- a/libperfmgr/HintManager.cc +++ b/libperfmgr/HintManager.cc @@ -16,6 +16,7 @@ #define LOG_TAG "libperfmgr" +#include <algorithm> #include <set> #include <android-base/file.h> @@ -24,7 +25,9 @@ #include <json/reader.h> #include <json/value.h> +#include "perfmgr/FileNode.h" #include "perfmgr/HintManager.h" +#include "perfmgr/PropertyNode.h" namespace android { namespace perfmgr { @@ -222,20 +225,44 @@ std::vector<std::unique_ptr<Node>> HintManager::ParseNodes( } else { reset = nodes[i]["ResetOnInit"].asBool(); } - LOG(VERBOSE) << "Node[" << i << "]'s ResetOnInit: " << reset; + LOG(VERBOSE) << "Node[" << i << "]'s ResetOnInit: " << reset ? "true" + : "false"; - bool hold_fd = false; - if (nodes[i]["HoldFd"].empty() || !nodes[i]["HoldFd"].isBool()) { - LOG(INFO) << "Failed to read Node" << i - << "'s HoldFd, set to 'false'"; + bool is_file = true; + std::string node_type = nodes[i]["Type"].asString(); + LOG(VERBOSE) << "Node[" << i << "]'s Type: " << node_type; + if (node_type.empty()) { + LOG(ERROR) << "Failed to read " + << "Node[" << i << "]'s Type, set to File as default"; + } else if (node_type == "File") { + is_file = true; + } else if (node_type == "Property") { + is_file = false; } else { - hold_fd = nodes[i]["HoldFd"].asBool(); + LOG(ERROR) << "Invalid Node[" << i + << "]'s Type: only File and Property supported."; + nodes_parsed.clear(); + return nodes_parsed; } - LOG(VERBOSE) << "Node[" << i << "]'s HoldFd: " << hold_fd; - nodes_parsed.emplace_back(std::make_unique<Node>( - name, path, values_parsed, static_cast<std::size_t>(default_index), - reset, hold_fd)); + if (is_file) { + bool hold_fd = false; + if (nodes[i]["HoldFd"].empty() || !nodes[i]["HoldFd"].isBool()) { + LOG(INFO) << "Failed to read Node" << i + << "'s HoldFd, set to 'false'"; + } else { + hold_fd = nodes[i]["HoldFd"].asBool(); + } + LOG(VERBOSE) << "Node[" << i << "]'s HoldFd: " << hold_fd; + + nodes_parsed.emplace_back(std::make_unique<FileNode>( + name, path, values_parsed, + static_cast<std::size_t>(default_index), reset, hold_fd)); + } else { + nodes_parsed.emplace_back(std::make_unique<PropertyNode>( + name, path, values_parsed, + static_cast<std::size_t>(default_index), reset)); + } } LOG(INFO) << nodes_parsed.size() << " Nodes parsed successfully"; return nodes_parsed; diff --git a/libperfmgr/Node.cc b/libperfmgr/Node.cc index c35c3ca1..90251014 100644 --- a/libperfmgr/Node.cc +++ b/libperfmgr/Node.cc @@ -28,22 +28,16 @@ namespace perfmgr { Node::Node(std::string name, std::string node_path, std::vector<RequestGroup> req_sorted, std::size_t default_val_index, - bool reset_on_init, bool hold_fd) + bool reset_on_init) : name_(name), node_path_(node_path), req_sorted_(std::move(req_sorted)), default_val_index_(default_val_index), reset_on_init_(reset_on_init), - hold_fd_(hold_fd) { - if (reset_on_init) { - // Assigning an invalid value so the next Update() will update the - // Node's value to default - current_val_index_ = req_sorted_.size(); - Update(); - } else { - current_val_index_ = default_val_index; - } -} + // Assigning an invalid value so the next Update() will update the + // Node's value to default + current_val_index_(reset_on_init ? req_sorted_.size() + : default_val_index) {} bool Node::AddRequest(std::size_t value_index, const std::string& hint_type, ReqTime end_time) { @@ -66,55 +60,11 @@ bool Node::RemoveRequest(const std::string& hint_type) { return ret; } -std::chrono::milliseconds Node::Update(bool log_error) { - std::size_t value_index = default_val_index_; - std::chrono::milliseconds expire_time = std::chrono::milliseconds::max(); - - // Find the highest outstanding request's expire time - for (std::size_t i = 0; i < req_sorted_.size(); i++) { - if (req_sorted_[i].GetExpireTime(&expire_time)) { - value_index = i; - break; - } - } - - // Update node only if request index changes - if (value_index != current_val_index_) { - std::string req_value = req_sorted_[value_index].GetRequestValue(); - - fd_.reset(TEMP_FAILURE_RETRY( - open(node_path_.c_str(), O_WRONLY | O_CLOEXEC | O_TRUNC))); - - if (fd_ == -1 || !android::base::WriteStringToFd(req_value, fd_)) { - if (log_error) { - LOG(WARNING) << "Failed to write to node: " << node_path_ - << " with value: " << req_value << ", fd: " << fd_; - } - // Retry in 500ms or sooner - expire_time = std::min(expire_time, std::chrono::milliseconds(500)); - } else { - // For regular file system, we need fsync - fsync(fd_); - // Some dev node requires file to remain open during the entire hint - // duration e.g. /dev/cpu_dma_latency, so fd_ is intentionally kept - // open during any requested value other than default one. If - // request a default value, node will write the value and then - // release the fd. - if ((!hold_fd_) || value_index == default_val_index_) { - fd_.reset(); - } - // Update current index only when succeed - current_val_index_ = value_index; - } - } - return expire_time; -} - -std::string Node::GetName() const { +const std::string& Node::GetName() const { return name_; } -std::string Node::GetPath() const { +const std::string& Node::GetPath() const { return node_path_; } @@ -138,10 +88,6 @@ bool Node::GetResetOnInit() const { return reset_on_init_; } -bool Node::GetHoldFd() const { - return hold_fd_; -} - std::vector<std::string> Node::GetValues() const { std::vector<std::string> values; for (const auto& value : req_sorted_) { @@ -150,19 +96,5 @@ std::vector<std::string> Node::GetValues() const { return values; } -void Node::DumpToFd(int fd) { - std::string node_value; - if (!android::base::ReadFileToString(node_path_, &node_value)) { - LOG(ERROR) << "Failed to read node path: " << node_path_; - } - node_value = android::base::Trim(node_value); - std::string buf(android::base::StringPrintf( - "%s\t%s\t%zu\t%s\n", name_.c_str(), node_path_.c_str(), - current_val_index_, node_value.c_str())); - if (!android::base::WriteStringToFd(buf, fd)) { - LOG(ERROR) << "Failed to dump fd: " << fd; - } -} - } // namespace perfmgr } // namespace android diff --git a/libperfmgr/PropertyNode.cc b/libperfmgr/PropertyNode.cc new file mode 100644 index 00000000..cf07b3cc --- /dev/null +++ b/libperfmgr/PropertyNode.cc @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2017 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 specic language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "libperfmgr" + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> + +#include "perfmgr/PropertyNode.h" + +namespace android { +namespace perfmgr { + +PropertyNode::PropertyNode(std::string name, std::string node_path, + std::vector<RequestGroup> req_sorted, + std::size_t default_val_index, bool reset_on_init) + : Node(name, node_path, std::move(req_sorted), default_val_index, + reset_on_init) { + if (reset_on_init) { + Update(); + } +} + +std::chrono::milliseconds PropertyNode::Update(bool) { + std::size_t value_index = default_val_index_; + std::chrono::milliseconds expire_time = std::chrono::milliseconds::max(); + + // Find the highest outstanding request's expire time + for (std::size_t i = 0; i < req_sorted_.size(); i++) { + if (req_sorted_[i].GetExpireTime(&expire_time)) { + value_index = i; + break; + } + } + + // Update node only if request index changes + if (value_index != current_val_index_) { + const std::string& req_value = req_sorted_[value_index].GetRequestValue(); + + if (!android::base::SetProperty(node_path_, req_value)) { + LOG(WARNING) << "Failed to set property to : " << node_path_ + << " with value: " << req_value; + } else { + // Update current index only when succeed + current_val_index_ = value_index; + } + } + return expire_time; +} + +void PropertyNode::DumpToFd(int fd) const { + std::string node_value = android::base::GetProperty(node_path_, ""); + std::string buf(android::base::StringPrintf( + "%s\t%s\t%zu\t%s\n", name_.c_str(), node_path_.c_str(), + current_val_index_, node_value.c_str())); + if (!android::base::WriteStringToFd(buf, fd)) { + LOG(ERROR) << "Failed to dump fd: " << fd; + } +} + +} // namespace perfmgr +} // namespace android diff --git a/libperfmgr/RequestGroup.cc b/libperfmgr/RequestGroup.cc index 7f7ddf4b..857d17a4 100644 --- a/libperfmgr/RequestGroup.cc +++ b/libperfmgr/RequestGroup.cc @@ -37,7 +37,7 @@ bool RequestGroup::RemoveRequest(const std::string& hint_type) { return request_map_.erase(hint_type); } -std::string RequestGroup::GetRequestValue() const { +const std::string& RequestGroup::GetRequestValue() const { return request_value_; } diff --git a/libperfmgr/config_schema.json b/libperfmgr/config_schema.json index b74bacbe..c26f9555 100644 --- a/libperfmgr/config_schema.json +++ b/libperfmgr/config_schema.json @@ -33,7 +33,7 @@ "type": "string", "id": "/properties/Nodes/items/properties/Path", "title": "The Path Schema.", - "description": "The path of the node.", + "description": "For File type node, it is filesystem path of the file; for Property type node, it is the key of the property.", "minLength": 1 }, "Values": { @@ -62,11 +62,17 @@ "title": "The Reset On Init Schema.", "description": "Flag if node will be set to default value on initialization; if not present, it will be set to false." }, + "Type": { + "type": "string", + "id": "/properties/Nodes/items/properties/Type", + "title": "The type Schema.", + "description": "Type of Node (File or Property), if not present, it will be set to File." + }, "HoldFd": { "type": "boolean", "id": "/properties/Nodes/items/properties/HoldFd", "title": "The Hold Fd Schema.", - "description": "Flag if node will hold the file descriptor on non-default values; if not present, it will be set to false." + "description": "Flag if node will hold the file descriptor on non-default values; if not present, it will be set to false. This is only honoured for File type node." } } } diff --git a/libperfmgr/include/perfmgr/FileNode.h b/libperfmgr/include/perfmgr/FileNode.h new file mode 100644 index 00000000..6d6c2190 --- /dev/null +++ b/libperfmgr/include/perfmgr/FileNode.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2017 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 specic language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_LIBPERFMGR_FILENODE_H_ +#define ANDROID_LIBPERFMGR_FILENODE_H_ + +#include <cstddef> +#include <string> +#include <vector> + +#include <android-base/unique_fd.h> + +#include "perfmgr/Node.h" + +namespace android { +namespace perfmgr { + +// FileNode represents file +class FileNode : public Node { + public: + FileNode(std::string name, std::string node_path, + std::vector<RequestGroup> req_sorted, std::size_t default_val_index, + bool reset_on_init, bool hold_fd = false); + + std::chrono::milliseconds Update(bool log_error = true) override; + + bool GetHoldFd() const; + + void DumpToFd(int fd) const override; + + private: + FileNode(const Node& other) = delete; + FileNode& operator=(Node const&) = delete; + + const bool hold_fd_; + android::base::unique_fd fd_; +}; + +} // namespace perfmgr +} // namespace android + +#endif // ANDROID_LIBPERFMGR_FILENODE_H_ diff --git a/libperfmgr/include/perfmgr/Node.h b/libperfmgr/include/perfmgr/Node.h index 560979b0..7dc35413 100644 --- a/libperfmgr/include/perfmgr/Node.h +++ b/libperfmgr/include/perfmgr/Node.h @@ -46,9 +46,7 @@ namespace perfmgr { // protection from caller e.g. NodeLooperThread. class Node { public: - Node(std::string name, std::string node_path, - std::vector<RequestGroup> req_sorted, std::size_t default_val_index, - bool reset_on_init, bool hold_fd = false); + virtual ~Node() {} // Return true if successfully add a request bool AddRequest(std::size_t value_index, const std::string& hint_type, @@ -61,18 +59,20 @@ class Node { // std::chrono::milliseconds::max() if no active request on Node; update // node's controlled file node value and the current value index based on // active request. - std::chrono::milliseconds Update(bool log_error = true); + virtual std::chrono::milliseconds Update(bool log_error = true) = 0; - std::string GetName() const; - std::string GetPath() const; + const std::string& GetName() const; + const std::string& GetPath() const; std::vector<std::string> GetValues() const; std::size_t GetDefaultIndex() const; bool GetResetOnInit() const; - bool GetHoldFd() const; bool GetValueIndex(const std::string value, std::size_t* index) const; - void DumpToFd(int fd); + virtual void DumpToFd(int fd) const = 0; - private: + protected: + Node(std::string name, std::string node_path, + std::vector<RequestGroup> req_sorted, std::size_t default_val_index, + bool reset_on_init); Node(const Node& other) = delete; Node& operator=(Node const&) = delete; @@ -81,10 +81,8 @@ class Node { // request vector, one entry per possible value, sorted by priority std::vector<RequestGroup> req_sorted_; const std::size_t default_val_index_; - std::size_t current_val_index_; const bool reset_on_init_; - const bool hold_fd_; - android::base::unique_fd fd_; + std::size_t current_val_index_; }; } // namespace perfmgr diff --git a/libperfmgr/include/perfmgr/PropertyNode.h b/libperfmgr/include/perfmgr/PropertyNode.h new file mode 100644 index 00000000..8c40e4e1 --- /dev/null +++ b/libperfmgr/include/perfmgr/PropertyNode.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 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 specic language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_LIBPERFMGR_PROPERTYNODE_H_ +#define ANDROID_LIBPERFMGR_PROPERTYNODE_H_ + +#include <cstddef> +#include <string> +#include <vector> + +#include "perfmgr/Node.h" + +namespace android { +namespace perfmgr { + +// PropertyNode represents managed system properties +class PropertyNode : public Node { + public: + PropertyNode(std::string name, std::string node_path, + std::vector<RequestGroup> req_sorted, + std::size_t default_val_index, bool reset_on_init); + + std::chrono::milliseconds Update(bool log_error = true) override; + + void DumpToFd(int fd) const override; + + private: + PropertyNode(const Node& other) = delete; + PropertyNode& operator=(Node const&) = delete; +}; + +} // namespace perfmgr +} // namespace android + +#endif // ANDROID_LIBPERFMGR_PROPERTYNODE_H_ diff --git a/libperfmgr/include/perfmgr/RequestGroup.h b/libperfmgr/include/perfmgr/RequestGroup.h index 144adc2d..2d997b59 100644 --- a/libperfmgr/include/perfmgr/RequestGroup.h +++ b/libperfmgr/include/perfmgr/RequestGroup.h @@ -45,7 +45,7 @@ class RequestGroup { // request_map_ is empty. bool GetExpireTime(std::chrono::milliseconds* expire_time); // Return the request value. - std::string GetRequestValue() const; + const std::string& GetRequestValue() const; // Return true for adding request, false for extending expire time of // existing active request on given hint_type. bool AddRequest(const std::string& hint_type, ReqTime end_time); diff --git a/libperfmgr/tests/NodeTest.cc b/libperfmgr/tests/FileNodeTest.cc index 02429ccd..3bec8510 100644 --- a/libperfmgr/tests/NodeTest.cc +++ b/libperfmgr/tests/FileNodeTest.cc @@ -23,7 +23,7 @@ #include <gtest/gtest.h> -#include "perfmgr/Node.h" +#include "perfmgr/FileNode.h" namespace android { namespace perfmgr { @@ -41,26 +41,27 @@ static inline void _VerifyPathValue(const std::string& path, } // Test init with no default value -TEST(NodeTest, NoInitDefaultTest) { +TEST(FileNodeTest, NoInitDefaultTest) { TemporaryFile tf; - Node t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, false); + FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, false); _VerifyPathValue(tf.path, ""); } // Test init with default value -TEST(NodeTest, InitDefaultTest) { +TEST(FileNodeTest, InitDefaultTest) { TemporaryFile tf; - Node t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, true); + FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, true); _VerifyPathValue(tf.path, "value1"); TemporaryFile tf2; - Node t2("t2", tf2.path, {{"value0"}, {"value1"}, {"value2"}}, 0, true); + FileNode t2("t2", tf2.path, {{"value0"}, {"value1"}, {"value2"}}, 0, true); _VerifyPathValue(tf2.path, "value0"); } // Test DumpToFd -TEST(NodeTest, DumpToFdTest) { +TEST(FileNodeTest, DumpToFdTest) { TemporaryFile tf; - Node t("test_dump", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, true); + FileNode t("test_dump", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, + true); TemporaryFile dumptf; t.DumpToFd(dumptf.fd); fsync(dumptf.fd); @@ -70,9 +71,9 @@ TEST(NodeTest, DumpToFdTest) { } // Test GetValueIndex -TEST(NodeTest, GetValueIndexTest) { +TEST(FileNodeTest, GetValueIndexTest) { TemporaryFile tf; - Node t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, false); + FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, false); std::size_t index = 0; EXPECT_TRUE(t.GetValueIndex("value2", &index)); EXPECT_EQ(2u, index); @@ -82,9 +83,9 @@ TEST(NodeTest, GetValueIndexTest) { } // Test GetValues -TEST(NodeTest, GetValuesTest) { +TEST(FileNodeTest, GetValuesTest) { TemporaryFile tf; - Node t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, false); + FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, false); std::vector values = t.GetValues(); EXPECT_EQ(3u, values.size()); EXPECT_EQ("value0", values[0]); @@ -93,10 +94,10 @@ TEST(NodeTest, GetValuesTest) { } // Test get more properties -TEST(NodeTest, GetPropertiesTest) { +TEST(FileNodeTest, GetPropertiesTest) { std::string test_name = "TESTREQ_1"; std::string test_path = "TEST_PATH"; - Node t(test_name, test_path, {}, 0, false, true); + FileNode t(test_name, test_path, {}, 0, false, true); EXPECT_EQ(test_name, t.GetName()); EXPECT_EQ(test_path, t.GetPath()); EXPECT_EQ(0u, t.GetValues().size()); @@ -106,9 +107,9 @@ TEST(NodeTest, GetPropertiesTest) { } // Test add request fail and retry -TEST(NodeTest, AddRequestTestFail) { - Node t("t", "/sys/android/nonexist_node_test", - {{"value0"}, {"value1"}, {"value2"}}, 2, true); +TEST(FileNodeTest, AddRequestTestFail) { + FileNode t("t", "/sys/android/nonexist_node_test", + {{"value0"}, {"value1"}, {"value2"}}, 2, true); auto start = std::chrono::steady_clock::now(); EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 200ms)); std::chrono::milliseconds expire_time = t.Update(); @@ -124,9 +125,9 @@ TEST(NodeTest, AddRequestTestFail) { } // Test add request -TEST(NodeTest, AddRequestTest) { +TEST(FileNodeTest, AddRequestTest) { TemporaryFile tf; - Node t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 2, true); + FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 2, true); auto start = std::chrono::steady_clock::now(); EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 500ms)); std::chrono::milliseconds expire_time = t.Update(); @@ -154,9 +155,9 @@ TEST(NodeTest, AddRequestTest) { } // Test remove request -TEST(NodeTest, RemoveRequestTest) { +TEST(FileNodeTest, RemoveRequestTest) { TemporaryFile tf; - Node t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 2, true); + FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 2, true); auto start = std::chrono::steady_clock::now(); EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 500ms)); std::chrono::milliseconds expire_time = t.Update(); @@ -184,9 +185,10 @@ TEST(NodeTest, RemoveRequestTest) { } // Test add request with holding fd -TEST(NodeTest, AddRequestTestHoldFdOverride) { +TEST(FileNodeTest, AddRequestTestHoldFdOverride) { TemporaryFile tf; - Node t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 2, true, true); + FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 2, true, + true); EXPECT_TRUE(t.GetHoldFd()); auto start = std::chrono::steady_clock::now(); EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 500ms)); diff --git a/libperfmgr/tests/HintManagerTest.cc b/libperfmgr/tests/HintManagerTest.cc index 2355b8ff..33a3ad16 100644 --- a/libperfmgr/tests/HintManagerTest.cc +++ b/libperfmgr/tests/HintManagerTest.cc @@ -19,11 +19,14 @@ #include <android-base/file.h> #include <android-base/logging.h> +#include <android-base/properties.h> #include <android-base/test_utils.h> #include <gtest/gtest.h> +#include "perfmgr/FileNode.h" #include "perfmgr/HintManager.h" +#include "perfmgr/PropertyNode.h" namespace android { namespace perfmgr { @@ -55,6 +58,16 @@ constexpr auto kSLEEP_TOLERANCE_MS = 50ms; // "384000" // ], // "HoldFd": true +// }, +// { +// "Name": "ModeProperty", +// "Path": "vendor.pwhal.mode", +// "Values": [ +// "HIGH", +// "LOW", +// "NONE" +// ], +// "Type": "Property" // } // ], // "Actions": [ @@ -65,6 +78,12 @@ constexpr auto kSLEEP_TOLERANCE_MS = 50ms; // "Duration": 800 // }, // { +// "PowerHint": "INTERACTION", +// "Node": "ModeProperty", +// "Value": "LOW", +// "Duration": 800 +// }, +// { // "PowerHint": "LAUNCH", // "Node": "CPUCluster0MinFreq", // "Value": "1134000", @@ -72,6 +91,12 @@ constexpr auto kSLEEP_TOLERANCE_MS = 50ms; // }, // { // "PowerHint": "LAUNCH", +// "Node": "ModeProperty", +// "Value": "HIGH", +// "Duration": 500 +// }, +// { +// "PowerHint": "LAUNCH", // "Node": "CPUCluster1MinFreq", // "Value": "1512000", // "Duration": 2000 @@ -85,13 +110,16 @@ constexpr char kJSON_RAW[] = "\"DefaultIndex\":2,\"ResetOnInit\":true},{\"Name\":\"CPUCluster1MinFreq\"," "\"Path\":\"/sys/devices/system/cpu/cpu4/cpufreq/" "scaling_min_freq\",\"Values\":[\"1512000\",\"1134000\",\"384000\"]," - "\"HoldFd\":true}],\"Actions\":[{\"PowerHint\":\"INTERACTION\",\"Node\":" + "\"HoldFd\":true},{\"Name\":\"ModeProperty\",\"Path\":\"vendor.pwhal." + "mode\",\"Values\":[\"HIGH\",\"LOW\",\"NONE\"],\"Type\":\"Property\"}]," + "\"Actions\":[{\"PowerHint\":\"INTERACTION\",\"Node\":" "\"CPUCluster1MinFreq\",\"Value\":\"1134000\",\"Duration\":800},{" - "\"PowerHint\":" - "\"LAUNCH\",\"Node\":\"CPUCluster0MinFreq\",\"Value\":\"1134000\"," - "\"Duration\":" - "500},{\"PowerHint\":\"LAUNCH\",\"Node\":\"CPUCluster1MinFreq\"," - "\"Value\":\"1512000\",\"Duration\":2000}]}"; + "\"PowerHint\":\"INTERACTION\",\"Node\":\"ModeProperty\",\"Value\":\"LOW\"," + "\"Duration\":800},{\"PowerHint\":\"LAUNCH\",\"Node\":" + "\"CPUCluster0MinFreq\",\"Value\":\"1134000\",\"Duration\":500},{" + "\"PowerHint\":\"LAUNCH\",\"Node\":\"ModeProperty\",\"Value\":\"HIGH\"," + "\"Duration\":500},{\"PowerHint\":\"LAUNCH\",\"Node\":" + "\"CPUCluster1MinFreq\",\"Value\":\"1512000\",\"Duration\":2000}]}"; class HintManagerTest : public ::testing::Test, public HintManager { protected: @@ -99,36 +127,42 @@ class HintManagerTest : public ::testing::Test, public HintManager { : HintManager(nullptr, std::map<std::string, std::vector<NodeAction>>{}) { android::base::SetMinimumLogSeverity(android::base::VERBOSE); + prop_ = "vendor.pwhal.mode"; } virtual void SetUp() { - // Set up dummy nodes + // Set up 3 dummy nodes std::unique_ptr<TemporaryFile> tf = std::make_unique<TemporaryFile>(); - nodes_.emplace_back( - new Node("n0", tf->path, - {{"n0_value0"}, {"n0_value1"}, {"n0_value2"}}, 2, false)); + nodes_.emplace_back(new FileNode( + "n0", tf->path, {{"n0_value0"}, {"n0_value1"}, {"n0_value2"}}, 2, + false)); files_.emplace_back(std::move(tf)); tf = std::make_unique<TemporaryFile>(); - nodes_.emplace_back( - new Node("n1", tf->path, - {{"n1_value0"}, {"n1_value1"}, {"n1_value2"}}, 2, true)); + nodes_.emplace_back(new FileNode( + "n1", tf->path, {{"n1_value0"}, {"n1_value1"}, {"n1_value2"}}, 2, + true)); files_.emplace_back(std::move(tf)); + nodes_.emplace_back(new PropertyNode( + "n2", prop_, {{"n2_value0"}, {"n2_value1"}, {"n2_value2"}}, 2, + true)); nm_ = new NodeLooperThread(std::move(nodes_)); // Set up dummy actions // "INTERACTION" // Node0, value1, 800ms // Node1, value1, forever + // Node2, value1, 800ms // "LAUNCH" // Node0, value0, forever // Node1, value0, 400ms + // Node2, value0, 400ms actions_ = std::map<std::string, std::vector<NodeAction>>{ - {"INTERACTION", {{0, 1, 800ms}, {1, 1, 0ms}}}, - {"LAUNCH", {{0, 0, 0ms}, {1, 0, 400ms}}}}; + {"INTERACTION", {{0, 1, 800ms}, {1, 1, 0ms}, {2, 1, 800ms}}}, + {"LAUNCH", {{0, 0, 0ms}, {1, 0, 400ms}, {2, 0, 400ms}}}}; // Prepare dummy files to replace the nodes' path in example json_doc files_.emplace_back(std::make_unique<TemporaryFile>()); files_.emplace_back(std::make_unique<TemporaryFile>()); - // replace filepath + // replace file path json_doc_ = kJSON_RAW; std::string from = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq"; @@ -137,6 +171,8 @@ class HintManagerTest : public ::testing::Test, public HintManager { from = "/sys/devices/system/cpu/cpu4/cpufreq/scaling_min_freq"; start_pos = json_doc_.find(from); json_doc_.replace(start_pos, from.length(), files_[1 + 2]->path); + EXPECT_TRUE(android::base::SetProperty(prop_, "")) + << "failed to clear property"; } virtual void TearDown() { @@ -150,8 +186,15 @@ class HintManagerTest : public ::testing::Test, public HintManager { std::vector<std::unique_ptr<Node>> nodes_; std::vector<std::unique_ptr<TemporaryFile>> files_; std::string json_doc_; + std::string prop_; }; +static inline void _VerifyPropertyValue(const std::string& path, + const std::string& value) { + std::string s = android::base::GetProperty(path, ""); + EXPECT_EQ(value, s); +} + static inline void _VerifyPathValue(const std::string& path, const std::string& value) { std::string s; @@ -176,9 +219,10 @@ TEST_F(HintManagerTest, HintInitDefaultTest) { EXPECT_TRUE(hm.IsRunning()); _VerifyPathValue(files_[0]->path, ""); _VerifyPathValue(files_[1]->path, "n1_value2"); + _VerifyPropertyValue(prop_, ""); } -// Test hint/cancel/expire +// Test hint/cancel/expire with dummy actions TEST_F(HintManagerTest, HintTest) { HintManager hm(nm_, actions_); EXPECT_TRUE(hm.IsRunning()); @@ -186,6 +230,7 @@ TEST_F(HintManagerTest, HintTest) { std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS); _VerifyPathValue(files_[0]->path, "n0_value1"); _VerifyPathValue(files_[1]->path, "n1_value1"); + _VerifyPropertyValue(prop_, "n2_value1"); // this won't change the expire time of INTERACTION hint EXPECT_TRUE(hm.DoHint("INTERACTION", 200ms)); // now place new hint @@ -193,36 +238,42 @@ TEST_F(HintManagerTest, HintTest) { std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS); _VerifyPathValue(files_[0]->path, "n0_value0"); _VerifyPathValue(files_[1]->path, "n1_value0"); + _VerifyPropertyValue(prop_, "n2_value0"); EXPECT_TRUE(hm.DoHint("LAUNCH", 500ms)); - // no"LAUNCH" node1 not expired + // "LAUNCH" node1 not expired std::this_thread::sleep_for(400ms); _VerifyPathValue(files_[0]->path, "n0_value0"); _VerifyPathValue(files_[1]->path, "n1_value0"); + _VerifyPropertyValue(prop_, "n2_value0"); // "LAUNCH" node1 expired std::this_thread::sleep_for(100ms + kSLEEP_TOLERANCE_MS); _VerifyPathValue(files_[0]->path, "n0_value0"); _VerifyPathValue(files_[1]->path, "n1_value1"); + _VerifyPropertyValue(prop_, "n2_value1"); EXPECT_TRUE(hm.EndHint("LAUNCH")); std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS); // "LAUNCH" canceled _VerifyPathValue(files_[0]->path, "n0_value1"); _VerifyPathValue(files_[1]->path, "n1_value1"); + _VerifyPropertyValue(prop_, "n2_value1"); std::this_thread::sleep_for(200ms); // "INTERACTION" node0 expired _VerifyPathValue(files_[0]->path, "n0_value2"); _VerifyPathValue(files_[1]->path, "n1_value1"); + _VerifyPropertyValue(prop_, "n2_value2"); EXPECT_TRUE(hm.EndHint("INTERACTION")); std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS); // "INTERACTION" canceled _VerifyPathValue(files_[0]->path, "n0_value2"); _VerifyPathValue(files_[1]->path, "n1_value2"); + _VerifyPropertyValue(prop_, "n2_value2"); } -// Test parsing nodes with duplicate name +// Test parsing nodes TEST_F(HintManagerTest, ParseNodesTest) { std::vector<std::unique_ptr<Node>> nodes = HintManager::ParseNodes(json_doc_); - EXPECT_EQ(2u, nodes.size()); + EXPECT_EQ(3u, nodes.size()); EXPECT_EQ("CPUCluster0MinFreq", nodes[0]->GetName()); EXPECT_EQ("CPUCluster1MinFreq", nodes[1]->GetName()); EXPECT_EQ(files_[0 + 2]->path, nodes[0]->GetPath()); @@ -237,11 +288,19 @@ TEST_F(HintManagerTest, ParseNodesTest) { EXPECT_EQ(2u, nodes[1]->GetDefaultIndex()); EXPECT_TRUE(nodes[0]->GetResetOnInit()); EXPECT_FALSE(nodes[1]->GetResetOnInit()); - EXPECT_FALSE(nodes[0]->GetHoldFd()); - EXPECT_TRUE(nodes[1]->GetHoldFd()); + // no dynamic_cast intentionally in Android + EXPECT_FALSE(reinterpret_cast<FileNode*>(nodes[0].get())->GetHoldFd()); + EXPECT_TRUE(reinterpret_cast<FileNode*>(nodes[1].get())->GetHoldFd()); + EXPECT_EQ("ModeProperty", nodes[2]->GetName()); + EXPECT_EQ(prop_, nodes[2]->GetPath()); + EXPECT_EQ("HIGH", nodes[2]->GetValues()[0]); + EXPECT_EQ("LOW", nodes[2]->GetValues()[1]); + EXPECT_EQ("NONE", nodes[2]->GetValues()[2]); + EXPECT_EQ(2u, nodes[2]->GetDefaultIndex()); + EXPECT_FALSE(nodes[2]->GetResetOnInit()); } -// Test parsing actions +// Test parsing nodes with duplicate name TEST_F(HintManagerTest, ParseNodesDuplicateNameTest) { std::string from = "CPUCluster0MinFreq"; size_t start_pos = json_doc_.find(from); @@ -251,6 +310,15 @@ TEST_F(HintManagerTest, ParseNodesDuplicateNameTest) { EXPECT_EQ(0u, nodes.size()); } +TEST_F(HintManagerTest, ParsePropertyNodesDuplicatNameTest) { + std::string from = "ModeProperty"; + size_t start_pos = json_doc_.find(from); + json_doc_.replace(start_pos, from.length(), "CPUCluster1MinFreq"); + std::vector<std::unique_ptr<Node>> nodes = + HintManager::ParseNodes(json_doc_); + EXPECT_EQ(0u, nodes.size()); +} + // Test parsing nodes with duplicate path TEST_F(HintManagerTest, ParseNodesDuplicatePathTest) { std::string from = files_[0 + 2]->path; @@ -262,7 +330,7 @@ TEST_F(HintManagerTest, ParseNodesDuplicatePathTest) { } // Test parsing invalid json for nodes -TEST_F(HintManagerTest, ParseBadNodesTest) { +TEST_F(HintManagerTest, ParseBadFileNodesTest) { std::vector<std::unique_ptr<Node>> nodes = HintManager::ParseNodes("invalid json"); EXPECT_EQ(0u, nodes.size()); @@ -279,24 +347,34 @@ TEST_F(HintManagerTest, ParseActionsTest) { std::map<std::string, std::vector<NodeAction>> actions = HintManager::ParseActions(json_doc_, nodes); EXPECT_EQ(2u, actions.size()); - EXPECT_EQ(1u, actions["INTERACTION"].size()); + EXPECT_EQ(2u, actions["INTERACTION"].size()); EXPECT_EQ(1u, actions["INTERACTION"][0].node_index); EXPECT_EQ(1u, actions["INTERACTION"][0].value_index); EXPECT_EQ(std::chrono::milliseconds(800).count(), actions["INTERACTION"][0].timeout_ms.count()); - EXPECT_EQ(2u, actions["LAUNCH"].size()); + EXPECT_EQ(2u, actions["INTERACTION"][1].node_index); + EXPECT_EQ(1u, actions["INTERACTION"][1].value_index); + EXPECT_EQ(std::chrono::milliseconds(800).count(), + actions["INTERACTION"][1].timeout_ms.count()); + + EXPECT_EQ(3u, actions["LAUNCH"].size()); EXPECT_EQ(0u, actions["LAUNCH"][0].node_index); EXPECT_EQ(1u, actions["LAUNCH"][0].value_index); EXPECT_EQ(std::chrono::milliseconds(500).count(), actions["LAUNCH"][0].timeout_ms.count()); - EXPECT_EQ(1u, actions["LAUNCH"][1].node_index); + EXPECT_EQ(2u, actions["LAUNCH"][1].node_index); EXPECT_EQ(0u, actions["LAUNCH"][1].value_index); - EXPECT_EQ(std::chrono::milliseconds(2000).count(), + EXPECT_EQ(std::chrono::milliseconds(500).count(), actions["LAUNCH"][1].timeout_ms.count()); + + EXPECT_EQ(1u, actions["LAUNCH"][2].node_index); + EXPECT_EQ(0u, actions["LAUNCH"][2].value_index); + EXPECT_EQ(std::chrono::milliseconds(2000).count(), + actions["LAUNCH"][2].timeout_ms.count()); } // Test parsing actions with duplicate node @@ -304,10 +382,10 @@ TEST_F(HintManagerTest, ParseActionDuplicateNodeTest) { std::string from = "\"Node\":\"CPUCluster0MinFreq\""; size_t start_pos = json_doc_.find(from); json_doc_.replace(start_pos, from.length(), - "\"Node\": \"CPUCluster1MinFreq\""); + "\"Node\":\"CPUCluster1MinFreq\""); std::vector<std::unique_ptr<Node>> nodes = HintManager::ParseNodes(json_doc_); - EXPECT_EQ(2u, nodes.size()); + EXPECT_EQ(3u, nodes.size()); std::map<std::string, std::vector<NodeAction>> actions = HintManager::ParseActions(json_doc_, nodes); EXPECT_EQ(0u, actions.size()); @@ -339,29 +417,35 @@ TEST_F(HintManagerTest, GetFromJSONTest) { // Initial default value on Node0 _VerifyPathValue(files_[0 + 2]->path, "384000"); _VerifyPathValue(files_[1 + 2]->path, ""); + _VerifyPropertyValue(prop_, ""); // Do INTERACTION EXPECT_TRUE(hm->DoHint("INTERACTION")); std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS); _VerifyPathValue(files_[0 + 2]->path, "384000"); _VerifyPathValue(files_[1 + 2]->path, "1134000"); + _VerifyPropertyValue(prop_, "LOW"); // Do LAUNCH EXPECT_TRUE(hm->DoHint("LAUNCH")); std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS); _VerifyPathValue(files_[0 + 2]->path, "1134000"); _VerifyPathValue(files_[1 + 2]->path, "1512000"); + _VerifyPropertyValue(prop_, "HIGH"); std::this_thread::sleep_for(500ms); // "LAUNCH" node0 expired _VerifyPathValue(files_[0 + 2]->path, "384000"); _VerifyPathValue(files_[1 + 2]->path, "1512000"); + _VerifyPropertyValue(prop_, "LOW"); EXPECT_TRUE(hm->EndHint("LAUNCH")); std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS); // "LAUNCH" canceled _VerifyPathValue(files_[0 + 2]->path, "384000"); _VerifyPathValue(files_[1 + 2]->path, "1134000"); + _VerifyPropertyValue(prop_, "LOW"); std::this_thread::sleep_for(300ms); // "INTERACTION" node1 expired _VerifyPathValue(files_[0 + 2]->path, "384000"); _VerifyPathValue(files_[1 + 2]->path, "384000"); + _VerifyPropertyValue(prop_, "NONE"); } } // namespace perfmgr diff --git a/libperfmgr/tests/NodeLooperThreadTest.cc b/libperfmgr/tests/NodeLooperThreadTest.cc index 24659cf7..9f36f896 100644 --- a/libperfmgr/tests/NodeLooperThreadTest.cc +++ b/libperfmgr/tests/NodeLooperThreadTest.cc @@ -22,6 +22,7 @@ #include <gtest/gtest.h> +#include "perfmgr/FileNode.h" #include "perfmgr/NodeLooperThread.h" namespace android { @@ -35,14 +36,14 @@ class NodeLooperThreadTest : public ::testing::Test { protected: virtual void SetUp() { std::unique_ptr<TemporaryFile> tf = std::make_unique<TemporaryFile>(); - nodes_.emplace_back( - new Node("n0", tf->path, - {{"n0_value0"}, {"n0_value1"}, {"n0_value2"}}, 2, false)); + nodes_.emplace_back(new FileNode( + "n0", tf->path, {{"n0_value0"}, {"n0_value1"}, {"n0_value2"}}, 2, + false)); files_.emplace_back(std::move(tf)); tf = std::make_unique<TemporaryFile>(); - nodes_.emplace_back( - new Node("n1", tf->path, - {{"n1_value0"}, {"n1_value1"}, {"n1_value2"}}, 2, true)); + nodes_.emplace_back(new FileNode( + "n1", tf->path, {{"n1_value0"}, {"n1_value1"}, {"n1_value2"}}, 2, + true)); files_.emplace_back(std::move(tf)); } diff --git a/libperfmgr/tests/PropertyNodeTest.cc b/libperfmgr/tests/PropertyNodeTest.cc new file mode 100644 index 00000000..925f76d2 --- /dev/null +++ b/libperfmgr/tests/PropertyNodeTest.cc @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2017 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 specic language governing permissions and + * limitations under the License. + */ + +#include <algorithm> +#include <thread> + +#include <android-base/file.h> +#include <android-base/properties.h> +#include <android-base/stringprintf.h> +#include <android-base/test_utils.h> + +#include <gtest/gtest.h> + +#include "perfmgr/PropertyNode.h" + +namespace android { +namespace perfmgr { + +using namespace std::chrono_literals; + +constexpr double kTIMING_TOLERANCE_MS = std::chrono::milliseconds(25).count(); +constexpr auto kSLEEP_TOLERANCE_MS = 2ms; + +static inline void _VerifyPropertyValue(const std::string& path, + const std::string& value) { + std::string s = android::base::GetProperty(path, ""); + EXPECT_EQ(value, s); +} + +static inline const std::string _InitProperty(const std::string path) { + EXPECT_TRUE(android::base::SetProperty(path, "")) + << "failed to clear property"; + return path; +} + +// Test init with no default value +TEST(PropertyNodeTest, NoInitDefaultTest) { + std::string key = _InitProperty("test.libperfmgr.key"); + PropertyNode t("t", key, {{"value0"}, {"value1"}, {"value2"}}, 1, false); + _VerifyPropertyValue(key, ""); +} + +// Test init with default value +TEST(PropertyNodeTest, InitDefaultTest) { + std::string key = _InitProperty("test.libperfmgr.key"); + PropertyNode t("t", key, {{"value0"}, {"value1"}, {"value2"}}, 1, true); + _VerifyPropertyValue(key, "value1"); + std::string key2 = _InitProperty("test.libperfmgr.key2"); + PropertyNode t2("t2", key2, {{"value0"}, {"value1"}, {"value2"}}, 0, true); + _VerifyPropertyValue(key2, "value0"); +} + +// Test DumpToFd +TEST(PropertyNodeTest, DumpToFdTest) { + std::string key = _InitProperty("test.libperfmgr.key"); + PropertyNode t("test_dump", key, {{"value0"}, {"value1"}, {"value2"}}, 1, + true); + TemporaryFile dumptf; + t.DumpToFd(dumptf.fd); + fsync(dumptf.fd); + std::string buf( + android::base::StringPrintf("test_dump\t%s\t1\tvalue1\n", key.c_str())); + std::string s; + EXPECT_TRUE(android::base::ReadFileToString(dumptf.path, &s)) + << strerror(errno); + EXPECT_EQ(buf, s); +} + +// Test GetValueIndex +TEST(PropertyNodeTest, GetValueIndexTest) { + std::string key = _InitProperty("test.libperfmgr.key"); + PropertyNode t("t", key, {{"value0"}, {"value1"}, {"value2"}}, 1, false); + std::size_t index = 0; + EXPECT_TRUE(t.GetValueIndex("value2", &index)); + EXPECT_EQ(2u, index); + index = 1234; + EXPECT_FALSE(t.GetValueIndex("NON_EXIST", &index)); + EXPECT_EQ(1234u, index); +} + +// Test GetValues +TEST(PropertyNodeTest, GetValuesTest) { + std::string key = _InitProperty("test.libperfmgr.key"); + PropertyNode t("t", key, {{"value0"}, {"value1"}, {"value2"}}, 1, false); + std::vector values = t.GetValues(); + EXPECT_EQ(3u, values.size()); + EXPECT_EQ("value0", values[0]); + EXPECT_EQ("value1", values[1]); + EXPECT_EQ("value2", values[2]); +} + +// Test get more properties +TEST(PropertyNodeTest, GetPropertiesTest) { + std::string test_name = "TESTREQ_1"; + std::string test_path = "TEST_PATH"; + PropertyNode t(test_name, test_path, {}, 0, false); + EXPECT_EQ(test_name, t.GetName()); + EXPECT_EQ(test_path, t.GetPath()); + EXPECT_EQ(0u, t.GetValues().size()); + EXPECT_EQ(0u, t.GetDefaultIndex()); + EXPECT_FALSE(t.GetResetOnInit()); +} + +// Test add request +TEST(PropertyNodeTest, AddRequestTest) { + std::string key = _InitProperty("test.libperfmgr.key"); + PropertyNode t("t", key, {{"value0"}, {"value1"}, {"value2"}}, 2, true); + auto start = std::chrono::steady_clock::now(); + EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 500ms)); + std::chrono::milliseconds expire_time = t.Update(); + // Add request @ value1 + _VerifyPropertyValue(key, "value1"); + EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(), + kTIMING_TOLERANCE_MS); + // Add request @ value0 higher prio than value1 + EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 200ms)); + expire_time = t.Update(); + _VerifyPropertyValue(key, "value0"); + EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(), + kTIMING_TOLERANCE_MS); + // Let high prio request timeout, now only request @ value1 active + std::this_thread::sleep_for(expire_time + kSLEEP_TOLERANCE_MS); + expire_time = t.Update(); + _VerifyPropertyValue(key, "value1"); + EXPECT_NEAR(std::chrono::milliseconds(300).count(), expire_time.count(), + kTIMING_TOLERANCE_MS); + // Let all requests timeout, now default value2 + std::this_thread::sleep_for(expire_time + kSLEEP_TOLERANCE_MS); + expire_time = t.Update(); + _VerifyPropertyValue(key, "value2"); + EXPECT_EQ(std::chrono::milliseconds::max(), expire_time); +} + +// Test remove request +TEST(PropertyNodeTest, RemoveRequestTest) { + std::string key = _InitProperty("test.libperfmgr.key"); + PropertyNode t("t", key, {{"value0"}, {"value1"}, {"value2"}}, 2, true); + auto start = std::chrono::steady_clock::now(); + EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 500ms)); + std::chrono::milliseconds expire_time = t.Update(); + // Add request @ value1 + _VerifyPropertyValue(key, "value1"); + EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(), + kTIMING_TOLERANCE_MS); + // Add request @ value0 higher prio than value1 + EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 200ms)); + expire_time = t.Update(); + _VerifyPropertyValue(key, "value0"); + EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(), + kTIMING_TOLERANCE_MS); + // Remove high prio request, now only request @ value1 active + t.RemoveRequest("LAUNCH"); + expire_time = t.Update(); + _VerifyPropertyValue(key, "value1"); + EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(), + kTIMING_TOLERANCE_MS); + // Remove request, now default value2 + t.RemoveRequest("INTERACTION"); + expire_time = t.Update(); + _VerifyPropertyValue(key, "value2"); + EXPECT_EQ(std::chrono::milliseconds::max(), expire_time); +} + +// Test add request +TEST(PropertyNodeTest, AddRequestTestOverride) { + std::string key = _InitProperty("test.libperfmgr.key"); + PropertyNode t("t", key, {{"value0"}, {"value1"}, {"value2"}}, 2, true); + auto start = std::chrono::steady_clock::now(); + EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 500ms)); + std::chrono::milliseconds expire_time = t.Update(); + // Add request @ value1 + _VerifyPropertyValue(key, "value1"); + EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(), + kTIMING_TOLERANCE_MS); + // Add request @ value0 higher prio than value1 + EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 200ms)); + expire_time = t.Update(); + _VerifyPropertyValue(key, "value0"); + EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(), + kTIMING_TOLERANCE_MS); + // Add request @ value0 shorter + EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 100ms)); + expire_time = t.Update(); + _VerifyPropertyValue(key, "value0"); + EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(), + kTIMING_TOLERANCE_MS); + // Add request @ value0 longer + EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 300ms)); + expire_time = t.Update(); + _VerifyPropertyValue(key, "value0"); + EXPECT_NEAR(std::chrono::milliseconds(300).count(), expire_time.count(), + kTIMING_TOLERANCE_MS); + // Remove high prio request, now only request @ value1 active + t.RemoveRequest("LAUNCH"); + expire_time = t.Update(); + _VerifyPropertyValue(key, "value1"); + EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(), + kTIMING_TOLERANCE_MS); + // Remove request, now default value2 + t.RemoveRequest("INTERACTION"); + expire_time = t.Update(); + _VerifyPropertyValue(key, "value2"); + EXPECT_EQ(std::chrono::milliseconds::max(), expire_time); +} + +} // namespace perfmgr +} // namespace android |