VSyncMonitorTest.java revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
1// Copyright 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.content.browser;
6
7import android.content.Context;
8import android.os.SystemClock;
9import android.test.InstrumentationTestCase;
10import android.test.suitebuilder.annotation.MediumTest;
11
12import org.chromium.base.ThreadUtils;
13import org.chromium.ui.VSyncMonitor;
14
15import java.util.Arrays;
16import java.util.concurrent.Callable;
17
18public class VSyncMonitorTest extends InstrumentationTestCase {
19    private static class VSyncDataCollector implements VSyncMonitor.Listener {
20        public long mFramePeriods[];
21        public int mFrameCount;
22        public long mLastVSyncCpuTimeMillis;
23
24        private final boolean mActivelyRequestUpdate;
25        private boolean mDone;
26        private long mPreviousVSyncTimeMicros;
27        private Object mSyncRoot = new Object();
28
29        VSyncDataCollector(int frames, boolean activelyRequestUpdate) {
30            mFramePeriods = new long[frames];
31            mActivelyRequestUpdate = activelyRequestUpdate;
32        }
33
34        public boolean isDone() {
35            synchronized (mSyncRoot) {
36                return mDone;
37            }
38        }
39
40        @Override
41        public void onVSync(VSyncMonitor monitor, long vsyncTimeMicros) {
42            mLastVSyncCpuTimeMillis = SystemClock.uptimeMillis();
43            if (mPreviousVSyncTimeMicros == 0) {
44                mPreviousVSyncTimeMicros = vsyncTimeMicros;
45                return;
46            }
47            if (mFrameCount >= mFramePeriods.length) {
48                synchronized (mSyncRoot) {
49                    mDone = true;
50                    mSyncRoot.notify();
51                }
52                return;
53            }
54            mFramePeriods[mFrameCount++] = vsyncTimeMicros - mPreviousVSyncTimeMicros;
55            mPreviousVSyncTimeMicros = vsyncTimeMicros;
56            if (mActivelyRequestUpdate) monitor.requestUpdate();
57        }
58
59        public void waitTillDone() throws InterruptedException {
60            synchronized (mSyncRoot) {
61                while (!isDone()) {
62                    mSyncRoot.wait();
63                }
64            }
65        }
66    }
67
68    // The vsync monitor must be created on the UI thread to avoid associating the underlying
69    // Choreographer with the Looper from the test runner thread.
70    private VSyncMonitor createVSyncMonitor(
71            final VSyncMonitor.Listener listener, final boolean enableJBVSync) {
72        return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<VSyncMonitor>() {
73            @Override
74            public VSyncMonitor call() {
75                Context context = getInstrumentation().getContext();
76                return new VSyncMonitor(context, listener, enableJBVSync);
77            }
78        });
79    }
80
81    // Check that the vsync period roughly matches the timestamps that the monitor generates.
82    private void performVSyncPeriodTest(boolean enableJBVSync) throws InterruptedException {
83        // Collect roughly one second of data on a 60 fps display.
84        collectAndCheckVSync(enableJBVSync, 60, true);
85        collectAndCheckVSync(enableJBVSync, VSyncMonitor.MAX_AUTO_ONVSYNC_COUNT, false);
86    }
87
88    private void collectAndCheckVSync(
89            boolean enableJBVSync, final int totalFrames, final boolean activeFrames)
90            throws InterruptedException {
91        VSyncDataCollector collector = new VSyncDataCollector(totalFrames, activeFrames);
92        VSyncMonitor monitor = createVSyncMonitor(collector, enableJBVSync);
93
94        long reportedFramePeriod = monitor.getVSyncPeriodInMicroseconds();
95        assertTrue(reportedFramePeriod > 0);
96
97        assertFalse(collector.isDone());
98        monitor.requestUpdate();
99        collector.waitTillDone();
100        assertTrue(collector.isDone());
101        monitor.stop();
102
103        // Check that the median frame rate is within 10% of the reported frame period.
104        assertTrue(collector.mFrameCount == totalFrames);
105        Arrays.sort(collector.mFramePeriods, 0, collector.mFramePeriods.length);
106        long medianFramePeriod = collector.mFramePeriods[collector.mFramePeriods.length / 2];
107        if (Math.abs(medianFramePeriod - reportedFramePeriod) > reportedFramePeriod * .1) {
108            fail("Measured median frame period " + medianFramePeriod
109                    + " differs by more than 10% from the reported frame period "
110                    + reportedFramePeriod + " for "
111                    + (activeFrames ? "requested" : "automatically sent") + " frames");
112        }
113    }
114
115    // Check that the vsync period roughly matches the timestamps that the monitor generates.
116    @MediumTest
117    public void testVSyncPeriodAllowJBVSync() throws InterruptedException {
118        performVSyncPeriodTest(true);
119    }
120
121    // Check that the vsync period roughly matches the timestamps that the monitor generates.
122    @MediumTest
123    public void testVSyncPeriodDisallowJBVSync() throws InterruptedException {
124        performVSyncPeriodTest(false);
125    }
126
127    // Check that the vsync period roughly matches the timestamps that the monitor generates.
128    private void performVSyncActivationFromIdle(boolean enableJBVSync) throws InterruptedException {
129        VSyncDataCollector collector = new VSyncDataCollector(1, false);
130        VSyncMonitor monitor = createVSyncMonitor(collector, enableJBVSync);
131
132        monitor.requestUpdate();
133        collector.waitTillDone();
134        assertTrue(collector.isDone());
135        monitor.stop();
136
137        long period = monitor.getVSyncPeriodInMicroseconds() / 1000;
138        long delay = SystemClock.uptimeMillis() - collector.mLastVSyncCpuTimeMillis;
139
140        // The VSync should have activated immediately instead of at the next real vsync.
141        assertTrue(delay < period);
142    }
143
144    @MediumTest
145    public void testVSyncActivationFromIdleAllowJBVSync() throws InterruptedException {
146        performVSyncActivationFromIdle(true);
147    }
148
149    @MediumTest
150    public void testVSyncActivationFromIdleDisallowJBVSync() throws InterruptedException {
151        performVSyncActivationFromIdle(false);
152    }
153}
154