1 /*
2  * Copyright 2022 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 "UidCpuStatsCollector.h"
18 
19 #include <android-base/file.h>
20 #include <android-base/stringprintf.h>
21 #include <gmock/gmock.h>
22 
23 #include <inttypes.h>
24 
25 #include <unordered_map>
26 
27 namespace android {
28 namespace automotive {
29 namespace watchdog {
30 
31 using ::android::base::StringAppendF;
32 using ::android::base::WriteStringToFile;
33 using ::testing::UnorderedElementsAreArray;
34 
35 namespace {
36 
toString(const std::unordered_map<uid_t,int64_t> & cpuTimeMillisByUid)37 std::string toString(const std::unordered_map<uid_t, int64_t>& cpuTimeMillisByUid) {
38     std::string buffer;
39     for (const auto& [uid, cpuTime] : cpuTimeMillisByUid) {
40         StringAppendF(&buffer, "{%d: %" PRId64 "}\n", uid, cpuTime);
41     }
42     return buffer;
43 }
44 
45 }  // namespace
46 
TEST(UidCpuStatsCollectorTest,TestValidStatFile)47 TEST(UidCpuStatsCollectorTest, TestValidStatFile) {
48     // Format: <uid>: <user_time_micro_seconds> <system_time_micro_seconds>
49     constexpr char firstSnapshot[] = "0: 7000000 5000000\n"
50                                      "100: 1256700 4545636\n"
51                                      "1009: 500000 500000\n"
52                                      "1001000: 40000 30000\n";
53     std::unordered_map<uid_t, int64_t> expectedFirstUsage = {{0, 12'000},
54                                                              {100, 5'801},
55                                                              {1009, 1'000},
56                                                              {1001000, 70}};
57 
58     TemporaryFile tf;
59     ASSERT_NE(tf.fd, -1);
60     ASSERT_TRUE(WriteStringToFile(firstSnapshot, tf.path));
61 
62     UidCpuStatsCollector collector(tf.path);
63     collector.init();
64 
65     ASSERT_TRUE(collector.enabled()) << "Temporary file is inaccessible";
66     ASSERT_RESULT_OK(collector.collect());
67 
68     const auto& actualFirstUsage = collector.deltaStats();
69     EXPECT_THAT(actualFirstUsage, UnorderedElementsAreArray(expectedFirstUsage))
70             << "Expected: " << toString(expectedFirstUsage)
71             << "Actual: " << toString(actualFirstUsage);
72 
73     constexpr char secondSnapshot[] = "0: 7500000 5000000\n"
74                                       "100: 1266700 4565636\n"
75                                       "1009: 700000 600000\n"
76                                       "1001000: 40000 30000\n";
77     std::unordered_map<uid_t, int64_t> expectedSecondUsage = {{0, 500}, {100, 30}, {1009, 300}};
78 
79     ASSERT_TRUE(WriteStringToFile(secondSnapshot, tf.path));
80     ASSERT_RESULT_OK(collector.collect());
81 
82     const auto& actualSecondUsage = collector.deltaStats();
83     EXPECT_THAT(actualSecondUsage, UnorderedElementsAreArray(expectedSecondUsage))
84             << "Expected: " << toString(expectedSecondUsage)
85             << "Actual: " << toString(actualSecondUsage);
86 }
87 
TEST(UidCpuStatsCollectorTest,TestErrorOnInvalidStatFile)88 TEST(UidCpuStatsCollectorTest, TestErrorOnInvalidStatFile) {
89     constexpr char contents[] = "0: 7000000 5000000\n"
90                                 "100: 1256700 4545636\n"
91                                 "1009: 500000 500000\n"
92                                 "1001000: CORRUPTED DATA\n";
93     TemporaryFile tf;
94     ASSERT_NE(tf.fd, -1);
95     ASSERT_TRUE(WriteStringToFile(contents, tf.path));
96 
97     UidCpuStatsCollector collector(tf.path);
98     collector.init();
99 
100     ASSERT_TRUE(collector.enabled()) << "Temporary file is inaccessible";
101     EXPECT_FALSE(collector.collect().ok()) << "No error returned for invalid file";
102 }
103 
TEST(UidCpuStatsCollectorTest,TestErrorOnEmptyStatFile)104 TEST(UidCpuStatsCollectorTest, TestErrorOnEmptyStatFile) {
105     TemporaryFile tf;
106     ASSERT_NE(tf.fd, -1);
107 
108     UidCpuStatsCollector collector(tf.path);
109     collector.init();
110 
111     ASSERT_TRUE(collector.enabled()) << "Temporary file is inaccessible";
112     EXPECT_FALSE(collector.collect().ok()) << "No error returned for invalid file";
113 }
114 
115 }  // namespace watchdog
116 }  // namespace automotive
117 }  // namespace android
118