Merge "Add CTS for FD support in StrictJarFile."
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
new file mode 100644
index 0000000..77f70e7
--- /dev/null
+++ b/PREUPLOAD.cfg
@@ -0,0 +1,5 @@
+[Hook Scripts]
+checkstyle_hook = ../development/tools/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
+ -fw apps/CtsVerifier/src/com/android/cts/verifier/usb/
+ apps/CtsVerifierUSBCompanion/
+ tests/tests/print/
diff --git a/apps/CameraITS/pymodules/its/image.py b/apps/CameraITS/pymodules/its/image.py
index 4d98f9c..60a2173f 100644
--- a/apps/CameraITS/pymodules/its/image.py
+++ b/apps/CameraITS/pymodules/its/image.py
@@ -408,8 +408,9 @@
Returns:
The black level value for the specified channel.
"""
- if cap_res.has_key("android.sensor.dynamicBlackLevel"):
- black_levels = cap_res["android.sensor.dynamicBlackLevel"]
+ if (cap_res.has_key('android.sensor.dynamicBlackLevel') and
+ cap_res['android.sensor.dynamicBlackLevel'] is not None):
+ black_levels = cap_res['android.sensor.dynamicBlackLevel']
else:
black_levels = props['android.sensor.blackLevelPattern']
idxs = its.image.get_canonical_cfa_order(props)
diff --git a/apps/CameraITS/tests/scene0/test_metadata.py b/apps/CameraITS/tests/scene0/test_metadata.py
index e5fbba5..69ed19d 100644
--- a/apps/CameraITS/tests/scene0/test_metadata.py
+++ b/apps/CameraITS/tests/scene0/test_metadata.py
@@ -49,7 +49,6 @@
check('props.has_key("android.info.supportedHardwareLevel")')
check('props["android.info.supportedHardwareLevel"] is not None')
check('props["android.info.supportedHardwareLevel"] in [0,1,2,3]')
- full = getval('props["android.info.supportedHardwareLevel"]') == 1
manual_sensor = its.caps.manual_sensor(props)
# Test: rollingShutterSkew, and frameDuration tags must all be present,
@@ -75,7 +74,12 @@
check('props["android.scaler.croppingType"] is not None')
check('props["android.scaler.croppingType"] in [0,1]')
- assert(not failed)
+ # Test: android.sensor.blackLevelPattern exists for RAW and is not None
+ if its.caps.raw(props):
+ check('props.has_key("android.sensor.blackLevelPattern")')
+ check('props["android.sensor.blackLevelPattern"] is not None')
+
+ assert not failed
if not its.caps.legacy(props):
# Test: pixel_pitch, FOV, and hyperfocal distance are reasonable
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index ff05850..03bf268 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -287,6 +287,12 @@
'turn_off_screen.py'), screen_id_arg]
retcode = subprocess.call(cmd)
assert retcode == 0
+ print 'Shutting down DUT screen: ', device_id
+ screen_id_arg = ('screen=%s' % device_id)
+ cmd = ['python', os.path.join(os.environ['CAMERA_ITS_TOP'], 'tools',
+ 'turn_off_screen.py'), screen_id_arg]
+ retcode = subprocess.call(cmd)
+ assert retcode == 0
print "ITS tests finished. Please go back to CtsVerifier and proceed"
diff --git a/apps/CameraITS/tools/turn_off_screen.py b/apps/CameraITS/tools/turn_off_screen.py
index 4163ab4..207042b 100644
--- a/apps/CameraITS/tools/turn_off_screen.py
+++ b/apps/CameraITS/tools/turn_off_screen.py
@@ -33,10 +33,10 @@
process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
cmd_ret = process.stdout.read()
screen_state = re.split(r'[s|=]', cmd_ret)[-1]
- if screen_state == 'OFF\n':
- print 'Screen OFF. Turning ON.'
+ if 'OFF' in screen_state:
+ print 'Screen already OFF.'
else:
- wakeup = ('adb -s %s shell input keyevent POWER' % screen_id)
- subprocess.Popen(wakeup.split())
+ pwrdn = ('adb -s %s shell input keyevent POWER' % screen_id)
+ subprocess.Popen(pwrdn.split())
if __name__ == '__main__':
main()
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index b776ecc..d49f279 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1055,6 +1055,17 @@
<meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
</activity>
+ <activity android:name=".location.GnssStatusTestsActivity"
+ android:label="@string/location_gnss_status_test"
+ android:screenOrientation="locked">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.cts.intent.category.MANUAL_TEST"/>
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_hardware"/>
+ <meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
+ </activity>
+
<activity android:name=".location.LocationListenerActivity"
android:label="@string/location_listener_activity"
android:configChanges="keyboardHidden|orientation|screenSize">
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 668776f..6311fa3 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -568,6 +568,7 @@
<string name="location_gnss_reg_test">GNSS Measurement Registration Test</string>
<string name="location_gnss_value_test">GNSS Measurement Values Test</string>
<string name="location_gnss_nav_msg_test">GNSS Navigation Message Test</string>
+ <string name="location_gnss_status_test">GNSS Status Test</string>
<string name="location_gnss_test_info">This test verifies basic GNSS behavior.
Make sure the device has line of sight to GNSS satellites
(for example, stationary on a windowsill. If needed, try again, outside, also with the
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsSerializer.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsSerializer.java
index 193c94c0..2a6c146 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsSerializer.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsSerializer.java
@@ -371,7 +371,7 @@
try {
Object keyValue = getKeyValue(md, keyObj);
if (keyValue == null) {
- return new MetadataEntry(keyName, JSONObject.NULL);
+ return null;
}
int arrayLen = Array.getLength(keyValue);
Type elmtType = ((GenericArrayType)keyType).getGenericComponentType();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssStatusTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssStatusTestsActivity.java
new file mode 100644
index 0000000..a64c5d3
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/location/GnssStatusTestsActivity.java
@@ -0,0 +1,15 @@
+package com.android.cts.verifier.location;
+
+import com.android.cts.verifier.location.base.GnssCtsTestActivity;
+import android.location.cts.GnssStatusTest;
+
+/**
+ * Activity to execute CTS GnssStatusTest.
+ * It is a wrapper for {@link GnssStatusTest} running with AndroidJUnitRunner.
+ */
+
+public class GnssStatusTestsActivity extends GnssCtsTestActivity {
+ public GnssStatusTestsActivity() {
+ super(GnssStatusTest.class);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/streamquality/PlayVideoActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/streamquality/PlayVideoActivity.java
index b8d8c30..017ae42 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/streamquality/PlayVideoActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/streamquality/PlayVideoActivity.java
@@ -51,7 +51,7 @@
static final String EXTRA_STREAM = "com.android.cts.verifier.streamquality.EXTRA_STREAM";
private static final String TAG = PlayVideoActivity.class.getName();
- private static final long ENABLE_PASS_DELAY = 60 * 1000;
+ private static final long ENABLE_PASS_DELAY = 5 * 1000;
private static final int FAIL_DIALOG_ID = 1;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/streamquality/StreamingVideoActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/streamquality/StreamingVideoActivity.java
index 00a52ae..8354fae 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/streamquality/StreamingVideoActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/streamquality/StreamingVideoActivity.java
@@ -109,7 +109,7 @@
"618FBB112E1B2FBB66DA9F203AE8CC7DF93C7400" +
".20498AA006E999F42BE69D66E3596F2C7CA18114";
private static final String RTSP_LOOKUP_URI_TEMPLATE =
- "http://redirector.c.youtube.com/videoplayback?id=271de9756065677e" +
+ "http://redirector.gvt1.com/videoplayback?id=271de9756065677e" +
"&source=youtube&protocol=rtsp&sparams=ip,ipbits,expire,id,itag,source" +
"&ip=0.0.0.0&ipbits=0&expire=19000000000&key=ik0&alr=yes" +
"&itag=%d" +
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/usb/device/UsbDeviceTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/usb/device/UsbDeviceTestActivity.java
index d7c575b..d762096 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/usb/device/UsbDeviceTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/usb/device/UsbDeviceTestActivity.java
@@ -16,6 +16,16 @@
package com.android.cts.verifier.usb.device;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeNotNull;
+import static org.junit.Assume.assumeTrue;
+
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -36,32 +46,24 @@
import android.util.Log;
import android.util.Pair;
import android.widget.Toast;
+
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
+
import org.junit.AssumptionViolatedException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
-import java.util.Map;
-import java.util.Random;
-import java.util.Set;
import java.util.HashMap;
import java.util.LinkedList;
+import java.util.Map;
import java.util.NoSuchElementException;
+import java.util.Random;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeNotNull;
-import static org.junit.Assume.assumeTrue;
-
public class UsbDeviceTestActivity extends PassFailButtons.Activity {
private static final String ACTION_USB_PERMISSION =
"com.android.cts.verifier.usb.device.USB_PERMISSION";
@@ -73,6 +75,10 @@
private final BroadcastReceiver mUsbDeviceConnectionReceiver;
private Thread mTestThread;
+ private static long now() {
+ return System.nanoTime() / 1000000;
+ }
+
/**
* Run a {@link Invokable} and expect a {@link Throwable} of a certain type.
*
@@ -356,14 +362,14 @@
*/
private void receiveWithEmptyBuffer(@NonNull UsbDeviceConnection connection,
@NonNull UsbEndpoint in, @Nullable byte[] buffer, int offset, int length) {
- long startTime = System.currentTimeMillis();
+ long startTime = now();
int numReceived;
if (offset == 0) {
numReceived = connection.bulkTransfer(in, buffer, length, 0);
} else {
numReceived = connection.bulkTransfer(in, buffer, offset, length, 0);
}
- long endTime = System.currentTimeMillis();
+ long endTime = now();
assertEquals(-1, numReceived);
// The transfer should block
@@ -424,7 +430,7 @@
}
/**
- * Send a USB request and receive it back.
+ * Send a USB request using the {@link UsbRequest#queue legacy path} and receive it back.
*
* @param connection The connection to use
* @param in The endpoint to receive requests from
@@ -437,9 +443,10 @@
* @param limitInSlice The limited parameter in the final buffer
* @param useDirectBuffer If the buffer to be used should be a direct buffer
*/
- private void echoUsbRequest(@NonNull UsbDeviceConnection connection, @NonNull UsbEndpoint in,
- @NonNull UsbEndpoint out, int size, int originalSize, int sliceStart, int sliceEnd,
- int positionInSlice, int limitInSlice, boolean useDirectBuffer) {
+ private void echoUsbRequestLegacy(@NonNull UsbDeviceConnection connection,
+ @NonNull UsbEndpoint in, @NonNull UsbEndpoint out, int size, int originalSize,
+ int sliceStart, int sliceEnd, int positionInSlice, int limitInSlice,
+ boolean useDirectBuffer) {
Random random = new Random();
UsbRequest sent = new UsbRequest();
@@ -542,12 +549,127 @@
* @param connection The connection to use
* @param in The endpoint to receive requests from
* @param out The endpoint to send requests to
+ * @param originalSize The size of the original buffer
+ * @param sliceStart The start of the final buffer in the original buffer
+ * @param sliceEnd The end of the final buffer in the original buffer
+ * @param positionInSlice The position parameter in the final buffer
+ * @param limitInSlice The limited parameter in the final buffer
+ * @param useDirectBuffer If the buffer to be used should be a direct buffer
+ */
+ private void echoUsbRequest(@NonNull UsbDeviceConnection connection, @NonNull UsbEndpoint in,
+ @NonNull UsbEndpoint out, int originalSize, int sliceStart, int sliceEnd,
+ int positionInSlice, int limitInSlice, boolean useDirectBuffer,
+ boolean makeSendBufferReadOnly) {
+ Random random = new Random();
+
+ UsbRequest sent = new UsbRequest();
+ boolean isInited = sent.initialize(connection, out);
+ assertTrue(isInited);
+ Object sentClientData = new Object();
+ sent.setClientData(sentClientData);
+
+ UsbRequest receive = new UsbRequest();
+ isInited = receive.initialize(connection, in);
+ assertTrue(isInited);
+ Object receiveClientData = new Object();
+ receive.setClientData(receiveClientData);
+
+ ByteBuffer bufferSent;
+ if (useDirectBuffer) {
+ bufferSent = ByteBuffer.allocateDirect(originalSize);
+ } else {
+ bufferSent = ByteBuffer.allocate(originalSize);
+ }
+ for (int i = 0; i < originalSize; i++) {
+ bufferSent.put((byte) random.nextInt());
+ }
+ if (makeSendBufferReadOnly) {
+ bufferSent = bufferSent.asReadOnlyBuffer();
+ }
+ bufferSent.position(sliceStart);
+ bufferSent.limit(sliceEnd);
+ ByteBuffer bufferSentSliced = bufferSent.slice();
+ bufferSentSliced.position(positionInSlice);
+ bufferSentSliced.limit(limitInSlice);
+
+ bufferSent.position(0);
+ bufferSent.limit(originalSize);
+
+ ByteBuffer bufferReceived;
+ if (useDirectBuffer) {
+ bufferReceived = ByteBuffer.allocateDirect(originalSize);
+ } else {
+ bufferReceived = ByteBuffer.allocate(originalSize);
+ }
+ bufferReceived.position(sliceStart);
+ bufferReceived.limit(sliceEnd);
+ ByteBuffer bufferReceivedSliced = bufferReceived.slice();
+ bufferReceivedSliced.position(positionInSlice);
+ bufferReceivedSliced.limit(limitInSlice);
+
+ bufferReceived.position(0);
+ bufferReceived.limit(originalSize);
+
+ boolean wasQueued = receive.enqueue(bufferReceivedSliced);
+ assertTrue(wasQueued);
+ wasQueued = sent.enqueue(bufferSentSliced);
+ assertTrue(wasQueued);
+
+ for (int reqRun = 0; reqRun < 2; reqRun++) {
+ UsbRequest finished = connection.requestWait();
+
+ if (finished == receive) {
+ assertEquals(limitInSlice, bufferReceivedSliced.limit());
+ assertEquals(limitInSlice, bufferReceivedSliced.position());
+
+ for (int i = 0; i < originalSize; i++) {
+ if (i >= sliceStart + positionInSlice && i < sliceStart + limitInSlice) {
+ assertEquals(bufferSent.get(i), bufferReceived.get(i));
+ } else {
+ assertEquals(0, bufferReceived.get(i));
+ }
+ }
+
+ assertSame(receiveClientData, finished.getClientData());
+ assertSame(in, finished.getEndpoint());
+ } else {
+ assertEquals(limitInSlice, bufferSentSliced.limit());
+ assertEquals(limitInSlice, bufferSentSliced.position());
+
+ assertSame(sent, finished);
+ assertSame(sentClientData, finished.getClientData());
+ assertSame(out, finished.getEndpoint());
+ }
+ finished.close();
+ }
+ }
+
+ /**
+ * Send a USB request using the {@link UsbRequest#queue legacy path} and receive it back.
+ *
+ * @param connection The connection to use
+ * @param in The endpoint to receive requests from
+ * @param out The endpoint to send requests to
+ * @param size The size of the request to send
+ * @param useDirectBuffer If the buffer to be used should be a direct buffer
+ */
+ private void echoUsbRequestLegacy(@NonNull UsbDeviceConnection connection,
+ @NonNull UsbEndpoint in, @NonNull UsbEndpoint out, int size, boolean useDirectBuffer) {
+ echoUsbRequestLegacy(connection, in, out, size, size, 0, size, 0, size, useDirectBuffer);
+ }
+
+ /**
+ * Send a USB request and receive it back.
+ *
+ * @param connection The connection to use
+ * @param in The endpoint to receive requests from
+ * @param out The endpoint to send requests to
* @param size The size of the request to send
* @param useDirectBuffer If the buffer to be used should be a direct buffer
*/
private void echoUsbRequest(@NonNull UsbDeviceConnection connection, @NonNull UsbEndpoint in,
@NonNull UsbEndpoint out, int size, boolean useDirectBuffer) {
- echoUsbRequest(connection, in, out, size, size, 0, size, 0, size, useDirectBuffer);
+ echoUsbRequest(connection, in, out, size, 0, size, 0, size, useDirectBuffer, false);
}
/**
@@ -557,7 +679,7 @@
* @param in The endpoint to receive requests from
* @param out The endpoint to send requests to
*/
- private void echoOversizedUsbRequest(@NonNull UsbDeviceConnection connection,
+ private void echoOversizedUsbRequestLegacy(@NonNull UsbDeviceConnection connection,
@NonNull UsbEndpoint in, @NonNull UsbEndpoint out) {
Random random = new Random();
int totalSize = MAX_BUFFER_SIZE * 3 / 2;
@@ -601,12 +723,58 @@
}
/**
- * Send a USB request with size 0.
+ * Time out while waiting for USB requests.
+ *
+ * @param connection The connection to use
+ */
+ private void timeoutWhileWaitingForUsbRequest(@NonNull UsbDeviceConnection connection)
+ throws Throwable {
+ assertException(() -> connection.requestWait(-1), IllegalArgumentException.class);
+
+ long startTime = now();
+ UsbRequest req = connection.requestWait(100);
+ assertNull(req);
+ assertTrue(now() - startTime >= 100);
+ assertTrue(now() - startTime < 400);
+
+ startTime = now();
+ req = connection.requestWait(0);
+ assertNull(req);
+ assertTrue(now() - startTime < 400);
+ }
+
+ /**
+ * Receive a USB request before a timeout triggers
+ *
+ * @param connection The connection to use
+ * @param in The endpoint to receive requests from
+ */
+ private void receiveAfterTimeout(@NonNull UsbDeviceConnection connection,
+ @NonNull UsbEndpoint in, int timeout) throws InterruptedException {
+ UsbRequest reqQueued = new UsbRequest();
+ ByteBuffer buffer = ByteBuffer.allocate(1);
+
+ reqQueued.initialize(connection, in);
+ reqQueued.enqueue(buffer);
+
+ // Let the kernel receive and process the request
+ Thread.sleep(50);
+
+ long startTime = now();
+ UsbRequest reqFinished = connection.requestWait(timeout);
+ assertTrue(now() - startTime < timeout + 50);
+ assertSame(reqQueued, reqFinished);
+ reqFinished.close();
+ }
+
+ /**
+ * Send a USB request with size 0 using the {@link UsbRequest#queue legacy path}.
*
* @param connection The connection to use
* @param out The endpoint to send requests to
+ * @param useDirectBuffer Send data from a direct buffer
*/
- private void sendZeroLengthRequest(@NonNull UsbDeviceConnection connection,
+ private void sendZeroLengthRequestLegacy(@NonNull UsbDeviceConnection connection,
@NonNull UsbEndpoint out, boolean useDirectBuffer) {
UsbRequest sent = new UsbRequest();
boolean isInited = sent.initialize(connection, out);
@@ -627,12 +795,58 @@
}
/**
+ * Send a USB request with size 0.
+ *
+ * @param connection The connection to use
+ * @param out The endpoint to send requests to
+ * @param useDirectBuffer Send data from a direct buffer
+ */
+ private void sendZeroLengthRequest(@NonNull UsbDeviceConnection connection,
+ @NonNull UsbEndpoint out, boolean useDirectBuffer) {
+ UsbRequest sent = new UsbRequest();
+ boolean isInited = sent.initialize(connection, out);
+ assertTrue(isInited);
+
+ ByteBuffer buffer;
+ if (useDirectBuffer) {
+ buffer = ByteBuffer.allocateDirect(0);
+ } else {
+ buffer = ByteBuffer.allocate(0);
+ }
+
+ boolean isQueued = sent.enqueue(buffer);
+ assumeTrue(isQueued);
+ UsbRequest finished = connection.requestWait();
+ assertSame(finished, sent);
+ finished.close();
+ }
+
+ /**
+ * Send a USB request with a null buffer.
+ *
+ * @param connection The connection to use
+ * @param out The endpoint to send requests to
+ */
+ private void sendNullRequest(@NonNull UsbDeviceConnection connection,
+ @NonNull UsbEndpoint out) {
+ UsbRequest sent = new UsbRequest();
+ boolean isInited = sent.initialize(connection, out);
+ assertTrue(isInited);
+
+ boolean isQueued = sent.enqueue(null);
+ assumeTrue(isQueued);
+ UsbRequest finished = connection.requestWait();
+ assertSame(finished, sent);
+ finished.close();
+ }
+
+ /**
* Receive a USB request with size 0.
*
* @param connection The connection to use
* @param in The endpoint to recevie requests from
*/
- private void receiveZeroLengthRequest(@NonNull UsbDeviceConnection connection,
+ private void receiveZeroLengthRequestLegacy(@NonNull UsbDeviceConnection connection,
@NonNull UsbEndpoint in, boolean useDirectBuffer) {
UsbRequest zeroReceived = new UsbRequest();
boolean isInited = zeroReceived.initialize(connection, in);
@@ -656,20 +870,20 @@
buffer1 = ByteBuffer.allocate(1);
}
- boolean isQueued = zeroReceived.queue(buffer, 0);
+ boolean isQueued = zeroReceived.enqueue(buffer);
assumeTrue(isQueued);
- isQueued = oneReceived.queue(buffer1, 0);
+ isQueued = oneReceived.enqueue(buffer1);
assumeTrue(isQueued);
// We expect both to be returned after some time
ArrayList<UsbRequest> finished = new ArrayList<>(2);
// We expect both request to come back after the delay, but then quickly
- long startTime = System.currentTimeMillis();
+ long startTime = now();
finished.add(connection.requestWait());
- long firstReturned = System.currentTimeMillis();
+ long firstReturned = now();
finished.add(connection.requestWait());
- long secondReturned = System.currentTimeMillis();
+ long secondReturned = now();
assumeTrue(firstReturned - startTime > 100);
assumeTrue(secondReturned - firstReturned < 100);
@@ -679,6 +893,130 @@
}
/**
+ * Tests the {@link UsbRequest#queue legacy implementaion} of {@link UsbRequest} and
+ * {@link UsbDeviceConnection#requestWait()}.
+ *
+ * @param connection The connection to use for testing
+ * @param iface The interface of the android accessory interface of the device
+ * @throws Throwable
+ */
+ private void usbRequestLegacyTests(@NonNull UsbDeviceConnection connection,
+ @NonNull UsbInterface iface) throws Throwable {
+ // Find bulk in and out endpoints
+ assumeTrue(iface.getEndpointCount() == 2);
+ final UsbEndpoint in = getEndpoint(iface, UsbConstants.USB_DIR_IN);
+ final UsbEndpoint out = getEndpoint(iface, UsbConstants.USB_DIR_OUT);
+ assertNotNull(in);
+ assertNotNull(out);
+
+ // Single threaded send and receive
+ nextTest(connection, in, out, "Echo 1 byte");
+ echoUsbRequestLegacy(connection, in, out, 1, true);
+
+ nextTest(connection, in, out, "Echo 1 byte");
+ echoUsbRequestLegacy(connection, in, out, 1, false);
+
+ nextTest(connection, in, out, "Echo max bytes");
+ echoUsbRequestLegacy(connection, in, out, MAX_BUFFER_SIZE, true);
+
+ nextTest(connection, in, out, "Echo max bytes");
+ echoUsbRequestLegacy(connection, in, out, MAX_BUFFER_SIZE, false);
+
+ nextTest(connection, in, out, "Echo oversized buffer");
+ echoOversizedUsbRequestLegacy(connection, in, out);
+
+ // Send empty requests
+ sendZeroLengthRequestLegacy(connection, out, true);
+ sendZeroLengthRequestLegacy(connection, out, false);
+
+ // waitRequest with timeout
+ timeoutWhileWaitingForUsbRequest(connection);
+
+ nextTest(connection, in, out, "Receive byte after some time");
+ receiveAfterTimeout(connection, in, 400);
+
+ nextTest(connection, in, out, "Receive byte immediately");
+ // Make sure the data is received before we queue the request for it
+ Thread.sleep(50);
+ receiveAfterTimeout(connection, in, 0);
+
+ /* TODO: Unreliable
+
+ // Zero length means waiting for the next data and then return
+ nextTest(connection, in, out, "Receive byte after some time");
+ receiveZeroLengthRequestLegacy(connection, in, true);
+
+ nextTest(connection, in, out, "Receive byte after some time");
+ receiveZeroLengthRequestLegacy(connection, in, true);
+
+ */
+
+ // UsbRequest.queue ignores position, limit, arrayOffset, and capacity
+ nextTest(connection, in, out, "Echo 42 bytes");
+ echoUsbRequestLegacy(connection, in, out, 42, 42, 0, 42, 5, 42, false);
+
+ nextTest(connection, in, out, "Echo 42 bytes");
+ echoUsbRequestLegacy(connection, in, out, 42, 42, 0, 42, 0, 36, false);
+
+ nextTest(connection, in, out, "Echo 42 bytes");
+ echoUsbRequestLegacy(connection, in, out, 42, 42, 5, 42, 0, 36, false);
+
+ nextTest(connection, in, out, "Echo 42 bytes");
+ echoUsbRequestLegacy(connection, in, out, 42, 42, 0, 36, 0, 31, false);
+
+ nextTest(connection, in, out, "Echo 42 bytes");
+ echoUsbRequestLegacy(connection, in, out, 42, 47, 0, 47, 0, 47, false);
+
+ nextTest(connection, in, out, "Echo 42 bytes");
+ echoUsbRequestLegacy(connection, in, out, 42, 47, 5, 47, 0, 42, false);
+
+ nextTest(connection, in, out, "Echo 42 bytes");
+ echoUsbRequestLegacy(connection, in, out, 42, 47, 0, 42, 0, 42, false);
+
+ nextTest(connection, in, out, "Echo 42 bytes");
+ echoUsbRequestLegacy(connection, in, out, 42, 47, 0, 47, 5, 47, false);
+
+ nextTest(connection, in, out, "Echo 42 bytes");
+ echoUsbRequestLegacy(connection, in, out, 42, 47, 5, 47, 5, 36, false);
+
+ // Illegal arguments
+ final UsbRequest req1 = new UsbRequest();
+ assertException(() -> req1.initialize(null, in), NullPointerException.class);
+ assertException(() -> req1.initialize(connection, null), NullPointerException.class);
+ boolean isInited = req1.initialize(connection, in);
+ assertTrue(isInited);
+ assertException(() -> req1.queue(null, 0), NullPointerException.class);
+ assertException(() -> req1.queue(ByteBuffer.allocate(1).asReadOnlyBuffer(), 1),
+ IllegalArgumentException.class);
+ req1.close();
+
+ // Cannot queue closed request
+ assertException(() -> req1.queue(ByteBuffer.allocate(1), 1), NullPointerException.class);
+ assertException(() -> req1.queue(ByteBuffer.allocateDirect(1), 1),
+ NullPointerException.class);
+ }
+
+ /**
+ * Repeat c n times
+ *
+ * @param c The character to repeat
+ * @param n The number of times to repeat
+ *
+ * @return c repeated n times
+ */
+ public static String repeat(char c, int n) {
+ final StringBuilder result = new StringBuilder();
+ for (int i = 0; i < n; i++) {
+ if (c != ' ' && i % 10 == 0) {
+ result.append(i / 10);
+ } else {
+ result.append(c);
+ }
+ }
+ return result.toString();
+ }
+
+ /**
* Tests {@link UsbRequest} and {@link UsbDeviceConnection#requestWait()}.
*
* @param connection The connection to use for testing
@@ -707,12 +1045,10 @@
nextTest(connection, in, out, "Echo max bytes");
echoUsbRequest(connection, in, out, MAX_BUFFER_SIZE, false);
- nextTest(connection, in, out, "Echo oversized buffer");
- echoOversizedUsbRequest(connection, in, out);
-
// Send empty requests
sendZeroLengthRequest(connection, out, true);
sendZeroLengthRequest(connection, out, false);
+ sendNullRequest(connection, out);
/* TODO: Unreliable
@@ -725,33 +1061,38 @@
*/
- // UsbRequest.queue ignores position, limit, arrayOffset, and capacity
- nextTest(connection, in, out, "Echo 42 bytes");
- echoUsbRequest(connection, in, out, 42, 42, 0, 42, 5, 42, false);
+ for (int startOfSlice : new int[]{0, 1}) {
+ for (int endOffsetOfSlice : new int[]{0, 2}) {
+ for (int positionInSlice : new int[]{0, 5}) {
+ for (int limitOffsetInSlice : new int[]{0, 11}) {
+ for (boolean useDirectBuffer : new boolean[]{true, false}) {
+ for (boolean makeSendBufferReadOnly : new boolean[]{true, false}) {
+ int sliceSize = 42 + positionInSlice + limitOffsetInSlice;
+ int originalSize = sliceSize + startOfSlice + endOffsetOfSlice;
- nextTest(connection, in, out, "Echo 42 bytes");
- echoUsbRequest(connection, in, out, 42, 42, 0, 42, 0, 36, false);
+ nextTest(connection, in, out, "Echo 42 bytes");
- nextTest(connection, in, out, "Echo 42 bytes");
- echoUsbRequest(connection, in, out, 42, 42, 5, 42, 0, 36, false);
+ // Log buffer, slice, and data offsets
+ Log.i(LOG_TAG,
+ "buffer" + (makeSendBufferReadOnly ? "(ro): [" : ": [")
+ + repeat('.', originalSize) + "]");
+ Log.i(LOG_TAG,
+ "slice: " + repeat(' ', startOfSlice) + " [" + repeat(
+ '.', sliceSize) + "]");
+ Log.i(LOG_TAG,
+ "data: " + repeat(' ', startOfSlice + positionInSlice)
+ + " [" + repeat('.', 42) + "]");
- nextTest(connection, in, out, "Echo 42 bytes");
- echoUsbRequest(connection, in, out, 42, 42, 0, 36, 0, 31, false);
-
- nextTest(connection, in, out, "Echo 42 bytes");
- echoUsbRequest(connection, in, out, 42, 47, 0, 47, 0, 47, false);
-
- nextTest(connection, in, out, "Echo 42 bytes");
- echoUsbRequest(connection, in, out, 42, 47, 5, 47, 0, 42, false);
-
- nextTest(connection, in, out, "Echo 42 bytes");
- echoUsbRequest(connection, in, out, 42, 47, 0, 42, 0, 42, false);
-
- nextTest(connection, in, out, "Echo 42 bytes");
- echoUsbRequest(connection, in, out, 42, 47, 0, 47, 5, 47, false);
-
- nextTest(connection, in, out, "Echo 42 bytes");
- echoUsbRequest(connection, in, out, 42, 47, 5, 47, 5, 36, false);
+ echoUsbRequest(connection, in, out, originalSize, startOfSlice,
+ originalSize - endOffsetOfSlice, positionInSlice,
+ sliceSize - limitOffsetInSlice, useDirectBuffer,
+ makeSendBufferReadOnly);
+ }
+ }
+ }
+ }
+ }
+ }
// Illegal arguments
final UsbRequest req1 = new UsbRequest();
@@ -759,15 +1100,16 @@
assertException(() -> req1.initialize(connection, null), NullPointerException.class);
boolean isInited = req1.initialize(connection, in);
assertTrue(isInited);
- assertException(() -> req1.queue(null, 0), NullPointerException.class);
- assertException(() -> req1.queue(ByteBuffer.allocate(1).asReadOnlyBuffer(), 1),
+ assertException(() -> req1.enqueue(ByteBuffer.allocate(16384 + 1).asReadOnlyBuffer()),
+ IllegalArgumentException.class);
+ assertException(() -> req1.enqueue(ByteBuffer.allocate(1).asReadOnlyBuffer()),
IllegalArgumentException.class);
req1.close();
// Cannot queue closed request
- assertException(() -> req1.queue(ByteBuffer.allocate(1), 1), NullPointerException.class);
- assertException(() -> req1.queue(ByteBuffer.allocateDirect(1), 1),
- NullPointerException.class);
+ assertException(() -> req1.enqueue(ByteBuffer.allocate(1)), IllegalStateException.class);
+ assertException(() -> req1.enqueue(ByteBuffer.allocateDirect(1)),
+ IllegalStateException.class);
// Initialize
UsbRequest req2 = new UsbRequest();
@@ -907,6 +1249,7 @@
*/
private class QueuerThread extends TestThread {
private static final int MAX_IN_FLIGHT = 64;
+ private static final long RUN_TIME = 10 * 1000;
private final AtomicInteger mCounter;
@@ -940,7 +1283,9 @@
public void run() {
Random random = new Random();
- while (!mShouldStop) {
+ long endTime = now() + RUN_TIME;
+
+ while (now() < endTime && !mShouldStop) {
try {
int counter = mCounter.getAndIncrement();
@@ -953,6 +1298,7 @@
ByteBuffer writeBuffer = mBufferRecycler.get();
int data = random.nextInt();
writeBuffer.put((byte)1).putInt(counter).putInt(data);
+ writeBuffer.flip();
// Send read that will receive the data back from the write as the other side
// will echo all requests.
@@ -988,7 +1334,7 @@
// Send both requests to the system. Once they finish the ReceiverThread will
// be notified
- boolean isQueued = writeRequest.queue(writeBuffer, 9);
+ boolean isQueued = writeRequest.enqueue(writeBuffer);
assertTrue(isQueued);
isQueued = readRequest.queue(readBuffer, 9);
@@ -998,6 +1344,7 @@
mErrors.add(t);
mErrors.notify();
}
+ break;
}
}
}
@@ -1104,6 +1451,7 @@
mErrors.add(t);
mErrors.notify();
}
+ break;
}
}
}
@@ -1165,7 +1513,7 @@
@Override
protected @NonNull ByteBuffer create() {
- return ByteBuffer.allocate(9);
+ return ByteBuffer.allocateDirect(9);
}
};
@@ -1194,30 +1542,7 @@
queuer2.start();
receiver.start();
- // Run for 10 seconds or until we have an error
- final long RUN_TIME = 10 * 1000;
-
- long startTime = System.currentTimeMillis();
- while (true) {
- long timeLeft = RUN_TIME - (System.currentTimeMillis() - startTime);
-
- synchronized (errors) {
- if (!errors.isEmpty() || timeLeft < 0) {
- break;
- } else {
- try {
- errors.wait(timeLeft);
- } catch (InterruptedException e) {
- errors.add(e);
- break;
- }
- }
- }
- }
-
- Log.i(LOG_TAG, "Stopping sending new requests");
- queuer1.abort();
- queuer2.abort();
+ Log.i(LOG_TAG, "Waiting for queuers to stop");
try {
queuer1.join();
@@ -1632,6 +1957,7 @@
boolean claimed = connection.claimInterface(iface, false);
assertTrue(claimed);
+ usbRequestLegacyTests(connection, iface);
usbRequestTests(connection, iface);
parallelUsbRequestsTests(connection, iface);
ctrlTransferTests(connection);
diff --git a/apps/CtsVerifierUSBCompanion/src/com/android/cts/verifierusbcompanion/DeviceTestCompanion.java b/apps/CtsVerifierUSBCompanion/src/com/android/cts/verifierusbcompanion/DeviceTestCompanion.java
index 9899167..15ea2f4 100644
--- a/apps/CtsVerifierUSBCompanion/src/com/android/cts/verifierusbcompanion/DeviceTestCompanion.java
+++ b/apps/CtsVerifierUSBCompanion/src/com/android/cts/verifierusbcompanion/DeviceTestCompanion.java
@@ -16,6 +16,9 @@
package com.android.cts.verifierusbcompanion;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
import android.content.Context;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
@@ -26,12 +29,7 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
-import java.nio.IntBuffer;
import java.nio.charset.Charset;
-import java.util.Arrays;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
/**
* Companion code for com.android.cts.verifier.usb.device.UsbDeviceTestActivity
@@ -199,12 +197,15 @@
isSuccess = true;
}
break;
- case "Receive byte after some time": {
+ case "Receive byte after some time":
Thread.sleep(200);
os.write(new byte[1]);
isSuccess = true;
- }
- break;
+ break;
+ case "Receive byte immediately":
+ os.write(new byte[1]);
+ isSuccess = true;
+ break;
case "Echo until stop signal":
isSuccess = echoUntilStopSignal(is, os);
break;
diff --git a/apps/cts-usb-accessory/cts-usb-accessory.c b/apps/cts-usb-accessory/cts-usb-accessory.c
index c5da3ef..0fbfce7 100644
--- a/apps/cts-usb-accessory/cts-usb-accessory.c
+++ b/apps/cts-usb-accessory/cts-usb-accessory.c
@@ -29,6 +29,8 @@
#include <usbhost/usbhost.h>
#include <linux/usb/f_accessory.h>
+#define USB_CONTROL_READ_TIMEOUT_MS 200
+
static struct usb_device *sDevice = NULL;
static int sAfterUnplug = 0;
static char* sDeviceSerial = NULL;
@@ -91,7 +93,7 @@
return 0;
}
- char* serial = usb_device_get_serial(device);
+ char* serial = usb_device_get_serial(device, USB_CONTROL_READ_TIMEOUT_MS);
if (sDeviceSerial && (!serial || strcmp(sDeviceSerial, serial))) {
free(serial);
return 0;
@@ -111,7 +113,7 @@
printf("Found Android device in accessory mode (%x:%x)...\n",
vendorId, productId);
sDevice = device;
- sDeviceSerial = usb_device_get_serial(sDevice);
+ sDeviceSerial = usb_device_get_serial(sDevice, USB_CONTROL_READ_TIMEOUT_MS);
usb_descriptor_iter_init(device, &iter);
while ((desc = usb_descriptor_iter_next(&iter)) != NULL && (!intf || !ep1 || !ep2)) {
diff --git a/common/host-side/tradefed/res/report/compatibility_result.xsl b/common/host-side/tradefed/res/report/compatibility_result.xsl
index 007f494..156f3c6 100644
--- a/common/host-side/tradefed/res/report/compatibility_result.xsl
+++ b/common/host-side/tradefed/res/report/compatibility_result.xsl
@@ -88,6 +88,18 @@
</td>
</tr>
<tr>
+ <td class="rowtitle">Modules Done</td>
+ <td>
+ <xsl:value-of select="Result/Summary/@modules_done"/>
+ </td>
+ </tr>
+ <tr>
+ <td class="rowtitle">Modules Total</td>
+ <td>
+ <xsl:value-of select="Result/Summary/@modules_total"/>
+ </td>
+ </tr>
+ <tr>
<td class="rowtitle">Fingerprint</td>
<td>
<xsl:value-of select="Result/Build/@build_fingerprint"/>
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
index 83d4141..0887d07 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
@@ -208,6 +208,13 @@
}
/**
+ * @return a {@link File} in the resultDir for logging invocation failures
+ */
+ public File getInvocationFailureFile() throws FileNotFoundException {
+ return new File(getResultDir(), "invocation_failure.txt");
+ }
+
+ /**
* @return a {@link String} to use for directory suffixes created from the given time.
*/
public static String getDirSuffix(long millis) {
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/InvocationFailureHandler.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/InvocationFailureHandler.java
new file mode 100644
index 0000000..e588fbb
--- /dev/null
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/InvocationFailureHandler.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 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.compatibility.common.tradefed.result;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.FileUtil;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+
+/**
+ * A helper class for setting and checking whether an invocation has failed.
+ */
+public class InvocationFailureHandler {
+
+ /**
+ * Determine whether the invocation for this session has previously failed.
+ *
+ * @param buildHelper the {@link CompatibilityBuildHelper} from which to retrieve invocation
+ * failure file
+ * @return if invocation has previously failed
+ */
+ public static boolean hasFailed(final CompatibilityBuildHelper buildHelper) {
+ try {
+ File f = buildHelper.getInvocationFailureFile();
+ return (f.exists() && f.length() != 0);
+ } catch (FileNotFoundException e) {
+ CLog.e("Could not find invocation failure file for session %s",
+ buildHelper.getDirSuffix(buildHelper.getStartTime()));
+ CLog.e(e);
+ return false;
+ }
+ }
+
+ /**
+ * Write the cause of invocation failure to the result's invocation failure file.
+ *
+ * @param buildHelper the {@link CompatibilityBuildHelper} from which to retrieve the
+ * invocation failure file
+ * @param cause the throwable responsible for invocation failure
+ */
+ public static void setFailed(final CompatibilityBuildHelper buildHelper, Throwable cause) {
+ try {
+ File f = buildHelper.getInvocationFailureFile();
+ if (!f.exists()) {
+ f.createNewFile();
+ FileUtil.writeToFile(cause.toString(), f);
+ }
+ } catch (IOException e) {
+ CLog.e("Exception while writing invocation failure file.");
+ CLog.e(e);
+ }
+ }
+}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
index bbdc8d8..7310f56 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
@@ -16,6 +16,7 @@
package com.android.compatibility.common.tradefed.result;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.tradefed.result.InvocationFailureHandler;
import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
import com.android.compatibility.common.util.ICaseResult;
import com.android.compatibility.common.util.IInvocationResult;
@@ -484,6 +485,7 @@
@Override
public void invocationFailed(Throwable cause) {
warn("Invocation failed: %s", cause);
+ InvocationFailureHandler.setFailed(mBuildHelper, cause);
}
/**
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
index 6dabac6..e68d913 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
@@ -18,6 +18,7 @@
import com.android.compatibility.SuiteInfo;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.tradefed.result.InvocationFailureHandler;
import com.android.compatibility.common.tradefed.result.SubPlanCreator;
import com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker;
import com.android.compatibility.common.tradefed.targetprep.SystemStatusChecker;
@@ -71,6 +72,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
/**
* A Test for running Compatibility Suites
@@ -95,6 +97,9 @@
public static final String DEVICE_TOKEN_OPTION = "device-token";
public static final String LOGCAT_ON_FAILURE_SIZE_OPTION = "logcat-on-failure-size";
+ // Constants for checking invocation or preconditions preparation failure
+ private static final int NUM_PREP_ATTEMPTS = 10;
+ private static final int MINUTES_PER_PREP_ATTEMPT = 2;
@Option(name = SUBPLAN_OPTION,
description = "the subplan to run",
@@ -310,7 +315,7 @@
// throw a {@link FileNotFoundException}
mModuleRepo.initialize(mTotalShards, mBuildHelper.getTestsDir(), getAbis(),
mDeviceTokens, mTestArgs, mModuleArgs, mIncludeFilters,
- mExcludeFilters, mBuildHelper.getBuildInfo());
+ mExcludeFilters);
// Add the entire list of modules to the CompatibilityBuildHelper for reporting
mBuildHelper.setModuleIds(mModuleRepo.getModuleIds());
@@ -358,11 +363,20 @@
}
mModuleRepo.setPrepared(isPrepared);
- if (!mModuleRepo.isPrepared()) {
- CLog.logAndDisplay(LogLevel.ERROR,
- "Incorrect preparation detected, exiting test run from %s",
- mDevice.getSerialNumber());
- return;
+ int prepAttempt = 1;
+ while (!mModuleRepo.isPrepared(MINUTES_PER_PREP_ATTEMPT, TimeUnit.MINUTES)) {
+ if (prepAttempt >= NUM_PREP_ATTEMPTS
+ || InvocationFailureHandler.hasFailed(mBuildHelper)) {
+ CLog.logAndDisplay(LogLevel.ERROR,
+ "Incorrect preparation detected, exiting test run from %s",
+ mDevice.getSerialNumber());
+ return;
+ } else {
+ CLog.logAndDisplay(LogLevel.INFO,
+ "Device %s on standby while all shards complete preparation",
+ mDevice.getSerialNumber());
+ }
+ prepAttempt++;
}
// Run the tests
@@ -386,6 +400,9 @@
runPreModuleCheck(module.getName(), checkers, mDevice, listener);
}
try {
+ if (module.getTest() instanceof IBuildReceiver) {
+ ((IBuildReceiver)module.getTest()).setBuild(mBuildHelper.getBuildInfo());
+ }
module.run(listener);
} catch (DeviceUnresponsiveException due) {
// being able to catch a DeviceUnresponsiveException here implies that recovery
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleRepo.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleRepo.java
index 540373b..cbefc7c 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleRepo.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleRepo.java
@@ -15,13 +15,13 @@
*/
package com.android.compatibility.common.tradefed.testtype;
-import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.testtype.IAbi;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
/**
* Interface for accessing tests from the Compatibility repository.
@@ -31,7 +31,7 @@
/**
* @return true after each shard has prepared successfully.
*/
- boolean isPrepared();
+ boolean isPrepared(long timeout, TimeUnit unit);
/**
* Indicates to the repo whether a shard is prepared to run.
@@ -48,7 +48,7 @@
*/
void initialize(int shards, File testsDir, Set<IAbi> abis, List<String> deviceTokens,
List<String> testArgs, List<String> moduleArgs, Set<String> mIncludeFilters,
- Set<String> mExcludeFilters, IBuildInfo buildInfo);
+ Set<String> mExcludeFilters);
/**
* @return a {@link Map} of all modules to run on the device referenced by the given serial.
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
index 412f9747..57d5db3 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
@@ -17,14 +17,12 @@
import com.android.compatibility.common.util.TestFilter;
import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.config.ConfigurationException;
import com.android.tradefed.config.ConfigurationFactory;
import com.android.tradefed.config.IConfiguration;
import com.android.tradefed.config.IConfigurationFactory;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.IAbi;
-import com.android.tradefed.testtype.IBuildReceiver;
import com.android.tradefed.testtype.IRemoteTest;
import com.android.tradefed.testtype.IShardableTest;
import com.android.tradefed.testtype.ITestFileFilterReceiver;
@@ -202,13 +200,13 @@
* {@inheritDoc}
*/
@Override
- public boolean isPrepared() {
+ public boolean isPrepared(long timeout, TimeUnit unit) {
+ // returns true only if CountDownLatch reaches zero && no shards have setPrepared to false
try {
- mPreparedLatch.await();
+ return (mPreparedLatch.await(timeout, unit)) ? mPrepared : false;
} catch (InterruptedException e) {
return false;
}
- return mPrepared;
}
/**
@@ -234,7 +232,7 @@
@Override
public void initialize(int shards, File testsDir, Set<IAbi> abis, List<String> deviceTokens,
List<String> testArgs, List<String> moduleArgs, Set<String> includeFilters,
- Set<String> excludeFilters, IBuildInfo buildInfo) {
+ Set<String> excludeFilters) {
CLog.d("Initializing ModuleRepo\nShards:%d\nTests Dir:%s\nABIs:%s\nDevice Tokens:%s\n" +
"Test Args:%s\nModule Args:%s\nIncludes:%s\nExcludes:%s",
shards, testsDir.getAbsolutePath(), abis, deviceTokens, testArgs, moduleArgs,
@@ -316,12 +314,9 @@
}
List<IRemoteTest> shardedTests = tests;
if (mShards > 1) {
- shardedTests = splitShardableTests(tests, buildInfo);
+ shardedTests = splitShardableTests(tests);
}
for (IRemoteTest test : shardedTests) {
- if (test instanceof IBuildReceiver) {
- ((IBuildReceiver)test).setBuild(buildInfo);
- }
addModuleDef(name, abi, test, pathArg);
}
}
@@ -339,14 +334,10 @@
mLargeModulesPerShard = mLargeModules.size() / shards;
}
- private static List<IRemoteTest> splitShardableTests(List<IRemoteTest> tests,
- IBuildInfo buildInfo) {
+ private static List<IRemoteTest> splitShardableTests(List<IRemoteTest> tests) {
ArrayList<IRemoteTest> shardedList = new ArrayList<>(tests.size());
for (IRemoteTest test : tests) {
if (test instanceof IShardableTest) {
- if (test instanceof IBuildReceiver) {
- ((IBuildReceiver)test).setBuild(buildInfo);
- }
shardedList.addAll(((IShardableTest)test).split());
} else {
shardedList.add(test);
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanCreatorTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanCreatorTest.java
index 23b2b05..600f6d1 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanCreatorTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanCreatorTest.java
@@ -21,12 +21,11 @@
import com.android.compatibility.common.util.ICaseResult;
import com.android.compatibility.common.util.IInvocationResult;
import com.android.compatibility.common.util.IModuleResult;
-import com.android.compatibility.common.util.InvocationResult;
import com.android.compatibility.common.util.ITestResult;
+import com.android.compatibility.common.util.InvocationResult;
import com.android.compatibility.common.util.ResultHandler;
import com.android.compatibility.common.util.TestFilter;
import com.android.compatibility.common.util.TestStatus;
-import com.android.ddmlib.testrunner.TestIdentifier;
import com.android.tradefed.build.BuildInfo;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.config.ArgsOptionParser;
@@ -47,7 +46,6 @@
private static final String SUITE_VERSION = "5.0";
private static final String SUITE_PLAN = "cts";
private static final String SUITE_BUILD = "12345";
- private static final String REPORT_VERSION = "5.0";
private static final String NAME_A = "ModuleA";
private static final String NAME_B = "ModuleB";
private static final String ABI = "mips64";
@@ -59,7 +57,6 @@
private static final String EXAMPLE_BUILD_PRODUCT = "wolverine";
private static final String DEVICE_A = "device123";
private static final String DEVICE_B = "device456";
- private static final String DEVICES = "device456,device123";
private static final String CLASS_A = "android.test.Foor";
private static final String CLASS_B = "android.test.Bar";
private static final String METHOD_1 = "testBlah1";
@@ -68,8 +65,6 @@
private static final String METHOD_4 = "testBlah4";
private static final long START_MS = 1431586801000L;
private static final long END_MS = 1431673199000L;
- private static final String START_DISPLAY = "Fri Aug 20 15:13:03 PDT 2010";
- private static final String END_DISPLAY = "Fri Aug 20 15:13:04 PDT 2010";
private static final String REFERENCE_URL="http://android.com";
private static final String LOG_URL ="file:///path/to/logs";
private static final String COMMAND_LINE_ARGS = "cts -m CtsMyModuleTestCases";
@@ -169,6 +164,7 @@
return mResultsDir;
}
+ @Override
public File getSubPlansDir() throws FileNotFoundException {
return mSubPlansDir;
}
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
index 8851117..46958b8 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
@@ -16,10 +16,7 @@
package com.android.compatibility.common.tradefed.testtype;
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildProvider;
import com.android.compatibility.common.tradefed.testtype.ModuleRepo.ConfigFilter;
-import com.android.compatibility.common.tradefed.testtype.IModuleDef;
-import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.testtype.Abi;
import com.android.tradefed.testtype.IAbi;
import com.android.tradefed.testtype.IRemoteTest;
@@ -37,6 +34,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
public class ModuleRepoTest extends TestCase {
@@ -87,11 +85,6 @@
"armeabi-v7a FooModuleC",
"armeabi-v7a FooModuleB"
};
- private static final String ROOT_DIR_NAME = "root";
- private static final String BASE_DIR_NAME = "android-tests";
- private static final String TESTCASES = "testcases";
- private static final String ROOT_PROPERTY = "TESTS_ROOT";
- private static final String SUITE_NAME = "TESTS";
static {
SERIALS.add(SERIAL1);
@@ -110,26 +103,12 @@
}
private IModuleRepo mRepo;
private File mTestsDir;
- private IBuildInfo mBuild;
private File mRoot = null;
@Override
public void setUp() throws Exception {
mTestsDir = setUpConfigs();
mRepo = new ModuleRepo();
- mRoot = FileUtil.createTempDir(ROOT_DIR_NAME);
- File base = new File(mRoot, BASE_DIR_NAME);
- base.mkdirs();
- File tests = new File(base, TESTCASES);
- tests.mkdirs();
- System.setProperty(ROOT_PROPERTY, mRoot.getAbsolutePath());
- CompatibilityBuildProvider provider = new CompatibilityBuildProvider() {
- @Override
- protected String getSuiteInfoName() {
- return SUITE_NAME;
- }
- };
- mBuild = provider.getBuild();
}
private File setUpConfigs() throws IOException {
@@ -144,7 +123,8 @@
createConfig(testsDir, name, token, TEST_STUB);
}
- private void createConfig(File testsDir, String name, String token, String moduleClass) throws IOException {
+ private void createConfig(File testsDir, String name, String token, String moduleClass)
+ throws IOException {
File config = new File(testsDir, String.format(FILENAME, name));
String preparer = "";
if (token != null) {
@@ -165,7 +145,7 @@
public void testInitialization() throws Exception {
mRepo.initialize(3, mTestsDir, ABIS, DEVICE_TOKENS, TEST_ARGS, MODULE_ARGS, INCLUDES,
- EXCLUDES, mBuild);
+ EXCLUDES);
assertTrue("Should be initialized", mRepo.isInitialized());
assertEquals("Wrong number of shards", 3, mRepo.getNumberOfShards());
assertEquals("Wrong number of modules per shard", 2, mRepo.getModulesPerShard());
@@ -210,7 +190,7 @@
excludeFilters.add(ID_A_32);
excludeFilters.add(MODULE_NAME_B);
mRepo.initialize(1, mTestsDir, ABIS, DEVICE_TOKENS, TEST_ARGS, MODULE_ARGS, includeFilters,
- excludeFilters, mBuild);
+ excludeFilters);
List<IModuleDef> modules = mRepo.getModules(SERIAL1);
assertEquals("Incorrect number of modules", 1, modules.size());
IModuleDef module = modules.get(0);
@@ -220,7 +200,7 @@
public void testParsing() throws Exception {
mRepo.initialize(1, mTestsDir, ABIS, DEVICE_TOKENS, TEST_ARGS, MODULE_ARGS, INCLUDES,
- EXCLUDES, mBuild);
+ EXCLUDES);
List<IModuleDef> modules = mRepo.getModules(SERIAL3);
Set<String> idSet = new HashSet<>();
for (IModuleDef module : modules) {
@@ -255,7 +235,7 @@
ArrayList<String> emptyList = new ArrayList<>();
mRepo.initialize(3, mTestsDir, abis, DEVICE_TOKENS, emptyList, emptyList, INCLUDES,
- EXCLUDES, mBuild);
+ EXCLUDES);
List<IModuleDef> modules = new ArrayList<>();
modules.addAll(mRepo.getLargeModules());
@@ -267,7 +247,6 @@
for (IModuleDef def : modules) {
IRemoteTest test = def.getTest();
if (test instanceof IShardableTest) {
- assertNotNull("Build not set", ((ShardableTestStub)test).mBuildInfo);
shardableCount++;
}
}
@@ -276,7 +255,7 @@
public void testGetModuleIds() {
mRepo.initialize(3, mTestsDir, ABIS, DEVICE_TOKENS, TEST_ARGS, MODULE_ARGS, INCLUDES,
- EXCLUDES, mBuild);
+ EXCLUDES);
assertTrue("Should be initialized", mRepo.isInitialized());
assertArrayEquals(EXPECTED_MODULE_IDS, mRepo.getModuleIds());
@@ -284,22 +263,22 @@
public void testIsPrepared() {
mRepo.initialize(3, mTestsDir, ABIS, DEVICE_TOKENS, TEST_ARGS, MODULE_ARGS, INCLUDES,
- EXCLUDES, mBuild);
+ EXCLUDES);
assertTrue("Should be initialized", mRepo.isInitialized());
mRepo.setPrepared(true);
mRepo.setPrepared(true);
mRepo.setPrepared(true); // each shard should call setPrepared() once
- assertTrue(mRepo.isPrepared());
+ assertTrue(mRepo.isPrepared(0, TimeUnit.MINUTES));
}
public void testIsNotPrepared() {
mRepo.initialize(3, mTestsDir, ABIS, DEVICE_TOKENS, TEST_ARGS, MODULE_ARGS, INCLUDES,
- EXCLUDES, mBuild);
+ EXCLUDES);
assertTrue("Should be initialized", mRepo.isInitialized());
mRepo.setPrepared(true);
mRepo.setPrepared(false); // mRepo should return false for setPrepared() after third call
mRepo.setPrepared(true);
- assertFalse(mRepo.isPrepared());
+ assertFalse(mRepo.isPrepared(0, TimeUnit.MINUTES));
}
private void assertArrayEquals(Object[] expected, Object[] actual) {
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ShardableTestStub.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ShardableTestStub.java
index 1b3f955..f4db091 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ShardableTestStub.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ShardableTestStub.java
@@ -28,8 +28,6 @@
import com.android.tradefed.testtype.ITestCollector;
import com.android.tradefed.testtype.ITestFilterReceiver;
-import junit.framework.Assert;
-
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
@@ -69,8 +67,6 @@
*/
@Override
public Collection<IRemoteTest> split() {
- Assert.assertNotNull(mBuildInfo);
-
mShards = new ArrayList<>();
for (int i = 0; i < 3; i++) {
mShards.add(new ShardableTestStub());
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/SubPlanTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/SubPlanTest.java
index fa24b32..75b5df6 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/SubPlanTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/SubPlanTest.java
@@ -66,13 +66,15 @@
// Serialize to file
File subPlanFile = FileUtil.createTempFile("test-subPlan-serialization", ".txt");
- OutputStream subPlanOutputStream = new FileOutputStream(subPlanFile);
- subPlan.serialize(subPlanOutputStream);
- subPlanOutputStream.close();
-
- // Parse subPlan and assert correctness
- checkSubPlan(subPlanFile);
-
+ try {
+ OutputStream subPlanOutputStream = new FileOutputStream(subPlanFile);
+ subPlan.serialize(subPlanOutputStream);
+ subPlanOutputStream.close();
+ // Parse subPlan and assert correctness
+ checkSubPlan(subPlanFile);
+ } finally {
+ FileUtil.deleteFile(subPlanFile);
+ }
}
public void testParsing() throws Exception {
@@ -97,6 +99,7 @@
checkSubPlan(planFile);
} finally {
writer.close();
+ FileUtil.deleteFile(planFile);
}
}
diff --git a/common/host-side/util/tests/Android.mk b/common/host-side/util/tests/Android.mk
index 4a78835..9bd3a4b 100644
--- a/common/host-side/util/tests/Android.mk
+++ b/common/host-side/util/tests/Android.mk
@@ -24,4 +24,4 @@
LOCAL_MODULE_TAGS := optional
-include $(BUILD_HOST_JAVA_LIBRARY)
\ No newline at end of file
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/common/host-side/util/tests/src/com/android/compatibility/common/util/DynamicConfigHandlerTest.java b/common/host-side/util/tests/src/com/android/compatibility/common/util/DynamicConfigHandlerTest.java
index 36c4970..a1d3d0a2 100644
--- a/common/host-side/util/tests/src/com/android/compatibility/common/util/DynamicConfigHandlerTest.java
+++ b/common/host-side/util/tests/src/com/android/compatibility/common/util/DynamicConfigHandlerTest.java
@@ -16,6 +16,9 @@
package com.android.compatibility.common.util;
+import com.android.tradefed.util.FileUtil;
+import com.android.tradefed.util.StreamUtil;
+
import junit.framework.TestCase;
import java.io.File;
@@ -92,29 +95,31 @@
public void testDynamicConfigHandler() throws Exception {
String module = "test1";
File localConfigFile = createFileFromStr(localConfig, module);
+ try {
+ File mergedFile = DynamicConfigHandler
+ .getMergedDynamicConfigFile(localConfigFile, overrideJson, module);
- File mergedFile = DynamicConfigHandler
- .getMergedDynamicConfigFile(localConfigFile, overrideJson, module);
+ Map<String, List<String>> configMap = DynamicConfig.createConfigMap(mergedFile);
- Map<String, List<String>> configMap = DynamicConfig.createConfigMap(mergedFile);
+ assertEquals("override-config-val-1", configMap.get("override-config-1").get(0));
+ assertTrue(configMap.get("override-config-list-1")
+ .contains("override-config-list-val-1-1"));
+ assertTrue(configMap.get("override-config-list-1")
+ .contains("override-config-list-val-1-2"));
+ assertTrue(configMap.get("override-config-list-3").size() == 0);
- assertEquals("override-config-val-1", configMap.get("override-config-1").get(0));
- assertTrue(configMap.get("override-config-list-1")
- .contains("override-config-list-val-1-1"));
- assertTrue(configMap.get("override-config-list-1")
- .contains("override-config-list-val-1-2"));
- assertTrue(configMap.get("override-config-list-3").size() == 0);
+ assertEquals("test config 1", configMap.get("test-config-1").get(0));
+ assertTrue(configMap.get("config-list").contains("config2"));
- assertEquals("test config 1", configMap.get("test-config-1").get(0));
- assertTrue(configMap.get("config-list").contains("config2"));
-
- assertEquals("override-config-val-2", configMap.get("override-config-2").get(0));
- assertEquals(1, configMap.get("override-config-list-2").size());
- assertTrue(configMap.get("override-config-list-2")
- .contains("override-config-list-val-2-1"));
+ assertEquals("override-config-val-2", configMap.get("override-config-2").get(0));
+ assertEquals(1, configMap.get("override-config-list-2").size());
+ assertTrue(configMap.get("override-config-list-2")
+ .contains("override-config-list-val-2-1"));
+ } finally {
+ FileUtil.deleteFile(localConfigFile);
+ }
}
-
private File createFileFromStr(String configStr, String module) throws IOException {
File file = File.createTempFile(module, "dynamic");
FileOutputStream stream = null;
@@ -123,9 +128,7 @@
stream.write(configStr.getBytes());
stream.flush();
} finally {
- if (stream != null) {
- stream.close();
- }
+ StreamUtil.close(stream);
}
return file;
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
index de9fdc2..68b19af 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
@@ -132,20 +132,4 @@
// DISALLOW_UNMUTE_MICROPHONE and DISALLOW_ADJUST_VOLUME can only be set by device owners
// and profile owners on the primary user.
}
-
- @Override
- public void testDelegatedCertInstaller() throws Exception {
- if (!mHasFeature) {
- return;
- }
-
- try {
- super.testDelegatedCertInstaller();
- } finally {
- // In managed profile, clearing password through dpm is not allowed. Recreate user to
- // clear password instead.
- removeUser(mUserId);
- createManagedProfile();
- }
- }
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
index e08cacf..c34c97f3 100755
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
@@ -199,6 +199,12 @@
android:exported="true"
android:theme="@style/TranslucentTheme"
/>
+ <activity android:name=".AnimationTestActivity"
+ android:exported="true"
+ />
+ <activity android:name=".VirtualDisplayActivity"
+ android:exported="true"
+ />
</application>
</manifest>
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/anim/animation_with_background.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/anim/animation_with_background.xml
new file mode 100644
index 0000000..877ecc4
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/anim/animation_with_background.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2016 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
+ -->
+<translate
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromYDelta="100%" android:toYDelta="0"
+ android:interpolator="@android:interpolator/linear"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="500"
+ android:background="#ff0000">
+</translate>
\ No newline at end of file
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/virtual_display_layout.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/virtual_display_layout.xml
new file mode 100644
index 0000000..deac584
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/virtual_display_layout.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2016 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
+ -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <SurfaceView
+ android:id="@+id/surfaceView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+</RelativeLayout>
\ No newline at end of file
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AnimationTestActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AnimationTestActivity.java
new file mode 100644
index 0000000..5ae923e
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AnimationTestActivity.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 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 android.server.cts;
+
+import android.app.Activity;
+
+public class AnimationTestActivity extends Activity {
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ overridePendingTransition(R.anim.animation_with_background, -1);
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VirtualDisplayActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VirtualDisplayActivity.java
new file mode 100644
index 0000000..05f7b91
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VirtualDisplayActivity.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2016 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 android.server.cts;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Display;
+import android.view.Surface;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.WindowManager;
+
+/**
+ * Activity that is able to create and destroy a virtual display.
+ */
+public class VirtualDisplayActivity extends Activity {
+ private static final String TAG = "VirtualDisplayActivity";
+
+ private static final int DEFAULT_DENSITY_DPI = 160;
+ private static final String KEY_DENSITY_DPI = "densityDpi";
+
+ private DisplayManager mDisplayManager;
+ private VirtualDisplay mVirtualDisplay;
+
+ private Surface mSurface;
+ private SurfaceView mSurfaceView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.virtual_display_layout);
+
+ mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView);
+ mSurface = mSurfaceView.getHolder().getSurface();
+
+ mDisplayManager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE);
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ final Bundle extras = intent.getExtras();
+ if (extras == null) {
+ return;
+ }
+
+ String command = extras.getString("command");
+ switch (command) {
+ case "create_display":
+ createVirtualDisplay(extras);
+ break;
+ case "destroy_display":
+ destroyVirtualDisplay();
+ break;
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ destroyVirtualDisplay();
+ }
+
+ private void createVirtualDisplay(Bundle extras) {
+ if (mVirtualDisplay == null) {
+ final int width = mSurfaceView.getWidth();
+ final int height = mSurfaceView.getHeight();
+ final int densityDpi = extras.getInt(KEY_DENSITY_DPI, DEFAULT_DENSITY_DPI);
+ Log.d(TAG, "createVirtualDisplay: " + width + "x" + height + ", dpi: "
+ + densityDpi);
+ final int flags = 0;
+ mVirtualDisplay = mDisplayManager.createVirtualDisplay("VirtualDisplay", width, height,
+ densityDpi, mSurface, flags);
+ }
+ }
+
+ private void destroyVirtualDisplay() {
+ if (mVirtualDisplay != null) {
+ Log.d(TAG, "destroyVirtualDisplay");
+ mVirtualDisplay.release();
+ mVirtualDisplay = null;
+ }
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java
index 3179ac5..f5481b8 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java
@@ -82,7 +82,13 @@
*/
public void testConfigurationUpdatesWhenRotatingWhileDocked() throws Exception {
setDeviceRotation(0);
- launchActivityInStack(RESIZEABLE_ACTIVITY_NAME, DOCKED_STACK_ID);
+ launchActivityInDockStack(LAUNCHING_ACTIVITY);
+ // Launch our own activity to side in case Recents (or other activity to side) doesn't
+ // support rotation.
+ launchActivityToSide(false /* randomData */, false /* multipleTask */, TEST_ACTIVITY_NAME);
+ // Launch target activity in docked stack.
+ launchActivity(false /* toSide */, false /* randomData */, false /* multipleTask */,
+ RESIZEABLE_ACTIVITY_NAME);
final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
DOCKED_STACK_ID);
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
new file mode 100644
index 0000000..8b77ff4
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2016 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 android.server.cts;
+
+import com.android.tradefed.device.CollectingOutputReceiver;
+import com.android.tradefed.device.DeviceNotAvailableException;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static android.server.cts.StateLogger.log;
+
+/**
+ * Build: mmma -j32 cts/hostsidetests/services
+ * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test android.server.cts.ActivityManagerDisplayTests
+ */
+public class ActivityManagerDisplayTests extends ActivityManagerTestBase {
+ private static final String DUMPSYS_ACTIVITY_PROCESSES = "dumpsys activity processes";
+
+ private static final String VIRTUAL_DISPLAY_ACTIVITY = "VirtualDisplayActivity";
+
+ private static final int CUSTOM_DENSITY_DPI = 222;
+
+ /** Temp storage used for parsing. */
+ private final LinkedList<String> mDumpLines = new LinkedList<>();
+
+ /**
+ * Tests that the global configuration is equal to the default display's override configuration.
+ */
+ public void testDefaultDisplayOverrideConfiguration() throws Exception {
+ final DisplaysState ds = getDisplaysStates();
+ assertNotNull("Global configuration must not be empty.", ds.mGlobalConfig);
+ final String primaryDisplayOverrideConfig = ds.mDisplayConfigs.get(0);
+ assertEquals("Primary display's configuration should not be equal to global configuration.",
+ ds.mGlobalConfig, primaryDisplayOverrideConfig);
+ }
+
+ /**
+ * Tests that secondary display has override configuration set.
+ */
+ public void testCreateVirtualDisplayWithCustomConfig() throws Exception {
+ // Start an activity that is able to create virtual displays.
+ executeShellCommand(getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY));
+ mAmWmState.computeState(mDevice, new String[] { VIRTUAL_DISPLAY_ACTIVITY },
+ false /* compareTaskAndStackBounds */);
+ final DisplaysState originalDS = getDisplaysStates();
+ final int originalDisplayCount = originalDS.mDisplayConfigs.size();
+
+ // Create virtual display with custom density dpi.
+ executeShellCommand(getCreateVirtualDisplayCommand(CUSTOM_DENSITY_DPI));
+
+ // Wait for the virtual display to be created and get configurations.
+ DisplaysState ds = getDisplaysStateAfterCreation(originalDisplayCount + 1);
+ assertEquals("New virtual display should be created",
+ originalDisplayCount + 1, ds.mDisplayConfigs.size());
+
+ // Find the id of newly added display.
+ int newDisplayId = -1;
+ for (Integer displayId : ds.mDisplayConfigs.keySet()) {
+ if (!originalDS.mDisplayConfigs.containsKey(displayId)) {
+ newDisplayId = displayId;
+ break;
+ }
+ }
+ assertFalse(-1 == newDisplayId);
+
+ // Find the density of created display.
+ final String newDisplayConfig = ds.mDisplayConfigs.get(newDisplayId);
+ final String[] configParts = newDisplayConfig.split(" ");
+ int newDensityDpi = -1;
+ for (String part : configParts) {
+ if (part.endsWith("dpi")) {
+ final String densityDpiString = part.substring(0, part.length() - 3);
+ newDensityDpi = Integer.parseInt(densityDpiString);
+ break;
+ }
+ }
+ assertEquals(CUSTOM_DENSITY_DPI, newDensityDpi);
+
+ // Destroy the created display.
+ executeShellCommand(getDestroyVirtualDisplayCommand());
+ }
+
+ private DisplaysState getDisplaysStateAfterCreation(int expectedDisplayCount)
+ throws DeviceNotAvailableException {
+ DisplaysState ds = getDisplaysStates();
+
+ while (ds.mDisplayConfigs.size() != expectedDisplayCount) {
+ log("***Waiting for the correct number of displays...");
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ log(e.toString());
+ }
+ ds = getDisplaysStates();
+ }
+
+ return ds;
+ }
+
+ private DisplaysState getDisplaysStates() throws DeviceNotAvailableException {
+ final CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
+ mDevice.executeShellCommand(DUMPSYS_ACTIVITY_PROCESSES, outputReceiver);
+ String dump = outputReceiver.getOutput();
+ mDumpLines.clear();
+
+ Collections.addAll(mDumpLines, dump.split("\\n"));
+
+ return DisplaysState.create(mDumpLines);
+ }
+
+ /** Contains the configurations applied to attached displays. */
+ private static final class DisplaysState {
+ private static final Pattern sGlobalConfigurationPattern =
+ Pattern.compile("mGlobalConfiguration: (\\{.*\\})");
+ private static final Pattern sDisplayOverrideConfigurationsPattern =
+ Pattern.compile("Display override configurations:");
+ private static final Pattern sDisplayConfigPattern =
+ Pattern.compile("(\\d+): (\\{.*\\})");
+
+ private String mGlobalConfig;
+ private Map<Integer, String> mDisplayConfigs = new HashMap<>();
+
+ static DisplaysState create(LinkedList<String> dump) {
+ final DisplaysState result = new DisplaysState();
+
+ while (!dump.isEmpty()) {
+ final String line = dump.pop().trim();
+
+ Matcher matcher = sDisplayOverrideConfigurationsPattern.matcher(line);
+ if (matcher.matches()) {
+ log(line);
+ while (DisplaysState.shouldContinueExtracting(dump, sDisplayConfigPattern)) {
+ final String displayOverrideConfigLine = dump.pop().trim();
+ log(displayOverrideConfigLine);
+ matcher = sDisplayConfigPattern.matcher(displayOverrideConfigLine);
+ matcher.matches();
+ final Integer displayId = Integer.valueOf(matcher.group(1));
+ result.mDisplayConfigs.put(displayId, matcher.group(2));
+ }
+ continue;
+ }
+
+ matcher = sGlobalConfigurationPattern.matcher(line);
+ if (matcher.matches()) {
+ log(line);
+ result.mGlobalConfig = matcher.group(1);
+ }
+ }
+
+ return result;
+ }
+
+ /** Check if next line in dump matches the pattern and we should continue extracting. */
+ static boolean shouldContinueExtracting(LinkedList<String> dump, Pattern matchingPattern) {
+ if (dump.isEmpty()) {
+ return false;
+ }
+
+ final String line = dump.peek().trim();
+ return matchingPattern.matcher(line).matches();
+ }
+ }
+
+ private static String getCreateVirtualDisplayCommand(int densityDpi) {
+ StringBuilder commandBuilder = new StringBuilder(getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY));
+ commandBuilder.append(" -f 0x20000000");
+ commandBuilder.append(" --es command create_display");
+ if (densityDpi != -1) {
+ commandBuilder.append(" --ei densityDpi ").append(densityDpi);
+ }
+ return commandBuilder.toString();
+ }
+
+ private static String getDestroyVirtualDisplayCommand() {
+ return getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY) + " -f 0x20000000" +
+ " --es command destroy_display";
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
index f7716a5..d095398 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
@@ -19,6 +19,8 @@
import java.awt.Rectangle;
import java.lang.Exception;
import java.lang.String;
+import java.util.ArrayList;
+import java.util.List;
/**
* Build: mmma -j32 cts/hostsidetests/services
@@ -65,11 +67,117 @@
executeShellCommand(String.format("input tap %d %d", tapX, tapY));
mAmWmState.computeState(mDevice, new String[] {TAP_TO_FINISH_ACTIVITY},
false /* compareTaskAndStackBounds */);
- mAmWmState.assertContainsStack("Must contain pinned stack.", PINNED_STACK_ID);
- mAmWmState.assertFrontStack("Pinned stack must be the front stack.", PINNED_STACK_ID);
mAmWmState.assertVisibility(TAP_TO_FINISH_ACTIVITY, true);
}
+ public void testPinnedStackDefaultBounds() throws Exception {
+ setDeviceRotation(0 /* ROTATION_0 */);
+ ActivityManagerState amState = mAmWmState.getAmState();
+ WindowManagerState wmState = mAmWmState.getWmState();
+ amState.computeState(mDevice, ActivityManagerState.DUMP_MODE_PIP);
+ wmState.computeState(mDevice, WindowManagerState.DUMP_MODE_POLICY);
+ Rectangle defaultPipBounds = amState.getDefaultPinnedStackBounds();
+ Rectangle stableBounds = wmState.getStableBounds();
+ assertTrue(defaultPipBounds.width > 0 && defaultPipBounds.height > 0);
+ assertTrue(stableBounds.contains(defaultPipBounds));
+
+ setDeviceRotation(1 /* ROTATION_90 */);
+ amState = mAmWmState.getAmState();
+ wmState = mAmWmState.getWmState();
+ amState.computeState(mDevice, ActivityManagerState.DUMP_MODE_PIP);
+ wmState.computeState(mDevice, WindowManagerState.DUMP_MODE_POLICY);
+ defaultPipBounds = amState.getDefaultPinnedStackBounds();
+ stableBounds = wmState.getStableBounds();
+ assertTrue(defaultPipBounds.width > 0 && defaultPipBounds.height > 0);
+ assertTrue(stableBounds.contains(defaultPipBounds));
+ }
+
+ public void testPinnedStackMovementBounds() throws Exception {
+ setDeviceRotation(0 /* ROTATION_0 */);
+ ActivityManagerState amState = mAmWmState.getAmState();
+ WindowManagerState wmState = mAmWmState.getWmState();
+ amState.computeState(mDevice, ActivityManagerState.DUMP_MODE_PIP);
+ wmState.computeState(mDevice, WindowManagerState.DUMP_MODE_POLICY);
+ Rectangle pipMovementBounds = amState.getPinnedStackMomentBounds();
+ Rectangle stableBounds = wmState.getStableBounds();
+ assertTrue(pipMovementBounds.width > 0 && pipMovementBounds.height > 0);
+ assertTrue(stableBounds.contains(pipMovementBounds));
+
+ setDeviceRotation(1 /* ROTATION_90 */);
+ amState = mAmWmState.getAmState();
+ wmState = mAmWmState.getWmState();
+ amState.computeState(mDevice, ActivityManagerState.DUMP_MODE_PIP);
+ wmState.computeState(mDevice, WindowManagerState.DUMP_MODE_POLICY);
+ pipMovementBounds = amState.getPinnedStackMomentBounds();
+ stableBounds = wmState.getStableBounds();
+ assertTrue(pipMovementBounds.width > 0 && pipMovementBounds.height > 0);
+ assertTrue(stableBounds.contains(pipMovementBounds));
+ }
+
+ public void testPinnedStackOutOfBoundsInsetsNonNegative() throws Exception {
+ final WindowManagerState wmState = mAmWmState.getWmState();
+
+ // Launch an activity into the pinned stack
+ executeShellCommand(getAmStartCmd(LAUNCH_TAP_TO_FINISH_ACTIVITY));
+
+ // Get the display dimensions
+ WindowManagerState.WindowState windowState = getWindowState(TAP_TO_FINISH_ACTIVITY);
+ WindowManagerState.Display display = wmState.getDisplay(windowState.getDisplayId());
+ Rectangle displayRect = display.getDisplayRect();
+
+ // Move the pinned stack offscreen
+ String moveStackOffscreenCommand = String.format("am stack resize 4 %d %d %d %d",
+ displayRect.width - 200, 0, displayRect.width + 200, 500);
+ executeShellCommand(moveStackOffscreenCommand);
+
+ // Ensure that the surface insets are not negative
+ windowState = getWindowState(TAP_TO_FINISH_ACTIVITY);
+ Rectangle contentInsets = windowState.getContentInsets();
+ assertTrue(contentInsets.x >= 0 && contentInsets.y >= 0 && contentInsets.width >= 0 &&
+ contentInsets.height >= 0);
+ }
+
+ public void testPinnedStackInBoundsAfterRotation() throws Exception {
+ // Launch an activity into the pinned stack
+ executeShellCommand(getAmStartCmd(LAUNCH_TAP_TO_FINISH_ACTIVITY));
+
+ // Ensure that the PIP stack is fully visible in each orientation
+ setDeviceRotation(0 /* ROTATION_0 */);
+ assertPinnedStackActivityIsInDisplayBounds(TAP_TO_FINISH_ACTIVITY);
+ setDeviceRotation(1 /* ROTATION_90 */);
+ assertPinnedStackActivityIsInDisplayBounds(TAP_TO_FINISH_ACTIVITY);
+ setDeviceRotation(2 /* ROTATION_180 */);
+ assertPinnedStackActivityIsInDisplayBounds(TAP_TO_FINISH_ACTIVITY);
+ setDeviceRotation(3 /* ROTATION_270 */);
+ assertPinnedStackActivityIsInDisplayBounds(TAP_TO_FINISH_ACTIVITY);
+ setDeviceRotation(0 /* ROTATION_0 */);
+ }
+
+ /**
+ * Asserts that the pinned stack bounds is contained in the display bounds.
+ */
+ private void assertPinnedStackActivityIsInDisplayBounds(String activity) throws Exception {
+ final WindowManagerState.WindowState windowState = getWindowState(TAP_TO_FINISH_ACTIVITY);
+ final WindowManagerState.Display display = mAmWmState.getWmState().getDisplay(
+ windowState.getDisplayId());
+ final Rectangle displayRect = display.getDisplayRect();
+ final Rectangle pinnedStackBounds =
+ mAmWmState.getAmState().getStackById(PINNED_STACK_ID).getBounds();
+ assertTrue(displayRect.contains(pinnedStackBounds));
+ }
+
+ /**
+ * @return the window state for the given {@param activity}'s window.
+ */
+ private WindowManagerState.WindowState getWindowState(String activity) throws Exception {
+ String windowName = getWindowName(activity);
+ mAmWmState.computeState(mDevice, true /* visibleOnly */,
+ new String[] {activity});
+ final List<WindowManagerState.WindowState> tempWindowList = new ArrayList<>();
+ mAmWmState.getWmState().getMatchingWindowState(windowName, tempWindowList);
+ return tempWindowList.get(0);
+ }
+
private void pinnedStackTester(String startActivity, String topActivityName,
boolean moveTopToPinnedStack, boolean isFocusable) throws Exception {
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/AnimationBackgroundTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/AnimationBackgroundTests.java
new file mode 100644
index 0000000..d34a5cf
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/AnimationBackgroundTests.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 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 android.server.cts;
+
+/**
+ * Build: mmma -j32 cts/hostsidetests/services
+ * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test android.server.cts.AnimationBackgroundTests
+ */
+public class AnimationBackgroundTests extends ActivityManagerTestBase {
+
+ public void testAnimationBackground_duringAnimation() throws Exception {
+ executeShellCommand(getAmStartCmd(LAUNCHING_ACTIVITY));
+ launchActivity(false, false, false, "AnimationTestActivity");
+
+ // Make sure we are in the middle of the animation.
+ Thread.sleep(250);
+ mAmWmState.computeState(mDevice, null);
+ assertTrue("window animation background needs to be showing", mAmWmState.getWmState()
+ .getStack(FULLSCREEN_WORKSPACE_STACK_ID)
+ .isWindowAnimationBackgroundSurfaceShowing());
+ }
+
+ public void testAnimationBackground_gone() throws Exception {
+ executeShellCommand(getAmStartCmd(LAUNCHING_ACTIVITY));
+ launchActivity(false, false, false, "AnimationTestActivity");
+ mAmWmState.computeState(mDevice, new String[] { "AnimationTestActivity "});
+ assertFalse("window animation background needs to be gone", mAmWmState.getWmState()
+ .getStack(FULLSCREEN_WORKSPACE_STACK_ID)
+ .isWindowAnimationBackgroundSurfaceShowing());
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerState.java b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerState.java
index 33bf8cc..1c41c92 100644
--- a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerState.java
+++ b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerState.java
@@ -36,7 +36,11 @@
import static android.server.cts.StateLogger.logE;
class ActivityManagerState {
+ public static final int DUMP_MODE_ACTIVITIES = 0;
+ public static final int DUMP_MODE_PIP = 1;
+
private static final String DUMPSYS_ACTIVITY_ACTIVITIES = "dumpsys activity activities";
+ private static final String DUMPSYS_ACTIVITY_PIP = "dumpsys activity pip";
// Copied from ActivityRecord.java
private static final int APPLICATION_ACTIVITY_TYPE = 0;
@@ -49,6 +53,10 @@
Pattern.compile("ResumedActivity\\: ActivityRecord\\{(.+) u(\\d+) (\\S+) (\\S+)\\}");
private final Pattern mFocusedStackPattern =
Pattern.compile("mFocusedStack=ActivityStack\\{(.+) stackId=(\\d+), (.+)\\}(.+)");
+ private final Pattern mDefaultPinnedStackBoundsPattern = Pattern.compile(
+ "defaultBounds=\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]");
+ private final Pattern mPinnedStackMovementBoundsPattern = Pattern.compile(
+ "movementBounds=\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]");
private final Pattern[] mExtractStackExitPatterns =
{ mStackIdPattern, mResumedActivityPattern, mFocusedStackPattern};
@@ -59,8 +67,14 @@
private String mResumedActivityRecord = null;
private final List<String> mResumedActivities = new ArrayList();
private final LinkedList<String> mSysDump = new LinkedList();
+ private final Rectangle mDefaultPinnedStackBounds = new Rectangle();
+ private final Rectangle mPinnedStackMovementBounds = new Rectangle();
void computeState(ITestDevice device) throws DeviceNotAvailableException {
+ computeState(device, DUMP_MODE_ACTIVITIES);
+ }
+
+ void computeState(ITestDevice device, int dumpMode) throws DeviceNotAvailableException {
// It is possible the system is in the middle of transition to the right state when we get
// the dump. We try a few times to get the information we need before giving up.
int retriesLeft = 3;
@@ -84,7 +98,14 @@
}
final CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
- device.executeShellCommand(DUMPSYS_ACTIVITY_ACTIVITIES, outputReceiver);
+ String dumpsysCmd = "";
+ switch (dumpMode) {
+ case DUMP_MODE_ACTIVITIES:
+ dumpsysCmd = DUMPSYS_ACTIVITY_ACTIVITIES; break;
+ case DUMP_MODE_PIP:
+ dumpsysCmd = DUMPSYS_ACTIVITY_PIP; break;
+ }
+ device.executeShellCommand(dumpsysCmd, outputReceiver);
dump = outputReceiver.getOutput();
parseSysDump(dump);
@@ -154,6 +175,30 @@
log(displayId);
currentDisplayId = Integer.parseInt(displayId);
}
+
+ matcher = mDefaultPinnedStackBoundsPattern.matcher(line);
+ if (matcher.matches()) {
+ log(line);
+ int left = Integer.parseInt(matcher.group(1));
+ int top = Integer.parseInt(matcher.group(2));
+ int right = Integer.parseInt(matcher.group(3));
+ int bottom = Integer.parseInt(matcher.group(4));
+ mDefaultPinnedStackBounds.setBounds(left, top, right - left, bottom - top);
+ log(mDefaultPinnedStackBounds.toString());
+ continue;
+ }
+
+ matcher = mPinnedStackMovementBoundsPattern.matcher(line);
+ if (matcher.matches()) {
+ log(line);
+ int left = Integer.parseInt(matcher.group(1));
+ int top = Integer.parseInt(matcher.group(2));
+ int right = Integer.parseInt(matcher.group(3));
+ int bottom = Integer.parseInt(matcher.group(4));
+ mPinnedStackMovementBounds.setBounds(left, top, right - left, bottom - top);
+ log(mPinnedStackMovementBounds.toString());
+ continue;
+ }
}
}
@@ -275,6 +320,14 @@
return null;
}
+ Rectangle getDefaultPinnedStackBounds() {
+ return mDefaultPinnedStackBounds;
+ }
+
+ Rectangle getPinnedStackMomentBounds() {
+ return mPinnedStackMovementBounds;
+ }
+
static class ActivityStack extends ActivityContainer {
private static final Pattern TASK_ID_PATTERN = Pattern.compile("Task id #(\\d+)");
diff --git a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java
index 25e1a41..53ea94d2 100644
--- a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java
+++ b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java
@@ -371,6 +371,10 @@
runCommandAndPrintOutput("input keyevent 82");
}
+ /**
+ * Sets the device rotation, value corresponds to one of {@link Surface.ROTATION_0},
+ * {@link Surface.ROTATION_90}, {@link Surface.ROTATION_180}, {@link Surface.ROTATION_270}.
+ */
protected void setDeviceRotation(int rotation) throws DeviceNotAvailableException {
setAccelerometerRotation(0);
setUserRotation(rotation);
diff --git a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/WindowManagerState.java b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/WindowManagerState.java
index 2d89268..494e501 100644
--- a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/WindowManagerState.java
+++ b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/WindowManagerState.java
@@ -19,7 +19,6 @@
import com.android.tradefed.device.CollectingOutputReceiver;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
import java.awt.Rectangle;
import java.lang.String;
@@ -38,10 +37,12 @@
public static final int DUMP_MODE_APPS = 0;
public static final int DUMP_MODE_VISIBLE = 1;
public static final int DUMP_MODE_VISIBLE_APPS = 2;
+ public static final int DUMP_MODE_POLICY = 3;
private static final String DUMPSYS_WINDOWS_APPS = "dumpsys window -a apps";
private static final String DUMPSYS_WINDOWS_VISIBLE = "dumpsys window -a visible";
private static final String DUMPSYS_WINDOWS_VISIBLE_APPS = "dumpsys window visible-apps";
+ private static final String DUMPSYS_WINDOWS_POLICY = "dumpsys window policy";
private static final Pattern sWindowPattern =
Pattern.compile("Window #(\\d+) Window\\{([0-9a-fA-F]+) u(\\d+) (.+)\\}\\:");
@@ -62,6 +63,8 @@
private static final Pattern sFocusedAppPattern =
Pattern.compile("mFocusedApp=AppWindowToken\\{(.+) token=Token\\{(.+) "
+ "ActivityRecord\\{(.+) u(\\d+) (\\S+) (\\S+)");
+ private static final Pattern sStableBoundsPattern = Pattern.compile(
+ "mStable=\\((\\d+),(\\d+)\\)-\\((\\d+),(\\d+)\\)");
private static final Pattern sLastAppTransitionPattern =
Pattern.compile("mLastUsedAppTransition=(.+)");
@@ -82,6 +85,7 @@
private String mFocusedWindow = null;
private String mFocusedApp = null;
private String mLastTransition = null;
+ private Rectangle mStableBounds = new Rectangle();
private final LinkedList<String> mSysDump = new LinkedList();
void computeState(ITestDevice device, int dumpMode) throws DeviceNotAvailableException {
@@ -111,6 +115,8 @@
switch (dumpMode) {
case DUMP_MODE_APPS:
dumpsysCmd = DUMPSYS_WINDOWS_APPS; break;
+ case DUMP_MODE_POLICY:
+ dumpsysCmd = DUMPSYS_WINDOWS_POLICY; break;
case DUMP_MODE_VISIBLE:
dumpsysCmd = DUMPSYS_WINDOWS_VISIBLE; break;
case DUMP_MODE_VISIBLE_APPS:
@@ -234,6 +240,18 @@
mLastTransition = lastAppTransitionPattern;
continue;
}
+
+ matcher = sStableBoundsPattern.matcher(line);
+ if (matcher.matches()) {
+ log(line);
+ int left = Integer.parseInt(matcher.group(1));
+ int top = Integer.parseInt(matcher.group(2));
+ int right = Integer.parseInt(matcher.group(3));
+ int bottom = Integer.parseInt(matcher.group(4));
+ mStableBounds.setBounds(left, top, right - left, bottom - top);
+ log(mStableBounds.toString());
+ continue;
+ }
}
}
@@ -315,6 +333,10 @@
return null;
}
+ Rectangle getStableBounds() {
+ return mStableBounds;
+ }
+
private void reset() {
mSysDump.clear();
mStacks.clear();
@@ -328,9 +350,12 @@
static class WindowStack extends WindowContainer {
private static final Pattern sTaskIdPattern = Pattern.compile("taskId=(\\d+)");
+ private static final Pattern sWindowAnimationBackgroundSurfacePattern =
+ Pattern.compile("mWindowAnimationBackgroundSurface:");
int mStackId;
ArrayList<WindowTask> mTasks = new ArrayList();
+ boolean mWindowAnimationBackgroundSurfaceShowing;
private WindowStack() {
@@ -362,6 +387,7 @@
final List<Pattern> taskExitPatterns = new ArrayList();
Collections.addAll(taskExitPatterns, exitPatterns);
taskExitPatterns.add(sTaskIdPattern);
+ taskExitPatterns.add(sWindowAnimationBackgroundSurfacePattern);
final Pattern[] taskExitPatternsArray =
taskExitPatterns.toArray(new Pattern[taskExitPatterns.size()]);
@@ -383,9 +409,22 @@
if (extractBounds(line)) {
continue;
}
+
+ if (extractWindowAnimationBackgroundSurface(line)) {
+ continue;
+ }
}
}
+ boolean extractWindowAnimationBackgroundSurface(String line) {
+ if (sWindowAnimationBackgroundSurfacePattern.matcher(line).matches()) {
+ log(line);
+ mWindowAnimationBackgroundSurfaceShowing = true;
+ return true;
+ }
+ return false;
+ }
+
WindowTask getTask(int taskId) {
for (WindowTask task : mTasks) {
if (taskId == task.mTaskId) {
@@ -394,6 +433,10 @@
}
return null;
}
+
+ boolean isWindowAnimationBackgroundSurfaceShowing() {
+ return mWindowAnimationBackgroundSurfaceShowing;
+ }
}
static class WindowTask extends WindowContainer {
@@ -631,6 +674,8 @@
private static final int WINDOW_TYPE_DEBUGGER = 3;
private static final String RECT_STR = "\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]";
+ private static final String NEGATIVE_VALUES_ALLOWED_RECT_STR =
+ "\\[([-\\d]+),([-\\d]+)\\]\\[([-\\d]+),([-\\d]+)\\]";
private static final Pattern sMainFramePattern = Pattern.compile("mFrame=" + RECT_STR + ".+");
private static final Pattern sFramePattern =
Pattern.compile("Frames: containing=" + RECT_STR + " parent=" + RECT_STR);
@@ -640,6 +685,8 @@
Pattern.compile("mDisplayId=(\\d+) stackId=(\\d+) (.+)");
private static final Pattern sSurfaceInsetsPattern =
Pattern.compile("Cur insets.+surface=" + RECT_STR + ".+");
+ private static final Pattern sContentInsetsPattern =
+ Pattern.compile("Cur insets.+content=" + NEGATIVE_VALUES_ALLOWED_RECT_STR + ".+");
private static final Pattern sLayerPattern =
Pattern.compile("Surface:.+layer=(\\d+).+");
private static final Pattern sCropPattern =
@@ -656,6 +703,7 @@
private Rectangle mContentFrame = new Rectangle();
private Rectangle mFrame = new Rectangle();
private Rectangle mSurfaceInsets = new Rectangle();
+ private Rectangle mContentInsets = new Rectangle();
private Rectangle mCrop = new Rectangle();
@@ -709,6 +757,10 @@
return mSurfaceInsets;
}
+ Rectangle getContentInsets() {
+ return mContentInsets;
+ }
+
Rectangle getContentFrame() {
return mContentFrame;
}
@@ -789,6 +841,12 @@
mSurfaceInsets = extractBounds(matcher);
}
+ matcher = sContentInsetsPattern.matcher(line);
+ if (matcher.matches()) {
+ log(TAG + "CONTENT INSETS: " + line);
+ mContentInsets = extractBounds(matcher);
+ }
+
matcher = sLayerPattern.matcher(line);
if (matcher.matches()) {
log(TAG + "LAYER: " + line);
diff --git a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
index f27eb4e..5a5c63b 100644
--- a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
+++ b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
@@ -125,7 +125,7 @@
mCompletionService = new ExecutorCompletionService<>(mExecutionService);
}
- private Map<String, File> extractReferenceImages(String zipFile) {
+ private Map<String, File> extractReferenceImages(String zipFile) throws Exception {
final Map<String, File> references = new HashMap<>();
final InputStream zipStream = ThemeHostTest.class.getResourceAsStream(zipFile);
if (zipStream != null) {
@@ -150,7 +150,12 @@
fail("Failed to unzip assets: " + zipFile);
}
} else {
- fail("Failed to get resource: " + zipFile);
+ if (checkHardwareTypeSkipTest(mDevice.executeShellCommand(HARDWARE_TYPE_CMD).trim())) {
+ Log.logAndDisplay(LogLevel.WARN, LOG_TAG,
+ "Could not obtain resources for skipped themes test: " + zipFile);
+ } else {
+ fail("Failed to get resource: " + zipFile);
+ }
}
return references;
diff --git a/libs/deviceutil/src/android/cts/util/SynchronousPixelCopy.java b/libs/deviceutil/src/android/cts/util/SynchronousPixelCopy.java
new file mode 100644
index 0000000..ce0855a
--- /dev/null
+++ b/libs/deviceutil/src/android/cts/util/SynchronousPixelCopy.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 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 android.cts.util;
+
+import static org.junit.Assert.fail;
+
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.view.PixelCopy;
+import android.view.Surface;
+import android.view.Window;
+import android.view.PixelCopy.OnPixelCopyFinishedListener;
+import android.view.SurfaceView;
+
+public class SynchronousPixelCopy implements OnPixelCopyFinishedListener {
+ private static Handler sHandler;
+ static {
+ HandlerThread thread = new HandlerThread("PixelCopyHelper");
+ thread.start();
+ sHandler = new Handler(thread.getLooper());
+ }
+
+ private int mStatus = -1;
+
+ public int request(Surface source, Bitmap dest) {
+ synchronized (this) {
+ PixelCopy.request(source, dest, this, sHandler);
+ return getResultLocked();
+ }
+ }
+
+ public int request(Surface source, Rect srcRect, Bitmap dest) {
+ synchronized (this) {
+ PixelCopy.request(source, srcRect, dest, this, sHandler);
+ return getResultLocked();
+ }
+ }
+
+ public int request(SurfaceView source, Bitmap dest) {
+ synchronized (this) {
+ PixelCopy.request(source, dest, this, sHandler);
+ return getResultLocked();
+ }
+ }
+
+ public int request(SurfaceView source, Rect srcRect, Bitmap dest) {
+ synchronized (this) {
+ PixelCopy.request(source, srcRect, dest, this, sHandler);
+ return getResultLocked();
+ }
+ }
+
+ public int request(Window source, Bitmap dest) {
+ synchronized (this) {
+ PixelCopy.request(source, dest, this, sHandler);
+ return getResultLocked();
+ }
+ }
+
+ public int request(Window source, Rect srcRect, Bitmap dest) {
+ synchronized (this) {
+ PixelCopy.request(source, srcRect, dest, this, sHandler);
+ return getResultLocked();
+ }
+ }
+
+ private int getResultLocked() {
+ try {
+ this.wait(250);
+ } catch (InterruptedException e) {
+ fail("PixelCopy request didn't complete within 250ms");
+ }
+ return mStatus;
+ }
+
+ @Override
+ public void onPixelCopyFinished(int copyResult) {
+ synchronized (this) {
+ mStatus = copyResult;
+ this.notify();
+ }
+ }
+}
diff --git a/libs/deviceutil/src/android/cts/util/transition/TargetTracking.java b/libs/deviceutil/src/android/cts/util/transition/TargetTracking.java
new file mode 100644
index 0000000..008a0b9
--- /dev/null
+++ b/libs/deviceutil/src/android/cts/util/transition/TargetTracking.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016 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 android.cts.util.transition;
+
+import android.graphics.Rect;
+import android.view.View;
+
+import java.util.ArrayList;
+
+public interface TargetTracking {
+ ArrayList<View> getTrackedTargets();
+ void clearTargets();
+ Rect getCapturedEpicenter();
+}
diff --git a/libs/deviceutil/src/android/cts/util/transition/TrackingTransition.java b/libs/deviceutil/src/android/cts/util/transition/TrackingTransition.java
new file mode 100644
index 0000000..38e6a5c
--- /dev/null
+++ b/libs/deviceutil/src/android/cts/util/transition/TrackingTransition.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 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 android.cts.util.transition;
+
+import android.animation.Animator;
+import android.graphics.Rect;
+import android.transition.Transition;
+import android.transition.TransitionValues;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+
+/**
+ * A transition that tracks which targets are applied to it.
+ * It will assume any target that it applies to will have differences
+ * between the start and end state, regardless of the differences
+ * that actually exist. In other words, it doesn't actually check
+ * any size or position differences or any other property of the view.
+ * It just records the difference.
+ * <p>
+ * Both start and end value Views are recorded, but no actual animation
+ * is created.
+ */
+public class TrackingTransition extends Transition implements TargetTracking {
+ public final ArrayList<View> targets = new ArrayList<>();
+ private final Rect[] mEpicenter = new Rect[1];
+ private static String PROP = "tracking:prop";
+ private static String[] PROPS = { PROP };
+
+ @Override
+ public String[] getTransitionProperties() {
+ return PROPS;
+ }
+
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ transitionValues.values.put(PROP, 0);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ transitionValues.values.put(PROP, 1);
+ }
+
+ @Override
+ public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues != null) {
+ targets.add(startValues.view);
+ }
+ if (endValues != null) {
+ targets.add(endValues.view);
+ }
+ Rect epicenter = getEpicenter();
+ if (epicenter != null) {
+ mEpicenter[0] = new Rect(epicenter);
+ } else {
+ mEpicenter[0] = null;
+ }
+ return null;
+ }
+
+ @Override
+ public ArrayList<View> getTrackedTargets() {
+ return targets;
+ }
+
+ @Override
+ public void clearTargets() {
+ targets.clear();
+ }
+
+ @Override
+ public Rect getCapturedEpicenter() {
+ return mEpicenter[0];
+ }
+}
diff --git a/libs/deviceutil/src/android/cts/util/transition/TrackingVisibility.java b/libs/deviceutil/src/android/cts/util/transition/TrackingVisibility.java
new file mode 100644
index 0000000..63486e3
--- /dev/null
+++ b/libs/deviceutil/src/android/cts/util/transition/TrackingVisibility.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 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 android.cts.util.transition;
+
+import android.animation.Animator;
+import android.graphics.Rect;
+import android.transition.TransitionValues;
+import android.transition.Visibility;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+
+/**
+ * Visibility transition that tracks which targets are applied to it.
+ * This transition does no animation.
+ */
+public class TrackingVisibility extends Visibility implements TargetTracking {
+ public final ArrayList<View> targets = new ArrayList<>();
+ private final Rect[] mEpicenter = new Rect[1];
+
+ @Override
+ public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
+ TransitionValues endValues) {
+ targets.add(endValues.view);
+ Rect epicenter = getEpicenter();
+ if (epicenter != null) {
+ mEpicenter[0] = new Rect(epicenter);
+ } else {
+ mEpicenter[0] = null;
+ }
+ return null;
+ }
+
+ @Override
+ public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
+ TransitionValues endValues) {
+ targets.add(startValues.view);
+ Rect epicenter = getEpicenter();
+ if (epicenter != null) {
+ mEpicenter[0] = new Rect(epicenter);
+ } else {
+ mEpicenter[0] = null;
+ }
+ return null;
+ }
+
+ @Override
+ public ArrayList<View> getTrackedTargets() {
+ return targets;
+ }
+
+ @Override
+ public void clearTargets() {
+ targets.clear();
+ }
+
+ @Override
+ public Rect getCapturedEpicenter() {
+ return mEpicenter[0];
+ }
+}
diff --git a/tests/accessibilityservice/AndroidManifest.xml b/tests/accessibilityservice/AndroidManifest.xml
index 3381f48..e13cdee 100644
--- a/tests/accessibilityservice/AndroidManifest.xml
+++ b/tests/accessibilityservice/AndroidManifest.xml
@@ -67,8 +67,7 @@
<activity
android:label="@string/accessibility_soft_keyboard_modes_activity"
- android:name=".AccessibilitySoftKeyboardModesTest$SoftKeyboardModesActivity"
- android:windowSoftInputMode="stateAlwaysVisible" />
+ android:name=".AccessibilitySoftKeyboardModesTest$SoftKeyboardModesActivity" />
<service
android:name=".InstrumentedAccessibilityService"
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
index 6542778..8dbbeef 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
@@ -333,39 +333,38 @@
}
}
+ // This test assumes device's screen contains its center (W/2, H/2) with some surroundings
+ // and should work for rectangular, round and round with chin screens.
public void testClickWhenMagnified_matchesActualTouch() throws InterruptedException {
if (!mHasTouchScreen) {
return;
}
- final int clickXInsideView = 10;
- final int clickYInsideView = 20;
- int clickX = clickXInsideView + mViewBounds.left;
- int clickY = clickYInsideView + mViewBounds.top;
+ final int clickShiftFromCenterX = 10;
+ final int clickShiftFromCenterY = 20;
+ final Resources res = getInstrumentation().getTargetContext().getResources();
+ final DisplayMetrics metrics = res.getDisplayMetrics();
+ final int centerX = metrics.widthPixels / 2;
+ final int centerY = metrics.heightPixels / 2;
final float TOUCH_TOLERANCE = 2.0f;
StubMagnificationAccessibilityService magnificationService =
StubMagnificationAccessibilityService.enableSelf(this);
android.accessibilityservice.AccessibilityService.MagnificationController
magnificationController = magnificationService.getMagnificationController();
- final Resources res = getInstrumentation().getTargetContext().getResources();
- final DisplayMetrics metrics = res.getDisplayMetrics();
try {
- // Magnify screen by 2x from upper left corner
+ // Magnify screen by 2x with a magnification center in the center of the screen
final AtomicBoolean setScale = new AtomicBoolean();
final float magnificationFactor = 2.0f;
- // Center to have (0,0) in the upper-left corner
- final float centerX = metrics.widthPixels / (2.0f * magnificationFactor) - 1.0f;
- final float centerY = metrics.heightPixels / (2.0f * magnificationFactor) - 1.0f;
magnificationService.runOnServiceSync(() -> {
setScale.set(magnificationController.setScale(magnificationFactor, false));
- // Make sure the upper right corner is on the screen
magnificationController.setCenter(centerX, centerY, false);
});
assertTrue("Failed to set scale", setScale.get());
- GestureDescription click = createClick((int) (clickX * magnificationFactor),
- (int) (clickY * magnificationFactor));
+ final int clickMagnifiedX = (int) (centerX + magnificationFactor * clickShiftFromCenterX);
+ final int clickMagnifiedY = (int) (centerY + magnificationFactor * clickShiftFromCenterY);
+ GestureDescription click = createClick(clickMagnifiedX, clickMagnifiedY);
mService.runOnServiceSync(() -> mService.doDispatchGesture(click, mCallback, null));
mCallback.assertGestureCompletes(GESTURE_COMPLETION_TIMEOUT);
waitForMotionEvents(3);
@@ -382,14 +381,18 @@
MotionEvent clickDown = mMotionEvents.get(0);
MotionEvent clickUp = mMotionEvents.get(1);
+ final int centerXInsideView = centerX - mViewBounds.left;
+ final int centerYInsideView = centerY - mViewBounds.top;
+ final int expectedClickXInsideView = centerXInsideView + clickShiftFromCenterX;
+ final int expectedClickYInsideView = centerYInsideView + clickShiftFromCenterY;
assertEquals(MotionEvent.ACTION_DOWN, clickDown.getActionMasked());
- assertEquals((float) clickXInsideView, clickDown.getX(), TOUCH_TOLERANCE);
- assertEquals((float) clickYInsideView, clickDown.getY(), TOUCH_TOLERANCE);
+ assertEquals((float) expectedClickXInsideView, clickDown.getX(), TOUCH_TOLERANCE);
+ assertEquals((float) expectedClickYInsideView, clickDown.getY(), TOUCH_TOLERANCE);
assertEquals(clickDown.getDownTime(), clickDown.getEventTime());
assertEquals(MotionEvent.ACTION_UP, clickUp.getActionMasked());
- assertEquals((float) clickXInsideView, clickUp.getX(), TOUCH_TOLERANCE);
- assertEquals((float) clickYInsideView, clickUp.getY(), TOUCH_TOLERANCE);
+ assertEquals((float) expectedClickXInsideView, clickUp.getX(), TOUCH_TOLERANCE);
+ assertEquals((float) expectedClickYInsideView, clickUp.getY(), TOUCH_TOLERANCE);
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
index 263c402..3c1db3b 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
@@ -16,24 +16,18 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityService.SoftKeyboardController;
+import android.app.Activity;
import android.app.UiAutomation;
-import android.content.ContentResolver;
-import android.content.Context;
import android.os.Bundle;
-import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
-import android.provider.Settings;
import android.test.ActivityInstrumentationTestCase2;
+import android.view.View;
import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
import android.accessibilityservice.cts.R;
+import android.view.accessibility.AccessibilityWindowInfo;
+import android.view.inputmethod.InputMethodManager;
-import java.io.BufferedReader;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.TimeoutException;
@@ -91,7 +85,11 @@
@Override
public void tearDown() throws Exception {
+ mKeyboardController.setShowMode(SHOW_MODE_AUTO);
mService.runOnServiceSync(() -> mService.disableSelf());
+ Activity activity = getActivity();
+ activity.getSystemService(InputMethodManager.class)
+ .hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
}
public void testApiReturnValues_shouldChangeValueOnRequestAndSendCallback() throws Exception {
@@ -109,7 +107,7 @@
};
mKeyboardController.addOnShowModeChangedListener(listener);
- // The soft keyboard should be in its' default mode.
+ // The soft keyboard should be in its default mode.
assertEquals(SHOW_MODE_AUTO, mKeyboardController.getShowMode());
// Set the show mode to SHOW_MODE_HIDDEN.
@@ -130,56 +128,18 @@
assertTrue(mKeyboardController.removeOnShowModeChangedListener(listener));
}
- public void testHideSoftKeyboard_shouldHideAndShowKeyboardOnRequest() throws Exception {
- // The soft keyboard should be in its' default mode.
+ public void testHideSoftKeyboard_shouldHideKeyboardOnRequest() throws Exception {
+ // The soft keyboard should be in its default mode.
assertEquals(SHOW_MODE_AUTO, mKeyboardController.getShowMode());
- // Note: This Activity always has a visible keyboard (due to windowSoftInputMode being set
- // to stateAlwaysVisible).
- waitForIdle();
- int numWindowsWithIme = mUiAutomation.getWindows().size();
-
+ forceImeToBeShown();
+ waitForImePresentToBe(true);
// Request the keyboard be hidden.
assertTrue(mKeyboardController.setShowMode(SHOW_MODE_HIDDEN));
- waitForWindowStateChanged();
- waitForIdle();
- // Make sure the keyboard is hidden.
- assertEquals(numWindowsWithIme - 1, mUiAutomation.getWindows().size());
+ waitForImePresentToBe(false);
- // Request the default keyboard mode.
assertTrue(mKeyboardController.setShowMode(SHOW_MODE_AUTO));
- waitForWindowStateChanged();
- waitForIdle();
-
- // Make sure the keyboard is visible.
- assertEquals(numWindowsWithIme, mUiAutomation.getWindows().size());
- }
-
- public void testHideSoftKeyboard_shouldHideKeyboardUntilServiceIsDisabled() throws Exception {
- // The soft keyboard should be in its' default mode.
- assertEquals(SHOW_MODE_AUTO, mKeyboardController.getShowMode());
-
- // Note: This Activity always has a visible keyboard (due to windowSoftInputMode being set
- // to stateAlwaysVisible).
- waitForIdle();
- int numWindowsWithIme = mUiAutomation.getWindows().size();
-
- // Set the show mode to SHOW_MODE_HIDDEN.
- assertTrue(mKeyboardController.setShowMode(SHOW_MODE_HIDDEN));
- waitForWindowStateChanged();
- waitForIdle();
-
- // Make sure the keyboard is hidden.
- assertEquals(numWindowsWithIme - 1, mUiAutomation.getWindows().size());
-
- // Make sure we can see the soft keyboard once all Accessibility Services are disabled.
- mService.disableSelf();
- waitForWindowStateChanged();
- waitForIdle();
-
- // See how many windows are present.
- assertEquals(numWindowsWithIme, mUiAutomation.getWindows().size());
}
private void waitForCallbackValueWithLock(int expectedValue) throws Exception {
@@ -202,7 +162,7 @@
+ "> does not match expected value < " + expectedValue + ">");
}
- private void waitForWindowStateChanged() throws Exception {
+ private void waitForWindowChanges() {
try {
mUiAutomation.executeAndWaitForEvent(new Runnable() {
@Override
@@ -223,8 +183,31 @@
}
}
- private void waitForIdle() throws TimeoutException {
- mUiAutomation.waitForIdle(TIMEOUT_ACCESSIBILITY_STATE_IDLE, TIMEOUT_ASYNC_PROCESSING);
+ private boolean isImeWindowPresent() {
+ List<AccessibilityWindowInfo> windows = mUiAutomation.getWindows();
+ for (int i = 0; i < windows.size(); i++) {
+ if (windows.get(i).getType() == AccessibilityWindowInfo.TYPE_INPUT_METHOD) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void waitForImePresentToBe(boolean imeShown) {
+ long timeOutTime = System.currentTimeMillis() + TIMEOUT_ASYNC_PROCESSING;
+ while (isImeWindowPresent() != imeShown) {
+ assertTrue(System.currentTimeMillis() < timeOutTime);
+ waitForWindowChanges();
+ }
+ }
+
+ private void forceImeToBeShown() {
+ getInstrumentation().runOnMainSync(() -> {
+ Activity activity = getActivity();
+ View editText = activity.findViewById(R.id.edit_text);
+ activity.getSystemService(InputMethodManager.class)
+ .showSoftInput(editText, InputMethodManager.SHOW_FORCED);
+ });
}
/**
diff --git a/tests/app/app/res/layout/fragment_b.xml b/tests/app/app/res/layout/fragment_b.xml
deleted file mode 100644
index d8ed961..0000000
--- a/tests/app/app/res/layout/fragment_b.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/textB"
- android:text="@string/hello"/>
-</LinearLayout>
diff --git a/tests/app/app/res/layout/fragment_c.xml b/tests/app/app/res/layout/fragment_c.xml
deleted file mode 100644
index ed3c753..0000000
--- a/tests/app/app/res/layout/fragment_c.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/textC"
- android:text="@string/hello"/>
-</LinearLayout>
diff --git a/tests/app/app/res/layout/fragment_end.xml b/tests/app/app/res/layout/fragment_end.xml
deleted file mode 100644
index aa3d9e8..0000000
--- a/tests/app/app/res/layout/fragment_end.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:transitionName="destination"
- android:id="@+id/hello"
- android:text="@string/hello"/>
- <View android:layout_width="10dp"
- android:layout_height="10dp"
- android:background="#0F0"
- android:id="@+id/greenSquare"/>
- <View android:layout_width="10dp"
- android:layout_height="10dp"
- android:background="#F00"
- android:id="@+id/redSquare"/>
-</LinearLayout>
diff --git a/tests/app/src/android/app/cts/FragmentReplaceTest.java b/tests/app/src/android/app/cts/FragmentReplaceTest.java
deleted file mode 100644
index ad9e3af..0000000
--- a/tests/app/src/android/app/cts/FragmentReplaceTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.app.cts;
-
-import android.app.stubs.FragmentTestActivity;
-import android.app.stubs.FragmentTestActivity.TestFragment;
-import android.app.stubs.R;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-
-/**
- * Test to prevent regressions in FragmentManager fragment replace method. See b/24693644
- */
-public class FragmentReplaceTest extends
- ActivityInstrumentationTestCase2<FragmentTestActivity> {
- private FragmentTestActivity mActivity;
-
-
- public FragmentReplaceTest() {
- super(FragmentTestActivity.class);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mActivity = getActivity();
- }
-
- @UiThreadTest
- public void testReplaceFragment() throws Throwable {
- mActivity.getFragmentManager().beginTransaction()
- .add(R.id.content, new TestFragment(R.layout.fragment_a))
- .addToBackStack(null)
- .commit();
- mActivity.getFragmentManager().executePendingTransactions();
- assertNotNull(mActivity.findViewById(R.id.textA));
- assertNull(mActivity.findViewById(R.id.textB));
- assertNull(mActivity.findViewById(R.id.textC));
-
- mActivity.getFragmentManager().beginTransaction()
- .add(R.id.content, new TestFragment(R.layout.fragment_b))
- .addToBackStack(null)
- .commit();
- mActivity.getFragmentManager().executePendingTransactions();
- assertNotNull(mActivity.findViewById(R.id.textA));
- assertNotNull(mActivity.findViewById(R.id.textB));
- assertNull(mActivity.findViewById(R.id.textC));
-
- mActivity.getFragmentManager().beginTransaction()
- .replace(R.id.content, new TestFragment(R.layout.fragment_c))
- .addToBackStack(null)
- .commit();
- mActivity.getFragmentManager().executePendingTransactions();
- assertNull(mActivity.findViewById(R.id.textA));
- assertNull(mActivity.findViewById(R.id.textB));
- assertNotNull(mActivity.findViewById(R.id.textC));
- }
-}
diff --git a/tests/app/src/android/app/cts/FragmentTransitionTest.java b/tests/app/src/android/app/cts/FragmentTransitionTest.java
deleted file mode 100644
index 7270672..0000000
--- a/tests/app/src/android/app/cts/FragmentTransitionTest.java
+++ /dev/null
@@ -1,466 +0,0 @@
-/*
- * Copyright (C) 2015 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 android.app.cts;
-
-import android.app.FragmentManager;
-import android.app.stubs.FragmentTestActivity;
-import android.app.stubs.FragmentTestActivity.OnTransitionListener;
-import android.app.stubs.FragmentTestActivity.TestFragment;
-import android.app.stubs.R;
-import android.os.Debug;
-import android.os.SystemClock;
-import android.test.ActivityInstrumentationTestCase2;
-import android.view.View;
-
-public class FragmentTransitionTest extends
- ActivityInstrumentationTestCase2<FragmentTestActivity> {
- private TestFragment mStartFragment;
- private TestFragment mMidFragment;
- private TestFragment mEndFragment;
- private FragmentTestActivity mActivity;
-
- public FragmentTransitionTest() {
- super(FragmentTestActivity.class);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mStartFragment = null;
- mMidFragment = null;
- mEndFragment = null;
- mActivity = getActivity();
- }
-
- public void testFragmentTransition() throws Throwable {
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mStartFragment = new TestFragment(R.layout.fragment_start);
- mActivity.getFragmentManager().beginTransaction()
- .replace(R.id.content, mStartFragment)
- .commit();
- mActivity.getFragmentManager().executePendingTransactions();
- }
- });
- waitForEnd(mStartFragment, TestFragment.ENTER);
- assertTrue(mStartFragment.wasEndCalled(TestFragment.ENTER));
- mStartFragment.clearNotifications();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- final View sharedElement = mActivity.findViewById(R.id.hello);
- assertEquals("source", sharedElement.getTransitionName());
-
- mEndFragment = new TestFragment(R.layout.fragment_end);
- mActivity.getFragmentManager().beginTransaction()
- .replace(R.id.content, mEndFragment)
- .addSharedElement(sharedElement, "destination")
- .addToBackStack(null)
- .commit();
- mActivity.getFragmentManager().executePendingTransactions();
- }
- });
- waitForEnd(mEndFragment, TestFragment.ENTER);
- assertTrue(mEndFragment.wasEndCalled(TestFragment.ENTER));
- assertTrue(mStartFragment.wasEndCalled(TestFragment.EXIT));
- assertTrue(mEndFragment.wasEndCalled(TestFragment.SHARED_ELEMENT_ENTER));
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- final View textView = mActivity.findViewById(R.id.hello);
- assertEquals("destination", textView.getTransitionName());
- mActivity.getFragmentManager().popBackStack();
- mActivity.getFragmentManager().executePendingTransactions();
- }
- });
- waitForEnd(mStartFragment, TestFragment.REENTER);
- assertTrue(mStartFragment.wasEndCalled(TestFragment.REENTER));
- assertTrue(mEndFragment.wasEndCalled(TestFragment.RETURN));
- }
-
- public void testFirstOutLastInTransition() throws Throwable {
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mStartFragment = new TestFragment(R.layout.fragment_start);
- mActivity.getFragmentManager().beginTransaction()
- .replace(R.id.content, mStartFragment)
- .commit();
- mActivity.getFragmentManager().executePendingTransactions();
- }
- });
- waitForEnd(mStartFragment, TestFragment.ENTER);
- assertTrue(mStartFragment.wasEndCalled(TestFragment.ENTER));
- mStartFragment.clearNotifications();
-
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mMidFragment = new TestFragment(R.layout.checkbox_layout);
- mEndFragment = new TestFragment(R.layout.fragment_end);
- mActivity.getFragmentManager().beginTransaction()
- .replace(R.id.content, mMidFragment)
- .replace(R.id.content, mEndFragment)
- .addToBackStack(null)
- .commit();
- mActivity.getFragmentManager().executePendingTransactions();
- }
- });
- waitForEnd(mEndFragment, TestFragment.ENTER);
- assertTrue(mEndFragment.wasEndCalled(TestFragment.ENTER));
- assertFalse(mEndFragment.wasEndCalled(TestFragment.EXIT));
- assertFalse(mEndFragment.wasEndCalled(TestFragment.RETURN));
- assertFalse(mEndFragment.wasEndCalled(TestFragment.REENTER));
-
- assertTrue(mStartFragment.wasEndCalled(TestFragment.EXIT));
- assertFalse(mStartFragment.wasEndCalled(TestFragment.ENTER));
- assertFalse(mStartFragment.wasEndCalled(TestFragment.RETURN));
- assertFalse(mStartFragment.wasEndCalled(TestFragment.REENTER));
-
- assertFalse(mMidFragment.wasStartCalled(TestFragment.ENTER));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.EXIT));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.REENTER));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.RETURN));
-
- mStartFragment.clearNotifications();
- mEndFragment.clearNotifications();
-
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mActivity.getFragmentManager().popBackStack();
- mActivity.getFragmentManager().executePendingTransactions();
- }
- });
- waitForEnd(mEndFragment, TestFragment.RETURN);
- assertTrue(mEndFragment.wasEndCalled(TestFragment.RETURN));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.ENTER));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.EXIT));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.REENTER));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.RETURN));
-
- assertTrue(mStartFragment.wasStartCalled(TestFragment.REENTER));
- assertFalse(mStartFragment.wasStartCalled(TestFragment.ENTER));
- assertFalse(mStartFragment.wasStartCalled(TestFragment.EXIT));
- assertFalse(mStartFragment.wasStartCalled(TestFragment.RETURN));
- }
-
- public void testPopTwo() throws Throwable {
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mStartFragment = new TestFragment(R.layout.fragment_start);
- mActivity.getFragmentManager().beginTransaction()
- .replace(R.id.content, mStartFragment)
- .commit();
- mActivity.getFragmentManager().executePendingTransactions();
- }
- });
- waitForEnd(mStartFragment, TestFragment.ENTER);
- assertTrue(mStartFragment.wasEndCalled(TestFragment.ENTER));
- mStartFragment.clearNotifications();
-
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mMidFragment = new TestFragment(R.layout.checkbox_layout);
- mActivity.getFragmentManager().beginTransaction()
- .replace(R.id.content, mMidFragment)
- .addToBackStack(null)
- .commit();
- mActivity.getFragmentManager().executePendingTransactions();
- }
- });
- waitForEnd(mMidFragment, TestFragment.ENTER);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mEndFragment = new TestFragment(R.layout.fragment_end);
- mActivity.getFragmentManager().beginTransaction()
- .replace(R.id.content, mEndFragment)
- .addToBackStack(null)
- .commit();
- mActivity.getFragmentManager().executePendingTransactions();
- }
- });
- waitForEnd(mEndFragment, TestFragment.ENTER);
- assertTrue(mEndFragment.wasEndCalled(TestFragment.ENTER));
- assertFalse(mEndFragment.wasEndCalled(TestFragment.EXIT));
- assertFalse(mEndFragment.wasEndCalled(TestFragment.RETURN));
- assertFalse(mEndFragment.wasEndCalled(TestFragment.REENTER));
-
- assertTrue(mStartFragment.wasEndCalled(TestFragment.EXIT));
- assertFalse(mStartFragment.wasEndCalled(TestFragment.ENTER));
- assertFalse(mStartFragment.wasEndCalled(TestFragment.RETURN));
- assertFalse(mStartFragment.wasEndCalled(TestFragment.REENTER));
-
- assertTrue(mMidFragment.wasStartCalled(TestFragment.ENTER));
- assertTrue(mMidFragment.wasStartCalled(TestFragment.EXIT));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.REENTER));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.RETURN));
-
- mStartFragment.clearNotifications();
- mMidFragment.clearNotifications();
- mEndFragment.clearNotifications();
-
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- FragmentManager fm = mActivity.getFragmentManager();
- int id = fm.getBackStackEntryAt(0).getId();
- fm.popBackStack(id, FragmentManager.POP_BACK_STACK_INCLUSIVE);
- fm.executePendingTransactions();
- }
- });
- waitForEnd(mEndFragment, TestFragment.RETURN);
- assertTrue(mEndFragment.wasEndCalled(TestFragment.RETURN));
-
- assertFalse(mMidFragment.wasStartCalled(TestFragment.ENTER));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.EXIT));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.REENTER));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.RETURN));
-
- assertTrue(mStartFragment.wasStartCalled(TestFragment.REENTER));
- assertFalse(mStartFragment.wasStartCalled(TestFragment.ENTER));
- assertFalse(mStartFragment.wasStartCalled(TestFragment.EXIT));
- assertFalse(mStartFragment.wasStartCalled(TestFragment.RETURN));
- }
-
- public void testNullTransition() throws Throwable {
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mStartFragment = new TestFragment(R.layout.fragment_start);
- mStartFragment.clearTransitions();
- mActivity.getFragmentManager().beginTransaction()
- .replace(R.id.content, mStartFragment)
- .commit();
- mActivity.getFragmentManager().executePendingTransactions();
- }
- });
- waitForStart(mStartFragment, TestFragment.ENTER);
- // No transitions
- assertFalse(mStartFragment.wasStartCalled(TestFragment.ENTER));
-
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mMidFragment = new TestFragment(R.layout.checkbox_layout);
- mEndFragment = new TestFragment(R.layout.fragment_end);
- mEndFragment.clearTransitions();
- mActivity.getFragmentManager().beginTransaction()
- .replace(R.id.content, mMidFragment)
- .replace(R.id.content, mEndFragment)
- .addToBackStack(null)
- .commit();
- mActivity.getFragmentManager().executePendingTransactions();
- }
- });
- waitForStart(mEndFragment, TestFragment.ENTER);
- assertFalse(mEndFragment.wasEndCalled(TestFragment.ENTER));
- assertFalse(mEndFragment.wasEndCalled(TestFragment.EXIT));
- assertFalse(mEndFragment.wasEndCalled(TestFragment.RETURN));
- assertFalse(mEndFragment.wasEndCalled(TestFragment.REENTER));
-
- assertFalse(mStartFragment.wasEndCalled(TestFragment.EXIT));
- assertFalse(mStartFragment.wasEndCalled(TestFragment.ENTER));
- assertFalse(mStartFragment.wasEndCalled(TestFragment.RETURN));
- assertFalse(mStartFragment.wasEndCalled(TestFragment.REENTER));
-
- assertFalse(mMidFragment.wasStartCalled(TestFragment.ENTER));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.EXIT));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.REENTER));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.RETURN));
-
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mActivity.getFragmentManager().popBackStack();
- mActivity.getFragmentManager().executePendingTransactions();
- }
- });
- waitForStart(mEndFragment, TestFragment.RETURN);
- assertFalse(mEndFragment.wasEndCalled(TestFragment.RETURN));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.ENTER));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.EXIT));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.REENTER));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.RETURN));
-
- assertFalse(mStartFragment.wasStartCalled(TestFragment.REENTER));
- assertFalse(mStartFragment.wasStartCalled(TestFragment.ENTER));
- assertFalse(mStartFragment.wasStartCalled(TestFragment.EXIT));
- assertFalse(mStartFragment.wasStartCalled(TestFragment.RETURN));
- }
-
- public void testRemoveAdded() throws Throwable {
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mStartFragment = new TestFragment(R.layout.fragment_start);
- mActivity.getFragmentManager().beginTransaction()
- .replace(R.id.content, mStartFragment)
- .commit();
- mActivity.getFragmentManager().executePendingTransactions();
- }
- });
- waitForEnd(mStartFragment, TestFragment.ENTER);
- assertTrue(mStartFragment.wasEndCalled(TestFragment.ENTER));
- mStartFragment.clearNotifications();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mEndFragment = new TestFragment(R.layout.fragment_end);
- mActivity.getFragmentManager().beginTransaction()
- .replace(R.id.content, mEndFragment)
- .replace(R.id.content, mStartFragment)
- .replace(R.id.content, mEndFragment)
- .addToBackStack(null)
- .commit();
- mActivity.getFragmentManager().executePendingTransactions();
- }
- });
- waitForEnd(mEndFragment, TestFragment.ENTER);
- assertTrue(mEndFragment.wasEndCalled(TestFragment.ENTER));
- assertTrue(mStartFragment.wasEndCalled(TestFragment.EXIT));
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mActivity.getFragmentManager().popBackStack();
- mActivity.getFragmentManager().executePendingTransactions();
- }
- });
- waitForEnd(mStartFragment, TestFragment.REENTER);
- assertTrue(mStartFragment.wasEndCalled(TestFragment.REENTER));
- assertTrue(mEndFragment.wasEndCalled(TestFragment.RETURN));
- }
-
- public void testAddRemoved() throws Throwable {
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mStartFragment = new TestFragment(R.layout.fragment_start);
- mActivity.getFragmentManager().beginTransaction()
- .replace(R.id.content, mStartFragment)
- .commit();
- mActivity.getFragmentManager().executePendingTransactions();
- }
- });
- waitForEnd(mStartFragment, TestFragment.ENTER);
- assertTrue(mStartFragment.wasEndCalled(TestFragment.ENTER));
- mStartFragment.clearNotifications();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mEndFragment = new TestFragment(R.layout.fragment_end);
- mActivity.getFragmentManager().beginTransaction()
- .replace(R.id.content, mEndFragment)
- .replace(R.id.content, mStartFragment)
- .addToBackStack(null)
- .commit();
- mActivity.getFragmentManager().executePendingTransactions();
- }
- });
- waitForStart(mStartFragment, TestFragment.ENTER);
- assertFalse(mStartFragment.wasStartCalled(TestFragment.ENTER));
- assertFalse(mStartFragment.wasStartCalled(TestFragment.EXIT));
- assertFalse(mEndFragment.wasStartCalled(TestFragment.ENTER));
- assertFalse(mEndFragment.wasStartCalled(TestFragment.EXIT));
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mActivity.getFragmentManager().popBackStack();
- mActivity.getFragmentManager().executePendingTransactions();
- }
- });
- waitForStart(mStartFragment, TestFragment.REENTER);
- assertFalse(mStartFragment.wasStartCalled(TestFragment.REENTER));
- assertFalse(mStartFragment.wasStartCalled(TestFragment.RETURN));
- assertFalse(mEndFragment.wasStartCalled(TestFragment.REENTER));
- assertFalse(mEndFragment.wasStartCalled(TestFragment.RETURN));
- }
-
- private boolean waitForStart(TestFragment fragment, int key) throws InterruptedException {
- final boolean started;
- WaitForTransition listener = new WaitForTransition(key, true);
- fragment.setOnTransitionListener(listener);
- final long endTime = SystemClock.uptimeMillis() + 100;
- synchronized (listener) {
- long waitTime;
- while ((waitTime = endTime - SystemClock.uptimeMillis()) > 0 &&
- !listener.isDone()) {
- listener.wait(waitTime);
- }
- started = listener.isDone();
- }
- fragment.setOnTransitionListener(null);
- getInstrumentation().waitForIdleSync();
- return started;
- }
-
- private boolean waitForEnd(TestFragment fragment, int key) throws InterruptedException {
- if (!waitForStart(fragment, key)) {
- return false;
- }
- final boolean ended;
- WaitForTransition listener = new WaitForTransition(key, false);
- fragment.setOnTransitionListener(listener);
- final long endTime = SystemClock.uptimeMillis() + 400;
- synchronized (listener) {
- long waitTime;
- while ((waitTime = endTime - SystemClock.uptimeMillis()) > 0 &&
- !listener.isDone()) {
- listener.wait(waitTime);
- }
- ended = listener.isDone();
- }
- fragment.setOnTransitionListener(null);
- getInstrumentation().waitForIdleSync();
- return ended;
- }
-
- private static class WaitForTransition implements OnTransitionListener {
- final int key;
- final boolean isStart;
- boolean isDone;
-
- public WaitForTransition(int key, boolean isStart) {
- this.key = key;
- this.isStart = isStart;
- }
-
- protected boolean isComplete(TestFragment fragment) {
- if (isStart) {
- return fragment.wasStartCalled(key);
- } else {
- return fragment.wasEndCalled(key);
- }
- }
-
- public synchronized boolean isDone() {
- return isDone;
- }
-
- @Override
- public synchronized void onTransition(TestFragment fragment) {
- isDone = isComplete(fragment);
- if (isDone) {
- notifyAll();
- }
- }
- }
-
-}
diff --git a/tests/camera/src/android/hardware/camera2/cts/BurstCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/BurstCaptureTest.java
index 5fbc682..de48dd6 100644
--- a/tests/camera/src/android/hardware/camera2/cts/BurstCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/BurstCaptureTest.java
@@ -32,7 +32,6 @@
import java.util.List;
import java.util.ArrayList;
-import java.util.Arrays;
public class BurstCaptureTest extends Camera2SurfaceViewTestCase {
private static final String TAG = "BurstCaptureTest";
@@ -99,31 +98,8 @@
final long minStillFrameDuration =
config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, stillSize);
- // Add 0.05 here so Fps like 29.99 evaluated to 30
- int minBurstFps = (int) Math.floor(1e9 / minStillFrameDuration + 0.05f);
- boolean foundConstantMaxYUVRange = false;
- boolean foundYUVStreamingRange = false;
- // Find suitable target FPS range - as high as possible that covers the max YUV rate
- // Also verify that there's a good preview rate as well
- List<Range<Integer> > fpsRanges = Arrays.asList(
- mStaticInfo.getAeAvailableTargetFpsRangesChecked());
- Range<Integer> targetRange = null;
- for (Range<Integer> fpsRange : fpsRanges) {
- if (fpsRange.getLower() == minBurstFps && fpsRange.getUpper() == minBurstFps) {
- foundConstantMaxYUVRange = true;
- targetRange = fpsRange;
- }
- if (fpsRange.getLower() <= 15 && fpsRange.getUpper() == minBurstFps) {
- foundYUVStreamingRange = true;
- }
- }
-
- assertTrue(String.format("Cam %s: Target FPS range of (%d, %d) must be supported",
- cameraId, minBurstFps, minBurstFps), foundConstantMaxYUVRange);
- assertTrue(String.format(
- "Cam %s: Target FPS range of (x, %d) where x <= 15 must be supported",
- cameraId, minBurstFps), foundYUVStreamingRange);
+ Range<Integer> targetRange = getSuitableFpsRangeForDuration(cameraId, minStillFrameDuration);
Log.i(TAG, String.format("Selected frame rate range %d - %d for YUV burst",
targetRange.getLower(), targetRange.getUpper()));
diff --git a/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java b/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
index 7e28403..b88a80f 100644
--- a/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
@@ -22,6 +22,7 @@
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
+import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
@@ -32,6 +33,7 @@
import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
import android.hardware.camera2.params.InputConfiguration;
+import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.media.ImageWriter;
@@ -39,6 +41,7 @@
import android.os.SystemClock;
import android.util.Log;
import android.util.Pair;
+import android.util.Range;
import android.util.Size;
import android.view.Surface;
@@ -338,6 +341,209 @@
}
/**
+ * Test multiple capture KPI for YUV_420_888 format: the average time duration
+ * between sending out image capture requests and receiving capture results.
+ * <p>
+ * It measures capture latency, which is the time between sending out the capture
+ * request and getting the full capture result, and the frame duration, which is the timestamp
+ * gap between results.
+ * </p>
+ */
+ public void testMultipleCapture() throws Exception {
+ double[] avgResultTimes = new double[mCameraIds.length];
+ double[] avgDurationMs = new double[mCameraIds.length];
+
+ // A simple CaptureSession StateCallback to handle onCaptureQueueEmpty
+ class MultipleCaptureStateCallback extends CameraCaptureSession.StateCallback {
+ private ConditionVariable captureQueueEmptyCond = new ConditionVariable();
+ private int captureQueueEmptied = 0;
+
+ @Override
+ public void onConfigured(CameraCaptureSession session) {
+ // Empty implementation
+ }
+
+ @Override
+ public void onConfigureFailed(CameraCaptureSession session) {
+ // Empty implementation
+ }
+
+ @Override
+ public void onCaptureQueueEmpty(CameraCaptureSession session) {
+ captureQueueEmptied++;
+ if (VERBOSE) {
+ Log.v(TAG, "onCaptureQueueEmpty received. captureQueueEmptied = "
+ + captureQueueEmptied);
+ }
+
+ captureQueueEmptyCond.open();
+ }
+
+ /* Wait for onCaptureQueueEmpty, return immediately if an onCaptureQueueEmpty was
+ * already received, otherwise, wait for one to arrive. */
+ public void waitForCaptureQueueEmpty(long timeout) {
+ if (captureQueueEmptied > 0) {
+ captureQueueEmptied--;
+ return;
+ }
+
+ if (captureQueueEmptyCond.block(timeout)) {
+ captureQueueEmptyCond.close();
+ captureQueueEmptied = 0;
+ } else {
+ throw new TimeoutRuntimeException("Unable to receive onCaptureQueueEmpty after "
+ + timeout + "ms");
+ }
+ }
+ }
+
+ final MultipleCaptureStateCallback sessionListener = new MultipleCaptureStateCallback();
+
+ int counter = 0;
+ for (String id : mCameraIds) {
+ // Do NOT move these variables to outer scope
+ // They will be passed to DeviceReportLog and their references will be stored
+ String streamName = "test_multiple_capture";
+ mReportLog = new DeviceReportLog(REPORT_LOG_NAME, streamName);
+ mReportLog.addValue("camera_id", id, ResultType.NEUTRAL, ResultUnit.NONE);
+ long[] startTimes = new long[NUM_MAX_IMAGES];
+ double[] getResultTimes = new double[NUM_MAX_IMAGES];
+ double[] frameDurationMs = new double[NUM_MAX_IMAGES-1];
+ try {
+ openDevice(id);
+
+ if (!mStaticInfo.isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
+ continue;
+ }
+
+ for (int i = 0; i < NUM_TEST_LOOPS; i++) {
+
+ // setup builders and listeners
+ CaptureRequest.Builder previewBuilder =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ CaptureRequest.Builder captureBuilder =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+ SimpleCaptureCallback previewResultListener =
+ new SimpleCaptureCallback();
+ SimpleTimingResultListener captureResultListener =
+ new SimpleTimingResultListener();
+ SimpleImageReaderListener imageListener =
+ new SimpleImageReaderListener(/*asyncMode*/true, NUM_MAX_IMAGES);
+
+ Size maxYuvSize = CameraTestUtils.getSortedSizesForFormat(
+ id, mCameraManager, ImageFormat.YUV_420_888, /*bound*/null).get(0);
+ // Find minimum frame duration for YUV_420_888
+ StreamConfigurationMap config = mStaticInfo.getCharacteristics().get(
+ CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ final long minStillFrameDuration =
+ config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, maxYuvSize);
+ Range<Integer> targetRange = getSuitableFpsRangeForDuration(id,
+ minStillFrameDuration);
+ previewBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,
+ targetRange);
+ captureBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,
+ targetRange);
+
+ prepareCaptureAndStartPreview(previewBuilder, captureBuilder,
+ mOrderedPreviewSizes.get(0), maxYuvSize,
+ ImageFormat.YUV_420_888, previewResultListener,
+ sessionListener, NUM_MAX_IMAGES, imageListener);
+
+ // Converge AE
+ waitForAeStable(previewResultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+
+ if (mStaticInfo.isAeLockSupported()) {
+ // Lock AE if possible to improve stability
+ previewBuilder.set(CaptureRequest.CONTROL_AE_LOCK, true);
+ mSession.setRepeatingRequest(previewBuilder.build(), previewResultListener,
+ mHandler);
+ if (mStaticInfo.isHardwareLevelAtLeastLimited()) {
+ // Legacy mode doesn't output AE state
+ waitForResultValue(previewResultListener, CaptureResult.CONTROL_AE_STATE,
+ CaptureResult.CONTROL_AE_STATE_LOCKED, NUM_RESULTS_WAIT_TIMEOUT);
+ }
+ }
+
+ // Capture NUM_MAX_IMAGES images based on onCaptureQueueEmpty callback
+ for (int j = 0; j < NUM_MAX_IMAGES; j++) {
+
+ // Capture an image and get image data
+ startTimes[j] = SystemClock.elapsedRealtime();
+ CaptureRequest request = captureBuilder.build();
+ mSession.capture(request, captureResultListener, mHandler);
+
+ // Wait for capture queue empty for the current request
+ sessionListener.waitForCaptureQueueEmpty(
+ CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS);
+ }
+
+ // Acquire the capture result time and frame duration
+ long prevTimestamp = -1;
+ for (int j = 0; j < NUM_MAX_IMAGES; j++) {
+ Pair<CaptureResult, Long> captureResultNTime =
+ captureResultListener.getCaptureResultNTime(
+ CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
+
+ getResultTimes[j] +=
+ (double)(captureResultNTime.second - startTimes[j])/NUM_TEST_LOOPS;
+
+ // Collect inter-frame timestamp
+ long timestamp = captureResultNTime.first.get(CaptureResult.SENSOR_TIMESTAMP);
+ if (prevTimestamp != -1) {
+ frameDurationMs[j-1] +=
+ (double)(timestamp - prevTimestamp)/(NUM_TEST_LOOPS * 1000000.0);
+ }
+ prevTimestamp = timestamp;
+ }
+
+ // simulate real scenario (preview runs a bit)
+ waitForNumResults(previewResultListener, NUM_RESULTS_WAIT);
+
+ stopPreview();
+ }
+
+ for (int i = 0; i < getResultTimes.length; i++) {
+ Log.v(TAG, "Camera " + id + " result time[" + i + "] is " +
+ getResultTimes[i] + " ms");
+ }
+ for (int i = 0; i < NUM_MAX_IMAGES-1; i++) {
+ Log.v(TAG, "Camera " + id + " frame duration time[" + i + "] is " +
+ frameDurationMs[i] + " ms");
+ }
+
+ mReportLog.addValues("camera_multiple_capture_result_latency", getResultTimes,
+ ResultType.LOWER_BETTER, ResultUnit.MS);
+ mReportLog.addValues("camera_multiple_capture_frame_duration", frameDurationMs,
+ ResultType.LOWER_BETTER, ResultUnit.MS);
+
+
+ avgResultTimes[counter] = Stat.getAverage(getResultTimes);
+ avgDurationMs[counter] = Stat.getAverage(frameDurationMs);
+ }
+ finally {
+ closeImageReader();
+ closeDevice();
+ }
+ counter++;
+ mReportLog.submit(getInstrumentation());
+ }
+
+ // Result will not be reported in CTS report if no summary is printed.
+ if (mCameraIds.length != 0) {
+ String streamName = "test_multiple_capture_average";
+ mReportLog = new DeviceReportLog(REPORT_LOG_NAME, streamName);
+ mReportLog.setSummary("camera_multiple_capture_result_average_latency_for_all_cameras",
+ Stat.getAverage(avgResultTimes), ResultType.LOWER_BETTER, ResultUnit.MS);
+ mReportLog.submit(getInstrumentation());
+ mReportLog = new DeviceReportLog(REPORT_LOG_NAME, streamName);
+ mReportLog.setSummary("camera_multiple_capture_frame_duration_average_for_all_cameras",
+ Stat.getAverage(avgDurationMs), ResultType.LOWER_BETTER, ResultUnit.MS);
+ mReportLog.submit(getInstrumentation());
+ }
+ }
+
+ /**
* Test reprocessing shot-to-shot latency with default NR and edge options, i.e., from the time
* a reprocess request is issued to the time the reprocess image is returned.
*/
diff --git a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
index d101036..d466662 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
@@ -1282,14 +1282,15 @@
duration, expectedDurationMs));
}
- // TODO: Don't skip this for video snapshot
- if (!mStaticInfo.isHardwareLevelLegacy()) {
- assertTrue(String.format(
- "Camera %s: Video duration doesn't match: recorded %fms, expected %fms.",
- mCamera.getId(), duration, expectedDurationMs),
- Math.abs(duration - expectedDurationMs) <
- DURATION_MARGIN * expectedDurationMs);
- }
+ // Do rest of validation only for better-than-LEGACY devices
+ if (mStaticInfo.isHardwareLevelLegacy()) return;
+
+ // TODO: Don't skip this one for video snapshot on LEGACY
+ assertTrue(String.format(
+ "Camera %s: Video duration doesn't match: recorded %fms, expected %fms.",
+ mCamera.getId(), duration, expectedDurationMs),
+ Math.abs(duration - expectedDurationMs) <
+ DURATION_MARGIN * expectedDurationMs);
// Check for framedrop
long lastSampleUs = 0;
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
index a95f4f3..feb0bc7 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
@@ -55,6 +55,7 @@
import com.android.ex.camera2.blocking.BlockingStateCallback;
import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
+import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -696,6 +697,27 @@
CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format,
CaptureCallback resultListener, int maxNumImages,
ImageReader.OnImageAvailableListener imageListener) throws Exception {
+ prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, captureSz,
+ format, resultListener, null, maxNumImages, imageListener);
+ }
+
+ /**
+ * Setup single capture configuration and start preview.
+ *
+ * @param previewRequest The capture request to be used for preview
+ * @param stillRequest The capture request to be used for still capture
+ * @param previewSz Preview size
+ * @param captureSz Still capture size
+ * @param format The single capture image format
+ * @param resultListener Capture result listener
+ * @param sessionListener Session listener
+ * @param maxNumImages The max number of images set to the image reader
+ * @param imageListener The single capture capture image listener
+ */
+ protected void prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
+ CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format,
+ CaptureCallback resultListener, CameraCaptureSession.StateCallback sessionListener,
+ int maxNumImages, ImageReader.OnImageAvailableListener imageListener) throws Exception {
if (VERBOSE) {
Log.v(TAG, String.format("Prepare single capture (%s) and preview (%s)",
captureSz.toString(), previewSz.toString()));
@@ -711,7 +733,11 @@
List<Surface> outputSurfaces = new ArrayList<Surface>();
outputSurfaces.add(mPreviewSurface);
outputSurfaces.add(mReaderSurface);
- mSessionListener = new BlockingSessionCallback();
+ if (sessionListener == null) {
+ mSessionListener = new BlockingSessionCallback();
+ } else {
+ mSessionListener = new BlockingSessionCallback(sessionListener);
+ }
mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
// Configure the requests.
@@ -791,4 +817,33 @@
}
return info.isCapabilitySupported(cap);
}
+
+ protected Range<Integer> getSuitableFpsRangeForDuration(String cameraId, long frameDuration) {
+ // Add 0.05 here so Fps like 29.99 evaluated to 30
+ int minBurstFps = (int) Math.floor(1e9 / frameDuration + 0.05f);
+ boolean foundConstantMaxYUVRange = false;
+ boolean foundYUVStreamingRange = false;
+
+ // Find suitable target FPS range - as high as possible that covers the max YUV rate
+ // Also verify that there's a good preview rate as well
+ List<Range<Integer> > fpsRanges = Arrays.asList(
+ mStaticInfo.getAeAvailableTargetFpsRangesChecked());
+ Range<Integer> targetRange = null;
+ for (Range<Integer> fpsRange : fpsRanges) {
+ if (fpsRange.getLower() == minBurstFps && fpsRange.getUpper() == minBurstFps) {
+ foundConstantMaxYUVRange = true;
+ targetRange = fpsRange;
+ }
+ if (fpsRange.getLower() <= 15 && fpsRange.getUpper() == minBurstFps) {
+ foundYUVStreamingRange = true;
+ }
+ }
+
+ assertTrue(String.format("Cam %s: Target FPS range of (%d, %d) must be supported",
+ cameraId, minBurstFps, minBurstFps), foundConstantMaxYUVRange);
+ assertTrue(String.format(
+ "Cam %s: Target FPS range of (x, %d) where x <= 15 must be supported",
+ cameraId, minBurstFps), foundYUVStreamingRange);
+ return targetRange;
+ }
}
diff --git a/tests/deviceadmin/uninstalltest/src/android/devicepolicy/cts/uiautomatertest/DeviceAdminUninstallTest.java b/tests/deviceadmin/uninstalltest/src/android/devicepolicy/cts/uiautomatertest/DeviceAdminUninstallTest.java
index a8e7cc1..d4f04de 100644
--- a/tests/deviceadmin/uninstalltest/src/android/devicepolicy/cts/uiautomatertest/DeviceAdminUninstallTest.java
+++ b/tests/deviceadmin/uninstalltest/src/android/devicepolicy/cts/uiautomatertest/DeviceAdminUninstallTest.java
@@ -15,6 +15,9 @@
*/
package android.devicepolicy.cts.uiautomatertest;
+import static android.content.Intent.CATEGORY_DEFAULT;
+import static android.provider.Settings.ACTION_SETTINGS;
+
import android.app.Activity;
import android.app.Instrumentation;
import android.app.admin.DevicePolicyManager;
@@ -26,6 +29,19 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiScrollable;
+import android.support.test.uiautomator.UiSelector;
+import android.support.test.uiautomator.Until;
+import android.util.Log;
import android.widget.ScrollView;
import org.junit.After;
@@ -38,18 +54,6 @@
import java.io.StringWriter;
import java.util.List;
import java.util.regex.Pattern;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.filters.LargeTest;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.BySelector;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiObjectNotFoundException;
-import android.support.test.uiautomator.UiScrollable;
-import android.support.test.uiautomator.UiSelector;
-import android.support.test.uiautomator.Until;
-import android.util.Log;
@RunWith(AndroidJUnit4.class)
@LargeTest
@@ -60,14 +64,6 @@
private static final String URI_PACKAGE_PREFIX = "package:";
private static final String UNINSTALL_BUTTON_TEXT_REGEX = "(?i)uninstall";
- private static final UiSelector DEACTIVATE_AND_UNINSTALL_BUTTON_SELECTOR = new UiSelector()
- .resourceId("com.android.settings:id/action_button");
- // Changing the below two selectors to match the button based on text due to b/29960172
- private static final UiSelector UNINSTALL_BUTTON_SELECTOR = new UiSelector()
- .clickable(true).textMatches(UNINSTALL_BUTTON_TEXT_REGEX);
- private static final BySelector UNINSTALL_BUTTON_BYSELECTOR = By
- .clickable(true).text(Pattern.compile(UNINSTALL_BUTTON_TEXT_REGEX));
-
private static final UiSelector OK_BUTTON_SELECTOR = new UiSelector()
.resourceId("android:id/button1");
private static final UiSelector SCROLL_VIEW_SELECTOR =
@@ -83,7 +79,10 @@
private DevicePolicyManager mDpm;
private PackageManager mPm;
private Activity mActivity;
+ private UiSelector mDeactivateAndUninstallButtonSelector;
+ private BySelector mUninstallButtonSelector;
private boolean mHasFeature;
+ private boolean mIsWatch;
@Before
public void setUp() throws Exception {
@@ -97,6 +96,17 @@
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mUiDevice = UiDevice.getInstance(mInstrumentation);
mDpm = mContext.getSystemService(DevicePolicyManager.class);
+ mIsWatch = mPm.hasSystemFeature(PackageManager.FEATURE_WATCH);
+ if (mIsWatch) {
+ mUninstallButtonSelector = By.clickable(true).hasDescendant(
+ By.text(Pattern.compile(UNINSTALL_BUTTON_TEXT_REGEX)));
+ } else {
+ mUninstallButtonSelector = By.clickable(true).text(
+ Pattern.compile(UNINSTALL_BUTTON_TEXT_REGEX));
+ }
+ mDeactivateAndUninstallButtonSelector = new UiSelector().resourceId(
+ getDefaultSettingsPackageName() + ":id/action_button");
+
startTestActivity();
}
@@ -158,15 +168,21 @@
private void automateUninstallThroughUi() {
String errorMessage = "No exception in UI Automation";
- UiObject uninstallButton = mUiDevice.findObject(UNINSTALL_BUTTON_SELECTOR);
UiScrollable scrollable = new UiScrollable(SCROLL_VIEW_SELECTOR);
- UiObject deactivateButton = mUiDevice.findObject(DEACTIVATE_AND_UNINSTALL_BUTTON_SELECTOR);
+ UiObject deactivateButton = mUiDevice.findObject(mDeactivateAndUninstallButtonSelector);
UiObject okButton = mUiDevice.findObject(OK_BUTTON_SELECTOR);
- mUiDevice.wait(Until.hasObject(UNINSTALL_BUTTON_BYSELECTOR), WAIT_FOR_ACTIVITY_TIMEOUT);
try {
- uninstallButton.clickAndWaitForNewWindow();
- scrollable.scrollIntoView(DEACTIVATE_AND_UNINSTALL_BUTTON_SELECTOR);
+ UiObject2 uninstallButton = mUiDevice.wait(
+ Until.findObject(mUninstallButtonSelector), WAIT_FOR_ACTIVITY_TIMEOUT);
+ uninstallButton.clickAndWait(Until.newWindow(), WAIT_FOR_ACTIVITY_TIMEOUT);
+
+ /** Watch specific: Extra confirm button click */
+ if (mIsWatch) {
+ okButton.clickAndWaitForNewWindow();
+ }
+
+ scrollable.scrollIntoView(mDeactivateAndUninstallButtonSelector);
deactivateButton.clickAndWaitForNewWindow();
waitTillNoActiveAdmin();
okButton.clickAndWaitForNewWindow();
@@ -185,6 +201,12 @@
return errorWriter.toString();
}
+ private String getDefaultSettingsPackageName() {
+ Intent intent = new Intent(ACTION_SETTINGS).addCategory(CATEGORY_DEFAULT);
+ String packageName = intent.resolveActivity(mPm).getPackageName();
+ return packageName;
+ }
+
@Test
public void uninstallPackageWithActiveAdmin() {
if (!mHasFeature) {
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index 1d83241..dbba163 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -1,5 +1,12 @@
[
{
+ description: "Disable ListeningPortsTest",
+ names: [
+ "android.security.cts.ListeningPortsTest"
+ ],
+ bug: 31803630
+},
+{
description: "some AlarmClockTests are not robust across different device types",
names: [
"android.alarmclock.cts.DismissAlarmTest#testAll",
diff --git a/tests/filesystem/src/android/filesystem/cts/FileUtil.java b/tests/filesystem/src/android/filesystem/cts/FileUtil.java
index fc8eee0..8149105 100755
--- a/tests/filesystem/src/android/filesystem/cts/FileUtil.java
+++ b/tests/filesystem/src/android/filesystem/cts/FileUtil.java
@@ -41,6 +41,8 @@
private static final String TAG = "FileUtil";
private static final Random mRandom = new Random(0);
private static long mFileId = 0;
+
+ public static final int BUFFER_SIZE = 10 * 1024 * 1024;
/**
* create array with different data per each call
*
@@ -140,7 +142,6 @@
*/
public static File createNewFilledFile(Context context, String dirName, long length)
throws IOException {
- final int BUFFER_SIZE = 10 * 1024 * 1024;
File file = createNewFile(context, dirName);
FileOutputStream out = new FileOutputStream(file);
byte[] data = generateRandomData(BUFFER_SIZE);
diff --git a/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java b/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java
index a2df743..7b97f8f 100644
--- a/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java
+++ b/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java
@@ -17,7 +17,7 @@
package android.filesystem.cts;
import android.cts.util.CtsAndroidTestCase;
-
+import android.os.Environment;
import com.android.compatibility.common.util.CddTest;
import com.android.compatibility.common.util.DeviceReportLog;
@@ -51,11 +51,17 @@
@CddTest(requirement="8.2")
public void testRandomUpdate() throws Exception {
final int WRITE_BUFFER_SIZE = 4 * 1024;
- final long fileSize = 256 * 1024 * 1024;
+ final long usableSpace = Environment.getDataDirectory().getUsableSpace();
+ long fileSize = 256 * 1024 * 1024;
+ while (usableSpace < fileSize) {
+ fileSize = fileSize / 2;
+ }
String streamName = "test_random_update";
DeviceReportLog report = new DeviceReportLog(REPORT_LOG_NAME, streamName);
- FileUtil.doRandomWriteTest(getContext(), DIR_RANDOM_WR, report, fileSize,
+ if (fileSize > FileUtil.BUFFER_SIZE) {
+ FileUtil.doRandomWriteTest(getContext(), DIR_RANDOM_WR, report, fileSize,
WRITE_BUFFER_SIZE);
+ }
report.submit(getInstrumentation());
}
}
diff --git a/tests/fragment/Android.mk b/tests/fragment/Android.mk
index 2352564..ab76caf 100644
--- a/tests/fragment/Android.mk
+++ b/tests/fragment/Android.mk
@@ -28,7 +28,14 @@
LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES += \
+ android-support-test \
+ mockito-target-minus-junit4 \
+ android-common \
+ ctsdeviceutil \
+ ctstestrunner \
+ platform-test-annotations
+#LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util android-support-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/app/app/res/layout/fragment_a.xml b/tests/fragment/res/animator/slow_fade_out.xml
similarity index 60%
copy from tests/app/app/res/layout/fragment_a.xml
copy to tests/fragment/res/animator/slow_fade_out.xml
index 38e0423..d8d2ad2 100644
--- a/tests/app/app/res/layout/fragment_a.xml
+++ b/tests/fragment/res/animator/slow_fade_out.xml
@@ -13,13 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/textA"
- android:text="@string/hello"/>
-</LinearLayout>
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="5000"
+ android:propertyName="alpha"
+ android:valueFrom="1.0"
+ android:valueTo="0.0"/>
diff --git a/tests/fragment/res/layout/scene1.xml b/tests/fragment/res/layout/scene1.xml
new file mode 100644
index 0000000..d0509c3
--- /dev/null
+++ b/tests/fragment/res/layout/scene1.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/squareContainer"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <View android:id="@+id/greenSquare"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:transitionName="greenSquare"
+ android:background="#080"/>
+ <View android:id="@+id/blueSquare"
+ android:transitionName="blueSquare"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#008"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/fragment/res/layout/scene2.xml b/tests/fragment/res/layout/scene2.xml
new file mode 100644
index 0000000..ef809a42
--- /dev/null
+++ b/tests/fragment/res/layout/scene2.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/squareContainer"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <View android:id="@+id/blueSquare"
+ android:transitionName="blueSquare"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#008"/>
+ <View android:id="@+id/greenSquare"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:transitionName="greenSquare"
+ android:background="#080"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/fragment/res/layout/scene3.xml b/tests/fragment/res/layout/scene3.xml
new file mode 100644
index 0000000..15519fd
--- /dev/null
+++ b/tests/fragment/res/layout/scene3.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/squareContainer"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <View android:id="@+id/redSquare"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#800"/>
+ <View android:id="@+id/blueSquare"
+ android:transitionName="shared"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#008"/>
+ <View android:id="@+id/greenSquare"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:transitionName="greenSquare"
+ android:background="#080"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/app/app/res/layout/fragment_a.xml b/tests/fragment/res/layout/text_a.xml
similarity index 61%
copy from tests/app/app/res/layout/fragment_a.xml
copy to tests/fragment/res/layout/text_a.xml
index 38e0423..b164469 100644
--- a/tests/app/app/res/layout/fragment_a.xml
+++ b/tests/fragment/res/layout/text_a.xml
@@ -13,13 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/textA"
- android:text="@string/hello"/>
-</LinearLayout>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/textA"/>
diff --git a/tests/app/app/res/layout/fragment_a.xml b/tests/fragment/res/layout/text_b.xml
similarity index 61%
copy from tests/app/app/res/layout/fragment_a.xml
copy to tests/fragment/res/layout/text_b.xml
index 38e0423..3d307fc 100644
--- a/tests/app/app/res/layout/fragment_a.xml
+++ b/tests/fragment/res/layout/text_b.xml
@@ -13,13 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/textA"
- android:text="@string/hello"/>
-</LinearLayout>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/textB"/>
diff --git a/tests/app/app/res/layout/fragment_a.xml b/tests/fragment/res/layout/text_c.xml
similarity index 61%
rename from tests/app/app/res/layout/fragment_a.xml
rename to tests/fragment/res/layout/text_c.xml
index 38e0423..e40b5ab 100644
--- a/tests/app/app/res/layout/fragment_a.xml
+++ b/tests/fragment/res/layout/text_c.xml
@@ -13,13 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/textA"
- android:text="@string/hello"/>
-</LinearLayout>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/textC"/>
diff --git a/tests/fragment/src/android/fragment/cts/CountCallsFragment.java b/tests/fragment/src/android/fragment/cts/CountCallsFragment.java
new file mode 100644
index 0000000..a262772
--- /dev/null
+++ b/tests/fragment/src/android/fragment/cts/CountCallsFragment.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 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 android.fragment.cts;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Counts the number of onCreateView, onHiddenChanged (onHide, onShow), onAttach, and onDetach
+ * calls.
+ */
+public class CountCallsFragment extends StrictViewFragment {
+ public int onCreateViewCount = 0;
+ public int onDestroyViewCount = 0;
+ public int onHideCount = 0;
+ public int onShowCount = 0;
+ public int onAttachCount = 0;
+ public int onDetachCount = 0;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ onCreateViewCount++;
+ return super.onCreateView(inflater, container, savedInstanceState);
+ }
+
+ @Override
+ public void onHiddenChanged(boolean hidden) {
+ if (hidden) {
+ onHideCount++;
+ } else {
+ onShowCount++;
+ }
+ super.onHiddenChanged(hidden);
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ onAttachCount++;
+ super.onAttach(context);
+ }
+
+ @Override
+ public void onDetach() {
+ onDetachCount++;
+ super.onDetach();
+ }
+
+ @Override
+ public void onDestroyView() {
+ onDestroyViewCount++;
+ super.onDestroyView();
+ }
+}
diff --git a/tests/fragment/src/android/fragment/cts/FragmentAnimatorTest.java b/tests/fragment/src/android/fragment/cts/FragmentAnimatorTest.java
index 2d75395..f0dac9a 100644
--- a/tests/fragment/src/android/fragment/cts/FragmentAnimatorTest.java
+++ b/tests/fragment/src/android/fragment/cts/FragmentAnimatorTest.java
@@ -18,6 +18,9 @@
import static junit.framework.Assert.*;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -26,14 +29,18 @@
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.app.Fragment;
+import android.app.FragmentController;
import android.app.FragmentManager;
+import android.app.FragmentManagerNonConfig;
import android.app.Instrumentation;
import android.os.Debug;
+import android.os.Parcelable;
import android.os.SystemClock;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
+import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
@@ -79,7 +86,7 @@
.add(R.id.fragmentContainer, fragment)
.addToBackStack(null)
.commit();
- FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.waitForExecution(mActivityRule);
assertEnterPopExit(fragment);
}
@@ -92,14 +99,14 @@
// One fragment with a view
final AnimatorFragment fragment = new AnimatorFragment();
fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
- FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.waitForExecution(mActivityRule);
fm.beginTransaction()
.setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
.remove(fragment)
.addToBackStack(null)
.commit();
- FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.waitForExecution(mActivityRule);
assertExitPopEnter(fragment);
}
@@ -112,14 +119,14 @@
// One fragment with a view
final AnimatorFragment fragment = new AnimatorFragment();
fm.beginTransaction().add(R.id.fragmentContainer, fragment).hide(fragment).commit();
- FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.waitForExecution(mActivityRule);
fm.beginTransaction()
.setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
.show(fragment)
.addToBackStack(null)
.commit();
- FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.waitForExecution(mActivityRule);
assertEnterPopExit(fragment);
}
@@ -132,14 +139,14 @@
// One fragment with a view
final AnimatorFragment fragment = new AnimatorFragment();
fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
- FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.waitForExecution(mActivityRule);
fm.beginTransaction()
.setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
.hide(fragment)
.addToBackStack(null)
.commit();
- FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.waitForExecution(mActivityRule);
assertExitPopEnter(fragment);
}
@@ -152,14 +159,14 @@
// One fragment with a view
final AnimatorFragment fragment = new AnimatorFragment();
fm.beginTransaction().add(R.id.fragmentContainer, fragment).detach(fragment).commit();
- FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.waitForExecution(mActivityRule);
fm.beginTransaction()
.setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
.attach(fragment)
.addToBackStack(null)
.commit();
- FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.waitForExecution(mActivityRule);
assertEnterPopExit(fragment);
}
@@ -172,14 +179,14 @@
// One fragment with a view
final AnimatorFragment fragment = new AnimatorFragment();
fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
- FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.waitForExecution(mActivityRule);
fm.beginTransaction()
.setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
.detach(fragment)
.addToBackStack(null)
.commit();
- FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.waitForExecution(mActivityRule);
assertExitPopEnter(fragment);
}
@@ -197,7 +204,7 @@
.add(R.id.fragmentContainer, fragment1, "1")
.add(R.id.fragmentContainer, fragment2, "2")
.commit();
- FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.waitForExecution(mActivityRule);
final AnimatorFragment fragment3 = new AnimatorFragment();
fm.beginTransaction()
@@ -205,16 +212,14 @@
.replace(R.id.fragmentContainer, fragment3)
.addToBackStack(null)
.commit();
- FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.waitForExecution(mActivityRule);
assertFragmentAnimation(fragment1, 1, false, EXIT);
assertFragmentAnimation(fragment2, 1, false, EXIT);
assertFragmentAnimation(fragment3, 1, true, ENTER);
- mInstrumentation.waitForIdleSync();
-
fm.popBackStack();
- FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.waitForExecution(mActivityRule);
assertFragmentAnimation(fragment3, 2, false, POP_EXIT);
final AnimatorFragment replacement1 = (AnimatorFragment) fm.findFragmentByTag("1");
@@ -224,24 +229,190 @@
assertFragmentAnimation(replacement2, expectedAnimations, true, POP_ENTER);
}
+ // Ensure that adding and popping a Fragment uses the enter and popExit animators,
+ // but the animators are delayed when an entering Fragment is postponed.
+ @Test
+ public void postponedAddAnimators() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+
+ final AnimatorFragment fragment = new AnimatorFragment();
+ fragment.postponeEnterTransition();
+ fm.beginTransaction()
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .add(R.id.fragmentContainer, fragment)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponed(fragment, 0);
+ fragment.startPostponedEnterTransition();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ assertEnterPopExit(fragment);
+ }
+
+ // Ensure that removing and popping a Fragment uses the exit and popEnter animators,
+ // but the animators are delayed when an entering Fragment is postponed.
+ @Test
+ public void postponedRemoveAnimators() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+
+ final AnimatorFragment fragment = new AnimatorFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ fm.beginTransaction()
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .remove(fragment)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertExitPostponedPopEnter(fragment);
+ }
+
+ // Ensure that adding and popping a Fragment is postponed in both directions
+ // when the fragments have been marked for postponing.
+ @Test
+ public void postponedAddRemove() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+
+ final AnimatorFragment fragment1 = new AnimatorFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ final AnimatorFragment fragment2 = new AnimatorFragment();
+ fragment2.postponeEnterTransition();
+
+ fm.beginTransaction()
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponed(fragment2, 0);
+ assertNotNull(fragment1.getView());
+ assertEquals(View.VISIBLE, fragment1.getView().getVisibility());
+ assertTrue(fragment1.getView().isAttachedToWindow());
+
+ fragment2.startPostponedEnterTransition();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertExitPostponedPopEnter(fragment1);
+ }
+
+ // Popping a postponed transaction should result in no animators
+ @Test
+ public void popPostponed() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+
+ final AnimatorFragment fragment1 = new AnimatorFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ assertEquals(0, fragment1.numAnimators);
+
+ final AnimatorFragment fragment2 = new AnimatorFragment();
+ fragment2.postponeEnterTransition();
+
+ fm.beginTransaction()
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponed(fragment2, 0);
+
+ // Now pop the postponed transaction
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ assertNotNull(fragment1.getView());
+ assertEquals(View.VISIBLE, fragment1.getView().getVisibility());
+ assertTrue(fragment1.getView().isAttachedToWindow());
+ assertTrue(fragment1.isAdded());
+
+ assertNull(fragment2.getView());
+ assertFalse(fragment2.isAdded());
+
+ assertEquals(0, fragment1.numAnimators);
+ assertEquals(0, fragment2.numAnimators);
+ assertNull(fragment1.animator);
+ assertNull(fragment2.animator);
+ }
+
+ // Make sure that if the state was saved while a Fragment was animating that its
+ // state is proper after restoring.
+ @Test
+ public void saveWhileAnimatingAway() throws Throwable {
+ final FragmentController fc1 = FragmentTestUtil.createController(mActivityRule);
+ FragmentTestUtil.resume(mActivityRule, fc1, null);
+
+ final FragmentManager fm1 = fc1.getFragmentManager();
+
+ StrictViewFragment fragment1 = new StrictViewFragment();
+ fragment1.setLayoutId(R.layout.scene1);
+ fm1.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1, "1")
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ StrictViewFragment fragment2 = new StrictViewFragment();
+
+ fm1.beginTransaction()
+ .setCustomAnimations(0, 0, 0, R.animator.slow_fade_out)
+ .replace(R.id.fragmentContainer, fragment2, "2")
+ .addToBackStack(null)
+ .commit();
+ mInstrumentation.runOnMainSync(fm1::executePendingTransactions);
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ fm1.popBackStack();
+
+ mInstrumentation.runOnMainSync(fm1::executePendingTransactions);
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ // Now fragment2 should be animating away
+ assertFalse(fragment2.isAdded());
+ assertEquals(fragment2, fm1.findFragmentByTag("2")); // still exists because it is animating
+
+ Pair<Parcelable, FragmentManagerNonConfig> state =
+ FragmentTestUtil.destroy(mActivityRule, fc1);
+
+ final FragmentController fc2 = FragmentTestUtil.createController(mActivityRule);
+ FragmentTestUtil.resume(mActivityRule, fc2, state);
+
+ final FragmentManager fm2 = fc2.getFragmentManager();
+ Fragment fragment2restored = fm2.findFragmentByTag("2");
+ assertNull(fragment2restored);
+
+ Fragment fragment1restored = fm2.findFragmentByTag("1");
+ assertNotNull(fragment1restored);
+ assertNotNull(fragment1restored.getView());
+ }
+
private void assertEnterPopExit(AnimatorFragment fragment) throws Throwable {
assertFragmentAnimation(fragment, 1, true, ENTER);
- mInstrumentation.waitForIdleSync();
final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
fm.popBackStack();
- FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.waitForExecution(mActivityRule);
assertFragmentAnimation(fragment, 2, false, POP_EXIT);
}
private void assertExitPopEnter(AnimatorFragment fragment) throws Throwable {
assertFragmentAnimation(fragment, 1, false, EXIT);
- mInstrumentation.waitForIdleSync();
final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
fm.popBackStack();
- FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.waitForExecution(mActivityRule);
AnimatorFragment replacement = (AnimatorFragment) fm.findFragmentByTag("1");
@@ -250,6 +421,19 @@
assertFragmentAnimation(replacement, expectedAnimators, true, POP_ENTER);
}
+ private void assertExitPostponedPopEnter(AnimatorFragment fragment) throws Throwable {
+ assertFragmentAnimation(fragment, 1, false, EXIT);
+
+ fragment.postponeEnterTransition();
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ assertPostponed(fragment, 1);
+
+ fragment.startPostponedEnterTransition();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ assertFragmentAnimation(fragment, 2, true, POP_ENTER);
+ }
+
private void assertFragmentAnimation(AnimatorFragment fragment, int numAnimators,
boolean isEnter, int animatorResourceId) throws InterruptedException {
assertEquals(numAnimators, fragment.numAnimators);
@@ -257,7 +441,14 @@
assertEquals(animatorResourceId, fragment.resourceId);
assertNotNull(fragment.animator);
assertTrue(fragment.wasStarted);
- assertTrue(fragment.endLatch.await(100, TimeUnit.MILLISECONDS));
+ assertTrue(fragment.endLatch.await(200, TimeUnit.MILLISECONDS));
+ }
+
+ private void assertPostponed(AnimatorFragment fragment, int expectedAnimators)
+ throws InterruptedException {
+ assertTrue(fragment.mOnCreateViewCalled);
+ assertEquals(View.INVISIBLE, fragment.getView().getVisibility());
+ assertEquals(expectedAnimators, fragment.numAnimators);
}
public static class AnimatorFragment extends StrictViewFragment {
diff --git a/tests/fragment/src/android/fragment/cts/FragmentExecuteTests.java b/tests/fragment/src/android/fragment/cts/FragmentExecuteTests.java
new file mode 100644
index 0000000..7d65b8c
--- /dev/null
+++ b/tests/fragment/src/android/fragment/cts/FragmentExecuteTests.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2016 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 android.fragment.cts;
+
+import static junit.framework.Assert.*;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.app.FragmentManager;
+import android.app.Instrumentation;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.ViewGroup;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class FragmentExecuteTests {
+ @Rule
+ public ActivityTestRule<FragmentTestActivity> mActivityRule =
+ new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
+
+ private Instrumentation mInstrumentation;
+
+ @Before
+ public void setupContentView() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ }
+
+ // Test that when executePendingBindings is called after something has been
+ // committed that it returns true and that the transaction was executed.
+ @Test
+ public void executeAndPopNormal() throws Throwable {
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment, "1")
+ .addToBackStack(null)
+ .commit();
+ assertTrue(fm.executePendingTransactions());
+ }
+ });
+
+ FragmentTestUtil.assertChildren(container, fragment);
+ assertEquals(1, fm.getBackStackEntryCount());
+ assertEquals(fragment, fm.findFragmentById(R.id.fragmentContainer));
+ assertEquals(fragment, fm.findFragmentByTag("1"));
+
+ assertTrue(FragmentTestUtil.popBackStackImmediate(mActivityRule));
+
+ FragmentTestUtil.assertChildren(container);
+ assertEquals(0, fm.getBackStackEntryCount());
+ assertNull(fm.findFragmentById(R.id.fragmentContainer));
+ assertNull(fm.findFragmentByTag("1"));
+ }
+
+ // Test that when executePendingBindings is called when nothing has been
+ // committed that it returns false and that the fragment manager is unchanged.
+ @Test
+ public void executeAndPopNothing() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+
+ assertEquals(0, fm.getBackStackEntryCount());
+ assertFalse(FragmentTestUtil.executePendingTransactions(mActivityRule));
+ assertEquals(0, fm.getBackStackEntryCount());
+ assertFalse(FragmentTestUtil.popBackStackImmediate(mActivityRule));
+ assertEquals(0, fm.getBackStackEntryCount());
+ }
+
+ // Test that when popBackStackImmediate is called when something is in the queue and
+ // there is a back stack to pop, it will execute both and return true.
+ @Test
+ public void popBackStackImmediateSomething() throws Throwable {
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment, "1")
+ .addToBackStack(null)
+ .commit();
+
+ assertTrue(FragmentTestUtil.popBackStackImmediate(mActivityRule));
+
+ FragmentTestUtil.assertChildren(container);
+ assertEquals(0, fm.getBackStackEntryCount());
+ assertNull(fm.findFragmentById(R.id.fragmentContainer));
+ assertNull(fm.findFragmentByTag("1"));
+ }
+
+ // Test that when popBackStackImmediate is called when something is in the queue and
+ // there is no back stack to pop, it will execute the thing in the queue and
+ // return false.
+ @Test
+ public void popBackStackImmediateNothing() throws Throwable {
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment, "1")
+ .commit();
+
+ assertFalse(FragmentTestUtil.popBackStackImmediate(mActivityRule));
+
+ FragmentTestUtil.assertChildren(container, fragment);
+ assertEquals(0, fm.getBackStackEntryCount());
+ assertEquals(fragment, fm.findFragmentById(R.id.fragmentContainer));
+ assertEquals(fragment, fm.findFragmentByTag("1"));
+ }
+
+ // Test popBackStackImmediate(int, int)
+ @Test
+ public void popBackStackImmediateInt() throws Throwable {
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+
+ final int commit1 = fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1, "1")
+ .addToBackStack(null)
+ .commit();
+
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment2, "2")
+ .addToBackStack(null)
+ .commit();
+
+ final StrictViewFragment fragment3 = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment3, "3")
+ .addToBackStack(null)
+ .commit();
+
+ assertTrue(FragmentTestUtil.popBackStackImmediate(mActivityRule, commit1, 0));
+ assertFalse(fragment2.isAdded());
+ assertTrue(fragment2.mCalledOnDestroy || !fragment2.mCalledOnCreate);
+ assertFalse(fragment3.isAdded());
+ assertTrue(fragment3.mCalledOnDestroy || !fragment3.mCalledOnCreate);
+
+ assertFalse(FragmentTestUtil.popBackStackImmediate(mActivityRule, commit1, 0));
+
+ FragmentTestUtil.assertChildren(container, fragment1);
+ assertEquals(1, fm.getBackStackEntryCount());
+ assertEquals(fragment1, fm.findFragmentById(R.id.fragmentContainer));
+ assertEquals(fragment1, fm.findFragmentByTag("1"));
+
+ final StrictViewFragment fragment4 = new StrictViewFragment();
+ final int commit4 = fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment4, "4")
+ .addToBackStack(null)
+ .commit();
+
+ final StrictViewFragment fragment5 = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment5, "5")
+ .addToBackStack(null)
+ .commit();
+
+ assertTrue(FragmentTestUtil.popBackStackImmediate(mActivityRule, commit4,
+ FragmentManager.POP_BACK_STACK_INCLUSIVE));
+ assertFalse(fragment4.isAdded());
+ assertTrue(fragment4.mCalledOnDestroy || !fragment4.mCalledOnCreate);
+ assertFalse(fragment5.isAdded());
+ assertTrue(fragment5.mCalledOnDestroy || !fragment5.mCalledOnCreate);
+
+ assertFalse(FragmentTestUtil.popBackStackImmediate(mActivityRule, commit4,
+ FragmentManager.POP_BACK_STACK_INCLUSIVE));
+
+ FragmentTestUtil.assertChildren(container, fragment1);
+ assertEquals(1, fm.getBackStackEntryCount());
+ assertEquals(fragment1, fm.findFragmentById(R.id.fragmentContainer));
+ assertEquals(fragment1, fm.findFragmentByTag("1"));
+ }
+
+ // Test popBackStackImmediate(String, int)
+ @Test
+ public void popBackStackImmediateString() throws Throwable {
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1, "1")
+ .addToBackStack("1")
+ .commit();
+
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment2, "2")
+ .addToBackStack("2")
+ .commit();
+
+ final StrictViewFragment fragment3 = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment3, "3")
+ .addToBackStack("3")
+ .commit();
+
+ assertTrue(FragmentTestUtil.popBackStackImmediate(mActivityRule, "1", 0));
+ assertFalse(fragment2.isAdded());
+ assertTrue(fragment2.mCalledOnDestroy || !fragment2.mCalledOnCreate);
+ assertFalse(fragment3.isAdded());
+ assertTrue(fragment3.mCalledOnDestroy || !fragment3.mCalledOnCreate);
+
+ assertFalse(FragmentTestUtil.popBackStackImmediate(mActivityRule, "1", 0));
+
+ FragmentTestUtil.assertChildren(container, fragment1);
+ assertEquals(1, fm.getBackStackEntryCount());
+ assertEquals(fragment1, fm.findFragmentById(R.id.fragmentContainer));
+ assertEquals(fragment1, fm.findFragmentByTag("1"));
+
+ final StrictViewFragment fragment4 = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment4, "4")
+ .addToBackStack("4")
+ .commit();
+
+ final StrictViewFragment fragment5 = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment5, "5")
+ .addToBackStack("5")
+ .commit();
+
+ assertTrue(FragmentTestUtil.popBackStackImmediate(mActivityRule, "4",
+ FragmentManager.POP_BACK_STACK_INCLUSIVE));
+ assertFalse(fragment4.isAdded());
+ assertTrue(fragment4.mCalledOnDestroy || !fragment4.mCalledOnCreate);
+ assertFalse(fragment5.isAdded());
+ assertTrue(fragment5.mCalledOnDestroy || !fragment5.mCalledOnCreate);
+
+ assertFalse(FragmentTestUtil.popBackStackImmediate(mActivityRule, "4",
+ FragmentManager.POP_BACK_STACK_INCLUSIVE));
+
+ FragmentTestUtil.assertChildren(container, fragment1);
+ assertEquals(1, fm.getBackStackEntryCount());
+ assertEquals(fragment1, fm.findFragmentById(R.id.fragmentContainer));
+ assertEquals(fragment1, fm.findFragmentByTag("1"));
+ }
+
+}
diff --git a/tests/fragment/src/android/fragment/cts/FragmentLifecycleTest.java b/tests/fragment/src/android/fragment/cts/FragmentLifecycleTest.java
index 01c4e70..89812c3 100644
--- a/tests/fragment/src/android/fragment/cts/FragmentLifecycleTest.java
+++ b/tests/fragment/src/android/fragment/cts/FragmentLifecycleTest.java
@@ -17,18 +17,40 @@
package android.fragment.cts;
+import android.app.FragmentController;
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentController;
+import android.app.FragmentHostCallback;
import android.app.FragmentManager;
+import android.app.FragmentManager.FragmentLifecycleCallbacks;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Debug;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.MediumTest;
+import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
+
+import android.view.Window;
+import android.widget.TextView;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
import static junit.framework.Assert.assertNotNull;
import static junit.framework.TestCase.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+@MediumTest
@RunWith(AndroidJUnit4.class)
public class FragmentLifecycleTest {
@@ -37,7 +59,6 @@
new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
@Test
- @MediumTest
public void basicLifecycle() throws Throwable {
final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
final StrictFragment strictFragment = new StrictFragment();
@@ -65,7 +86,6 @@
}
@Test
- @MediumTest
public void detachment() throws Throwable {
final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
final StrictFragment f1 = new StrictFragment();
@@ -105,7 +125,6 @@
}
@Test
- @MediumTest
public void basicBackStack() throws Throwable {
final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
final StrictFragment f1 = new StrictFragment();
@@ -136,7 +155,6 @@
}
@Test
- @MediumTest
public void attachBackStack() throws Throwable {
final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
final StrictFragment f1 = new StrictFragment();
@@ -158,7 +176,6 @@
}
@Test
- @MediumTest
public void viewLifecycle() throws Throwable {
// Test basic lifecycle when the fragment creates a view
@@ -183,7 +200,6 @@
}
@Test
- @MediumTest
public void viewReplace() throws Throwable {
// Replace one view with another, then reverse it with the back stack
@@ -223,6 +239,160 @@
assertTrue("fragment 1's view not attached", newView1.isAttachedToWindow());
}
+ @Test
+ public void viewReplaceMultiple() throws Throwable {
+ // Replace several views with one, then reverse it with the back stack
+
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment f1 = new StrictViewFragment();
+ final StrictViewFragment f2 = new StrictViewFragment();
+ final StrictViewFragment f3 = new StrictViewFragment();
+
+ fm.beginTransaction().add(android.R.id.content, f1).commit();
+ fm.beginTransaction().add(android.R.id.content, f2).commit();
+ executePendingTransactions(fm);
+
+ assertTrue("fragment 1 is not added", f1.isAdded());
+ assertTrue("fragment 2 is not added", f2.isAdded());
+
+ View origView1 = f1.getView();
+ assertNotNull("fragment 1 returned null view", origView1);
+ assertTrue("fragment 1's view not attached", origView1.isAttachedToWindow());
+ assertSame(origView1, ((ViewGroup)origView1.getParent()).getChildAt(0));
+
+ View origView2 = f2.getView();
+ assertNotNull("fragment 2 returned null view", origView2);
+ assertTrue("fragment 2's view not attached", origView2.isAttachedToWindow());
+ assertSame(origView2, ((ViewGroup)origView1.getParent()).getChildAt(1));
+
+ fm.beginTransaction().replace(android.R.id.content, f3).addToBackStack("stack1").commit();
+ executePendingTransactions(fm);
+
+ assertFalse("fragment 1 is added", f1.isAdded());
+ assertFalse("fragment 2 is added", f2.isAdded());
+ assertTrue("fragment 3 is added", f3.isAdded());
+ assertNull("fragment 1 returned non-null view", f1.getView());
+ assertNull("fragment 2 returned non-null view", f2.getView());
+ assertFalse("fragment 1's old view still attached", origView1.isAttachedToWindow());
+ assertFalse("fragment 2's old view still attached", origView2.isAttachedToWindow());
+ View origView3 = f3.getView();
+ assertNotNull("fragment 3 returned null view", origView3);
+ assertTrue("fragment 3's view not attached", origView3.isAttachedToWindow());
+
+ fm.popBackStack();
+ executePendingTransactions(fm);
+
+ assertTrue("fragment 1 is not added", f1.isAdded());
+ assertTrue("fragment 2 is not added", f2.isAdded());
+ assertFalse("fragment 3 is added", f3.isAdded());
+ assertNull("fragment 3 returned non-null view", f3.getView());
+ assertFalse("fragment 3's view still attached", origView3.isAttachedToWindow());
+ View newView1 = f1.getView();
+ View newView2 = f2.getView();
+ assertNotSame("fragment 1 had same view from last attachment", origView1, newView1);
+ assertNotSame("fragment 2 had same view from last attachment", origView2, newView1);
+ assertTrue("fragment 1's view not attached", newView1.isAttachedToWindow());
+ assertTrue("fragment 2's view not attached", newView2.isAttachedToWindow());
+ assertSame(newView1, ((ViewGroup)newView1.getParent()).getChildAt(0));
+ assertSame(newView2, ((ViewGroup)newView1.getParent()).getChildAt(1));
+ }
+
+ /**
+ * This tests that fragments call onDestroy when the activity finishes.
+ */
+ @Test
+ public void fragmentDestroyedOnFinish() throws Throwable {
+ final FragmentController fc = FragmentTestUtil.createController(mActivityRule);
+ FragmentTestUtil.resume(mActivityRule, fc, null);
+ final StrictViewFragment fragmentA = StrictViewFragment.create(R.layout.text_a);
+ final StrictViewFragment fragmentB = StrictViewFragment.create(R.layout.text_b);
+ mActivityRule.runOnUiThread(() -> {
+ FragmentManager fm = fc.getFragmentManager();
+
+ fm.beginTransaction()
+ .add(android.R.id.content, fragmentA)
+ .commit();
+ fm.executePendingTransactions();
+ fm.beginTransaction()
+ .replace(android.R.id.content, fragmentB)
+ .addToBackStack(null)
+ .commit();
+ fm.executePendingTransactions();
+ });
+ FragmentTestUtil.destroy(mActivityRule, fc);
+ assertTrue(fragmentB.mCalledOnDestroy);
+ assertTrue(fragmentA.mCalledOnDestroy);
+ }
+
+ /**
+ * This test confirms that as long as a parent fragment has called super.onCreate,
+ * any child fragments added, committed and with transactions executed will be brought
+ * to at least the CREATED state by the time the parent fragment receives onCreateView.
+ * This means the child fragment will have received onAttach/onCreate.
+ */
+ @Test
+ @MediumTest
+ public void childFragmentManagerAttach() throws Throwable {
+ mActivityRule.runOnUiThread(new Runnable() {
+ public void run() {
+ FragmentController fc = FragmentController.createController(
+ new HostCallbacks(mActivityRule.getActivity()));
+ fc.attachHost(null);
+ fc.dispatchCreate();
+
+ FragmentLifecycleCallbacks mockLc = mock(FragmentLifecycleCallbacks.class);
+ FragmentLifecycleCallbacks mockRecursiveLc = mock(FragmentLifecycleCallbacks.class);
+
+ FragmentManager fm = fc.getFragmentManager();
+ fm.registerFragmentLifecycleCallbacks(mockLc, false);
+ fm.registerFragmentLifecycleCallbacks(mockRecursiveLc, true);
+
+ ChildFragmentManagerFragment fragment = new ChildFragmentManagerFragment();
+ fm.beginTransaction()
+ .add(android.R.id.content, fragment)
+ .commitNow();
+
+ verify(mockLc, times(1)).onFragmentCreated(fm, fragment, null);
+
+ fc.dispatchActivityCreated();
+
+ Fragment childFragment = fragment.getChildFragment();
+
+ verify(mockLc, times(1)).onFragmentActivityCreated(fm, fragment, null);
+ verify(mockRecursiveLc, times(1)).onFragmentActivityCreated(fm, fragment, null);
+ verify(mockRecursiveLc, times(1)).onFragmentActivityCreated(fm, childFragment, null);
+
+ fc.dispatchStart();
+
+ verify(mockLc, times(1)).onFragmentStarted(fm, fragment);
+ verify(mockRecursiveLc, times(1)).onFragmentStarted(fm, fragment);
+ verify(mockRecursiveLc, times(1)).onFragmentStarted(fm, childFragment);
+
+ fc.dispatchResume();
+
+ verify(mockLc, times(1)).onFragmentResumed(fm, fragment);
+ verify(mockRecursiveLc, times(1)).onFragmentResumed(fm, fragment);
+ verify(mockRecursiveLc, times(1)).onFragmentResumed(fm, childFragment);
+
+ // Confirm that the parent fragment received onAttachFragment
+ assertTrue("parent fragment did not receive onAttachFragment",
+ fragment.mCalledOnAttachFragment);
+
+ fc.dispatchStop();
+
+ verify(mockLc, times(1)).onFragmentStopped(fm, fragment);
+ verify(mockRecursiveLc, times(1)).onFragmentStopped(fm, fragment);
+ verify(mockRecursiveLc, times(1)).onFragmentStopped(fm, childFragment);
+
+ fc.dispatchDestroy();
+
+ verify(mockLc, times(1)).onFragmentDestroyed(fm, fragment);
+ verify(mockRecursiveLc, times(1)).onFragmentDestroyed(fm, fragment);
+ verify(mockRecursiveLc, times(1)).onFragmentDestroyed(fm, childFragment);
+ }
+ });
+ }
+
private void executePendingTransactions(final FragmentManager fm) throws Throwable {
mActivityRule.runOnUiThread(new Runnable() {
@Override
@@ -231,4 +401,135 @@
}
});
}
+
+ /**
+ * This tests a deliberately odd use of a child fragment, added in onCreateView instead
+ * of elsewhere. It simulates creating a UI child fragment added to the view hierarchy
+ * created by this fragment.
+ */
+ public static class ChildFragmentManagerFragment extends StrictFragment {
+ private FragmentManager mSavedChildFragmentManager;
+ private ChildFragmentManagerChildFragment mChildFragment;
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ mSavedChildFragmentManager = getChildFragmentManager();
+ }
+
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ assertSame("child FragmentManagers not the same instance", mSavedChildFragmentManager,
+ getChildFragmentManager());
+ ChildFragmentManagerChildFragment child =
+ (ChildFragmentManagerChildFragment) mSavedChildFragmentManager
+ .findFragmentByTag("tag");
+ if (child == null) {
+ child = new ChildFragmentManagerChildFragment("foo");
+ mSavedChildFragmentManager.beginTransaction()
+ .add(child, "tag")
+ .commitNow();
+ assertEquals("argument strings don't match", "foo", child.getString());
+ }
+ mChildFragment = child;
+ return new TextView(container.getContext());
+ }
+
+
+ public Fragment getChildFragment() {
+ return mChildFragment;
+ }
+ }
+
+ public static class ChildFragmentManagerChildFragment extends StrictFragment {
+ private String mString;
+
+ public ChildFragmentManagerChildFragment() {
+ }
+
+ public ChildFragmentManagerChildFragment(String arg) {
+ final Bundle b = new Bundle();
+ b.putString("string", arg);
+ setArguments(b);
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ mString = getArguments().getString("string", "NO VALUE");
+ }
+
+ public String getString() {
+ return mString;
+ }
+ }
+
+ static class HostCallbacks extends FragmentHostCallback<Activity> {
+ private final Activity mActivity;
+
+ public HostCallbacks(Activity activity) {
+ super(activity, null, 0);
+ mActivity = activity;
+ }
+
+ @Override
+ public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ }
+
+ @Override
+ public boolean onShouldSaveFragmentState(Fragment fragment) {
+ return !mActivity.isFinishing();
+ }
+
+ @Override
+ public LayoutInflater onGetLayoutInflater() {
+ return mActivity.getLayoutInflater().cloneInContext(mActivity);
+ }
+
+ @Override
+ public Activity onGetHost() {
+ return mActivity;
+ }
+
+ @Override
+ public void onStartActivityFromFragment(
+ Fragment fragment, Intent intent, int requestCode, Bundle options) {
+ mActivity.startActivityFromFragment(fragment, intent, requestCode, options);
+ }
+
+ @Override
+ public void onRequestPermissionsFromFragment( Fragment fragment,
+ String[] permissions, int requestCode) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean onHasWindowAnimations() {
+ return mActivity.getWindow() != null;
+ }
+
+ @Override
+ public int onGetWindowAnimations() {
+ final Window w = mActivity.getWindow();
+ return (w == null) ? 0 : w.getAttributes().windowAnimations;
+ }
+
+ @Override
+ public void onAttachFragment(Fragment fragment) {
+ mActivity.onAttachFragment(fragment);
+ }
+
+ @Override
+ public View onFindViewById(int id) {
+ return mActivity.findViewById(id);
+ }
+
+ @Override
+ public boolean onHasView() {
+ final Window w = mActivity.getWindow();
+ return (w != null && w.peekDecorView() != null);
+ }
+ }
}
diff --git a/tests/fragment/src/android/fragment/cts/FragmentOptimizationTest.java b/tests/fragment/src/android/fragment/cts/FragmentOptimizationTest.java
new file mode 100644
index 0000000..253bbcb
--- /dev/null
+++ b/tests/fragment/src/android/fragment/cts/FragmentOptimizationTest.java
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2016 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 android.fragment.cts;
+
+import static org.junit.Assert.*;
+
+import android.app.FragmentManager;
+import android.app.Instrumentation;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.ViewGroup;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class FragmentOptimizationTest {
+ @Rule
+ public ActivityTestRule<FragmentTestActivity> mActivityRule =
+ new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
+
+ private ViewGroup mContainer;
+ private FragmentManager mFM;
+ private Instrumentation mInstrumentation;
+
+ @Before
+ public void setup() {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ mContainer = (ViewGroup) mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ mFM = mActivityRule.getActivity().getFragmentManager();
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ }
+
+ // Test that when you add and replace a fragment that only the replace's add
+ // actually creates a View.
+ @Test
+ public void addReplace() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction().add(R.id.fragmentContainer, fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction()
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+ mFM.executePendingTransactions();
+ }
+ });
+ assertEquals(0, fragment1.onCreateViewCount);
+ FragmentTestUtil.assertChildren(mContainer, fragment2);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.popBackStack();
+ mFM.popBackStack();
+ mFM.executePendingTransactions();
+ }
+ });
+ FragmentTestUtil.assertChildren(mContainer);
+ }
+
+ // Test that it is possible to merge a transaction that starts with pop and adds
+ // the same view back again.
+ @Test
+ public void startWithPop() throws Throwable {
+ // Start with a single fragment on the back stack
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ mFM.beginTransaction().add(R.id.fragmentContainer, fragment1).addToBackStack(null).commit();
+
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+ // Now pop and add
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.popBackStack();
+ mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.executePendingTransactions();
+ }
+ });
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+ assertEquals(1, fragment1.onCreateViewCount);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+ FragmentTestUtil.assertChildren(mContainer);
+ assertEquals(1, fragment1.onCreateViewCount);
+ }
+
+ // Popping the back stack in the middle of other operations doesn't fool it.
+ @Test
+ public void middlePop() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ final CountCallsFragment fragment2 = new CountCallsFragment();
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.popBackStack();
+ mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+ mFM.executePendingTransactions();
+ }
+ });
+ FragmentTestUtil.assertChildren(mContainer, fragment2);
+ assertEquals(0, fragment1.onAttachCount);
+ assertEquals(1, fragment2.onCreateViewCount);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+ FragmentTestUtil.assertChildren(mContainer);
+ assertEquals(1, fragment2.onDetachCount);
+ }
+
+ // ensure that removing a view after adding it is optimized into no
+ // View being created. Hide still gets notified.
+ @Test
+ public void optimizeRemove() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ final int[] id = new int[1];
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ id[0] = mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().remove(fragment1).addToBackStack(null).commit();
+ mFM.executePendingTransactions();
+ }
+ });
+ FragmentTestUtil.assertChildren(mContainer);
+ assertEquals(0, fragment1.onCreateViewCount);
+ assertEquals(1, fragment1.onHideCount);
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(0, fragment1.onDetachCount);
+ assertEquals(0, fragment1.onAttachCount);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule, id[0],
+ FragmentManager.POP_BACK_STACK_INCLUSIVE);
+ FragmentTestUtil.assertChildren(mContainer);
+ assertEquals(0, fragment1.onCreateViewCount);
+ assertEquals(1, fragment1.onHideCount);
+ assertEquals(1, fragment1.onShowCount);
+ assertEquals(0, fragment1.onDetachCount);
+ assertEquals(0, fragment1.onAttachCount);
+ }
+
+ // Ensure that removing and adding the same view results in no operation
+ @Test
+ public void optimizeAdd() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ int id = mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertEquals(1, fragment1.onCreateViewCount);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction()
+ .remove(fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.executePendingTransactions();
+ }
+ });
+
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+ // should be optimized out
+ assertEquals(1, fragment1.onCreateViewCount);
+
+ mFM.popBackStack(id, 0);
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+ // optimize out going back, too
+ assertEquals(1, fragment1.onCreateViewCount);
+ }
+
+ // detaching, then attaching results in on change. Hide still functions
+ @Test
+ public void optimizeAttach() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ int id = mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertEquals(1, fragment1.onAttachCount);
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction().detach(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().attach(fragment1).addToBackStack(null).commit();
+ mFM.executePendingTransactions();
+ }
+ });
+
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+ // can optimize out the detach/attach
+ assertEquals(0, fragment1.onDestroyViewCount);
+ assertEquals(1, fragment1.onHideCount);
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(1, fragment1.onCreateViewCount);
+ assertEquals(1, fragment1.onAttachCount);
+ assertEquals(0, fragment1.onDetachCount);
+
+ mFM.popBackStack(id, 0);
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+ // optimized out again, but not the show
+ assertEquals(0, fragment1.onDestroyViewCount);
+ assertEquals(1, fragment1.onHideCount);
+ assertEquals(1, fragment1.onShowCount);
+ assertEquals(1, fragment1.onCreateViewCount);
+ assertEquals(1, fragment1.onAttachCount);
+ assertEquals(0, fragment1.onDetachCount);
+ }
+
+ // attaching, then detaching shouldn't result in a View being created
+ @Test
+ public void optimizeDetach() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ int id = mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .detach(fragment1)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ // the add detach is not fully optimized out
+ assertEquals(1, fragment1.onAttachCount);
+ assertEquals(0, fragment1.onDetachCount);
+ assertTrue(fragment1.isDetached());
+ assertEquals(0, fragment1.onCreateViewCount);
+ FragmentTestUtil.assertChildren(mContainer);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction().attach(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().detach(fragment1).addToBackStack(null).commit();
+ mFM.executePendingTransactions();
+ }
+ });
+
+ FragmentTestUtil.assertChildren(mContainer);
+ // can optimize out the attach/detach, and the hide call
+ assertEquals(1, fragment1.onAttachCount);
+ assertEquals(0, fragment1.onDetachCount);
+ assertEquals(1, fragment1.onHideCount);
+ assertTrue(fragment1.isHidden());
+ assertEquals(0, fragment1.onShowCount);
+
+ mFM.popBackStack(id, 0);
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(mContainer);
+
+ // we can optimize out the attach/detach on the way back
+ assertEquals(1, fragment1.onAttachCount);
+ assertEquals(0, fragment1.onDetachCount);
+ assertEquals(1, fragment1.onShowCount);
+ assertEquals(1, fragment1.onHideCount);
+ assertFalse(fragment1.isHidden());
+ }
+
+ // show, then hide should optimize out
+ @Test
+ public void optimizeHide() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ int id = mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .hide(fragment1)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(1, fragment1.onHideCount);
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction()
+ .show(fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.beginTransaction()
+ .remove(fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.beginTransaction()
+ .hide(fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.executePendingTransactions();
+ }
+ });
+
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+ // optimize out hide/show
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(1, fragment1.onHideCount);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule, id, 0);
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+ // still optimized out
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(1, fragment1.onHideCount);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction().show(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
+ mFM.executePendingTransactions();
+ }
+ });
+
+ // The show/hide can be optimized out and nothing should change.
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(1, fragment1.onHideCount);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule, id, 0);
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(1, fragment1.onHideCount);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction().show(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().detach(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().attach(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
+ mFM.executePendingTransactions();
+ }
+ });
+
+ // the detach/attach should not affect the show/hide, so show/hide should cancel each other
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(1, fragment1.onHideCount);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule, id, 0);
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(1, fragment1.onHideCount);
+ }
+
+ // hiding and showing the same view should optimize out
+ @Test
+ public void optimizeShow() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ int id = mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(0, fragment1.onHideCount);
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().detach(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().attach(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().show(fragment1).addToBackStack(null).commit();
+ mFM.executePendingTransactions();
+ }
+ });
+
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+ // can optimize out the show/hide
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(0, fragment1.onHideCount);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule, id,
+ FragmentManager.POP_BACK_STACK_INCLUSIVE);
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(0, fragment1.onHideCount);
+ }
+
+ // The View order shouldn't be messed up by optimization -- a view that
+ // is optimized to not remove/add should be in its correct position after
+ // the transaction completes.
+ @Test
+ public void viewOrder() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ int id = mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+ final CountCallsFragment fragment2 = new CountCallsFragment();
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction()
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+ mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+
+ mFM.executePendingTransactions();
+ }
+ });
+ FragmentTestUtil.assertChildren(mContainer, fragment2, fragment1);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule, id, 0);
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+ }
+
+ // Popping an added transaction results in no operation
+ @Test
+ public void addPopBackStack() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.popBackStack();
+ mFM.executePendingTransactions();
+ }
+ });
+ FragmentTestUtil.assertChildren(mContainer);
+
+ // Was never instantiated because it was popped before anything could happen
+ assertEquals(0, fragment1.onCreateViewCount);
+ }
+
+ // A non-back-stack transaction doesn't interfere with back stack add/pop
+ // optimization.
+ @Test
+ public void popNonBackStack() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ final CountCallsFragment fragment2 = new CountCallsFragment();
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.beginTransaction()
+ .replace(R.id.fragmentContainer, fragment2)
+ .commit();
+ mFM.popBackStack();
+ mFM.executePendingTransactions();
+ }
+ });
+ FragmentTestUtil.assertChildren(mContainer, fragment2);
+
+ // It should be optimized with the replace, so no View creation
+ assertEquals(0, fragment1.onCreateViewCount);
+ }
+
+ // When optimization is disabled, the transaction prior to the disabled optimization
+ // transaction should all be run prior to running the non-optimized transaction.
+ @Test
+ public void noOptimization() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ final CountCallsFragment fragment2 = new CountCallsFragment();
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.beginTransaction()
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .setAllowOptimization(false)
+ .commit();
+ mFM.executePendingTransactions();
+ }
+ });
+ FragmentTestUtil.assertChildren(mContainer, fragment2);
+
+ // No optimization, so fragment1 should have created its View
+ assertEquals(1, fragment1.onCreateViewCount);
+ }
+
+}
diff --git a/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java b/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java
index c83d4fa..b4407a0 100644
--- a/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java
+++ b/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java
@@ -15,20 +15,80 @@
*/
package android.fragment.cts;
+import static org.junit.Assert.assertEquals;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentController;
+import android.app.FragmentManagerNonConfig;
import android.app.Instrumentation;
+import android.os.Handler;
+import android.os.Parcelable;
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;
+import android.util.Pair;
+import android.view.ViewGroup;
public class FragmentTestUtil {
- public static void executePendingTransactions(final ActivityTestRule<FragmentTestActivity> rule)
- throws Throwable {
+ public static void waitForExecution(final ActivityTestRule<FragmentTestActivity> rule) {
+ // Wait for two cycles. When starting a postponed transition, it will post to
+ // the UI thread and then the execution will be added onto the queue after that.
+ // The two-cycle wait makes sure fragments have the opportunity to complete both
+ // before returning.
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ instrumentation.runOnMainSync(() -> {});
+ instrumentation.runOnMainSync(() -> {});
+ }
+
+ public static boolean executePendingTransactions(
+ final ActivityTestRule<FragmentTestActivity> rule) {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ final boolean[] ret = new boolean[1];
instrumentation.runOnMainSync(new Runnable() {
@Override
public void run() {
- rule.getActivity().getFragmentManager().executePendingTransactions();
+ ret[0] = rule.getActivity().getFragmentManager().executePendingTransactions();
}
});
+ return ret[0];
+ }
+
+ public static boolean popBackStackImmediate(final ActivityTestRule<FragmentTestActivity> rule) {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ final boolean[] ret = new boolean[1];
+ instrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ ret[0] = rule.getActivity().getFragmentManager().popBackStackImmediate();
+ }
+ });
+ return ret[0];
+ }
+
+ public static boolean popBackStackImmediate(final ActivityTestRule<FragmentTestActivity> rule,
+ final int id, final int flags) {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ final boolean[] ret = new boolean[1];
+ instrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ ret[0] = rule.getActivity().getFragmentManager().popBackStackImmediate(id, flags);
+ }
+ });
+ return ret[0];
+ }
+
+ public static boolean popBackStackImmediate(final ActivityTestRule<FragmentTestActivity> rule,
+ final String name, final int flags) {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ final boolean[] ret = new boolean[1];
+ instrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ ret[0] = rule.getActivity().getFragmentManager().popBackStackImmediate(name, flags);
+ }
+ });
+ return ret[0];
}
public static void setContentView(final ActivityTestRule<FragmentTestActivity> rule,
@@ -41,4 +101,63 @@
}
});
}
+
+ public static void assertChildren(ViewGroup container, Fragment... fragments) {
+ final int numFragments = fragments == null ? 0 : fragments.length;
+ assertEquals("There aren't the correct number of fragment Views in its container",
+ numFragments, container.getChildCount());
+ for (int i = 0; i < numFragments; i++) {
+ assertEquals("Wrong Fragment View order for [" + i + "]", container.getChildAt(i),
+ fragments[i].getView());
+ }
+ }
+
+ public static FragmentController createController(ActivityTestRule<FragmentTestActivity> rule) {
+ final FragmentController[] controller = new FragmentController[1];
+ final FragmentTestActivity activity = rule.getActivity();
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ instrumentation.runOnMainSync(() -> {
+ HostCallbacks hostCallbacks = new HostCallbacks(activity, null, 0);
+ controller[0] = FragmentController.createController(hostCallbacks);
+ });
+ return controller[0];
+ }
+
+
+ public static void resume(ActivityTestRule<FragmentTestActivity> rule,
+ FragmentController fragmentController,
+ Pair<Parcelable, FragmentManagerNonConfig> savedState) {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ instrumentation.runOnMainSync(() -> {
+ fragmentController.attachHost(null);
+ if (savedState != null) {
+ fragmentController.restoreAllState(savedState.first, savedState.second);
+ }
+ fragmentController.dispatchCreate();
+ fragmentController.dispatchActivityCreated();
+ fragmentController.noteStateNotSaved();
+ fragmentController.execPendingActions();
+ fragmentController.dispatchStart();
+ fragmentController.reportLoaderStart();
+ fragmentController.dispatchResume();
+ fragmentController.execPendingActions();
+ });
+ }
+
+ public static Pair<Parcelable, FragmentManagerNonConfig> destroy(
+ ActivityTestRule<FragmentTestActivity> rule, FragmentController fragmentController) {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ final Pair<Parcelable, FragmentManagerNonConfig>[] result = new Pair[1];
+ instrumentation.runOnMainSync(() -> {
+ fragmentController.dispatchPause();
+ final Parcelable savedState = fragmentController.saveAllState();
+ final FragmentManagerNonConfig nonConfig = fragmentController.retainNestedNonConfig();
+ fragmentController.dispatchStop();
+ fragmentController.doLoaderStop(false);
+ fragmentController.dispatchDestroy();
+ fragmentController.doLoaderDestroy();
+ result[0] = Pair.create(savedState, nonConfig);
+ });
+ return result[0];
+ }
}
diff --git a/tests/fragment/src/android/fragment/cts/FragmentTransitionTest.java b/tests/fragment/src/android/fragment/cts/FragmentTransitionTest.java
new file mode 100644
index 0000000..aec626f
--- /dev/null
+++ b/tests/fragment/src/android/fragment/cts/FragmentTransitionTest.java
@@ -0,0 +1,861 @@
+/*
+ * Copyright (C) 2016 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 android.fragment.cts;
+
+import static junit.framework.Assert.*;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.Instrumentation;
+import android.app.SharedElementCallback;
+import android.cts.util.transition.TargetTracking;
+import android.cts.util.transition.TrackingTransition;
+import android.graphics.Rect;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.transition.TransitionSet;
+import android.view.View;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.ArgumentCaptor;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@MediumTest
+@RunWith(Parameterized.class)
+public class FragmentTransitionTest {
+ private final boolean mOptimize;
+
+ @Parameterized.Parameters
+ public static Object[] data() {
+ return new Boolean[] {
+ false, true
+ };
+ }
+
+ @Rule
+ public ActivityTestRule<FragmentTestActivity> mActivityRule =
+ new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
+
+ private Instrumentation mInstrumentation;
+ private FragmentManager mFragmentManager;
+
+ public FragmentTransitionTest(final boolean optimize) {
+ mOptimize = optimize;
+ }
+
+ @Before
+ public void setup() throws Throwable {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mFragmentManager = mActivityRule.getActivity().getFragmentManager();
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ }
+
+ // Test that normal view transitions (enter, exit, reenter, return) run with
+ // a single fragment.
+ @Test
+ public void enterExitTransitions() throws Throwable {
+ // enter transition
+ TransitionFragment fragment = setupInitialFragment();
+ final View blue = findBlue();
+ final View green = findBlue();
+
+ // exit transition
+ mFragmentManager.beginTransaction()
+ .setAllowOptimization(mOptimize)
+ .remove(fragment)
+ .addToBackStack(null)
+ .commit();
+
+ fragment.waitForTransition();
+ verifyAndClearTransition(fragment.exitTransition, null, green, blue);
+ verifyNoOtherTransitions(fragment);
+
+ // reenter transition
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+ fragment.waitForTransition();
+ final View green2 = findGreen();
+ final View blue2 = findBlue();
+ verifyAndClearTransition(fragment.reenterTransition, null, green2, blue2);
+ verifyNoOtherTransitions(fragment);
+
+ // return transition
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+ fragment.waitForTransition();
+ verifyAndClearTransition(fragment.returnTransition, null, green2, blue2);
+ verifyNoOtherTransitions(fragment);
+ }
+
+ // Test that shared elements transition from one fragment to the next
+ // and back during pop.
+ @Test
+ public void sharedElement() throws Throwable {
+ TransitionFragment fragment1 = setupInitialFragment();
+
+ // Now do a transition to scene2
+ TransitionFragment fragment2 = new TransitionFragment();
+ fragment2.setLayoutId(R.layout.scene2);
+
+ verifyTransition(fragment1, fragment2, "blueSquare");
+
+ // Now pop the back stack
+ verifyPopTransition(1, fragment2, fragment1);
+ }
+
+ // Test that shared element transitions through multiple fragments work together
+ @Test
+ public void intermediateFragment() throws Throwable {
+ TransitionFragment fragment1 = setupInitialFragment();
+
+ final TransitionFragment fragment2 = new TransitionFragment();
+ fragment2.setLayoutId(R.layout.scene3);
+
+ verifyTransition(fragment1, fragment2, "shared");
+
+ final TransitionFragment fragment3 = new TransitionFragment();
+ fragment3.setLayoutId(R.layout.scene2);
+
+ verifyTransition(fragment2, fragment3, "blueSquare");
+
+ // Should transfer backwards when popping multiple:
+ verifyPopTransition(2, fragment3, fragment1, fragment2);
+ }
+
+ // Adding/removing the same fragment multiple times shouldn't mess anything up
+ @Test
+ public void removeAdded() throws Throwable {
+ final TransitionFragment fragment1 = setupInitialFragment();
+
+ final View startBlue = findBlue();
+ final View startGreen = findGreen();
+
+ final TransitionFragment fragment2 = new TransitionFragment();
+ fragment2.setLayoutId(R.layout.scene2);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFragmentManager.beginTransaction()
+ .setAllowOptimization(mOptimize)
+ .replace(R.id.fragmentContainer, fragment2)
+ .replace(R.id.fragmentContainer, fragment1)
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+ }
+ });
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ // should be a normal transition from fragment1 to fragment2
+ fragment2.waitForTransition();
+ final View endBlue = findBlue();
+ final View endGreen = findGreen();
+ verifyAndClearTransition(fragment1.exitTransition, null, startBlue, startGreen);
+ verifyAndClearTransition(fragment2.enterTransition, null, endBlue, endGreen);
+ verifyNoOtherTransitions(fragment1);
+ verifyNoOtherTransitions(fragment2);
+
+ // Pop should also do the same thing
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ fragment1.waitForTransition();
+ final View popBlue = findBlue();
+ final View popGreen = findGreen();
+ verifyAndClearTransition(fragment1.reenterTransition, null, popBlue, popGreen);
+ verifyAndClearTransition(fragment2.returnTransition, null, endBlue, endGreen);
+ verifyNoOtherTransitions(fragment1);
+ verifyNoOtherTransitions(fragment2);
+ }
+
+ // Make sure that shared elements on two different fragment containers don't interact
+ @Test
+ public void crossContainer() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
+ TransitionFragment fragment1 = new TransitionFragment();
+ fragment1.setLayoutId(R.layout.scene1);
+ TransitionFragment fragment2 = new TransitionFragment();
+ fragment2.setLayoutId(R.layout.scene1);
+ mFragmentManager.beginTransaction()
+ .setAllowOptimization(mOptimize)
+ .add(R.id.fragmentContainer1, fragment1)
+ .add(R.id.fragmentContainer2, fragment2)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ fragment1.waitForTransition();
+ final View greenSquare1 = findViewById(fragment1, R.id.greenSquare);
+ final View blueSquare1 = findViewById(fragment1, R.id.blueSquare);
+ verifyAndClearTransition(fragment1.enterTransition, null, greenSquare1, blueSquare1);
+ verifyNoOtherTransitions(fragment1);
+ fragment2.waitForTransition();
+ final View greenSquare2 = findViewById(fragment2, R.id.greenSquare);
+ final View blueSquare2 = findViewById(fragment2, R.id.blueSquare);
+ verifyAndClearTransition(fragment2.enterTransition, null, greenSquare2, blueSquare2);
+ verifyNoOtherTransitions(fragment2);
+
+ // Make sure the correct transitions are run when the target names
+ // are different in both shared elements. We may fool the system.
+ verifyCrossTransition(false, fragment1, fragment2);
+
+ // Make sure the correct transitions are run when the source names
+ // are different in both shared elements. We may fool the system.
+ verifyCrossTransition(true, fragment1, fragment2);
+ }
+
+ // Make sure that onSharedElementStart and onSharedElementEnd are called
+ @Test
+ public void callStartEndWithSharedElements() throws Throwable {
+ TransitionFragment fragment1 = setupInitialFragment();
+
+ // Now do a transition to scene2
+ TransitionFragment fragment2 = new TransitionFragment();
+ fragment2.setLayoutId(R.layout.scene2);
+
+ SharedElementCallback enterCallback = mock(SharedElementCallback.class);
+ fragment2.setEnterSharedElementCallback(enterCallback);
+
+ final View startBlue = findBlue();
+
+ verifyTransition(fragment1, fragment2, "blueSquare");
+
+ ArgumentCaptor<List> names = ArgumentCaptor.forClass(List.class);
+ ArgumentCaptor<List> views = ArgumentCaptor.forClass(List.class);
+ ArgumentCaptor<List> snapshots = ArgumentCaptor.forClass(List.class);
+ verify(enterCallback).onSharedElementStart(names.capture(), views.capture(),
+ snapshots.capture());
+ assertEquals(1, names.getValue().size());
+ assertEquals(1, views.getValue().size());
+ assertNull(snapshots.getValue());
+ assertEquals("blueSquare", names.getValue().get(0));
+ assertEquals(startBlue, views.getValue().get(0));
+
+ final View endBlue = findBlue();
+
+ verify(enterCallback).onSharedElementEnd(names.capture(), views.capture(),
+ snapshots.capture());
+ assertEquals(1, names.getValue().size());
+ assertEquals(1, views.getValue().size());
+ assertNull(snapshots.getValue());
+ assertEquals("blueSquare", names.getValue().get(0));
+ assertEquals(endBlue, views.getValue().get(0));
+
+ // Now pop the back stack
+ reset(enterCallback);
+ verifyPopTransition(1, fragment2, fragment1);
+
+ verify(enterCallback).onSharedElementStart(names.capture(), views.capture(),
+ snapshots.capture());
+ assertEquals(1, names.getValue().size());
+ assertEquals(1, views.getValue().size());
+ assertNull(snapshots.getValue());
+ assertEquals("blueSquare", names.getValue().get(0));
+ assertEquals(endBlue, views.getValue().get(0));
+
+ final View reenterBlue = findBlue();
+
+ verify(enterCallback).onSharedElementEnd(names.capture(), views.capture(),
+ snapshots.capture());
+ assertEquals(1, names.getValue().size());
+ assertEquals(1, views.getValue().size());
+ assertNull(snapshots.getValue());
+ assertEquals("blueSquare", names.getValue().get(0));
+ assertEquals(reenterBlue, views.getValue().get(0));
+ }
+
+ // Make sure that onMapSharedElement works to change the shared element going out
+ @Test
+ public void onMapSharedElementOut() throws Throwable {
+ TransitionFragment fragment1 = setupInitialFragment();
+
+ // Now do a transition to scene2
+ TransitionFragment fragment2 = new TransitionFragment();
+ fragment2.setLayoutId(R.layout.scene2);
+
+ final View startBlue = findBlue();
+ final View startGreen = findGreen();
+
+ final Rect startGreenBounds = getBoundsOnScreen(startGreen);
+
+ SharedElementCallback mapOut = new SharedElementCallback() {
+ @Override
+ public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
+ assertEquals(1, names.size());
+ assertEquals("blueSquare", names.get(0));
+ assertEquals(1, sharedElements.size());
+ assertEquals(startBlue, sharedElements.get("blueSquare"));
+ sharedElements.put("blueSquare", startGreen);
+ }
+ };
+ fragment1.setExitSharedElementCallback(mapOut);
+
+ mFragmentManager.beginTransaction()
+ .addSharedElement(startBlue, "blueSquare")
+ .replace(R.id.fragmentContainer, fragment2)
+ .setAllowOptimization(mOptimize)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ fragment1.waitForTransition();
+ fragment2.waitForTransition();
+
+ final View endBlue = findBlue();
+ final Rect endBlueBounds = getBoundsOnScreen(endBlue);
+
+ verifyAndClearTransition(fragment2.sharedElementEnter, startGreenBounds, startGreen,
+ endBlue);
+
+ SharedElementCallback mapBack = new SharedElementCallback() {
+ @Override
+ public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
+ assertEquals(1, names.size());
+ assertEquals("blueSquare", names.get(0));
+ assertEquals(1, sharedElements.size());
+ final View expectedBlue = findViewById(fragment1, R.id.blueSquare);
+ assertEquals(expectedBlue, sharedElements.get("blueSquare"));
+ final View greenSquare = findViewById(fragment1, R.id.greenSquare);
+ sharedElements.put("blueSquare", greenSquare);
+ }
+ };
+ fragment1.setExitSharedElementCallback(mapBack);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ fragment1.waitForTransition();
+ fragment2.waitForTransition();
+
+ final View reenterGreen = findGreen();
+ verifyAndClearTransition(fragment2.sharedElementReturn, endBlueBounds, endBlue,
+ reenterGreen);
+ }
+
+ // Make sure that onMapSharedElement works to change the shared element target
+ @Test
+ public void onMapSharedElementIn() throws Throwable {
+ TransitionFragment fragment1 = setupInitialFragment();
+
+ // Now do a transition to scene2
+ TransitionFragment fragment2 = new TransitionFragment();
+ fragment2.setLayoutId(R.layout.scene2);
+
+ final View startBlue = findBlue();
+ final Rect startBlueBounds = getBoundsOnScreen(startBlue);
+
+ SharedElementCallback mapIn = new SharedElementCallback() {
+ @Override
+ public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
+ assertEquals(1, names.size());
+ assertEquals("blueSquare", names.get(0));
+ assertEquals(1, sharedElements.size());
+ final View blueSquare = findViewById(fragment2, R.id.blueSquare);
+ assertEquals(blueSquare, sharedElements.get("blueSquare"));
+ final View greenSquare = findViewById(fragment2, R.id.greenSquare);
+ sharedElements.put("blueSquare", greenSquare);
+ }
+ };
+ fragment2.setEnterSharedElementCallback(mapIn);
+
+ mFragmentManager.beginTransaction()
+ .addSharedElement(startBlue, "blueSquare")
+ .replace(R.id.fragmentContainer, fragment2)
+ .setAllowOptimization(mOptimize)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ fragment1.waitForTransition();
+ fragment2.waitForTransition();
+
+ final View endGreen = findGreen();
+ final View endBlue = findBlue();
+ final Rect endGreenBounds = getBoundsOnScreen(endGreen);
+
+ verifyAndClearTransition(fragment2.sharedElementEnter, startBlueBounds, startBlue,
+ endGreen);
+
+ SharedElementCallback mapBack = new SharedElementCallback() {
+ @Override
+ public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
+ assertEquals(1, names.size());
+ assertEquals("blueSquare", names.get(0));
+ assertEquals(1, sharedElements.size());
+ assertEquals(endBlue, sharedElements.get("blueSquare"));
+ sharedElements.put("blueSquare", endGreen);
+ }
+ };
+ fragment2.setEnterSharedElementCallback(mapBack);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ fragment1.waitForTransition();
+ fragment2.waitForTransition();
+
+ final View reenterBlue = findBlue();
+ verifyAndClearTransition(fragment2.sharedElementReturn, endGreenBounds, endGreen,
+ reenterBlue);
+ }
+
+ // Ensure that shared element transitions that have targets properly target the views
+ @Test
+ public void complexSharedElementTransition() throws Throwable {
+ TransitionFragment fragment1 = setupInitialFragment();
+
+ // Now do a transition to scene2
+ ComplexTransitionFragment fragment2 = new ComplexTransitionFragment();
+ fragment2.setLayoutId(R.layout.scene2);
+
+ final View startBlue = findBlue();
+ final View startGreen = findGreen();
+ final Rect startBlueBounds = getBoundsOnScreen(startBlue);
+
+ mFragmentManager.beginTransaction()
+ .addSharedElement(startBlue, "blueSquare")
+ .addSharedElement(startGreen, "greenSquare")
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ fragment1.waitForTransition();
+ fragment2.waitForTransition();
+
+ final View endBlue = findBlue();
+ final View endGreen = findGreen();
+ final Rect endBlueBounds = getBoundsOnScreen(endBlue);
+
+ verifyAndClearTransition(fragment2.sharedElementEnterTransition1, startBlueBounds,
+ startBlue, endBlue);
+ verifyAndClearTransition(fragment2.sharedElementEnterTransition2, startBlueBounds,
+ startGreen, endGreen);
+
+ // Now see if it works when popped
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ fragment1.waitForTransition();
+ fragment2.waitForTransition();
+
+ final View reenterBlue = findBlue();
+ final View reenterGreen = findGreen();
+
+ verifyAndClearTransition(fragment2.sharedElementReturnTransition1, endBlueBounds,
+ endBlue, reenterBlue);
+ verifyAndClearTransition(fragment2.sharedElementReturnTransition2, endBlueBounds,
+ endGreen, reenterGreen);
+ }
+
+ // Ensure that after transitions have executed that they don't have any targets or other
+ // unfortunate modifications.
+ @Test
+ public void transitionsEndUnchanged() throws Throwable {
+ TransitionFragment fragment1 = setupInitialFragment();
+
+ // Now do a transition to scene2
+ TransitionFragment fragment2 = new TransitionFragment();
+ fragment2.setLayoutId(R.layout.scene2);
+
+ verifyTransition(fragment1, fragment2, "blueSquare");
+ assertEquals(0, fragment1.exitTransition.getTargets().size());
+ assertEquals(0, fragment2.sharedElementEnter.getTargets().size());
+ assertEquals(0, fragment2.enterTransition.getTargets().size());
+ assertNull(fragment1.exitTransition.getEpicenterCallback());
+ assertNull(fragment2.enterTransition.getEpicenterCallback());
+ assertNull(fragment2.sharedElementEnter.getEpicenterCallback());
+
+ // Now pop the back stack
+ verifyPopTransition(1, fragment2, fragment1);
+
+ assertEquals(0, fragment2.returnTransition.getTargets().size());
+ assertEquals(0, fragment2.sharedElementReturn.getTargets().size());
+ assertEquals(0, fragment1.reenterTransition.getTargets().size());
+ assertNull(fragment2.returnTransition.getEpicenterCallback());
+ assertNull(fragment2.sharedElementReturn.getEpicenterCallback());
+ assertNull(fragment2.reenterTransition.getEpicenterCallback());
+ }
+
+ // Ensure that transitions are done when a fragment is shown and hidden
+ @Test
+ public void showHideTransition() throws Throwable {
+ TransitionFragment fragment1 = setupInitialFragment();
+ TransitionFragment fragment2 = new TransitionFragment();
+ fragment2.setLayoutId(R.layout.scene2);
+
+ final View startBlue = findBlue();
+ final View startGreen = findGreen();
+
+ mFragmentManager.beginTransaction()
+ .setAllowOptimization(mOptimize)
+ .add(R.id.fragmentContainer, fragment2)
+ .hide(fragment1)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ fragment1.waitForTransition();
+ fragment2.waitForTransition();
+
+ final View endGreen = findViewById(fragment2, R.id.greenSquare);
+ final View endBlue = findViewById(fragment2, R.id.blueSquare);
+
+ assertEquals(View.GONE, fragment1.getView().getVisibility());
+ assertEquals(View.VISIBLE, startGreen.getVisibility());
+ assertEquals(View.VISIBLE, startBlue.getVisibility());
+
+ verifyAndClearTransition(fragment1.exitTransition, null, startGreen, startBlue);
+ verifyNoOtherTransitions(fragment1);
+
+ verifyAndClearTransition(fragment2.enterTransition, null, endGreen, endBlue);
+ verifyNoOtherTransitions(fragment2);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ fragment1.waitForTransition();
+ fragment2.waitForTransition();
+
+ verifyAndClearTransition(fragment1.reenterTransition, null, startGreen, startBlue);
+ verifyNoOtherTransitions(fragment1);
+
+ assertEquals(View.VISIBLE, fragment1.getView().getVisibility());
+ assertEquals(View.VISIBLE, startGreen.getVisibility());
+ assertEquals(View.VISIBLE, startBlue.getVisibility());
+
+ verifyAndClearTransition(fragment2.returnTransition, null, endGreen, endBlue);
+ verifyNoOtherTransitions(fragment2);
+ }
+
+ // Ensure that transitions are done when a fragment is attached and detached
+ @Test
+ public void attachDetachTransition() throws Throwable {
+ TransitionFragment fragment1 = setupInitialFragment();
+ TransitionFragment fragment2 = new TransitionFragment();
+ fragment2.setLayoutId(R.layout.scene2);
+
+ final View startBlue = findBlue();
+ final View startGreen = findGreen();
+
+ mFragmentManager.beginTransaction()
+ .setAllowOptimization(mOptimize)
+ .add(R.id.fragmentContainer, fragment2)
+ .detach(fragment1)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ final View endGreen = findViewById(fragment2, R.id.greenSquare);
+ final View endBlue = findViewById(fragment2, R.id.blueSquare);
+
+ verifyAndClearTransition(fragment1.exitTransition, null, startGreen, startBlue);
+ verifyNoOtherTransitions(fragment1);
+
+ verifyAndClearTransition(fragment2.enterTransition, null, endGreen, endBlue);
+ verifyNoOtherTransitions(fragment2);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ final View reenterBlue = findBlue();
+ final View reenterGreen = findGreen();
+
+ verifyAndClearTransition(fragment1.reenterTransition, null, reenterGreen, reenterBlue);
+ verifyNoOtherTransitions(fragment1);
+
+ verifyAndClearTransition(fragment2.returnTransition, null, endGreen, endBlue);
+ verifyNoOtherTransitions(fragment2);
+ }
+
+ private TransitionFragment setupInitialFragment() throws Throwable {
+ TransitionFragment fragment1 = new TransitionFragment();
+ fragment1.setLayoutId(R.layout.scene1);
+ mFragmentManager.beginTransaction()
+ .setAllowOptimization(mOptimize)
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ fragment1.waitForTransition();
+ final View blueSquare1 = findBlue();
+ final View greenSquare1 = findGreen();
+ verifyAndClearTransition(fragment1.enterTransition, null, blueSquare1, greenSquare1);
+ verifyNoOtherTransitions(fragment1);
+ return fragment1;
+ }
+
+ private View findViewById(Fragment fragment, int id) {
+ return fragment.getView().findViewById(id);
+ }
+
+ private View findGreen() {
+ return mActivityRule.getActivity().findViewById(R.id.greenSquare);
+ }
+
+ private View findBlue() {
+ return mActivityRule.getActivity().findViewById(R.id.blueSquare);
+ }
+
+ private View findRed() {
+ return mActivityRule.getActivity().findViewById(R.id.redSquare);
+ }
+
+ private void verifyAndClearTransition(TargetTracking transition, Rect epicenter,
+ View... expected) {
+ if (epicenter == null) {
+ assertNull(transition.getCapturedEpicenter());
+ } else {
+ assertEquals(epicenter, transition.getCapturedEpicenter());
+ }
+ ArrayList<View> targets = transition.getTrackedTargets();
+ String errorMessage = "Expected: [" + expected.length + "] {" +
+ Arrays.stream(expected).map(v -> v.toString()).collect(Collectors.joining(", ")) +
+ "}, but got: [" + targets.size() + "] {" +
+ targets.stream().map(v -> v.toString()).collect(Collectors.joining(", ")) +
+ "}";
+ assertEquals(errorMessage, expected.length, targets.size());
+ for (View view : expected) {
+ assertTrue(errorMessage, targets.contains(view));
+ }
+ transition.clearTargets();
+ }
+
+ private void verifyNoOtherTransitions(TransitionFragment fragment) {
+ assertEquals(0, fragment.enterTransition.targets.size());
+ assertEquals(0, fragment.exitTransition.targets.size());
+ assertEquals(0, fragment.reenterTransition.targets.size());
+ assertEquals(0, fragment.returnTransition.targets.size());
+ assertEquals(0, fragment.sharedElementEnter.targets.size());
+ assertEquals(0, fragment.sharedElementReturn.targets.size());
+ }
+
+ private void verifyTransition(TransitionFragment from, TransitionFragment to,
+ String sharedElementName) throws Throwable {
+ final View startBlue = findBlue();
+ final View startGreen = findGreen();
+ final View startRed = findRed();
+
+ final Rect startBlueRect = getBoundsOnScreen(startBlue);
+
+ mFragmentManager.beginTransaction()
+ .setAllowOptimization(mOptimize)
+ .addSharedElement(startBlue, sharedElementName)
+ .replace(R.id.fragmentContainer, to)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ to.waitForTransition();
+ final View endGreen = findGreen();
+ final View endBlue = findBlue();
+ final View endRed = findRed();
+ final Rect endBlueRect = getBoundsOnScreen(endBlue);
+
+ if (startRed != null) {
+ verifyAndClearTransition(from.exitTransition, startBlueRect, startGreen, startRed);
+ } else {
+ verifyAndClearTransition(from.exitTransition, startBlueRect, startGreen);
+ }
+ verifyNoOtherTransitions(from);
+
+ if (endRed != null) {
+ verifyAndClearTransition(to.enterTransition, endBlueRect, endGreen, endRed);
+ } else {
+ verifyAndClearTransition(to.enterTransition, endBlueRect, endGreen);
+ }
+ verifyAndClearTransition(to.sharedElementEnter, startBlueRect, startBlue, endBlue);
+ verifyNoOtherTransitions(to);
+ }
+
+ private void verifyCrossTransition(boolean swapSource,
+ TransitionFragment from1, TransitionFragment from2) throws Throwable {
+
+ final TransitionFragment to1 = new TransitionFragment();
+ to1.setLayoutId(R.layout.scene2);
+ final TransitionFragment to2 = new TransitionFragment();
+ to2.setLayoutId(R.layout.scene2);
+
+ final View fromExit1 = findViewById(from1, R.id.greenSquare);
+ final View fromShared1 = findViewById(from1, R.id.blueSquare);
+ final Rect fromSharedRect1 = getBoundsOnScreen(fromShared1);
+
+ final int fromExitId2 = swapSource ? R.id.blueSquare : R.id.greenSquare;
+ final int fromSharedId2 = swapSource ? R.id.greenSquare : R.id.blueSquare;
+ final View fromExit2 = findViewById(from2, fromExitId2);
+ final View fromShared2 = findViewById(from2, fromSharedId2);
+ final Rect fromSharedRect2 = getBoundsOnScreen(fromShared2);
+
+ final String sharedElementName = swapSource ? "blueSquare" : "greenSquare";
+
+ mActivityRule.runOnUiThread(() -> {
+ mFragmentManager.beginTransaction()
+ .setAllowOptimization(mOptimize)
+ .addSharedElement(fromShared1, "blueSquare")
+ .replace(R.id.fragmentContainer1, to1)
+ .addToBackStack(null)
+ .commit();
+ mFragmentManager.beginTransaction()
+ .setAllowOptimization(mOptimize)
+ .addSharedElement(fromShared2, sharedElementName)
+ .replace(R.id.fragmentContainer2, to2)
+ .addToBackStack(null)
+ .commit();
+ });
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ from1.waitForTransition();
+ from2.waitForTransition();
+ to1.waitForTransition();
+ to2.waitForTransition();
+
+ final View toEnter1 = findViewById(to1, R.id.greenSquare);
+ final View toShared1 = findViewById(to1, R.id.blueSquare);
+ final Rect toSharedRect1 = getBoundsOnScreen(toShared1);
+
+ final View toEnter2 = findViewById(to2, fromSharedId2);
+ final View toShared2 = findViewById(to2, fromExitId2);
+ final Rect toSharedRect2 = getBoundsOnScreen(toShared2);
+
+ verifyAndClearTransition(from1.exitTransition, fromSharedRect1, fromExit1);
+ verifyAndClearTransition(from2.exitTransition, fromSharedRect2, fromExit2);
+ verifyNoOtherTransitions(from1);
+ verifyNoOtherTransitions(from2);
+
+ verifyAndClearTransition(to1.enterTransition, toSharedRect1, toEnter1);
+ verifyAndClearTransition(to2.enterTransition, toSharedRect2, toEnter2);
+ verifyAndClearTransition(to1.sharedElementEnter, fromSharedRect1, fromShared1, toShared1);
+ verifyAndClearTransition(to2.sharedElementEnter, fromSharedRect2, fromShared2, toShared2);
+ verifyNoOtherTransitions(to1);
+ verifyNoOtherTransitions(to2);
+
+ // Now pop it back
+ mActivityRule.runOnUiThread(() -> {
+ mFragmentManager.popBackStack();
+ mFragmentManager.popBackStack();
+ });
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ from1.waitForTransition();
+ from2.waitForTransition();
+ to1.waitForTransition();
+ to2.waitForTransition();
+
+ final View returnEnter1 = findViewById(from1, R.id.greenSquare);
+ final View returnShared1 = findViewById(from1, R.id.blueSquare);
+
+ final View returnEnter2 = findViewById(from2, fromExitId2);
+ final View returnShared2 = findViewById(from2, fromSharedId2);
+
+ verifyAndClearTransition(to1.returnTransition, toSharedRect1, toEnter1);
+ verifyAndClearTransition(to2.returnTransition, toSharedRect2, toEnter2);
+ verifyAndClearTransition(to1.sharedElementReturn, toSharedRect1, toShared1, returnShared1);
+ verifyAndClearTransition(to2.sharedElementReturn, toSharedRect2, toShared2, returnShared2);
+ verifyNoOtherTransitions(to1);
+ verifyNoOtherTransitions(to2);
+
+ verifyAndClearTransition(from1.reenterTransition, fromSharedRect1, returnEnter1);
+ verifyAndClearTransition(from2.reenterTransition, fromSharedRect2, returnEnter2);
+ verifyNoOtherTransitions(from1);
+ verifyNoOtherTransitions(from2);
+ }
+
+ private void verifyPopTransition(final int numPops, TransitionFragment from,
+ TransitionFragment to, TransitionFragment... others) throws Throwable {
+ final View startBlue = findBlue();
+ final View startGreen = findGreen();
+ final View startRed = findRed();
+ final Rect startSharedRect = getBoundsOnScreen(startBlue);
+
+ mInstrumentation.runOnMainSync(() -> {
+ for (int i = 0; i < numPops; i++) {
+ mFragmentManager.popBackStack();
+ }
+ });
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ to.waitForTransition();
+ final View endGreen = findGreen();
+ final View endBlue = findBlue();
+ final View endRed = findRed();
+ final Rect endSharedRect = getBoundsOnScreen(endBlue);
+
+ if (startRed != null) {
+ verifyAndClearTransition(from.returnTransition, startSharedRect, startGreen, startRed);
+ } else {
+ verifyAndClearTransition(from.returnTransition, startSharedRect, startGreen);
+ }
+ verifyAndClearTransition(from.sharedElementReturn, startSharedRect, startBlue, endBlue);
+ verifyNoOtherTransitions(from);
+
+ if (endRed != null) {
+ verifyAndClearTransition(to.reenterTransition, endSharedRect, endGreen, endRed);
+ } else {
+ verifyAndClearTransition(to.reenterTransition, endSharedRect, endGreen);
+ }
+ verifyNoOtherTransitions(to);
+
+ if (others != null) {
+ for (TransitionFragment fragment : others) {
+ verifyNoOtherTransitions(fragment);
+ }
+ }
+ }
+
+ private static Rect getBoundsOnScreen(View view) {
+ final int[] loc = new int[2];
+ view.getLocationOnScreen(loc);
+ return new Rect(loc[0], loc[1], loc[0] + view.getWidth(), loc[1] + view.getHeight());
+ }
+
+ public static class ComplexTransitionFragment extends TransitionFragment {
+ public final TrackingTransition sharedElementEnterTransition1 = new TrackingTransition();
+ public final TrackingTransition sharedElementEnterTransition2 = new TrackingTransition();
+ public final TrackingTransition sharedElementReturnTransition1 = new TrackingTransition();
+ public final TrackingTransition sharedElementReturnTransition2 = new TrackingTransition();
+
+ public final TransitionSet sharedElementEnterTransition = new TransitionSet()
+ .addTransition(sharedElementEnterTransition1)
+ .addTransition(sharedElementEnterTransition2);
+ public final TransitionSet sharedElementReturnTransition = new TransitionSet()
+ .addTransition(sharedElementReturnTransition1)
+ .addTransition(sharedElementReturnTransition2);
+
+ public ComplexTransitionFragment() {
+ sharedElementEnterTransition1.addTarget(R.id.blueSquare);
+ sharedElementEnterTransition2.addTarget(R.id.greenSquare);
+ sharedElementReturnTransition1.addTarget(R.id.blueSquare);
+ sharedElementReturnTransition2.addTarget(R.id.greenSquare);
+ setSharedElementEnterTransition(sharedElementEnterTransition);
+ setSharedElementReturnTransition(sharedElementReturnTransition);
+ }
+
+ }
+}
diff --git a/tests/fragment/src/android/fragment/cts/FragmentViewTests.java b/tests/fragment/src/android/fragment/cts/FragmentViewTests.java
index ef28a1d..1599307 100644
--- a/tests/fragment/src/android/fragment/cts/FragmentViewTests.java
+++ b/tests/fragment/src/android/fragment/cts/FragmentViewTests.java
@@ -24,6 +24,7 @@
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.Instrumentation;
+import android.os.Debug;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import android.support.test.rule.ActivityTestRule;
@@ -63,13 +64,13 @@
final StrictViewFragment fragment1 = new StrictViewFragment();
fm.beginTransaction().add(R.id.fragmentContainer, fragment1).addToBackStack(null).commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment1);
+ FragmentTestUtil.assertChildren(container, fragment1);
// Add another on top
final StrictViewFragment fragment2 = new StrictViewFragment();
fm.beginTransaction().add(R.id.fragmentContainer, fragment2).addToBackStack(null).commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment1, fragment2);
+ FragmentTestUtil.assertChildren(container, fragment1, fragment2);
// Now add two in one transaction:
final StrictViewFragment fragment3 = new StrictViewFragment();
@@ -80,20 +81,20 @@
.addToBackStack(null)
.commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment1, fragment2, fragment3, fragment4);
+ FragmentTestUtil.assertChildren(container, fragment1, fragment2, fragment3, fragment4);
fm.popBackStack();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment1, fragment2);
+ FragmentTestUtil.assertChildren(container, fragment1, fragment2);
fm.popBackStack();
FragmentTestUtil.executePendingTransactions(mActivityRule);
assertEquals(1, container.getChildCount());
- assertChildren(container, fragment1);
+ FragmentTestUtil.assertChildren(container, fragment1);
fm.popBackStack();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container);
+ FragmentTestUtil.assertChildren(container);
}
// Add fragments to multiple containers in the same transaction. Make sure that
@@ -110,12 +111,12 @@
final StrictViewFragment fragment1 = new StrictViewFragment();
fm.beginTransaction().add(R.id.fragmentContainer1, fragment1).addToBackStack(null).commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container1, fragment1);
+ FragmentTestUtil.assertChildren(container1, fragment1);
final StrictViewFragment fragment2 = new StrictViewFragment();
fm.beginTransaction().add(R.id.fragmentContainer2, fragment2).addToBackStack(null).commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container2, fragment2);
+ FragmentTestUtil.assertChildren(container2, fragment2);
final StrictViewFragment fragment3 = new StrictViewFragment();
final StrictViewFragment fragment4 = new StrictViewFragment();
@@ -126,18 +127,18 @@
.commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container1, fragment1, fragment3);
- assertChildren(container2, fragment2, fragment4);
+ FragmentTestUtil.assertChildren(container1, fragment1, fragment3);
+ FragmentTestUtil.assertChildren(container2, fragment2, fragment4);
fm.popBackStack();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container1, fragment1);
- assertChildren(container2, fragment2);
+ FragmentTestUtil.assertChildren(container1, fragment1);
+ FragmentTestUtil.assertChildren(container2, fragment2);
fm.popBackStack();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container1, fragment1);
- assertChildren(container2);
+ FragmentTestUtil.assertChildren(container1, fragment1);
+ FragmentTestUtil.assertChildren(container2);
fm.popBackStack();
FragmentTestUtil.executePendingTransactions(mActivityRule);
@@ -189,19 +190,19 @@
.add(R.id.fragmentContainer, fragment4, "4")
.commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment1, fragment2, fragment3, fragment4);
+ FragmentTestUtil.assertChildren(container, fragment1, fragment2, fragment3, fragment4);
// Remove a view
fm.beginTransaction().remove(fragment4).addToBackStack(null).commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
assertEquals(3, container.getChildCount());
- assertChildren(container, fragment1, fragment2, fragment3);
+ FragmentTestUtil.assertChildren(container, fragment1, fragment2, fragment3);
// remove another one
fm.beginTransaction().remove(fragment2).addToBackStack(null).commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment1, fragment3);
+ FragmentTestUtil.assertChildren(container, fragment1, fragment3);
// Now remove the remaining:
fm.beginTransaction()
@@ -210,23 +211,23 @@
.addToBackStack(null)
.commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container);
+ FragmentTestUtil.assertChildren(container);
fm.popBackStack();
FragmentTestUtil.executePendingTransactions(mActivityRule);
final Fragment replacement1 = fm.findFragmentByTag("1");
final Fragment replacement3 = fm.findFragmentByTag("3");
- assertChildren(container, replacement1, replacement3);
+ FragmentTestUtil.assertChildren(container, replacement1, replacement3);
fm.popBackStack();
FragmentTestUtil.executePendingTransactions(mActivityRule);
final Fragment replacement2 = fm.findFragmentByTag("2");
- assertChildren(container, replacement1, replacement3, replacement2);
+ FragmentTestUtil.assertChildren(container, replacement1, replacement3, replacement2);
fm.popBackStack();
FragmentTestUtil.executePendingTransactions(mActivityRule);
final Fragment replacement4 = fm.findFragmentByTag("4");
- assertChildren(container, replacement1, replacement3, replacement2, replacement4);
+ FragmentTestUtil.assertChildren(container, replacement1, replacement3, replacement2, replacement4);
}
// Removing a hidden fragment should remove the View and popping should bring it back hidden
@@ -239,20 +240,19 @@
final StrictViewFragment fragment1 = new StrictViewFragment();
fm.beginTransaction().add(R.id.fragmentContainer, fragment1, "1").hide(fragment1).commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment1);
+ FragmentTestUtil.assertChildren(container, fragment1);
assertTrue(fragment1.isHidden());
fm.beginTransaction().remove(fragment1).addToBackStack(null).commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container);
+ FragmentTestUtil.assertChildren(container);
fm.popBackStack();
FragmentTestUtil.executePendingTransactions(mActivityRule);
final Fragment replacement1 = fm.findFragmentByTag("1");
- assertChildren(container, replacement1);
+ FragmentTestUtil.assertChildren(container, replacement1);
assertTrue(replacement1.isHidden());
assertEquals(View.GONE, replacement1.getView().getVisibility());
- mInstrumentation.waitForIdleSync();
}
// Removing a detached fragment should do nothing to the View and popping should bring
@@ -269,17 +269,17 @@
.detach(fragment1)
.commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container);
+ FragmentTestUtil.assertChildren(container);
assertTrue(fragment1.isDetached());
fm.beginTransaction().remove(fragment1).addToBackStack(null).commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container);
+ FragmentTestUtil.assertChildren(container);
fm.popBackStack();
FragmentTestUtil.executePendingTransactions(mActivityRule);
final Fragment replacement1 = fm.findFragmentByTag("1");
- assertChildren(container);
+ FragmentTestUtil.assertChildren(container);
assertTrue(replacement1.isDetached());
}
@@ -299,11 +299,11 @@
.addToBackStack(null)
.commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment);
+ FragmentTestUtil.assertChildren(container, fragment);
fm.popBackStack();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container);
+ FragmentTestUtil.assertChildren(container);
}
// Removing a fragment that isn't in should throw
@@ -332,20 +332,20 @@
fm.beginTransaction().add(R.id.fragmentContainer, fragment).commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment);
+ FragmentTestUtil.assertChildren(container, fragment);
assertEquals(View.VISIBLE, fragment.getView().getVisibility());
fm.beginTransaction().hide(fragment).addToBackStack(null).commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment);
+ FragmentTestUtil.assertChildren(container, fragment);
assertTrue(fragment.isHidden());
assertEquals(View.GONE, fragment.getView().getVisibility());
fm.popBackStack();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment);
+ FragmentTestUtil.assertChildren(container, fragment);
assertFalse(fragment.isHidden());
assertEquals(View.VISIBLE, fragment.getView().getVisibility());
}
@@ -387,7 +387,7 @@
}
// Show a hidden fragment and its View should be VISIBLE. Then pop it and the View should be
- // BONE.
+ // GONE.
@Test
public void showFragment() throws Throwable {
FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
@@ -398,21 +398,21 @@
fm.beginTransaction().add(R.id.fragmentContainer, fragment).hide(fragment).commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment);
+ FragmentTestUtil.assertChildren(container, fragment);
assertTrue(fragment.isHidden());
assertEquals(View.GONE, fragment.getView().getVisibility());
fm.beginTransaction().show(fragment).addToBackStack(null).commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment);
+ FragmentTestUtil.assertChildren(container, fragment);
assertFalse(fragment.isHidden());
assertEquals(View.VISIBLE, fragment.getView().getVisibility());
fm.popBackStack();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment);
+ FragmentTestUtil.assertChildren(container, fragment);
assertTrue(fragment.isHidden());
assertEquals(View.GONE, fragment.getView().getVisibility());
}
@@ -464,20 +464,20 @@
fm.beginTransaction().add(R.id.fragmentContainer, fragment).commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment);
+ FragmentTestUtil.assertChildren(container, fragment);
assertFalse(fragment.isDetached());
assertEquals(View.VISIBLE, fragment.getView().getVisibility());
fm.beginTransaction().detach(fragment).addToBackStack(null).commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container);
+ FragmentTestUtil.assertChildren(container);
assertTrue(fragment.isDetached());
fm.popBackStack();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment);
+ FragmentTestUtil.assertChildren(container, fragment);
assertFalse(fragment.isDetached());
assertEquals(View.VISIBLE, fragment.getView().getVisibility());
}
@@ -494,7 +494,7 @@
fm.beginTransaction().add(R.id.fragmentContainer, fragment).hide(fragment).commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment);
+ FragmentTestUtil.assertChildren(container, fragment);
assertFalse(fragment.isDetached());
assertTrue(fragment.isHidden());
assertEquals(View.GONE, fragment.getView().getVisibility());
@@ -502,14 +502,14 @@
fm.beginTransaction().detach(fragment).addToBackStack(null).commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container);
+ FragmentTestUtil.assertChildren(container);
assertTrue(fragment.isHidden());
assertTrue(fragment.isDetached());
fm.popBackStack();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment);
+ FragmentTestUtil.assertChildren(container, fragment);
assertTrue(fragment.isHidden());
assertFalse(fragment.isDetached());
assertEquals(View.GONE, fragment.getView().getVisibility());
@@ -563,20 +563,20 @@
fm.beginTransaction().add(R.id.fragmentContainer, fragment).detach(fragment).commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container);
+ FragmentTestUtil.assertChildren(container);
assertTrue(fragment.isDetached());
fm.beginTransaction().attach(fragment).addToBackStack(null).commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment);
+ FragmentTestUtil.assertChildren(container, fragment);
assertFalse(fragment.isDetached());
assertEquals(View.VISIBLE, fragment.getView().getVisibility());
fm.popBackStack();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container);
+ FragmentTestUtil.assertChildren(container);
assertTrue(fragment.isDetached());
}
@@ -596,14 +596,14 @@
.commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container);
+ FragmentTestUtil.assertChildren(container);
assertTrue(fragment.isDetached());
assertTrue(fragment.isHidden());
fm.beginTransaction().attach(fragment).addToBackStack(null).commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment);
+ FragmentTestUtil.assertChildren(container, fragment);
assertTrue(fragment.isHidden());
assertFalse(fragment.isDetached());
assertEquals(View.GONE, fragment.getView().getVisibility());
@@ -611,7 +611,7 @@
fm.popBackStack();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container);
+ FragmentTestUtil.assertChildren(container);
assertTrue(fragment.isDetached());
assertTrue(fragment.isHidden());
}
@@ -662,7 +662,7 @@
fm.beginTransaction().add(R.id.fragmentContainer, fragment1, "1").commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment1);
+ FragmentTestUtil.assertChildren(container, fragment1);
final StrictViewFragment fragment2 = new StrictViewFragment();
fm.beginTransaction()
@@ -671,7 +671,7 @@
.commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment2);
+ FragmentTestUtil.assertChildren(container, fragment2);
assertEquals(View.VISIBLE, fragment2.getView().getVisibility());
fm.popBackStack();
@@ -679,7 +679,7 @@
Fragment replacement1 = fm.findFragmentByTag("1");
assertNotNull(replacement1);
- assertChildren(container, replacement1);
+ FragmentTestUtil.assertChildren(container, replacement1);
assertFalse(replacement1.isHidden());
assertTrue(replacement1.isAdded());
assertFalse(replacement1.isDetached());
@@ -702,7 +702,7 @@
.commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment1, fragment2);
+ FragmentTestUtil.assertChildren(container, fragment1, fragment2);
final StrictViewFragment fragment3 = new StrictViewFragment();
fm.beginTransaction()
@@ -711,7 +711,7 @@
.commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment3);
+ FragmentTestUtil.assertChildren(container, fragment3);
assertEquals(View.VISIBLE, fragment3.getView().getVisibility());
fm.popBackStack();
@@ -721,7 +721,7 @@
Fragment replacement2 = fm.findFragmentByTag("2");
assertNotNull(replacement1);
assertNotNull(replacement2);
- assertChildren(container, replacement1, replacement2);
+ FragmentTestUtil.assertChildren(container, replacement1, replacement2);
assertFalse(replacement1.isHidden());
assertTrue(replacement1.isAdded());
assertFalse(replacement1.isDetached());
@@ -749,13 +749,129 @@
.commit();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container, fragment);
+ FragmentTestUtil.assertChildren(container, fragment);
assertEquals(View.VISIBLE, fragment.getView().getVisibility());
fm.popBackStack();
FragmentTestUtil.executePendingTransactions(mActivityRule);
- assertChildren(container);
+ FragmentTestUtil.assertChildren(container);
+ }
+
+ // Replace a fragment that exists with itself
+ @Test
+ public void replaceExisting() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1, "1")
+ .add(R.id.fragmentContainer, fragment2, "2")
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment1, fragment2);
+
+ fm.beginTransaction()
+ .replace(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment1);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ final Fragment replacement1 = fm.findFragmentByTag("1");
+ final Fragment replacement2 = fm.findFragmentByTag("2");
+
+ assertSame(fragment1, replacement1);
+ FragmentTestUtil.assertChildren(container, replacement1, replacement2);
+ }
+
+ // Have two replace operations in the same transaction to ensure that they
+ // don't interfere with each other
+ @Test
+ public void replaceReplace() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
+ ViewGroup container1 = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer1);
+ ViewGroup container2 = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer2);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ final StrictViewFragment fragment3 = new StrictViewFragment();
+ final StrictViewFragment fragment4 = new StrictViewFragment();
+ final StrictViewFragment fragment5 = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer1, fragment1)
+ .add(R.id.fragmentContainer2, fragment2)
+ .replace(R.id.fragmentContainer1, fragment3)
+ .replace(R.id.fragmentContainer2, fragment4)
+ .replace(R.id.fragmentContainer1, fragment5)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container1, fragment5);
+ assertChildren(container2, fragment4);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container1);
+ assertChildren(container2);
+ }
+
+ // Test to prevent regressions in FragmentManager fragment replace method. See b/24693644
+ @Test
+ public void testReplaceFragment() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ StrictViewFragment fragmentA = new StrictViewFragment();
+ fragmentA.setLayoutId(R.layout.text_a);
+
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragmentA)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertNotNull(findViewById(R.id.textA));
+ assertNull(findViewById(R.id.textB));
+ assertNull(findViewById(R.id.textC));
+
+ StrictViewFragment fragmentB = new StrictViewFragment();
+ fragmentB.setLayoutId(R.layout.text_b);
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragmentB)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertNotNull(findViewById(R.id.textA));
+ assertNotNull(findViewById(R.id.textB));
+ assertNull(findViewById(R.id.textC));
+
+ StrictViewFragment fragmentC = new StrictViewFragment();
+ fragmentC.setLayoutId(R.layout.text_c);
+ fm.beginTransaction()
+ .replace(R.id.fragmentContainer, fragmentC)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertNull(findViewById(R.id.textA));
+ assertNull(findViewById(R.id.textB));
+ assertNotNull(findViewById(R.id.textC));
+ }
+
+ private View findViewById(int viewId) {
+ return mActivityRule.getActivity().findViewById(viewId);
}
private void assertChildren(ViewGroup container, Fragment... fragments) {
diff --git a/tests/fragment/src/android/fragment/cts/HostCallbacks.java b/tests/fragment/src/android/fragment/cts/HostCallbacks.java
new file mode 100644
index 0000000..fd45aa1
--- /dev/null
+++ b/tests/fragment/src/android/fragment/cts/HostCallbacks.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 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 android.fragment.cts;
+
+import android.app.FragmentHostCallback;
+import android.os.Handler;
+import android.view.View;
+
+class HostCallbacks extends FragmentHostCallback<FragmentTestActivity> {
+ private final FragmentTestActivity mActivity;
+
+ public HostCallbacks(FragmentTestActivity activity, Handler handler, int windowAnimations) {
+ super(activity, handler, windowAnimations);
+ mActivity = activity;
+ }
+
+ @Override
+ public FragmentTestActivity onGetHost() {
+ return mActivity;
+ }
+
+ @Override
+ public View onFindViewById(int id) {
+ return mActivity.findViewById(id);
+ }
+}
diff --git a/tests/fragment/src/android/fragment/cts/PostponedTransitionTest.java b/tests/fragment/src/android/fragment/cts/PostponedTransitionTest.java
new file mode 100644
index 0000000..29a723b
--- /dev/null
+++ b/tests/fragment/src/android/fragment/cts/PostponedTransitionTest.java
@@ -0,0 +1,809 @@
+/*
+ * Copyright (C) 2016 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 android.fragment.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Fragment;
+import android.app.FragmentController;
+import android.app.FragmentManager;
+import android.app.FragmentManagerNonConfig;
+import android.app.Instrumentation;
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.Parcelable;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class PostponedTransitionTest {
+ @Rule
+ public ActivityTestRule<FragmentTestActivity> mActivityRule =
+ new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
+
+ private Instrumentation mInstrumentation;
+ private PostponedFragment1 mBeginningFragment;
+
+ @Before
+ public void setupContainer() throws Throwable {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ mBeginningFragment = new PostponedFragment1();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, mBeginningFragment)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ mBeginningFragment.startPostponedEnterTransition();
+ mBeginningFragment.waitForTransition();
+ clearTargets(mBeginningFragment);
+ }
+
+ // Ensure that replacing with a fragment that has a postponed transition
+ // will properly postpone it, both adding and popping.
+ @Test
+ public void replaceTransition() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final View startBlue = mActivityRule.getActivity().findViewById(R.id.blueSquare);
+
+ final PostponedFragment2 fragment = new PostponedFragment2();
+ fm.beginTransaction()
+ .addSharedElement(startBlue, "blueSquare")
+ .replace(R.id.fragmentContainer, fragment)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ // should be postponed now
+ assertPostponedTransition(mBeginningFragment, fragment, null);
+
+ // start the postponed transition
+ fragment.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertForwardTransition(mBeginningFragment, fragment);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ // should be postponed going back, too
+ assertPostponedTransition(fragment, mBeginningFragment, null);
+
+ // start the postponed transition
+ mBeginningFragment.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertBackTransition(fragment, mBeginningFragment);
+ }
+
+ // Ensure that postponed transition is forced after another has been committed.
+ // This tests when the transactions are executed together
+ @Test
+ public void forcedTransition1() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final View startBlue = mActivityRule.getActivity().findViewById(R.id.blueSquare);
+
+ final PostponedFragment2 fragment2 = new PostponedFragment2();
+ final PostponedFragment1 fragment3 = new PostponedFragment1();
+
+ final int commit[] = new int[1];
+ // Need to run this on the UI thread so that the transaction doesn't start
+ // between the two
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ commit[0] = fm.beginTransaction()
+ .addSharedElement(startBlue, "blueSquare")
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+
+ fm.beginTransaction()
+ .addSharedElement(startBlue, "blueSquare")
+ .replace(R.id.fragmentContainer, fragment3)
+ .addToBackStack(null)
+ .commit();
+ }
+ });
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ // transition to fragment2 should be started
+ assertForwardTransition(mBeginningFragment, fragment2);
+
+ // fragment3 should be postponed, but fragment2 should be executed with no transition.
+ assertPostponedTransition(fragment2, fragment3, mBeginningFragment);
+
+ // start the postponed transition
+ fragment3.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertForwardTransition(fragment2, fragment3);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule, commit[0],
+ FragmentManager.POP_BACK_STACK_INCLUSIVE);
+
+ assertBackTransition(fragment3, fragment2);
+
+ assertPostponedTransition(fragment2, mBeginningFragment, fragment3);
+
+ // start the postponed transition
+ mBeginningFragment.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertBackTransition(fragment2, mBeginningFragment);
+ }
+
+ // Ensure that postponed transition is forced after another has been committed.
+ // This tests when the transactions are processed separately.
+ @Test
+ public void forcedTransition2() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final View startBlue = mActivityRule.getActivity().findViewById(R.id.blueSquare);
+
+ final PostponedFragment2 fragment2 = new PostponedFragment2();
+
+ fm.beginTransaction()
+ .addSharedElement(startBlue, "blueSquare")
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponedTransition(mBeginningFragment, fragment2, null);
+
+ final PostponedFragment1 fragment3 = new PostponedFragment1();
+ fm.beginTransaction()
+ .addSharedElement(startBlue, "blueSquare")
+ .replace(R.id.fragmentContainer, fragment3)
+ .addToBackStack(null)
+ .commit();
+
+ // This should cancel the mBeginningFragment -> fragment2 transition
+ // and start fragment2 -> fragment3 transition postponed
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ // fragment3 should be postponed, but fragment2 should be executed with no transition.
+ assertPostponedTransition(fragment2, fragment3, mBeginningFragment);
+
+ // start the postponed transition
+ fragment3.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertForwardTransition(fragment2, fragment3);
+
+ // Pop back to fragment2, but it should be postponed
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ assertPostponedTransition(fragment3, fragment2, null);
+
+ // Pop to mBeginningFragment -- should cancel the fragment2 transition and
+ // start the mBeginningFragment transaction postponed
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ assertPostponedTransition(fragment2, mBeginningFragment, fragment3);
+
+ // start the postponed transition
+ mBeginningFragment.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertBackTransition(fragment2, mBeginningFragment);
+ }
+
+ // Do a bunch of things to one fragment in a transaction and see if it can screw things up.
+ @Test
+ public void crazyTransition() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final View startBlue = mActivityRule.getActivity().findViewById(R.id.blueSquare);
+
+ final PostponedFragment2 fragment2 = new PostponedFragment2();
+
+ fm.beginTransaction()
+ .addSharedElement(startBlue, "blueSquare")
+ .hide(mBeginningFragment)
+ .replace(R.id.fragmentContainer, fragment2)
+ .hide(fragment2)
+ .detach(fragment2)
+ .attach(fragment2)
+ .show(fragment2)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponedTransition(mBeginningFragment, fragment2, null);
+
+ // start the postponed transition
+ fragment2.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertForwardTransition(mBeginningFragment, fragment2);
+
+ // Pop back to fragment2, but it should be postponed
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ assertPostponedTransition(fragment2, mBeginningFragment, null);
+
+ // start the postponed transition
+ mBeginningFragment.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertBackTransition(fragment2, mBeginningFragment);
+ }
+
+ // Execute transactions on different containers and ensure that they don't conflict
+ @Test
+ public void differentContainers() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ fm.beginTransaction().remove(mBeginningFragment).commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
+
+ TransitionFragment fragment1 = new PostponedFragment1();
+ TransitionFragment fragment2 = new PostponedFragment1();
+
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer1, fragment1)
+ .add(R.id.fragmentContainer2, fragment2)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ fragment1.startPostponedEnterTransition();
+ fragment2.startPostponedEnterTransition();
+ fragment1.waitForTransition();
+ fragment2.waitForTransition();
+ clearTargets(fragment1);
+ clearTargets(fragment2);
+
+ final View startBlue1 = fragment1.getView().findViewById(R.id.blueSquare);
+ final View startBlue2 = fragment2.getView().findViewById(R.id.blueSquare);
+
+ final TransitionFragment fragment3 = new PostponedFragment2();
+
+ fm.beginTransaction()
+ .addSharedElement(startBlue1, "blueSquare")
+ .replace(R.id.fragmentContainer1, fragment3)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponedTransition(fragment1, fragment3, null);
+
+ final TransitionFragment fragment4 = new PostponedFragment2();
+
+ fm.beginTransaction()
+ .addSharedElement(startBlue2, "blueSquare")
+ .replace(R.id.fragmentContainer2, fragment4)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponedTransition(fragment1, fragment3, null);
+ assertPostponedTransition(fragment2, fragment4, null);
+
+ // start the postponed transition
+ fragment3.startPostponedEnterTransition();
+
+ // make sure only one ran
+ assertForwardTransition(fragment1, fragment3);
+ assertPostponedTransition(fragment2, fragment4, null);
+
+ // start the postponed transition
+ fragment4.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertForwardTransition(fragment2, fragment4);
+
+ // Pop back to fragment2 -- should be postponed
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ assertPostponedTransition(fragment4, fragment2, null);
+
+ // Pop back to fragment1 -- also should be postponed
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ assertPostponedTransition(fragment4, fragment2, null);
+ assertPostponedTransition(fragment3, fragment1, null);
+
+ // start the postponed transition
+ fragment2.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertBackTransition(fragment4, fragment2);
+
+ // but not the postponed one
+ assertPostponedTransition(fragment3, fragment1, null);
+
+ // start the postponed transition
+ fragment1.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertBackTransition(fragment3, fragment1);
+ }
+
+ // Execute transactions on different containers and ensure that they don't conflict.
+ // The postponement can be started out-of-order
+ @Test
+ public void outOfOrderContainers() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ fm.beginTransaction().remove(mBeginningFragment).commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
+
+ TransitionFragment fragment1 = new PostponedFragment1();
+ TransitionFragment fragment2 = new PostponedFragment1();
+
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer1, fragment1)
+ .add(R.id.fragmentContainer2, fragment2)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ fragment1.startPostponedEnterTransition();
+ fragment2.startPostponedEnterTransition();
+ fragment1.waitForTransition();
+ fragment2.waitForTransition();
+ clearTargets(fragment1);
+ clearTargets(fragment2);
+
+ final View startBlue1 = fragment1.getView().findViewById(R.id.blueSquare);
+ final View startBlue2 = fragment2.getView().findViewById(R.id.blueSquare);
+
+ final TransitionFragment fragment3 = new PostponedFragment2();
+
+ fm.beginTransaction()
+ .addSharedElement(startBlue1, "blueSquare")
+ .replace(R.id.fragmentContainer1, fragment3)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponedTransition(fragment1, fragment3, null);
+
+ final TransitionFragment fragment4 = new PostponedFragment2();
+
+ fm.beginTransaction()
+ .addSharedElement(startBlue2, "blueSquare")
+ .replace(R.id.fragmentContainer2, fragment4)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponedTransition(fragment1, fragment3, null);
+ assertPostponedTransition(fragment2, fragment4, null);
+
+ // start the postponed transition
+ fragment4.startPostponedEnterTransition();
+
+ // make sure only one ran
+ assertForwardTransition(fragment2, fragment4);
+ assertPostponedTransition(fragment1, fragment3, null);
+
+ // start the postponed transition
+ fragment3.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertForwardTransition(fragment1, fragment3);
+
+ // Pop back to fragment2 -- should be postponed
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ assertPostponedTransition(fragment4, fragment2, null);
+
+ // Pop back to fragment1 -- also should be postponed
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ assertPostponedTransition(fragment4, fragment2, null);
+ assertPostponedTransition(fragment3, fragment1, null);
+
+ // start the postponed transition
+ fragment1.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertBackTransition(fragment3, fragment1);
+
+ // but not the postponed one
+ assertPostponedTransition(fragment4, fragment2, null);
+
+ // start the postponed transition
+ fragment2.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertBackTransition(fragment4, fragment2);
+ }
+
+ // Make sure that commitNow for a transaction on a different fragment container doesn't
+ // affect the postponed transaction
+ @Test
+ public void commitNowNoEffect() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ fm.beginTransaction().remove(mBeginningFragment).commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
+
+ final TransitionFragment fragment1 = new PostponedFragment1();
+ final TransitionFragment fragment2 = new PostponedFragment1();
+
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer1, fragment1)
+ .add(R.id.fragmentContainer2, fragment2)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ fragment1.startPostponedEnterTransition();
+ fragment2.startPostponedEnterTransition();
+ fragment1.waitForTransition();
+ fragment2.waitForTransition();
+ clearTargets(fragment1);
+ clearTargets(fragment2);
+
+ final View startBlue1 = fragment1.getView().findViewById(R.id.blueSquare);
+ final View startBlue2 = fragment2.getView().findViewById(R.id.blueSquare);
+
+ final TransitionFragment fragment3 = new PostponedFragment2();
+ final StrictFragment strictFragment1 = new StrictFragment();
+
+ fm.beginTransaction()
+ .addSharedElement(startBlue1, "blueSquare")
+ .replace(R.id.fragmentContainer1, fragment3)
+ .add(strictFragment1, "1")
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponedTransition(fragment1, fragment3, null);
+
+ final TransitionFragment fragment4 = new PostponedFragment2();
+ final StrictFragment strictFragment2 = new StrictFragment();
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ fm.beginTransaction()
+ .addSharedElement(startBlue2, "blueSquare")
+ .replace(R.id.fragmentContainer2, fragment4)
+ .remove(strictFragment1)
+ .add(strictFragment2, "2")
+ .commitNow();
+ }
+ });
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponedTransition(fragment1, fragment3, null);
+ assertPostponedTransition(fragment2, fragment4, null);
+
+ // start the postponed transition
+ fragment4.startPostponedEnterTransition();
+
+ // make sure only one ran
+ assertForwardTransition(fragment2, fragment4);
+ assertPostponedTransition(fragment1, fragment3, null);
+
+ // start the postponed transition
+ fragment3.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertForwardTransition(fragment1, fragment3);
+ }
+
+ // Make sure that commitNow for a transaction affecting a postponed fragment in the same
+ // container forces the postponed transition to start.
+ @Test
+ public void commitNowStartsPostponed() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final View startBlue1 = mBeginningFragment.getView().findViewById(R.id.blueSquare);
+
+ final TransitionFragment fragment2 = new PostponedFragment2();
+ final TransitionFragment fragment1 = new PostponedFragment1();
+
+ fm.beginTransaction()
+ .addSharedElement(startBlue1, "blueSquare")
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ final View startBlue2 = fragment2.getView().findViewById(R.id.blueSquare);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ fm.beginTransaction()
+ .addSharedElement(startBlue2, "blueSquare")
+ .replace(R.id.fragmentContainer, fragment1)
+ .commitNow();
+ }
+ });
+
+ assertPostponedTransition(fragment2, fragment1, mBeginningFragment);
+
+ // start the postponed transition
+ fragment1.startPostponedEnterTransition();
+
+ assertForwardTransition(fragment2, fragment1);
+ }
+
+ // Make sure that when a transaction that removes a view is postponed that
+ // another transaction doesn't accidentally remove the view early.
+ @Test
+ public void noAccidentalRemoval() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ fm.beginTransaction().remove(mBeginningFragment).commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
+
+ TransitionFragment fragment1 = new PostponedFragment1();
+
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer1, fragment1)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ fragment1.startPostponedEnterTransition();
+ fragment1.waitForTransition();
+ clearTargets(fragment1);
+
+ TransitionFragment fragment2 = new PostponedFragment2();
+ // Create a postponed transaction that removes a view
+ fm.beginTransaction()
+ .replace(R.id.fragmentContainer1, fragment2)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ assertPostponedTransition(fragment1, fragment2, null);
+
+ TransitionFragment fragment3 = new PostponedFragment1();
+ // Create a transaction that doesn't interfere with the previously postponed one
+ fm.beginTransaction()
+ .replace(R.id.fragmentContainer2, fragment3)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponedTransition(fragment1, fragment2, null);
+
+ fragment3.startPostponedEnterTransition();
+ fragment3.waitForTransition();
+ clearTargets(fragment3);
+
+ assertPostponedTransition(fragment1, fragment2, null);
+ }
+
+ // Ensure that a postponed transaction that is popped runs immediately and that
+ // the transaction results in the original state with no transition.
+ @Test
+ public void popPostponedTransaction() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+ final View startBlue = mBeginningFragment.getView().findViewById(R.id.blueSquare);
+
+ final TransitionFragment fragment = new PostponedFragment2();
+
+ fm.beginTransaction()
+ .addSharedElement(startBlue, "blueSquare")
+ .replace(R.id.fragmentContainer, fragment)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponedTransition(mBeginningFragment, fragment, null);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ fragment.waitForNoTransition();
+ mBeginningFragment.waitForNoTransition();
+
+ assureNoTransition(fragment);
+ assureNoTransition(mBeginningFragment);
+
+ assertFalse(fragment.isAdded());
+ assertNull(fragment.getView());
+ assertNotNull(mBeginningFragment.getView());
+ assertEquals(View.VISIBLE, mBeginningFragment.getView().getVisibility());
+ assertTrue(mBeginningFragment.getView().isAttachedToWindow());
+ }
+
+ // Make sure that when saving the state during a postponed transaction that it saves
+ // the state as if it wasn't postponed.
+ @Test
+ public void saveWhilePostponed() throws Throwable {
+ final FragmentController fc1 = FragmentTestUtil.createController(mActivityRule);
+ FragmentTestUtil.resume(mActivityRule, fc1, null);
+
+ final FragmentManager fm1 = fc1.getFragmentManager();
+
+ PostponedFragment1 fragment1 = new PostponedFragment1();
+ fm1.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1, "1")
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ Pair<Parcelable, FragmentManagerNonConfig> state =
+ FragmentTestUtil.destroy(mActivityRule, fc1);
+
+ final FragmentController fc2 = FragmentTestUtil.createController(mActivityRule);
+ FragmentTestUtil.resume(mActivityRule, fc2, state);
+
+ final FragmentManager fm2 = fc2.getFragmentManager();
+ Fragment fragment2 = fm2.findFragmentByTag("1");
+ assertNotNull(fragment2);
+ assertNotNull(fragment2.getView());
+ assertEquals(View.VISIBLE, fragment2.getView().getVisibility());
+ assertTrue(fragment2.isResumed());
+ assertTrue(fragment2.isAdded());
+ assertTrue(fragment2.getView().isAttachedToWindow());
+
+ mInstrumentation.runOnMainSync(() -> {
+ assertTrue(fm2.popBackStackImmediate());
+ });
+
+ assertFalse(fragment2.isResumed());
+ assertFalse(fragment2.isAdded());
+ assertNull(fragment2.getView());
+ }
+
+ private void assertPostponedTransition(TransitionFragment fromFragment,
+ TransitionFragment toFragment, TransitionFragment removedFragment)
+ throws InterruptedException {
+ if (removedFragment != null) {
+ assertNull(removedFragment.getView());
+ assureNoTransition(removedFragment);
+ }
+
+ toFragment.waitForNoTransition();
+ assertNotNull(fromFragment.getView());
+ assertNotNull(toFragment.getView());
+ assertTrue(fromFragment.getView().isAttachedToWindow());
+ assertTrue(toFragment.getView().isAttachedToWindow());
+ assertEquals(View.VISIBLE, fromFragment.getView().getVisibility());
+ assertEquals(View.INVISIBLE, toFragment.getView().getVisibility());
+ assureNoTransition(fromFragment);
+ assureNoTransition(toFragment);
+ assertTrue(fromFragment.isResumed());
+ assertFalse(toFragment.isResumed());
+ }
+
+ private void clearTargets(TransitionFragment fragment) {
+ fragment.enterTransition.targets.clear();
+ fragment.reenterTransition.targets.clear();
+ fragment.exitTransition.targets.clear();
+ fragment.returnTransition.targets.clear();
+ fragment.sharedElementEnter.targets.clear();
+ fragment.sharedElementReturn.targets.clear();
+ }
+
+ private void assureNoTransition(TransitionFragment fragment) {
+ assertEquals(0, fragment.enterTransition.targets.size());
+ assertEquals(0, fragment.reenterTransition.targets.size());
+ assertEquals(0, fragment.enterTransition.targets.size());
+ assertEquals(0, fragment.returnTransition.targets.size());
+ assertEquals(0, fragment.sharedElementEnter.targets.size());
+ assertEquals(0, fragment.sharedElementReturn.targets.size());
+ }
+
+ private void assertForwardTransition(TransitionFragment start, TransitionFragment end)
+ throws InterruptedException {
+ start.waitForTransition();
+ end.waitForTransition();
+ assertEquals(0, start.enterTransition.targets.size());
+ assertEquals(1, end.enterTransition.targets.size());
+
+ assertEquals(0, start.reenterTransition.targets.size());
+ assertEquals(0, end.reenterTransition.targets.size());
+
+ assertEquals(0, start.returnTransition.targets.size());
+ assertEquals(0, end.returnTransition.targets.size());
+
+ assertEquals(1, start.exitTransition.targets.size());
+ assertEquals(0, end.exitTransition.targets.size());
+
+ assertEquals(0, start.sharedElementEnter.targets.size());
+ assertEquals(2, end.sharedElementEnter.targets.size());
+
+ assertEquals(0, start.sharedElementReturn.targets.size());
+ assertEquals(0, end.sharedElementReturn.targets.size());
+
+ final View blue = end.getView().findViewById(R.id.blueSquare);
+ assertTrue(end.sharedElementEnter.targets.contains(blue));
+ assertEquals("blueSquare", end.sharedElementEnter.targets.get(0).getTransitionName());
+ assertEquals("blueSquare", end.sharedElementEnter.targets.get(1).getTransitionName());
+
+ assertNoTargets(start);
+ assertNoTargets(end);
+
+ clearTargets(start);
+ clearTargets(end);
+ }
+
+ private void assertBackTransition(TransitionFragment start, TransitionFragment end)
+ throws InterruptedException {
+ start.waitForTransition();
+ end.waitForTransition();
+ assertEquals(1, end.reenterTransition.targets.size());
+ assertEquals(0, start.reenterTransition.targets.size());
+
+ assertEquals(0, end.returnTransition.targets.size());
+ assertEquals(1, start.returnTransition.targets.size());
+
+ assertEquals(0, start.enterTransition.targets.size());
+ assertEquals(0, end.enterTransition.targets.size());
+
+ assertEquals(0, start.exitTransition.targets.size());
+ assertEquals(0, end.exitTransition.targets.size());
+
+ assertEquals(0, start.sharedElementEnter.targets.size());
+ assertEquals(0, end.sharedElementEnter.targets.size());
+
+ assertEquals(2, start.sharedElementReturn.targets.size());
+ assertEquals(0, end.sharedElementReturn.targets.size());
+
+ final View blue = end.getView().findViewById(R.id.blueSquare);
+ assertTrue(start.sharedElementReturn.targets.contains(blue));
+ assertEquals("blueSquare", start.sharedElementReturn.targets.get(0).getTransitionName());
+ assertEquals("blueSquare", start.sharedElementReturn.targets.get(1).getTransitionName());
+
+ assertNoTargets(end);
+ assertNoTargets(start);
+
+ clearTargets(start);
+ clearTargets(end);
+ }
+
+ private static void assertNoTargets(TransitionFragment fragment) {
+ assertTrue(fragment.enterTransition.getTargets().isEmpty());
+ assertTrue(fragment.reenterTransition.getTargets().isEmpty());
+ assertTrue(fragment.exitTransition.getTargets().isEmpty());
+ assertTrue(fragment.returnTransition.getTargets().isEmpty());
+ assertTrue(fragment.sharedElementEnter.getTargets().isEmpty());
+ assertTrue(fragment.sharedElementReturn.getTargets().isEmpty());
+ }
+
+ public static class PostponedFragment1 extends TransitionFragment {
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ postponeEnterTransition();
+ return inflater.inflate(R.layout.scene1, container, false);
+ }
+ }
+
+ public static class PostponedFragment2 extends TransitionFragment {
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ postponeEnterTransition();
+ return inflater.inflate(R.layout.scene2, container, false);
+ }
+ }
+}
diff --git a/tests/fragment/src/android/fragment/cts/StrictFragment.java b/tests/fragment/src/android/fragment/cts/StrictFragment.java
index f0c4a8a..f552eaa 100644
--- a/tests/fragment/src/android/fragment/cts/StrictFragment.java
+++ b/tests/fragment/src/android/fragment/cts/StrictFragment.java
@@ -37,7 +37,8 @@
boolean mCalledOnAttach, mCalledOnCreate, mCalledOnActivityCreated,
mCalledOnStart, mCalledOnResume, mCalledOnSaveInstanceState,
- mCalledOnPause, mCalledOnStop, mCalledOnDestroy, mCalledOnDetach;
+ mCalledOnPause, mCalledOnStop, mCalledOnDestroy, mCalledOnDetach,
+ mCalledOnAttachFragment;
static String stateToString(int state) {
switch (state) {
@@ -82,6 +83,12 @@
}
@Override
+ public void onAttachFragment(Fragment childFragment) {
+ super.onAttachFragment(childFragment);
+ mCalledOnAttachFragment = true;
+ }
+
+ @Override
public void onAttach(Context context) {
super.onAttach(context);
mCalledOnAttach = true;
@@ -134,7 +141,10 @@
super.onSaveInstanceState(outState);
mCalledOnSaveInstanceState = true;
checkGetActivity();
- checkStateAtLeast("onSaveInstanceState", STARTED);
+ // FIXME: We should not allow onSaveInstanceState except when STARTED or greater.
+ // But FragmentManager currently does it in saveAllState for fragments on the
+ // back stack, so fragments may be in the CREATED state.
+ checkStateAtLeast("onSaveInstanceState", CREATED);
}
@Override
diff --git a/tests/fragment/src/android/fragment/cts/StrictViewFragment.java b/tests/fragment/src/android/fragment/cts/StrictViewFragment.java
index 8c0ab51..d39cc125 100644
--- a/tests/fragment/src/android/fragment/cts/StrictViewFragment.java
+++ b/tests/fragment/src/android/fragment/cts/StrictViewFragment.java
@@ -24,13 +24,24 @@
public class StrictViewFragment extends StrictFragment {
boolean mOnCreateViewCalled, mOnViewCreatedCalled, mOnDestroyViewCalled;
+ int mLayoutId = R.layout.strict_view_fragment;
+
+ public void setLayoutId(int layoutId) {
+ mLayoutId = layoutId;
+ }
+
+ public static StrictViewFragment create(int layoutId) {
+ StrictViewFragment fragment = new StrictViewFragment();
+ fragment.mLayoutId = layoutId;
+ return fragment;
+ }
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
checkGetActivity();
checkState("onCreateView", CREATED);
- final View result = inflater.inflate(R.layout.strict_view_fragment, container, false);
+ final View result = inflater.inflate(mLayoutId, container, false);
mOnCreateViewCalled = true;
return result;
}
diff --git a/tests/fragment/src/android/fragment/cts/TransitionFragment.java b/tests/fragment/src/android/fragment/cts/TransitionFragment.java
new file mode 100644
index 0000000..3fede11
--- /dev/null
+++ b/tests/fragment/src/android/fragment/cts/TransitionFragment.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 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 android.fragment.cts;
+
+import static android.cts.util.CtsMockitoUtils.within;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+
+import android.cts.util.transition.TrackingTransition;
+import android.cts.util.transition.TrackingVisibility;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.transition.Transition;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A fragment that has transitions that can be tracked.
+ */
+public class TransitionFragment extends StrictViewFragment {
+ public final TrackingVisibility enterTransition = new TrackingVisibility();
+ public final TrackingVisibility reenterTransition = new TrackingVisibility();
+ public final TrackingVisibility exitTransition = new TrackingVisibility();
+ public final TrackingVisibility returnTransition = new TrackingVisibility();
+ public final TrackingTransition sharedElementEnter = new TrackingTransition();
+ public final TrackingTransition sharedElementReturn = new TrackingTransition();
+
+ private Transition.TransitionListener mListener = mock(Transition.TransitionListener.class);
+
+ public TransitionFragment() {
+ setEnterTransition(enterTransition);
+ setReenterTransition(reenterTransition);
+ setExitTransition(exitTransition);
+ setReturnTransition(returnTransition);
+ setSharedElementEnterTransition(sharedElementEnter);
+ setSharedElementReturnTransition(sharedElementReturn);
+ enterTransition.addListener(mListener);
+ reenterTransition.addListener(mListener);
+ exitTransition.addListener(mListener);
+ returnTransition.addListener(mListener);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ checkGetActivity();
+ checkState("onCreateView", CREATED);
+ mOnCreateViewCalled = true;
+ return super.onCreateView(inflater, container, savedInstanceState);
+ }
+
+ void waitForTransition() throws InterruptedException {
+ verify(mListener, within(300)).onTransitionEnd(any());
+ reset(mListener);
+ }
+
+ void waitForNoTransition() throws InterruptedException {
+ SystemClock.sleep(250);
+ verify(mListener, never()).onTransitionStart(any());
+ }
+}
diff --git a/tests/tests/bionic/Android.build.copy.libs.mk b/tests/tests/bionic/Android.build.copy.libs.mk
index 0e99d41..dd7d4e0 100644
--- a/tests/tests/bionic/Android.build.copy.libs.mk
+++ b/tests/tests/bionic/Android.build.copy.libs.mk
@@ -92,6 +92,8 @@
prebuilt-elf-files/libtest_invalid-zero_shdr_table_offset.so \
prebuilt-elf-files/libtest_invalid-zero_shentsize.so \
prebuilt-elf-files/libtest_invalid-zero_shstrndx.so \
+ prebuilt-elf-files/libtest_invalid-textrels.so \
+ prebuilt-elf-files/libtest_invalid-textrels2.so \
private_namespace_libs_external/libnstest_private_external.so \
private_namespace_libs/libnstest_dlopened.so \
private_namespace_libs/libnstest_private.so \
diff --git a/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_am_density_golden_160.png b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_am_density_golden_160.png
new file mode 100644
index 0000000..2e77270
--- /dev/null
+++ b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_am_density_golden_160.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_am_density_golden_320.png b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_am_density_golden_320.png
new file mode 100644
index 0000000..5a5c3d2
--- /dev/null
+++ b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_am_density_golden_320.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_am_density_golden_80.png b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_am_density_golden_80.png
new file mode 100644
index 0000000..611b27b
--- /dev/null
+++ b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_am_density_golden_80.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_density_golden_160.png b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_density_golden_160.png
new file mode 100644
index 0000000..2e77270
--- /dev/null
+++ b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_density_golden_160.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_density_golden_320.png b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_density_golden_320.png
new file mode 100644
index 0000000..e8beaa5
--- /dev/null
+++ b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_density_golden_320.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_density_golden_80.png b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_density_golden_80.png
new file mode 100644
index 0000000..b869ed7
--- /dev/null
+++ b/tests/tests/graphics/res/drawable-nodpi/bitmap_shader_density_golden_80.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable/bitmap_shader_am_density.xml b/tests/tests/graphics/res/drawable/bitmap_shader_am_density.xml
new file mode 100644
index 0000000..dfecfbb
--- /dev/null
+++ b/tests/tests/graphics/res/drawable/bitmap_shader_am_density.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/bitmap_shader_density_internal"
+ android:tileModeX="repeat"
+ android:tileModeY="clamp"
+ android:autoMirrored="true" />
diff --git a/tests/tests/graphics/res/drawable/bitmap_shader_am_density_internal.png b/tests/tests/graphics/res/drawable/bitmap_shader_am_density_internal.png
new file mode 100644
index 0000000..b6d4d89
--- /dev/null
+++ b/tests/tests/graphics/res/drawable/bitmap_shader_am_density_internal.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable/bitmap_shader_density.xml b/tests/tests/graphics/res/drawable/bitmap_shader_density.xml
new file mode 100644
index 0000000..435b06a
--- /dev/null
+++ b/tests/tests/graphics/res/drawable/bitmap_shader_density.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/bitmap_shader_density_internal"
+ android:tileModeX="repeat"
+ android:tileModeY="clamp" />
diff --git a/tests/tests/graphics/res/drawable/bitmap_shader_density_internal.png b/tests/tests/graphics/res/drawable/bitmap_shader_density_internal.png
new file mode 100644
index 0000000..b6d4d89
--- /dev/null
+++ b/tests/tests/graphics/res/drawable/bitmap_shader_density_internal.png
Binary files differ
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
index a37704a..4197bf0 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
@@ -55,6 +55,7 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.AttributeSet;
+import android.util.LayoutDirection;
import android.util.Xml;
import android.view.Gravity;
@@ -534,15 +535,9 @@
};
private static final int[] DENSITY_IMAGES = new int[] {
- R.drawable.bitmap_density
- };
-
- private static final int[][] DENSITY_GOLDEN_IMAGES = new int[][] {
- {
- R.drawable.bitmap_density_golden_160,
- R.drawable.bitmap_density_golden_80,
- R.drawable.bitmap_density_golden_320,
- }
+ R.drawable.bitmap_density,
+ R.drawable.bitmap_shader_density,
+ R.drawable.bitmap_shader_am_density,
};
@Test
@@ -550,15 +545,16 @@
final Resources res = mContext.getResources();
final int densityDpi = res.getConfiguration().densityDpi;
try {
- verifyPreloadDensityInner(res, DENSITY_IMAGES[0], DENSITY_VALUES,
- DENSITY_GOLDEN_IMAGES[0]);
+ for (int i = 0; i < DENSITY_IMAGES.length; i++) {
+ verifyPreloadDensityInner(res, DENSITY_IMAGES[i], DENSITY_VALUES);
+ }
} finally {
DrawableTestUtils.setResourcesDensity(res, densityDpi);
}
}
- private void verifyPreloadDensityInner(Resources res, int sourceResId, int[] densities,
- int[] goldenResIds) throws XmlPullParserException, IOException {
+ private void verifyPreloadDensityInner(Resources res, int sourceResId, int[] densities)
+ throws XmlPullParserException, IOException {
final Rect tempPadding = new Rect();
// Capture initial state at preload density.
@@ -574,7 +570,7 @@
final int origHeight = preloadedDrawable.getIntrinsicHeight();
assertFalse(preloadedDrawable.getPadding(tempPadding));
- compareOrSave(preloadedDrawable, preloadDensityDpi, sourceResId, goldenResIds[0]);
+ compareOrSave(preloadedDrawable, preloadDensityDpi, sourceResId);
for (int i = 1; i < densities.length; i++) {
final int scaledDensityDpi = densities[i];
@@ -583,6 +579,7 @@
final BitmapDrawable scaledDrawable =
(BitmapDrawable) preloadedConstantState.newDrawable(res);
+ scaledDrawable.setLayoutDirection(LayoutDirection.RTL);
// Sizes are rounded.
assertEquals(Math.round(origWidth * scale), scaledDrawable.getIntrinsicWidth());
@@ -591,7 +588,7 @@
// Bitmaps have no padding.
assertFalse(scaledDrawable.getPadding(tempPadding));
- compareOrSave(scaledDrawable, scaledDensityDpi, sourceResId, goldenResIds[i]);
+ compareOrSave(scaledDrawable, scaledDensityDpi, sourceResId);
// Ensure theme density is applied correctly. Unlike most
// drawables, we don't have any loss of accuracy because density
@@ -606,7 +603,7 @@
}
}
- private void compareOrSave(Drawable dr, int densityDpi, int sourceResId, int goldenResId) {
+ private void compareOrSave(Drawable dr, int densityDpi, int sourceResId) {
final int width = dr.getIntrinsicWidth();
final int height = dr.getIntrinsicHeight();
final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
@@ -619,6 +616,7 @@
if (DBG_DUMP_PNG) {
saveGoldenImage(bitmap, sourceResId, densityDpi);
} else {
+ final int goldenResId = getGoldenImageResId(sourceResId, densityDpi);
final Bitmap golden = BitmapFactory.decodeResource(
mContext.getResources(), goldenResId);
DrawableTestUtils.compareImages(densityDpi + " dpi", golden, bitmap,
@@ -626,28 +624,32 @@
}
}
+ private int getGoldenImageResId(int sourceResId, int densityDpi) {
+ final String name = getGoldenImageName(sourceResId, densityDpi);
+ return mContext.getResources().getIdentifier(name, "drawable", mContext.getPackageName());
+ }
+
+ private String getGoldenImageName(int sourceResId, int densityDpi) {
+ return mContext.getResources().getResourceEntryName(sourceResId) + "_golden_" + densityDpi;
+ }
+
private void saveGoldenImage(Bitmap bitmap, int sourceResId, int densityDpi) {
// Save the image to the disk.
FileOutputStream out = null;
try {
- final String outputFolder = "/sdcard/temp/";
- final File folder = new File(outputFolder);
- if (!folder.exists()) {
- folder.mkdir();
+ final File outputFolder = new File("/sdcard/temp/");
+ if (!outputFolder.exists()) {
+ outputFolder.mkdir();
}
- final String sourceFilename = new File(
- mContext.getResources().getString(sourceResId)).getName();
- final String sourceTitle = sourceFilename.substring(0, sourceFilename.lastIndexOf("."));
- final String outputTitle = sourceTitle + "_golden_" + densityDpi;
- final String outputFilename = outputFolder + outputTitle + ".png";
- final File outputFile = new File(outputFilename);
- if (!outputFile.exists()) {
- outputFile.createNewFile();
+ final String goldenFilename = getGoldenImageName(sourceResId, densityDpi) + ".png";
+ final File goldenFile = new File(outputFolder, goldenFilename);
+ if (!goldenFile.exists()) {
+ goldenFile.createNewFile();
}
- out = new FileOutputStream(outputFile, false);
+ out = new FileOutputStream(goldenFile, false);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
} catch (Exception e) {
e.printStackTrace();
diff --git a/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java b/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
old mode 100755
new mode 100644
diff --git a/tests/tests/jni/Android.mk b/tests/tests/jni/Android.mk
index 4a81a85..7672d2f 100644
--- a/tests/tests/jni/Android.mk
+++ b/tests/tests/jni/Android.mk
@@ -32,7 +32,13 @@
LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
-LOCAL_JNI_SHARED_LIBRARIES := libjnitest libnativehelper_compat_libc++
+LOCAL_JNI_SHARED_LIBRARIES := \
+ libjninamespacea1 \
+ libjninamespacea2 \
+ libjninamespaceb \
+ libjnicommon \
+ libjnitest \
+ libnativehelper_compat_libc++
LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -41,4 +47,4 @@
include $(BUILD_CTS_PACKAGE)
# Include the associated library's makefile.
-include $(LOCAL_PATH)/libjnitest/Android.mk
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/jni/libjnicommon/Android.mk b/tests/tests/jni/libjnicommon/Android.mk
new file mode 100644
index 0000000..0f8044e
--- /dev/null
+++ b/tests/tests/jni/libjnicommon/Android.mk
@@ -0,0 +1,37 @@
+# Copyright (C) 2016 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.
+
+#
+# This is the shared library included by the JNI test app.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libjnicommon
+
+# Don't include this package in any configuration by default.
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := common.cpp
+
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
+
+LOCAL_SHARED_LIBRARIES := libdl liblog libnativehelper_compat_libc++
+
+LOCAL_SDK_VERSION := 23
+LOCAL_NDK_STL_VARIANT := c++_static
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/jni/libjnicommon/common.cpp b/tests/tests/jni/libjnicommon/common.cpp
new file mode 100644
index 0000000..02cdb24
--- /dev/null
+++ b/tests/tests/jni/libjnicommon/common.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "common.h"
+
+int global = 0;
+
+void incrementGlobal() {
+ ++global;
+}
+
+int getGlobal() {
+ return global;
+}
diff --git a/tests/tests/jni/libjnicommon/common.h b/tests/tests/jni/libjnicommon/common.h
new file mode 100644
index 0000000..c82eece
--- /dev/null
+++ b/tests/tests/jni/libjnicommon/common.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+void incrementGlobal();
+int getGlobal();
+
+#endif // COMMON_H
diff --git a/tests/tests/jni/libjninamespacea1/Android.mk b/tests/tests/jni/libjninamespacea1/Android.mk
new file mode 100644
index 0000000..8016c50
--- /dev/null
+++ b/tests/tests/jni/libjninamespacea1/Android.mk
@@ -0,0 +1,38 @@
+# Copyright (C) 2016 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.
+
+#
+# This is the shared library included by the JNI test app.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libjninamespacea1
+
+# Don't include this package in any configuration by default.
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := namespacea1.cpp
+
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) $(LOCAL_PATH)/../libjnicommon/
+
+LOCAL_LDLIBS += -llog
+LOCAL_SHARED_LIBRARIES := libdl liblog libnativehelper_compat_libc++ libjnicommon
+
+LOCAL_SDK_VERSION := 23
+LOCAL_NDK_STL_VARIANT := c++_static
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/jni/libjninamespacea1/namespacea1.cpp b/tests/tests/jni/libjninamespacea1/namespacea1.cpp
new file mode 100644
index 0000000..decb4f1
--- /dev/null
+++ b/tests/tests/jni/libjninamespacea1/namespacea1.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "common.h"
+
+#include <android/log.h>
+#include <jni.h>
+#include <JNIHelp.h>
+
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,"namespacea1",__VA_ARGS__)
+
+int global = 0;
+
+jint JNI_OnLoad(JavaVM*, void*) {
+ LOGI("JNI_OnLoad namespacea1");
+ return JNI_VERSION_1_4;
+}
+
+extern "C" JNIEXPORT void JNICALL
+ Java_android_jni_cts_ClassNamespaceA1_incrementGlobal(JNIEnv*, jclass) {
+ incrementGlobal();
+}
+
+extern "C" JNIEXPORT jint JNICALL
+ Java_android_jni_cts_ClassNamespaceA1_getGlobal(JNIEnv*, jclass) {
+ return getGlobal();
+}
diff --git a/tests/tests/jni/libjninamespacea2/Android.mk b/tests/tests/jni/libjninamespacea2/Android.mk
new file mode 100644
index 0000000..9b515c9
--- /dev/null
+++ b/tests/tests/jni/libjninamespacea2/Android.mk
@@ -0,0 +1,38 @@
+# Copyright (C) 2016 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.
+
+#
+# This is the shared library included by the JNI test app.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libjninamespacea2
+
+# Don't include this package in any configuration by default.
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := namespacea2.cpp
+
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) $(LOCAL_PATH)/../libjnicommon/
+
+LOCAL_LDLIBS += -llog
+LOCAL_SHARED_LIBRARIES := libdl liblog libnativehelper_compat_libc++ libjnicommon
+
+LOCAL_SDK_VERSION := 23
+LOCAL_NDK_STL_VARIANT := c++_static
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/jni/libjninamespacea2/namespacea2.cpp b/tests/tests/jni/libjninamespacea2/namespacea2.cpp
new file mode 100644
index 0000000..809266e
--- /dev/null
+++ b/tests/tests/jni/libjninamespacea2/namespacea2.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "common.h"
+
+#include <android/log.h>
+#include <jni.h>
+#include <JNIHelp.h>
+
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,"namespacea2",__VA_ARGS__)
+
+int global = 0;
+
+jint JNI_OnLoad(JavaVM*, void*) {
+ LOGI("JNI_OnLoad namespacea2");
+ return JNI_VERSION_1_4;
+}
+
+extern "C" JNIEXPORT void JNICALL
+ Java_android_jni_cts_ClassNamespaceA2_incrementGlobal(JNIEnv*, jclass) {
+ incrementGlobal();
+}
+
+extern "C" JNIEXPORT jint JNICALL
+ Java_android_jni_cts_ClassNamespaceA2_getGlobal(JNIEnv*, jclass) {
+ return getGlobal();
+}
diff --git a/tests/tests/jni/libjninamespaceb/Android.mk b/tests/tests/jni/libjninamespaceb/Android.mk
new file mode 100644
index 0000000..4d3e158b
--- /dev/null
+++ b/tests/tests/jni/libjninamespaceb/Android.mk
@@ -0,0 +1,38 @@
+# Copyright (C) 2016 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.
+
+#
+# This is the shared library included by the JNI test app.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libjninamespaceb
+
+# Don't include this package in any configuration by default.
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := namespaceb.cpp
+
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) $(LOCAL_PATH)/../libjnicommon/
+
+LOCAL_LDLIBS += -llog
+LOCAL_SHARED_LIBRARIES := libdl liblog libnativehelper_compat_libc++ libjnicommon
+
+LOCAL_SDK_VERSION := 23
+LOCAL_NDK_STL_VARIANT := c++_static
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/jni/libjninamespaceb/namespaceb.cpp b/tests/tests/jni/libjninamespaceb/namespaceb.cpp
new file mode 100644
index 0000000..b3bedcb
--- /dev/null
+++ b/tests/tests/jni/libjninamespaceb/namespaceb.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "common.h"
+
+#include <android/log.h>
+#include <jni.h>
+#include <JNIHelp.h>
+
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,"namespaceb",__VA_ARGS__)
+
+int global = 0;
+
+jint JNI_OnLoad(JavaVM*, void*) {
+ LOGI("JNI_OnLoad namespaceb");
+ return JNI_VERSION_1_4;
+}
+
+extern "C" JNIEXPORT void JNICALL
+ Java_android_jni_cts_ClassNamespaceB_incrementGlobal(JNIEnv*, jclass) {
+ incrementGlobal();
+}
+
+extern "C" JNIEXPORT jint JNICALL
+ Java_android_jni_cts_ClassNamespaceB_getGlobal(JNIEnv*, jclass) {
+ return getGlobal();
+}
diff --git a/tests/tests/jni/src/android/jni/cts/JniStaticTest.java b/tests/tests/jni/src/android/jni/cts/JniStaticTest.java
index 91ebe73..d2e90c6 100644
--- a/tests/tests/jni/src/android/jni/cts/JniStaticTest.java
+++ b/tests/tests/jni/src/android/jni/cts/JniStaticTest.java
@@ -42,6 +42,13 @@
}
}
+ public void test_linker_namespaces_classloaders() throws Exception {
+ String error = LinkerNamespacesHelper.runClassLoaderNamespaces();
+ if (error != null) {
+ fail(error);
+ }
+ }
+
/**
* Test that accessing classes true JNI works as expected. b/19382130
*/
diff --git a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
index 132a21f..754e6b2 100644
--- a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
+++ b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
@@ -17,8 +17,11 @@
package android.jni.cts;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.support.test.InstrumentationRegistry;
+import dalvik.system.PathClassLoader;
import java.io.BufferedReader;
import java.io.IOException;
@@ -85,4 +88,139 @@
private static native String runAccessibilityTestImpl(String[] publicSystemLibs,
String[] publicVendorLibs);
+
+ private static void invokeIncrementGlobal(Class<?> clazz) throws Exception {
+ clazz.getMethod("incrementGlobal").invoke(null);
+ }
+ private static int invokeGetGlobal(Class<?> clazz) throws Exception {
+ return (Integer)clazz.getMethod("getGlobal").invoke(null);
+ }
+
+ private static ApplicationInfo getApplicationInfo(String packageName) {
+ PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+ try {
+ return pm.getApplicationInfo(packageName, 0);
+ } catch (NameNotFoundException nnfe) {
+ throw new RuntimeException(nnfe);
+ }
+ }
+
+ private static String getSourcePath(String packageName) {
+ String sourcePath = getApplicationInfo(packageName).sourceDir;
+ if (sourcePath == null) {
+ throw new IllegalStateException("No source path path found for " + packageName);
+ }
+ return sourcePath;
+ }
+
+ private static String getNativePath(String packageName) {
+ String nativePath = getApplicationInfo(packageName).nativeLibraryDir;
+ if (nativePath == null) {
+ throw new IllegalStateException("No native path path found for " + packageName);
+ }
+ return nativePath;
+ }
+
+ // Verify the behaviour of native library loading in class loaders.
+ // In this test:
+ // - libjninamespacea1, libjninamespacea2 and libjninamespaceb depend on libjnicommon
+ // - loaderA will load ClassNamespaceA1 (loading libjninamespacea1)
+ // - loaderA will load ClassNamespaceA2 (loading libjninamespacea2)
+ // - loaderB will load ClassNamespaceB (loading libjninamespaceb)
+ // - incrementGlobal/getGlobal operate on a static global from libjnicommon
+ // and each class should get its own view on it.
+ //
+ // This is a test case for 2 different scenarios:
+ // - loading native libraries in different class loaders
+ // - loading native libraries in the same class loader
+ // Ideally we would have 2 different tests but JNI doesn't allow loading the same library in
+ // different class loaders. So to keep the number of native libraries manageable we just
+ // re-use the same class loaders for the two tests.
+ public static String runClassLoaderNamespaces() throws Exception {
+ // Test for different class loaders.
+ // Verify that common dependencies get a separate copy in each class loader.
+ // libjnicommon should be loaded twice:
+ // in the namespace for loaderA and the one for loaderB.
+ String apkPath = getSourcePath("android.jni.cts");
+ String nativePath = getNativePath("android.jni.cts");
+ PathClassLoader loaderA = new PathClassLoader(
+ apkPath, nativePath, ClassLoader.getSystemClassLoader());
+ Class<?> testA1Class = loaderA.loadClass("android.jni.cts.ClassNamespaceA1");
+ PathClassLoader loaderB = new PathClassLoader(
+ apkPath, nativePath, ClassLoader.getSystemClassLoader());
+ Class<?> testBClass = loaderB.loadClass("android.jni.cts.ClassNamespaceB");
+
+ int globalA1 = invokeGetGlobal(testA1Class);
+ int globalB = invokeGetGlobal(testBClass);
+ if (globalA1 != 0 || globalB != 0) {
+ return "Expected globals to be 0/0: globalA1=" + globalA1 + " globalB=" + globalB;
+ }
+
+ invokeIncrementGlobal(testA1Class);
+ globalA1 = invokeGetGlobal(testA1Class);
+ globalB = invokeGetGlobal(testBClass);
+ if (globalA1 != 1 || globalB != 0) {
+ return "Expected globals to be 1/0: globalA1=" + globalA1 + " globalB=" + globalB;
+ }
+
+ invokeIncrementGlobal(testBClass);
+ globalA1 = invokeGetGlobal(testA1Class);
+ globalB = invokeGetGlobal(testBClass);
+ if (globalA1 != 1 || globalB != 1) {
+ return "Expected globals to be 1/1: globalA1=" + globalA1 + " globalB=" + globalB;
+ }
+
+ // Test for the same class loaders.
+ // Verify that if we load ClassNamespaceA2 into loaderA we get the same view on the
+ // globals.
+ Class<?> testA2Class = loaderA.loadClass("android.jni.cts.ClassNamespaceA2");
+
+ int globalA2 = invokeGetGlobal(testA2Class);
+ if (globalA1 != 1 || globalA2 !=1) {
+ return "Expected globals to be 1/1: globalA1=" + globalA1 + " globalA2=" + globalA2;
+ }
+
+ invokeIncrementGlobal(testA1Class);
+ globalA1 = invokeGetGlobal(testA1Class);
+ globalA2 = invokeGetGlobal(testA2Class);
+ if (globalA1 != 2 || globalA2 != 2) {
+ return "Expected globals to be 2/2: globalA1=" + globalA1 + " globalA2=" + globalA2;
+ }
+
+ invokeIncrementGlobal(testA2Class);
+ globalA1 = invokeGetGlobal(testA1Class);
+ globalA2 = invokeGetGlobal(testA2Class);
+ if (globalA1 != 3 || globalA2 != 3) {
+ return "Expected globals to be 2/2: globalA1=" + globalA1 + " globalA2=" + globalA2;
+ }
+ // On success we return null.
+ return null;
+ }
+}
+
+class ClassNamespaceA1 {
+ static {
+ System.loadLibrary("jninamespacea1");
+ }
+
+ public static native void incrementGlobal();
+ public static native int getGlobal();
+}
+
+class ClassNamespaceA2 {
+ static {
+ System.loadLibrary("jninamespacea2");
+ }
+
+ public static native void incrementGlobal();
+ public static native int getGlobal();
+}
+
+class ClassNamespaceB {
+ static {
+ System.loadLibrary("jninamespaceb");
+ }
+
+ public static native void incrementGlobal();
+ public static native int getGlobal();
}
diff --git a/tests/tests/location/src/android/location/cts/GnssNavigationMessageTest.java b/tests/tests/location/src/android/location/cts/GnssNavigationMessageTest.java
index 6988a3f..d69be8b 100644
--- a/tests/tests/location/src/android/location/cts/GnssNavigationMessageTest.java
+++ b/tests/tests/location/src/android/location/cts/GnssNavigationMessageTest.java
@@ -79,13 +79,19 @@
mTestLocationManager
.registerGnssNavigationMessageCallback(mTestGnssNavigationMessageListener);
- mTestGnssNavigationMessageListener.await();
+ boolean success = mTestGnssNavigationMessageListener.await();
+
if (!mTestGnssNavigationMessageListener.verifyState()) {
return;
}
+ SoftAssert.failOrWarning(isMeasurementTestStrict(),
+ "Time elapsed without getting enough navigation messages."
+ + " Possibly, the test has been run deep indoors."
+ + " Consider retrying test outdoors.",
+ success);
+
List<GnssNavigationMessage> events = mTestGnssNavigationMessageListener.getEvents();
- assertTrue("No Gps Navigation Message received.", !events.isEmpty());
// Verify mandatory GnssNavigationMessage field values.
TestMeasurementUtil.verifyGnssNavMessageMandatoryField(mTestLocationManager, events);
diff --git a/tests/tests/location/src/android/location/cts/GnssStatusTest.java b/tests/tests/location/src/android/location/cts/GnssStatusTest.java
new file mode 100644
index 0000000..e8f3cb13
--- /dev/null
+++ b/tests/tests/location/src/android/location/cts/GnssStatusTest.java
@@ -0,0 +1,49 @@
+package android.location.cts;
+
+import android.location.GnssStatus;
+
+public class GnssStatusTest extends GnssTestCase {
+
+ private static final String TAG = "GnssStatusTest";
+ private static final int LOCATION_TO_COLLECT_COUNT = 1;
+ private static final int STATUS_TO_COLLECT_COUNT = 3;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mTestLocationManager = new TestLocationManager(getContext());
+ }
+
+ /**
+ * Tests that one can listen for {@link GnssStatus}.
+ */
+ public void testGnssStatusChanges() throws Exception {
+ // Checks if GPS hardware feature is present, skips test (pass) if not,
+ // and hard asserts that Location/GPS (Provider) is turned on if is Cts Verifier.
+ if (!TestMeasurementUtil.canTestRunOnCurrentDevice(mTestLocationManager,
+ TAG, MIN_HARDWARE_YEAR_MEASUREMENTS_REQUIRED, isCtsVerifierTest())) {
+ return;
+ }
+
+ // Register Gps Status Listener.
+ TestGnssStatusCallback testGnssStatusCallback =
+ new TestGnssStatusCallback(TAG, STATUS_TO_COLLECT_COUNT);
+ mTestLocationManager.registerGnssStatusCallback(testGnssStatusCallback);
+
+ TestLocationListener locationListener = new TestLocationListener(LOCATION_TO_COLLECT_COUNT);
+ mTestLocationManager.requestLocationUpdates(locationListener);
+
+ boolean success = testGnssStatusCallback.awaitStart();
+ success = success ? testGnssStatusCallback.awaitStatus() : false;
+ success = success ? testGnssStatusCallback.awaitTtff() : false;
+ mTestLocationManager.removeLocationUpdates(locationListener);
+ success = success ? testGnssStatusCallback.awaitStop() : false;
+ mTestLocationManager.unregisterGnssStatusCallback(testGnssStatusCallback);
+
+ SoftAssert.failOrWarning(isMeasurementTestStrict(),
+ "Time elapsed without getting the right status changes."
+ + " Possibly, the test has been run deep indoors."
+ + " Consider retrying test outdoors.",
+ success);
+ }
+}
diff --git a/tests/tests/location/src/android/location/cts/TestGnssStatusCallback.java b/tests/tests/location/src/android/location/cts/TestGnssStatusCallback.java
index cc4c930..c63fb01 100644
--- a/tests/tests/location/src/android/location/cts/TestGnssStatusCallback.java
+++ b/tests/tests/location/src/android/location/cts/TestGnssStatusCallback.java
@@ -18,6 +18,7 @@
import android.location.GnssStatus;
+import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -28,34 +29,50 @@
*/
class TestGnssStatusCallback extends GnssStatus.Callback {
+ private final String mTag;
private volatile boolean mGpsStatusReceived;
private GnssStatus mGnssStatus = null;
// Timeout in sec for count down latch wait
private static final int TIMEOUT_IN_SEC = 90;
- private final CountDownLatch mCountDownLatch;
+ private final CountDownLatch mLatchStart;
+ private final CountDownLatch mLatchStatus;
+ private final CountDownLatch mLatchTtff;
+ private final CountDownLatch mLatchStop;
+
// Store list of Prn for Satellites.
private List<List<Integer>> mGpsSatellitePrns;
- TestGnssStatusCallback(int gpsStatusCountToCollect) {
- mCountDownLatch = new CountDownLatch(gpsStatusCountToCollect);
+ TestGnssStatusCallback(String tag, int gpsStatusCountToCollect) {
+ this.mTag = tag;
+ mLatchStart = new CountDownLatch(1);
+ mLatchStatus = new CountDownLatch(gpsStatusCountToCollect);
+ mLatchTtff = new CountDownLatch(1);
+ mLatchStop = new CountDownLatch(1);
mGpsSatellitePrns = new ArrayList<List<Integer>>();
}
@Override
public void onStarted() {
+ Log.i(mTag, "Gnss Status Listener Started");
+ mLatchStart.countDown();
}
@Override
public void onStopped() {
+ Log.i(mTag, "Gnss Status Listener Stopped");
+ mLatchStop.countDown();
}
@Override
public void onFirstFix(int ttffMillis) {
+ Log.i(mTag, "Gnss Status Listener Received TTFF");
+ mLatchTtff.countDown();
}
@Override
public void onSatelliteStatusChanged(GnssStatus status) {
- mCountDownLatch.countDown();
+ Log.i(mTag, "Gnss Status Listener Received Status Update");
+ mLatchStatus.countDown();
}
/**
@@ -86,7 +103,19 @@
return mGnssStatus;
}
- public boolean await() throws InterruptedException {
- return TestUtils.waitFor(mCountDownLatch, TIMEOUT_IN_SEC);
+ public boolean awaitStart() throws InterruptedException {
+ return TestUtils.waitFor(mLatchStart, TIMEOUT_IN_SEC);
+ }
+
+ public boolean awaitStatus() throws InterruptedException {
+ return TestUtils.waitFor(mLatchStatus, TIMEOUT_IN_SEC);
+ }
+
+ public boolean awaitTtff() throws InterruptedException {
+ return TestUtils.waitFor(mLatchTtff, TIMEOUT_IN_SEC);
+ }
+
+ public boolean awaitStop() throws InterruptedException {
+ return TestUtils.waitFor(mLatchStop, TIMEOUT_IN_SEC);
}
}
diff --git a/tests/tests/location/src/android/location/cts/TestLocationManager.java b/tests/tests/location/src/android/location/cts/TestLocationManager.java
index 26cc057..cca2b78 100644
--- a/tests/tests/location/src/android/location/cts/TestLocationManager.java
+++ b/tests/tests/location/src/android/location/cts/TestLocationManager.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.location.GnssMeasurementsEvent;
import android.location.GnssNavigationMessage;
+import android.location.GnssStatus;
import android.location.GpsStatus;
import android.location.LocationListener;
import android.location.LocationManager;
@@ -193,6 +194,28 @@
}
/**
+ * Add a GNSS Status callback.
+ *
+ * @param callback a {@link GnssStatus.Callback} object to register.
+ * @return {@code true} if the listener was added successfully, {@code false} otherwise.
+ */
+ public boolean registerGnssStatusCallback(GnssStatus.Callback callback) {
+ Log.i(TAG, "Add Gnss Status Callback.");
+ return mLocationManager.registerGnssStatusCallback(
+ callback, new Handler(Looper.getMainLooper()));
+ }
+
+ /**
+ * Removes a GNSS Status callback.
+ *
+ * @param callback a {@link GnssStatus.Callback} object to remove.
+ */
+ public void unregisterGnssStatusCallback(GnssStatus.Callback callback) {
+ Log.i(TAG, "Remove Gnss Status Callback.");
+ mLocationManager.unregisterGnssStatusCallback(callback);
+ }
+
+ /**
* Get LocationManager
*
* @return locationManager
diff --git a/tests/tests/media/src/android/media/cts/AudioNativeTest.java b/tests/tests/media/src/android/media/cts/AudioNativeTest.java
index bfc34d1..7301cc7 100644
--- a/tests/tests/media/src/android/media/cts/AudioNativeTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioNativeTest.java
@@ -24,7 +24,6 @@
import android.media.AudioManager;
public class AudioNativeTest extends CtsAndroidTestCase {
- // Assume stereo here until b/23899814 is fixed.
public static final int MAX_CHANNEL_COUNT = 2;
public static final int MAX_INDEX_MASK = (1 << MAX_CHANNEL_COUNT) - 1;
@@ -215,11 +214,7 @@
}
AudioTrackNative track = new AudioTrackNative();
- // TODO: when b/23899814 is fixed, use AudioManager.getDevices() to enumerate
- // actual devices and their channel counts instead of assuming stereo.
- //
int maxOutputChannels = 2;
-
int validIndexMask = (1 << maxOutputChannels) - 1;
for (int mask = 0; mask <= MAX_INDEX_MASK; ++mask) {
@@ -241,14 +236,20 @@
if (!hasMicrophone()) {
return;
}
+
+ AudioManager audioManager =
+ (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
AudioRecordNative recorder = new AudioRecordNative();
- // TODO: when b/23899814 is fixed, use AudioManager.getDevices() to enumerate
- // actual devices and their channel counts instead of assuming stereo.
- //
- int maxInputChannels = 2;
+ int maxInputChannels = 0;
+ for (AudioDeviceInfo deviceInfo :
+ audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)) {
+ for (int channels : deviceInfo.getChannelCounts()) {
+ maxInputChannels = Math.max(channels, maxInputChannels);
+ }
+ }
- int validIndexMask = (1 << maxInputChannels) -1;
+ int validIndexMask = (1 << maxInputChannels) - 1;
for (int mask = 0; mask <= MAX_INDEX_MASK; ++mask) {
int channelCount = Long.bitCount(mask);
diff --git a/tests/tests/os/assets/minijail/isolated-i386.policy b/tests/tests/os/assets/minijail/isolated-i386.policy
index 63f651c..43ece54 100644
--- a/tests/tests/os/assets/minijail/isolated-i386.policy
+++ b/tests/tests/os/assets/minijail/isolated-i386.policy
@@ -56,6 +56,7 @@
rename: return EPERM
rmdir: return EPERM
select: 1
+set_thread_area: 1
setfsgid32: return EPERM
setfsuid32: return EPERM
setgid32: return EPERM
diff --git a/tests/tests/os/src/android/os/cts/AsyncTaskTest.java b/tests/tests/os/src/android/os/cts/AsyncTaskTest.java
index 5bf1f59..1c9aaba 100644
--- a/tests/tests/os/src/android/os/cts/AsyncTaskTest.java
+++ b/tests/tests/os/src/android/os/cts/AsyncTaskTest.java
@@ -16,12 +16,14 @@
package android.os.cts;
+import android.support.annotation.NonNull;
import android.cts.util.PollingCheck;
import android.os.AsyncTask;
import android.test.InstrumentationTestCase;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
public class AsyncTaskTest extends InstrumentationTestCase {
@@ -169,6 +171,47 @@
}
}
+ public void testException() throws Throwable {
+ final CountDownLatch calledOnCancelled = new CountDownLatch(1);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mAsyncTask = new AsyncTask() {
+ @Override
+ protected Object doInBackground(Object... params) {
+ throw new RuntimeException();
+ }
+
+ @Override
+ protected void onPostExecute(Object o) {
+ fail("onPostExecute should not be called");
+ }
+
+ @Override
+ protected void onCancelled(Object o) {
+ calledOnCancelled.countDown();
+ }
+ };
+ }
+ });
+
+ mAsyncTask.executeOnExecutor(new Executor() {
+ @Override
+ public void execute(@NonNull Runnable command) {
+ try {
+ command.run();
+ fail("Exception not thrown");
+ } catch (Throwable tr) {
+ // expected
+ }
+ }
+ });
+
+ if (!calledOnCancelled.await(5, TimeUnit.SECONDS)) {
+ fail("onCancelled not called!");
+ }
+ }
+
private void startAsyncTask() throws Throwable {
runTestOnUiThread(new Runnable() {
public void run() {
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index 9f9c029..938596f 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -3015,7 +3015,7 @@
<!-- @SystemApi Allows access to MAC addresses of WiFi and Bluetooth peer devices.
@hide -->
<permission android:name="android.permission.PEERS_MAC_ADDRESS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|setup" />
<!-- Allows the Nfc stack to dispatch Nfc messages to applications. Applications
can use this permission to ensure incoming Nfc messages are from the Nfc stack
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_AllUriTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_AllUriTest.java
index 0256738..24021a7 100644
--- a/tests/tests/provider/src/android/provider/cts/ContactsContract_AllUriTest.java
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_AllUriTest.java
@@ -16,18 +16,24 @@
package android.provider.cts;
+import android.content.ContentResolver;
+import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.CancellationSignal;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.SyncState;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
import junit.framework.AssertionFailedError;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.util.ArrayList;
@LargeTest
@@ -39,67 +45,74 @@
// The following markers are planned, but not implemented and the definition below is not all
// correct yet.
// "d" : supports delete.
- // "u" : supports delete.
+ // "u" : supports update.
// "i" : supports insert.
// "r" : supports read.
// "w" : supports write.
// "s" : has x_times_contacted and x_last_time_contacted.
// "t" : has x_times_used and x_last_time_used.
private static final String[][] URIs = {
- {"content://com.android.contacts/contacts", "s"},
- {"content://com.android.contacts/contacts/1", "s"},
+ {"content://com.android.contacts/contacts", "sud"},
+ {"content://com.android.contacts/contacts/1", "sud"},
{"content://com.android.contacts/contacts/1/data", "t"},
{"content://com.android.contacts/contacts/1/entities", "t"},
{"content://com.android.contacts/contacts/1/suggestions"},
{"content://com.android.contacts/contacts/1/suggestions/XXX"},
- {"content://com.android.contacts/contacts/1/photo"},
- {"content://com.android.contacts/contacts/1/display_photo", "-rw"},
- {"content://com.android.contacts/contacts_corp/1/photo", "-rw"},
- {"content://com.android.contacts/contacts_corp/1/display_photo", "-rw"},
- {"content://com.android.contacts/contacts/1/stream_items"},
+ {"content://com.android.contacts/contacts/1/photo", "r"},
+ {"content://com.android.contacts/contacts/1/display_photo", "-r"},
+ {"content://com.android.contacts/contacts_corp/1/photo", "-r"},
+ {"content://com.android.contacts/contacts_corp/1/display_photo", "-r"},
+
{"content://com.android.contacts/contacts/filter", "s"},
{"content://com.android.contacts/contacts/filter/XXX", "s"},
- {"content://com.android.contacts/contacts/lookup/nlookup", "s"},
+
+ {"content://com.android.contacts/contacts/lookup/nlookup", "sud"},
{"content://com.android.contacts/contacts/lookup/nlookup/data", "t"},
- {"content://com.android.contacts/contacts/lookup/nlookup/photo", "t"},
- {"content://com.android.contacts/contacts/lookup/nlookup/1", "s"},
+ {"content://com.android.contacts/contacts/lookup/nlookup/photo", "tr"},
+
+ {"content://com.android.contacts/contacts/lookup/nlookup/1", "sud"},
{"content://com.android.contacts/contacts/lookup/nlookup/1/data"},
- {"content://com.android.contacts/contacts/lookup/nlookup/1/photo"},
- {"content://com.android.contacts/contacts/lookup/nlookup/display_photo", "-rw"},
- {"content://com.android.contacts/contacts/lookup/nlookup/1/display_photo", "-rw"},
+ {"content://com.android.contacts/contacts/lookup/nlookup/1/photo", "r"},
+ {"content://com.android.contacts/contacts/lookup/nlookup/display_photo", "-r"},
+ {"content://com.android.contacts/contacts/lookup/nlookup/1/display_photo", "-r"},
{"content://com.android.contacts/contacts/lookup/nlookup/entities"},
{"content://com.android.contacts/contacts/lookup/nlookup/1/entities"},
- {"content://com.android.contacts/contacts/lookup/nlookup/stream_items"},
- {"content://com.android.contacts/contacts/lookup/nlookup/1/stream_items"},
- {"content://com.android.contacts/contacts/as_vcard/nlookup"},
+
+ {"content://com.android.contacts/contacts/as_vcard/nlookup", "r"},
{"content://com.android.contacts/contacts/as_multi_vcard/XXX"},
+
{"content://com.android.contacts/contacts/strequent/", "s"},
{"content://com.android.contacts/contacts/strequent/filter/XXX", "s"},
+
{"content://com.android.contacts/contacts/group/XXX"},
+
{"content://com.android.contacts/contacts/frequent", "s"},
{"content://com.android.contacts/contacts/delete_usage", "-d"},
{"content://com.android.contacts/contacts/filter_enterprise?directory=0", "s"},
{"content://com.android.contacts/contacts/filter_enterprise/XXX?directory=0", "s"},
- {"content://com.android.contacts/raw_contacts", "s"},
- {"content://com.android.contacts/raw_contacts/1", "s"},
- {"content://com.android.contacts/raw_contacts/1/data", "t"},
+
+ {"content://com.android.contacts/raw_contacts", "siud"},
+ {"content://com.android.contacts/raw_contacts/1", "sud"},
+ {"content://com.android.contacts/raw_contacts/1/data", "tu"},
{"content://com.android.contacts/raw_contacts/1/display_photo", "-rw"},
{"content://com.android.contacts/raw_contacts/1/entity"},
- {"content://com.android.contacts/raw_contacts/1/stream_items"},
- {"content://com.android.contacts/raw_contacts/1/stream_items/1"},
+
{"content://com.android.contacts/raw_contact_entities"},
{"content://com.android.contacts/raw_contact_entities_corp", "!"},
- {"content://com.android.contacts/data", "t"},
- {"content://com.android.contacts/data/1", "t"},
+
+ {"content://com.android.contacts/data", "tud"},
+ {"content://com.android.contacts/data/1", "tudr"},
{"content://com.android.contacts/data/phones", "t"},
{"content://com.android.contacts/data_enterprise/phones", "!"},
- {"content://com.android.contacts/data/phones/1", "t"},
+ {"content://com.android.contacts/data/phones/1", "tud"},
{"content://com.android.contacts/data/phones/filter", "t"},
{"content://com.android.contacts/data/phones/filter/XXX", "t"},
+
{"content://com.android.contacts/data/phones/filter_enterprise?directory=0", "t"},
{"content://com.android.contacts/data/phones/filter_enterprise/XXX?directory=0", "t"},
+
{"content://com.android.contacts/data/emails", "t"},
- {"content://com.android.contacts/data/emails/1", "t"},
+ {"content://com.android.contacts/data/emails/1", "tud"},
{"content://com.android.contacts/data/emails/lookup", "t"},
{"content://com.android.contacts/data/emails/lookup/XXX", "t"},
{"content://com.android.contacts/data/emails/filter", "t"},
@@ -109,10 +122,10 @@
{"content://com.android.contacts/data/emails/lookup_enterprise", "t"},
{"content://com.android.contacts/data/emails/lookup_enterprise/XXX", "t"},
{"content://com.android.contacts/data/postals", "t"},
- {"content://com.android.contacts/data/postals/1", "t"},
- {"content://com.android.contacts/data/usagefeedback/XXX", "-u"},
+ {"content://com.android.contacts/data/postals/1", "tud"},
+ {"content://com.android.contacts/data/usagefeedback/1,2,3", "-u"},
{"content://com.android.contacts/data/callables/", "t"},
- {"content://com.android.contacts/data/callables/1", "t"},
+ {"content://com.android.contacts/data/callables/1", "tud"},
{"content://com.android.contacts/data/callables/filter", "t"},
{"content://com.android.contacts/data/callables/filter/XXX", "t"},
{"content://com.android.contacts/data/callables/filter_enterprise?directory=0", "t"},
@@ -121,95 +134,54 @@
{"content://com.android.contacts/data/contactables/", "t"},
{"content://com.android.contacts/data/contactables/filter", "t"},
{"content://com.android.contacts/data/contactables/filter/XXX", "t"},
- {"content://com.android.contacts/groups"},
- {"content://com.android.contacts/groups/1"},
+
+ {"content://com.android.contacts/groups", "iud"},
+ {"content://com.android.contacts/groups/1", "ud"},
{"content://com.android.contacts/groups_summary"},
- {"content://com.android.contacts/syncstate"},
- {"content://com.android.contacts/syncstate/1", "-du"},
- {"content://com.android.contacts/profile/syncstate"},
+ {"content://com.android.contacts/syncstate", "iud"},
+ {"content://com.android.contacts/syncstate/1", "-ud"},
+ {"content://com.android.contacts/profile/syncstate", "iud"},
{"content://com.android.contacts/phone_lookup/XXX"},
{"content://com.android.contacts/phone_lookup_enterprise/XXX"},
- {"content://com.android.contacts/aggregation_exceptions"},
- {"content://com.android.contacts/settings"},
- {"content://com.android.contacts/status_updates"},
+ {"content://com.android.contacts/aggregation_exceptions", "u"},
+ {"content://com.android.contacts/settings", "ud"},
+ {"content://com.android.contacts/status_updates", "ud"},
{"content://com.android.contacts/status_updates/1"},
{"content://com.android.contacts/search_suggest_query"},
{"content://com.android.contacts/search_suggest_query/XXX"},
{"content://com.android.contacts/search_suggest_shortcut/XXX"},
{"content://com.android.contacts/provider_status"},
- {"content://com.android.contacts/directories"},
+ {"content://com.android.contacts/directories", "u"},
{"content://com.android.contacts/directories/1"},
{"content://com.android.contacts/directories_enterprise"},
{"content://com.android.contacts/directories_enterprise/1"},
{"content://com.android.contacts/complete_name"},
- {"content://com.android.contacts/profile", "s"},
+ {"content://com.android.contacts/profile", "su"},
{"content://com.android.contacts/profile/entities", "s"},
- {"content://com.android.contacts/profile/data", "t"},
- {"content://com.android.contacts/profile/data/1", "t"},
+ {"content://com.android.contacts/profile/data", "tud"},
+ {"content://com.android.contacts/profile/data/1", "td"},
{"content://com.android.contacts/profile/photo", "t"},
- {"content://com.android.contacts/profile/display_photo", "-rw"},
- {"content://com.android.contacts/profile/as_vcard"},
- {"content://com.android.contacts/profile/raw_contacts", "s"},
- {"content://com.android.contacts/profile/raw_contacts/1", "s"},
- {"content://com.android.contacts/profile/raw_contacts/1/data", "t"},
+ {"content://com.android.contacts/profile/display_photo", "-r"},
+ {"content://com.android.contacts/profile/as_vcard", "r"},
+ {"content://com.android.contacts/profile/raw_contacts", "siud"},
+
+ // Note this should have supported update... Too late to add.
+ {"content://com.android.contacts/profile/raw_contacts/1", "sd"},
+ {"content://com.android.contacts/profile/raw_contacts/1/data", "tu"},
{"content://com.android.contacts/profile/raw_contacts/1/entity"},
- {"content://com.android.contacts/profile/status_updates"},
+ {"content://com.android.contacts/profile/status_updates", "ud"},
{"content://com.android.contacts/profile/raw_contact_entities"},
- {"content://com.android.contacts/stream_items"},
- {"content://com.android.contacts/stream_items/photo"},
- {"content://com.android.contacts/stream_items/1"},
- {"content://com.android.contacts/stream_items/1/photo"},
- {"content://com.android.contacts/stream_items/1/photo/1"},
- {"content://com.android.contacts/stream_items_limit"},
- {"content://com.android.contacts/display_photo/1", "-rw"},
+ {"content://com.android.contacts/display_photo/1", "-r"},
{"content://com.android.contacts/photo_dimensions"},
{"content://com.android.contacts/deleted_contacts"},
{"content://com.android.contacts/deleted_contacts/1"},
- {"content://com.android.contacts/directory_file_enterprise/XXX", "-rw"},
-
- {"content://contacts/extensions"},
- {"content://contacts/extensions/1"},
- {"content://contacts/groups"},
- {"content://contacts/groups/1"},
- {"content://contacts/groups/name/XXX/members"},
- {"content://contacts/groups/system_id/XXX/members"},
- {"content://contacts/groupmembership"},
- {"content://contacts/groupmembership/1"},
- {"content://contacts/people", "s"},
- {"content://contacts/people/filter/XXX"},
- {"content://contacts/people/1"},
- {"content://contacts/people/1/extensions"},
- {"content://contacts/people/1/extensions/1"},
- {"content://contacts/people/1/phones"},
- {"content://contacts/people/1/phones/1"},
- {"content://contacts/people/1/photo"},
- {"content://contacts/people/1/contact_methods"},
- {"content://contacts/people/1/contact_methods/1"},
- {"content://contacts/people/1/organizations"},
- {"content://contacts/people/1/organizations/1"},
- {"content://contacts/people/1/groupmembership"},
- {"content://contacts/people/1/groupmembership/1"},
- {"content://contacts/people/1/update_contact_time", "-u"},
- {"content://contacts/deleted_people", "-"},
- {"content://contacts/deleted_groups", "-"},
- {"content://contacts/phones"},
- {"content://contacts/phones/filter/XXX"},
- {"content://contacts/phones/1"},
- {"content://contacts/photos"},
- {"content://contacts/photos/1"},
- {"content://contacts/contact_methods"},
- {"content://contacts/contact_methods/email"},
- {"content://contacts/contact_methods/1"},
- {"content://contacts/organizations"},
- {"content://contacts/organizations/1"},
- {"content://contacts/search_suggest_query"},
- {"content://contacts/search_suggest_query/XXX"},
- {"content://contacts/search_suggest_shortcut/XXX"},
- {"content://contacts/settings"},
+ {"content://com.android.contacts/directory_file_enterprise/XXX?directory=0", "-"},
};
private static final String[] ARG1 = {"-1"};
+ private ContentResolver mResolver;
+
private ArrayList<String> mFailures;
@Override
@@ -217,6 +189,7 @@
super.setUp();
mFailures = new ArrayList<>();
+ mResolver = getContext().getContentResolver();
}
@Override
@@ -228,11 +201,14 @@
super.tearDown();
}
- private void addFailure(String message) {
- mFailures.add(message);
- Log.e(TAG, "Failed: " + message);
- if (mFailures.size() > 100) {
- // Too many failures...
+ private void addFailure(String message, Throwable th) {
+ Log.e(TAG, "Failed: " + message, th);
+
+ final int MAX = 100;
+ if (mFailures.size() == MAX) {
+ mFailures.add("Too many failures.");
+ } else if (mFailures.size() > MAX) {
+ // Too many failures already...
} else {
mFailures.add(message);
}
@@ -261,6 +237,10 @@
mFailures = null;
}
+ private static Uri getUri(String[] path) {
+ return Uri.parse(path[0]);
+ }
+
private static boolean supportsQuery(String[] path) {
if (path.length == 1) {
return true; // supports query by default.
@@ -268,12 +248,28 @@
return !(path[1].contains("-") || path[1].contains("!"));
}
- private static Uri getUri(String[] path) {
- return Uri.parse(path[0]);
+ private static boolean supportsInsert(String[] path) {
+ return (path.length) >= 2 && path[1].contains("i");
+ }
+
+ private static boolean supportsUpdate(String[] path) {
+ return (path.length) >= 2 && path[1].contains("u");
+ }
+
+ private static boolean supportsDelete(String[] path) {
+ return (path.length) >= 2 && path[1].contains("d");
+ }
+
+ private static boolean supportsRead(String[] path) {
+ return (path.length) >= 2 && path[1].contains("r");
+ }
+
+ private static boolean supportsWrite(String[] path) {
+ return (path.length) >= 2 && path[1].contains("w");
}
private String[] getColumns(Uri uri) {
- try (Cursor c = getContext().getContentResolver().query(uri,
+ try (Cursor c = mResolver.query(uri,
null, // projection
"1=2", // selection
null, // selection args
@@ -287,32 +283,47 @@
String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
try {
- try (Cursor c = getContext().getContentResolver().query(uri, projection, selection,
+ try (Cursor c = mResolver.query(uri, projection, selection,
selectionArgs, sortOrder)) {
c.moveToFirst();
}
} catch (Throwable th) {
- addFailure("Query failed: URI=" + uri + " Message=" + th.getMessage());
+ addFailure("Query failed: URI=" + uri + " Message=" + th.getMessage(), th);
}
try {
// With CancellationSignal.
- try (Cursor c = getContext().getContentResolver().query(uri, projection, selection,
+ try (Cursor c = mResolver.query(uri, projection, selection,
selectionArgs, sortOrder, new CancellationSignal())) {
c.moveToFirst();
}
} catch (Throwable th) {
- addFailure("Query with cancel failed: URI=" + uri + " Message=" + th.getMessage());
+ addFailure("Query with cancel failed: URI=" + uri + " Message=" + th.getMessage(), th);
}
try {
// With limit.
- try (Cursor c = getContext().getContentResolver().query(
+ try (Cursor c = mResolver.query(
uri.buildUpon().appendQueryParameter(
ContactsContract.LIMIT_PARAM_KEY, "0").build(),
projection, selection, selectionArgs, sortOrder)) {
c.moveToFirst();
}
} catch (Throwable th) {
- addFailure("Query with limit failed: URI=" + uri + " Message=" + th.getMessage());
+ addFailure("Query with limit failed: URI=" + uri + " Message=" + th.getMessage(), th);
+ }
+
+ try {
+ // With account.
+ try (Cursor c = mResolver.query(
+ uri.buildUpon()
+ .appendQueryParameter(RawContacts.ACCOUNT_NAME, "a")
+ .appendQueryParameter(RawContacts.ACCOUNT_TYPE, "b")
+ .appendQueryParameter(RawContacts.DATA_SET, "c")
+ .build(),
+ projection, selection, selectionArgs, sortOrder)) {
+ c.moveToFirst();
+ }
+ } catch (Throwable th) {
+ addFailure("Query with limit failed: URI=" + uri + " Message=" + th.getMessage(), th);
}
}
@@ -320,7 +331,7 @@
String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
try {
- try (Cursor c = getContext().getContentResolver().query(uri, projection, selection,
+ try (Cursor c = mResolver.query(uri, projection, selection,
selectionArgs, sortOrder)) {
c.moveToFirst();
}
@@ -328,7 +339,7 @@
// pass.
return;
}
- addFailure("Query on " + uri + " expected to fail, but succeeded.");
+ addFailure("Query on " + uri + " expected to fail, but succeeded.", null);
}
/**
@@ -355,8 +366,8 @@
final Uri uri = getUri(path);
for (String column : getColumns(uri)) {
- if (column.toLowerCase().startsWith(ContactsContract.HIDDEN_COLUMN_PREFIX)) { // doesn't seem to be working
- addFailure("Uri " + uri + " returned hidden column " + column);
+ if (column.toLowerCase().startsWith(ContactsContract.HIDDEN_COLUMN_PREFIX)) {
+ addFailure("Uri " + uri + " returned hidden column " + column, null);
}
}
}
@@ -514,13 +525,13 @@
private void checkColumnAccessible(Uri uri, String column) {
try {
- try (Cursor c = getContext().getContentResolver().query(
+ try (Cursor c = mResolver.query(
uri, new String[]{column}, column + "=0", null, column
)) {
c.moveToFirst();
}
} catch (Throwable th) {
- addFailure("Query failed: URI=" + uri + " Message=" + th.getMessage());
+ addFailure("Query failed: URI=" + uri + " Message=" + th.getMessage(), th);
}
}
@@ -538,7 +549,7 @@
private void checkColumnNotAccessibleInner(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
try {
- try (Cursor c = getContext().getContentResolver().query(uri, projection, selection,
+ try (Cursor c = mResolver.query(uri, projection, selection,
selectionArgs, sortOrder)) {
c.moveToFirst();
}
@@ -547,7 +558,7 @@
return;
}
addFailure("Query on " + uri +
- " expected to throw IllegalArgumentException, but succeeded.");
+ " expected to throw IllegalArgumentException, but succeeded.", null);
}
private void checkColumnNotAccessible(Uri uri, String column) {
@@ -590,4 +601,93 @@
}
failIfFailed();
}
+
+ private void checkExecutable(String operation, Uri uri, boolean shouldWork, Runnable r) {
+ if (shouldWork) {
+ try {
+ r.run();
+ } catch (Exception e) {
+ addFailure(operation + " for '" + uri + "' failed: " + e.getMessage(), e);
+ }
+ } else {
+ try {
+ r.run();
+ addFailure(operation + " for '" + uri + "' NOT failed.", null);
+ } catch (Exception expected) {
+ }
+ }
+ }
+
+ public void testAllOperations() {
+ final ContentValues cv = new ContentValues();
+
+ for (String[] path : URIs) {
+ final Uri uri = getUri(path);
+
+ cv.clear();
+ if (supportsQuery(path)) {
+ cv.put(getColumns(uri)[0], 1);
+ } else {
+ cv.put("_id", 1);
+ }
+ if (uri.toString().contains("syncstate")) {
+ cv.put(SyncState.ACCOUNT_NAME, "abc");
+ cv.put(SyncState.ACCOUNT_TYPE, "def");
+ }
+
+ checkExecutable("insert", uri, supportsInsert(path), () -> {
+ final Uri newUri = mResolver.insert(uri, cv);
+ if (newUri == null) {
+ addFailure("Insert for '" + uri + "' returned null.", null);
+ } else {
+ // "profile/raw_contacts/#" is missing update support. too late to add, so
+ // just skip.
+ if (!newUri.toString().startsWith(
+ "content://com.android.contacts/profile/raw_contacts/")) {
+ checkExecutable("insert -> update", newUri, true, () -> {
+ mResolver.update(newUri, cv, null, null);
+ });
+ }
+ checkExecutable("insert -> delete", newUri, true, () -> {
+ mResolver.delete(newUri, null, null);
+ });
+ }
+ });
+ checkExecutable("update", uri, supportsUpdate(path), () -> {
+ mResolver.update(uri, cv, "1=2", null);
+ });
+ checkExecutable("delete", uri, supportsDelete(path), () -> {
+ mResolver.delete(uri, "1=2", null);
+ });
+ }
+ failIfFailed();
+ }
+
+ public void testAllFileOperations() {
+ for (String[] path : URIs) {
+ final Uri uri = getUri(path);
+
+ checkExecutable("openInputStream", uri, supportsRead(path), () -> {
+ try (InputStream st = mResolver.openInputStream(uri)) {
+ } catch (FileNotFoundException e) {
+ // TODO This happens because we try to read nonexistent photos. Ideally
+ // we should actually check it's readable.
+ if (e.getMessage().contains("Stream I/O not supported")) {
+ throw new RuntimeException("Caught Exception: " + e.toString(), e);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Caught Exception: " + e.toString(), e);
+ }
+ });
+ checkExecutable("openOutputStream", uri, supportsWrite(path), () -> {
+ try (OutputStream st = mResolver.openOutputStream(uri)) {
+ } catch (Exception e) {
+ throw new RuntimeException("Caught Exception: " + e.toString(), e);
+ }
+ });
+ }
+ failIfFailed();
+ }
}
+
+
diff --git a/tests/tests/security/Android.mk b/tests/tests/security/Android.mk
index 00c08fe..4668107 100644
--- a/tests/tests/security/Android.mk
+++ b/tests/tests/security/Android.mk
@@ -57,7 +57,8 @@
libeffects
LOCAL_SRC_FILES := $(call all-java-files-under, src)\
- src/android/security/cts/activity/ISecureRandomService.aidl
+ src/android/security/cts/activity/ISecureRandomService.aidl\
+ aidl/android/security/cts/IIsolatedService.aidl
LOCAL_PACKAGE_NAME := CtsSecurityTestCases
diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml
index 33dbf2f..757d719 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -36,6 +36,10 @@
android:isolatedProcess="true"
android:exported="true"/>
+ <service android:name="android.security.cts.IsolatedService"
+ android:process=":Isolated"
+ android:isolatedProcess="true"/>
+
<service android:name="android.security.cts.activity.SecureRandomService"
android:process=":secureRandom"/>
<activity
diff --git a/tests/tests/security/aidl/android/security/cts/IIsolatedService.aidl b/tests/tests/security/aidl/android/security/cts/IIsolatedService.aidl
new file mode 100644
index 0000000..16db0fe
--- /dev/null
+++ b/tests/tests/security/aidl/android/security/cts/IIsolatedService.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2016 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 android.security.cts;
+
+interface IIsolatedService {
+ String[] getCachedSystemServices();
+ IBinder getSystemService(String serviceName);
+}
diff --git a/tests/tests/security/res/raw/cve_2016_3755.mp4 b/tests/tests/security/res/raw/cve_2016_3755.mp4
new file mode 100644
index 0000000..014bd06
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2016_3755.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2016_3878_b_29493002.mp4 b/tests/tests/security/res/raw/cve_2016_3878_b_29493002.mp4
new file mode 100644
index 0000000..469a1b3
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2016_3878_b_29493002.mp4
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/CryptoProviderWorkaroundTest.java b/tests/tests/security/src/android/security/cts/CryptoProviderWorkaroundTest.java
new file mode 100644
index 0000000..861c443
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/CryptoProviderWorkaroundTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2016 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 android.security.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import dalvik.system.VMRuntime;
+import java.security.NoSuchProviderException;
+import java.security.SecureRandom;
+import java.security.Security;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * http://b/28550092 : Removal of "Crypto" provider in N caused application compatibility
+ * issues for callers of SecureRandom. To improve compatibility the provider is not registered
+ * as a JCA Provider obtainable via Security.getProvider() but is made available for
+ * SecureRandom.getInstance() iff the application targets API <= 23.
+ */
+@RunWith(JUnit4.class)
+public class CryptoProviderWorkaroundTest {
+ @Test
+ public void cryptoProvider_withWorkaround_Success() throws Exception {
+ // Assert that SecureRandom is still using the default value. Sanity check.
+ assertEquals(SecureRandom.DEFAULT_SDK_TARGET_FOR_CRYPTO_PROVIDER_WORKAROUND,
+ SecureRandom.getSdkTargetForCryptoProviderWorkaround());
+
+ try {
+ // Modify the maximum target SDK to apply the workaround, thereby enabling the
+ // workaround for the current SDK and enabling it to be tested.
+ SecureRandom.setSdkTargetForCryptoProviderWorkaround(
+ VMRuntime.getRuntime().getTargetSdkVersion());
+
+ // Assert that the crypto provider is not installed...
+ assertNull(Security.getProvider("Crypto"));
+ SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
+ assertNotNull(sr);
+ // ...but we can get a SecureRandom from it...
+ assertEquals("org.apache.harmony.security.provider.crypto.CryptoProvider",
+ sr.getProvider().getClass().getName());
+ // ...yet it's not installed. So the workaround worked.
+ assertNull(Security.getProvider("Crypto"));
+ } finally {
+ // Reset the target SDK for the workaround to the default / real value.
+ SecureRandom.setSdkTargetForCryptoProviderWorkaround(
+ SecureRandom.DEFAULT_SDK_TARGET_FOR_CRYPTO_PROVIDER_WORKAROUND);
+ }
+ }
+
+ @Test
+ public void cryptoProvider_withoutWorkaround_Failure() throws Exception {
+ // Assert that SecureRandom is still using the default value. Sanity check.
+ assertEquals(SecureRandom.DEFAULT_SDK_TARGET_FOR_CRYPTO_PROVIDER_WORKAROUND,
+ SecureRandom.getSdkTargetForCryptoProviderWorkaround());
+
+ try {
+ // We set the limit SDK for the workaround at the previous one, indicating that the
+ // workaround shouldn't be in place.
+ SecureRandom.setSdkTargetForCryptoProviderWorkaround(
+ VMRuntime.getRuntime().getTargetSdkVersion() - 1);
+
+ SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
+ fail("Should throw " + NoSuchProviderException.class.getName());
+ } catch(NoSuchProviderException expected) {
+ // The workaround doesn't work. As expected.
+ } finally {
+ // Reset the target SDK for the workaround to the default / real value.
+ SecureRandom.setSdkTargetForCryptoProviderWorkaround(
+ SecureRandom.DEFAULT_SDK_TARGET_FOR_CRYPTO_PROVIDER_WORKAROUND);
+ }
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java b/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java
new file mode 100644
index 0000000..ac264d0
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 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 android.security.cts;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.security.cts.IIsolatedService;
+import android.security.cts.IsolatedService;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import com.android.internal.util.ArrayUtils;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import junit.framework.Assert;
+
+public class IsolatedProcessTest extends AndroidTestCase {
+ static final String TAG = IsolatedProcessTest.class.getSimpleName();
+
+ private static final long BIND_SERVICE_TIMEOUT = 5000;
+
+ // No service other than these should be visible to an isolated process
+ private static final String[] SERVICES_ALLOWED_TO_ISOLATED_PROCESS = {
+ "package",
+ Context.ACTIVITY_SERVICE
+ };
+ // Arbitrary set of services to test accessibility from an isolated process
+ private static final String[] RESTRICTED_SERVICES_TO_TEST = {
+ Context.ALARM_SERVICE,
+ Context.WINDOW_SERVICE,
+ Context.POWER_SERVICE
+ };
+
+ private CountDownLatch mLatch;
+ private IIsolatedService mService;
+ private final ServiceConnection mServiceConnection = new ServiceConnection() {
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.e(TAG, "Isolated service " + name + " died abruptly");
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mService = IIsolatedService.Stub.asInterface(service);
+ mLatch.countDown();
+ }
+ };
+
+ @Override
+ public void setUp() throws InterruptedException {
+ mLatch = new CountDownLatch(1);
+ Intent serviceIntent = new Intent(mContext, IsolatedService.class);
+ mContext.bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
+ Assert.assertTrue("Timed out while waiting to bind to isolated service",
+ mLatch.await(BIND_SERVICE_TIMEOUT, TimeUnit.MILLISECONDS));
+ }
+
+ public void testGetCachedServicesFromIsolatedService() throws RemoteException {
+ String[] cachedServices = mService.getCachedSystemServices();
+ for (String serviceName : cachedServices) {
+ Assert.assertTrue(serviceName + " should not be accessbible from an isolated process",
+ ArrayUtils.contains(SERVICES_ALLOWED_TO_ISOLATED_PROCESS, serviceName));
+ }
+ }
+
+ public void testGetServiceFromIsolatedService() throws RemoteException {
+ for (String serviceName : RESTRICTED_SERVICES_TO_TEST) {
+ IBinder service = mService.getSystemService(serviceName);
+ Assert.assertNull(serviceName + " should not be accessible from an isolated process",
+ service);
+ }
+ }
+
+ @Override
+ public void tearDown() {
+ mContext.unbindService(mServiceConnection);
+ }
+
+}
diff --git a/tests/tests/security/src/android/security/cts/IsolatedService.java b/tests/tests/security/src/android/security/cts/IsolatedService.java
new file mode 100644
index 0000000..28d2a65
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/IsolatedService.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 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 android.security.cts;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+public class IsolatedService extends Service {
+
+ private static final String TAG = IsolatedService.class.getSimpleName();
+ private static final String SERVICE_MANAGER_CLASS_NAME = "android.os.ServiceManager";
+ private static final String SERVICE_MANAGER_INTERNAL_CACHE_NAME = "sCache";
+ private static final String GET_SERVICE_METHOD_NAME = "getService";
+
+ private String[] getServicesCachedInServiceManager() {
+ ArrayList<String> cachedServices = new ArrayList<String>();
+ try {
+ Class<?> serviceManager = Class.forName(SERVICE_MANAGER_CLASS_NAME);
+ Field cacheField = serviceManager.getDeclaredField(SERVICE_MANAGER_INTERNAL_CACHE_NAME);
+ cacheField.setAccessible(true);
+ HashMap<String, IBinder> sCache = (HashMap<String, IBinder>) cacheField.get(null);
+ for (Map.Entry<String, IBinder> serviceEntry : sCache.entrySet()) {
+ if (serviceEntry.getValue() != null) {
+ cachedServices.add(serviceEntry.getKey());
+ }
+ }
+ } catch (ClassCastException | ReflectiveOperationException exc) {
+ Log.w(TAG, "Unable to retrieve service manager cache via reflection ", exc);
+ }
+ return cachedServices.toArray(new String[cachedServices.size()]);
+ }
+
+ private IBinder getServiceFromServiceManager(String serviceName) {
+ try {
+ Class<?> serviceManager = Class.forName(SERVICE_MANAGER_CLASS_NAME);
+ Method getServiceMethod =
+ serviceManager.getDeclaredMethod(GET_SERVICE_METHOD_NAME, String.class);
+ IBinder service = (IBinder) getServiceMethod.invoke(null, serviceName);
+ return service;
+ } catch (ClassCastException | ReflectiveOperationException exc) {
+ Log.w(TAG, "Unable to call ServiceManager.getService() ", exc);
+ }
+ return null;
+ }
+
+ private final IIsolatedService.Stub mBinder = new IIsolatedService.Stub() {
+
+ public String[] getCachedSystemServices() {
+ return getServicesCachedInServiceManager();
+ }
+
+ public IBinder getSystemService(String serviceName) {
+ return getServiceFromServiceManager(serviceName);
+ }
+ };
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index a2679ad..d3b08af 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -158,10 +158,18 @@
doStagefrightTest(R.raw.bug_28333006);
}
- public void testStagefright_bug_14388161() throws Exception {
+ public void testStagefright_bug_14388161() throws Exception {
doStagefrightTestMediaPlayer(R.raw.bug_14388161);
}
+ public void testStagefright_cve_2016_3755() throws Exception {
+ doStagefrightTest(R.raw.cve_2016_3755);
+ }
+
+ public void testStagefright_cve_2016_3878_b_29493002() throws Exception {
+ doStagefrightTest(R.raw.cve_2016_3878_b_29493002);
+ }
+
private void doStagefrightTest(final int rid) throws Exception {
doStagefrightTestMediaPlayer(rid);
doStagefrightTestMediaCodec(rid);
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockConference.java b/tests/tests/telecom/src/android/telecom/cts/MockConference.java
index d84610d..302f91f 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockConference.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockConference.java
@@ -143,6 +143,10 @@
public void setRemoteConference(RemoteConference remoteConference) {
mRemoteConference = remoteConference;
+ Bundle bundle = remoteConference.getExtras();
+ if (bundle != null) {
+ this.putExtras(bundle);
+ }
}
public RemoteConference getRemoteConference() {
@@ -155,6 +159,7 @@
@Override
public void onExtrasChanged(Bundle extras) {
+ setExtras(extras);
mOnExtrasChanged.invoke(extras);
}
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/RemoteConferenceTest.java b/tests/tests/telecom/src/android/telecom/cts/RemoteConferenceTest.java
index 3246b9c..57654f40 100644
--- a/tests/tests/telecom/src/android/telecom/cts/RemoteConferenceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/RemoteConferenceTest.java
@@ -455,7 +455,8 @@
mRemoteConference.setExtras(extras);
callbackInvoker.waitForCount(1, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
assertEquals(mRemoteConferenceObject, callbackInvoker.getArgs(0)[0]);
- assertTrue(areBundlesEqual(extras, (Bundle) callbackInvoker.getArgs(0)[1]));
+ assertTrue(((Bundle) callbackInvoker.getArgs(0)[1]).containsKey(
+ TelecomManager.EXTRA_CALL_DISCONNECT_MESSAGE));
mRemoteConferenceObject.unregisterCallback(callback);
}
@@ -469,8 +470,9 @@
remoteConnections.add(((MockConnection)c).getRemoteConnection());
}
assertEquals(remoteConnections, remoteConferenceObject.getConnections());
- assertEquals(remoteConference.getDisconnectCause(), remoteConferenceObject.getDisconnectCause());
- assertEquals(remoteConference.getExtras(), remoteConferenceObject.getExtras());
+ assertEquals(remoteConference.getDisconnectCause(),
+ remoteConferenceObject.getDisconnectCause());
+ assertTrue(areBundlesEqual(remoteConferenceObject.getExtras(), conference.getExtras()));
}
private void addRemoteConnectionOutgoingCalls() {
@@ -572,7 +574,7 @@
setupConnectionServices(managerConnectionService, remoteConnectionService,
FLAG_REGISTER | FLAG_ENABLE);
} catch(Exception e) {
- fail("Error in setting up the connection services");
+ fail("Error in setting up the connection services: " + e.toString());
}
placeAndVerifyCall();
diff --git a/tests/tests/transition/AndroidManifest.xml b/tests/tests/transition/AndroidManifest.xml
index 63de0cd..1fb4f0e 100644
--- a/tests/tests/transition/AndroidManifest.xml
+++ b/tests/tests/transition/AndroidManifest.xml
@@ -21,6 +21,8 @@
<application>
<activity android:name="android.transition.cts.TransitionActivity"
android:label="TransitionActivity"/>
+ <activity android:name="android.transition.cts.TargetActivity"
+ android:label="TargetActivity"/>
<uses-library android:name="android.test.runner" />
</application>
diff --git a/tests/tests/transition/res/layout/scene10.xml b/tests/tests/transition/res/layout/scene10.xml
index 16e3c20..5403354 100644
--- a/tests/tests/transition/res/layout/scene10.xml
+++ b/tests/tests/transition/res/layout/scene10.xml
@@ -14,33 +14,39 @@
limitations under the License.
-->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:transitionName="holder"
- android:id="@+id/holder">
- <View android:layout_width="10dp"
- android:layout_height="10dp"
- android:background="#F00"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:id="@+id/redSquare" />
- <View android:layout_width="10dp"
- android:layout_height="10dp"
- android:background="#0F0"
- android:layout_alignParentRight="true"
- android:layout_alignParentTop="true"
- android:id="@+id/greenSquare"/>
- <View android:layout_width="10dp"
- android:layout_height="10dp"
- android:background="#00F"
- android:layout_alignParentRight="true"
- android:layout_alignParentBottom="true"
- android:id="@+id/blueSquare" />
- <View android:layout_width="10dp"
- android:layout_height="10dp"
- android:background="#FF0"
- android:layout_alignParentLeft="true"
- android:layout_alignParentBottom="true"
- android:id="@+id/yellowSquare"/>
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:transitionGroup="false"
+ android:transitionName="holder"
+ android:id="@+id/holder">
+ <View
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#F00"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:id="@+id/redSquare"/>
+ <View
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#0F0"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentTop="true"
+ android:id="@+id/greenSquare"/>
+ <View
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#00F"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentBottom="true"
+ android:id="@+id/blueSquare"/>
+ <View
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#FF0"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentBottom="true"
+ android:id="@+id/yellowSquare"/>
</RelativeLayout>
diff --git a/tests/tests/transition/res/layout/scene11.xml b/tests/tests/transition/res/layout/scene11.xml
index 8ecad8d..dc6ef19 100644
--- a/tests/tests/transition/res/layout/scene11.xml
+++ b/tests/tests/transition/res/layout/scene11.xml
@@ -14,11 +14,12 @@
limitations under the License.
-->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:transitionName="holder"
- android:id="@+id/holder">
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:transitionName="holder"
+ android:id="@+id/holder">
<ImageView
android:id="@+id/redSquare"
android:src="#F00"
diff --git a/tests/tests/transition/res/layout/scene12.xml b/tests/tests/transition/res/layout/scene12.xml
new file mode 100644
index 0000000..559ab39
--- /dev/null
+++ b/tests/tests/transition/res/layout/scene12.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:transitionName="holder"
+ android:transitionGroup="false"
+ android:orientation="horizontal"
+ android:id="@+id/holder">
+ <View
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#F00"
+ android:id="@+id/redSquare"/>
+ <View
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#0F0"
+ android:id="@+id/greenSquare"/>
+ <View
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#00F"
+ android:id="@+id/blueSquare"/>
+ <View
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#FF0"
+ android:id="@+id/yellowSquare"/>
+</LinearLayout>
diff --git a/tests/tests/transition/res/layout/scene13.xml b/tests/tests/transition/res/layout/scene13.xml
new file mode 100644
index 0000000..b7c783f
--- /dev/null
+++ b/tests/tests/transition/res/layout/scene13.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:transitionGroup="false"
+ android:transitionName="holder"
+ android:id="@+id/holder">
+ <View
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#F00"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentTop="true"
+ android:transitionName="redSquare"
+ android:id="@+id/redSquare"/>
+ <View
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#0F0"
+ android:layout_toRightOf="@id/redSquare"
+ android:id="@+id/greenSquare"/>
+ <View
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#00F"
+ android:layout_toRightOf="@id/greenSquare"
+ android:id="@+id/blueSquare"/>
+ <View
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#FF0"
+ android:layout_toRightOf="@id/blueSquare"
+ android:id="@+id/yellowSquare"/>
+</RelativeLayout>
diff --git a/tests/tests/transition/src/android/transition/cts/ActivityTransitionTest.java b/tests/tests/transition/src/android/transition/cts/ActivityTransitionTest.java
new file mode 100644
index 0000000..e568072
--- /dev/null
+++ b/tests/tests/transition/src/android/transition/cts/ActivityTransitionTest.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2016 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 android.transition.cts;
+
+import static android.cts.util.CtsMockitoUtils.within;
+
+import static junit.framework.Assert.*;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityOptions;
+import android.content.Intent;
+import android.cts.util.PollingCheck;
+import android.cts.util.transition.TargetTracking;
+import android.cts.util.transition.TrackingTransition;
+import android.cts.util.transition.TrackingVisibility;
+import android.os.Bundle;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.transition.Transition.TransitionListener;
+import android.view.View;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ActivityTransitionTest extends BaseTransitionTest {
+ private TransitionListener mExitListener;
+ private TransitionListener mReenterListener;
+ private TransitionListener mSharedElementReenterListener;
+ private TrackingVisibility mExitTransition;
+ private TrackingVisibility mReenterTransition;
+ private TrackingTransition mSharedElementReenterTransition;
+
+ @Override
+ public void setup() {
+ super.setup();
+ mExitTransition = new TrackingVisibility();
+ mExitListener = mock(TransitionListener.class);
+ mExitTransition.addListener(mExitListener);
+ mActivity.getWindow().setExitTransition(mExitTransition);
+
+ mReenterTransition = new TrackingVisibility();
+ mReenterListener = mock(TransitionListener.class);
+ mReenterTransition.addListener(mReenterListener);
+ mActivity.getWindow().setReenterTransition(mReenterTransition);
+
+ mSharedElementReenterTransition = new TrackingTransition();
+ mSharedElementReenterListener = mock(TransitionListener.class);
+ mSharedElementReenterTransition.addListener(mSharedElementReenterListener);
+ mActivity.getWindow().setSharedElementReenterTransition(mSharedElementReenterTransition);
+ }
+
+ @After
+ public void cleanup() {
+ if (TargetActivity.sLastCreated != null) {
+ mInstrumentation.runOnMainSync(() -> TargetActivity.sLastCreated.finish());
+ }
+ TargetActivity.sLastCreated = null;
+ }
+
+ // Views that are outside the visible area only during the shared element start
+ // should not be stripped from the transition.
+ @Test
+ public void viewsNotStripped() throws Throwable {
+ enterScene(R.layout.scene10);
+ mInstrumentation.runOnMainSync(() -> {
+ View sharedElement = mActivity.findViewById(R.id.blueSquare);
+ Bundle options = ActivityOptions.makeSceneTransitionAnimation(mActivity,
+ sharedElement, "holder").toBundle();
+ Intent intent = new Intent(mActivity, TargetActivity.class);
+ intent.putExtra(TargetActivity.EXTRA_LAYOUT_ID, R.layout.scene12);
+ mActivity.startActivity(intent, options);
+ });
+
+ TargetActivity targetActivity = waitForTargetActivity();
+ verify(targetActivity.enterListener, within(3000)).onTransitionEnd(any());
+ verify(mExitListener, times(1)).onTransitionEnd(any());
+
+ // Now check the targets... they should all be there
+ assertTargetContains(targetActivity.enterTransition,
+ R.id.redSquare, R.id.greenSquare, R.id.blueSquare, R.id.yellowSquare);
+ assertTargetExcludes(targetActivity.enterTransition, R.id.holder);
+
+ assertTargetContains(targetActivity.sharedElementEnterTransition, R.id.holder);
+ assertTargetExcludes(targetActivity.sharedElementEnterTransition,
+ R.id.redSquare, R.id.greenSquare, R.id.blueSquare, R.id.yellowSquare);
+
+ assertTargetContains(mExitTransition, R.id.redSquare, R.id.greenSquare, R.id.yellowSquare);
+ assertTargetExcludes(mExitTransition, R.id.blueSquare, R.id.holder);
+
+ assertEquals(View.VISIBLE, targetActivity.findViewById(R.id.redSquare).getVisibility());
+ assertEquals(View.VISIBLE, targetActivity.findViewById(R.id.greenSquare).getVisibility());
+ assertEquals(View.VISIBLE, targetActivity.findViewById(R.id.holder).getVisibility());
+
+ assertEquals(1, targetActivity.findViewById(R.id.redSquare).getAlpha(), 0.01f);
+ assertEquals(1, targetActivity.findViewById(R.id.greenSquare).getAlpha(), 0.01f);
+ assertEquals(1, targetActivity.findViewById(R.id.holder).getAlpha(), 0.01f);
+
+ mInstrumentation.runOnMainSync(() -> targetActivity.finishAfterTransition());
+ verify(mReenterListener, within(3000)).onTransitionEnd(any());
+ verify(mSharedElementReenterListener, within(3000)).onTransitionEnd(any());
+ verify(targetActivity.returnListener, times(1)).onTransitionEnd(any());
+
+ // return targets are stripped also
+ assertTargetContains(targetActivity.returnTransition,
+ R.id.redSquare, R.id.greenSquare, R.id.blueSquare, R.id.yellowSquare);
+ assertTargetExcludes(targetActivity.returnTransition, R.id.holder);
+
+ assertTargetContains(mReenterTransition,
+ R.id.redSquare, R.id.greenSquare, R.id.yellowSquare);
+ assertTargetExcludes(mReenterTransition, R.id.blueSquare, R.id.holder);
+
+ assertTargetContains(targetActivity.sharedElementReturnTransition,
+ R.id.holder);
+ assertTargetExcludes(targetActivity.sharedElementReturnTransition,
+ R.id.redSquare, R.id.greenSquare, R.id.blueSquare, R.id.yellowSquare);
+
+ assertTargetContains(mSharedElementReenterTransition, R.id.blueSquare);
+ assertTargetExcludes(mSharedElementReenterTransition,
+ R.id.redSquare, R.id.greenSquare, R.id.yellowSquare);
+
+ assertEquals(View.VISIBLE, mActivity.findViewById(R.id.redSquare).getVisibility());
+ assertEquals(View.VISIBLE, mActivity.findViewById(R.id.greenSquare).getVisibility());
+ assertEquals(View.VISIBLE, mActivity.findViewById(R.id.holder).getVisibility());
+
+ assertEquals(1, mActivity.findViewById(R.id.redSquare).getAlpha(), 0.01f);
+ assertEquals(1, mActivity.findViewById(R.id.greenSquare).getAlpha(), 0.01f);
+ assertEquals(1, mActivity.findViewById(R.id.holder).getAlpha(), 0.01f);
+
+ TargetActivity.sLastCreated = null;
+ }
+
+ // Views that are outside the visible area during initial layout should be stripped from
+ // the transition.
+ @Test
+ public void viewsStripped() throws Throwable {
+ enterScene(R.layout.scene13);
+ mInstrumentation.runOnMainSync(() -> {
+ View sharedElement = mActivity.findViewById(R.id.redSquare);
+ Bundle options = ActivityOptions.makeSceneTransitionAnimation(mActivity,
+ sharedElement, "redSquare").toBundle();
+ Intent intent = new Intent(mActivity, TargetActivity.class);
+ intent.putExtra(TargetActivity.EXTRA_LAYOUT_ID, R.layout.scene13);
+ mActivity.startActivity(intent, options);
+ });
+
+ TargetActivity targetActivity = waitForTargetActivity();
+ verify(targetActivity.enterListener, within(3000)).onTransitionEnd(any());
+ verify(mExitListener, times(1)).onTransitionEnd(any());
+
+ // Now check the targets... they should all be stripped
+ assertTargetExcludes(targetActivity.enterTransition, R.id.holder,
+ R.id.redSquare, R.id.greenSquare, R.id.blueSquare, R.id.yellowSquare);
+
+ assertTargetExcludes(mExitTransition, R.id.holder,
+ R.id.redSquare, R.id.greenSquare, R.id.blueSquare, R.id.yellowSquare);
+
+ assertTargetContains(targetActivity.sharedElementEnterTransition, R.id.redSquare);
+ assertTargetExcludes(targetActivity.sharedElementEnterTransition,
+ R.id.greenSquare, R.id.blueSquare, R.id.yellowSquare);
+
+ assertEquals(View.VISIBLE, targetActivity.findViewById(R.id.redSquare).getVisibility());
+ assertEquals(View.VISIBLE, targetActivity.findViewById(R.id.greenSquare).getVisibility());
+ assertEquals(View.VISIBLE, targetActivity.findViewById(R.id.holder).getVisibility());
+
+ assertEquals(1, targetActivity.findViewById(R.id.redSquare).getAlpha(), 0.01f);
+ assertEquals(1, targetActivity.findViewById(R.id.greenSquare).getAlpha(), 0.01f);
+ assertEquals(1, targetActivity.findViewById(R.id.holder).getAlpha(), 0.01f);
+
+ mInstrumentation.runOnMainSync(() -> targetActivity.finishAfterTransition());
+ verify(mReenterListener, within(3000)).onTransitionEnd(any());
+ verify(mSharedElementReenterListener, within(3000)).onTransitionEnd(any());
+ verify(targetActivity.returnListener, times(1)).onTransitionEnd(any());
+
+ // return targets are stripped also
+ assertTargetExcludes(targetActivity.returnTransition,
+ R.id.redSquare, R.id.greenSquare, R.id.blueSquare, R.id.yellowSquare);
+
+ assertTargetExcludes(mReenterTransition, R.id.holder,
+ R.id.redSquare, R.id.greenSquare, R.id.blueSquare, R.id.yellowSquare);
+
+ assertTargetContains(targetActivity.sharedElementReturnTransition,
+ R.id.redSquare);
+ assertTargetExcludes(targetActivity.sharedElementReturnTransition,
+ R.id.greenSquare, R.id.blueSquare, R.id.yellowSquare);
+
+ assertTargetContains(mSharedElementReenterTransition, R.id.redSquare);
+ assertTargetExcludes(mSharedElementReenterTransition,
+ R.id.blueSquare, R.id.greenSquare, R.id.yellowSquare);
+
+ assertEquals(View.VISIBLE, mActivity.findViewById(R.id.greenSquare).getVisibility());
+ assertEquals(View.VISIBLE, mActivity.findViewById(R.id.holder).getVisibility());
+ assertEquals(View.VISIBLE, mActivity.findViewById(R.id.redSquare).getVisibility());
+
+ assertEquals(1, mActivity.findViewById(R.id.redSquare).getAlpha(), 0.01f);
+ assertEquals(1, mActivity.findViewById(R.id.greenSquare).getAlpha(), 0.01f);
+ assertEquals(1, mActivity.findViewById(R.id.holder).getAlpha(), 0.01f);
+
+ TargetActivity.sLastCreated = null;
+ }
+
+ private TargetActivity waitForTargetActivity() {
+ PollingCheck.waitFor(() -> TargetActivity.sLastCreated != null);
+ // Just make sure that we're not in the middle of running on the UI thread.
+ mInstrumentation.runOnMainSync(() -> {});
+ return TargetActivity.sLastCreated;
+ }
+
+ private Set<Integer> getTargetViewIds(TargetTracking transition) {
+ return transition.getTrackedTargets().stream()
+ .map(v -> v.getId())
+ .collect(Collectors.toSet());
+ }
+
+ private void assertTargetContains(TargetTracking transition, int... ids) {
+ Set<Integer> targets = getTargetViewIds(transition);
+ for (int id : ids) {
+ assertTrueWithId(id, "%s was not included from the transition", targets.contains(id));
+ }
+ }
+
+ private void assertTargetExcludes(TargetTracking transition, int... ids) {
+ Set<Integer> targets = getTargetViewIds(transition);
+ for (int id : ids) {
+ assertTrueWithId(id, "%s was not excluded from the transition", !targets.contains(id));
+ }
+ }
+
+ private void assertTrueWithId(int id, String message, boolean valueToAssert) {
+ if (!valueToAssert) {
+ fail(String.format(message, mActivity.getResources().getResourceName(id)));
+ }
+ }
+}
diff --git a/tests/tests/transition/src/android/transition/cts/TargetActivity.java b/tests/tests/transition/src/android/transition/cts/TargetActivity.java
new file mode 100644
index 0000000..dd4bd08
--- /dev/null
+++ b/tests/tests/transition/src/android/transition/cts/TargetActivity.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 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 android.transition.cts;
+
+import static org.mockito.Mockito.mock;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.cts.util.transition.TrackingTransition;
+import android.cts.util.transition.TrackingVisibility;
+import android.os.Bundle;
+import android.transition.Transition.TransitionListener;
+
+public class TargetActivity extends Activity {
+ public static final String EXTRA_LAYOUT_ID = "layoutId";
+
+ final TrackingVisibility enterTransition = new TrackingVisibility();
+ final TrackingVisibility returnTransition = new TrackingVisibility();
+ final TrackingTransition sharedElementEnterTransition = new TrackingTransition();
+ final TrackingTransition sharedElementReturnTransition = new TrackingTransition();
+
+ final TransitionListener enterListener = mock(TransitionListener.class);
+ final TransitionListener returnListener = mock(TransitionListener.class);
+
+ public static TargetActivity sLastCreated;
+
+ @Override
+ public void onCreate(Bundle bundle){
+ super.onCreate(bundle);
+ Intent intent = getIntent();
+ int layoutId = R.layout.transition_main;
+ if (intent != null) {
+ layoutId = intent.getIntExtra(EXTRA_LAYOUT_ID, layoutId);
+ }
+ setContentView(layoutId);
+ getWindow().setEnterTransition(enterTransition);
+ getWindow().setReturnTransition(returnTransition);
+ getWindow().setSharedElementEnterTransition(sharedElementEnterTransition);
+ getWindow().setSharedElementReturnTransition(sharedElementReturnTransition);
+ enterTransition.addListener(enterListener);
+ returnTransition.addListener(returnListener);
+
+ sLastCreated = this;
+ }
+}
diff --git a/tests/tests/uirendering/Android.mk b/tests/tests/uirendering/Android.mk
index f101fcc..4dd9b1d 100644
--- a/tests/tests/uirendering/Android.mk
+++ b/tests/tests/uirendering/Android.mk
@@ -28,6 +28,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
ctstestrunner \
+ ctsdeviceutil \
mockito-target-minus-junit4 \
android-support-test
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
index 7c27d5d..61d239b 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
@@ -17,8 +17,8 @@
package android.uirendering.cts.testclasses;
import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
import android.annotation.ColorInt;
-import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
@@ -152,8 +152,8 @@
root.addView(child);
mAnimator = ObjectAnimator.ofInt(child, "translationY", 0, 20);
- mAnimator.setRepeatMode(mAnimator.REVERSE);
- mAnimator.setRepeatCount(mAnimator.INFINITE);
+ mAnimator.setRepeatMode(ValueAnimator.REVERSE);
+ mAnimator.setRepeatCount(ValueAnimator.INFINITE);
mAnimator.setDuration(200);
mAnimator.start();
}
@@ -165,8 +165,7 @@
createTest()
.addLayout(R.layout.frame_layout, initializer, true)
- .runWithAnimationVerifier(new ColorCountVerifier(Color.WHITE, 90 * 90 - 50 * 50),
- null);
+ .runWithAnimationVerifier(new ColorCountVerifier(Color.WHITE, 90 * 90 - 50 * 50));
}
@Test
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SurfaceViewTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SurfaceViewTests.java
index 64c4b77..8b83dc0 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SurfaceViewTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SurfaceViewTests.java
@@ -16,9 +16,13 @@
package android.uirendering.cts.testclasses;
import android.animation.ObjectAnimator;
+import android.cts.util.SynchronousPixelCopy;
import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.support.test.filters.MediumTest;
import android.uirendering.cts.R;
import android.uirendering.cts.bitmapverifiers.ColorVerifier;
@@ -26,14 +30,18 @@
import android.uirendering.cts.testinfrastructure.CanvasClient;
import android.uirendering.cts.testinfrastructure.ViewInitializer;
import android.view.Gravity;
+import android.view.PixelCopy;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.FrameLayout;
+import org.junit.Assert;
import org.junit.Test;
+import java.util.concurrent.CountDownLatch;
+
@MediumTest
public class SurfaceViewTests extends ActivityTestBase {
@@ -99,13 +107,79 @@
}
};
Screenshotter screenshotter = testOffset -> {
- getInstrumentation().waitForIdleSync();
Bitmap source = getInstrumentation().getUiAutomation().takeScreenshot();
return Bitmap.createBitmap(source, testOffset.x, testOffset.y, TEST_WIDTH, TEST_HEIGHT);
};
createTest()
.addLayout(R.layout.frame_layout, initializer, true)
- .runWithAnimationVerifier(new ColorVerifier(Color.WHITE, 0 /* zero tolerance */),
- screenshotter);
+ .withScreenshotter(screenshotter)
+ .runWithAnimationVerifier(new ColorVerifier(Color.WHITE, 0 /* zero tolerance */));
+ }
+
+ private static class SurfaceViewHelper implements ViewInitializer, Screenshotter, SurfaceHolder.Callback {
+ private final CanvasClient mCanvasClient;
+ private final CountDownLatch mFence = new CountDownLatch(1);
+ private SurfaceView mSurfaceView;
+
+ public SurfaceViewHelper(CanvasClient canvasClient) {
+ mCanvasClient = canvasClient;
+ }
+
+ @Override
+ public Bitmap takeScreenshot(Point point /* ignored */) {
+ SynchronousPixelCopy copy = new SynchronousPixelCopy();
+ Bitmap dest = Bitmap.createBitmap(
+ TEST_WIDTH, TEST_HEIGHT, Config.ARGB_8888);
+ Rect srcRect = new Rect(0, 0, TEST_WIDTH, TEST_HEIGHT);
+ int copyResult = copy.request(mSurfaceView, srcRect, dest);
+ Assert.assertEquals(PixelCopy.SUCCESS, copyResult);
+ return dest;
+ }
+
+ @Override
+ public void initializeView(View view) {
+ FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout);
+ mSurfaceView = new SurfaceView(view.getContext());
+ mSurfaceView.getHolder().addCallback(this);
+ root.addView(mSurfaceView, new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT));
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ // TODO: Remove the post() which is a temporary workaround for b/32484713
+ mSurfaceView.post(() -> {
+ Canvas canvas = holder.lockHardwareCanvas();
+ mCanvasClient.draw(canvas, width, height);
+ holder.unlockCanvasAndPost(canvas);
+ mFence.countDown();
+ });
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ }
+
+ public CountDownLatch getFence() {
+ return mFence;
+ }
+ }
+
+ @Test
+ public void testSurfaceHolderHardwareCanvas() {
+ SurfaceViewHelper helper = new SurfaceViewHelper((canvas, width, height) -> {
+ Assert.assertNotNull(canvas);
+ Assert.assertTrue(canvas.isHardwareAccelerated());
+ canvas.drawColor(Color.GREEN);
+ });
+ createTest()
+ .addLayout(R.layout.frame_layout, helper, true, helper.getFence())
+ .withScreenshotter(helper)
+ .runWithVerifier(new ColorVerifier(Color.GREEN, 0 /* zero tolerance */));
}
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
index 76c4ee3..f961cdb 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
@@ -15,25 +15,20 @@
*/
package android.uirendering.cts.testinfrastructure;
-import static org.junit.Assert.fail;
-
import android.annotation.Nullable;
import android.app.Instrumentation;
import android.content.Intent;
+import android.cts.util.SynchronousPixelCopy;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Point;
import android.graphics.Rect;
-import android.os.Handler;
-import android.os.HandlerThread;
import android.support.test.InstrumentationRegistry;
import android.uirendering.cts.bitmapcomparers.BitmapComparer;
import android.uirendering.cts.bitmapverifiers.BitmapVerifier;
import android.uirendering.cts.util.BitmapAsserter;
import android.util.Log;
import android.view.PixelCopy;
-import android.view.PixelCopy.OnPixelCopyFinishedListener;
-import android.view.Window;
import org.junit.After;
import org.junit.AfterClass;
@@ -61,6 +56,7 @@
public static final int TEST_HEIGHT = 90;
private TestCaseBuilder mTestCaseBuilder;
+ private Screenshotter mScreenshotter;
private static DrawActivity sActivity;
@@ -124,14 +120,18 @@
}
public Bitmap takeScreenshot(Point testOffset) {
- SynchronousPixelCopy copy = new SynchronousPixelCopy();
- Bitmap dest = Bitmap.createBitmap(
- TEST_WIDTH, TEST_HEIGHT, Config.ARGB_8888);
- Rect srcRect = new Rect(testOffset.x, testOffset.y,
- testOffset.x + TEST_WIDTH, testOffset.y + TEST_HEIGHT);
- int copyResult = copy.request(getActivity().getWindow(), srcRect, dest);
- Assert.assertEquals(PixelCopy.SUCCESS, copyResult);
- return dest;
+ if (mScreenshotter == null) {
+ SynchronousPixelCopy copy = new SynchronousPixelCopy();
+ Bitmap dest = Bitmap.createBitmap(
+ TEST_WIDTH, TEST_HEIGHT, Config.ARGB_8888);
+ Rect srcRect = new Rect(testOffset.x, testOffset.y,
+ testOffset.x + TEST_WIDTH, testOffset.y + TEST_HEIGHT);
+ int copyResult = copy.request(getActivity().getWindow(), srcRect, dest);
+ Assert.assertEquals(PixelCopy.SUCCESS, copyResult);
+ return dest;
+ } else {
+ return mScreenshotter.takeScreenshot(testOffset);
+ }
}
protected Point runRenderSpec(TestCase testCase) {
@@ -159,6 +159,7 @@
protected TestCaseBuilder createTest() {
mTestCaseBuilder = new TestCaseBuilder();
+ mScreenshotter = null;
return mTestCaseBuilder;
}
@@ -221,8 +222,7 @@
* A screenshot is captured several times in a loop, to ensure that valid output is produced
* at many different times during the animation.
*/
- public void runWithAnimationVerifier(BitmapVerifier bitmapVerifier,
- @Nullable Screenshotter screenshotter) {
+ public void runWithAnimationVerifier(BitmapVerifier bitmapVerifier) {
if (mTestCases.size() == 0) {
throw new IllegalStateException("Need at least one test to run");
}
@@ -236,12 +236,7 @@
} catch (InterruptedException e) {
e.printStackTrace();
}
- Bitmap testCaseBitmap;
- if (screenshotter == null) {
- testCaseBitmap = takeScreenshot(testOffset);
- } else {
- testCaseBitmap = screenshotter.takeScreenshot(testOffset);
- }
+ Bitmap testCaseBitmap = takeScreenshot(testOffset);
mBitmapAsserter.assertBitmapIsVerified(testCaseBitmap, bitmapVerifier,
getName(), testCase.getDebugString());
}
@@ -262,6 +257,12 @@
});
}
+ public TestCaseBuilder withScreenshotter(Screenshotter screenshotter) {
+ Assert.assertNull("Screenshotter is already set!", mScreenshotter);
+ mScreenshotter = screenshotter;
+ return this;
+ }
+
public TestCaseBuilder addLayout(int layoutId, @Nullable ViewInitializer viewInitializer) {
return addLayout(layoutId, viewInitializer, false)
.addLayout(layoutId, viewInitializer, true);
@@ -349,39 +350,4 @@
return debug;
}
}
-
- private static class SynchronousPixelCopy implements OnPixelCopyFinishedListener {
- private static Handler sHandler;
- static {
- HandlerThread thread = new HandlerThread("PixelCopyHelper");
- thread.start();
- sHandler = new Handler(thread.getLooper());
- }
-
- private int mStatus = -1;
-
- public int request(Window source, Rect srcRect, Bitmap dest) {
- synchronized (this) {
- PixelCopy.request(source, srcRect, dest, this, sHandler);
- return getResultLocked();
- }
- }
-
- private int getResultLocked() {
- try {
- this.wait(250);
- } catch (InterruptedException e) {
- fail("PixelCopy request didn't complete within 250ms");
- }
- return mStatus;
- }
-
- @Override
- public void onPixelCopyFinished(int copyResult) {
- synchronized (this) {
- mStatus = copyResult;
- this.notify();
- }
- }
- }
}
diff --git a/tests/tests/util/src/android/util/cts/HalfTest.java b/tests/tests/util/src/android/util/cts/HalfTest.java
new file mode 100644
index 0000000..a82a982
--- /dev/null
+++ b/tests/tests/util/src/android/util/cts/HalfTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2016 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 android.util.cts;
+
+import android.util.Half;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static android.util.Half.*;
+
+import static org.junit.Assert.assertEquals;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class HalfTest {
+ private static void assertShortEquals(short a, short b) {
+ assertEquals((long) (a & 0xffff), (long) (b & 0xffff));
+ }
+
+ private static void assertShortEquals(int a, short b) {
+ assertEquals((long) (a & 0xffff), (long) (b & 0xffff));
+ }
+
+ @Test
+ public void testSingleToHalf() {
+ // Zeroes, NaN and infinities
+ assertShortEquals(POSITIVE_ZERO, valueOf(0.0f));
+ assertShortEquals(NEGATIVE_ZERO, valueOf(-0.0f));
+ assertShortEquals(NaN, valueOf(Float.NaN));
+ assertShortEquals(POSITIVE_INFINITY, valueOf(Float.POSITIVE_INFINITY));
+ assertShortEquals(NEGATIVE_INFINITY, valueOf(Float.NEGATIVE_INFINITY));
+ // Known values
+ assertShortEquals(0x3c01, valueOf(1.0009765625f));
+ assertShortEquals(0xc000, valueOf(-2.0f));
+ assertShortEquals(0x0400, valueOf(6.10352e-5f));
+ assertShortEquals(0x7bff, valueOf(65504.0f));
+ assertShortEquals(0x3555, valueOf(1.0f / 3.0f));
+ // Denormals
+ assertShortEquals(0x03ff, valueOf(6.09756e-5f));
+ assertShortEquals(MIN_VALUE, valueOf(5.96046e-8f));
+ assertShortEquals(0x83ff, valueOf(-6.09756e-5f));
+ assertShortEquals(0x8001, valueOf(-5.96046e-8f));
+ // Denormals (flushed to +/-0)
+ assertShortEquals(POSITIVE_ZERO, valueOf(5.96046e-9f));
+ assertShortEquals(NEGATIVE_ZERO, valueOf(-5.96046e-9f));
+ }
+
+ @Test
+ public void testHalfToSingle() {
+ // Zeroes, NaN and infinities
+ assertEquals(0.0f, toFloat(valueOf(0.0f)), 0.00001f);
+ assertEquals(-0.0f, toFloat(valueOf(-0.0f)), 0.00001f);
+ assertEquals(Float.NaN, toFloat(valueOf(Float.NaN)), 0.00001f);
+ assertEquals(Float.POSITIVE_INFINITY, toFloat(valueOf(Float.POSITIVE_INFINITY)), 0.00001f);
+ assertEquals(Float.NEGATIVE_INFINITY, toFloat(valueOf(Float.NEGATIVE_INFINITY)), 0.00001f);
+ // Known values
+ assertEquals(1.0009765625f, toFloat(valueOf(1.0009765625f)), 0.00001f);
+ assertEquals(-2.0f, toFloat(valueOf(-2.0f)), 0.00001f);
+ assertEquals(6.1035156e-5f, toFloat(valueOf(6.10352e-5f)), 0.00001f); // Inexact
+ assertEquals(65504.0f, toFloat(valueOf(65504.0f)), 0.00001f);
+ assertEquals(0.33325195f, toFloat(valueOf(1.0f / 3.0f)), 0.00001f); // Inexact
+ // Denormals (flushed to +/-0)
+ assertEquals(6.097555e-5f, toFloat(valueOf(6.09756e-5f)), 1e-6f);
+ assertEquals(5.9604645e-8f, toFloat(valueOf(5.96046e-8f)), 1e-9f);
+ assertEquals(-6.097555e-5f, toFloat(valueOf(-6.09756e-5f)), 1e-6f);
+ assertEquals(-5.9604645e-8f, toFloat(valueOf(-5.96046e-8f)), 1e-9f);
+ }
+
+ @Test
+ public void testHexString() {
+ assertEquals("NaN", toHexString(NaN));
+ assertEquals("Infinity", toHexString(POSITIVE_INFINITY));
+ assertEquals("-Infinity", toHexString(NEGATIVE_INFINITY));
+ assertEquals("0x0.0p0", toHexString(POSITIVE_ZERO));
+ assertEquals("-0x0.0p0", toHexString(NEGATIVE_ZERO));
+ assertEquals("0x1.0p0", toHexString(valueOf(1.0f)));
+ assertEquals("-0x1.0p0", toHexString(valueOf(-1.0f)));
+ assertEquals("0x1.0p1", toHexString(valueOf(2.0f)));
+ assertEquals("0x1.0p8", toHexString(valueOf(256.0f)));
+ assertEquals("0x1.0p-1", toHexString(valueOf(0.5f)));
+ assertEquals("0x1.0p-2", toHexString(valueOf(0.25f)));
+ assertEquals("0x1.3ffp15", toHexString(MAX_VALUE));
+ assertEquals("0x0.1p-14", toHexString(MIN_VALUE));
+ assertEquals("0x1.0p-14", toHexString(MIN_NORMAL));
+ assertEquals("-0x1.3ffp15", toHexString(LOWEST_VALUE));
+ }
+
+ @Test
+ public void testString() {
+ assertEquals("NaN", Half.toString(NaN));
+ assertEquals("Infinity", Half.toString(POSITIVE_INFINITY));
+ assertEquals("-Infinity", Half.toString(NEGATIVE_INFINITY));
+ assertEquals("0.0", Half.toString(POSITIVE_ZERO));
+ assertEquals("-0.0", Half.toString(NEGATIVE_ZERO));
+ assertEquals("1.0", Half.toString(valueOf(1.0f)));
+ assertEquals("-1.0", Half.toString(valueOf(-1.0f)));
+ assertEquals("2.0", Half.toString(valueOf(2.0f)));
+ assertEquals("256.0", Half.toString(valueOf(256.0f)));
+ assertEquals("0.5", Half.toString(valueOf(0.5f)));
+ assertEquals("0.25", Half.toString(valueOf(0.25f)));
+ assertEquals("65504.0", Half.toString(MAX_VALUE));
+ assertEquals("5.9604645E-8", Half.toString(MIN_VALUE));
+ assertEquals("6.1035156E-5", Half.toString(MIN_NORMAL));
+ assertEquals("-65504.0", Half.toString(LOWEST_VALUE));
+ }
+}
diff --git a/tests/tests/util/src/android/util/cts/TimeUtilsTest.java b/tests/tests/util/src/android/util/cts/TimeUtilsTest.java
index d6d1453..3362caf 100644
--- a/tests/tests/util/src/android/util/cts/TimeUtilsTest.java
+++ b/tests/tests/util/src/android/util/cts/TimeUtilsTest.java
@@ -118,4 +118,33 @@
c.getTimeInMillis(),
country);
}
+
+ @Test
+ public void testFormatDuration() {
+ assertFormatDuration("0", 0);
+ assertFormatDuration("-1ms", -1);
+ assertFormatDuration("+1ms", 1);
+ assertFormatDuration("+10ms", 10);
+ assertFormatDuration("+100ms", 100);
+ assertFormatDuration("+101ms", 101);
+ assertFormatDuration("+330ms", 330);
+ assertFormatDuration("+1s0ms", 1000);
+ assertFormatDuration("+1s330ms", 1330);
+ assertFormatDuration("+10s24ms", 10024);
+ assertFormatDuration("+1m0s30ms", 60030);
+ assertFormatDuration("+1h0m0s30ms", 3600030);
+ assertFormatDuration("+1d0h0m0s30ms", 86400030);
+ }
+
+ @Test
+ public void testFormatHugeDuration() {
+ assertFormatDuration("+15542d1h11m11s555ms", 1342833071555L);
+ assertFormatDuration("-15542d1h11m11s555ms", -1342833071555L);
+ }
+
+ private void assertFormatDuration(String expected, long duration) {
+ StringBuilder sb = new StringBuilder();
+ TimeUtils.formatDuration(duration, sb);
+ assertEquals("formatDuration(" + duration + ")", expected, sb.toString());
+ }
}
diff --git a/tests/tests/view/src/android/view/cts/PixelCopyTest.java b/tests/tests/view/src/android/view/cts/PixelCopyTest.java
index e9a32d4..7c4ca3e 100644
--- a/tests/tests/view/src/android/view/cts/PixelCopyTest.java
+++ b/tests/tests/view/src/android/view/cts/PixelCopyTest.java
@@ -24,6 +24,7 @@
import static org.mockito.Mockito.when;
import android.app.Instrumentation;
+import android.cts.util.SynchronousPixelCopy;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Color;
@@ -31,8 +32,6 @@
import android.graphics.SurfaceTexture;
import android.os.Debug;
import android.os.Debug.MemoryInfo;
-import android.os.Handler;
-import android.os.HandlerThread;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
import android.support.test.filters.MediumTest;
@@ -40,9 +39,7 @@
import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
import android.view.PixelCopy;
-import android.view.PixelCopy.OnPixelCopyFinishedListener;
import android.view.Surface;
-import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
@@ -451,76 +448,6 @@
return (error < threshold);
}
- private static class SynchronousPixelCopy implements OnPixelCopyFinishedListener {
- private static Handler sHandler;
- static {
- HandlerThread thread = new HandlerThread("PixelCopyHelper");
- thread.start();
- sHandler = new Handler(thread.getLooper());
- }
-
- private int mStatus = -1;
-
- public int request(Surface source, Bitmap dest) {
- synchronized (this) {
- PixelCopy.request(source, dest, this, sHandler);
- return getResultLocked();
- }
- }
-
- public int request(Surface source, Rect srcRect, Bitmap dest) {
- synchronized (this) {
- PixelCopy.request(source, srcRect, dest, this, sHandler);
- return getResultLocked();
- }
- }
-
- public int request(SurfaceView source, Bitmap dest) {
- synchronized (this) {
- PixelCopy.request(source, dest, this, sHandler);
- return getResultLocked();
- }
- }
-
- public int request(SurfaceView source, Rect srcRect, Bitmap dest) {
- synchronized (this) {
- PixelCopy.request(source, srcRect, dest, this, sHandler);
- return getResultLocked();
- }
- }
-
- public int request(Window source, Bitmap dest) {
- synchronized (this) {
- PixelCopy.request(source, dest, this, sHandler);
- return getResultLocked();
- }
- }
-
- public int request(Window source, Rect srcRect, Bitmap dest) {
- synchronized (this) {
- PixelCopy.request(source, srcRect, dest, this, sHandler);
- return getResultLocked();
- }
- }
-
- private int getResultLocked() {
- try {
- this.wait(1000);
- } catch (InterruptedException e) {
- fail("PixelCopy request didn't complete within 1s");
- }
- return mStatus;
- }
-
- @Override
- public void onPixelCopyFinished(int copyResult) {
- synchronized (this) {
- mStatus = copyResult;
- this.notify();
- }
- }
- }
-
private static class SurfaceTextureRule implements TestRule {
private SurfaceTexture mSurfaceTexture = null;
private Surface mSurface = null;
diff --git a/tools/cts-tradefed/etc/cts-tradefed b/tools/cts-tradefed/etc/cts-tradefed
index 75f5f40..f09b581 100755
--- a/tools/cts-tradefed/etc/cts-tradefed
+++ b/tools/cts-tradefed/etc/cts-tradefed
@@ -100,7 +100,7 @@
google-tf-prod-tests"
for JAR in $OPTIONAL_JARS; do
- if [ -f "$JAR.jar" ]; then
+ if [ -f "${JAR_DIR}/${JAR}.jar" ]; then
JAR_PATH=${JAR_PATH}:${JAR_DIR}/${JAR}.jar
fi;
done
diff --git a/tools/cts-tradefed/res/config/cts-known-failures.xml b/tools/cts-tradefed/res/config/cts-known-failures.xml
index 83b6382..10bb65c 100644
--- a/tools/cts-tradefed/res/config/cts-known-failures.xml
+++ b/tools/cts-tradefed/res/config/cts-known-failures.xml
@@ -215,4 +215,7 @@
<!-- b/26235244 -->
<option name="compatibility:exclude-filter" value="android.util.cts.EventLogTest#testWriteEventWithOversizeValue" />
+
+ <!-- b/31803630 -->
+ <option name="compatibility:exclude-filter" value="CtsSecurityTestCases android.security.cts.ListeningPortsTest" />
</configuration>
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget/TestStubs.java b/tools/vm-tests-tf/src/dot/junit/opcodes/iget/TestStubs.java
index 7745918..b5de007 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/iget/TestStubs.java
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget/TestStubs.java
@@ -19,6 +19,6 @@
public class TestStubs {
// used by testVFE4
private int TestStubField = 50;
- // used by testVFE15
+ // used by testVFE15 and testVFE35
protected int TestStubProtectedField = 50;
}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget/Test_iget.java b/tools/vm-tests-tf/src/dot/junit/opcodes/iget/Test_iget.java
index c3a0f09..938beec 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/iget/Test_iget.java
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget/Test_iget.java
@@ -31,7 +31,7 @@
import dot.junit.opcodes.iget.d.T_iget_9;
public class Test_iget extends DxTestCase {
-
+
/**
* @title type - int
*/
@@ -64,9 +64,9 @@
public void testE2() {
loadAndRun("dot.junit.opcodes.iget.d.T_iget_9", NullPointerException.class);
}
-
+
/**
- * @constraint A11
+ * @constraint A11
* @title constant pool index
*/
public void testVFE1() {
@@ -74,23 +74,22 @@
}
/**
- *
- * @constraint A23
+ * @constraint A23
* @title number of registers
*/
public void testVFE2() {
load("dot.junit.opcodes.iget.d.T_iget_3", VerifyError.class);
}
-
+
/**
- * @constraint B13
- * @title read integer from long field - only field with same name but
+ * @constraint B13
+ * @title read integer from long field - only field with same name but
* different type exist
*/
public void testVFE3() {
loadAndRun("dot.junit.opcodes.iget.d.T_iget_13", NoSuchFieldError.class);
}
-
+
/**
* @constraint n/a
* @title Attempt to read inaccessible private field.
@@ -115,7 +114,7 @@
public void testVFE6() {
loadAndRun("dot.junit.opcodes.iget.d.T_iget_8", NoSuchFieldError.class);
}
-
+
/**
* @constraint n/a
* @title Attempt to read superclass' private field from subclass.
@@ -124,69 +123,63 @@
//@uses dot.junit.opcodes.iget.d.T_iget_1
loadAndRun("dot.junit.opcodes.iget.d.T_iget_12", IllegalAccessError.class);
}
-
+
/**
- * @constraint B1
+ * @constraint B1
* @title iget shall not work for reference fields
*/
public void testVFE8() {
load("dot.junit.opcodes.iget.d.T_iget_14", VerifyError.class);
}
-
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget shall not work for short fields
*/
public void testVFE9() {
load("dot.junit.opcodes.iget.d.T_iget_15", VerifyError.class);
}
-
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget shall not work for boolean fields
*/
public void testVFE10() {
load("dot.junit.opcodes.iget.d.T_iget_16", VerifyError.class);
}
-
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget shall not work for char fields
*/
public void testVFE11() {
load("dot.junit.opcodes.iget.d.T_iget_17", VerifyError.class);
}
-
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget shall not work for byte fields
*/
public void testVFE12() {
load("dot.junit.opcodes.iget.d.T_iget_18", VerifyError.class);
- }
-
+ }
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget shall not work for double fields
*/
public void testVFE13() {
load("dot.junit.opcodes.iget.d.T_iget_19", VerifyError.class);
- }
-
+ }
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget shall not work for long fields
*/
public void testVFE14() {
load("dot.junit.opcodes.iget.d.T_iget_20", VerifyError.class);
}
-
+
/**
* @constraint B12
* @title Attempt to read protected field of unrelated class.
@@ -195,18 +188,17 @@
//@uses dot.junit.opcodes.iget.TestStubs
loadAndRun("dot.junit.opcodes.iget.d.T_iget_21", IllegalAccessError.class);
}
-
+
/**
* @constraint A11
* @title Attempt to read static field.
*/
public void testVFE16() {
- //@uses dot.junit.opcodes.iget.TestStubs
loadAndRun("dot.junit.opcodes.iget.d.T_iget_5", IncompatibleClassChangeError.class);
}
/**
- * @constraint B6
+ * @constraint B6
* @title instance fields may only be accessed on already initialized instances.
*/
public void testVFE30() {
@@ -214,11 +206,20 @@
}
/**
- * @constraint N/A
+ * @constraint N/A
* @title instance fields may only be accessed on reference values.
*/
public void testVFE31() {
load("dot.junit.opcodes.iget.d.T_iget_31", VerifyError.class);
}
+
+ /**
+ * @constraint N/A
+ * @title Attempt to read inaccessible protected field on uninitialized reference.
+ */
+ public void testVFE35() {
+ //@uses dot.junit.opcodes.iget.TestStubs
+ load("dot.junit.opcodes.iget.d.T_iget_35", VerifyError.class);
+ }
}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget/d/T_iget_35.d b/tools/vm-tests-tf/src/dot/junit/opcodes/iget/d/T_iget_35.d
new file mode 100644
index 0000000..57df084
--- /dev/null
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget/d/T_iget_35.d
@@ -0,0 +1,31 @@
+; Copyright (C) 2016 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.
+
+.source T_iget_35.java
+.class public dot.junit.opcodes.iget.d.T_iget_35
+.super java/lang/Object
+
+.method public <init>()V
+.limit regs 1
+ invoke-direct {v0}, java/lang/Object/<init>()V
+ return-void
+.end method
+
+.method public run()I
+.limit regs 2
+ iget v0, v1, dot.junit.opcodes.iget.TestStubs.TestStubProtectedField I
+ return v0
+.end method
+
+
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget/d/T_iget_35.java b/tools/vm-tests-tf/src/dot/junit/opcodes/iget/d/T_iget_35.java
new file mode 100644
index 0000000..696a6a1f
--- /dev/null
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget/d/T_iget_35.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 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 dot.junit.opcodes.iget.d;
+
+public class T_iget_35 {
+
+ public int run() {
+ return 0;
+ }
+}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_boolean/TestStubs.java b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_boolean/TestStubs.java
index 8dcd427..af78d741 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_boolean/TestStubs.java
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_boolean/TestStubs.java
@@ -19,6 +19,6 @@
public class TestStubs {
// used by testVFE4
private boolean TestStubField = true;
- // used by testVFE15
+ // used by testVFE15 and testVFE35
protected boolean TestStubProtectedField = true;
}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_boolean/Test_iget_boolean.java b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_boolean/Test_iget_boolean.java
index f16b89c..1f6a94a 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_boolean/Test_iget_boolean.java
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_boolean/Test_iget_boolean.java
@@ -30,16 +30,15 @@
import dot.junit.opcodes.iget_boolean.d.T_iget_boolean_9;
public class Test_iget_boolean extends DxTestCase {
-
+
/**
- * @title get boolean from field
+ * @title get boolean from field
*/
public void testN1() {
T_iget_boolean_1 t = new T_iget_boolean_1();
assertEquals(true, t.run());
}
-
/**
* @title access protected field from subclass
*/
@@ -59,7 +58,7 @@
}
/**
- * @constraint A11
+ * @constraint A11
* @title constant pool index
*/
public void testVFE1() {
@@ -67,22 +66,22 @@
}
/**
- * @constraint A23
+ * @constraint A23
* @title number of registers
*/
public void testVFE2() {
load("dot.junit.opcodes.iget_boolean.d.T_iget_boolean_3", VerifyError.class);
}
-
+
/**
- * @constraint B13
- * @title read boolean from long field - only field with same name but
+ * @constraint B13
+ * @title read boolean from long field - only field with same name but
* different type exists
*/
public void testVFE3() {
loadAndRun("dot.junit.opcodes.iget_boolean.d.T_iget_boolean_13", NoSuchFieldError.class);
}
-
+
/**
* @constraint n/a
* @title Attempt to read inaccessible field
@@ -108,7 +107,7 @@
public void testVFE6() {
loadAndRun("dot.junit.opcodes.iget_boolean.d.T_iget_boolean_8", NoSuchFieldError.class);
}
-
+
/**
* @constraint n/a
* @title Attempt to read superclass' private field from subclass.
@@ -118,64 +117,63 @@
//@uses dot.junit.opcodes.iget_boolean.d.T_iget_boolean_12
loadAndRun("dot.junit.opcodes.iget_boolean.d.T_iget_boolean_12", IllegalAccessError.class);
}
-
+
/**
- * @constraint B1
+ * @constraint B1
* @title iget_boolean shall not work for reference fields
*/
public void testVFE8() {
load("dot.junit.opcodes.iget_boolean.d.T_iget_boolean_14", VerifyError.class);
}
-
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget_boolean shall not work for short fields
*/
public void testVFE9() {
load("dot.junit.opcodes.iget_boolean.d.T_iget_boolean_15", VerifyError.class);
}
-
+
/**
- * @constraint B1
+ * @constraint B1
* @title iget_boolean shall not work for int fields
*/
public void testVFE10() {
load("dot.junit.opcodes.iget_boolean.d.T_iget_boolean_16", VerifyError.class);
}
-
+
/**
- * @constraint B1
+ * @constraint B1
* @title iget_boolean shall not work for char fields
*/
public void testVFE11() {
load("dot.junit.opcodes.iget_boolean.d.T_iget_boolean_17", VerifyError.class);
}
-
+
/**
- * @constraint B1
+ * @constraint B1
* @title iget_boolean shall not work for byte fields
*/
public void testVFE12() {
load("dot.junit.opcodes.iget_boolean.d.T_iget_boolean_18", VerifyError.class);
- }
-
+ }
+
/**
- * @constraint B1
+ * @constraint B1
* @title iget_boolean shall not work for double fields
*/
public void testVFE13() {
load("dot.junit.opcodes.iget_boolean.d.T_iget_boolean_19", VerifyError.class);
- }
-
+ }
+
/**
- * @constraint B1
+ * @constraint B1
* @title iget_boolean shall not work for long fields
*/
public void testVFE14() {
load("dot.junit.opcodes.iget_boolean.d.T_iget_boolean_20", VerifyError.class);
}
-
+
/**
* @constraint B12
* @title Attempt to read inaccessible protected field.
@@ -191,14 +189,13 @@
* @title Attempt to read static field.
*/
public void testVFE16() {
- //@uses dot.junit.opcodes.iget_boolean.TestStubs
loadAndRun("dot.junit.opcodes.iget_boolean.d.T_iget_boolean_5",
IncompatibleClassChangeError.class);
}
/**
- * @constraint B6
- * @title instance fields may only be accessed on already initialized instances.
+ * @constraint B6
+ * @title instance fields may only be accessed on already initialized instances.
*/
public void testVFE30() {
load("dot.junit.opcodes.iget_boolean.d.T_iget_boolean_30", VerifyError.class);
@@ -211,4 +208,13 @@
public void testVFE31() {
load("dot.junit.opcodes.iget_boolean.d.T_iget_boolean_31", VerifyError.class);
}
+
+ /**
+ * @constraint N/A
+ * @title Attempt to read inaccessible protected field on uninitialized reference.
+ */
+ public void testVFE35() {
+ //@uses dot.junit.opcodes.iget_boolean.TestStubs
+ load("dot.junit.opcodes.iget_boolean.d.T_iget_boolean_35", VerifyError.class);
+ }
}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_boolean/d/T_iget_boolean_35.d b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_boolean/d/T_iget_boolean_35.d
new file mode 100644
index 0000000..babcc08
--- /dev/null
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_boolean/d/T_iget_boolean_35.d
@@ -0,0 +1,31 @@
+; Copyright (C) 2016 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.
+
+.source T_iget_boolean_35.java
+.class public dot.junit.opcodes.iget_boolean.d.T_iget_boolean_35
+.super java/lang/Object
+
+.method public <init>()V
+.limit regs 1
+ invoke-direct {v0}, java/lang/Object/<init>()V
+ return-void
+.end method
+
+.method public run()Z
+.limit regs 2
+ iget-boolean v0, v1, dot.junit.opcodes.iget_boolean.TestStubs.TestStubProtectedField Z
+ return v0
+.end method
+
+
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_boolean/d/T_iget_boolean_35.java b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_boolean/d/T_iget_boolean_35.java
new file mode 100644
index 0000000..7c95c37
--- /dev/null
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_boolean/d/T_iget_boolean_35.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 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 dot.junit.opcodes.iget_boolean.d;
+
+public class T_iget_boolean_35 {
+
+ public boolean run(){
+ return false;
+ }
+}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_byte/TestStubs.java b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_byte/TestStubs.java
index 90b1f13..ffae3b1 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_byte/TestStubs.java
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_byte/TestStubs.java
@@ -19,6 +19,6 @@
public class TestStubs {
// used by testVFE4
private byte TestStubField = 50;
- // ussed by testVFE15
+ // used by testVFE15 and testVFE35
protected byte TestStubProtectedField = 50;
}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_byte/Test_iget_byte.java b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_byte/Test_iget_byte.java
index 410dad4..4e7bbc7 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_byte/Test_iget_byte.java
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_byte/Test_iget_byte.java
@@ -30,16 +30,15 @@
import dot.junit.opcodes.iget_byte.d.T_iget_byte_9;
public class Test_iget_byte extends DxTestCase {
-
+
/**
- * @title get byte from field
+ * @title get byte from field
*/
public void testN1() {
T_iget_byte_1 t = new T_iget_byte_1();
assertEquals(77, t.run());
}
-
/**
* @title access protected field from subclass
*/
@@ -55,10 +54,10 @@
*/
public void testE2() {
loadAndRun("dot.junit.opcodes.iget_byte.d.T_iget_byte_9", NullPointerException.class);
- }
+ }
/**
- * @constraint A11
+ * @constraint A11
* @title constant pool index
*/
public void testVFE1() {
@@ -66,22 +65,22 @@
}
/**
- * @constraint A23
+ * @constraint A23
* @title number of registers
*/
public void testVFE2() {
load("dot.junit.opcodes.iget_byte.d.T_iget_byte_3", VerifyError.class);
}
-
+
/**
- * @constraint B13
- * @title read byte from long field - only field with same name but
+ * @constraint B13
+ * @title read byte from long field - only field with same name but
* different type exists
*/
public void testVFE3() {
loadAndRun("dot.junit.opcodes.iget_byte.d.T_iget_byte_13", NoSuchFieldError.class);
}
-
+
/**
* @constraint n/a
* @title Attempt to read inaccessible field.
@@ -106,7 +105,7 @@
public void testVFE6() {
loadAndRun("dot.junit.opcodes.iget_byte.d.T_iget_byte_8", NoSuchFieldError.class);
}
-
+
/**
* @constraint n/a
* @title Attempt to read superclass' private field from subclass.
@@ -115,63 +114,63 @@
//@uses dot.junit.opcodes.iget_byte.d.T_iget_byte_1
loadAndRun("dot.junit.opcodes.iget_byte.d.T_iget_byte_12", IllegalAccessError.class);
}
-
+
/**
- * @constraint B1
+ * @constraint B1
* @title iget_byte shall not work for reference fields
*/
public void testVFE8() {
load("dot.junit.opcodes.iget_byte.d.T_iget_byte_14", VerifyError.class);
}
-
+
/**
- * @constraint B1
+ * @constraint B1
* @title iget_byte shall not work for short fields
*/
public void testVFE9() {
load("dot.junit.opcodes.iget_byte.d.T_iget_byte_15", VerifyError.class);
}
-
+
/**
- * @constraint B1
+ * @constraint B1
* @title iget_byte shall not work for int fields
*/
public void testVFE10() {
load("dot.junit.opcodes.iget_byte.d.T_iget_byte_16", VerifyError.class);
}
-
+
/**
- * @constraint B1
+ * @constraint B1
* @title iget_byte shall not work for char fields
*/
public void testVFE11() {
load("dot.junit.opcodes.iget_byte.d.T_iget_byte_17", VerifyError.class);
}
-
+
/**
- * @constraint B1
+ * @constraint B1
* @title iget_byte shall not work for boolean fields
*/
public void testVFE12() {
load("dot.junit.opcodes.iget_byte.d.T_iget_byte_18", VerifyError.class);
- }
-
+ }
+
/**
- * @constraint B1
+ * @constraint B1
* @title iget_byte shall not work for double fields
*/
public void testVFE13() {
load("dot.junit.opcodes.iget_byte.d.T_iget_byte_19", VerifyError.class);
- }
-
+ }
+
/**
- * @constraint B1
+ * @constraint B1
* @title iget_byte shall not work for long fields
*/
public void testVFE14() {
load("dot.junit.opcodes.iget_byte.d.T_iget_byte_20", VerifyError.class);
}
-
+
/**
* @constraint B12
* @title Attempt to read inaccessible protected field.
@@ -181,20 +180,18 @@
loadAndRun("dot.junit.opcodes.iget_byte.d.T_iget_byte_21", IllegalAccessError.class);
}
-
/**
* @constraint A11
* @title Attempt to read static field.
*/
public void testVFE16() {
- //@uses dot.junit.opcodes.iget_byte.TestStubs
loadAndRun("dot.junit.opcodes.iget_byte.d.T_iget_byte_5",
IncompatibleClassChangeError.class);
}
/**
- * @constraint B6
- * @title instance fields may only be accessed on already initialized instances.
+ * @constraint B6
+ * @title instance fields may only be accessed on already initialized instances.
*/
public void testVFE30() {
load("dot.junit.opcodes.iget_byte.d.T_iget_byte_30", VerifyError.class);
@@ -207,4 +204,13 @@
public void testVFE31() {
load("dot.junit.opcodes.iget_byte.d.T_iget_byte_31", VerifyError.class);
}
+
+ /**
+ * @constraint N/A
+ * @title Attempt to read inaccessible protected field on uninitialized reference.
+ */
+ public void testVFE35() {
+ //@uses dot.junit.opcodes.iget_byte.TestStubs
+ load("dot.junit.opcodes.iget_byte.d.T_iget_byte_35", VerifyError.class);
+ }
}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_byte/d/T_iget_byte_35.d b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_byte/d/T_iget_byte_35.d
new file mode 100644
index 0000000..8792c96
--- /dev/null
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_byte/d/T_iget_byte_35.d
@@ -0,0 +1,33 @@
+; Copyright (C) 2016 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.
+
+.source T_iget_byte_35.java
+.class public dot.junit.opcodes.iget_byte.d.T_iget_byte_35
+.super java/lang/Object
+
+.method public <init>()V
+.limit regs 1
+ invoke-direct {v0}, java/lang/Object/<init>()V
+ return-void
+.end method
+
+.method public run()B
+.limit regs 2
+ iget-byte v0, v1, dot.junit.opcodes.iget_byte.TestStubs.TestStubProtectedField B
+ return v0
+.end method
+
+
+
+
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_byte/d/T_iget_byte_35.java b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_byte/d/T_iget_byte_35.java
new file mode 100644
index 0000000..17e78f1
--- /dev/null
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_byte/d/T_iget_byte_35.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 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 dot.junit.opcodes.iget_byte.d;
+
+public class T_iget_byte_35 {
+
+ public byte run() {
+ return 0;
+ }
+}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_char/TestStubs.java b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_char/TestStubs.java
index 5bf73ae..6969a104 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_char/TestStubs.java
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_char/TestStubs.java
@@ -19,6 +19,6 @@
public class TestStubs {
// used by testVFE4
private char TestStubField = 50;
- // ussed by testVFE15
+ // ussed by testVFE15 and testVFE35
protected char TestStubProtectedField = 50;
}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_char/Test_iget_char.java b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_char/Test_iget_char.java
index ff86ae3..c2b8f1c 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_char/Test_iget_char.java
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_char/Test_iget_char.java
@@ -30,9 +30,9 @@
import dot.junit.opcodes.iget_char.d.T_iget_char_9;
public class Test_iget_char extends DxTestCase {
-
+
/**
- * @title get char from field
+ * @title get char from field
*/
public void testN1() {
T_iget_char_1 t = new T_iget_char_1();
@@ -56,10 +56,9 @@
public void testE2() {
loadAndRun("dot.junit.opcodes.iget_char.d.T_iget_char_9", NullPointerException.class);
}
-
/**
- * @constraint A11
+ * @constraint A11
* @title constant pool index
*/
public void testVFE1() {
@@ -67,24 +66,22 @@
}
/**
- *
- * @constraint A23
+ * @constraint A23
* @title number of registers
*/
public void testVFE2() {
load("dot.junit.opcodes.iget_char.d.T_iget_char_3", VerifyError.class);
}
-
+
/**
- *
- * @constraint B13
- * @title read char from long field - only field with same name but
+ * @constraint B13
+ * @title read char from long field - only field with same name but
* different type exists
*/
public void testVFE3() {
loadAndRun("dot.junit.opcodes.iget_char.d.T_iget_char_13", NoSuchFieldError.class);
}
-
+
/**
* @constraint n/a
* @title Attempt to read inaccessible field.
@@ -109,7 +106,7 @@
public void testVFE6() {
loadAndRun("dot.junit.opcodes.iget_char.d.T_iget_char_8", NoSuchFieldError.class);
}
-
+
/**
* @constraint n/a
* @title Attempt to read superclass' private field from subclass.
@@ -118,67 +115,63 @@
//@uses dot.junit.opcodes.iget_char.d.T_iget_char_1
loadAndRun("dot.junit.opcodes.iget_char.d.T_iget_char_12", IllegalAccessError.class);
}
-
+
/**
- * @constraint B1
+ * @constraint B1
* @title iget_char shall not work for reference fields
*/
public void testVFE8() {
load("dot.junit.opcodes.iget_char.d.T_iget_char_14", VerifyError.class);
}
-
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget_char shall not work for short fields
*/
public void testVFE9() {
load("dot.junit.opcodes.iget_char.d.T_iget_char_15", VerifyError.class);
}
-
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget_char shall not work for int fields
*/
public void testVFE10() {
load("dot.junit.opcodes.iget_char.d.T_iget_char_16", VerifyError.class);
}
-
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget_char shall not work for byte fields
*/
public void testVFE11() {
load("dot.junit.opcodes.iget_char.d.T_iget_char_17", VerifyError.class);
}
-
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget_char shall not work for boolean fields
*/
public void testVFE12() {
load("dot.junit.opcodes.iget_char.d.T_iget_char_18", VerifyError.class);
- }
-
+ }
+
/**
- * @constraint B1
+ * @constraint B1
* @title iget_char shall not work for double fields
*/
public void testVFE13() {
load("dot.junit.opcodes.iget_char.d.T_iget_char_19", VerifyError.class);
- }
-
+ }
+
/**
- * @constraint B1
+ * @constraint B1
* @title iget_char shall not work for long fields
*/
public void testVFE14() {
load("dot.junit.opcodes.iget_char.d.T_iget_char_20", VerifyError.class);
}
-
+
/**
* @constraint B12
* @title Attempt to read inaccessible protected field.
@@ -194,15 +187,13 @@
* @title Attempt to read static field.
*/
public void testVFE16() {
- //@uses dot.junit.opcodes.iget_char.TestStubs
loadAndRun("dot.junit.opcodes.iget_char.d.T_iget_char_5",
IncompatibleClassChangeError.class);
}
-
/**
- * @constraint B6
- * @title instance fields may only be accessed on already initialized instances.
+ * @constraint B6
+ * @title instance fields may only be accessed on already initialized instances.
*/
public void testVFE30() {
load("dot.junit.opcodes.iget_char.d.T_iget_char_30", VerifyError.class);
@@ -215,4 +206,13 @@
public void testVFE31() {
load("dot.junit.opcodes.iget_char.d.T_iget_char_31", VerifyError.class);
}
+
+ /**
+ * @constraint N/A
+ * @title Attempt to read inaccessible protected field on uninitialized reference.
+ */
+ public void testVFE35() {
+ //@uses dot.junit.opcodes.iget_char.TestStubs
+ load("dot.junit.opcodes.iget_char.d.T_iget_char_35", VerifyError.class);
+ }
}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_char/d/T_iget_char_35.d b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_char/d/T_iget_char_35.d
new file mode 100644
index 0000000..53ac999
--- /dev/null
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_char/d/T_iget_char_35.d
@@ -0,0 +1,31 @@
+; Copyright (C) 2016 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.
+
+.source T_iget_char_35.java
+.class public dot.junit.opcodes.iget_char.d.T_iget_char_35
+.super java/lang/Object
+
+.method public <init>()V
+.limit regs 1
+ invoke-direct {v0}, java/lang/Object/<init>()V
+ return-void
+.end method
+
+.method public run()C
+.limit regs 2
+ iget-char v0, v1, dot.junit.opcodes.iget_char.TestStubs.TestStubProtectedField C
+ return v0
+.end method
+
+
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_char/d/T_iget_char_35.java b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_char/d/T_iget_char_35.java
new file mode 100644
index 0000000..0b3434a
--- /dev/null
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_char/d/T_iget_char_35.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 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 dot.junit.opcodes.iget_char.d;
+
+public class T_iget_char_35 {
+
+ public char run() {
+ return 0;
+ }
+}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_object/TestStubs.java b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_object/TestStubs.java
index 0a68e56..74d7f93 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_object/TestStubs.java
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_object/TestStubs.java
@@ -19,6 +19,6 @@
public class TestStubs {
// used by testVFE4
private Object TestStubField = null;
- // used by testVFE15
+ // used by testVFE16 and testVFE35
protected Object TestStubProtectedField = null;
}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_object/Test_iget_object.java b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_object/Test_iget_object.java
index 7314141..9ae5548 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_object/Test_iget_object.java
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_object/Test_iget_object.java
@@ -24,6 +24,7 @@
import dot.junit.opcodes.iget_object.d.T_iget_object_13;
import dot.junit.opcodes.iget_object.d.T_iget_object_21;
import dot.junit.opcodes.iget_object.d.T_iget_object_22;
+import dot.junit.opcodes.iget_object.d.T_iget_object_35;
import dot.junit.opcodes.iget_object.d.T_iget_object_5;
import dot.junit.opcodes.iget_object.d.T_iget_object_6;
import dot.junit.opcodes.iget_object.d.T_iget_object_7;
@@ -31,9 +32,9 @@
import dot.junit.opcodes.iget_object.d.T_iget_object_9;
public class Test_iget_object extends DxTestCase {
-
+
/**
- * @title get reference from field
+ * @title get reference from field
*/
public void testN1() {
T_iget_object_1 t = new T_iget_object_1();
@@ -56,10 +57,10 @@
*/
public void testE2() {
loadAndRun("dot.junit.opcodes.iget_object.d.T_iget_object_9", NullPointerException.class);
- }
+ }
/**
- * @constraint A11
+ * @constraint A11
* @title constant pool index
*/
public void testVFE1() {
@@ -67,24 +68,22 @@
}
/**
- *
- * @constraint A23
+ * @constraint A23
* @title number of registers
*/
public void testVFE2() {
load("dot.junit.opcodes.iget_object.d.T_iget_object_3", VerifyError.class);
}
-
+
/**
- *
- * @constraint B13
- * @title (read object from long field - only field with same name but
+ * @constraint B13
+ * @title (read object from long field - only field with same name but
* different type exists)
*/
public void testVFE3() {
loadAndRun("dot.junit.opcodes.iget_object.d.T_iget_object_13", NoSuchFieldError.class);
}
-
+
/**
* @constraint n/a
* @title Attempt to read inaccessible field.
@@ -109,7 +108,7 @@
public void testVFE6() {
loadAndRun("dot.junit.opcodes.iget_object.d.T_iget_object_8", NoSuchFieldError.class);
}
-
+
/**
* @constraint n/a
* @title Attempt to read superclass' private field from subclass.
@@ -118,78 +117,71 @@
//@uses dot.junit.opcodes.iget_object.d.T_iget_object_1
loadAndRun("dot.junit.opcodes.iget_object.d.T_iget_object_12", IllegalAccessError.class);
}
-
+
/**
- * @constraint B1
+ * @constraint B1
* @title iget_object shall not work for short fields
*/
public void testVFE8() {
load("dot.junit.opcodes.iget_object.d.T_iget_object_14", VerifyError.class);
}
-
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget_object shall not work for char fields
*/
public void testVFE9() {
load("dot.junit.opcodes.iget_object.d.T_iget_object_15", VerifyError.class);
}
-
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget_object shall not work for int fields
*/
public void testVFE10() {
load("dot.junit.opcodes.iget_object.d.T_iget_object_16", VerifyError.class);
}
-
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget_object shall not work for byte fields
*/
public void testVFE11() {
load("dot.junit.opcodes.iget_object.d.T_iget_object_17", VerifyError.class);
}
-
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget_object shall not work for boolean fields
*/
public void testVFE12() {
load("dot.junit.opcodes.iget_object.d.T_iget_object_18", VerifyError.class);
- }
-
+ }
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget_object shall not work for double fields
*/
public void testVFE13() {
load("dot.junit.opcodes.iget_object.d.T_iget_object_19", VerifyError.class);
- }
-
+ }
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget_object shall not work for long fields
*/
public void testVFE14() {
load("dot.junit.opcodes.iget_object.d.T_iget_object_20", VerifyError.class);
- }
-
+ }
+
/**
- *
- * @constraint B13
+ * @constraint B13
* @title only field of different type exists
*/
public void testVFE15() {
loadAndRun("dot.junit.opcodes.iget_object.d.T_iget_object_21", NoSuchFieldError.class);
}
-
+
/**
* @constraint B12
* @title Attempt to read inaccessible protected field.
@@ -204,14 +196,13 @@
* @title Attempt to read static field.
*/
public void testVFE17() {
- //@uses dot.junit.opcodes.iget_object.TestStubs
loadAndRun("dot.junit.opcodes.iget_object.d.T_iget_object_5",
IncompatibleClassChangeError.class);
}
/**
- * @constraint B6
- * @title instance fields may only be accessed on already initialized instances.
+ * @constraint B6
+ * @title instance fields may only be accessed on already initialized instances.
*/
public void testVFE30() {
load("dot.junit.opcodes.iget_object.d.T_iget_object_30", VerifyError.class);
@@ -219,9 +210,18 @@
/**
* @constraint N/A
- * @title instance fields may only be accessed on already initialized instances.
+ * @title instance fields may only be accessed on already initialized instances.
*/
public void testVFE31() {
load("dot.junit.opcodes.iget_object.d.T_iget_object_31", VerifyError.class);
}
+
+ /**
+ * @constraint N/A
+ * @title Attempt to read inaccessible protected field on uninitialized reference.
+ */
+ public void testVFE35() {
+ //@uses dot.junit.opcodes.iget_object.TestStubs
+ load("dot.junit.opcodes.iget_object.d.T_iget_object_35", VerifyError.class);
+ }
}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_object/d/T_iget_object_35.d b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_object/d/T_iget_object_35.d
new file mode 100644
index 0000000..a4fc423
--- /dev/null
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_object/d/T_iget_object_35.d
@@ -0,0 +1,31 @@
+; Copyright (C) 2016 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.
+
+.source T_iget_object_35.java
+.class public dot.junit.opcodes.iget_object.d.T_iget_object_35
+.super java/lang/Object
+
+.method public <init>()V
+.limit regs 1
+ invoke-direct {v0}, java/lang/Object/<init>()V
+ return-void
+.end method
+
+.method public run()Ljava/lang/Object;
+.limit regs 2
+ iget-object v0, v1, dot.junit.opcodes.iget_object.TestStubs.TestStubProtectedField Ljava/lang/Object;
+ return-object v0
+.end method
+
+
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_object/d/T_iget_object_35.java b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_object/d/T_iget_object_35.java
new file mode 100644
index 0000000..f7ceb9b
--- /dev/null
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_object/d/T_iget_object_35.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 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 dot.junit.opcodes.iget_object.d;
+
+public class T_iget_object_35 {
+
+ public Object run() {
+ return null;
+ }
+}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_short/TestStubs.java b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_short/TestStubs.java
index 8580ec3..f299301 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_short/TestStubs.java
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_short/TestStubs.java
@@ -19,6 +19,6 @@
public class TestStubs {
// used by testVFE4
private short TestStubField = 50;
- // used by testVFE15
+ // used by testVFE15 and testVFE35
protected short TestStubProtectedField = 50;
}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_short/Test_iget_short.java b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_short/Test_iget_short.java
index 566ec3f..bd77708 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_short/Test_iget_short.java
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_short/Test_iget_short.java
@@ -30,7 +30,7 @@
import dot.junit.opcodes.iget_short.d.T_iget_short_9;
public class Test_iget_short extends DxTestCase {
-
+
/**
* @title get short from field
*/
@@ -58,7 +58,7 @@
}
/**
- * @constraint A11
+ * @constraint A11
* @title constant pool index
*/
public void testVFE1() {
@@ -66,24 +66,22 @@
}
/**
- *
- * @constraint A23
+ * @constraint A23
* @title number of registers
*/
public void testVFE2() {
load("dot.junit.opcodes.iget_short.d.T_iget_short_3", VerifyError.class);
}
-
+
/**
- *
- * @constraint B13
- * @title read short from long field - only field with same name but
+ * @constraint B13
+ * @title read short from long field - only field with same name but
* different type exists
*/
public void testVFE3() {
loadAndRun("dot.junit.opcodes.iget_short.d.T_iget_short_13", NoSuchFieldError.class);
}
-
+
/**
* @constraint n/a
* @title Attempt to read inaccessible field.
@@ -108,7 +106,7 @@
public void testVFE6() {
loadAndRun("dot.junit.opcodes.iget_short.d.T_iget_short_8", NoSuchFieldError.class);
}
-
+
/**
* @constraint n/a
* @title Attempt to read superclass' private field from subclass.
@@ -117,69 +115,63 @@
//@uses dot.junit.opcodes.iget_short.d.T_iget_short_1
loadAndRun("dot.junit.opcodes.iget_short.d.T_iget_short_12", IllegalAccessError.class);
}
-
+
/**
- * @constraint B1
+ * @constraint B1
* @title iget_short shall not work for reference fields
*/
public void testVFE8() {
load("dot.junit.opcodes.iget_short.d.T_iget_short_14", VerifyError.class);
}
-
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget_short shall not work for char fields
*/
public void testVFE9() {
load("dot.junit.opcodes.iget_short.d.T_iget_short_15", VerifyError.class);
}
-
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget_short shall not work for int fields
*/
public void testVFE10() {
load("dot.junit.opcodes.iget_short.d.T_iget_short_16", VerifyError.class);
}
-
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget_short shall not work for byte fields
*/
public void testVFE11() {
load("dot.junit.opcodes.iget_short.d.T_iget_short_17", VerifyError.class);
}
-
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget_short shall not work for boolean fields
*/
public void testVFE12() {
load("dot.junit.opcodes.iget_short.d.T_iget_short_18", VerifyError.class);
- }
-
+ }
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget_short shall not work for double fields
*/
public void testVFE13() {
load("dot.junit.opcodes.iget_short.d.T_iget_short_19", VerifyError.class);
- }
-
+ }
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget_short shall not work for long fields
*/
public void testVFE14() {
load("dot.junit.opcodes.iget_short.d.T_iget_short_20", VerifyError.class);
}
-
+
/**
* @constraint B12
* @title Attempt to read inaccessible protected field.
@@ -194,24 +186,32 @@
* @title Attempt to read static field.
*/
public void testVFE16() {
- //@uses dot.junit.opcodes.iget_short.TestStubs
loadAndRun("dot.junit.opcodes.iget_short.d.T_iget_short_5",
IncompatibleClassChangeError.class);
}
/**
- * @constraint B6
- * @title instance fields may only be accessed on already initialized instances.
+ * @constraint B6
+ * @title instance fields may only be accessed on already initialized instances.
*/
public void testVFE30() {
load("dot.junit.opcodes.iget_short.d.T_iget_short_30", VerifyError.class);
}
/**
- * @constraint N/A
- * @title instance fields may only be accessed on already initialized instances.
+ * @constraint N/A
+ * @title instance fields may only be accessed on already initialized instances.
*/
public void testVFE31() {
load("dot.junit.opcodes.iget_short.d.T_iget_short_31", VerifyError.class);
}
+
+ /**
+ * @constraint N/A
+ * @title Attempt to read inaccessible protected field on uninitialized reference.
+ */
+ public void testVFE35() {
+ //@uses dot.junit.opcodes.iget_short.TestStubs
+ load("dot.junit.opcodes.iget_short.d.T_iget_short_35", VerifyError.class);
+ }
}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_short/d/T_iget_short_21.d b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_short/d/T_iget_short_21.d
index e806df2..f3298fb 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_short/d/T_iget_short_21.d
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_short/d/T_iget_short_21.d
@@ -24,14 +24,14 @@
return-void
.end method
-.method public run()V
+.method public run()S
.limit regs 3
new-instance v0, Ldot/junit/opcodes/iget_short/TestStubs;
invoke-direct {v0}, dot/junit/opcodes/iget_short/TestStubs/<init>()V
-
+
iget-short v1, v0, dot.junit.opcodes.iget_short.TestStubs.TestStubProtectedField S
- return-void
+ return v1
.end method
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_short/d/T_iget_short_21.java b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_short/d/T_iget_short_21.java
index 5bb7f6e..bcb8dca 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_short/d/T_iget_short_21.java
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_short/d/T_iget_short_21.java
@@ -18,6 +18,7 @@
public class T_iget_short_21 {
- public void run() {
+ public short run() {
+ return 0;
}
}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_short/d/T_iget_short_35.d b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_short/d/T_iget_short_35.d
new file mode 100644
index 0000000..5cde5bb
--- /dev/null
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_short/d/T_iget_short_35.d
@@ -0,0 +1,32 @@
+; Copyright (C) 2016 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.
+
+.source T_iget_short_35.java
+.class public dot.junit.opcodes.iget_short.d.T_iget_short_35
+.super java/lang/Object
+
+.method public <init>()V
+.limit regs 1
+ invoke-direct {v0}, java/lang/Object/<init>()V
+ return-void
+.end method
+
+.method public run()S
+.limit regs 2
+ iget-short v0, v1, dot.junit.opcodes.iget_short.TestStubs.TestStubProtectedField S
+ return v0
+.end method
+
+
+
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_short/d/T_iget_short_35.java b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_short/d/T_iget_short_35.java
new file mode 100644
index 0000000..f92f2b6
--- /dev/null
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_short/d/T_iget_short_35.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 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 dot.junit.opcodes.iget_short.d;
+
+public class T_iget_short_35 {
+
+ public short run() {
+ return 0;
+ }
+}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_wide/TestStubs.java b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_wide/TestStubs.java
index f90a470..79e71c4 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_wide/TestStubs.java
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_wide/TestStubs.java
@@ -19,6 +19,6 @@
public class TestStubs {
// used by testVFE4
private long TestStubField = 50;
- // ussed by testVFE15
+ // ussed by testVFE15 and testVFE35
protected long TestStubProtectedField = 50;
}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_wide/Test_iget_wide.java b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_wide/Test_iget_wide.java
index f82a1d3..6bb996f 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_wide/Test_iget_wide.java
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_wide/Test_iget_wide.java
@@ -31,7 +31,7 @@
import dot.junit.opcodes.iget_wide.d.T_iget_wide_9;
public class Test_iget_wide extends DxTestCase {
-
+
/**
* @title type - long
*/
@@ -63,10 +63,10 @@
*/
public void testE2() {
loadAndRun("dot.junit.opcodes.iget_wide.d.T_iget_wide_9", NullPointerException.class);
- }
+ }
/**
- * @constraint A11
+ * @constraint A11
* @title constant pool index
*/
public void testVFE1() {
@@ -74,24 +74,22 @@
}
/**
- *
- * @constraint A23
+ * @constraint A23
* @title number of registers
*/
public void testVFE2() {
load("dot.junit.opcodes.iget_wide.d.T_iget_wide_3", VerifyError.class);
}
-
+
/**
- *
- * @constraint B13
- * @title read long from integer field - only field with same name but
+ * @constraint B13
+ * @title read long from integer field - only field with same name but
* different type exists
*/
public void testVFE3() {
loadAndRun("dot.junit.opcodes.iget_wide.d.T_iget_wide_13", NoSuchFieldError.class);
}
-
+
/**
* @constraint n/a
* @title Attempt to read inaccessible field.
@@ -103,7 +101,7 @@
/**
* @constraint n/a
- * @title Attempt to read field of undefined class.
+ * @title Attempt to read field of undefined class.
*/
public void testVFE5() {
loadAndRun("dot.junit.opcodes.iget_wide.d.T_iget_wide_7", NoClassDefFoundError.class);
@@ -125,69 +123,63 @@
//@uses dot.junit.opcodes.iget_wide.d.T_iget_wide_1
loadAndRun("dot.junit.opcodes.iget_wide.d.T_iget_wide_12", IllegalAccessError.class);
}
-
+
/**
- * @constraint B1
+ * @constraint B1
* @title iget-wide shall not work for reference fields
*/
public void testVFE8() {
load("dot.junit.opcodes.iget_wide.d.T_iget_wide_14", VerifyError.class);
}
-
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget-wide shall not work for short fields
*/
public void testVFE9() {
load("dot.junit.opcodes.iget_wide.d.T_iget_wide_15", VerifyError.class);
}
-
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget-wide shall not work for boolean fields
*/
public void testVFE10() {
load("dot.junit.opcodes.iget_wide.d.T_iget_wide_16", VerifyError.class);
}
-
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget-wide shall not work for char fields
*/
public void testVFE11() {
load("dot.junit.opcodes.iget_wide.d.T_iget_wide_17", VerifyError.class);
}
-
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget-wide shall not work for byte fields
*/
public void testVFE12() {
load("dot.junit.opcodes.iget_wide.d.T_iget_wide_18", VerifyError.class);
- }
-
+ }
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget-wide shall not work for float fields
*/
public void testVFE13() {
load("dot.junit.opcodes.iget_wide.d.T_iget_wide_19", VerifyError.class);
- }
-
+ }
+
/**
- *
- * @constraint B1
+ * @constraint B1
* @title iget-wide shall not work for int fields
*/
public void testVFE14() {
load("dot.junit.opcodes.iget_wide.d.T_iget_wide_20", VerifyError.class);
}
-
+
/**
* @constraint B12
* @title Attempt to read inaccessible protected field.
@@ -202,14 +194,13 @@
* @title Attempt to read static field.
*/
public void testVFE16() {
- //@uses dot.junit.opcodes.iget_wide.TestStubs
- loadAndRun("dot.junit.opcodes.iget_wide.d.T_iget_wide_5",
+ loadAndRun("dot.junit.opcodes.iget_wide.d.T_iget_wide_5",
IncompatibleClassChangeError.class);
}
/**
- * @constraint B6
- * @title instance fields may only be accessed on already initialized instances.
+ * @constraint B6
+ * @title instance fields may only be accessed on already initialized instances.
*/
public void testVFE30() {
load("dot.junit.opcodes.iget_wide.d.T_iget_wide_30", VerifyError.class);
@@ -217,9 +208,18 @@
/**
* @constraint N/A
- * @title instance fields may only be accessed on already initialized instances.
+ * @title instance fields may only be accessed on already initialized instances.
*/
public void testVFE31() {
load("dot.junit.opcodes.iget_wide.d.T_iget_wide_31", VerifyError.class);
}
+
+ /**
+ * @constraint N/A
+ * @title Attempt to read inaccessible protected field on uninitialized reference.
+ */
+ public void testVFE35() {
+ //@uses dot.junit.opcodes.iget_wide.TestStubs
+ load("dot.junit.opcodes.iget_wide.d.T_iget_wide_35", VerifyError.class);
+ }
}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_wide/d/T_iget_wide_35.d b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_wide/d/T_iget_wide_35.d
new file mode 100644
index 0000000..505b06e
--- /dev/null
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_wide/d/T_iget_wide_35.d
@@ -0,0 +1,31 @@
+; Copyright (C) 2016 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.
+
+.source T_iget_wide_35.java
+.class public dot.junit.opcodes.iget_wide.d.T_iget_wide_35
+.super java/lang/Object
+
+.method public <init>()V
+.limit regs 1
+ invoke-direct {v0}, java/lang/Object/<init>()V
+ return-void
+.end method
+
+.method public run()J
+.limit regs 2
+ iget-wide v0, v1, dot.junit.opcodes.iget_wide.TestStubs.TestStubProtectedField J
+ return-wide v0
+.end method
+
+
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/iget_wide/d/T_iget_wide_35.java b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_wide/d/T_iget_wide_35.java
new file mode 100644
index 0000000..5a8fc86
--- /dev/null
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/iget_wide/d/T_iget_wide_35.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 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 dot.junit.opcodes.iget_wide.d;
+
+public class T_iget_wide_35 {
+
+ public long run() {
+ return -99;
+ }
+}