• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 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 package com.android.internal.util;
18 
19 import static android.text.TextUtils.formatSimple;
20 
21 import static com.android.internal.util.LatencyTracker.STATSD_ACTION;
22 
23 import static com.google.common.truth.Truth.assertThat;
24 import static com.google.common.truth.Truth.assertWithMessage;
25 
26 import android.provider.DeviceConfig;
27 import android.util.Log;
28 
29 import androidx.test.ext.junit.runners.AndroidJUnit4;
30 import androidx.test.filters.SmallTest;
31 
32 import com.google.common.truth.Expect;
33 
34 import org.junit.Before;
35 import org.junit.Rule;
36 import org.junit.Test;
37 import org.junit.runner.RunWith;
38 
39 import java.lang.reflect.Field;
40 import java.lang.reflect.Modifier;
41 import java.time.Duration;
42 import java.util.Arrays;
43 import java.util.HashMap;
44 import java.util.HashSet;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.stream.Collectors;
48 
49 @SmallTest
50 @RunWith(AndroidJUnit4.class)
51 public class LatencyTrackerTest {
52     private static final String TAG = LatencyTrackerTest.class.getSimpleName();
53     private static final String ENUM_NAME_PREFIX = "UIACTION_LATENCY_REPORTED__ACTION__";
54     private static final String ACTION_ENABLE_SUFFIX = "_enable";
55     private static final Duration TEST_TIMEOUT = Duration.ofMillis(500);
56 
57     @Rule
58     public final Expect mExpect = Expect.create();
59 
60     @Before
setUp()61     public void setUp() {
62         DeviceConfig.deleteProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
63                 LatencyTracker.SETTINGS_ENABLED_KEY);
64         getAllActions().forEach(action -> {
65             DeviceConfig.deleteProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
66                     action.getName().toLowerCase() + ACTION_ENABLE_SUFFIX);
67         });
68     }
69 
70     @Test
testCujsMapToEnumsCorrectly()71     public void testCujsMapToEnumsCorrectly() {
72         List<Field> actions = getAllActions();
73         Map<Integer, String> enumsMap = Arrays.stream(FrameworkStatsLog.class.getDeclaredFields())
74                 .filter(f -> f.getName().startsWith(ENUM_NAME_PREFIX)
75                         && Modifier.isStatic(f.getModifiers())
76                         && f.getType() == int.class)
77                 .collect(Collectors.toMap(this::getIntFieldChecked, Field::getName));
78 
79         assertThat(enumsMap.size() - 1).isEqualTo(actions.size());
80 
81         actions.forEach(f -> {
82             final int action = getIntFieldChecked(f);
83             final String actionName = f.getName();
84             final String expectedEnumName = formatSimple("%s%s", ENUM_NAME_PREFIX, actionName);
85             final int enumKey = STATSD_ACTION[action];
86             final String enumName = enumsMap.get(enumKey);
87             final String expectedActionName = LatencyTracker.getNameOfAction(enumKey);
88             mExpect
89                     .withMessage(formatSimple(
90                             "%s (%d) not matches %s (%d)", actionName, action, enumName, enumKey))
91                     .that(expectedEnumName.equals(enumName))
92                     .isTrue();
93             mExpect
94                     .withMessage(
95                             formatSimple("getNameOfAction(%d) not matches: %s, expected=%s",
96                                     enumKey, actionName, expectedActionName))
97                     .that(actionName.equals(expectedActionName))
98                     .isTrue();
99         });
100     }
101 
102     @Test
testCujTypeEnumCorrectlyDefined()103     public void testCujTypeEnumCorrectlyDefined() throws Exception {
104         List<Field> cujEnumFields = getAllActions();
105         HashSet<Integer> allValues = new HashSet<>();
106         for (Field field : cujEnumFields) {
107             int fieldValue = field.getInt(null);
108             assertWithMessage(
109                     "Field %s must have a mapping to a value in STATSD_ACTION",
110                     field.getName())
111                     .that(fieldValue < STATSD_ACTION.length)
112                     .isTrue();
113             assertWithMessage("All CujType values must be unique. Field %s repeats existing value.",
114                     field.getName())
115                     .that(allValues.add(fieldValue))
116                     .isTrue();
117         }
118     }
119 
120     @Test
121     public void testIsEnabled_globalEnabled() {
122         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
123                 LatencyTracker.SETTINGS_ENABLED_KEY, "true", false);
124         LatencyTracker latencyTracker = new LatencyTracker();
125         waitForLatencyTrackerToUpdateProperties(latencyTracker);
126         assertThat(latencyTracker.isEnabled()).isTrue();
127     }
128 
129     @Test
130     public void testIsEnabled_globalDisabled() {
131         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
132                 LatencyTracker.SETTINGS_ENABLED_KEY, "false", false);
133         LatencyTracker latencyTracker = new LatencyTracker();
134         waitForLatencyTrackerToUpdateProperties(latencyTracker);
135         assertThat(latencyTracker.isEnabled()).isFalse();
136     }
137 
138     @Test
139     public void testIsEnabledAction_useGlobalValueWhenActionEnableIsNotSet() {
140         LatencyTracker latencyTracker = new LatencyTracker();
141         // using a single test action, but this applies to all actions
142         int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
143         Log.i(TAG, "setting property=" + LatencyTracker.SETTINGS_ENABLED_KEY + ", value=true");
144         latencyTracker.mDeviceConfigPropertiesUpdated.close();
145         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
146                 LatencyTracker.SETTINGS_ENABLED_KEY, "true", false);
147         waitForLatencyTrackerToUpdateProperties(latencyTracker);
148         assertThat(
149                 latencyTracker.isEnabled(action)).isTrue();
150 
151         Log.i(TAG, "setting property=" + LatencyTracker.SETTINGS_ENABLED_KEY
152                 + ", value=false");
153         latencyTracker.mDeviceConfigPropertiesUpdated.close();
154         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
155                 LatencyTracker.SETTINGS_ENABLED_KEY, "false", false);
156         waitForLatencyTrackerToUpdateProperties(latencyTracker);
157         assertThat(latencyTracker.isEnabled(action)).isFalse();
158     }
159 
160     @Test
161     public void testIsEnabledAction_actionPropertyOverridesGlobalProperty()
162             throws DeviceConfig.BadConfigException {
163         LatencyTracker latencyTracker = new LatencyTracker();
164         // using a single test action, but this applies to all actions
165         int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
166         String actionEnableProperty = "action_show_voice_interaction" + ACTION_ENABLE_SUFFIX;
167         Log.i(TAG, "setting property=" + actionEnableProperty + ", value=true");
168 
169         latencyTracker.mDeviceConfigPropertiesUpdated.close();
170         Map<String, String> properties = new HashMap<String, String>() {{
171             put(LatencyTracker.SETTINGS_ENABLED_KEY, "false");
172             put(actionEnableProperty, "true");
173         }};
174         DeviceConfig.setProperties(
175                 new DeviceConfig.Properties(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
176                         properties));
177         waitForLatencyTrackerToUpdateProperties(latencyTracker);
178         assertThat(latencyTracker.isEnabled(action)).isTrue();
179 
180         latencyTracker.mDeviceConfigPropertiesUpdated.close();
181         Log.i(TAG, "setting property=" + actionEnableProperty + ", value=false");
182         properties.put(LatencyTracker.SETTINGS_ENABLED_KEY, "true");
183         properties.put(actionEnableProperty, "false");
184         DeviceConfig.setProperties(
185                     new DeviceConfig.Properties(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
186                             properties));
187         waitForLatencyTrackerToUpdateProperties(latencyTracker);
188         assertThat(latencyTracker.isEnabled(action)).isFalse();
189     }
190 
191     private void waitForLatencyTrackerToUpdateProperties(LatencyTracker latencyTracker) {
192         try {
193             Thread.sleep(TEST_TIMEOUT.toMillis());
194         } catch (InterruptedException e) {
195             e.printStackTrace();
196         }
197         assertThat(latencyTracker.mDeviceConfigPropertiesUpdated.block(
198                 TEST_TIMEOUT.toMillis())).isTrue();
199     }
200 
201     private List<Field> getAllActions() {
202         return Arrays.stream(LatencyTracker.class.getDeclaredFields())
203                 .filter(field -> field.getName().startsWith("ACTION_")
204                         && Modifier.isStatic(field.getModifiers())
205                         && field.getType() == int.class)
206                 .collect(Collectors.toList());
207     }
208 
getIntFieldChecked(Field field)209     private int getIntFieldChecked(Field field) {
210         try {
211             return field.getInt(null);
212         } catch (IllegalAccessException ex) {
213             throw new RuntimeException(ex);
214         }
215     }
216 }
217