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 android.content.res; 18 19 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; 20 import static org.xmlpull.v1.XmlPullParser.END_TAG; 21 import static org.xmlpull.v1.XmlPullParser.START_DOCUMENT; 22 import static org.xmlpull.v1.XmlPullParser.START_TAG; 23 24 import android.content.Context; 25 import android.perftests.utils.BenchmarkState; 26 import android.perftests.utils.PerfStatusReporter; 27 import android.text.TextUtils; 28 import android.util.Log; 29 30 import androidx.test.filters.LargeTest; 31 import androidx.test.platform.app.InstrumentationRegistry; 32 33 import com.android.perftests.core.R; 34 35 import org.junit.After; 36 import org.junit.Assert; 37 import org.junit.Before; 38 import org.junit.Rule; 39 import org.junit.Test; 40 import org.xmlpull.v1.XmlPullParserException; 41 42 import java.io.IOException; 43 import java.util.function.Function; 44 import java.util.function.Supplier; 45 46 @LargeTest 47 public class XmlBlockBenchmark { 48 private static final String TAG = "XmlBlockBenchmark"; 49 private static final String NAMESPACE_ANDROID = "http://schemas.android.com/apk/res/android"; 50 51 @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); 52 private XmlBlock.Parser mParser; 53 cleanCache()54 private void cleanCache() { 55 if (mParser != null) { 56 mParser.close(); 57 } 58 59 final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); 60 final Resources resources = context.getResources(); 61 resources.getImpl().clearAllCaches(); 62 Log.d(TAG, "cleanCache"); 63 } 64 getNewParser()65 private XmlBlock.Parser getNewParser() { 66 cleanCache(); 67 68 final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); 69 final Resources resources = context.getResources(); 70 return (XmlBlock.Parser) resources.getXml(R.layout.linear_layout_for_xmlblock_benchmark); 71 } 72 73 @Before setUp()74 public void setUp() { 75 mParser = getNewParser(); 76 } 77 78 @After tearDown()79 public void tearDown() { 80 cleanCache(); 81 } 82 safeNext()83 int safeNext() throws XmlPullParserException, IOException { 84 while (true) { 85 int parseState = mParser.next(); 86 if (parseState == START_TAG) { 87 return parseState; 88 } else if (parseState == END_DOCUMENT) { 89 mParser = getNewParser(); 90 } 91 } 92 } 93 94 @Test throwNpeCausedByNullDocument()95 public void throwNpeCausedByNullDocument() { 96 final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 97 mParser.close(); 98 while (state.keepRunning()) { 99 try { 100 mParser.getClassAttribute(); 101 } catch (NullPointerException e) { 102 continue; 103 } 104 Assert.fail("It shouldn't be here!"); 105 } 106 } 107 108 @Test getNext()109 public void getNext() throws XmlPullParserException, IOException { 110 final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 111 while (state.keepRunning()) { 112 int parseState = mParser.next(); 113 state.pauseTiming(); 114 if (parseState == END_DOCUMENT) { 115 mParser = getNewParser(); 116 } 117 state.resumeTiming(); 118 } 119 } 120 benchmarkTagFunction(BenchmarkState state, String name, Supplier<T> measureTarget)121 private <T> void benchmarkTagFunction(BenchmarkState state, String name, 122 Supplier<T> measureTarget) 123 throws XmlPullParserException, IOException { 124 while (state.keepRunning()) { 125 state.pauseTiming(); 126 int parseState = safeNext(); 127 128 if (parseState != END_DOCUMENT) { 129 final String tagName = mParser.getName(); 130 state.resumeTiming(); 131 final T value = measureTarget.get(); 132 state.pauseTiming(); 133 Log.d(TAG, 134 TextUtils.formatSimple("%s() in tag %s is %s", name, tagName, value)); 135 } 136 state.resumeTiming(); 137 } 138 } 139 140 @Test getNamespace()141 public void getNamespace() throws XmlPullParserException, IOException { 142 final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 143 benchmarkTagFunction(state, "getNamespace", () -> mParser.getNamespace()); 144 } 145 146 @Test getName()147 public void getName() throws XmlPullParserException, IOException { 148 final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 149 benchmarkTagFunction(state, "getName", () -> mParser.getName()); 150 } 151 152 @Test getText()153 public void getText() throws XmlPullParserException, IOException { 154 final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 155 benchmarkTagFunction(state, "getText", () -> mParser.getText()); 156 } 157 158 @Test getLineNumber()159 public void getLineNumber() throws XmlPullParserException, IOException { 160 final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 161 benchmarkTagFunction(state, "getLineNumber", () -> mParser.getLineNumber()); 162 } 163 164 @Test getAttributeCount()165 public void getAttributeCount() throws XmlPullParserException, IOException { 166 final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 167 benchmarkTagFunction(state, "getAttributeCount", () -> mParser.getAttributeCount()); 168 } 169 benchmarkAttributeFunction(BenchmarkState state, String name, Function<Integer, T> measureTarget)170 private <T> void benchmarkAttributeFunction(BenchmarkState state, String name, 171 Function<Integer, T> measureTarget) 172 throws XmlPullParserException, IOException { 173 boolean needNext = true; 174 boolean needGetCount = false; 175 int attributeCount = 0; 176 int i = 0; 177 while (state.keepRunning()) { 178 state.pauseTiming(); 179 if (needNext) { 180 int parseState = safeNext(); 181 if (parseState == START_TAG) { 182 needNext = false; 183 needGetCount = true; 184 } 185 } 186 187 if (needGetCount) { 188 attributeCount = mParser.getAttributeCount(); 189 needGetCount = false; 190 i = 0; 191 } 192 193 if (i < attributeCount) { 194 final String tagName = mParser.getName(); 195 final String attributeName = mParser.getAttributeName(i); 196 state.resumeTiming(); 197 final T value = measureTarget.apply(i); 198 state.pauseTiming(); 199 Log.d(TAG, 200 TextUtils.formatSimple("%s(%d:%s) in tag %s is %s", name, i, attributeName, 201 tagName, value)); 202 i++; 203 } 204 205 if (i >= attributeCount) { 206 needNext = true; 207 } 208 state.resumeTiming(); 209 } 210 } 211 212 @Test getAttributeNamespace()213 public void getAttributeNamespace() throws XmlPullParserException, IOException { 214 final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 215 benchmarkAttributeFunction(state, "getAttributeNamespace", 216 attributeIndex -> mParser.getAttributeNamespace(attributeIndex)); 217 } 218 219 @Test getAttributeName()220 public void getAttributeName() throws XmlPullParserException, IOException { 221 final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 222 benchmarkAttributeFunction(state, "getAttributeName", 223 attributeIndex -> mParser.getAttributeName(attributeIndex)); 224 } 225 226 @Test getAttributeNameResource()227 public void getAttributeNameResource() throws XmlPullParserException, IOException { 228 final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 229 benchmarkAttributeFunction(state, "getAttributeNameResource", 230 attributeIndex -> mParser.getAttributeNameResource(attributeIndex)); 231 } 232 233 /** 234 * benchmark {@link android.content.res.XmlBlock#nativeGetAttributeDataType(long, int)} and 235 * {@link android.content.res.XmlBlock#nativeGetAttributeData(long, int)} 236 */ 237 @Test getAttributeDataXXX()238 public void getAttributeDataXXX() throws XmlPullParserException, IOException { 239 final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 240 benchmarkAttributeFunction(state, "getAttributeDataXXX", 241 attributeIndex -> mParser.getAttributeValue(attributeIndex)); 242 } 243 244 @Test getSourceResId()245 public void getSourceResId() throws XmlPullParserException, IOException { 246 final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 247 benchmarkTagFunction(state, "getSourceResId", () -> mParser.getSourceResId()); 248 } 249 250 @Test getIdAttribute()251 public void getIdAttribute() throws XmlPullParserException, IOException { 252 final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 253 benchmarkTagFunction(state, "getIdAttribute", () -> mParser.getIdAttribute()); 254 } 255 256 @Test getClassAttribute()257 public void getClassAttribute() throws XmlPullParserException, IOException { 258 final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 259 benchmarkTagFunction(state, "getClassAttribute", () -> mParser.getClassAttribute()); 260 } 261 262 @Test getStyleAttribute()263 public void getStyleAttribute() throws XmlPullParserException, IOException { 264 final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 265 benchmarkTagFunction(state, "getStyleAttribute", () -> mParser.getStyleAttribute()); 266 } 267 268 @Test getAttributeIndex()269 public void getAttributeIndex() throws XmlPullParserException, IOException { 270 final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 271 benchmarkTagFunction(state, "getAttributeValue", 272 () -> mParser.getAttributeValue(NAMESPACE_ANDROID, "layout_width")); 273 } 274 275 @Test parseOneXmlDocument()276 public void parseOneXmlDocument() throws XmlPullParserException, IOException { 277 final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 278 while (state.keepRunning()) { 279 state.pauseTiming(); 280 mParser = getNewParser(); 281 state.resumeTiming(); 282 283 int parseState; 284 while ((parseState = mParser.next()) != END_DOCUMENT) { 285 if (parseState == START_DOCUMENT) { 286 state.pauseTiming(); 287 Log.d(TAG, "parseOneXmlDocument: start document"); 288 state.resumeTiming(); 289 } else if (parseState == START_TAG) { 290 final String tagName = mParser.getName(); 291 state.pauseTiming(); 292 Log.d(TAG, TextUtils.formatSimple("parseOneXmlDocument: tag %s {[", tagName)); 293 state.resumeTiming(); 294 for (int i = 0, count = mParser.getAttributeCount(); i < count; i++) { 295 final String attributeName = mParser.getAttributeName(i); 296 final String attributeValue = mParser.getAttributeValue(i); 297 298 state.pauseTiming(); 299 Log.d(TAG, TextUtils.formatSimple( 300 "parseOneXmlDocument: attribute %d {%s = %s},", i, attributeName, 301 attributeValue)); 302 state.resumeTiming(); 303 } 304 state.pauseTiming(); 305 Log.d(TAG, "parseOneXmlDocument: ]"); 306 state.resumeTiming(); 307 } else if (parseState == END_TAG) { 308 state.pauseTiming(); 309 Log.d(TAG, "parseOneXmlDocument: }"); 310 state.resumeTiming(); 311 } else { 312 final String text = mParser.getText(); 313 state.pauseTiming(); 314 Log.d(TAG, TextUtils.formatSimple( 315 "parseOneXmlDocument: parseState = %d, text = %s", parseState, text)); 316 state.resumeTiming(); 317 } 318 } 319 } 320 } 321 } 322