1 /*
<lambda>null2  * Copyright 2020 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 androidx.benchmark
18 
19 import android.os.Build
20 import androidx.test.ext.junit.runners.AndroidJUnit4
21 import androidx.test.filters.SmallTest
22 import kotlin.test.assertEquals
23 import org.junit.Assume.assumeFalse
24 import org.junit.Assume.assumeTrue
25 import org.junit.Test
26 import org.junit.runner.RunWith
27 
28 @SmallTest
29 @RunWith(AndroidJUnit4::class)
30 class MetricCaptureTest {
31     @Test
32     fun allocationCountCapture_simple() {
33         AllocationCountCapture().verifyMedian(100..110) { allocate(100) }
34     }
35 
36     @Test
37     fun allocationCountCapture_pauseResume() {
38         AllocationCountCapture().verifyMedian(100..110) {
39             allocate(100)
40 
41             capturePaused()
42             // these 1000 allocations shouldn't be counted, capture is paused!
43             allocate(1000)
44             captureResumed()
45         }
46     }
47 
48     @Test
49     fun cpuEventCounterCapture_outputName() {
50         CpuEventCounter().use {
51             assertEquals(
52                 listOf("instructions", "cpuCycles"),
53                 CpuEventCounterCapture(
54                         it,
55                         listOf(CpuEventCounter.Event.Instructions, CpuEventCounter.Event.CpuCycles)
56                     )
57                     .names
58             )
59         }
60     }
61 
62     @Test
63     fun cpuEventCounterCapture_multi() {
64         assumeFalse(DeviceInfo.isEmulator && Build.VERSION.SDK_INT == 28) // see b/357101113
65 
66         try {
67             // skip test if need root, or event fails to enable
68             CpuEventCounter.forceEnable()?.let { errorMessage -> assumeTrue(errorMessage, false) }
69 
70             CpuEventCounter().use { counter ->
71                 val firstEvents = listOf(CpuEventCounter.Event.Instructions)
72                 val secondEvents =
73                     listOf(CpuEventCounter.Event.Instructions, CpuEventCounter.Event.CpuCycles)
74 
75                 val firstCapture = CpuEventCounterCapture(counter, firstEvents)
76                 val secondCapture = CpuEventCounterCapture(counter, secondEvents)
77 
78                 val checkCapture: (CpuEventCounterCapture, List<CpuEventCounter.Event>) -> Unit =
79                     { capture, events ->
80                         capture.captureStart(0)
81                         assertEquals(events.getFlags(), counter.currentEventFlags)
82                         capture.captureStop(1, LongArray(events.size), 0)
83                     }
84 
85                 checkCapture(firstCapture, firstEvents)
86                 checkCapture(secondCapture, secondEvents)
87                 checkCapture(firstCapture, firstEvents)
88             }
89         } finally {
90             CpuEventCounter.reset()
91         }
92     }
93 }
94 
95 /**
96  * Measure many times, and verify the median.
97  *
98  * This is done to reduce variance, e.g. from random background allocations
99  */
verifyMediannull100 private fun MetricCapture.verifyMedian(expected: IntRange, block: MetricCapture.() -> Unit) {
101     assertEquals(1, names.size)
102     val longArray = longArrayOf(0L)
103     val results =
104         List(200) {
105             captureStart(System.nanoTime())
106             block()
107             captureStop(System.nanoTime(), longArray, 0)
108             longArray[0] * 1.0
109         }
110     val median = MetricResult(names[0], results).median.toInt()
111     if (median !in expected) {
112         throw AssertionError(
113             "observed median $median, expected $expected, saw: " + results.joinToString()
114         )
115     }
116 }
117