blob: 6f081c759df7a51da6c3b2b79c09327ad7cc13d4 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.screenshot;
import static com.google.common.util.concurrent.Futures.getUnchecked;
import static com.google.common.util.concurrent.Futures.immediateFuture;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static java.lang.Math.abs;
import android.content.Context;
import android.testing.AndroidTestingRunner;
import android.view.ScrollCaptureResponse;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.screenshot.ScrollCaptureClient.Session;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Tests for ScrollCaptureController which manages sequential image acquisition for long
* screenshots.
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class ScrollCaptureControllerTest extends SysuiTestCase {
private static final ScrollCaptureResponse EMPTY_RESPONSE =
new ScrollCaptureResponse.Builder().build();
@Test
public void testInfinite() {
ScrollCaptureController controller = new TestScenario()
.withPageHeight(100)
.withMaxPages(2.5f)
.withTileHeight(10)
.withAvailableRange(Integer.MIN_VALUE, Integer.MAX_VALUE)
.createController(mContext);
ScrollCaptureController.LongScreenshot screenshot =
getUnchecked(controller.run(EMPTY_RESPONSE));
assertEquals("top", -90, screenshot.getTop());
assertEquals("bottom", 160, screenshot.getBottom());
// Test that top portion is >= getTargetTopSizeRatio()
// (Due to tileHeight, top will almost always be larger than the target)
float topPortion = abs(screenshot.getTop()) / abs((float) screenshot.getBottom());
if (topPortion < controller.getTargetTopSizeRatio()) {
fail("expected top portion > "
+ (controller.getTargetTopSizeRatio() * 100) + "%"
+ " but was " + (topPortion * 100));
}
}
@Test
public void testInfiniteWithPartialResultsTop() {
ScrollCaptureController controller = new TestScenario()
.withPageHeight(100)
.withPageVisibleRange(5, 100) // <-- simulate 5px of invisible top
.withMaxPages(2.5f)
.withTileHeight(50)
.withAvailableRange(Integer.MIN_VALUE, Integer.MAX_VALUE)
.createController(mContext);
ScrollCaptureController.LongScreenshot screenshot =
getUnchecked(controller.run(EMPTY_RESPONSE));
// Each tile is cropped to the visible page size, which is inset 5px from the TOP
// requested result
// 0, 50 5, 50
// -45, 5 -40, 5
// -90, -40 -85, -40 <-- clear previous / top
// -40, 10 -40, 10 (not cropped, target is positioned fully within visible range)
// 10, 60 10, 60
// 60, 110 60, 110
// 110, 160 110, 160
// 160, 210 160, 210 <-- bottom
assertEquals("top", -85, screenshot.getTop());
assertEquals("bottom", 210, screenshot.getBottom());
}
@Test
public void testInfiniteWithPartialResultsBottom() {
ScrollCaptureController controller = new TestScenario()
.withPageHeight(100)
.withPageVisibleRange(0, 95) // <-- simulate 5px of invisible bottom
.withMaxPages(2.5f)
.withTileHeight(50)
.withAvailableRange(Integer.MIN_VALUE, Integer.MAX_VALUE)
.createController(mContext);
ScrollCaptureController.LongScreenshot screenshot =
getUnchecked(controller.run(EMPTY_RESPONSE));
// Each tile is cropped to the visible page size, which is inset 5px from the BOTTOM
// requested result
// 0, 50 0, 50 // not cropped, positioned within visible range
// -50, 0 -50, 0 <-- clear previous/reverse
// 0, 50 0, 45 // target now positioned at page bottom, bottom cropped
// 45, 95, 45, 90
// 90, 140, 140, 135
// 135, 185 185, 180
// 180, 230 180, 225 <-- bottom
assertEquals("top", -50, screenshot.getTop());
assertEquals("bottom", 225, screenshot.getBottom());
}
@Test
public void testLimitedBottom() {
ScrollCaptureController controller = new TestScenario()
.withPageHeight(100)
.withMaxPages(2.5f)
.withTileHeight(10)
.withAvailableRange(Integer.MIN_VALUE, 150)
.createController(mContext);
ScrollCaptureController.LongScreenshot screenshot =
getUnchecked(controller.run(EMPTY_RESPONSE));
assertEquals("top", -100, screenshot.getTop());
assertEquals("bottom", 150, screenshot.getBottom());
}
@Test
public void testLimitedTopAndBottom() {
ScrollCaptureController controller = new TestScenario()
.withPageHeight(100)
.withMaxPages(2.5f)
.withTileHeight(10)
.withAvailableRange(-50, 150)
.createController(mContext);
ScrollCaptureController.LongScreenshot screenshot =
getUnchecked(controller.run(EMPTY_RESPONSE));
assertEquals("top", -50, screenshot.getTop());
assertEquals("bottom", 150, screenshot.getBottom());
}
@Test
public void testVeryLimitedTopInfiniteBottom() {
ScrollCaptureController controller = new TestScenario()
.withPageHeight(100)
.withMaxPages(2.5f)
.withTileHeight(10)
.withAvailableRange(-10, Integer.MAX_VALUE)
.createController(mContext);
ScrollCaptureController.LongScreenshot screenshot =
getUnchecked(controller.run(EMPTY_RESPONSE));
assertEquals("top", -10, screenshot.getTop());
assertEquals("bottom", 240, screenshot.getBottom());
}
@Test
public void testVeryLimitedTopLimitedBottom() {
ScrollCaptureController controller = new TestScenario()
.withPageHeight(100)
.withMaxPages(2.5f)
.withTileHeight(10)
.withAvailableRange(-10, 200)
.createController(mContext);
ScrollCaptureController.LongScreenshot screenshot =
getUnchecked(controller.run(EMPTY_RESPONSE));
assertEquals("top", -10, screenshot.getTop());
assertEquals("bottom", 200, screenshot.getBottom());
}
/**
* Build and configure a stubbed controller for each test case.
*/
private static class TestScenario {
private int mPageHeight = -1;
private int mTileHeight = -1;
private boolean mAvailableRangeSet;
private int mAvailableTop;
private int mAvailableBottom;
private int mLocalVisibleTop;
private int mLocalVisibleBottom = -1;
private float mMaxPages = -1;
TestScenario withPageHeight(int pageHeight) {
if (pageHeight < 0) {
throw new IllegalArgumentException("pageHeight must be positive");
}
mPageHeight = pageHeight;
return this;
}
TestScenario withTileHeight(int tileHeight) {
if (tileHeight < 0) {
throw new IllegalArgumentException("tileHeight must be positive");
}
mTileHeight = tileHeight;
return this;
}
TestScenario withAvailableRange(int top, int bottom) {
mAvailableRangeSet = true;
mAvailableTop = top;
mAvailableBottom = bottom;
return this;
}
TestScenario withMaxPages(float maxPages) {
if (maxPages < 0) {
throw new IllegalArgumentException("maxPages must be positive");
}
mMaxPages = maxPages;
return this;
}
TestScenario withPageVisibleRange(int top, int bottom) {
if (top < 0 || bottom < 0) {
throw new IllegalArgumentException("top and bottom must be positive");
}
mLocalVisibleTop = top;
mLocalVisibleBottom = bottom;
return this;
}
ScrollCaptureController createController(Context context) {
if (mTileHeight < 0) {
throw new IllegalArgumentException("tileHeight not set");
}
if (!mAvailableRangeSet) {
throw new IllegalArgumentException("availableRange not set");
}
if (mPageHeight < 0) {
throw new IllegalArgumentException("pageHeight not set");
}
if (mMaxPages < 0) {
throw new IllegalArgumentException("maxPages not set");
}
// Default: page fully visible
if (mLocalVisibleBottom < 0) {
mLocalVisibleBottom = mPageHeight;
}
Session session = new FakeSession(mPageHeight, mMaxPages, mTileHeight,
mLocalVisibleTop, mLocalVisibleBottom, mAvailableTop, mAvailableBottom,
/* maxTiles */ 30);
ScrollCaptureClient client = mock(ScrollCaptureClient.class);
when(client.start(/* response */ any(), /* maxPages */ anyFloat()))
.thenReturn(immediateFuture(session));
return new ScrollCaptureController(context, context.getMainExecutor(),
client, new ImageTileSet(context.getMainThreadHandler()),
new UiEventLoggerFake());
}
}
}