Commit 3c870ab9 authored by bsheedy's avatar bsheedy Committed by Commit Bot

Add support for VR frame buffer dumps in render tests

Adds support for using render tests for VR UI through
VR's ability to dump the frame buffer to disk.

We can't use the standard approach of passing
RenderTestRule a View to render since most of the VR UI
is rendered natively using Skia. Instead, we refactor
RenderTestRule slightly to support taking either a
View or a Bitmap, then pass it the Bitmap of the
frame buffer dump.

Bug: 904012

Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:linux_vr;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel
Change-Id: Icebfff58cea4ac8ab29fb4dbadc715d729729eda
Reviewed-on: https://chromium-review.googlesource.com/c/1241471Reviewed-by: default avatarPeter Conn <peconn@chromium.org>
Commit-Queue: Brian Sheedy <bsheedy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#607340}
parent 07a9fc2c
......@@ -874,6 +874,7 @@ if (enable_vr || (enable_arcore && package_arcore)) {
data = [
"//chrome/android/shared_preference_files/test/",
"//components/test/data/js_dialogs/render_tests/",
"//components/test/data/permission_dialogs/render_tests/",
"//third_party/gvr-android-sdk/test-apks/",
]
}
......
......@@ -10,6 +10,8 @@ import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_SHORT_
import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_DAYDREAM;
import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_DAYDREAM_OR_STANDALONE;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PointF;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
......@@ -22,6 +24,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.Restriction;
import org.chromium.base.test.util.UrlUtils;
import org.chromium.chrome.browser.ChromeSwitches;
......@@ -30,10 +33,12 @@ import org.chromium.chrome.browser.vr.rules.HeadTrackingMode;
import org.chromium.chrome.browser.vr.util.NativeUiUtils;
import org.chromium.chrome.browser.vr.util.VrBrowserTransitionUtils;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.util.RenderTestRule;
import org.chromium.content_public.browser.test.util.JavaScriptUtils;
import org.chromium.net.test.EmbeddedTestServer;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
......@@ -52,15 +57,19 @@ public class VrBrowserDialogTest {
// A long enough sleep after triggering/interacting with a dialog to ensure that the interaction
// has propagated through the render pipeline, i.e. the result of the interaction will actually
// be visible on the screen.
private static final String TEST_IMAGE_DIR = "chrome/test/data/vr/UiCapture";
private static final String TEMP_IMAGE_DIR = "chrome/test/data/vr/UiCapture";
private static final File sBaseDirectory =
new File(UrlUtils.getIsolatedTestFilePath(TEST_IMAGE_DIR));
new File(UrlUtils.getIsolatedTestFilePath(TEMP_IMAGE_DIR));
// We explicitly instantiate a rule here instead of using parameterization since this class
// only ever runs in ChromeTabbedActivity.
@Rule
public ChromeTabbedActivityVrTestRule mVrTestRule = new ChromeTabbedActivityVrTestRule();
@Rule
public RenderTestRule mRenderTestRule =
new RenderTestRule("components/test/data/permission_dialogs/render_tests");
private VrBrowserTestFramework mVrBrowserTestFramework;
private EmbeddedTestServer mServer;
......@@ -81,14 +90,27 @@ public class VrBrowserDialogTest {
}
}
private void captureScreen(String filename) throws InterruptedException {
// Ensure that any UI changes that have been rendered and submitted have actually propogated
// to the screen.
NativeUiUtils.waitNumFrames(2);
File baseFilename = new File(sBaseDirectory, filename);
private void captureScreen(String filenameBase) throws InterruptedException {
File baseFilename = new File(sBaseDirectory, filenameBase);
NativeUiUtils.dumpNextFramesFrameBuffers(baseFilename.getPath());
}
private void compareCapturedImaged(String filenameBase, String suffix, String id)
throws IOException {
File filepath = new File(sBaseDirectory, filenameBase + suffix + ".png");
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bitmap = BitmapFactory.decodeFile(filepath.getPath(), options);
// The browser UI dump contains both eyes rendered, which is unnecessary for comparing
// since any difference in one should show up in the other. So, take the left half of
// the image to get the left eye, which reduces the amount of space the image saved to Git
// takes up.
if (suffix.equals(NativeUiUtils.FRAME_BUFFER_SUFFIX_BROWSER_UI)) {
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth() / 2, bitmap.getHeight());
}
mRenderTestRule.compareForResult(bitmap, id);
}
private void navigateAndDisplayPermissionPrompt(String page, String promptCommand)
throws InterruptedException, TimeoutException {
// Trying to grant permissions on file:// URLs ends up hitting DCHECKS, so load from a local
......@@ -146,15 +168,32 @@ public class VrBrowserDialogTest {
@Test
@LargeTest
@HeadTrackingMode(HeadTrackingMode.SupportedMode.FROZEN)
public void testMicrophonePermissionPrompt() throws InterruptedException, TimeoutException {
@Feature({"Browser", "RenderTest"})
public void testMicrophonePermissionPrompt()
throws InterruptedException, TimeoutException, IOException {
// Display audio permissions prompt.
navigateAndDisplayPermissionPrompt(
"test_navigation_2d_page", "navigator.getUserMedia({audio: true}, ()=>{}, ()=>{})");
// Capture image
captureScreen("MicrophonePermissionPrompt_Visible");
String filenameBase = "MicrophonePermissionPrompt_Visible";
captureScreen(filenameBase);
compareCapturedImaged(filenameBase, NativeUiUtils.FRAME_BUFFER_SUFFIX_BROWSER_UI,
"microphone_permission_prompt_visible_browser_ui");
// Ensure that the microphone icon appears before we capture the image.
NativeUiUtils.performActionAndWaitForVisibilityStatus(
UserFriendlyElementName.MICROPHONE_PERMISSION_INDICATOR, true /* visible */, () -> {
try {
NativeUiUtils.clickFallbackUiPositiveButton();
captureScreen("MicrophonePermissionPrompt_Granted");
} catch (InterruptedException e) {
Assert.fail("Interrupted while granting microphone permission: "
+ e.toString());
}
});
filenameBase = "MicrophonePermissionPrompt_Granted";
captureScreen(filenameBase);
compareCapturedImaged(filenameBase, NativeUiUtils.FRAME_BUFFER_SUFFIX_BROWSER_UI,
"microphone_permission_prompt_granted_browser_ui");
mVrBrowserTestFramework.assertNoJavaScriptErrors();
}
......
......@@ -44,6 +44,11 @@ public class NativeUiUtils {
// waiting for the suggestion box to appear doesn't work, e.g. if you need to input text, wait
// for autocomplete, then input more text before committing. 20 is arbitrary, but stable.
public static final int NUM_FRAMES_FOR_SUGGESTION_UPDATE = 20;
public static final String FRAME_BUFFER_SUFFIX_WEB_XR_OVERLAY = "_WebXrOverlay";
public static final String FRAME_BUFFER_SUFFIX_WEB_XR_CONTENT = "_WebXrContent";
public static final String FRAME_BUFFER_SUFFIX_BROWSER_UI = "_BrowserUi";
public static final String FRAME_BUFFER_SUFFIX_BROWSER_CONTENT = "_BrowserContent";
// Arbitrary but reasonable amount of time to expect the UI to stop updating after interacting
// with an element.
private static final int DEFAULT_UI_QUIESCENCE_TIMEOUT_MS = 2000;
......@@ -309,8 +314,9 @@ public class NativeUiUtils {
*/
public static void dumpNextFramesFrameBuffers(String filepathBase) throws InterruptedException {
// Clear out any existing images with the names of the files that may be created.
for (String suffix :
new String[] {"_WebXrOverlay", "_WebXrContent", "_BrowserUi", "_BrowserContent"}) {
for (String suffix : new String[] {FRAME_BUFFER_SUFFIX_WEB_XR_OVERLAY,
FRAME_BUFFER_SUFFIX_WEB_XR_CONTENT, FRAME_BUFFER_SUFFIX_BROWSER_UI,
FRAME_BUFFER_SUFFIX_BROWSER_CONTENT}) {
File dumpFile = new File(filepathBase, suffix + ".png");
Assert.assertFalse("Failed to delete existing screenshot",
dumpFile.exists() && !dumpFile.delete());
......
......@@ -83,6 +83,8 @@ UiElementName UserFriendlyElementNameToUiElementName(
return kWebVrAudioCaptureIndicator;
case UserFriendlyElementName::kWebXrHostedContent:
return kWebVrHostedUiContent;
case UserFriendlyElementName::kMicrophonePermissionIndicator:
return kAudioCaptureIndicator;
default:
NOTREACHED();
return kNone;
......
......@@ -36,6 +36,8 @@ enum class UserFriendlyElementName : int {
// is in use.
kWebXrHostedContent, // Hosted content in a WebXR immersive session, e.g.
// permission prompts.
kMicrophonePermissionIndicator, // The microphone icon that appears when a
// page is using the microphone permission.
};
// These are the types of actions that Java can request callbacks for once
......
......@@ -159,6 +159,22 @@ public class RenderTestRule extends TestWatcher {
}
});
compareForResult(testBitmap, id);
}
/**
* Compares the given |testBitmap| to the golden with the |id|. The RenderTestRule will throw
* an exception after the test method has completed if the view does not match the golden or if
* a golden is missing on a device it should be present (see
* {@link RenderTestRule#RENDER_TEST_MODEL_SDK_PAIRS}).
*
* Tests should prefer {@link RenderTestRule#render(View, String) render} to this if possible.
*
* @throws IOException if the rendered image cannot be saved to the device.
*/
public void compareForResult(Bitmap testBitmap, String id) throws IOException {
Assert.assertTrue("Render Tests must have the RenderTest feature.", mHasRenderTestFeature);
String filename = imageName(mTestClassName, mVariantPrefix, id);
BitmapFactory.Options options = new BitmapFactory.Options();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment