• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 specic language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <android-base/file.h>
18 #include <android-base/logging.h>
19 #include <android-base/properties.h>
20 #include <android-base/stringprintf.h>
21 #include <gtest/gtest.h>
22 
23 #include <algorithm>
24 #include <thread>
25 
26 #include "perfmgr/FileNode.h"
27 #include "perfmgr/HintManager.h"
28 #include "perfmgr/PropertyNode.h"
29 
30 namespace android {
31 namespace perfmgr {
32 
33 using namespace std::chrono_literals;
34 
35 constexpr auto kSLEEP_TOLERANCE_MS = 50ms;
36 
37 // JSON_CONFIG
38 // {
39 //     "Nodes": [
40 //         {
41 //             "Name": "CPUCluster0MinFreq",
42 //             "Path": "/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq",
43 //             "Values": [
44 //                 "1512000",
45 //                 "1134000",
46 //                 "384000"
47 //             ],
48 //             "DefaultIndex": 2,
49 //             "ResetOnInit": true
50 //         },
51 //         {
52 //             "Name": "CPUCluster1MinFreq",
53 //             "Path": "/sys/devices/system/cpu/cpu4/cpufreq/scaling_min_freq",
54 //             "Values": [
55 //                 "1512000",
56 //                 "1134000",
57 //                 "384000"
58 //             ],
59 //             "HoldFd": true
60 //         },
61 //         {
62 //             "Name": "ModeProperty",
63 //             "Path": "vendor.pwhal.mode",
64 //             "Values": [
65 //                 "HIGH",
66 //                 "LOW",
67 //                 "NONE"
68 //             ],
69 //             "Type": "Property"
70 //         }
71 //     ],
72 //     "Actions": [
73 //         {
74 //             "PowerHint": "INTERACTION",
75 //             "Node": "CPUCluster1MinFreq",
76 //             "Value": "1134000",
77 //             "Duration": 800
78 //         },
79 //         {
80 //             "PowerHint": "INTERACTION",
81 //             "Node": "ModeProperty",
82 //             "Value": "LOW",
83 //             "Duration": 800
84 //         },
85 //         {
86 //             "PowerHint": "LAUNCH",
87 //             "Node": "CPUCluster0MinFreq",
88 //             "Value": "1134000",
89 //             "Duration": 500
90 //         },
91 //         {
92 //             "PowerHint": "LAUNCH",
93 //             "Node": "ModeProperty",
94 //             "Value": "HIGH",
95 //             "Duration": 500
96 //         },
97 //         {
98 //             "PowerHint": "LAUNCH",
99 //             "Node": "CPUCluster1MinFreq",
100 //             "Value": "1512000",
101 //             "Duration": 2000
102 //         }
103 //     ]
104 // }
105 constexpr char kJSON_RAW[] =
106     "{\"Nodes\":[{\"Name\":\"CPUCluster0MinFreq\",\"Path\":\"/sys/devices/"
107     "system/cpu/cpu0/cpufreq/"
108     "scaling_min_freq\",\"Values\":[\"1512000\",\"1134000\",\"384000\"],"
109     "\"DefaultIndex\":2,\"ResetOnInit\":true},{\"Name\":\"CPUCluster1MinFreq\","
110     "\"Path\":\"/sys/devices/system/cpu/cpu4/cpufreq/"
111     "scaling_min_freq\",\"Values\":[\"1512000\",\"1134000\",\"384000\"],"
112     "\"HoldFd\":true},{\"Name\":\"ModeProperty\",\"Path\":\"vendor.pwhal."
113     "mode\",\"Values\":[\"HIGH\",\"LOW\",\"NONE\"],\"Type\":\"Property\"}],"
114     "\"Actions\":[{\"PowerHint\":\"INTERACTION\",\"Node\":"
115     "\"CPUCluster1MinFreq\",\"Value\":\"1134000\",\"Duration\":800},{"
116     "\"PowerHint\":\"INTERACTION\",\"Node\":\"ModeProperty\",\"Value\":\"LOW\","
117     "\"Duration\":800},{\"PowerHint\":\"LAUNCH\",\"Node\":"
118     "\"CPUCluster0MinFreq\",\"Value\":\"1134000\",\"Duration\":500},{"
119     "\"PowerHint\":\"LAUNCH\",\"Node\":\"ModeProperty\",\"Value\":\"HIGH\","
120     "\"Duration\":500},{\"PowerHint\":\"LAUNCH\",\"Node\":"
121     "\"CPUCluster1MinFreq\",\"Value\":\"1512000\",\"Duration\":2000}]}";
122 
123 class HintManagerTest : public ::testing::Test, public HintManager {
124   protected:
HintManagerTest()125     HintManagerTest()
126         : HintManager(nullptr,
127                       std::map<std::string, std::vector<NodeAction>>{}) {
128         android::base::SetMinimumLogSeverity(android::base::VERBOSE);
129         prop_ = "vendor.pwhal.mode";
130     }
131 
SetUp()132     virtual void SetUp() {
133         // Set up 3 dummy nodes
134         std::unique_ptr<TemporaryFile> tf = std::make_unique<TemporaryFile>();
135         nodes_.emplace_back(new FileNode(
136             "n0", tf->path, {{"n0_value0"}, {"n0_value1"}, {"n0_value2"}}, 2,
137             false));
138         files_.emplace_back(std::move(tf));
139         tf = std::make_unique<TemporaryFile>();
140         nodes_.emplace_back(new FileNode(
141             "n1", tf->path, {{"n1_value0"}, {"n1_value1"}, {"n1_value2"}}, 2,
142             true));
143         files_.emplace_back(std::move(tf));
144         nodes_.emplace_back(new PropertyNode(
145             "n2", prop_, {{"n2_value0"}, {"n2_value1"}, {"n2_value2"}}, 2,
146             true));
147         nm_ = new NodeLooperThread(std::move(nodes_));
148         // Set up dummy actions
149         // "INTERACTION"
150         // Node0, value1, 800ms
151         // Node1, value1, forever
152         // Node2, value1, 800ms
153         // "LAUNCH"
154         // Node0, value0, forever
155         // Node1, value0, 400ms
156         // Node2, value0, 400ms
157         actions_ = std::map<std::string, std::vector<NodeAction>>{
158             {"INTERACTION", {{0, 1, 800ms}, {1, 1, 0ms}, {2, 1, 800ms}}},
159             {"LAUNCH", {{0, 0, 0ms}, {1, 0, 400ms}, {2, 0, 400ms}}}};
160 
161         // Prepare dummy files to replace the nodes' path in example json_doc
162         files_.emplace_back(std::make_unique<TemporaryFile>());
163         files_.emplace_back(std::make_unique<TemporaryFile>());
164         // replace file path
165         json_doc_ = kJSON_RAW;
166         std::string from =
167             "/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq";
168         size_t start_pos = json_doc_.find(from);
169         json_doc_.replace(start_pos, from.length(), files_[0 + 2]->path);
170         from = "/sys/devices/system/cpu/cpu4/cpufreq/scaling_min_freq";
171         start_pos = json_doc_.find(from);
172         json_doc_.replace(start_pos, from.length(), files_[1 + 2]->path);
173         EXPECT_TRUE(android::base::SetProperty(prop_, ""))
174             << "failed to clear property";
175     }
176 
TearDown()177     virtual void TearDown() {
178         actions_.clear();
179         nodes_.clear();
180         files_.clear();
181         nm_ = nullptr;
182     }
183     sp<NodeLooperThread> nm_;
184     std::map<std::string, std::vector<NodeAction>> actions_;
185     std::vector<std::unique_ptr<Node>> nodes_;
186     std::vector<std::unique_ptr<TemporaryFile>> files_;
187     std::string json_doc_;
188     std::string prop_;
189 };
190 
_VerifyPropertyValue(const std::string & path,const std::string & value)191 static inline void _VerifyPropertyValue(const std::string& path,
192                                         const std::string& value) {
193     std::string s = android::base::GetProperty(path, "");
194     EXPECT_EQ(value, s);
195 }
196 
_VerifyPathValue(const std::string & path,const std::string & value)197 static inline void _VerifyPathValue(const std::string& path,
198                                     const std::string& value) {
199     std::string s;
200     EXPECT_TRUE(android::base::ReadFileToString(path, &s)) << strerror(errno);
201     EXPECT_EQ(value, s);
202 }
203 
204 // Test GetHints
TEST_F(HintManagerTest,GetHintsTest)205 TEST_F(HintManagerTest, GetHintsTest) {
206     HintManager hm(nm_, actions_);
207     EXPECT_TRUE(hm.Start());
208     std::vector<std::string> hints = hm.GetHints();
209     EXPECT_TRUE(hm.IsRunning());
210     EXPECT_EQ(2u, hints.size());
211     EXPECT_NE(std::find(hints.begin(), hints.end(), "INTERACTION"), hints.end());
212     EXPECT_NE(std::find(hints.begin(), hints.end(), "LAUNCH"), hints.end());
213 }
214 
215 // Test initialization of default values
TEST_F(HintManagerTest,HintInitDefaultTest)216 TEST_F(HintManagerTest, HintInitDefaultTest) {
217     HintManager hm(nm_, actions_);
218     EXPECT_TRUE(hm.Start());
219     std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
220     EXPECT_TRUE(hm.IsRunning());
221     _VerifyPathValue(files_[0]->path, "");
222     _VerifyPathValue(files_[1]->path, "n1_value2");
223     _VerifyPropertyValue(prop_, "n2_value2");
224 }
225 
226 // Test IsHintSupported
TEST_F(HintManagerTest,HintSupportedTest)227 TEST_F(HintManagerTest, HintSupportedTest) {
228     HintManager hm(nm_, actions_);
229     EXPECT_TRUE(hm.IsHintSupported("INTERACTION"));
230     EXPECT_TRUE(hm.IsHintSupported("LAUNCH"));
231     EXPECT_FALSE(hm.IsHintSupported("NO_SUCH_HINT"));
232 }
233 
234 // Test DumpToFd
TEST_F(HintManagerTest,DumpToFdTest)235 TEST_F(HintManagerTest, DumpToFdTest) {
236     HintManager hm(nm_, actions_);
237     TemporaryFile dumptf;
238     hm.DumpToFd(dumptf.fd);
239     fsync(dumptf.fd);
240     std::ostringstream dump_buf;
241     dump_buf << "========== Begin perfmgr nodes ==========\nNode Name\tNode "
242                 "Path\tCurrent Index\tCurrent Value\nn0\t"
243              << files_[0]->path << "\t2\t\nn1\t" << files_[1]->path
244              << "\t2\t\nn2\tvendor.pwhal.mode\t2\t\n==========  End perfmgr "
245                 "nodes  ==========\n";
246     _VerifyPathValue(dumptf.path, dump_buf.str());
247     TemporaryFile dumptf_started;
248     EXPECT_TRUE(hm.Start());
249     std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
250     EXPECT_TRUE(hm.IsRunning());
251     hm.DumpToFd(dumptf_started.fd);
252     fsync(dumptf_started.fd);
253     dump_buf.str("");
254     dump_buf.clear();
255     dump_buf << "========== Begin perfmgr nodes ==========\nNode Name\tNode "
256                 "Path\tCurrent Index\tCurrent Value\nn0\t"
257              << files_[0]->path << "\t2\t\nn1\t" << files_[1]->path
258              << "\t2\tn1_value2\nn2\tvendor.pwhal.mode\t2\tn2_value2\n========="
259                 "=  End perfmgr nodes  ==========\n";
260     _VerifyPathValue(dumptf_started.path, dump_buf.str());
261 }
262 
263 // Test hint/cancel/expire with dummy actions
TEST_F(HintManagerTest,HintTest)264 TEST_F(HintManagerTest, HintTest) {
265     HintManager hm(nm_, actions_);
266     EXPECT_TRUE(hm.Start());
267     EXPECT_TRUE(hm.IsRunning());
268     EXPECT_TRUE(hm.DoHint("INTERACTION"));
269     std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
270     _VerifyPathValue(files_[0]->path, "n0_value1");
271     _VerifyPathValue(files_[1]->path, "n1_value1");
272     _VerifyPropertyValue(prop_, "n2_value1");
273     // this won't change the expire time of INTERACTION hint
274     EXPECT_TRUE(hm.DoHint("INTERACTION", 200ms));
275     // now place new hint
276     EXPECT_TRUE(hm.DoHint("LAUNCH"));
277     std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
278     _VerifyPathValue(files_[0]->path, "n0_value0");
279     _VerifyPathValue(files_[1]->path, "n1_value0");
280     _VerifyPropertyValue(prop_, "n2_value0");
281     EXPECT_TRUE(hm.DoHint("LAUNCH", 500ms));
282     // "LAUNCH" node1 not expired
283     std::this_thread::sleep_for(400ms);
284     _VerifyPathValue(files_[0]->path, "n0_value0");
285     _VerifyPathValue(files_[1]->path, "n1_value0");
286     _VerifyPropertyValue(prop_, "n2_value0");
287     // "LAUNCH" node1 expired
288     std::this_thread::sleep_for(100ms + kSLEEP_TOLERANCE_MS);
289     _VerifyPathValue(files_[0]->path, "n0_value0");
290     _VerifyPathValue(files_[1]->path, "n1_value1");
291     _VerifyPropertyValue(prop_, "n2_value1");
292     EXPECT_TRUE(hm.EndHint("LAUNCH"));
293     std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
294     // "LAUNCH" canceled
295     _VerifyPathValue(files_[0]->path, "n0_value1");
296     _VerifyPathValue(files_[1]->path, "n1_value1");
297     _VerifyPropertyValue(prop_, "n2_value1");
298     std::this_thread::sleep_for(200ms);
299     // "INTERACTION" node0 expired
300     _VerifyPathValue(files_[0]->path, "n0_value2");
301     _VerifyPathValue(files_[1]->path, "n1_value1");
302     _VerifyPropertyValue(prop_, "n2_value2");
303     EXPECT_TRUE(hm.EndHint("INTERACTION"));
304     std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
305     // "INTERACTION" canceled
306     _VerifyPathValue(files_[0]->path, "n0_value2");
307     _VerifyPathValue(files_[1]->path, "n1_value2");
308     _VerifyPropertyValue(prop_, "n2_value2");
309 }
310 
311 // Test parsing nodes
TEST_F(HintManagerTest,ParseNodesTest)312 TEST_F(HintManagerTest, ParseNodesTest) {
313     std::vector<std::unique_ptr<Node>> nodes =
314         HintManager::ParseNodes(json_doc_);
315     EXPECT_EQ(3u, nodes.size());
316     EXPECT_EQ("CPUCluster0MinFreq", nodes[0]->GetName());
317     EXPECT_EQ("CPUCluster1MinFreq", nodes[1]->GetName());
318     EXPECT_EQ(files_[0 + 2]->path, nodes[0]->GetPath());
319     EXPECT_EQ(files_[1 + 2]->path, nodes[1]->GetPath());
320     EXPECT_EQ("1512000", nodes[0]->GetValues()[0]);
321     EXPECT_EQ("1134000", nodes[0]->GetValues()[1]);
322     EXPECT_EQ("384000", nodes[0]->GetValues()[2]);
323     EXPECT_EQ("1512000", nodes[1]->GetValues()[0]);
324     EXPECT_EQ("1134000", nodes[1]->GetValues()[1]);
325     EXPECT_EQ("384000", nodes[1]->GetValues()[2]);
326     EXPECT_EQ(2u, nodes[0]->GetDefaultIndex());
327     EXPECT_EQ(2u, nodes[1]->GetDefaultIndex());
328     EXPECT_TRUE(nodes[0]->GetResetOnInit());
329     EXPECT_FALSE(nodes[1]->GetResetOnInit());
330     // no dynamic_cast intentionally in Android
331     EXPECT_FALSE(reinterpret_cast<FileNode*>(nodes[0].get())->GetHoldFd());
332     EXPECT_TRUE(reinterpret_cast<FileNode*>(nodes[1].get())->GetHoldFd());
333     EXPECT_EQ("ModeProperty", nodes[2]->GetName());
334     EXPECT_EQ(prop_, nodes[2]->GetPath());
335     EXPECT_EQ("HIGH", nodes[2]->GetValues()[0]);
336     EXPECT_EQ("LOW", nodes[2]->GetValues()[1]);
337     EXPECT_EQ("NONE", nodes[2]->GetValues()[2]);
338     EXPECT_EQ(2u, nodes[2]->GetDefaultIndex());
339     EXPECT_FALSE(nodes[2]->GetResetOnInit());
340 }
341 
342 // Test parsing nodes with duplicate name
TEST_F(HintManagerTest,ParseNodesDuplicateNameTest)343 TEST_F(HintManagerTest, ParseNodesDuplicateNameTest) {
344     std::string from = "CPUCluster0MinFreq";
345     size_t start_pos = json_doc_.find(from);
346     json_doc_.replace(start_pos, from.length(), "CPUCluster1MinFreq");
347     std::vector<std::unique_ptr<Node>> nodes =
348         HintManager::ParseNodes(json_doc_);
349     EXPECT_EQ(0u, nodes.size());
350 }
351 
TEST_F(HintManagerTest,ParsePropertyNodesDuplicatNameTest)352 TEST_F(HintManagerTest, ParsePropertyNodesDuplicatNameTest) {
353     std::string from = "ModeProperty";
354     size_t start_pos = json_doc_.find(from);
355     json_doc_.replace(start_pos, from.length(), "CPUCluster1MinFreq");
356     std::vector<std::unique_ptr<Node>> nodes =
357         HintManager::ParseNodes(json_doc_);
358     EXPECT_EQ(0u, nodes.size());
359 }
360 
361 // Test parsing nodes with duplicate path
TEST_F(HintManagerTest,ParseNodesDuplicatePathTest)362 TEST_F(HintManagerTest, ParseNodesDuplicatePathTest) {
363     std::string from = files_[0 + 2]->path;
364     size_t start_pos = json_doc_.find(from);
365     json_doc_.replace(start_pos, from.length(), files_[1 + 2]->path);
366     std::vector<std::unique_ptr<Node>> nodes =
367         HintManager::ParseNodes(json_doc_);
368     EXPECT_EQ(0u, nodes.size());
369 }
370 
371 // Test parsing file node with duplicate value
TEST_F(HintManagerTest,ParseFileNodesDuplicateValueTest)372 TEST_F(HintManagerTest, ParseFileNodesDuplicateValueTest) {
373     std::string from = "1512000";
374     size_t start_pos = json_doc_.find(from);
375     json_doc_.replace(start_pos, from.length(), "1134000");
376     std::vector<std::unique_ptr<Node>> nodes =
377         HintManager::ParseNodes(json_doc_);
378     EXPECT_EQ(0u, nodes.size());
379 }
380 
381 // Test parsing property node with duplicate value
TEST_F(HintManagerTest,ParsePropertyNodesDuplicateValueTest)382 TEST_F(HintManagerTest, ParsePropertyNodesDuplicateValueTest) {
383     std::string from = "HIGH";
384     size_t start_pos = json_doc_.find(from);
385     json_doc_.replace(start_pos, from.length(), "LOW");
386     std::vector<std::unique_ptr<Node>> nodes =
387         HintManager::ParseNodes(json_doc_);
388     EXPECT_EQ(0u, nodes.size());
389 }
390 
391 // Test parsing file node with empty value
TEST_F(HintManagerTest,ParseFileNodesEmptyValueTest)392 TEST_F(HintManagerTest, ParseFileNodesEmptyValueTest) {
393     std::string from = "384000";
394     size_t start_pos = json_doc_.find(from);
395     json_doc_.replace(start_pos, from.length(), "");
396     std::vector<std::unique_ptr<Node>> nodes =
397         HintManager::ParseNodes(json_doc_);
398     EXPECT_EQ(0u, nodes.size());
399 }
400 
401 // Test parsing property node with empty value
TEST_F(HintManagerTest,ParsePropertyNodesEmptyValueTest)402 TEST_F(HintManagerTest, ParsePropertyNodesEmptyValueTest) {
403     std::string from = "LOW";
404     size_t start_pos = json_doc_.find(from);
405     json_doc_.replace(start_pos, from.length(), "");
406     std::vector<std::unique_ptr<Node>> nodes =
407         HintManager::ParseNodes(json_doc_);
408     EXPECT_EQ(3u, nodes.size());
409     EXPECT_EQ("CPUCluster0MinFreq", nodes[0]->GetName());
410     EXPECT_EQ("CPUCluster1MinFreq", nodes[1]->GetName());
411     EXPECT_EQ(files_[0 + 2]->path, nodes[0]->GetPath());
412     EXPECT_EQ(files_[1 + 2]->path, nodes[1]->GetPath());
413     EXPECT_EQ("1512000", nodes[0]->GetValues()[0]);
414     EXPECT_EQ("1134000", nodes[0]->GetValues()[1]);
415     EXPECT_EQ("384000", nodes[0]->GetValues()[2]);
416     EXPECT_EQ("1512000", nodes[1]->GetValues()[0]);
417     EXPECT_EQ("1134000", nodes[1]->GetValues()[1]);
418     EXPECT_EQ("384000", nodes[1]->GetValues()[2]);
419     EXPECT_EQ(2u, nodes[0]->GetDefaultIndex());
420     EXPECT_EQ(2u, nodes[1]->GetDefaultIndex());
421     EXPECT_TRUE(nodes[0]->GetResetOnInit());
422     EXPECT_FALSE(nodes[1]->GetResetOnInit());
423     // no dynamic_cast intentionally in Android
424     EXPECT_FALSE(reinterpret_cast<FileNode*>(nodes[0].get())->GetHoldFd());
425     EXPECT_TRUE(reinterpret_cast<FileNode*>(nodes[1].get())->GetHoldFd());
426     EXPECT_EQ("ModeProperty", nodes[2]->GetName());
427     EXPECT_EQ(prop_, nodes[2]->GetPath());
428     EXPECT_EQ("HIGH", nodes[2]->GetValues()[0]);
429     EXPECT_EQ("", nodes[2]->GetValues()[1]);
430     EXPECT_EQ("NONE", nodes[2]->GetValues()[2]);
431     EXPECT_EQ(2u, nodes[2]->GetDefaultIndex());
432     EXPECT_FALSE(nodes[2]->GetResetOnInit());
433 }
434 
435 // Test parsing invalid json for nodes
TEST_F(HintManagerTest,ParseBadFileNodesTest)436 TEST_F(HintManagerTest, ParseBadFileNodesTest) {
437     std::vector<std::unique_ptr<Node>> nodes =
438         HintManager::ParseNodes("invalid json");
439     EXPECT_EQ(0u, nodes.size());
440     nodes = HintManager::ParseNodes(
441         "{\"devices\":{\"15\":[\"armeabi-v7a\"],\"16\":[\"armeabi-v7a\"],"
442         "\"26\":[\"armeabi-v7a\",\"arm64-v8a\",\"x86\",\"x86_64\"]}}");
443     EXPECT_EQ(0u, nodes.size());
444 }
445 
446 // Test parsing actions
TEST_F(HintManagerTest,ParseActionsTest)447 TEST_F(HintManagerTest, ParseActionsTest) {
448     std::vector<std::unique_ptr<Node>> nodes =
449         HintManager::ParseNodes(json_doc_);
450     std::map<std::string, std::vector<NodeAction>> actions =
451         HintManager::ParseActions(json_doc_, nodes);
452     EXPECT_EQ(2u, actions.size());
453 
454     EXPECT_EQ(2u, actions["INTERACTION"].size());
455     EXPECT_EQ(1u, actions["INTERACTION"][0].node_index);
456     EXPECT_EQ(1u, actions["INTERACTION"][0].value_index);
457     EXPECT_EQ(std::chrono::milliseconds(800).count(),
458               actions["INTERACTION"][0].timeout_ms.count());
459 
460     EXPECT_EQ(2u, actions["INTERACTION"][1].node_index);
461     EXPECT_EQ(1u, actions["INTERACTION"][1].value_index);
462     EXPECT_EQ(std::chrono::milliseconds(800).count(),
463               actions["INTERACTION"][1].timeout_ms.count());
464 
465     EXPECT_EQ(3u, actions["LAUNCH"].size());
466 
467     EXPECT_EQ(0u, actions["LAUNCH"][0].node_index);
468     EXPECT_EQ(1u, actions["LAUNCH"][0].value_index);
469     EXPECT_EQ(std::chrono::milliseconds(500).count(),
470               actions["LAUNCH"][0].timeout_ms.count());
471 
472     EXPECT_EQ(2u, actions["LAUNCH"][1].node_index);
473     EXPECT_EQ(0u, actions["LAUNCH"][1].value_index);
474     EXPECT_EQ(std::chrono::milliseconds(500).count(),
475               actions["LAUNCH"][1].timeout_ms.count());
476 
477     EXPECT_EQ(1u, actions["LAUNCH"][2].node_index);
478     EXPECT_EQ(0u, actions["LAUNCH"][2].value_index);
479     EXPECT_EQ(std::chrono::milliseconds(2000).count(),
480               actions["LAUNCH"][2].timeout_ms.count());
481 }
482 
483 // Test parsing actions with duplicate File node
TEST_F(HintManagerTest,ParseActionDuplicateFileNodeTest)484 TEST_F(HintManagerTest, ParseActionDuplicateFileNodeTest) {
485     std::string from = "\"Node\":\"CPUCluster0MinFreq\"";
486     size_t start_pos = json_doc_.find(from);
487     json_doc_.replace(start_pos, from.length(),
488                       "\"Node\":\"CPUCluster1MinFreq\"");
489     std::vector<std::unique_ptr<Node>> nodes =
490         HintManager::ParseNodes(json_doc_);
491     EXPECT_EQ(3u, nodes.size());
492     std::map<std::string, std::vector<NodeAction>> actions =
493         HintManager::ParseActions(json_doc_, nodes);
494     EXPECT_EQ(0u, actions.size());
495 }
496 
497 // Test parsing actions with duplicate Property node
TEST_F(HintManagerTest,ParseActionDuplicatePropertyNodeTest)498 TEST_F(HintManagerTest, ParseActionDuplicatePropertyNodeTest) {
499     std::string from = "\"Node\":\"CPUCluster0MinFreq\"";
500     size_t start_pos = json_doc_.find(from);
501     json_doc_.replace(start_pos, from.length(), "\"Node\":\"ModeProperty\"");
502     std::vector<std::unique_ptr<Node>> nodes =
503         HintManager::ParseNodes(json_doc_);
504     EXPECT_EQ(3u, nodes.size());
505     std::map<std::string, std::vector<NodeAction>> actions =
506         HintManager::ParseActions(json_doc_, nodes);
507     EXPECT_EQ(0u, actions.size());
508 }
509 
510 // Test parsing invalid json for actions
TEST_F(HintManagerTest,ParseBadActionsTest)511 TEST_F(HintManagerTest, ParseBadActionsTest) {
512     std::vector<std::unique_ptr<Node>> nodes =
513         HintManager::ParseNodes(json_doc_);
514     std::map<std::string, std::vector<NodeAction>> actions =
515         HintManager::ParseActions("invalid json", nodes);
516     EXPECT_EQ(0u, actions.size());
517     actions = HintManager::ParseActions(
518         "{\"devices\":{\"15\":[\"armeabi-v7a\"],\"16\":[\"armeabi-v7a\"],"
519         "\"26\":[\"armeabi-v7a\",\"arm64-v8a\",\"x86\",\"x86_64\"]}}",
520         nodes);
521     EXPECT_EQ(0u, actions.size());
522 }
523 
524 // Test hint/cancel/expire with json config
TEST_F(HintManagerTest,GetFromJSONTest)525 TEST_F(HintManagerTest, GetFromJSONTest) {
526     TemporaryFile json_file;
527     ASSERT_TRUE(android::base::WriteStringToFile(json_doc_, json_file.path))
528         << strerror(errno);
529     std::unique_ptr<HintManager> hm =
530         HintManager::GetFromJSON(json_file.path, false);
531     EXPECT_NE(nullptr, hm.get());
532     EXPECT_FALSE(hm->IsRunning());
533     EXPECT_TRUE(hm->Start());
534     EXPECT_TRUE(hm->IsRunning());
535     hm = HintManager::GetFromJSON(json_file.path);
536     EXPECT_NE(nullptr, hm.get());
537     EXPECT_TRUE(hm->IsRunning());
538     std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
539     EXPECT_TRUE(hm->IsRunning());
540     // Initial default value on Node0
541     _VerifyPathValue(files_[0 + 2]->path, "384000");
542     _VerifyPathValue(files_[1 + 2]->path, "");
543     _VerifyPropertyValue(prop_, "");
544     // Do INTERACTION
545     EXPECT_TRUE(hm->DoHint("INTERACTION"));
546     std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
547     _VerifyPathValue(files_[0 + 2]->path, "384000");
548     _VerifyPathValue(files_[1 + 2]->path, "1134000");
549     _VerifyPropertyValue(prop_, "LOW");
550     // Do LAUNCH
551     EXPECT_TRUE(hm->DoHint("LAUNCH"));
552     std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
553     _VerifyPathValue(files_[0 + 2]->path, "1134000");
554     _VerifyPathValue(files_[1 + 2]->path, "1512000");
555     _VerifyPropertyValue(prop_, "HIGH");
556     std::this_thread::sleep_for(500ms);
557     // "LAUNCH" node0 expired
558     _VerifyPathValue(files_[0 + 2]->path, "384000");
559     _VerifyPathValue(files_[1 + 2]->path, "1512000");
560     _VerifyPropertyValue(prop_, "LOW");
561     EXPECT_TRUE(hm->EndHint("LAUNCH"));
562     std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
563     // "LAUNCH" canceled
564     _VerifyPathValue(files_[0 + 2]->path, "384000");
565     _VerifyPathValue(files_[1 + 2]->path, "1134000");
566     _VerifyPropertyValue(prop_, "LOW");
567     std::this_thread::sleep_for(300ms);
568     // "INTERACTION" node1 expired
569     _VerifyPathValue(files_[0 + 2]->path, "384000");
570     _VerifyPathValue(files_[1 + 2]->path, "384000");
571     _VerifyPropertyValue(prop_, "NONE");
572 }
573 
574 }  // namespace perfmgr
575 }  // namespace android
576