1 /* 2 * Copyright (C) 2019 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.util; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static com.google.common.truth.Truth.assertWithMessage; 21 import static java.nio.charset.StandardCharsets.UTF_8; 22 23 import android.os.SystemClock; 24 import androidx.test.filters.SmallTest; 25 import androidx.test.runner.AndroidJUnit4; 26 import com.android.modules.utils.build.SdkLevel; 27 import com.google.common.collect.Range; 28 import java.nio.ByteBuffer; 29 import java.nio.ByteOrder; 30 import java.util.Random; 31 import org.junit.Test; 32 import org.junit.runner.RunWith; 33 34 /** 35 * Internal tests for {@link StatsEvent}. 36 */ 37 @SmallTest 38 @RunWith(AndroidJUnit4.class) 39 public class StatsEventTest { 40 41 @Test testNoFields()42 public void testNoFields() { 43 final long minTimestamp = SystemClock.elapsedRealtimeNanos(); 44 final StatsEvent statsEvent = StatsEvent.newBuilder().usePooledBuffer().build(); 45 final long maxTimestamp = SystemClock.elapsedRealtimeNanos(); 46 47 final int expectedAtomId = 0; 48 assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId); 49 50 final ByteBuffer buffer = 51 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN); 52 53 assertWithMessage("Root element in buffer is not TYPE_OBJECT") 54 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT); 55 56 assertWithMessage("Incorrect number of elements in root object") 57 .that(buffer.get()).isEqualTo(3); 58 59 assertWithMessage("First element is not timestamp") 60 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG); 61 62 assertWithMessage("Incorrect timestamp") 63 .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp)); 64 65 assertWithMessage("Second element is not atom id") 66 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT); 67 68 assertWithMessage("Incorrect atom id") 69 .that(buffer.getInt()).isEqualTo(expectedAtomId); 70 71 assertWithMessage("Third element is not errors type") 72 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_ERRORS); 73 74 final int errorMask = buffer.getInt(); 75 76 assertWithMessage("ERROR_NO_ATOM_ID should be the only error in the error mask") 77 .that(errorMask).isEqualTo(StatsEvent.ERROR_NO_ATOM_ID); 78 79 assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position()); 80 81 statsEvent.release(); 82 } 83 84 @Test testOnlyAtomId()85 public void testOnlyAtomId() { 86 final int expectedAtomId = 109; 87 88 final long minTimestamp = SystemClock.elapsedRealtimeNanos(); 89 final StatsEvent statsEvent = StatsEvent.newBuilder() 90 .setAtomId(expectedAtomId) 91 .usePooledBuffer() 92 .build(); 93 final long maxTimestamp = SystemClock.elapsedRealtimeNanos(); 94 95 assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId); 96 97 final ByteBuffer buffer = 98 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN); 99 100 assertWithMessage("Root element in buffer is not TYPE_OBJECT") 101 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT); 102 103 assertWithMessage("Incorrect number of elements in root object") 104 .that(buffer.get()).isEqualTo(2); 105 106 assertWithMessage("First element is not timestamp") 107 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG); 108 109 assertWithMessage("Incorrect timestamp") 110 .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp)); 111 112 assertWithMessage("Second element is not atom id") 113 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT); 114 115 assertWithMessage("Incorrect atom id") 116 .that(buffer.getInt()).isEqualTo(expectedAtomId); 117 118 assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position()); 119 120 statsEvent.release(); 121 } 122 123 @Test testIntBooleanIntInt()124 public void testIntBooleanIntInt() { 125 final int expectedAtomId = 109; 126 final int field1 = 1; 127 final boolean field2 = true; 128 final int field3 = 3; 129 final int field4 = 4; 130 131 final long minTimestamp = SystemClock.elapsedRealtimeNanos(); 132 final StatsEvent statsEvent = StatsEvent.newBuilder() 133 .setAtomId(expectedAtomId) 134 .writeInt(field1) 135 .writeBoolean(field2) 136 .writeInt(field3) 137 .writeInt(field4) 138 .usePooledBuffer() 139 .build(); 140 final long maxTimestamp = SystemClock.elapsedRealtimeNanos(); 141 142 assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId); 143 144 final ByteBuffer buffer = 145 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN); 146 147 assertWithMessage("Root element in buffer is not TYPE_OBJECT") 148 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT); 149 150 assertWithMessage("Incorrect number of elements in root object") 151 .that(buffer.get()).isEqualTo(6); 152 153 assertWithMessage("First element is not timestamp") 154 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG); 155 156 assertWithMessage("Incorrect timestamp") 157 .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp)); 158 159 assertWithMessage("Second element is not atom id") 160 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT); 161 162 assertWithMessage("Incorrect atom id") 163 .that(buffer.getInt()).isEqualTo(expectedAtomId); 164 165 assertWithMessage("First field is not Int") 166 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT); 167 168 assertWithMessage("Incorrect field 1") 169 .that(buffer.getInt()).isEqualTo(field1); 170 171 assertWithMessage("Second field is not Boolean") 172 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_BOOLEAN); 173 174 assertWithMessage("Incorrect field 2") 175 .that(buffer.get()).isEqualTo(1); 176 177 assertWithMessage("Third field is not Int") 178 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT); 179 180 assertWithMessage("Incorrect field 3") 181 .that(buffer.getInt()).isEqualTo(field3); 182 183 assertWithMessage("Fourth field is not Int") 184 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT); 185 186 assertWithMessage("Incorrect field 4") 187 .that(buffer.getInt()).isEqualTo(field4); 188 189 assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position()); 190 191 statsEvent.release(); 192 } 193 194 @Test testStringFloatByteArray()195 public void testStringFloatByteArray() { 196 final int expectedAtomId = 109; 197 final String field1 = "Str 1"; 198 final float field2 = 9.334f; 199 final byte[] field3 = new byte[] { 56, 23, 89, -120 }; 200 201 final long minTimestamp = SystemClock.elapsedRealtimeNanos(); 202 final StatsEvent statsEvent = StatsEvent.newBuilder() 203 .setAtomId(expectedAtomId) 204 .writeString(field1) 205 .writeFloat(field2) 206 .writeByteArray(field3) 207 .usePooledBuffer() 208 .build(); 209 final long maxTimestamp = SystemClock.elapsedRealtimeNanos(); 210 211 assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId); 212 213 final ByteBuffer buffer = 214 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN); 215 216 assertWithMessage("Root element in buffer is not TYPE_OBJECT") 217 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT); 218 219 assertWithMessage("Incorrect number of elements in root object") 220 .that(buffer.get()).isEqualTo(5); 221 222 assertWithMessage("First element is not timestamp") 223 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG); 224 225 assertWithMessage("Incorrect timestamp") 226 .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp)); 227 228 assertWithMessage("Second element is not atom id") 229 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT); 230 231 assertWithMessage("Incorrect atom id") 232 .that(buffer.getInt()).isEqualTo(expectedAtomId); 233 234 assertWithMessage("First field is not String") 235 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_STRING); 236 237 final String field1Actual = getStringFromByteBuffer(buffer); 238 assertWithMessage("Incorrect field 1") 239 .that(field1Actual).isEqualTo(field1); 240 241 assertWithMessage("Second field is not Float") 242 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_FLOAT); 243 244 assertWithMessage("Incorrect field 2") 245 .that(buffer.getFloat()).isEqualTo(field2); 246 247 assertWithMessage("Third field is not byte array") 248 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_BYTE_ARRAY); 249 250 final byte[] field3Actual = getByteArrayFromByteBuffer(buffer); 251 assertWithMessage("Incorrect field 3") 252 .that(field3Actual).isEqualTo(field3); 253 254 assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position()); 255 256 statsEvent.release(); 257 } 258 259 @Test testAttributionChainLong()260 public void testAttributionChainLong() { 261 final int expectedAtomId = 109; 262 final int[] uids = new int[] { 1, 2, 3, 4, 5 }; 263 final String[] tags = new String[] { "1", "2", "3", "4", "5" }; 264 final long field2 = -230909823L; 265 266 final long minTimestamp = SystemClock.elapsedRealtimeNanos(); 267 final StatsEvent statsEvent = StatsEvent.newBuilder() 268 .setAtomId(expectedAtomId) 269 .writeAttributionChain(uids, tags) 270 .writeLong(field2) 271 .usePooledBuffer() 272 .build(); 273 final long maxTimestamp = SystemClock.elapsedRealtimeNanos(); 274 275 assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId); 276 277 final ByteBuffer buffer = 278 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN); 279 280 assertWithMessage("Root element in buffer is not TYPE_OBJECT") 281 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT); 282 283 assertWithMessage("Incorrect number of elements in root object") 284 .that(buffer.get()).isEqualTo(4); 285 286 assertWithMessage("First element is not timestamp") 287 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG); 288 289 assertWithMessage("Incorrect timestamp") 290 .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp)); 291 292 assertWithMessage("Second element is not atom id") 293 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT); 294 295 assertWithMessage("Incorrect atom id") 296 .that(buffer.getInt()).isEqualTo(expectedAtomId); 297 298 assertWithMessage("First field is not Attribution Chain") 299 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_ATTRIBUTION_CHAIN); 300 301 assertWithMessage("Incorrect number of attribution nodes") 302 .that(buffer.get()).isEqualTo((byte) uids.length); 303 304 for (int i = 0; i < tags.length; i++) { 305 assertWithMessage("Incorrect uid in Attribution Chain") 306 .that(buffer.getInt()).isEqualTo(uids[i]); 307 308 final String tag = getStringFromByteBuffer(buffer); 309 assertWithMessage("Incorrect tag in Attribution Chain") 310 .that(tag).isEqualTo(tags[i]); 311 } 312 313 assertWithMessage("Second field is not Long") 314 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG); 315 316 assertWithMessage("Incorrect field 2") 317 .that(buffer.getLong()).isEqualTo(field2); 318 319 assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position()); 320 321 statsEvent.release(); 322 } 323 324 @Test testKeyValuePairs()325 public void testKeyValuePairs() { 326 final int expectedAtomId = 109; 327 final SparseIntArray intMap = new SparseIntArray(); 328 final SparseLongArray longMap = new SparseLongArray(); 329 final SparseArray<String> stringMap = new SparseArray<>(); 330 final SparseArray<Float> floatMap = new SparseArray<>(); 331 intMap.put(1, -1); 332 intMap.put(2, -2); 333 stringMap.put(3, "abc"); 334 stringMap.put(4, "2h"); 335 floatMap.put(9, -234.344f); 336 337 final long minTimestamp = SystemClock.elapsedRealtimeNanos(); 338 final StatsEvent statsEvent = StatsEvent.newBuilder() 339 .setAtomId(expectedAtomId) 340 .writeKeyValuePairs(intMap, longMap, stringMap, floatMap) 341 .usePooledBuffer() 342 .build(); 343 final long maxTimestamp = SystemClock.elapsedRealtimeNanos(); 344 345 assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId); 346 347 final ByteBuffer buffer = 348 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN); 349 350 assertWithMessage("Root element in buffer is not TYPE_OBJECT") 351 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT); 352 353 assertWithMessage("Incorrect number of elements in root object") 354 .that(buffer.get()).isEqualTo(3); 355 356 assertWithMessage("First element is not timestamp") 357 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG); 358 359 assertWithMessage("Incorrect timestamp") 360 .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp)); 361 362 assertWithMessage("Second element is not atom id") 363 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT); 364 365 assertWithMessage("Incorrect atom id") 366 .that(buffer.getInt()).isEqualTo(expectedAtomId); 367 368 assertWithMessage("First field is not KeyValuePairs") 369 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_KEY_VALUE_PAIRS); 370 371 assertWithMessage("Incorrect number of key value pairs") 372 .that(buffer.get()).isEqualTo( 373 (byte) (intMap.size() + longMap.size() + stringMap.size() 374 + floatMap.size())); 375 376 for (int i = 0; i < intMap.size(); i++) { 377 assertWithMessage("Incorrect key in intMap") 378 .that(buffer.getInt()).isEqualTo(intMap.keyAt(i)); 379 assertWithMessage("The type id of the value should be TYPE_INT in intMap") 380 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT); 381 assertWithMessage("Incorrect value in intMap") 382 .that(buffer.getInt()).isEqualTo(intMap.valueAt(i)); 383 } 384 385 for (int i = 0; i < longMap.size(); i++) { 386 assertWithMessage("Incorrect key in longMap") 387 .that(buffer.getInt()).isEqualTo(longMap.keyAt(i)); 388 assertWithMessage("The type id of the value should be TYPE_LONG in longMap") 389 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG); 390 assertWithMessage("Incorrect value in longMap") 391 .that(buffer.getLong()).isEqualTo(longMap.valueAt(i)); 392 } 393 394 for (int i = 0; i < stringMap.size(); i++) { 395 assertWithMessage("Incorrect key in stringMap") 396 .that(buffer.getInt()).isEqualTo(stringMap.keyAt(i)); 397 assertWithMessage("The type id of the value should be TYPE_STRING in stringMap") 398 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_STRING); 399 final String value = getStringFromByteBuffer(buffer); 400 assertWithMessage("Incorrect value in stringMap") 401 .that(value).isEqualTo(stringMap.valueAt(i)); 402 } 403 404 for (int i = 0; i < floatMap.size(); i++) { 405 assertWithMessage("Incorrect key in floatMap") 406 .that(buffer.getInt()).isEqualTo(floatMap.keyAt(i)); 407 assertWithMessage("The type id of the value should be TYPE_FLOAT in floatMap") 408 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_FLOAT); 409 assertWithMessage("Incorrect value in floatMap") 410 .that(buffer.getFloat()).isEqualTo(floatMap.valueAt(i)); 411 } 412 413 assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position()); 414 415 statsEvent.release(); 416 } 417 418 @Test testBoolArrayIntArrayLongArray()419 public void testBoolArrayIntArrayLongArray() { 420 // Skip test if build version isn't T or greater. 421 if (!SdkLevel.isAtLeastT()) { 422 return; 423 } 424 425 final int expectedAtomId = 109; 426 final boolean[] field1 = new boolean[] {true, false, false}; 427 final int[] field1Converted = new int[] {1, 0, 0}; 428 final int[] field2 = new int[] {4, 11}; 429 final long[] field3 = new long[] {10000L, 10000L, 10000L}; 430 431 final long minTimestamp = SystemClock.elapsedRealtimeNanos(); 432 final StatsEvent statsEvent = StatsEvent.newBuilder() 433 .setAtomId(expectedAtomId) 434 .writeBooleanArray(field1) 435 .writeIntArray(field2) 436 .writeLongArray(field3) 437 .usePooledBuffer() 438 .build(); 439 final long maxTimestamp = SystemClock.elapsedRealtimeNanos(); 440 441 assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId); 442 443 final ByteBuffer buffer = 444 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN); 445 446 assertWithMessage("Root element in buffer is not TYPE_OBJECT") 447 .that(buffer.get()) 448 .isEqualTo(StatsEvent.TYPE_OBJECT); 449 450 assertWithMessage("Incorrect number of elements in root object") 451 .that(buffer.get()) 452 .isEqualTo(5); 453 454 assertWithMessage("First element is not timestamp") 455 .that(buffer.get()) 456 .isEqualTo(StatsEvent.TYPE_LONG); 457 458 assertWithMessage("Incorrect timestamp") 459 .that(buffer.getLong()) 460 .isIn(Range.closed(minTimestamp, maxTimestamp)); 461 462 assertWithMessage("Second element is not atom id") 463 .that(buffer.get()) 464 .isEqualTo(StatsEvent.TYPE_INT); 465 466 assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId); 467 468 assertWithMessage("First field is not list") 469 .that(buffer.get()) 470 .isEqualTo(StatsEvent.TYPE_LIST); 471 472 assertWithMessage("Incorrect number of elements in field 1 object") 473 .that(buffer.get()) 474 .isEqualTo(3); 475 476 assertWithMessage("Element type of field 1 is not boolean") 477 .that(buffer.get()) 478 .isEqualTo(StatsEvent.TYPE_BOOLEAN); 479 480 for (int i = 0; i < field1.length; i++) { 481 assertWithMessage("Incorrect field of field 1") 482 .that(buffer.get()) 483 .isEqualTo(field1Converted[i]); 484 } 485 486 assertWithMessage("Second field is not list") 487 .that(buffer.get()) 488 .isEqualTo(StatsEvent.TYPE_LIST); 489 490 assertWithMessage("Incorrect number of elements in field 2 object") 491 .that(buffer.get()) 492 .isEqualTo(2); 493 494 assertWithMessage("Element type of field 2 is not int") 495 .that(buffer.get()) 496 .isEqualTo(StatsEvent.TYPE_INT); 497 498 for (int i = 0; i < field2.length; i++) { 499 assertWithMessage("Incorrect field of field 2") 500 .that(buffer.getInt()) 501 .isEqualTo(field2[i]); 502 } 503 504 assertWithMessage("Third field is not list") 505 .that(buffer.get()) 506 .isEqualTo(StatsEvent.TYPE_LIST); 507 508 assertWithMessage("Incorrect number of elements in field 3 object") 509 .that(buffer.get()) 510 .isEqualTo(3); 511 512 assertWithMessage("Element type of field 3 is not long") 513 .that(buffer.get()) 514 .isEqualTo(StatsEvent.TYPE_LONG); 515 516 for (int i = 0; i < field3.length; i++) { 517 assertWithMessage("Incorrect field of field 3") 518 .that(buffer.getLong()) 519 .isEqualTo(field3[i]); 520 } 521 522 assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position()); 523 524 statsEvent.release(); 525 } 526 527 @Test testFloatArrayStringArray()528 public void testFloatArrayStringArray() { 529 // Skip test if build version isn't T or greater. 530 if (!SdkLevel.isAtLeastT()) { 531 return; 532 } 533 534 final int expectedAtomId = 109; 535 final float[] field1 = new float[] {0.21f, 0.13f}; 536 final String[] field2 = new String[] {"str1", "str2", "str3"}; 537 538 final long minTimestamp = SystemClock.elapsedRealtimeNanos(); 539 final StatsEvent statsEvent = StatsEvent.newBuilder() 540 .setAtomId(expectedAtomId) 541 .writeFloatArray(field1) 542 .writeStringArray(field2) 543 .usePooledBuffer() 544 .build(); 545 final long maxTimestamp = SystemClock.elapsedRealtimeNanos(); 546 547 assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId); 548 549 final ByteBuffer buffer = 550 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN); 551 552 assertWithMessage("Root element in buffer is not TYPE_OBJECT") 553 .that(buffer.get()) 554 .isEqualTo(StatsEvent.TYPE_OBJECT); 555 556 assertWithMessage("Incorrect number of elements in root object") 557 .that(buffer.get()) 558 .isEqualTo(4); 559 560 assertWithMessage("First element is not timestamp") 561 .that(buffer.get()) 562 .isEqualTo(StatsEvent.TYPE_LONG); 563 564 assertWithMessage("Incorrect timestamp") 565 .that(buffer.getLong()) 566 .isIn(Range.closed(minTimestamp, maxTimestamp)); 567 568 assertWithMessage("Second element is not atom id") 569 .that(buffer.get()) 570 .isEqualTo(StatsEvent.TYPE_INT); 571 572 assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId); 573 574 assertWithMessage("First field is not list") 575 .that(buffer.get()) 576 .isEqualTo(StatsEvent.TYPE_LIST); 577 578 assertWithMessage("Incorrect number of elements in field 1 object") 579 .that(buffer.get()) 580 .isEqualTo(2); 581 582 assertWithMessage("Element type of field 1 is not float") 583 .that(buffer.get()) 584 .isEqualTo(StatsEvent.TYPE_FLOAT); 585 586 for (int i = 0; i < field1.length; i++) { 587 assertWithMessage("Incorrect field of field 1") 588 .that(buffer.getFloat()) 589 .isEqualTo(field1[i]); 590 } 591 592 assertWithMessage("Second field is not list") 593 .that(buffer.get()) 594 .isEqualTo(StatsEvent.TYPE_LIST); 595 596 assertWithMessage("Incorrect number of elements in field 2 object") 597 .that(buffer.get()) 598 .isEqualTo(3); 599 600 assertWithMessage("Element type of field 2 is not string") 601 .that(buffer.get()) 602 .isEqualTo(StatsEvent.TYPE_STRING); 603 604 for (int i = 0; i < field2.length; i++) { 605 final String fieldElementActual = getStringFromByteBuffer(buffer); 606 assertWithMessage("Incorrect field of field 2") 607 .that(fieldElementActual) 608 .isEqualTo(field2[i]); 609 } 610 611 assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position()); 612 613 statsEvent.release(); 614 } 615 616 @Test testSingleAnnotations()617 public void testSingleAnnotations() { 618 final int expectedAtomId = 109; 619 final int field1 = 1; 620 final byte field1AnnotationId = 45; 621 final boolean field1AnnotationValue = false; 622 final boolean field2 = true; 623 final byte field2AnnotationId = 1; 624 final int field2AnnotationValue = 23; 625 626 final long minTimestamp = SystemClock.elapsedRealtimeNanos(); 627 final StatsEvent statsEvent = StatsEvent.newBuilder() 628 .setAtomId(expectedAtomId) 629 .writeInt(field1) 630 .addBooleanAnnotation(field1AnnotationId, field1AnnotationValue) 631 .writeBoolean(field2) 632 .addIntAnnotation(field2AnnotationId, field2AnnotationValue) 633 .usePooledBuffer() 634 .build(); 635 final long maxTimestamp = SystemClock.elapsedRealtimeNanos(); 636 637 assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId); 638 639 final ByteBuffer buffer = 640 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN); 641 642 assertWithMessage("Root element in buffer is not TYPE_OBJECT") 643 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT); 644 645 assertWithMessage("Incorrect number of elements in root object") 646 .that(buffer.get()).isEqualTo(4); 647 648 assertWithMessage("First element is not timestamp") 649 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG); 650 651 assertWithMessage("Incorrect timestamp") 652 .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp)); 653 654 assertWithMessage("Second element is not atom id") 655 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT); 656 657 assertWithMessage("Incorrect atom id") 658 .that(buffer.getInt()).isEqualTo(expectedAtomId); 659 660 final byte field1Header = buffer.get(); 661 final int field1AnnotationValueCount = field1Header >> 4; 662 final byte field1Type = (byte) (field1Header & 0x0F); 663 assertWithMessage("First field is not Int") 664 .that(field1Type).isEqualTo(StatsEvent.TYPE_INT); 665 assertWithMessage("First field annotation count is wrong") 666 .that(field1AnnotationValueCount).isEqualTo(1); 667 assertWithMessage("Incorrect field 1") 668 .that(buffer.getInt()).isEqualTo(field1); 669 assertWithMessage("First field's annotation id is wrong") 670 .that(buffer.get()).isEqualTo(field1AnnotationId); 671 assertWithMessage("First field's annotation type is wrong") 672 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_BOOLEAN); 673 assertWithMessage("First field's annotation value is wrong") 674 .that(buffer.get()).isEqualTo(field1AnnotationValue ? 1 : 0); 675 676 final byte field2Header = buffer.get(); 677 final int field2AnnotationValueCount = field2Header >> 4; 678 final byte field2Type = (byte) (field2Header & 0x0F); 679 assertWithMessage("Second field is not boolean") 680 .that(field2Type).isEqualTo(StatsEvent.TYPE_BOOLEAN); 681 assertWithMessage("Second field annotation count is wrong") 682 .that(field2AnnotationValueCount).isEqualTo(1); 683 assertWithMessage("Incorrect field 2") 684 .that(buffer.get()).isEqualTo(field2 ? 1 : 0); 685 assertWithMessage("Second field's annotation id is wrong") 686 .that(buffer.get()).isEqualTo(field2AnnotationId); 687 assertWithMessage("Second field's annotation type is wrong") 688 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT); 689 assertWithMessage("Second field's annotation value is wrong") 690 .that(buffer.getInt()).isEqualTo(field2AnnotationValue); 691 692 assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position()); 693 694 statsEvent.release(); 695 } 696 697 @Test testArrayFieldAnnotations()698 public void testArrayFieldAnnotations() { 699 // Skip test if build version isn't T or greater. 700 if (!SdkLevel.isAtLeastT()) { 701 return; 702 } 703 704 final int expectedAtomId = 109; 705 final int[] field1 = new int[] {4, 11}; 706 final byte boolAnnotationId = 45; 707 final boolean boolAnnotationValue = false; 708 final byte intAnnotationId = 1; 709 final int intAnnotationValue = 23; 710 711 final long minTimestamp = SystemClock.elapsedRealtimeNanos(); 712 final StatsEvent statsEvent = StatsEvent.newBuilder() 713 .setAtomId(expectedAtomId) 714 .writeIntArray(field1) 715 .addBooleanAnnotation(boolAnnotationId, boolAnnotationValue) 716 .addIntAnnotation(intAnnotationId, intAnnotationValue) 717 .usePooledBuffer() 718 .build(); 719 final long maxTimestamp = SystemClock.elapsedRealtimeNanos(); 720 721 assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId); 722 723 final ByteBuffer buffer = 724 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN); 725 726 assertWithMessage("Root element in buffer is not TYPE_OBJECT") 727 .that(buffer.get()) 728 .isEqualTo(StatsEvent.TYPE_OBJECT); 729 730 assertWithMessage("Incorrect number of elements in root object") 731 .that(buffer.get()) 732 .isEqualTo(3); 733 734 assertWithMessage("First element is not timestamp") 735 .that(buffer.get()) 736 .isEqualTo(StatsEvent.TYPE_LONG); 737 738 assertWithMessage("Incorrect timestamp") 739 .that(buffer.getLong()) 740 .isIn(Range.closed(minTimestamp, maxTimestamp)); 741 742 assertWithMessage("Second element is not atom id") 743 .that(buffer.get()) 744 .isEqualTo(StatsEvent.TYPE_INT); 745 746 assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId); 747 748 final byte field1Header = buffer.get(); 749 final int field1AnnotationValueCount = field1Header >> 4; 750 final byte field1Type = (byte) (field1Header & 0x0F); 751 assertWithMessage("First field is not list") 752 .that(field1Type).isEqualTo(StatsEvent.TYPE_LIST); 753 754 assertWithMessage("First field annotation count is wrong") 755 .that(field1AnnotationValueCount) 756 .isEqualTo(2); 757 758 assertWithMessage("Incorrect number of elements in field 1 object") 759 .that(buffer.get()) 760 .isEqualTo(2); 761 762 assertWithMessage("Element type of field 1 is not int") 763 .that(buffer.get()) 764 .isEqualTo(StatsEvent.TYPE_INT); 765 766 for (int i = 0; i < field1.length; i++) { 767 assertWithMessage("Incorrect field of field 1") 768 .that(buffer.getInt()).isEqualTo(field1[i]); 769 } 770 771 assertWithMessage("Field 1's first annotation id is wrong") 772 .that(buffer.get()) 773 .isEqualTo(boolAnnotationId); 774 assertWithMessage("Field 1's first annotation type is wrong") 775 .that(buffer.get()) 776 .isEqualTo(StatsEvent.TYPE_BOOLEAN); 777 assertWithMessage("Field 1's first annotation value is wrong") 778 .that(buffer.get()) 779 .isEqualTo(boolAnnotationValue ? 1 : 0); 780 781 assertWithMessage("Field 1's second annotation id is wrong") 782 .that(buffer.get()) 783 .isEqualTo(intAnnotationId); 784 assertWithMessage("Field 1's second annotation type is wrong") 785 .that(buffer.get()) 786 .isEqualTo(StatsEvent.TYPE_INT); 787 assertWithMessage("Field 1's second annotation value is wrong") 788 .that(buffer.getInt()) 789 .isEqualTo(intAnnotationValue); 790 791 assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position()); 792 793 statsEvent.release(); 794 } 795 796 @Test testAtomIdAnnotations()797 public void testAtomIdAnnotations() { 798 final int expectedAtomId = 109; 799 final byte atomAnnotationId = 84; 800 final int atomAnnotationValue = 9; 801 final int field1 = 1; 802 final byte field1AnnotationId = 45; 803 final boolean field1AnnotationValue = false; 804 final boolean field2 = true; 805 final byte field2AnnotationId = 1; 806 final int field2AnnotationValue = 23; 807 808 final long minTimestamp = SystemClock.elapsedRealtimeNanos(); 809 final StatsEvent statsEvent = StatsEvent.newBuilder() 810 .setAtomId(expectedAtomId) 811 .addIntAnnotation(atomAnnotationId, atomAnnotationValue) 812 .writeInt(field1) 813 .addBooleanAnnotation(field1AnnotationId, field1AnnotationValue) 814 .writeBoolean(field2) 815 .addIntAnnotation(field2AnnotationId, field2AnnotationValue) 816 .usePooledBuffer() 817 .build(); 818 final long maxTimestamp = SystemClock.elapsedRealtimeNanos(); 819 820 assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId); 821 822 final ByteBuffer buffer = 823 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN); 824 825 assertWithMessage("Root element in buffer is not TYPE_OBJECT") 826 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT); 827 828 assertWithMessage("Incorrect number of elements in root object") 829 .that(buffer.get()).isEqualTo(4); 830 831 assertWithMessage("First element is not timestamp") 832 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG); 833 834 assertWithMessage("Incorrect timestamp") 835 .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp)); 836 837 final byte atomIdHeader = buffer.get(); 838 final int atomIdAnnotationValueCount = atomIdHeader >> 4; 839 final byte atomIdValueType = (byte) (atomIdHeader & 0x0F); 840 assertWithMessage("Second element is not atom id") 841 .that(atomIdValueType).isEqualTo(StatsEvent.TYPE_INT); 842 assertWithMessage("Atom id annotation count is wrong") 843 .that(atomIdAnnotationValueCount).isEqualTo(1); 844 assertWithMessage("Incorrect atom id") 845 .that(buffer.getInt()).isEqualTo(expectedAtomId); 846 assertWithMessage("Atom id's annotation id is wrong") 847 .that(buffer.get()).isEqualTo(atomAnnotationId); 848 assertWithMessage("Atom id's annotation type is wrong") 849 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT); 850 assertWithMessage("Atom id's annotation value is wrong") 851 .that(buffer.getInt()).isEqualTo(atomAnnotationValue); 852 853 final byte field1Header = buffer.get(); 854 final int field1AnnotationValueCount = field1Header >> 4; 855 final byte field1Type = (byte) (field1Header & 0x0F); 856 assertWithMessage("First field is not Int") 857 .that(field1Type).isEqualTo(StatsEvent.TYPE_INT); 858 assertWithMessage("First field annotation count is wrong") 859 .that(field1AnnotationValueCount).isEqualTo(1); 860 assertWithMessage("Incorrect field 1") 861 .that(buffer.getInt()).isEqualTo(field1); 862 assertWithMessage("First field's annotation id is wrong") 863 .that(buffer.get()).isEqualTo(field1AnnotationId); 864 assertWithMessage("First field's annotation type is wrong") 865 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_BOOLEAN); 866 assertWithMessage("First field's annotation value is wrong") 867 .that(buffer.get()).isEqualTo(field1AnnotationValue ? 1 : 0); 868 869 final byte field2Header = buffer.get(); 870 final int field2AnnotationValueCount = field2Header >> 4; 871 final byte field2Type = (byte) (field2Header & 0x0F); 872 assertWithMessage("Second field is not boolean") 873 .that(field2Type).isEqualTo(StatsEvent.TYPE_BOOLEAN); 874 assertWithMessage("Second field annotation count is wrong") 875 .that(field2AnnotationValueCount).isEqualTo(1); 876 assertWithMessage("Incorrect field 2") 877 .that(buffer.get()).isEqualTo(field2 ? 1 : 0); 878 assertWithMessage("Second field's annotation id is wrong") 879 .that(buffer.get()).isEqualTo(field2AnnotationId); 880 assertWithMessage("Second field's annotation type is wrong") 881 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT); 882 assertWithMessage("Second field's annotation value is wrong") 883 .that(buffer.getInt()).isEqualTo(field2AnnotationValue); 884 885 assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position()); 886 887 statsEvent.release(); 888 } 889 890 @Test testSetAtomIdNotCalledImmediately()891 public void testSetAtomIdNotCalledImmediately() { 892 final int expectedAtomId = 109; 893 final int field1 = 25; 894 final boolean field2 = true; 895 896 final long minTimestamp = SystemClock.elapsedRealtimeNanos(); 897 final StatsEvent statsEvent = StatsEvent.newBuilder() 898 .writeInt(field1) 899 .setAtomId(expectedAtomId) 900 .writeBoolean(field2) 901 .usePooledBuffer() 902 .build(); 903 final long maxTimestamp = SystemClock.elapsedRealtimeNanos(); 904 905 assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId); 906 907 final ByteBuffer buffer = 908 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN); 909 910 assertWithMessage("Root element in buffer is not TYPE_OBJECT") 911 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT); 912 913 assertWithMessage("Incorrect number of elements in root object") 914 .that(buffer.get()).isEqualTo(3); 915 916 assertWithMessage("First element is not timestamp") 917 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG); 918 919 assertWithMessage("Incorrect timestamp") 920 .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp)); 921 922 assertWithMessage("Second element is not atom id") 923 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT); 924 925 assertWithMessage("Incorrect atom id") 926 .that(buffer.getInt()).isEqualTo(expectedAtomId); 927 928 assertWithMessage("Third element is not errors type") 929 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_ERRORS); 930 931 final int errorMask = buffer.getInt(); 932 933 assertWithMessage("ERROR_ATOM_ID_INVALID_POSITION should be the only error in the mask") 934 .that(errorMask).isEqualTo(StatsEvent.ERROR_ATOM_ID_INVALID_POSITION); 935 936 assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position()); 937 938 statsEvent.release(); 939 } 940 941 @Test testLargePulledEvent()942 public void testLargePulledEvent() { 943 final int expectedAtomId = 10_020; 944 byte[] field1 = new byte[10 * 1024]; 945 new Random().nextBytes(field1); 946 947 final long minTimestamp = SystemClock.elapsedRealtimeNanos(); 948 final StatsEvent statsEvent = 949 StatsEvent.newBuilder().setAtomId(expectedAtomId).writeByteArray(field1).build(); 950 final long maxTimestamp = SystemClock.elapsedRealtimeNanos(); 951 952 assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId); 953 954 final ByteBuffer buffer = 955 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN); 956 957 assertWithMessage("Root element in buffer is not TYPE_OBJECT") 958 .that(buffer.get()) 959 .isEqualTo(StatsEvent.TYPE_OBJECT); 960 961 assertWithMessage("Incorrect number of elements in root object") 962 .that(buffer.get()) 963 .isEqualTo(3); 964 965 assertWithMessage("First element is not timestamp") 966 .that(buffer.get()) 967 .isEqualTo(StatsEvent.TYPE_LONG); 968 969 assertWithMessage("Incorrect timestamp") 970 .that(buffer.getLong()) 971 .isIn(Range.closed(minTimestamp, maxTimestamp)); 972 973 assertWithMessage("Second element is not atom id") 974 .that(buffer.get()) 975 .isEqualTo(StatsEvent.TYPE_INT); 976 977 assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId); 978 979 assertWithMessage("Third element is not byte array") 980 .that(buffer.get()) 981 .isEqualTo(StatsEvent.TYPE_BYTE_ARRAY); 982 983 final byte[] field1Actual = getByteArrayFromByteBuffer(buffer); 984 assertWithMessage("Incorrect field 1").that(field1Actual).isEqualTo(field1); 985 986 assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position()); 987 988 statsEvent.release(); 989 } 990 991 @Test testPulledEventOverflow()992 public void testPulledEventOverflow() { 993 final int expectedAtomId = 10_020; 994 byte[] field1 = new byte[50 * 1024]; 995 new Random().nextBytes(field1); 996 997 final long minTimestamp = SystemClock.elapsedRealtimeNanos(); 998 final StatsEvent statsEvent = 999 StatsEvent.newBuilder().setAtomId(expectedAtomId).writeByteArray(field1).build(); 1000 final long maxTimestamp = SystemClock.elapsedRealtimeNanos(); 1001 1002 assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId); 1003 1004 final ByteBuffer buffer = 1005 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN); 1006 1007 assertWithMessage("Root element in buffer is not TYPE_OBJECT") 1008 .that(buffer.get()) 1009 .isEqualTo(StatsEvent.TYPE_OBJECT); 1010 1011 assertWithMessage("Incorrect number of elements in root object") 1012 .that(buffer.get()) 1013 .isEqualTo(3); 1014 1015 assertWithMessage("First element is not timestamp") 1016 .that(buffer.get()) 1017 .isEqualTo(StatsEvent.TYPE_LONG); 1018 1019 assertWithMessage("Incorrect timestamp") 1020 .that(buffer.getLong()) 1021 .isIn(Range.closed(minTimestamp, maxTimestamp)); 1022 1023 assertWithMessage("Second element is not atom id") 1024 .that(buffer.get()) 1025 .isEqualTo(StatsEvent.TYPE_INT); 1026 1027 assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId); 1028 1029 assertWithMessage("Third element is not errors type") 1030 .that(buffer.get()) 1031 .isEqualTo(StatsEvent.TYPE_ERRORS); 1032 1033 final int errorMask = buffer.getInt(); 1034 1035 assertWithMessage("ERROR_OVERFLOW should be the only error in the error mask") 1036 .that(errorMask) 1037 .isEqualTo(StatsEvent.ERROR_OVERFLOW); 1038 1039 assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position()); 1040 1041 statsEvent.release(); 1042 } 1043 1044 @Test testPushedEventOverflow()1045 public void testPushedEventOverflow() { 1046 final int expectedAtomId = 10_020; 1047 byte[] field1 = new byte[10 * 1024]; 1048 new Random().nextBytes(field1); 1049 1050 final long minTimestamp = SystemClock.elapsedRealtimeNanos(); 1051 final StatsEvent statsEvent = StatsEvent.newBuilder() 1052 .setAtomId(expectedAtomId) 1053 .writeByteArray(field1) 1054 .usePooledBuffer() 1055 .build(); 1056 final long maxTimestamp = SystemClock.elapsedRealtimeNanos(); 1057 1058 assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId); 1059 1060 final ByteBuffer buffer = 1061 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN); 1062 1063 assertWithMessage("Root element in buffer is not TYPE_OBJECT") 1064 .that(buffer.get()) 1065 .isEqualTo(StatsEvent.TYPE_OBJECT); 1066 1067 assertWithMessage("Incorrect number of elements in root object") 1068 .that(buffer.get()) 1069 .isEqualTo(3); 1070 1071 assertWithMessage("First element is not timestamp") 1072 .that(buffer.get()) 1073 .isEqualTo(StatsEvent.TYPE_LONG); 1074 1075 assertWithMessage("Incorrect timestamp") 1076 .that(buffer.getLong()) 1077 .isIn(Range.closed(minTimestamp, maxTimestamp)); 1078 1079 assertWithMessage("Second element is not atom id") 1080 .that(buffer.get()) 1081 .isEqualTo(StatsEvent.TYPE_INT); 1082 1083 assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId); 1084 1085 assertWithMessage("Third element is not errors type") 1086 .that(buffer.get()) 1087 .isEqualTo(StatsEvent.TYPE_ERRORS); 1088 1089 final int errorMask = buffer.getInt(); 1090 1091 assertWithMessage("ERROR_OVERFLOW should be the only error in the error mask") 1092 .that(errorMask) 1093 .isEqualTo(StatsEvent.ERROR_OVERFLOW); 1094 1095 assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position()); 1096 1097 statsEvent.release(); 1098 } 1099 getByteArrayFromByteBuffer(final ByteBuffer buffer)1100 private static byte[] getByteArrayFromByteBuffer(final ByteBuffer buffer) { 1101 final int numBytes = buffer.getInt(); 1102 byte[] bytes = new byte[numBytes]; 1103 buffer.get(bytes); 1104 return bytes; 1105 } 1106 getStringFromByteBuffer(final ByteBuffer buffer)1107 private static String getStringFromByteBuffer(final ByteBuffer buffer) { 1108 final byte[] bytes = getByteArrayFromByteBuffer(buffer); 1109 return new String(bytes, UTF_8); 1110 } 1111 } 1112