1/*
2 * Copyright (C) 2016 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 <gtest/gtest.h>
18
19#include "service/GraphicsStatsService.h"
20
21#include <frameworks/base/core/proto/android/service/graphicsstats.pb.h>
22
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <unistd.h>
28
29using namespace android;
30using namespace android::uirenderer;
31
32std::string findRootPath() {
33    char path[1024];
34    ssize_t r = readlink("/proc/self/exe", path, 1024);
35    // < 1023 because we need room for the null terminator
36    if (r <= 0 || r > 1023) {
37        int err = errno;
38        fprintf(stderr, "Failed to read from /proc/self/exe; r=%zd, err=%d (%s)\n",
39                r, err, strerror(err));
40        exit(EXIT_FAILURE);
41    }
42    while (--r > 0) {
43        if (path[r] == '/') {
44            path[r] = '\0';
45            return std::string(path);
46        }
47    }
48    return std::string();
49}
50
51// No code left untested
52TEST(GraphicsStats, findRootPath) {
53    std::string expected = "/data/nativetest/hwui_unit_tests";
54    EXPECT_EQ(expected, findRootPath());
55}
56
57TEST(GraphicsStats, saveLoad) {
58    std::string path = findRootPath() + "/test_saveLoad";
59    std::string packageName = "com.test.saveLoad";
60    ProfileData mockData;
61    mockData.jankFrameCount = 20;
62    mockData.totalFrameCount = 100;
63    mockData.statStartTime = 10000;
64    // Fill with patterned data we can recognize but which won't map to a
65    // memset or basic for iteration count
66    for (size_t i = 0; i < mockData.frameCounts.size(); i++) {
67        mockData.frameCounts[i] = ((i % 10) + 1) * 2;
68    }
69    for (size_t i = 0; i < mockData.slowFrameCounts.size(); i++) {
70        mockData.slowFrameCounts[i] = (i % 5) + 1;
71    }
72    GraphicsStatsService::saveBuffer(path, packageName, 5, 3000, 7000, &mockData);
73    service::GraphicsStatsProto loadedProto;
74    EXPECT_TRUE(GraphicsStatsService::parseFromFile(path, &loadedProto));
75    // Clean up the file
76    unlink(path.c_str());
77
78    EXPECT_EQ(packageName, loadedProto.package_name());
79    EXPECT_EQ(5, loadedProto.version_code());
80    EXPECT_EQ(3000, loadedProto.stats_start());
81    EXPECT_EQ(7000, loadedProto.stats_end());
82    // ASSERT here so we don't continue with a nullptr deref crash if this is false
83    ASSERT_TRUE(loadedProto.has_summary());
84    EXPECT_EQ(20, loadedProto.summary().janky_frames());
85    EXPECT_EQ(100, loadedProto.summary().total_frames());
86    EXPECT_EQ(mockData.frameCounts.size() + mockData.slowFrameCounts.size(),
87            (size_t) loadedProto.histogram_size());
88    for (size_t i = 0; i < (size_t) loadedProto.histogram_size(); i++) {
89        int expectedCount, expectedBucket;
90        if (i < mockData.frameCounts.size()) {
91            expectedCount = ((i % 10) + 1) * 2;
92            expectedBucket = JankTracker::frameTimeForFrameCountIndex(i);
93        } else {
94            int temp = i - mockData.frameCounts.size();
95            expectedCount = (temp % 5) + 1;
96            expectedBucket = JankTracker::frameTimeForSlowFrameCountIndex(temp);
97        }
98        EXPECT_EQ(expectedCount, loadedProto.histogram().Get(i).frame_count());
99        EXPECT_EQ(expectedBucket, loadedProto.histogram().Get(i).render_millis());
100    }
101}
102
103TEST(GraphicsStats, merge) {
104    std::string path = findRootPath() + "/test_merge";
105    std::string packageName = "com.test.merge";
106    ProfileData mockData;
107    mockData.jankFrameCount = 20;
108    mockData.totalFrameCount = 100;
109    mockData.statStartTime = 10000;
110    // Fill with patterned data we can recognize but which won't map to a
111    // memset or basic for iteration count
112    for (size_t i = 0; i < mockData.frameCounts.size(); i++) {
113        mockData.frameCounts[i] = ((i % 10) + 1) * 2;
114    }
115    for (size_t i = 0; i < mockData.slowFrameCounts.size(); i++) {
116        mockData.slowFrameCounts[i] = (i % 5) + 1;
117    }
118    GraphicsStatsService::saveBuffer(path, packageName, 5, 3000, 7000, &mockData);
119    mockData.jankFrameCount = 50;
120    mockData.totalFrameCount = 500;
121    for (size_t i = 0; i < mockData.frameCounts.size(); i++) {
122        mockData.frameCounts[i] = (i % 5) + 1;
123    }
124    for (size_t i = 0; i < mockData.slowFrameCounts.size(); i++) {
125        mockData.slowFrameCounts[i] = ((i % 10) + 1) * 2;
126    }
127    GraphicsStatsService::saveBuffer(path, packageName, 5, 7050, 10000, &mockData);
128
129    service::GraphicsStatsProto loadedProto;
130    EXPECT_TRUE(GraphicsStatsService::parseFromFile(path, &loadedProto));
131    // Clean up the file
132    unlink(path.c_str());
133
134    EXPECT_EQ(packageName, loadedProto.package_name());
135    EXPECT_EQ(5, loadedProto.version_code());
136    EXPECT_EQ(3000, loadedProto.stats_start());
137    EXPECT_EQ(10000, loadedProto.stats_end());
138    // ASSERT here so we don't continue with a nullptr deref crash if this is false
139    ASSERT_TRUE(loadedProto.has_summary());
140    EXPECT_EQ(20 + 50, loadedProto.summary().janky_frames());
141    EXPECT_EQ(100 + 500, loadedProto.summary().total_frames());
142    EXPECT_EQ(mockData.frameCounts.size() + mockData.slowFrameCounts.size(),
143            (size_t) loadedProto.histogram_size());
144    for (size_t i = 0; i < (size_t) loadedProto.histogram_size(); i++) {
145        int expectedCount, expectedBucket;
146        if (i < mockData.frameCounts.size()) {
147            expectedCount = ((i % 10) + 1) * 2;
148            expectedCount += (i % 5) + 1;
149            expectedBucket = JankTracker::frameTimeForFrameCountIndex(i);
150        } else {
151            int temp = i - mockData.frameCounts.size();
152            expectedCount = (temp % 5) + 1;
153            expectedCount += ((temp % 10) + 1) * 2;
154            expectedBucket = JankTracker::frameTimeForSlowFrameCountIndex(temp);
155        }
156        EXPECT_EQ(expectedCount, loadedProto.histogram().Get(i).frame_count());
157        EXPECT_EQ(expectedBucket, loadedProto.histogram().Get(i).render_millis());
158    }
159}